Repository source: docs/plans/deploy_header_prompt/deploy_header_prompt_step3_development_plan.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 3: Development Plan — Deploy prompt in the page header
Completion Contract
Normal entry: any zenmemes.com page, with
FORUM_DEPLOY_HEADER_PROMPT_ENABLEDon, in a browser that has deployed from Tools → Deploy.End-to-end outcome: Deploy now appears in the header when the checkout is behind; tapping it deploys and reloads on the new commit.
Recovery: a slow check gives up silently; a refused check clears the browser flag; a failed or lost deploy shows the message and links to Tools → Deploy.
Deployment check: the maintainer deploys this branch, then confirms the flow on his iPhone with a later commit waiting.
Release condition: the maintainer accepts the iPhone check in production. Until then, Step 4 stays blocked on acceptance.
Key Risks
A fetch on every visit. Impact: GitHub load and a held deploy lock. Early validation: Stage 1 tests show a second check inside ten minutes does not fetch. Mitigation: the server reads the time of the last fetch from
FETCH_HEAD; no new stored state.High risk: header crowding on phones. Impact: Deploy now pushes out New post or the account menu. Early validation: Stage 2 checks all five themes at phone width before Stage 3 adds behavior. Mitigation: reuse
.new-post-buttonsizing; hide the label on narrow screens if needed.Duplicate deploy taps. Impact: two deploys race. Mitigation: the button disables while a deploy runs; the existing deploy lock refuses a second one.
Concepts touched
Application update: changed. Update
docs/concepts/application_update.mdin Stage 4.Viewer session: used unchanged.
Stage 1 — Flag and check interval
Estimate: 0.06
Goal: add the site flag and let a check reuse a recent fetch.
Dependencies: none.
Expected changes: add
FORUM_DEPLOY_HEADER_PROMPT_ENABLEDtoFeatureFlagRegistry.php(off, site mutable, EXPERIENCE). GiveAppUpdate::check()an optional maximum age in seconds; a fetch newer than that returns the current state without fetching.DeployController.phpaccepts amax_agefield oncheck, clamped to at least 60 seconds. Tools → Deploy keeps its forced check.Verification approach:
./v3 test --smoke AppUpdateTest DeployPageTestplus the feature flag registry test. New tests: a recent fetch skips the fetch; an old one fetches; the page button still forces a fetch.Estimate details: driver: one new branch in
check(); uncertainty: low.Risks or open questions: none beyond Key Risks.
Canonical components/API contracts touched:
AppUpdate.phpcheck(),DeployController.phpsubmit(),FeatureFlagRegistry.php.
Stage 2 — Remember the operator and reveal the button
Estimate: 0.07
Goal: after a deploy, later visits check and reveal a hidden Deploy now button.
Dependencies: Stage 1.
Expected changes:
deploy_control.jsstores a browser flag after adeployedor completed queued deploy.app_header.phprenders a hidden Deploy now button beside New post only when the flag is on, so HTML stays the same for every viewer. A newdeploy_prompt.js, loaded only when the flag is on, waits about three seconds after load. It then sendscheckwithmax_age600 and aborts after fifteen seconds. It shows the button whenbehindis above zero; on 403 it clears the browser flag.TemplateRenderer.phppasses the script path, as it does forinbox_unread.js.Verification approach: new
tests/browser/deploy_prompt.mjscovers: flag off makes no request; no browser flag makes no request; a behind checkout reveals the button; a 403 clears the flag. Check all five themes at phone and desktop width.Estimate details: driver: header layout across themes; uncertainty: Engagify and Gloss header spacing.
Risks or open questions:
Impact: crowded phone header.
Early warning / validation: screenshots in each theme before Stage 3.
Mitigation: reuse
.new-post-button; drop the label below the phone breakpoint.
Canonical components/API contracts touched:
deploy_control.js,app_header.php,.new-post-buttoninsite.css,TemplateRenderer.php; pattern fromversion_check.js. A new script is needed becauseversion_check.jsruns for every visitor.
Stage 3 — Deploy from the header
Estimate: 0.05
Goal: the header button deploys and recovers from failure.
Dependencies: Stage 2.
Expected changes:
deploy_prompt.jspostsnowon tap, disables the button and shows Deploying…. Ondeployedit reloads. On a refusal or failure it shows the message with a link to Tools → Deploy. On a dropped connection or 502–504 it links to Tools → Deploy, which follows a lost reply.Verification approach: extend
deploy_prompt.mjswith stubbed replies for success, failure, 503 and a dropped connection. Confirm one attempt-log line per header deploy inDeployPageTest.Estimate details: driver: four reply cases; uncertainty: low, since
deploy_control.jsalready handles them.Risks or open questions: duplicate taps, covered in Key Risks.
Canonical components/API contracts touched:
/tools/deploy/nowaction and its JSON reply.
Stage 4 — Docs and production replay
Estimate: 0.04
Goal: record the feature and verify it on the maintainer's iPhone.
Dependencies: Stage 3.
Expected changes: update
docs/concepts/application_update.mdanddocs/runbooks/production_deploy.md. Final diff review. Open the PR with a checkout snippet.Verification approach: smoke set plus changed tests; full suite not run. Production replay is pending the maintainer: turn the flag on, deploy once from Tools → Deploy, merge a later commit, open the board on the iPhone, tap Deploy now. Also replay a dropped connection by locking the phone mid-deploy.
Estimate details: driver: production replay; uncertainty: depends on the maintainer's time.
Risks or open questions: a desktop pass does not satisfy the iPhone condition; the item stays blocked until he confirms.
Canonical components/API contracts touched: concept and runbook docs only.
Study snapshot
| Field | Value |
|---|---|
| Study / candidate | railspike-effort / v3/deploy_header_prompt |
| Checkpoint | Step 3 handoff |
| UTC date | 2026-11-10 |
| Rater | Claude (single rater) |
| Score | 0.24 projected total |
| Scope version | v2, unchanged; starting revision cdec8702 |
| Reference version | R1 |
| Exposure | Step 2 score 0.22 seen |
| Reason for change | Browser test file and recovery cases added; stored timestamp no longer needed (FETCH_HEAD), which offsets part of it |
Ledger update pending: indexed with the step 4 closeout.
Status: Approved Step 3 on 2026-11-10.
— Claude · 2026-11-10 UTC · Model: claude-opus-5-5