Uh oh!
There was an error while loading. Please reload this page.
ci(release): the human act is the environment approval, not a typed version (ADR-0125) - #10150
Merged
Conversation
…ersion (ADR-0125) Maintainer ruling, 2026-08-20: 「合并 changeset 后,发版本前只需要有人工批准就可以, 没必要现在这样一定需要我去手工输入版本号。」 The 2026-08-07 ruling 「版本发布必须是人工的」 is untouched and still binding. What changes is which human act carries it: before maintainer opens Actions -> Run workflow and TYPES the exact version after merging the Version Packages PR queues a deployment; the maintainer APPROVES the `release` environment, and only then does anything run Typing the version was doing two jobs and only one of them well. Proving a human was there was its point -- but the `workflow_dispatch` EVENT is what did that, not the typing. The cross-check against packages/cli/package.json is nearly vacuous: changeset publish ships what package.json says, so a wrong string never publishes the wrong version, it aborts the run. #10146 showed the cost of asking for the string at all -- that run's log said 17.5.0, which is @object-ui/console@17.5.0, the vendored objectui build, while the repo was publishing 17.1.0. D1 `publish` now runs on push to main, gated on ONE predicate computed by the existing hardened audit: main's @objectstack/cli version is absent from npm. True only just after a version PR merges, false on the other ~18 daily landings, so no ordinary merge queues a deployment. D2 `environment: release` holds the WHOLE job -- no checkout, no build, no publish -- and the job's name is computed from the audited version, so the approval screen reads "Publish 17.1.0 to npm (awaiting approval)". The maintainer confirms a version they are SHOWN, read from the object database at github.sha, instead of one they recall. D4 workflow_dispatch survives as the repair lane with no version input, plus a dispatch-only `force` for finishing a partial publish -- the one case D1's predicate cannot see. Same environment gate. D5 Workflow-level concurrency is replaced by per-job groups. The old group was keyed on github.event_name and its comment said the point was that the lanes can never displace each other; that key stops separating anything once both lanes are pushes. Worse, a job waiting on approval holds its run in progress, so under one group an hour spent deciding would evict the intervening pushes and stop #4935 regenerating. The guard keeps R2's tripwire -- version read from the object database at github.sha, refuse if the workspace disagrees -- and adds one: refuse if the tree has moved off the version the approval was given against. WHAT THIS COSTS, IN ONE LINE: the barrier moves from a property of the event (a push cannot become a dispatch) to a repo SETTING (required reviewers on the `release` environment) that no file here can assert. The maintainer confirmed it is configured on 2026-08-20; ADR-0125 D3 is conditional on that staying true, and both the workflow header and Prime Directive #15 now say so in the places a reader will actually be standing. AGENTS.md #15 gains the new reserved act -- never approve a pending `release` deployment -- and records that merging the Version Packages PR is no longer adjacent to the release but IS its trigger. Governed surface (docs/adr/**, AGENTS.md): pushed for the maintainer to merge by hand. No auto-merge armed. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SRPcZzc7rfBQsNxJw3BSRh
This was referenced Aug 20, 2026
…ratchet check-skill-line-ratchet went red in Lint & Repo Gates: AGENTS.md was 965 lines against a ceiling of 961, which main already sits on with zero headroom. The amendment had split one paragraph into three and spent four lines on the split alone. Raising the ceiling is not the remedy — the gate says that takes a maintainer ruling quoted in the PR, and there is none for this; the prose was simply too long. Refolded into one paragraph, net zero lines, with every load-bearing point kept: the barrier has moved from the workflow_dispatch EVENT to a repo setting no file here can assert, an unprotected environment mimics an approved one perfectly in the run log, and merging `chore: version packages` now queues a deployment in the maintainer's name rather than moving a version number. The new reserved act stays where it belongs — inline in the list of things a seat may not do, costing no line at all: ⛔ approving a pending `release` environment deployment, with the note that a deployment waiting for hours is the system working, not a state to clear. Gates re-run locally, all green: adr-links, adr-anchors, doc-authoring, doc-anchors, role-word, quick-reference-counts, skill-frame-sync, skill-frame-freshness, workflow-status-functions, required-contexts, pm/governed-prose, pm/skill-line-ratchet, pm/skill-id-lint. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SRPcZzc7rfBQsNxJw3BSRh
Uh oh!
There was an error while loading. Please reload this page.
os-steve pushed a commit
that referenced
this pull request
Aug 24, 2026
…er in PD #15 Two sentences, two governed files, nothing else. 1. `docs/adr/0125-...md` line 3 declared itself "Proposed (2026-08-20) — awaiting the maintainer's hand-merge" while being the authority the publish lane cites eleven times. The merge it says it awaits is, by its own text, the acceptance act; that merge landed as 81d1fa1 (#10150) on 2026-08-20. The date is read from that merge, not from today. 2. `AGENTS.md` Prime Directive #15 said the Version Packages PR "is regenerated on every push to `main`". The `version-pr` job left the push trigger; it now runs on a six-hourly `schedule` or an on-demand `refresh_version_pr` dispatch. `release.yml`'s `push: branches: [main]` trigger drives the publish lane instead, so the sentence now says which trigger it means. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ahemw8RcTgqtxrj15PEZx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
⛔ Governed surface —
docs/adr/**+AGENTS.md. Do not queue, do not arm auto-merge. This is yours to merge by hand (Prime Directive #14). No auto-merge is armed and none will be.Maintainer ruling, 2026-08-20:
What changes
The 2026-08-07 ruling 「版本发布必须是人工的」 is untouched and still binding. What changes is which human act carries it.
releaseenvironmentworkflow_dispatchevent — no push can synthesise itThat last row is the whole risk, and it is why D3 below exists.
Why the typed version was not the thing protecting you
Typing
17.1.0did two jobs:workflow_dispatchevent is what did that, not the typing.changeset publishships whatpackage.jsonsays, so a wrong string never publishes the wrong version, it aborts the run. It catches exactly one thing — you believing main carries a version it doesn't — and showing you the version covers that strictly better than asking you to recite it.#10146 is the cost of asking for the string at all: that run's log said 17.5.0, which is
@object-ui/console@17.5.0, the vendored objectui build, while this repo was publishing 17.1.0. A maintainer retyping a number read off a release log had a live chance of stopping their own release.The decisions (ADR-0125)
publishruns on push to main, gated on one predicate computed by the existing hardened audit: main's@objectstack/cliversion is absent from npm. True only just after a version PR merges; false on the other ~18 daily landings, so no ordinary merge queues a deployment. The version is read from the object database atgithub.shawith release workflow: publish pushes tags + npm but its version commit never reaches main — twice now (rc.3 c6a52d3, rc.4 a10cbc77); landing the commit must be part of the publish lane #6170's tripwire, never off disk.environment: releaseholds the whole job — no checkout, no build, no publish — and the job'sname:is computed from the audited version, so the approval screen reads “Publish 17.2.0 to npm (awaiting approval)”. You confirm a version you are shown.releaseenvironment loses its required reviewers. An unprotected environment passes automatically and silently and is indistinguishable in the log from an approved one. Under a push trigger that isn't a degraded gate, it's no gate — rc.3/rc.4 by design. You confirmed the reviewers are configured; the ADR, the workflow header and Prime Directive Add missing Field.phone() helper and factory methods for Action/Dashboard/Report #15 all now say the decision is conditional on that, and that no file here can check it.workflow_dispatchsurvives as the repair lane: no version input, plus a dispatch-onlyforcefor finishing a partial publish (the one case D1's predicate can't see — canary on npm, stragglers not). Same environment gate.github.event_nameand its comment said the point was that the lanes can never displace each other; that key stops separating anything once both lanes are pushes. Worse: a job waiting on approval holds its run in progress, so under one shared group an hour spent deciding would evict the intervening main pushes and stop chore: version packages (rc) #4935 regenerating.The guard keeps R2's tripwire and adds one: refuse if the tree has moved off the version the approval was given against.
AGENTS.md #15
Gains the new reserved act — ⛔ never approve a pending
releasedeployment — and records that merging the Version Packages PR is no longer adjacent to the release but is its trigger, which makes it the single most load-bearing prohibition in that directive.Verification
Repo gates run locally, all green:
check:adr-links,check:adr-anchors,check:doc-authoring,check:doc-anchors,check:required-contexts,check:workflow-status-functions,scripts/pm/check-governed-prose.mjs.check:workflow-status-functionscaught a real defect on the first pass: thepublishjob'sif:readneeds.*.outputs.*without a status function. Fixed assuccess() && ( … )— the parentheses are load-bearing, since&&binds tighter than||andsuccess() && A || Bwould have let theforcebranch publish on top of a failed audit, which is the job that computes the version the name, guard and tag all read.Job matrix after the change:
version-prevent == pushrelease-version-pr-<ref>release-integrityrelease-integrity-<ref>publishsuccess() && (pending || forced dispatch)release-publish-<ref>docker!cancelled() && (…published)One residual race is documented in the file rather than hidden: if the version-PR landing's audit job is itself pending behind an earlier one and a third push arrives, GitHub cancels it and the release quietly doesn't queue. Narrow, visible (no approval request arrives), and recoverable with no special handling — the version is still absent from npm, so the dispatch repair lane re-audits and queues the same deployment.
Landing this is a no-op, and that is the point
17.1.0 already shipped — run 32362556463 succeeded at 11:26 on
2d3860d, and npm now carrieslatest: 17.1.0with@objectstack/cli,@objectstack/spec,@objectstack/core,@objectstack/accountand@objectstack/consoleall present, plus the full tag set on the remote. (That run is also the production proof of #10148's fix: same lane, same pnpm pin, the publish it previously died before reaching.)So merging this PR queues nothing: main carries 17.1.0, npm has it, D1's predicate is false, and
publishis skipped on the landing push exactly as it will be on every ordinary merge. The new path first does anything real on the next Version Packages PR — which is the gentlest possible way to find out whether it behaves, since a wrong predicate shows up as no deployment queued rather than as an unwanted publish.What is verified here is the YAML's shape, every gate the repo runs against it, and the predicate's arithmetic. What cannot be verified from here is the end-to-end push→approve path; that is observed on the next release.