Skip to content

fix(triggers): point repository.directory at the directory the trigger packages actually live in - #15993

Open
os-warren wants to merge 4 commits into
mainfrom
claude/issue-15478-stale-repository-directory
Open

fix(triggers): point repository.directory at the directory the trigger packages actually live in#15993
os-warren wants to merge 4 commits into
mainfrom
claude/issue-15478-stale-repository-directory

Conversation

@os-warren

@os-warren os-warren commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

Fixes #15478

@objectstack/trigger-record-change and @objectstack/trigger-schedule each declared a repository.directory under packages/plugins/ that does not exist in this repository, so the Repository deep link on their npm pages resolves to a 404 instead of to their source. Both are published today at 17.3.0.

package was now
@objectstack/trigger-record-change packages/plugins/plugin-trigger-record-change packages/triggers/trigger-record-change
@objectstack/trigger-schedule packages/plugins/plugin-trigger-schedule packages/triggers/trigger-schedule

The whole class, not just the two named

The card names two packages. The census was scripted rather than eyeballed, over git ls-files for every tracked package.json (81 files, node_modules excluded), comparing repository.directory both against the filesystem and against the manifest's own directory — a plausible-but-wrong path fails exactly as silently as the current one, so resolving is not sufficient on its own.

The script refuses to print any number unless its control fires in both directions: a directory known to exist must report EXISTS, and a deliberately impossible one must report MISSING.

CONTROL exists-case  packages/triggers/trigger-record-change      -> EXISTS  (want EXISTS)
CONTROL missing-case packages/__deliberately_impossible_15478__    -> MISSING (want MISSING)
CONTROL OK
before after
tracked package.json 81 81
declare repository.directory 57 57
resolves and equals own directory 55 57
mismatched 2 0
declares nothing 24 24

The two the card names were the entire mismatched set; the sweep found no third. Both were also the entire non-resolving set, so no manifest points at a directory that exists but belongs to someone else.

How the field went stale — corrected

⚠️ An earlier revision of this PR and its changeset said the field survived "two pure renames" and that "nothing rewrote the field". That was false, and the true account argues more strongly for a mechanical check. Measured commit by commit:

commit when what it actually did
f15d6f6f6 2026-06-12 07:43 A copy, not a rename — 26 files, +2222/-19, with all four trigger directories briefly coexisting. Each copied manifest differs from its original in exactly one line: name. So this commit edited the manifest and left directory pointing at the path it was copied from.
290c62514 2026-06-12 07:48 Deleted the original directories five minutes later (merged as f8f7c549c, #1751).
ea4941ad8 2026-06-12 08:09 The only pure rename: packages/plugins/trigger-* to packages/triggers/trigger-*, 16 files, 0 insertions, 0 deletions. This is where the second segment went wrong.
9a43e042f 2026-07-21 #3380 rewrote repository.url and bugs in both of these manifests. The stale directory line sits as unchanged context one line below the edited url, inside the same repository object, in a reviewed hunk.

⭐ That last row is why the correction was worth making. "Nothing rewrote the field" reads as the field was never in anyone's way. The opposite is true in the way that matters: one commit edited its immediate neighbour inside the same JSON object, and six weeks later an author had the dead line on screen and shipped past it. A rule is more clearly needed when a human looked at the line and still missed it — so this strengthens the case on #15991 rather than softening it.

Same-migration rot in a different key — one more, corrected here

docs/adr/0041-flow-trigger-family.md carries the same dead path in its ## References section as a source pointer:

- Shipped triggers: `packages/plugins/plugin-trigger-record-change`,
  `packages/plugins/plugin-trigger-schedule`

That is the same defect class from the same rename — a source deep link that now 404s — so it is corrected with the manifests and named here rather than left as unreported drift.

Deliberately left as written: the same record's rename table and narrative at lines 46, 48 and 97-100. Those name the old identifiers as the decision's own history, and the table's right-hand column is a package name, not a path — not a pointer by construction. The anchored replacement could not have touched them in any case (they carry no packages/plugins/ prefix), but the distinction is the point: a References pointer is live, a decision record's account of what it renamed is not.

The bounding scan is a repo-wide git grep, so this is a measured set and not a sample. Across all tracked files at the merge base, packages/plugins/plugin-trigger appeared in exactly 4 places — the 2 manifests and these 2 ADR lines — all 4 corrected. Remaining plugin-trigger matches are the ADR history above plus packages/cli/CHANGELOG.md, a published-version record that must not be rewritten.

⚠️ Stated precisely, because the population must include the file doing the counting: the intermediate spelling packages/plugins/trigger- occurs 0 times at the merge base and 1 time at head — that one occurrence is this PR's own changeset, quoting the history above. It is a quotation, not a pointer.

Other path-shaped manifest keys were swept in the same pass: exactly one homepage/bugs/repository.url value repo-wide embeds a repo-relative path (packages/console, which exists and is unrelated to this migration), and pnpm-workspace.yaml already lists packages/triggers/*, so no workspace glob went stale.

⭐ Why no gate caught it: the rule never existed

Not the wrong population, and not a missing clause in an existing rule set. No script in this repo reads repository.directory at all.

Stated as the measurement rather than a characterisation: across scripts/, 245 lines contain the token repository, and a grep for any read of a manifest repository.directory returns zero. The only code-shaped uses of the token are CI's github.repository context (process.env.GITHUB_REPOSITORY and the repository: keys building an owner/repo API context in release-github-releases.mjs). The bulk of the remaining hits are incidental substring matches in file paths and identifiers — the largest single concentration, 27 lines in scripts/engine-double-contract.pinned.json, is the filename sys-metadata-repository-…test.ts — plus prose in comments. (An earlier revision of this body called them all "CI context or comment prose", which was imprecise; the load-bearing claim, zero reads, is unchanged and was independently confirmed at 0 reads across 6061 tracked code/config files.)

The near-miss worth naming is scripts/check-published-files.mjs. It has exactly the right population — every non-private workspace package — and already parses each manifest and validates files, types, module and exports subpaths. All six of its invariants concern the files whitelist: what the tarball contains. The word repository occurs 0 times in it. Nothing asks where the tarball says its source lives. The notch is a category, not an off-by-one: this repo gates published package contents and does not gate published package provenance metadata.

Two adjacent gates confirm the notch rather than closing it: check-published-readme-links.mjs resolves links in published markdown (READMEs, not manifests), and check-adr-links.mjs matches only [text](target) markdown links — the stale ADR pointer above is an inline code span, so that gate cannot see it by form.

No gate is changed in this PR. The check is filed as #15991, split out on the instruction in this card's own triage comment ("if the gate turns out to want a decision … land the two string fixes first and split the gate out"). The decision is real and measured: 24 manifests declare nothing, 14 of them publishable (including @objectstack/trigger-api, a sibling in this very family), so whether declaring the field is mandatory is a policy call, not a correction.

Changeset, not skip-changeset

skip-changeset does not apply. Its rule is "publishes nothing from any package", and this changes published package metadata: repository.directory ships inside the npm tarball regardless of the files whitelist, so a fix that never publishes leaves both package pages 404ing. A patch changeset covers both packages. No skip-changeset label is set on this PR.

Verification

All runs below are on head 6c2957384. origin/main moved during review; dispatch-gates.mjs reported STALE TREE against the older head, so origin/main (f7db8f4fd) was merged — not rebased, and nothing force-pushed. The merge touched none of this PR's four files, brought in no merge=os-regen path, and the re-derivation after it is clean. This PR's diff against origin/main remains exactly those four files.

Gate family derived mechanically from the actual changed files — node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, which reads the change set from the merge base itself (4 paths), never hand-built. Every exit code captured immediately after a single redirected command, never through a pipe, and every command given its own cd.

All 20 path-derived gates EXIT=0: adr-anchors, changeset-gate-self-tests, cross-package-test-inputs, doc-authoring, doc-formula-expressions, dts-closure, dual-build-cjs-loads, llms-txt, merge-driver, objectui-changeset, org-identifier, override-consistency, page-declaration-shape, pm-governed-merges, published-files, slot-lookup, test-source-alias, turbo-task-graph, type-source-resolution, workspace-manifest-cycles — plus whole-tree nul-bytes EXIT=0.

check:doc-formula-expressions first answered 3 = PREREQUISITE NOT MET on the fresh worktree, which is not a pass and is not reported as one; after turbo run build (73/73 tasks) it re-ran EXIT=0. On the same built tree, check:dts-closure EXIT=0 (164/164 declaration files across 72 built packages) and check:dual-build-cjs-loads EXIT=0 (103 published require entry points across 66 packages load; 619 emitted CommonJS files parse).

The check named in dispatch, with its own control: node scripts/check-adr-0087-registration.mjs --self-test EXIT=0 (325 assertions over real temp git repos), then --base origin/main --head 6c2957384328dbc25e32ed044ab441fba0682ff4 EXIT=0 ("adds no declared-breaking changeset, 1 non-breaking changeset seen").

Carried forward from the pre-merge head e0fcc1455, where the diff was identical apart from the changeset prose corrected above: trigger-record-change 10 files / 101 tests passed, trigger-schedule 4 files / 57 tests passed, typecheck EXIT=0 for both with the script names echoed in the output (a --filter matching zero scripts exits 0 having run nothing, so the echo is what distinguishes a pass from a no-op). This revision changes only changeset prose, so the suites were not re-run and that is stated rather than implied.

⚠️ Wall-clock figures from these runs are shared-box readings taken under scripts/pm/os-verify-lock.sh, which excludes other locked runs and not unlocked sibling work.

The census script lives outside the repository tree; no scratch file was on disk during any whole-tree gate run, and the working tree is clean at 6c2957384.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y

…r packages actually live in

`@objectstack/trigger-record-change` and `@objectstack/trigger-schedule` both
declared a `repository.directory` under `packages/plugins/` that does not exist
in this repository, so the "Repository" deep link on their npm pages resolves to
a 404 instead of to their source. Both are published at 17.3.0.

    @objectstack/trigger-record-change
      was packages/plugins/plugin-trigger-record-change
      now packages/triggers/trigger-record-change
    @objectstack/trigger-schedule
      was packages/plugins/plugin-trigger-schedule
      now packages/triggers/trigger-schedule

Residue of two same-day renames, not one: `plugin-trigger-*` -> `trigger-*`
inside `packages/plugins/` broke the first segment, and the later promotion of
`packages/plugins/trigger-*` to a first-class `packages/triggers/` directory
broke the second. Both were pure directory renames with zero content changes, so
nothing rewrote the field.

Scope as a measured set, not a general claim: across all 81 tracked package.json
files, 57 declare `repository.directory`; 55 resolved to the manifest's own
directory before this change and 57 of 57 resolve after it. The 24 manifests
that declare no `repository.directory` at all are untouched here.

ADR-0041's `## References` section carried the same dead path as a source
pointer and is corrected with it. The rename table earlier in that record
(`plugin-trigger-record-change` -> `@objectstack/trigger-record-change`) is the
decision's own history and is deliberately left as written.

The corrected value ships inside the npm tarball, so it reaches readers only by
being published: a patch changeset accompanies the fix rather than
`skip-changeset`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
@github-actions github-actions Bot added size/s dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation tooling labels Sep 5, 2026
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 2 changed file(s) yielded no anchor (packages/triggers/trigger-record-change/package.json, packages/triggers/trigger-schedule/package.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 2 changed package(s)).

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/triggers/trigger-record-change/package.json, packages/triggers/trigger-schedule/package.json) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 2 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json f50c394da55846da8d38f1e1efadbc24faa26ce2packageMentionDocs.

Copy link
Copy Markdown
Collaborator Author

Clause-② contract review · PR #15993 (card #15478) · domain:services

Tier. CONTRACT_REVIEW_TIER = 'claude-fable-5-1' (scripts/pm/dispatch-gates.mjs:9852). This review ran under an explicit model: fable override attested by the PM seat; tier stated as override + self-report: claude-fable-5-1. No exact-match claim is made.

Independence. The dev was a separate os-dev subagent of the PM session and the dev line is a branch; this is not the self-review case.

Instrument. Own detached worktree /home/user/objectstack-review-15993 at e0fcc1455, merge-base aa6ba0623 re-derived. git status was empty before and after every run; no tracked file was mutated, and none was owed — a 4-line metadata fix with no gate change carries no pin obligation, and I agree with the dev that none is claimed.

Verdict: PASS

The fix, the class census, the sweep, the ADR boundary, the gate characterisation, the changeset decision and the vacuity guard all hold under my own instruments. One sentence of shipped prose is false (§2 below) and wants a one-line correction before undraft. It concerns why the field went stale, not the fix, and the true story strengthens #15991 rather than weakening it — but it ships as CHANGELOG text, so it should not go out as written.

1. Is the class closed? — Yes, re-derived with a stronger property

Own instrument (census-15993-review.mjs, out of tree), population: every tracked file with basename package.json at the ref (git ls-tree -r). It measures the dev's stated property — declared value equals dirname(manifest) — not merely existence, and records existence as a secondary column. It refuses to print unless four controls fire: a known directory reports EXISTS, packages/__deliberately_impossible_15993_review__ reports MISSING, the comparator flags a/c vs a/b/package.json as MISMATCH, and accepts a/b/ (trailing slash) as EQUAL. All four fired on both runs.

ref tracked files package.json declare EXISTS EQUALS dirname mismatch undeclared
merge-base aa6ba0623 7711 81 57 55/57 55/57 2 (the two named, existsAtRef=false) 24
head e0fcc1455 7712 81 57 57/57 57/57 0 24

The merge-base run is the firing control: it names exactly the two card manifests and nothing else. EXISTS and EQUALS agree at both refs, so no manifest points at a directory that exists but is someone else's.

2. Is the sweep complete? — Yes. The causal account is not.

Own git grep -F at both refs, population: all tracked files (7711 / 7712), six spellings:

spelling merge-base head
packages/plugins/plugin-trigger 4 occurrences / 3 files (ADR :194-195, 2 manifests) 1 file — the changeset, quoting the old value
packages/plugins/trigger- (intermediate) 0 1 — the changeset's own prose (line 17), quoting history
packages/triggers/plugin-trigger (hybrid) 0 0
bare plugin-trigger-record-change / -schedule / @objectstack/plugin-trigger ADR history + packages/cli/CHANGELOG.md npm-name release rows same

No third path spelling exists between the two states; the sweep has no hole. (Nit: "intermediate spelling occurs 0 times" is 1 at head — in the PR's own changeset, as a quotation. Not a pointer; noting it for precision only.)

But the two-rename story is wrong on the facts, and the dev's proof of it (ea4941ad8 is 16 files 0/0) covers only the second step. Measured:

  • 998c4e4ea (parent): packages/plugins/plugin-trigger-* exists, directory correct for that path.
  • f15d6f6f6 2026-06-12 07:43 — "feat(approvals): ADR-0042 SLA auto-escalation + ADR-0041 mechanical landing": 26 files, +2222/−19, 0 deletions. It copies the two packages to packages/plugins/trigger-* and leaves the old dirs in place (git ls-tree -d f15d6f6f6 packages/plugins/ lists all four). The copied manifests are byte-identical to the originals except line 2, name, which was edited — so this commit touched the manifest and left directory stale. Not a rename, not content-free.
  • f8f7c549c 2026-06-12 07:49 — fix: remove stale plugin-trigger-* directories left behind by the rename #1751 fix-remove-stale-plugin-trigger-dirs deletes the old dirs (a third same-day commit the account omits).
  • ea4941ad8 2026-06-12 08:09 — pure rename packages/plugins/trigger-*packages/triggers/trigger-*, 16 files, 0/0. ✓ as stated.
  • 9a43e042f 2026-07-21 (docs: update repo references for framework → objectstack rename #3380) "update repo references for framework → objectstack rename": edits repository.url and bugs in both these manifests; the stale "directory" line is unchanged context, one line below the edited url, in a reviewed hunk.

So the sentence "Both were pure directory renames that changed no file contents, so nothing rewrote the field" is false under any reading, and #15991's "no content-touching diff existed for a reviewer to notice" is false twice over — one edit changed the manifest's name, another put the stale line on screen and still missed it. That is the stronger argument for a mechanical gate (a human-reviewed hunk containing the line did not catch it), so the correction costs #15991 nothing.

Required before undraft — in .changeset/trigger-packages-repository-directory-15478.md, replace the last sentence of the "residue" paragraph with something true, e.g.: "The first step (f15d6f6f6) copied the packages and edited only their name; the second (ea4941ad8) was a pure 16-file rename; a later repo-wide url rewrite (9a43e042f, #3380) edited the same repository block and left directory as it was. Nothing checked the field, because nothing reads it." The same correction belongs on #15991 as a comment (the PR body carries the claim too, but the body does not ship).

3. ADR-0041 boundary — correct

## References was a live source pointer with the dead path and is corrected. Lines 46/48/97-100 are the decision's "today → becomes" record, and the "becomes" column is a package name, not a path — it is not a pointer by construction. Nothing reads the table as data: git grep 0041 -- scripts/ .github/ → 0. scripts/check-adr-links.mjs:237 matches only [text](target) markdown links, so the code-span pointer was invisible to it by form, exactly as the dev says.

4. Gate answer — verified, and the sentence #15991 works from is right

  • Population scripts/: 336 tracked files. 245 lines contain repository; the only code-shaped use is release-github-releases.mjs:588 (process.env.GITHUB_REPOSITORY) plus four repository: keys building its owner/repo API context. Zero reads of a manifest repository field. Precision nit: most of the remaining hits are invariant strings in scripts/adr-anchors/*.json, not only "comment prose" — the substance holds.
  • Repo-wide, population 6061 tracked .mjs/.js/.cjs/.ts/.tsx/.yml/.yaml/.sh files: repository.directory / repository["directory"] read 0 times. "The rule never existed" is true on a named set.
  • scripts/check-published-files.mjs: six invariants DECLARED / COMPLETE / SUFFICIENT / MINIMAL / REGISTERED / GATED, all over the files whitelist and exports; the word repository occurs 0 times in the file. Contents gated, provenance metadata not gated is an accurate characterisation. No gate changed in this diff (4 files, none under scripts/). ✓

5. Changeset decision — proved on the artifact

Real npm pack on both packages at head (no dist/ built): 3-entry tarballs, and package/package.json inside each carries repository.directory. The live registry (npm view … repository) shows both at 17.3.0 with the dead packages/plugins/plugin-trigger-* path today. Both are private: false, in .changeset/config.json's fixed group, ignore: []; PR labels are documentation, dependencies, size/s, tooling — no skip-changeset. A patch changeset is the only route by which this fix reaches the 404. ✓

6. Vacuity trap — confirmed in both directions

pnpm --filter '@objectstack/__no_such_pkg_15993_review__' typecheckexit 0, output "No projects matched the filters", no script line. The real filter echoes trigger-record-change typecheck$ tsc --noEmit && tsc --noEmit -p tsconfig.test.json and trigger-schedule typecheck$ tsc --noEmit. My first run was exit 2 on TS2307 (unbuilt @objectstack/core / @objectstack/spec/*) — a prerequisite failure, not reported as a pass; after building the 21-package dependency closure (exit 0), typecheckexit 0, both names echoed, 0 error TS. Each exit captured after a single redirected command.

7. Declared NOT MEASURED — sanity-checked

8. Prose guard

Checked: "entire mismatched set" (named set: 81/57, §1), "0 elsewhere" (named set: 7712 files, §2), "never existed" (named sets: 336 + 6061 files, §4), "pnpm-workspace already lists packages/triggers/*" (pnpm-workspace.yaml:7 ✓), "exactly one path-shaped homepage/bugs/repository.url" (packages/console ✓, over all 81). The one claim that fails is the temporally-phrased one in §2 — the species the brief warned about.


🤖 Generated with Claude Code

https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y


Generated by Claude Code

…and a later commit edited the adjacent `url`

The shipped changeset claimed the stale `repository.directory` survived two
pure renames and that "nothing rewrote the field". Measured, that is false in
three ways, and the true account is a stronger argument for a mechanical check
rather than a weaker one.

  f15d6f6  NOT a rename: a 26-file copy, +2222/-19, with all four
             trigger directories briefly coexisting. Each copied manifest
             differs from its original in exactly one line -- `name` -- so
             this commit edited the manifest and left `directory` stale.
  290c625  deleted the originals five minutes later.
  ea4941a  the only pure rename: 16 files, 0 insertions, 0 deletions.
  9a43e04  six weeks later, rewrote `repository.url` and `bugs` in both
             of these manifests. The stale `directory` line sits as
             unchanged context ONE LINE BELOW the edited `url`.

The last point is the one worth the correction. "Nothing rewrote the field"
reads as the field never having been in anyone's way; in fact an author edited
its immediate neighbour inside the same JSON object and the dead line was on
screen in a reviewed hunk. A rule is more clearly needed when a human looked at
the line and still missed it, so this strengthens the case on the gate card.

No code change: the four-line metadata fix is unchanged and its verification
still stands. This commit corrects prose only, in the file that ships to
release notes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
@os-warren
os-warren marked this pull request as ready for review September 5, 2026 18:23
@os-warren
os-warren requested a review from hotlong as a code owner September 5, 2026 18:23

Copy link
Copy Markdown
Collaborator Author

PM verification — undrafted and armed

Verified at head 6c2957384328dbc25e32ed044ab441fba0682ff4. The four-line fix is byte-identical to the reviewed head (both manifests' blobs unchanged), with the changeset as the firing control in the same comparison. The false sentence is gone (0 occurrences of "nothing rewrote the field"), 9a43e042f is named, and check-adr-0087-registration exit 0.

⭐ The correction is better than the correction I asked for

I asked for three facts. The rewritten paragraph found a fourth commit and reframes the whole account as a three-commit sequence on 2026-06-12: f15d6f6f6 copied the packages (26 files, +2222/−19, all four directories briefly coexisting) and edited exactly one line of each copied manifest — its name — leaving directory pointing at the path it was copied from; 290c62514 deleted the originals five minutes later; and only ea4941ad8 was the pure 16-file rename, which broke a second segment. Then six weeks later 9a43e042f rewrote repository.url and bugs in these same manifests, with the stale directory line as unchanged context one line below the edited url.

And it ends where the card should:

So the field was not merely never in anyone's way: one commit edited its immediate neighbour inside the same object, and a later reviewed hunk had the wrong line on screen. Nothing caught it because nothing reads it.

⇒ That last sentence is the gate finding stated in eight words, and it is now in the artifact that ships.

⭐ The scope paragraph is also stated as a measured set rather than a general claim — 81 tracked manifests, 57 declaring the field, 55 → 57 of 57 resolving, 24 undeclared and deliberately left to a policy question. That is the shape I have been asking for all session and it arrived without being asked for here.

⭐ And it declined a correction it could not reproduce

The reviewer's second nit attributed most repository hits under scripts/ to scripts/adr-anchors/*.json invariant strings. The dev measured 1 of 245 lines there; the real concentration is 27 lines in scripts/engine-double-contract.pinned.json, and they are incidental filename substrings (sys-metadata-repository-…test.ts). ⇒ It replaced the sentence with the measurement itself rather than "swapping one imprecise characterisation for another."

That is the right response to a reviewer being partly wrong: the reviewer's substance was right (the dev's "comment prose" was inaccurate), its specifics were not, and adopting the specifics on authority would have shipped a second false sentence in the act of fixing the first. ⛔ The load-bearing claim — 0 reads of repository.directory across 6061 code/config files — is untouched and independently re-confirmed.

It verified the reviewer before accepting it

All three findings re-derived with its own instruments: git ls-tree -d showing all four trigger directories coexisting (proving copy, not rename); the copied-vs-original manifest diff being exactly one line, "name"; and the 9a43e042f hunk showing the stale directory as unchanged context one line below the changed url.

One tooling note worth keeping

dispatch-gates.mjs flagged STALE TREE at the correction commit — naming scripts/engine-double-contract.pinned.json as a changed derivation input — and said it "still exited 0 — a well-formed answer about a tree nobody is on." ⭐ A tool that returns a valid answer and tells you it is about the wrong tree is doing something most gates do not: separating "this computation succeeded" from "this computation is about your situation." The merge was needed, not optional, and the tool is why that was knowable.

Nit 1 fixed as asked: stated as 0 at merge base, 1 at head, that one being the changeset's own quotation.

Gates on the merged head: 20 path-derived + whole-tree nul-bytes exit 0; check:doc-formula-expressions first exit 3 = PREREQUISITE NOT MET on a fresh worktree, built, re-run exit 0 — ⛔ not reported as a pass while it was a 3; check:dts-closure 164/164 across 72 packages; check:dual-build-cjs-loads 103 require entry points across 66 packages.

The correction is also posted on #15991, so that card does not carry the false account into whoever takes the gate work — and it strengthens the case there: a rule that only fires on directory moves would have missed 9a43e042f entirely, since that commit moved nothing.

Undrafted and auto-merge armed.


Generated by Claude Code

@os-warren
os-warren enabled auto-merge September 5, 2026 18:23

Copy link
Copy Markdown
Collaborator Author

CI red — measured NOT this PR's

Lint & Repo Gates is the only failing job on head 6c2957384 (run 33983205576, job 101352067958). Every other job in that run is green: Type Check · workspace, · source gates, · consumer gates, · debt ledger, and the TypeScript Type Check rollup.

The failing step, named: step 141 Merge-driver wiring gatepnpm check:merge-drivernode scripts/check-regen-pending.mjs --self-test✗ self-test failed -- 1 failure(s) (cases and floor).

The control that settles ownership. The identical step fails on main itself at f7db8f4fd — run 33981169123, job 101346594800, step 141, same name, same closing line. A check that is red on the base branch is not this PR's, and this PR touches neither scripts/check-regen-pending.mjs nor scripts/git-merge-regen.mjs nor .gitattributes (its whole diff is two package.json repository.directory values, one ADR reference line and a changeset).

Where it is owned. Already filed by other seats, ahead of this comment: #15992 (p0, domain:devx, pm:dispatched), plus #15990 and #15994 on the same failure. #15992 records the cause as the self-test's gate stubs shelling out to an unpinned pnpm -s, which Corepack now resolves to pnpm latest = 12.3.4, a version that rejects -s. ⚠️ That diagnosis is quoted from #15992, not measured here — what is measured here is only that the step fails identically on main.

Why no fix is ported into this PR. The rule I work under says to port an existing fix rather than wait. It does not apply here, and the repo's own gate is why: scripts/check-regen-pending.mjs is a single-writer path, and No other open PR may claim the same single-writer path is a required check on this PR — carrying that file here would red the very gate that is currently green, and collide with #15992's fix PR. So this PR waits for #15992 to land on main and then takes it by merging main.

Why no re-run. An unpinned launcher resolving to a version that rejects a flag is deterministic, not a flake. Re-running would reproduce it. The one re-run this PR is entitled to is left unspent.

Local, for what it is worth as context rather than as the argument: node scripts/check-regen-pending.mjs --self-test passes here at origin/main f7db8f4fd (git 2.43.0, exit 0, 0 ), so the failure is environment-bound to the CI launcher — consistent with #15992's account, and the reason it cannot be reproduced by reading the diff.

State of this PR: contract review returned and its corrections are in (06409ff97). Nothing here is waiting on the author. It is waiting on main going green.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Base branch recovered — taken, as the earlier comment said it would be.

main is green again at f50c394da — run 33986517218, conclusion success. The Merge-driver wiring gate is fixed at its source by #16002 (for #15992): the self-test's throwaway fixture carried no packageManager, so Corepack re-resolved the registry's latest dist-tag every run, and when latest moved to a pnpm major whose CLI rejects -s, the stub exited 2 without running. ⭐ That is why the same self-test exited 0 locally while red in CI — a different pnpm answered the fixture, so the local reading was never the control.

Merged origin/main into this branch and pushed: 6c295738434b232a73. No rebase, no force-push.

Verified the merge did not disturb what was reviewed — all four of this PR's files byte-identical, with a firing control:

.changeset/trigger-packages-repository-directory-15478.md      SAME
docs/adr/0041-flow-trigger-family.md                           SAME
packages/triggers/trigger-record-change/package.json           SAME
packages/triggers/trigger-schedule/package.json                SAME
scripts/check-regen-pending.mjs                                DIFFERS  ← the control

git merge reported no conflict, a whole-tree conflict-marker scan found none, and node scripts/check-regen-pending.mjs --self-test on the merge product exits 0.

Auto-merge was already armed (18:23:59Z) and stays armed. Nothing here is waiting on the author.


Generated by Claude Code

@os-warren
os-warren added this pull request to the merge queue Sep 5, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/s tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Two published trigger packages declare a repository.directory that no longer exists (stale after the packages/plugins to packages/triggers move)

2 participants