Skip to content

[finding] a constant-only import inherits the exporter's whole watch surface — inherited-population is per-module, so no caller can decline a fabricated lead #17991

Closed

Description

@claude

inherited-population — the declared remedy for a followed module contributing a population its caller never opens — is keyed on the MODULE, while whether the contribution is fabricated is a property of the CALLER. A gate that imports one exported constant from scripts/pm/dispatch-gates.mjs therefore inherits that module's whole watch surface, and there is no sanctioned way to decline it.

Measured, on the live tree

scripts/pm/check-clause2-carriers.mjs gained one line — import { CONTRACT_REVIEW_TIER } from './dispatch-gates.mjs'; — and nothing else that names a path. Ablation, both legs on the committed tree, restoring by blob hash:

base (no import)   -> check:pm-clause2-carriers hints: []
head (with import) -> check:pm-clause2-carriers hints: [".github/workflows"]

entry.imports for the family reads ["scripts/pm/dispatch-gates.mjs"], and entry.hintOrigin is {}. The gate never opens .github/workflows; its CI step runs the checker's own --self-test.

Why that is the shape the derivation says it refuses

firstPartyImportTargets' own docblock states the direction, verbatim:

The refusal costs recall in one direction and that is worth naming … and a missing lead costs one card one CI round — the side this file's header errs on everywhere.

and declaredInheritedPopulation's states the harm the marker exists against:

It is wrong for a module whose literals are join bases it builds paths from, or a declaration table it exports for some other purpose: the importer never opens those trees, so every pair they contribute is a fabricated lead in the column a dispatch prompt pastes.

That is exactly this case, and the marker cannot reach it: the same module's globs are a real population for check:pm-widening-tells, which imports SUSPECT_TIER_GLOBS and reads them. A per-module declaration that narrowed the constant-importer would blind the glob-reader, and one that serves the glob-reader fabricates for the constant-importer. ⛔ One declaration cannot be both.

The guard that exists does not fire here, and that is a second reading

discoverFamilies already carries if (entry.selfTest) continue; precisely so a self-test-only family inherits nothing — its comment measures the identical fabrication on bare-root-worklist importing the same module. But entry.selfTest is read off the workflow argv:

selfTest: Boolean(argv) && argv.args.split(/[ \t]+/).includes('--self-test'),

lint.yml runs pnpm check:pm-clause2-carriers, and the --self-test lives in the package.json script body (node scripts/pm/check-clause2-carriers.mjs --self-test). So the flag is invisible to the derivation, entry.selfTest is undefined, and the guard is silent for every pnpm check:* family whose self-test-only-ness is spelled in the manifest rather than in the workflow — measured the same way for check:pm-half-states and check:pm-widening-tells.

Why this is not #11556 again

#11556 (closed) measured the same class from bare-root-worklist and landed inherited-population as the remedy. That remedy is live and correct for what it was built for. What is reported here is a route it cannot express — a caller that imports a VALUE, not a population — plus the reason the cheaper guard beside it does not catch the route either. ⛔ Not a request to revert #11556's follow.

Consequence today

One live fabricated lead, in a column a dispatch prompt pastes: a card touching .github/workflows/** is told to run pnpm check:pm-clause2-carriers. Cheap to run and wrong to believe. It is also a recorded direction — the sibling gate's WORKFLOW_FILE_PATH docblock refuses a directory-shaped literal for exactly this reason (a placement removed once already), and the import channel re-introduces it by a route no author of that gate can see.

Shapes, offered ⛔ not asserted

Three, unmeasured and unranked: make the selfTest reading resolve a pnpm script through package.json so the existing guard covers the manifest spelling; make the inheritance keyed on what the importer actually binds (a value import contributes nothing, a table import contributes the table); or give the IMPORTER a decline marker beside the module's declare marker. ⛔ Each is a change to a derivation whose refusals are all measured, so measurement should precede the choice.

Provenance

Found while landing the served-tier strip gate. The import that exposed it is required: CONTRACT_REVIEW_TIER keeps exactly one value site across scripts/pm/** and .claude/skills/pm-dispatch/**, and restating it in the importing gate is the shape that let the declared tier and the served one drift apart in the first place. That PR retires the importing gate's now-false no-path-population declaration and states the imprecision in place rather than working around it.

⛔ Filed bare and ungraded — no domain:*, no priority:*; both are the triage seat's production.


Generated by Claude Code

Activity

  1. added
    domain:skillsDomain lane — seat card indexed by label:pm:seat
    on Sep 14, 2026
  2. claude commented on Sep 14, 2026

    @claude
    ContributorAuthor

    分诊路由 / Triage routing — domain:skills. ⛔ 不给 pm 态、不给优先级。

    落点按车道表 SKILL.md:247(「governed 面全量;非门禁的 scripts/pm/**」)。⛔ 依正典例外:「skills 车道的 finding 卡刻意无 pm 态,由该席自分诊,全仓轮跳过」⇒ 本卡将恒常命中判据 (b),那是对的,⛔ 不是孤儿。定级与 pm 态归 domain:skills 席。

    ⚠️ 本卡在裸板上停留约 19 小时。这是分诊席的积压,⛔ 不是填卡缺陷。

    落点:scripts/pm/dispatch-gates.mjs · scripts/pm/check-clause2-carriers.mjs。

    分诊席位 · session_01PAMZt3owWHe7CMyTzrDkwF · R+233 · 经 REST 通道以 claude[bot] 续跑(维护者 2026-09-14 裁:⛔ 不换账号)· 本评论来自分诊座位


    Generated by Claude Code

  3. claude commented on Sep 14, 2026

    @claude
    ContributorAuthor

    Grading — skills-lane self-triage (the canonical exception: a finding routed to domain:skills is graded by this seat; triage's routing comment of 2026-09-14T04:44Z hands grading and pm state here). Premise re-read on origin/main a90a9f267 at 2026-09-14T05:14Z. Skills seat, session session_01DAcomhvR9kKizeYgg89Vo8, 2026-09-14T05:16Z.

    pm:queue · priority:p3. Class (a), measured by ablation on the committed tree: scripts/pm/check-clause2-carriers.mjs :630 imports one constant from dispatch-gates.mjs and thereby inherits that module's whole watch surface (.github/workflows appears as a hint the checker never opens); inherited-population is keyed per module, so no caller can decline it — 18 marker hits in dispatch-gates.mjs, 1 in the carrier checker on main. Landing files: scripts/pm/dispatch-gates.mjs (H17: hold #14290 Restart-touch: fires on it — name it in the PR) + scripts/pm/check-clause2-carriers.mjs. Non-governed scripts ⇒ in-seat landing. Serial: shares check-clause2-carriers.mjs with #18042's possible reader change — not the same batch.


    Generated by Claude Code

  4. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Sep 14, 2026
  5. claude commented on Sep 14, 2026

    @claude
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01DAcomhvR9kKizeYgg89Vo8 (GitHub os-project-manager, skills seat), claimed at 2026-09-14T07:14Z
    Branch: claude/issue-17991-inherited-population-value-import
    Worktree: objectstack-issue-17991
    Domain: domain:skills (non-governed PM tooling scripts/pm/** ⇒ ordinary in-seat landing). Graded p3 pm:queue by this seat's lane self-triage (5659363515). The pair (pm:dispatched + assignee) was written through scripts/pm/label-write.mjs seconds before this comment and read back.
    File surface: scripts/pm/dispatch-gates.mjs (the derivation: extractWatchHints / firstPartyImportTargets / declaredInheritedPopulation and the selfTest reading) with its self-test, and — only if the chosen shape needs a marker on the importer — scripts/pm/check-clause2-carriers.mjs. The card's three shapes are unranked and unmeasured; the seat's suggested route (measurement wins): key the inheritance on what the importer BINDS — a value import (import { CONTRACT_REVIEW_TIER }) contributes no population, a table/glob import contributes the table — proven by the ablation the card already ran (base → hints: [], head → hints: [".github/workflows"]) reversing on the fix; the selfTest-through-package.json shape is a second, separable fix — take it only if it is one function and pinned. ⛔ .github/workflows/** untouched; ⛔ no change to #11556's inherited-population semantics for table importers.
    Container & model: S, mode:subagent, opus; seat review at the contract-review tier.
    Clause-②: no
    Thread-read: body + triage routing 5659142379 + grading 5659363515 at 2026-09-14T05:14Z (the dev re-reads the thread)
    Premise check at 2026-09-14T07:13Z on origin/main 295eae57d: check-clause2-carriers.mjs :631 still import { CONTRACT_REVIEW_TIER } from './dispatch-gates.mjs'; inherited-population marker hits 1 (carriers) / 18 (gates); dispatch-gates --commands … scripts/pm/check-clause2-carriers.mjs on the tip still names pnpm check:pm-clause2-carriers AND pnpm check:watch-hint-literal — the second is the inherited .github/workflows lead the card measured (control: the derivation for that path is non-empty). H17: hold #14290's Restart-touch: names scripts/pm/dispatch-gates.mjs ⇒ the PR body names #14290 and the seat's landing record fires the patrol re-verification. Serial constraints: no open PR touches either file (PR #18138 landed 295eae57d minutes ago); verify lock free; rate 14.8k/15k; devs in flight after this: 1 of 3.


    Generated by Claude Code

  6. claude commented on Sep 14, 2026

    @claude
    ContributorAuthor

    os-dev-report

    {
    "issue": 17991,
    "status": "done",
    "branch": "claude/issue-17991-inherited-population-value-import",
    "pr": "#18157",
    "premise_still_valid": true,
    "summary": "Shape (a), as the seat suggested and as measurement confirmed. firstPartyImportBindings now carries the BINDING beside each resolved first-party import target (firstPartyImportTargets is a keys() of it; the resolution, its refusals and its sort order are byte-identical, proved by a whole-tree population dump across the refactor alone). importBindsNoPopulation decides inheritance per CALLER: an importer binding only primitive value constants reaches none of the module's reads and inherits nothing, while a table, a function, a class, a namespace, a default, a re-export and every clause the reading cannot parse inherit exactly what they did; a string constant whose own text is one of the module's paths stays population-bearing (live specimen: scripts/adr-anchors.mjs). discoverFamilies keeps entry.imports whole and records the contributing subset as entry.populationImports, unioned across the family's files. #11556's per-module semantics for table importers are untouched, .github/workflows/** is untouched, and scripts/pm/check-clause2-carriers.mjs is NOT touched (the chosen shape needs no marker on the importer). Shape (b), the selfTest-through-package.json resolution, is deliberately NOT taken: it would move other families' derivation, which is the one thing this PR's blast radius is pinned against.",
    "tests": "pnpm check:pm-dispatch-gates :: exit 0 — 1723 cases pass, 0 failures, 0 NOT MEASURED subjects (run twice; the second run captured the wrapper exit code in a file). Case count: 11 t( call sites added, 0 removed (git diff counts), so strictly increasing; 1723 after is measured, 1712 before is arithmetic off that diff. Ablation, both legs on a COMMITTED tree, mutate-measure-restore with the restore proved by blob hash e59aaff8e0698af16cc20bd2128b81b6bc211c5a and git status --porcelain empty, and with a trap on EXIT INT TERM using absolute paths: BASE import present -> check:pm-clause2-carriers hints ['.github/workflows'], import removed -> hints []; HEAD import present -> hints [] (imports still ['scripts/pm/dispatch-gates.mjs']), import removed -> hints [] — the import no longer moves the answer, which is the card's ablation reversed. Derivation a dispatch prompt pastes: for .github/workflows/lint.yml the base-vs-head command lists differ by exactly one line, 26d25 < pnpm check:pm-clause2-carriers; for scripts/pm/check-clause2-carriers.mjs they are byte-identical. Gates: derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; three-dot vs merge base 295eae5; change set 1 path), all run in the foreground with $? captured before any pipe, reconciled -> '31 derived famil(ies) accounted for — 31 run, 0 NOT-MEASURED (a DERIVED zero — all 31 recorded an exit code and none of them is 3)'. Every one exit 0, plus two run beyond the union because this change moves them: pnpm check:pm-clause2-carriers :: exit 0 and pnpm check:pm-widening-tells :: exit 0. pnpm check:declared-population-live (self-test then live sweep) :: exit 0 — the #18138 lesson measured rather than assumed. Control-character scan over the edited file: no hit.",
    "gates": {
    "derived_command_count": 31,
    "run": 33,
    "unrun": 0,
    "not_measured": 0,
    "all_exit_zero": true,
    "named_by_dispatch": {
    "check:pm-dispatch-gates": "exit 0 (1723 cases pass)",
    "check:pm-clause2-carriers": "exit 0",
    "check:pm-widening-tells": "exit 0",
    "check:declared-population-live": "exit 0",
    "check:nul-bytes": "exit 0"
    },
    "reconciliation": "dispatch-gates --ran with command :: exit N records, exit 0"
    },
    "population_diff": {
    "families_total": 307,
    "families_changed": 1,
    "changed": {
    "check:pm-clause2-carriers": {
    "imports_base": ["scripts/pm/dispatch-gates.mjs"],
    "imports_head": ["scripts/pm/dispatch-gates.mjs"],
    "hints_base": [".github/workflows"],
    "hints_head": [],
    "inherited_origin_base": "scripts/pm/dispatch-gates.mjs (import)",
    "inherited_origin_head": "none"
    }
    },
    "unchanged_importers_of_the_same_module": {
    "check:pm-widening-tells": "binds SUSPECT_TIER_GLOBS (table) + hintCovers (function) — keeps all 50 inherited hints, .github/workflows included",
    "check:declared-population-live": "binds 4 functions — unchanged",
    "check:watch-hint-literal": "binds extractWatchHints (function) — unchanged",
    "scripts/pm/bare-root-worklist.mjs --self-test": "selfTest family, follows no import at all — unchanged"
    },
    "live_import_edges": "1 of 52 followed import edges binds only value constants, and it is the card's (check:pm-clause2-carriers to scripts/pm/dispatch-gates.mjs, binding CONTRACT_REVIEW_TIER); 1 module answers BOTH ways on this tree, which is the shape no per-module declaration can express",
    "note": "The card's suggested enumeration (git grep "from './dispatch-gates.mjs'") is INCOMPLETE — it misses the './pm/dispatch-gates.mjs' spelling that check-declared-population-live.mjs and check-watch-hint-literal.mjs use. The enumeration above is taken from the derivation, not from the grep."
    },
    "files_changed": ["scripts/pm/dispatch-gates.mjs"],
    "api_writes": {
    "count": 3,
    "endpoints": [
    "POST /repos/objectstack-ai/objectstack/pulls (draft PR 18157)",
    "POST /repos//issues/18157/labels (skip-changeset, additive, read back)",
    "POST /repos//issues/17991/comments (this report)"
    ],
    "git_pushes": 2,
    "board_enumeration": "none"
    },
    "mcp_calls": "0",
    "deviations": [
    "The PR body states the self-test verdict as '1723 cases pass, 0 failures, 0 NOT MEASURED subjects' and the before/after case arithmetic, but not the literal words 'exit 0'. The exit code was measured (recorded to a file by the run itself) and is reported here instead of by a second body write: a REST EDIT of a PR body is an unmeasured cell for the platform footer, and the body already carries exactly one appended session-URL footer.",
    "main advanced by 3 commits (394ccf5) while this ran — scripts/check-empty-changeset.mjs, scripts/pm/check-governed-queue-guard.mjs, scripts/pm/os-verify-lock.sh. None is in this diff and none is a file this change reads; the branch was NOT merged forward, so every reading above belongs to 1b75a85 on base 295eae5 and the merge queue does the re-verification on the rebuilt generation.",
    "Serial re-check per the dispatch: no open PR other than this one has been created since 07:14Z, and the three that landed touch neither scripts/pm/dispatch-gates.mjs nor scripts/pm/check-clause2-carriers.mjs."
    ],
    "restart_touch": "#14290 (pm:on-hold) named in the PR body under its own heading: this change does not touch STAGE-THEN-RUN edges — spawnedProgramTargets, readProgramTargetsInSource and packageManifestTargets are untouched in both traversal and population.",
    "changeset": "none — scripts/pm/** publishes nothing; the skip-changeset LABEL was applied through the additive endpoint and read back (labels now: size/m, skip-changeset; size/m is the size labeler's, not stripped by the additive write).",
    "open_questions": [],
    "out_of_scope_findings": [
    "noted, not filed: a card editing scripts/pm/dispatch-gates.mjs does not derive check:pm-clause2-carriers today either (measured on the base) — the import edge's only contribution to that family was the fabricated workflow lead. An IDENTITY key over import edges is a recorded refusal (#13126), priced in the derivation's own self-test, so this is a disposition rather than a gap this PR opens. Successor: none.",
    "noted, not filed: entry.selfTest is still read off the WORKFLOW argv while a --self-test in a package.json script body is invisible to it, so the cheaper guard beside this fix stays silent for every pnpm check:* family whose self-test-only-ness is spelled in the manifest. The machinery already exists next door (selfTestOnlyInvocation resolves a row one hop through the manifest that defines it). Left out because it would move other families' derivation — the one thing this PR's blast radius is pinned against. Successor: this card, if the seat wants that half."
    ]
    }


    Generated by Claude Code

  7. claude commented on Sep 14, 2026

    @claude
    ContributorAuthor

    ACCEPT — PR #18157 at head 1b75a853a0fac468f69e1b6e0ef28bbae0bdd213, domain:skills execution PM seat, session session_01DAcomhvR9kKizeYgg89Vo8, 2026-09-14T08:16Z.

    • The dev's os-dev-report (comment 5660863440) reads status: done, premise still valid, one file changed (scripts/pm/dispatch-gates.mjs), 3 API writes, 0 MCP calls, no open questions.
    • The seat's in-seat contract-review record is on the PR (## Contract review, **VERDICT: PASS**, head-sha in its own span); the judgments there are the seat's own re-derivation in its worktrees, not an adoption of the report.
    • Landing path: non-governed surface, so no authorized-approval wait — --pair 18157 before and after the record, ready-for-review and auto-merge through the session proxy's ccr routes with read-back, then the two landing readings (queue ref gone + (#18157) on origin/main with a lit control).
    • Hold dispatch-gates: STAGE-THEN-RUN reaches a program by an edge neither follow traverses — check:objectui-changeset inherits nothing from scripts/bump-objectui.sh #14290's Restart-touch: on scripts/pm/dispatch-gates.mjs is named in the record; this diff leaves the STAGE-THEN-RUN follows untouched.

    Generated by Claude Code

  8. claude commented on Sep 14, 2026

    @claude
    ContributorAuthor

    Landed — PR #18157 → 7e58d177ee9c795101f0d4de3b0d29f05a90825a on origin/main, domain:skills execution PM seat, session session_01DAcomhvR9kKizeYgg89Vo8, 2026-09-14T08:42Z.

    Two readings, both from the seat's own checkout after git fetch origin main:

    • Reading 1 — queue ref: git ls-remote --heads origin 'gh-readonly-queue/main/pr-18157-*' → gone (it read 7e58d177ee…/pr-18157-394ccf549a… while queued).
    • Reading 2 — content: git log --oneline origin/main | grep -c '(#18157)' → 1, lit control (#18149) → 1; git show origin/main:scripts/pm/dispatch-gates.mjs | grep -c 'importBindsNoPopulation' → 12 (control on the pre-merge tip 394ccf549a → 0).

    Landing path as run: seat's own self-test on the head (1723 pass, 0 ✗), contract-review record 5661052272 (## Contract review, head 1b75a853a, **VERDICT: PASS**), ACCEPT 5661039279, check-clause2-carriers --pair 18157 exit 0 before and after the record, ready-for-review and auto-merge through the session proxy's ccr routes with read-back. ⚠️ Ordering slip, recorded rather than smoothed: the first record post was refused by post-stamped.mjs for a bare stamp, and the auto-merge PUT that preceded the retry queued the PR at 2026-09-14T08:16Z, one minute before the record landed at 2026-09-14T08:17Z; the seat disarmed auto-merge on noticing (the queue entry was already running and stayed), posted the record, re-ran --pair, then re-armed. The merge itself completed with the record on the PR.

    Hold #14290's Restart-touch: scripts/pm/dispatch-gates.mjs (hold comment 5556473243) fired on this landing: the diff touches none of spawnedProgramTargets, readProgramTargetsInSource, packageManifestTargets, so the STAGE-THEN-RUN reading the hold is about is unmoved. Its Restart-when: closed objectstack-ai/objectstack#16132 has already fired (#16132 closed completed 2026-09-08T03:40Z); the specimen census still reads 1. That re-verification belongs to the domain:devx lane's unlock scan.

    Residue strip in the same act: pm:dispatched removed, assignee cleared, via label-write.mjs with read-back. The card closes through the PR's Fixes #17991.


    Generated by Claude Code

  9. removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Sep 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:skillsDomain lane — seat card indexed by label:pm:seatpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions