Repository source: docs/plans/deploy_header_prompt/deploy_header_prompt_step4_implementation_summary.md
Debug mode: enabled
Effort study: railspike-effort / v3/deploy_header_prompt (ledger, standing enrollment under protocol P2)
Extended estimates: enabled
Cycle estimate: 0.24
Step 4: Implementation Summary — Deploy prompt in the page header
Branch: feat/deploy-header-prompt. Planning commit: 886d81ee.
Step 4 entry projection: 0.24, reaffirmed unchanged from Step 3 (Claude, 2026-11-10 UTC, exposure: Step 3 score seen). Stage estimates below are copied from Step 3, not new ratings.
Stage 1 - Flag and check interval
Original estimate: 0.06 Retrospective stage work: 0.05
Changes:
FeatureFlagRegistry.php: newFORUM_DEPLOY_HEADER_PROMPT_ENABLED("Deploy prompt"), off by default, site mutable, EXPERIENCE group after Unread.AppUpdate.php:check()takes an optional maximum age. A fetch newer than that returns outcomechecked, reasonrecent, without contacting GitHub. The time comes fromFETCH_HEAD, so no new state is stored.DeployController.php:checkreads an optionalmax_age, clamped to 60–86400 seconds. Without it, Check for updates still always fetches.
Verification:
Passed:
./v3 test --smoke AppUpdateTest DeployPageTest FeatureFlagEvaluatorTest, 480/480.New tests: a recent fetch is reused; the forced check still fetches; an hour-old fetch is refreshed.
Not run: full suite (maintainer runs it).
Not covered: the
max_ageclamp through the HTTP endpoint; Stage 2's browser test exercises that path.
Notes:
Reuse: the existing
checkaction and lock; no new endpoint.Workspace note: the shared workspace dropped untracked plan files once before the planning commit. The plans were rewritten from their approved text; the Step 1 debug entry is restored in this commit with a revision note.
Estimate details: original scope unchanged; no revisions.
— Claude · 2026-11-10 UTC · Model: claude-opus-5-5
Stage 2 - Remember the operator and reveal the button
Original estimate: 0.07 Retrospective stage work: 0.09
Changes:
deploy_control.js: remembers this browser (forum_deploy_promptin local storage) after Deploy now endsdeployedorup_to_date, or a deploy started from the page finishesdeployed. Check for updates alone does not.New
deploy_prompt.js: in a remembered browser, waits three seconds, sendscheckwithmax_age600 and gives up after fifteen seconds. Reveals the button whenbehindis above zero. A 403 forgets the browser. Does nothing on Tools → Deploy.app_header.php: a hidden Deploy now button and status line by the site name, only with the flag on. Server HTML stays the same for every viewer.TemplateRenderer.phpandlayout.php: load the script only with the flag on and the app header.site.css:.deploy-promptstyles from shared theme variables. Phones keep it on the site name's row; wider screens put it under the name.tests/Support/app_layout_fixture.php: a fifth argument turns the flag on.
Verification:
Passed:
node tests/browser/deploy_prompt.mjs. It covers flag off (no script, no request), a never-deployed browser (no request), the three-second delay, the request body, behind and up-to-date replies, a 403 forgetting the browser, and the tab bar unchanged on phones.Passed: every theme (Word 97, Chicago, Midnight, Gloss, Gloss Dark, Engagify) at 390 and 1100 wide. The button is usable, inside the header, and the destinations stay on one row. Screenshots reviewed for all twelve.
Passed:
./v3 test --smoke AppNavigationPlacementTest NavIconSetsTest DeployPageTest FeatureFlagEvaluatorTest, 449/449.Passed: header regressions
inbox_unread.mjs,nav_slots.mjs,viewer_nav_prepaint.mjs;git diff --check.Not run: full suite.
Notes:
Placement refinement (in scope, under the Stage 2 crowding mitigation): the plan placed the button beside New post. On phones that slot is in the bottom tab bar, and on desktop it wrapped Tools to a second row in Chicago and Gloss. The button now sits by the site name instead and does not reuse
.new-post-button.On wider screens the destinations shift right by about 20px when the button appears in Chicago, Gloss and Engagify. This is a post-paint change for a real state change, which
AGENTS.mdallows.Navigation icons: not applicable; Deploy now is an action, not a navigation destination.
Engagify's parody popups cover its header in screenshots; the geometry checks still pass there.
Estimate details: original scope unchanged; the placement rework is new understanding, not changed scope.
— Claude · 2026-11-10 UTC · Model: claude-opus-5-5
Stage 3 - Deploy from the header
Original estimate: 0.05 Retrospective stage work: 0.04
Changes:
deploy_prompt.js: tapping Deploy now disables the button, shows Deploying… and sendsnowwith a 150-second limit, matching the server's own limit.Outcome
deployedorup_to_datereloads the page.A failed or refused deploy, a 403, a 502–504 reply or a dropped connection shows a message with an Open Deploy link and re-enables the button. A 403 also forgets the browser.
Verification:
Passed:
node tests/browser/deploy_prompt.mjs, extended with success (reload), failure, 503, 403 and a dropped connection. Each case checks the message, the link to/tools/deploy/, the re-enabled button and a singlenowrequest.Passed:
./v3 test --smoke DeployPageTestandgit diff --check.Attempt log: the header sends the same
nowrequest as Tools → Deploy, soDeployPageTestandAppUpdateTestalready cover one log line per deploy. No new PHP test was added.Not run: full suite.
Notes:
Duplicate taps: the button is disabled while a deploy runs, and the server's deploy lock refuses a second one.
A lost reply is not followed in the header; the link sends the operator to Tools → Deploy, which already does that.
Estimate details: original scope unchanged; no revisions.
— Claude · 2026-11-10 UTC · Model: claude-opus-5-5
Stage 4 - Docs and production replay
Original estimate: 0.04 Retrospective stage work: 0.04 (production replay pending)
Changes:
docs/runbooks/production_deploy.md: new "Deploy now in the header" subsection.docs/concepts/application_update.md: the header prompt, the check interval and the new source paths.docs/plans/effort_estimation_trial.md: ledger row and closeout line.
Verification:
Passed: concept source paths resolve;
git diff --check.Passed: final diff review of
cdec8702..HEAD. One new script, no new dependency, no schema change.playwright-corewas installed from the existingpackage.jsonfor the browser check only.Pending (maintainer, production, iPhone): turn the flag on, deploy once from Tools → Deploy, merge a later commit, open the board, wait for Deploy now and tap it. Then lock the phone mid-deploy and confirm Open Deploy leads to a page that follows the deploy. A desktop pass does not satisfy this.
Not run: full suite.
Notes:
Feedback: no matching item in
docs/feedback/items/, so noFeedback:trailer.
Estimate details: original scope unchanged.
Cycle closeout
Delivered scope: v2 (Option A plus the site feature flag). Stages 1–4 complete; commits listed in the PR.
Retrospective cycle assessment: 0.22, single exposed rater; not a measured actual. Stage 2's placement rework added work; Stage 3 needed less.
Maintainer comfortable-fit judgment: pending.
Release status: blocked on the production iPhone replay and acceptance.
— Claude · 2026-11-10 UTC · Model: claude-opus-5-5