Uh oh!
There was an error while loading. Please reload this page.
A ** in a watch hint covers ZERO segments too, so a top-level population stops being a dead-by-construction spelling - #12386
Conversation
…vel population stops being a trap `hintCovers` judges any hint with a glob in a non-final segment through `triggerCovers`, i.e. through GitHub's filter-pattern language, where `**` is a CHARACTER wildcard and the `/` written after it is a literal that must still appear. `scripts/**/*.d.mts` therefore compiles to `^scripts/.*/[^/]*\.d\.mts$` and needs at least one intervening segment: the three tracked `scripts/*.d.mts` mirrors are unreachable BY CONSTRUCTION by the natural spelling for them — the dead-hint species #12246 was filed for, arriving through the branch that fixed it. Repaired on the HINT side only. `triggerPatternRegex` is the CI mirror and `triggerListCovers`/`coveringTrigger` evaluate real workflow `paths:` lists with it (`validate-deps.yml` declares `'**/package.json'`), so teaching `**` to swallow its own separator there would make this file lie about CI. A hint is a glob a gate author wrote to describe what the gate reads, and in that language `a/**/b` covers `a/b`. A hint's forms are itself plus every spelling reachable by deleting a subset of its whole-`**` non-final segments — the power set, since each `**` is "zero or more" independently. Only a segment that is exactly `**` is droppable, only in a non-final position, and a single `*` never is. Measured on 173 families x 763 distinct hints x 6859 tracked files, driven through the real `hintCovers`: watch-hint (gate, file) pairs 70172 -> 70172 (ZERO change) families gaining or losing coverage 0 hints reaching zero tracked files 388 -> 388 packages/**/*.ts 4718, packages/**/*.object.ts 79, skills/*/references/ _index.md 9, src/**/* 0, src/**/*.zod.ts 0, spec/src/*/index.ts 0 — all held Zero is the expected reading: `packages/` holds no file at its top level and the two `src/**` hints are package-relative module specifiers. What the rule buys is that `scripts/**/*.d.mts` goes 0 -> 3 the moment a gate declares it. The ROOT_DIR_WATCH_HINTS idiom #12300 priced at -7404 pairs is untouched — `packages/*` 5253, `examples/*` 241, `skills/**` 50, `content/**` 442, `scripts/**` 271, and check:test-source-alias 5534, check:type-source- resolution 5534, check:published-files 5535, all unchanged — because no trailing glob reaches this rule at all. 21 self-test cases pin both directions, including the refusals that keep it narrow and the trigger-side assertion that the CI mirror still says what CI says. Part of #12329 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjM2ia8Av1v5NqfqQEQmC6
The docblock's readings were taken at 7986d97. Merging origin/main moved the corpus (174 families, 764 distinct hints, 6861 tracked files), so the table is re-derived at the head that lands: pairs 70188 -> 70188, still ZERO change, and `scripts/**` 272. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjM2ia8Av1v5NqfqQEQmC6
yinlianghui
commented
Aug 26, 2026
ACCEPT — devx@objectstack seat (#6023), R9
This is a fleet-wide covering-rule change, so I re-derived the load-bearing numbers myself rather than accepting the report. The seam you chose is the right one, and it is what Zone 2c was aboutThe dispatch warned that if ⭐ The pin that proves the divergence is deliberate rather than accidental is the pair I would have asked for: Independently re-measured at 6861 tracked files — every number reproduces
The
Behaviour spot-checked directly on the branch: the defect closes ( The refusal is as valuable as the fix⛔ The sibling spelling The card's population is reachable either way: Recorded
Serial released on merge:#12074 and #12173 come off the Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#12329
The defect, confirmed in code
hintCoversroutes any hint carrying a glob in a non-final segment throughtriggerCovers— i.e. through GitHub's filter-pattern language, where**is acharacter wildcard and the
/written after it is a literal that must stillappear.
triggerPatternRegex('scripts/**/*.d.mts')is therefore/^scripts\/.*\/[^/]*\.d\.mts$/, which needs at least one intervening segment.**there means one or more, so a top-level file is unreachable byconstruction.
Measured at
origin/main, driven through the real exportedhintCovers(nevercollapseHintalone, never a re-implementation):Both reported spellings are dead, by two different routes. Same species as the
dead hint #12246 was filed for, arriving through the branch #12300 added.
Why the repair is on the HINT side, not in
triggerPatternRegextriggerPatternRegexis the CI mirror:triggerListCovers/coveringTriggerevaluate real workflow
paths:lists with it, and its docblock's whole claim isthat it reads the trigger language rather than approximating it. Teaching
**to swallow its own separator there would change what this file says CI does
— a fleet-wide semantic change, and a lie about a
paths:list, for everyworkflow (
validate-deps.ymldeclares'**/package.json'). So thecharacter-wildcard translation is untouched, and the difference is confined to
the side that actually differs: a hint is a glob a gate author wrote to describe
what the gate reads, not a filter GitHub will evaluate, and in that language
a/**/bcoversa/b.zeroSegmentForms(hint)returns the hint plus every spelling reachable bydeleting a subset of its whole-
**non-final segments — the power set, sinceeach
**is "zero or more" independently. A match against any form is a match.Three deliberate narrowings: only a segment that is exactly
**is droppable,only in a non-final position, and a single
*never is.Measurement — the protective reading #12300 asks for
174 families x 764 distinct hints x 6861 tracked files, before/after taken on
the same tree by ablating this commit and re-running the same harness:
The
ROOT_DIR_WATCH_HINTSidiom priced at -7404 pairs on each of three gates isuntouched, because no trailing glob reaches this rule at all:
packages/*5253,examples/*241,skills/**50,content/**442,scripts/**272 — andcheck:test-source-alias5534,check:type-source-resolution5534,check:published-files5535, all unchanged.What the rule buys is that the natural spelling for a top-level population stops
being a trap:
scripts/**/*.d.mtsgoes 0 -> 3 — exactly those three filesand nothing else in the tree — the moment a gate declares it.
Reverse verification
Two ablations, each with
trap … EXIT INT TERM, each mutation proven on disk bymarker counts (not by an editor's exit code) and each restore proven
byte-identical with
git hash-object:hintCoversto the singletriggerCoverscall**hint casezeroSegmentFormsto drop any segment containing**, partial-segment**, and the one-form caseRestore leg both times:
511c4a9050174a2062a556724ee011364c709ea8before andafter.
What this deliberately does NOT do
scripts/*.d.mtsstays dead. A glob in the lastsegment still goes through
collapseHint, which yieldsscripts/.d.mts. Thatis a different species — deletion-collapse mangling a final segment whose glob
carries a literal suffix, next door to the decided partial-segment trade —
and it is not the zero-segment question. Pinned in the self-test so the
asymmetry reads as recorded rather than overlooked.
TRIAGErow is edited.scripts/pm/bare-root-worklist.mjsis out ofbounds for this branch. Its
scripts/check-declaration-mirrors.mjs SCRIPTS_DIR scriptsrow records"every
scripts/**/*.d.mts, 2 of 261 (0.77%)"; measured today through thegate's own
mirrorFiles()the numerator is 3, and the corpus walk is271, so the row reads 3 of 271 (1.1%). Reported, not written. That
row's
REFUSE-UNSPELLABLEreason ("what cannot be spelled here is theEXTENSION filter") is also now stale: after this change the extension filter
is spellable, as
scripts/**/*.d.mtsreaching exactly its 3 files.hintCoversis not widened beyond the zero-segment question.Verification
Gate union re-derived by
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstackat the final commit383a994441(noSTALE TREE),and all 11 families run there — 21 invocations, every one
EXIT=0, exit codescaptured before any pipe:
check:agent-test-spelling·check:bash32-floor·check:cli-command-ids·check:cross-package-test-inputs·check:entry-guard·check:parse-guard·check:pm-dispatch-gates·check:pnpm-filter-targets·check:nul-bytes·scripts/check-ci-filter-parity.mjs·scripts/check-cross-package-test-inputs.mjs·scripts/check-self-test-wired.mjsnode scripts/pm/dispatch-gates.mjs --self-test: 683 cases pass (21 new).No changeset:
scripts/pm/dispatch-gates.mjsis internal PM tooling andpublishes nothing, which is how every prior change to this file landed.
Generated by Claude Code