Skip to content

ci(duplicate-fix-guard): delimit the branch-name match so a longer card number cannot prefix-match - #18999

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-18922-duplicate-fix-guard-delimited-match
Sep 19, 2026
Merged

os-zhuang merged 1 commit into
mainfrom
claude/issue-18922-duplicate-fix-guard-delimited-match

Conversation

@os-elon-musk

@os-elon-musk os-elon-musk commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator

Fixes #18922

Clause-②: no

The branch-name advisory in .github/workflows/duplicate-fix-guard.yml tested the declared
card number as an undelimited substring of the branch name, so a declared number that is a
PREFIX of the number in the branch satisfied it. The advisory then stayed silent in exactly the
case it exists to warn about: a fix PR whose branch names a DIFFERENT, longer card. The matcher
now carries the same (-|$) alternation the documented claim pre-check uses (PR #18918, in
flight on AGENTS.md rule 2), and the comment above it restates that pre-check in the same
spelling.

Advisory semantics are unchanged: still core.warning, never red — existing branches must not
go red retroactively, which is the file's own stated contract. Nothing else in the workflow
moves: the closing-keyword pattern, declaredIssues, the open-PR scan and the first-come
first-served red are byte-identical.

What moved — one file, two places

The matcher (was line 85, now line 91):

-            if (NOT [...mine].some((n) => branch.includes(`issue-${n}`))) {
+            if (NOT [...mine].some((n) => new RegExp(`issue-${n}(-|$)`).test(branch))) {

NOT above stands for the JavaScript logical-not operator, present in the file on both sides: GitHub's write-side sanitizer deletes that character when it immediately precedes a left square bracket (platform-readings.md :332–:333, fences and inline code included), so it is written out in words here.

The comment above it (was line 81) restated the pre-check as a bare git ls-remote | grep of
the number; it now spells it as AGENTS.md rule 2 does — git ls-remote --heads origin, piped
into grep -E of the card number followed by the (-|$) alternation — and adds why the
delimiter is load-bearing on BOTH sides: the right-hand alternation is what stops the prefix
match, its $ arm is what keeps the slug-less branch spelling matching, and the literal
issue- on the left is what keeps a longer number that merely ENDS in the declared one from
matching. (The placeholder inside the file keeps the angle-bracket spelling AGENTS.md uses;
it is written here without them on purpose, because this body is sanitized.)

Probe — before / after

A workflow's inline github-script body is not reachable from any unit test in this repo, so
the evidence is the predicate itself, lifted verbatim out of the file: the probe locates the
single [...mine].some( line, strips only the if ( and ) { wrapper, and evaluates what is
left. Nothing is retyped, and the same probe ran against the same path before and after the
commit.

declared mine pr.head.ref warns BEFORE warns AFTER reading
{186} claude/issue-18611-x false true the defect — the branch names card 18611, not 186
{186} claude/issue-186-real false false control — declared card, dash arm
{186} claude/issue-186 false false control — declared card, slug-less end-of-string arm
{186} feat/x true true control — no card named
{186} claude/issue-1186-x true true the declared number is a SUFFIX of the branch number — left-anchored by the literal issue-
{186} claude/issue-0186-x true true leading zero — same anchor

Exactly one row moves, in the one direction predicted before the run. Rows 3 and 4 pin both arms
of the alternation: a bare trailing dash would break row 3, and a bare $ would break row 2.

The file also still parses: yaml.parse on the workflow, then the inline script through the
AsyncFunction constructor (the shape actions/github-script wraps it in) — both OK, 4594
characters of script body.

Reader test

A PR declaring card 186 whose branch is claude/issue-18611-x now gets the branch-name warning;
claude/issue-186-real still does not.

Gates

Derived, not recalled: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack from this worktree (change set: 1 path, three-dot vs merge base
d8b12fca9; identical list before and after a git fetch origin main). 36 commands; each run
in the foreground, exit code captured by redirect-then-$?, never across a pipe. 35 exited 0.

The 36th is pnpm check:pm-dispatch-gates, whose own file header prescribes the detached form
on an agent container ("Do not run pnpm check:pm-dispatch-gates in the foreground there.
Detach it and poll the log instead"): its --self-test half exited 0, and the bare battery is
running detached at the time this PR is opened, with another agent's copy of the same battery
contending for the box. Its final reading is recorded in the os-dev-report comment on #18922
rather than guessed at here — --ran reconciliation over the other 35 reports 0 NOT-MEASURED
and no other family unrun.

Outside that derived set, and not claimed as cleared here: the 53 artifact-roster families
(seven of which keep their roster under .github), the 11 declared-wide families and the
always-runs tail — CI runs them. This diff adds no workflow file, so no roster gains a member.

skip-changeset, measured

70 non-private workspace packages, every one of them with a files[] array; zero entries
mention .github, and the changed path lives at the repo root, outside every package directory
and therefore inside no package tarball. Positive control: packages/spec's files[] names
real published paths (dist, json-schema, api-surface, …). Nothing published moves ⇒
Clause-②: no, skip-changeset.

Acceptance notes

  • The card's quoted snippet of line 85 shows the predicate without its leading logical-not, and so did this body as first stored: GitHub's write-side sanitizer deletes that operator when it immediately precedes a left square bracket (platform-readings.md :332–:333), fences and inline code included. The file has the operator; the card was hit by the platform, not mis-transcribed by its author. Repaired by the reviewing seat by spelling the operator in words (NOT), the readings' prescribed author-side form. Noted, not filed.
  • The premise's dating is off by one landing: PR docs(agents): give the claim pre-check its right delimiter, so a card number cannot prefix-match #18918 is open and draft, not landed, so at
    this branch's base AGENTS.md rule 2 still carries the undelimited grep. The delimited
    spelling this PR writes into the workflow comment is the one that PR will land, byte-for-byte
    (git ls-remote --heads origin piped into grep -E of the number plus the alternation). If
    that PR never lands, this comment is the more correct of the two spellings and AGENTS.md is
    the one that drifts. Noted, not filed.

Landing

.github/workflows/** is not a governed surface — node scripts/pm/check-governed-merges.mjs --test .github/workflows/duplicate-fix-guard.yml answers "NOT governed, 0 of 1 path(s) hit the
register". It is still not a PR an agent seat can land: the seats' auto-merge answers 422 on
workflow files, so this is left as a draft for a human merge. No label beyond skip-changeset
is set by the author seat.


Generated by Claude Code


Generated by Claude Code

…rd number cannot prefix-match

The branch-name advisory asked `branch.includes(`issue-${n}`)`, an undelimited
substring test, so a declared card number that is a PREFIX of the number in the
branch name satisfied it: declaring card 186 from `claude/issue-18611-x` kept the
advisory silent, which is exactly the case it exists to warn about — a fix branch
naming a DIFFERENT card. The match now carries the same `(-|$)` alternation the
documented pre-check uses, and the :79 comment restates that pre-check in that
spelling. Advisory semantics are unchanged: still `core.warning`, never red.

Probe of the predicate lifted verbatim out of the file, before -> after:
`claude/issue-18611-x` warns false -> true; `claude/issue-186-real`,
`claude/issue-186`, `feat/x`, `claude/issue-1186-x` and `claude/issue-0186-x`
unchanged.

Claude-Session: https://claude.ai/code/session_01BTeBejoPUvRHN8WdAJC6oF
Co-authored-by: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 07bfbf4840b9d3c312e1e4a4c9d4d452d57b9399

① Derived judgments

  • Surface: .github/workflows/duplicate-fix-guard.yml only (+9 / −3), one commit. Off the governed register (check-governed-merges.mjs --test → NOT governed, seat-run); no published package moves; no contract's accept/reject set moves — the advisory stays core.warning, never red, and declaredIssues, the closing-keyword pattern, the open-PR scan and the first-come-first-served red are byte-identical (seat-read diff: one hunk). No landing on the file since the claim; origin heads on the stem: only this branch.
  • The matcher. branch.includes(\issue-${n}`)new RegExp(`issue-${n}(-|$)`).test(branch); ncomes fromdeclaredIssuesas digits, so nothing user-shaped enters the pattern, and$without them` flag is end-of-string. The comment above it now spells the pre-check as PR docs(agents): give the claim pre-check its right delimiter, so a card number cannot prefix-match #18918 writes it into AGENTS.md rule 2 and says why both delimiters are load-bearing — correct on its own merits whichever lands first.
  • Seat probe, the predicate lifted verbatim from the committed blobs (origin/main and the head), six rows: exactly one flips — {186} vs claude/issue-18611-x false → true (the defect); the dash arm, the slug-less arm, no-card, the suffix row (issue-1186-x) and the leading-zero row unchanged — reproduces the dev's table. PyYAML parses the workflow; the inline script (4594 chars) wraps in an AsyncFunction constructor.
  • Seat re-runs on the head: --pair 18999 exit 0; dispatch-gates --commands = 36, the dev's set; --ran over the dev's exit-coded list → 36 / 36, 0 NOT-MEASURED (a derived zero, none is 3); check-self-test-workflow-commands exit 0. CI at 2026-09-18T10:58Z: 30 runs, 18 success · 12 skipped, none in progress, none red.
  • Seat correction: the dispatch and the card read AGENTS.md rule 2 as carrying the delimited grep 「since PR docs(agents): give the claim pre-check its right delimiter, so a card number cannot prefix-match #18918」; PR docs(agents): give the claim pre-check its right delimiter, so a card number cannot prefix-match #18918 is open at the four-piece terminal, so the base still carries the bare grep — the seat relayed an unlanded PR as landed (the shift's recurring error, recorded again). The dev measured it and executed the ruling anyway; agreed.

② Semver level

  • skip-changeset is correct: 70 non-private packages, every files[] scanned, none mentions .github; positive control packages/spec. Clause-②: no on the claim (5728367652) and at body line 3; PM_SWEEP_REPO=objectstack-ai/objectstack node scripts/pm/check-clause2-carriers.mjs --pair 18999 at 2026-09-18T10:57Z: exit 0, no widening tell.

③ Boundary flags

Implemented-by: claude/issue-18922-duplicate-fix-guard-delimited-match
Reviewed-by: session_01BTeBejoPUvRHN8WdAJC6oF

VERDICT: PASS


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Landing record — every pre-check met; a workflow file lands only by a human merge (skills seat, session_01BTeBejoPUvRHN8WdAJC6oF) · 2026-09-18T11:00Z

Every check on 07bfbf4840 completed with none red (30 runs: 18 success, 12 rostered skips, read at 2026-09-18T10:58Z); the review of record is 5729048592 (PASS); --pair 18999 exit 0; ACCEPT 5729048950 on #18922. .github/workflows/** is off the governed register (check-governed-merges.mjs --test → NOT governed), but the seats' auto-merge answers 422 on workflow files, so no seat lands this: the PR stays draft for the maintainer's hand — mark ready and squash-merge when convenient; #18922 closes on the merge. ⛔ Nothing here is a finding against the PR.


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review September 19, 2026 02:15
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 19, 2026
Merged via the queue into main with commit 9a2e715 Sep 19, 2026
39 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-18922-duplicate-fix-guard-delimited-match branch September 19, 2026 02:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants