diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 29af6c8..c08c858 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,6 +35,8 @@ jobs: run: python backend/security_static_checks.py - name: Public shell guardrails run: python scripts/check_public_shell.py + - name: Public shell synchronization + run: python scripts/sync_public_shell.py --check - name: Performance budgets run: python scripts/performance_budget.py diff --git a/backend/test_deep_audit_regressions.py b/backend/test_deep_audit_regressions.py index e5e1c55..162eaf8 100644 --- a/backend/test_deep_audit_regressions.py +++ b/backend/test_deep_audit_regressions.py @@ -1081,8 +1081,10 @@ def test_first_task_wizard_and_measurement_invariants(self): "lead_qualification: {", "homepage_describe_task_primary_click", "Describe your task", - "Guided first-task wizard", - "Trust before payment", + 'data-simplified-home="true"', + 'id="guided-task-intake"', + "Build a task draft", + 'data-home-section="how"', "first_task_wizard_started", "first_task_template_selected", "first_task_wizard_review_draft_click", @@ -1208,7 +1210,8 @@ def test_phase2_ui_flow_polish_invariants(self): "id=\"apply-portfolio-url\"", "New listing", "services-result-count", - "Filter services", + 'data-filter-toggle', + "function toggleServiceFilters(forceOpen)", "Keep payments on-platform", "You can apply to jobs before connecting payouts.", "grid-template-columns:repeat(auto-fit,minmax(240px,1fr))", @@ -1248,7 +1251,7 @@ def test_audit_redacts_sensitive_details_recursively(self): def test_trust_safety_page_covers_payment_and_dispute_safety(self): text = (REPO_ROOT / "frontend/trust-safety.html").read_text(encoding="utf-8", errors="ignore").lower() - required = ["stripe-powered processing", "payment review", "issue review", "off-platform", "dispute"] + required = ["stripe-powered processing", "payment review", "issue review", "off-platform", "available evidence", "dispute"] missing = [snippet for snippet in required if snippet not in text] self.assertEqual(missing, []) @@ -1608,28 +1611,33 @@ def test_admin_worker_activation_notifications_create_in_app_notifications(self) def test_jobs_page_highlights_worker_activation_path(self): text = (REPO_ROOT / "frontend/index.html").read_text(encoding="utf-8", errors="ignore") for snippet in [ - "New paid jobs", - "Apply directly through GoHireHumans", - "Newest open jobs are shown first", + "No public jobs right now", + "Create a worker profile", + "open job${total === 1 ? '' : 's'} ยท newest first", "worker_jobs_apply_cta_click", "worker_job_card_apply_click", "Apply now", "const sortedJobs = [...jobs].sort", ]: self.assertIn(snippet, text) + for contradictory in ["New paid jobs", "View open jobs"]: + self.assertNotIn(contradictory, text) def test_public_homepage_visual_cleanup_invariants(self): text = (REPO_ROOT / "frontend/index.html").read_text(encoding="utf-8", errors="ignore") for snippet in [ - "Human verification", - "Choose the fastest proof-backed check.", - "Trust before payment", - "For workers and agents", + 'data-simplified-home="true"', + "Describe the work. Hire the right human.", + "What do you need help with?", + "Clear scope, visible evidence, buyer approval.", + "A clear route for workers and agents.", "homepage_describe_task_primary_click", "homepage_high_intent_route_click", ]: self.assertIn(snippet, text) + self.assertEqual(text.count('data-home-section="'), 5) + self.assertNotIn("Four ways to start small.", text) self.assertNotIn("GA4 shows visitors", text) self.assertNotIn("High-intent visitors", text) @@ -1639,7 +1647,7 @@ def test_market_discovery_pages_capture_open_ended_demand(self): "lp-market-discovery", "homepage_request_any_task_click", "homepage_task_ideas_click", - "Describe any task you need a human to check.", + "Describe any task you need a human to do or check", ], "frontend/ideas.html": [ "What should people hire humans for?", @@ -1694,23 +1702,23 @@ def test_high_intent_seo_pages_feed_starter_offer_funnel(self): def test_first_orders_conversion_infrastructure_is_discoverable(self): required = { "frontend/index.html": [ - "lp-first-orders-proof", "homepage_starter_offers_click", "homepage_sample_deliverables_click", + "homepage_proof_packs_click", "What a strong application says", "job_application_cover_focus", ], "frontend/starter-offers.html": [ - "Human verification for AI work before you trust it.", + 'data-starter-simplified="true"', + "Start small when the work needs proof.", "starter_offer_draft_click", - "Choose by the risk you need checked", - "starter_problem_route_click", "AI Output Verification", "Automation QA Sprint", "Real-World Check", ], "frontend/pricing.html": [ - "Start with proof, then scale.", + 'data-pricing-order="fee-first"', + "Prefer a fixed starting point?", "pricing_proof_first_cta_click", "lead_research", ], @@ -1744,12 +1752,11 @@ def test_first_orders_conversion_infrastructure_is_discoverable(self): def test_growth_activation_pages_and_homepage_proof_are_discoverable(self): required = { "frontend/index.html": [ - "lp-first-orders-proof", "homepage_starter_offers_click", "homepage_sample_deliverables_click", "homepage_high_intent_route_click", - "Trust before payment", - "For workers and agents", + "Clear scope, visible evidence, buyer approval.", + "A clear route for workers and agents.", ], "frontend/post-a-small-task.html": [ "Humans who verify what your AI produces", @@ -2142,13 +2149,17 @@ def test_sitemap_noindex_guard_recognizes_attribute_order_and_directive_tokens(s def test_sitemapped_html_pages_use_single_canonical_public_nav(self): expected_labels = [ "GoHireHumans", - "Starter QA", "Marketplace", - "For Workers", + "Find Work", "For Agents", "Pricing", "Trust", "Sign in", + "Post a task", + ] + expected_mobile_labels = [ + "Marketplace", "Find Work", "For Agents", "Pricing", "Trust", + "Starter Offers", "Use Cases", "FAQ", "Sign in", "Post a task", ] failures = [] for rel in self._sitemapped_html_pages(): @@ -2172,6 +2183,10 @@ def test_sitemapped_html_pages_use_single_canonical_public_nav(self): missing.append("first nav uses canonical lp-nav + aria label") if labels[:8] != expected_labels: missing.append(f"top nav labels {labels[:8]!r}") + mobile = re.search(r'
', text, flags=re.S | re.I) + mobile_labels = self._nav_labels(mobile.group(1) if mobile else "") + if mobile_labels != expected_mobile_labels: + missing.append(f"mobile nav labels {mobile_labels!r}") if '