Skip to content

Commit 66fcc5e

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-16570-initobjects-indexes-param
2 parents 09dec14 + 8b37a09 commit 66fcc5e

2 files changed

Lines changed: 119 additions & 22 deletions

File tree

AGENTS.md

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,12 @@ Even inside your own worktree, operate defensively:
461461
The issue number is what makes in-flight work *discoverable* — `git ls-remote --heads
462462
origin | grep issue-<n>` is a one-command pre-check, and the Duplicate Fix Guard
463463
workflow warns on fix PRs whose branch names no declared issue.
464+
465+
**Off `main` is literal — a stacked series, each PR branched off the one below, is NOT a supported form.** No
466+
tooling represents it: squash landing destroys the ancestry link, so every descendant rewinds behind what landed and
467+
pays a rebuild lap per landing; and a breaking changeset's ADR-0087 disposition is base-relative, so a stacked card's
468+
two bases demand contradictory markers. A multi-card change uses a **trunk branch**: correct the trunk's disposition
469+
to `registered` before it merges, and pay the rebase laps. ⛔ No gate or merge-policy change is made for it.
464470
3. **Never `git push --force` / `--force-with-lease`, and never push `main`.** A
465471
force-push can clobber a parallel agent's work; `main` is shared — land all via PR.
466472
4. **Verify the current branch before every commit/push**
@@ -1037,16 +1043,9 @@ registry? Add it to `OPEN_CAPABILITY_REGISTRIES` in the same PR that fixes it.
10371043
schema is `.strict()`. The changeset is one of fourteen surfaces a retirement touches — follow the
10381044
`spec-property-retirement` skill (`.claude/skills/`) rather than reconstructing the kit, and note the two routes
10391045
imply **opposite** liveness-ledger dispositions.
1040-
**A breaking changeset must also state its ADR-0087 disposition, in writing.** Add exactly one marker to the
1041-
changeset body — `pnpm check:adr-0087-registration` enforces it, and the CI step is *Require an ADR-0087
1042-
disposition on a declared-breaking changeset*:
1043-
```
1044-
<!-- adr-0087: registered SOME-MIGRATION-ID -->
1045-
<!-- adr-0087: not-required (unpublished) why -->
1046-
<!-- adr-0087: not-required (already-registered SOME-MIGRATION-ID) why -->
1047-
<!-- adr-0087: not-required (no-migration-prescription) why -->
1048-
```
1049-
The gate prints the argument when it fails — that output is the authority.
1046+
**A breaking changeset must also state its ADR-0087 disposition, in writing** — exactly one marker in the
1047+
changeset body, enforced by `pnpm check:adr-0087-registration` (CI step *Require an ADR-0087 disposition on a
1048+
declared-breaking changeset*). ⛔ The categories are NOT copied here — the gate prints the full set when it fails.
10501049
4. **A removal that breaks the pinned sibling checkout ships together with the sibling fix and the pin bump — or it
10511050
does not ship.** The `Console Pin Gate` job builds objectui at the pinned `.objectui-sha` against **current** `main`,
10521051
so a removal or rename the pinned sibling still imports turns `main` red for every PR in the repo the moment it

scripts/check-partof-closing-keyword.mjs

Lines changed: 110 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,48 @@
109109
* their own, so a branch pushed to the default branch by any other route closes
110110
* the cards its intermediate commits name.
111111
*
112-
* The remedy is the contract this repository already carries, cited rather than
113-
* restated — the card relation is declared ONCE, in the PR body, and the merger
114-
* takes the squash message from that body. ⛔ Nothing in this gate's output asks
115-
* anyone to rewrite history: amend, rebase and force-push are forbidden here,
116-
* and a red on an already-pushed branch is repaired the way this repo's merges
117-
* already repair it, in the body and at the merge.
112+
* The contract is the one this repository already carries, cited rather than
113+
* restated — the card relation is declared ONCE, in the PR body, and no commit
114+
* carries a card trailer.
115+
*
116+
* ## RULE 2 — what the output may ask for, and when it may ask for nothing
117+
*
118+
* ⛔ Nothing in this gate's output asks anyone to rewrite history: amend, rebase
119+
* and force-push are forbidden here. That prohibition is absolute, which makes
120+
* the TIMING of a RULE 2 finding the whole story:
121+
*
122+
* - BEFORE the branch is pushed the remedy is ordinary work — reword the
123+
* commit messages, state the relation only in the body, push once. Nothing
124+
* published is rewritten and the check goes green.
125+
* - ONCE THE BRANCH IS PUSHED no author action clears the red. This gate reads
126+
* the PR's COMMIT LIST, so a new commit on top JOINS that list and leaves
127+
* the offending message in it; the only thing that would remove it is the
128+
* rewrite forbidden above. The red is PERMANENT for that branch, and saying
129+
* so plainly is the point of this section. An earlier revision instead told
130+
* the author to push reworded commits — which on a pushed branch IS the
131+
* forbidden rewrite — and a seat that read it instructed one; the dev's
132+
* refusal is what stopped it, not the wording.
133+
*
134+
* ⚠️ That earlier revision also had the merger "take the squash message from
135+
* that body". Measured, that is false: on 0a61db1f5, the squash of PR #16646,
136+
* the landed message is the branch commit's verbatim and carries its
137+
* `Refs #16624`, while the body's `Fixes #16624` appears nowhere in it. The
138+
* squash message is assembled from the COMMITS. This does not weaken RULE 2 —
139+
* it is RULE 2's premise: a trailer left on a pushed commit really does reach
140+
* the default branch's permanent history.
141+
*
142+
* What that costs depends on the spelling, and the output says so rather than
143+
* flattening it: Part-of and Refs land as a reference and move no card, while a
144+
* CLOSING keyword lands on the surface GitHub's parser reads. The asymmetry is
145+
* why the rule refuses all three at PR time rather than only the contradictory
146+
* ones.
147+
*
148+
* ⛔ Whether a pull request LANDS carrying this red is not this gate's call and
149+
* its output must not make it. The check run is advisory at the branch-
150+
* protection layer — absent from the required-context registry, and its workflow
151+
* subscribes to no `merge_group` event because a queue build carries no body for
152+
* it to judge — so that decision belongs to whoever lands the PR, under the
153+
* rules that bind them. This gate reports.
118154
*
119155
* ## Where the commit messages come from, and why the endpoint and not a walk
120156
*
@@ -314,7 +350,7 @@ const SELF_TEST_BATTERIES = Object.freeze({
314350
'Context reading: presence, not truthiness.': 4,
315351
'The wiring itself. A gate whose workflow step is deleted or whose': 6,
316352
'The predicate source this gate reuses must still be there to reuse.': 1,
317-
'RULE 2 — every card-relation spelling in a commit message is a finding,': 13,
353+
'RULE 2 — every card-relation spelling in a commit message is a finding,': 19,
318354
'The regression fixture: the squash that assembled a contradiction no': 4,
319355
'RULE 2 delegates to the sweep extractors at the commit-message reading.': 3,
320356
'The commit list input. An absent, broken or empty list can never read': 8,
@@ -505,10 +541,11 @@ export function commitTrailerFindings(commits) {
505541
`message that lands on the default branch — a text no one writes and no one reviews, which ` +
506542
`carries every trailer its inputs carried and can contradict itself where its parts did not. ` +
507543
`The trailer is also live on its own. ${RELATION_CONTRACT} ` +
508-
`Remedy: move this relation into the PR body, where it is stated once, and take the squash ` +
509-
`message from that body at merge. ` +
544+
`Remedy, while this branch is still UNPUSHED: reword this commit message so the relation is ` +
545+
`stated only in the PR body. ` +
510546
`⛔ Do NOT amend, rebase or force-push to remove it — rewriting pushed history is forbidden ` +
511-
`here, and it is not what fixes this.`,
547+
`here; on an already-pushed branch nothing removes it, and the summary below is what to do ` +
548+
`about that.`,
512549
);
513550
}
514551
return findings;
@@ -658,8 +695,32 @@ export function judge(ctx) {
658695
);
659696
for (const finding of commitFindings) lines.push(` ${finding}`, '');
660697
lines.push(
661-
' Pushing the reworded commits re-runs this check. ⛔ The repair is NOT a history rewrite:',
662-
' state the relation once in the PR body and take the squash message from that body at merge.',
698+
' ⛔ The repair is NOT a history rewrite. Amend, rebase and force-push are forbidden in this',
699+
' repository and this gate never asks for one. What it does ask for turns on one thing only:',
700+
'',
701+
' BRANCH NOT PUSHED YET — reword the commit messages now and push once. Nothing published is',
702+
' rewritten, the relation goes in the PR body where the contract puts it, and this check is green.',
703+
'',
704+
' BRANCH ALREADY PUSHED — no author action clears this red, and that is expected rather than a',
705+
" problem to solve. This gate reads the PR's COMMIT LIST, so a new commit on top JOINS that list",
706+
' and leaves the message above in it; the only thing that would remove it is the rewrite forbidden',
707+
' above. Three measured facts, so this red can be READ rather than acted on:',
708+
'',
709+
' 1. This check run is advisory at the branch-protection layer: it is absent from the',
710+
' required-context registry, and its workflow subscribes to no `merge_group` event because a',
711+
' queue build carries no PR body for it to judge.',
712+
' 2. The squash message is assembled from the COMMIT messages, not from the PR body. Measured on',
713+
" 0a61db1f5, the squash of PR #16646: the landed message is the branch commit's, verbatim,",
714+
" carrying its `Refs #16624`; the body's `Fixes #16624` is nowhere in it.",
715+
' 3. The card relation is safe either way. #16624 closed on that same merge although no commit',
716+
" message named a closing keyword for it — the PR BODY's keyword is what acts. Declaring the",
717+
' relation once in the body is the whole contract, and it already works.',
718+
'',
719+
' So the residue of landing this red is the trailer above sitting in the permanent history, and',
720+
' what that costs depends on its spelling: `Part of` and `Refs` land as a reference and move no',
721+
" card, while a CLOSING keyword lands on the surface GitHub's parser reads. ⛔ Weigh that under the",
722+
' landing rules that bind you — this gate found a real contradiction between your commits and your',
723+
' body, and it does not decide whether the pull request merges.',
663724
);
664725
}
665726
return { exit: EXIT_CONTRADICTION, lines: [...lines, ...(unread.length ? ['', ...unread] : [])] };
@@ -881,6 +942,43 @@ function selfTest() {
881942
named.includes(RELATION_CONTRACT),
882943
true,
883944
);
945+
// The guidance text itself, pinned. This gate once told the author to push
946+
// reworded commits while also forbidding a rewrite — unsatisfiable on a
947+
// pushed branch, and a seat that read it instructed an amend and force-push.
948+
// These six hold the repaired shape: the forbidden action stays forbidden,
949+
// the reachable remedy is scoped to an UNPUSHED branch, the pushed case is
950+
// named as permanent, the measured squash fact replaces the false one, and
951+
// the gate still refuses to decide the merge.
952+
t(
953+
'the guidance still forbids amend, rebase and force-push',
954+
/ Do NOT amend, rebase or force-push/.test(named),
955+
true,
956+
);
957+
t(
958+
'the reachable remedy is scoped to a branch that is NOT pushed yet',
959+
named.includes('UNPUSHED') && named.includes('BRANCH NOT PUSHED YET'),
960+
true,
961+
);
962+
t(
963+
'the already-pushed case is named, and named as clearing for nobody',
964+
named.includes('BRANCH ALREADY PUSHED') && named.includes('no author action clears this red'),
965+
true,
966+
);
967+
t(
968+
'the guidance no longer claims the squash message comes from the PR body',
969+
/squash message from that body/.test(named),
970+
false,
971+
);
972+
t(
973+
'the guidance states the MEASURED squash fact instead',
974+
named.includes('assembled from the COMMIT messages, not from the PR body') && named.includes('0a61db1f5'),
975+
true,
976+
);
977+
t(
978+
'the guidance leaves the landing decision to the lander rather than ordering a merge',
979+
named.includes('does not decide whether the pull request merges'),
980+
true,
981+
);
884982
t(
885983
'an ordinary commit message with no card relation is clean',
886984
commitVerdict('fix(cli): stop counting the walk instead of the tree\n\nBody prose about the change.\n').exit,

0 commit comments

Comments
 (0)