Skip to content

[finding] dispatch-gates cannot route a card that ADDS a generator to check:merge-driver — its population is the artefacts already routed, so the 'no recorded merge disposition' clause only ever fires in CI #15501

Description

@claude

Filed by the PM seat domain:devx @ objectstack (#6023), session session_012zGPuVVX3deAx9LdjK8jCk, from a measured CI red on PR #15492 (#14957). Filed finding, domain:devx; priority is triage's, but the cost is one full rework cycle per new generator, so it is not low.

What happened

PR #15492 added a new generator — a gen:platform-object-tenancy-census row in package.json, scripts/platform-object-tenancy-census.mjs, its committed artefact and its gate — and ran the derived family for its change set (78 commands, all run). CI then went red on check:merge-driver (git-merge-regen --self-test):

✗ generator(s) with NO recorded merge disposition:
  gen:platform-object-tenancy-census   [@objectstack/spec-monorepo]
  Every generator must be in ONE of the two ledgers in scripts/regen-artifacts.mjs.

The gate is right. The derivation could not have named it.

The gap, measured

scripts/pm/dispatch-gates.mjs places check:merge-driver through the path literals scripts/regen-artifacts.mjs declares (the hint-import widening records it: "check:merge-driver (+264, through regen-artifacts.mjs, which declares the artifacts the driver regenerates"). That population is the artefacts already routed — 17 paths today. A card that ADDS a generator touches package.json (a new gen: row) and a new scripts/*.mjs + artefact, none of which is in that population until the card itself adds the row it is being asked to add. Measured on bcf07e622:

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack -- package.json scripts/platform-object-tenancy-census.mjs
→ 0 lines mentioning merge-driver

So the one class of change git-merge-regen --self-test's "every generator must be in ONE of the two ledgers" clause exists to catch — a new generator — is exactly the class the derivation never routes to it. The gate fires only in CI, a cycle late, on every card of that shape.

What a fix owes (direction, not prescription)

  • Declare the gate's OTHER population: check:merge-driver also reads package.json (and packages/*/package.json for the declared owners) for gen: / check: script names. A ROOT_FILE_WATCH_HINTS / declared-hint entry naming those manifests would route any card touching a manifest to the gate — cheap (the self-test is ~1 s) and it is the population the failing clause actually sweeps.
  • Or a narrower rule in the derivation: a diff that adds a gen: row to any package.json derives check:merge-driver. Whoever takes it should measure the false-positive cost of the manifest-wide hint first (how many cards touch package.json for an unrelated reason) and pick.
  • Either way, the population declaration is the fix; ⛔ not editing the self-test to pass, and ⛔ not a hand list of generator names (that is the ledger the gate already owns).

Related: #15414 / #14918 (the same "a clause scoped to a population, with the run saying so nowhere" shape, on other gates); PR #15492 (the instance; the dev's rework adds the REGEN_ARTIFACTS row + .gitattributes line).


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions