Skip to content

chore(flagship): retire pano-base-feed flag — rip gate + dead fallback + IaC decl (ADR 0136)#3705

Merged
usirin merged 1 commit into
mainfrom
umut/3663-retire-pano-base-feed-flag
Jul 20, 2026
Merged

chore(flagship): retire pano-base-feed flag — rip gate + dead fallback + IaC decl (ADR 0136)#3705
usirin merged 1 commit into
mainfrom
umut/3663-retire-pano-base-feed-flag

Conversation

@usirin

@usirin usirin commented Jul 20, 2026

Copy link
Copy Markdown
Member

Retires the pano-base-feed flag (base-feed / viewer-overlay split, #2322, epic #2316 leg B), which graduated to serves-on@100% in prod. Per ADR 0136 (machine nominates, human confirms — confirmed at Gate 1) the gate + dead flag-off fallback are ripped, keeping only the served-on path byte-for-byte.

Consumers de-gated (live path only)

  • worker/features/pano/base-feed-route.tsGET /fate/pano/feed serves unconditionally (drop the 404-when-off gate + anonymous-flags-context read).
  • worker/features/pano/sources.tspostOverlaySource reads the viewer overlay whenever a viewer is present; the anon → inert-scalars degrade stays.
  • src/pages/PanoFeed.tsx — the sort feed always composes the identity-guarded overlay (drop the useFlag read); the saved/authed path keeps compose unset.

IaC declaration deleted

  • flagship/resources.tsPANO_BASE_FEED_FLAG config + panoBaseFeedFlag factory + import/re-export.
  • alchemy.run.ts — import + stack wiring.
  • src/flags/keys.tsPANO_BASE_FEED const + DECLARED_FLAGS entry.

Tests

  • Deleted the flag's own pano-base-feed.invariant.test.ts.
  • Kept the base-feed integration behavior tests, de-flagged (the base feed is now the source of truth); dropped the dark-when-off 404 assertion.
  • Reworded docblocks/mirrors that cited the retired flag so the grep stays clean under apps/web.

Grep-clean under apps/web (PANO_BASE_FEED / pano-base-feed). pnpm typecheck + biome clean; flagship + pano unit suites green (373 + 64 tests).

Closes #3663

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown

🚀 Preview deployed

  • web — Stage pr-3705 torn down.

@usirin

usirin commented Jul 20, 2026

Copy link
Copy Markdown
Member Author

review-code: PASS @ c55359d — merge-ready

Fresh re-review of PR #3705 against #3663's acceptance criteria at the post-rebase head. The prior verdicts on this PR were bound to fa4636e1 and do not count under ADR 0058; this verdict re-verifies every criterion from scratch at the current head, fetched into a per-run ref + isolated throwaway worktree (never a checkout). Non-blocking (§CP): the live CONTROL_PLANE_RE from origin/main intersects this diff emptyapps/web/** only.

Reviewed-head: @ c55359d

Union-of-removals — clean, and re-checked against the NEW main tip

This is the load-bearing re-check after the rebase. Main advanced to 218266a9 (the phoenix-mod-queue retirement, #3706), which lands after this branch's merge-base c55af6d9 and touches three of the same files: alchemy.run.ts, src/flags/keys.ts, flagship/resources.ts. That overlap is exactly the revival surface, so I simulated the merge rather than trusting the three-dot diff:

  • git merge-tree --write-tree 218266a9 <head> → tree 477cd301, no conflict.
  • Grepped all three overlapping files in the merged tree for every already-retired key — pano-optimistic-submit, pano-optimistic-comment-add, pano-optimistic-comment-delete, pano-optimistic-post-delete, pano-feed-edge-cache, phoenix-mod-queue: zero hits. phoenix-mod-queue stays retired through the merge; nothing already-removed is re-added.
  • Zero added lines in the diff mention any already-retired flag.
  • Branch is 1 ahead / 1 behind, mergeable: MERGEABLE, not dirty. The diff vs main's tip is exactly the pano-base-feed retirement — 12 files, −148 net.

Acceptance criteria (#3663 + ADR 0136)

  • [PASS] Gate + dead flag-off fallback removed across all three consumers; served-on behavior preserved.

    • worker/features/pano/base-feed-route.ts — the Flags read and the if (!on) return 404 branch are gone; handleBaseFeed runs Cloudflare.RequestPano read unconditionally. All five now-dead imports removed (PANO_BASE_FEED, Flags, anonymousFlagsContext, FlagsContext, makeRequestFlagsContext); HttpServerResponse correctly retained (still used downstream).
    • worker/features/pano/sources.tsthe anon→inert degrade is preserved: the guard narrows if (!on || !viewerId)if (!viewerId), so an anonymous viewer still gets ids.map(id => ({id, myVote: null, isSaved: null})). Flags/provideRequestFlags correctly retained — still consumed by commentSource's PHOENIX_PANO_STAMP_WAVE read.
    • src/pages/PanoFeed.tsxuseFlag(PANO_BASE_FEED, false) removed and compose un-threaded through FeedContent/FeedRows; the sort feed now always composes (line 215). useFlag correctly retained for the unrelated MEMBER_MUTE/PHOENIX_NAV_IA reads.
    • Served-on collapse verified at every call site repo-wide, not just the changed one: PanoFeed.tsx:215 (sort feed) sets compose; PanoFeed.tsx:311 (SavedContent, authed) and SearchPage.tsx:112 leave it unset — precisely the old flag-ON shape.
    • Live behavioral proof on the pr-3705 preview: GET /fate/pano/feed?sort=new&first=3200 with a real connection body, cache-control: public, s-maxage=30, cache-tag: pano-feed, and no set-cookie. Under the retired flag's default-off this route 404'd, so this observes the un-gated serve, the preserved edge-cache contract, and the no-session-validation property end-to-end.
  • [PASS] IaC declaration deleted; zero surviving references.

    • src/flags/keys.ts — the PANO_BASE_FEED const and its DECLARED_FLAGS entry both removed (no orphan row).
    • flagship/resources.tsPANO_BASE_FEED_FLAG config, the panoBaseFeedFlag factory, the import and the re-export all removed.
    • alchemy.run.ts — import + yield* panoBaseFeedFlag(flagship.appId) wiring removed.
    • Grep over the whole head tree for pano-base-feed|PANO_BASE_FEED|panoBaseFeedFlag|panoBaseFeed: only the two packages/pipeline-cli/src/tools/reachability-guard/ whole-word matcher fixtures remain. Confirmed pre-existing and untouched by this PR (neither file is in the changed set) — out of scope, and they assert the matcher, not a live flag.
  • [PASS] Invariant test deleted; integration test de-flagged; behavior coverage preserved.

    • flagship/pano-base-feed.invariant.test.ts deleted in full (26 lines).
    • tests/integration/pano-base-feed.test.tsBASE_FEED_ON_COOKIE and every call-site cookie arg dropped; the 404s when the flag is off assertion removed; the describe retitled to the real invariant. The pagination/cursor, anon-vs-authed byte-identity, myVote stamp, and Cache-Tag tests all survive.
    • PanoPostCard.compose.test.tsx retained — still covers the pending-neutral / resolved-scalars / anon-null branches, which are now the unconditional path.
    • No dangling references: two sibling invariant docblocks (mecmua-public-read, sozluk-stamp-wave) and three resources.ts "mirrors" pointers were repointed off the deleted file.
  • [PASS] typecheck + lint green at this head.

    • pnpm typecheck --force33/33 tasks successful, 0 cached (16.5s). Forced deliberately: an unforced run is replay-prone from sibling worktrees, so this is a real compile of c55359dd, not a FULL TURBO cache hit.
    • pnpm lintexit 0, 1844 files, 0 errors. The 9 warnings are all pre-existing in files this PR does not touch (e.g. worker/features/mute/read-mask.ts).
    • Targeted unit run in the review worktree: worker/features/flagship35 files / 167 tests passed, 0 failed (the deleted invariant file leaves the suite whole).

Fan-out (silent-failure · type-design · test-gap) — no in-scope findings. The change deletes branches and adds no swallow; it is strictly type-narrowing (the retired key can no longer be referenced, so a stale consumer is a compile error rather than a silent false); and the one behavior it changes (un-gated serve) is covered by both the de-flagged integration suite and the live 200 above.

CI at this head — the two red checks are INFRA, not code

  • integration testssuccess. Notably this clears the chore(flags): retire phoenix-authorship-loop flag #3707 CI-vs-prod flag-default divergence risk: the retired flag's IaC defaultVariation was "off", so CI stages provisioned it off while prod served it on, which is exactly the shape that flips behavior on retirement. It did not materialize here, because the suite drove the flag ON via the phoenix_flag_overrides dev-override cookie and this PR removes that override alongside the gate — CI and the new unconditional serve agree. No fixture update needed.
  • Analyze (javascript-typescript) — failure at the Initialize CodeQL step (toolchain bootstrap), and a parallel duplicate run of the same analysis at this identical SHA succeeded. Not a code finding.
  • produce run-evidence bundle — failure at the Install crabbox (pinned) step: the known 503-fetching-a-pinned-asset flake.
  • ci-required is success, as are lint / format / typecheck, unit + client tests, e2e (reads + authed + flows, blocking), deploy (web), detect deploy relevance, and every repo guard.

Both failures are rerunnable infra with no code cause — flagged for the shipper to rerun, not a FAIL.

All acceptance criteria pass at c55359dd. review-code does not mergeship-it is the authorized merge step.

@usirin

usirin commented Jul 20, 2026

Copy link
Copy Markdown
Member Author

review-design: PASS @ c55359d — merge-ready

Fresh re-render of PR #3705 over the pr-3705 preview deploy at the post-rebase head, judged against the ADR-0162 four-pillars design law. The prior design verdict was bound to fa4636e1 and does not count under ADR 0058. Judged the local captured bytes; the hosted upload degraded (see Evidence) and is evidence-only, so the verdict stands on what I saw locally. Non-blocking (§CP): live CONTROL_PLANE_RE ∩ diff is empty.

Reviewed-head: @ c55359d

Dispatched because pipeline-cli class-probe classify --namespaces names review-design for this diff (live UI_RE match on apps/web/src/pages/PanoFeed.tsx, components/pano/PanoPostCard.tsx, flags/keys.ts), resolved deterministically rather than by eye.

Preview freshness — verified, not assumed. The sticky preview comment cites c770e86, which is not the head. I confirmed refs/pull/3705/merge == c770e86c = Merge c55359dd into c55af6d9, i.e. the preview is the head merged onto base, and the comment's update timestamp (00:37:30) falls inside the head deploy job (00:36:19–00:37:34). The preview does reflect c55359dd.

Surface captured: /pano (the pano sort feed — the one route this diff can move).

This PR carries a real rendered delta on the preview. The retired flag's IaC defaultVariation was "off", so preview/dev stages previously rendered the non-compose path; retiring to the served-on shape means the preview now renders the compose path for the first time. That makes the capture load-bearing rather than ceremonial.

Hard-FAIL prohibitions (ADR 0162)

  • [PASS] Render exception (review-design's single render misses intermittent mount/init races (crashes on some ticks) #2594)pageErrors: [], no uncaught exception during the /pano render. The load-bearing check for this PR: compose is now unconditionally true on the sort feed, so the panoFeedOverlay identity guard is always in the render path instead of dark behind a flag. It mounted and painted clean for an anonymous viewer across all 20 rows, with the viewer scalars correctly held neutral (outline vote arrow, unstyled kaydet) — exactly the composed-anon behavior the flag's ON branch delivered, and the visual counterpart to the if (!viewerId) inert degrade preserved in sources.ts.
  • [PASS] Faint-for-meaning — row metadata (e2e tester · 18 dakika önce · 1 yorum · kaydet) and the (example.com) host read at muted-gray, legible on the white surface; nothing meaning-carrying sits on --text-faint. The diff changes no token, colour, or text style.
  • [PASS] Off-grid spacing/type — the row rhythm, rank gutter, and subnav sit on a consistent lattice. The diff removes a prop and a flag read; it touches no spacing or type step.
  • [PASS] Colour-alone meaning — the active sıcak tab carries an underline and a colour shift (two channels); the tartışma/meta tags are text-labelled, not hue-only.
  • [N/A] Missing focus ring — no interactive control is added, removed, or restyled by this diff.
  • [N/A] Void empty state — the captured feed renders 20 seeded rows; no empty-state code path is changed.
  • [N/A] Sub-36px tap target — no interactive control's hit area is changed.
  • [N/A] Golden-deviation (Decision (founder-fork): where does the perception gate live — per-PR, per-epic composition pass, or both? #2945)packages/design-capture/golden-pointer.json declares no blessed surfaces, so the changed surface has no golden baseline; this class does not apply.

Advisory (non-blocking)

  • The capture covers the anonymous /pano render. The authed compose branch (overlay pending → resolved under a matching identity) is not reachable from an unauthenticated capture; it is covered deterministically by PanoPostCard.compose.test.tsx (pending-neutral, resolved-scalars, anon-null branches) and by the green blocking e2e (reads + authed + flows) check at this head. Noting the capture bound rather than claiming visual coverage I don't have.
  • The screenshot upload to uploads.github.com/user-attachments/assets returned HTTP 400 (Invalid name for request), so hostedUrl is null. Per ADR 0165 this is a tolerated evidence-only degradation and does not affect the verdict.

Evidence

  • Preview: https://phoenix-phoenix-pr-3705-fbbb677iuch3u7oy.kampusinfra.workers.dev (stage pr-3705; deploy check green at this head; merge-ref freshness proven above)
  • /pano@desktop — captured locally and judged multimodally; pageErrors: []. Hosted embed unavailable (upload 400, above).
  • GET /fate/pano/feed?sort=new&first=3 → 200, cache-tag: pano-feed, no set-cookie — the un-gated serve behind the rendered feed.

No objective prohibition is violated, no surface threw during render, and no blessed golden applies. Design-merge-ready at c55359dd. review-design does not mergeship-it is the authorized merge step.

@usirin
usirin added this pull request to the merge queue Jul 20, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Jul 20, 2026
…k + IaC decl (ADR 0136)

The base-feed / viewer-overlay split flag (#2322, epic #2316 leg B) graduated to
serves-on@100%, so per ADR 0136 (machine nominates, human confirms — confirmed at
Gate 1) retire it. Rip the flag gate + the now-dead flag-off fallback across every
leg-B surface, keeping only the served-on path byte-for-byte:

- base-feed-route: the GET /fate/pano/feed route serves unconditionally (drop the
  404-when-off gate + the anonymous-flags-context read).
- postOverlaySource: reads the viewer overlay whenever a viewer is present (the
  anon → inert-scalars degrade stays); drop the flag guard.
- PanoFeed: the sort feed always composes the identity-guarded overlay (drop the
  useFlag read); the saved/authed path keeps compose unset.

Delete the IaC declaration + factory in flagship/resources.ts, the stack wiring in
alchemy.run.ts, and the key constant + DECLARED_FLAGS entry in flags/keys.ts. Remove
the flag's own invariant test; the base-feed integration behavior tests stay (de-
flagged — the base feed is now the source of truth). Reword docblocks/mirrors that
cited the retired flag so the grep stays clean under apps/web.

Closes #3663

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@usirin
usirin force-pushed the umut/3663-retire-pano-base-feed-flag branch from fa4636e to c55359d Compare July 20, 2026 00:35
@usirin
usirin added this pull request to the merge queue Jul 20, 2026
Merged via the queue into main with commit 40f4c23 Jul 20, 2026
41 of 43 checks passed
@usirin
usirin deleted the umut/3663-retire-pano-base-feed-flag branch July 20, 2026 01:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Retire pano-base-feed flag — rip FlagGate + fallback + IaC declaration (ADR 0136)

1 participant