Repository source: docs/plans/deploy_pending_commits/deploy_pending_commits_one_shot.md
Deploy pending commits — one-shot
Cycle: v3/deploy_pending_commits; Mode: one-shot; Record/index: this file, plans index
Debug mode: enabled (log; project .railspike_debug_on marker present)
Effort study: railspike-effort / v3/deploy_pending_commits, standing enrollment under protocol P2, reference R1 (ledger)
Extended estimates: enabled (new-cycle default; no real alternatives or work units, so no unit estimates)
Cycle estimate: 0.20
Status: technically complete on feat/deploy-pending-commits; pushed for review, not merged or deployed. Next action: maintainer review, merge and deployment.
Request and authorization
Feedback item: PF-20261104-F49, "Deploy shows commits and queue delay".
Original request, thread thread-20261103060933-51ef9554 (ilyag, 2026-11-03): "this page should give me all the info it can about what the update will do, which commits will be added. also, the "Deploy" button should communicate that it's done through the queue with delay"
Authorization, 2026-11-05: the maintainer pasted the thread and asked "let's work on this one, please. is it a one-shot or full?". The agent recommended a one-shot covering an incoming-commit list with a diffstat, clearer queued-deploy wording, and the sibling item PF-20261104-F41 as a follow-up. The maintainer replied "go ahead please". Under thread to pull request this authorizes one-shot execution of that bounded outcome, pushing the branch, opening the PR and replying in the thread.
Exclusions: merge, deployment, any change to how a deploy runs (fetch, fast-forward, lock, queue order), and PF-20261104-F41 (making the page understandable for non-technical operators).
Outcome and work type
Application change. An operator opening Tools → Deploy after Check for updates sees, before pressing anything:
a one-line summary of the update (commit count, files changed, lines added and removed);
under it, the commits the update will add (short hash, subject, author, date), newest first, up to 20, with "and N more" beyond that; merge commits are left out, and the list is collapsed when it is longer than five;
a warning when the update changes a file the server keeps a local edit to, because that is the case where a deploy can stop with
local_changes_conflict;a queued button labelled "Queue deploy"; after pressing it the status line says it usually starts within a minute, then how long it has waited for the task worker.
./v3 deploy --status and --check print the same incoming commits and size as Key: value lines.
Approach and reuse decisions
State: extend
AppUpdate::inspect()(local-only, no network) withincoming_commits,incoming_more,incoming_statandincoming_local_overlap, computed only when the checkout is behindorigin/main. SamegitValue()/git()helpers, sameHEAD…origin/mainrange the behind count already uses. No new class.Page: extend
templates/pages/deploy.php's existingcodebase-factstable with an "Incoming" row; no new component or stylesheet beyond a small list rule intool-details.cssnext to.deploy-actions.Commit links: reuse
SourceRepository::fromProjectRoot()(theFORUM_SOURCE_REPOSITORY_URLsetting) for commit and compare links, linking only when the host isgithub.com, whose/commit/and/compare/a...bpaths are known; otherwise plain text.Queue wording: rename the button and reuse the existing status line and worker-quiet sentence; no new static help text (
AGENTS.mdUI copy: prefer removing text, explain only what the interface cannot show). The queued task'srequested_at(already returned bylatestByType()) gives the waiting time after a reload, passed to the script as a data attribute.CLI: extend
scripts/deploy.php's$printState.No new dependency, route, table, schema or format change.
Concepts touched
Application update: changed (scenario gains the incoming-commit preview and queued wording). No inventory additions.
Risks
Page cost:
inspect()runs on every page load and status poll. Mitigation: the extra Git calls (onelog, twodiff) run only when behind, are local, and are bounded by the 20-commit limit.Stale preview: the list reflects the last fetch, not GitHub now. Mitigation: it sits in the row that already shows "(checked …)", and Check for updates refreshes it.
Diverged checkout: when the server is also ahead, a deploy refuses anyway; the list shows
origin/main's side only (HEAD..origin/main), and the stat uses the merge base (HEAD...origin/main).
Verification and recovery plan
Tests:
AppUpdateTestfor incoming commits, the more-count beyond the limit, the stat and local-overlap;DeployPageTestfor the rendered row, links and button wording.Run the smoke set plus those tests; the full suite is not run.
Render
/tools/deploy/through the local server as an operator with a checkout that is behind, at phone and desktop widths, in the default theme and Word97/Chicago (table and buttons are theme-styled).Recovery: reverting the commit restores the previous page; no state is written.
Starting revision and branch: 058b2c6b069e023802871aa141f570a6407217a5 on main; branch feat/deploy-pending-commits.
Planning commit: 708d4b8e (confirmed with Git).
Study history
Intake, 2026-11-05T14:45Z, 0.20. Single exposed rater: Claude (claude-opus-5-5). Pristine intake missing:
AppUpdate,DeployController, the template, page script, CLI and tests were inspected before rating. Scope v1 as above. R1 hashes not re-verified this cycle. Compared with R1's duplicate preview suppression (0.10) and about-copy integration (0.20), and recent cycles source repository link (0.12) and Chicago post actions (0.15).Execution entry, 2026-11-05T14:47Z, 0.20 reaffirmed. Same scope v1; no new evidence between intake and entry.
Closeout, 2026-11-05. Projection 0.20 reaffirmed. Retrospective scope assessment 0.20, from the same single exposed rater, not an independent rating. Corrections: none. Additions: none from the requester; the merge filter and collapsed list are design decisions inside scope v1. Delivered scope v1 in full. Maintainer comfortable-fit judgment and acceptance pending. No active-work measurement was collected.
Dated execution history
2026-11-05: Record created before implementation; planning commit
708d4b8e. An earlier local version carried aFeedback:trailer without the item update; it was amended before anything else happened and never pushed.2026-11-05, implementation:
AppUpdate::incoming()behindinspect(),INCOMING_LIMIT = 20; Incoming row,Queue deploylabel anddata-deploy-queued-atindeploy.php; waiting time indeploy_control.js;commitLinkBase()inDeployController, wired fromSourceRepositoryinApplication;Incoming-*lines inscripts/deploy.php; runbook and concept entry updated.2026-11-05, decision after rendering: with 20 commits the phone layout pushed the buttons several screens down, and most entries in this repository's history are merges that repeat their branch's commits. The list now leaves out merges (
--no-merges, with "more" counted the same way) and sits in a<details>whose summary carries the count and size, open when it has five commits or fewer. The local-edit warning stays outside it. The Behind row still counts all commits, merges included, so the two numbers can differ.2026-11-05, decision: no static help line about the queue under the buttons. The label says it is queued; the status line says when it usually starts and then how long it has waited; the existing sentence for a quiet worker stays.
2026-11-05, scope note: the Incoming row shows to everyone who sees the page's Git facts (read-only viewers already see commit and branch), matching the existing table. No new exposure beyond commit subjects and author names that are on the public repository.
Outcome
Delivered: after Check for updates, an operator sees what a deploy would bring in (count, size, the commits with links to GitHub, and a warning when it touches a file the server keeps a local edit to) before choosing Queue deploy or Deploy now; a queued deploy shows how long it has waited. The CLI prints the same. Limitations: the preview reflects the last fetch, not GitHub now; production shows it only after deployment, and the first deploy that contains it still shows the old page until it lands. Follow-up: PF-20261104-F41, making the page understandable for non-technical operators.
Verification
Passed:
./v3 test --smoke DeployPageTest AppUpdateTest, 457 run, 457 passed. New tests: incoming commits and size after a check, nothing before a fetch, the 20-commit limit and more-count, merges left out, local-edit overlap, the rendered row with GitHub links and HTML escaping, plain hashes for a non-GitHub repository, and theQueue deploylabel on the operator page.Passed: rendered the operator page from a checkout behind this repository's
mainand served it with the real assets; Playwright with Chrome at 390 and 1280 px in light, dark, Word97, Chicago and Midnight: no horizontal overflow, Incoming row and Queue deploy visible. Screenshots checked for a long update (collapsed) and a two-commit update with a local-edit overlap (open).Passed: with stubbed POST responses, pressing Queue deploy shows "Queued for the task worker. It usually starts within a minute." and the first poll shows "Queued; waiting for the task worker… 0:05".
Passed: the CLI state printer against the same checkout prints
Incoming-Size,Incoming-CommitandIncoming-Changes-Local-Edit.Passed:
./v3 feedback check(0 problems; 21 pre-existing review notes) and./v3 feedback index;git diff --check.Not run: the full suite (maintainer runs it). Not verified: a real queued deploy picked up by cron on the server, an iPhone, and the page-load waiting time from a real queued task (covered only by reading
requested_at'sgmdate('c')format, whichDate.parseaccepts). Inapplicable: nav icons (no destination added or renamed), read-model and dependency checks (none changed;playwright-corewas installed from the existingpackage.jsonfor screenshots only).
Final review
Diff checked for duplication (one new private method in AppUpdate, no new component; list styles sit with the existing .deploy-actions rules), file scope (deploy page, its script and CSS, CLI, tests, runbook, concept entry, feedback item, this record, index and ledger) and documentation (runbook and concept entry describe the new row and label).
Commit, merge and publication status
Planning commit 708d4b8e; completion commit 29895d7d on feat/deploy-pending-commits, confirmed with Git; this reference update is a record-only handoff commit. Pushed to origin as PR #26; thread reply reply-20261105145656-10e36db1 posted as claude. Not merged, not deployed.
— Claude · 2026-11-05 UTC · Model: claude-opus-5-5