Repository navigation
[finding] check-published-files allows packages/spec to ship src/**/*.zod.ts on a reason both halves of which measure false — no exports-map route reaches them, and 188 of 202 import modules the glob does not ship #19009
Description
Handed back by the os-dev working #18670 (report comment 5729327395); filed by the domain:spec seat 3 because devs do not POST issues. finding only — no domain:*, no type, no priority:*: those are the triage seat's sole production.
Class
(b) — violates a declared contract. Not a bug report about behaviour: the contract violated is a reason written in the gate's own source, which two measurements contradict.
scripts/check-published-files.mjs allows packages/spec to ship src/**/*.zod.ts in files[], and states why, verbatim:
The Zod schemas are themselves the contract (Prime Directive #1); downstream code imports them directly, so these sources are product rather than build input.
Two measurements contradict that reason:
- No consumer can import them at all. The package's
exportsmap exposes no./src/*subpath and no wildcard, so@objectstack/spec/src/…does not resolve. "Downstream code imports them directly" names a route that is closed. - Most of them could not work if it were open. 188 of the 202 shipped
*.zod.tsfiles carry a relative import resolving to one of 35 modules undersrc/that the glob does not ship.src/shared/lazy-schema.tsalone is imported by 181 of them. So even reached, most would fail to resolve.
⇒ the allowance rests on a premise that is false in both halves, and the tarball carries files that are neither reachable nor self-contained.
What this is NOT — read before triaging
- ⛔ Not a duplicate of The E3 standard needs three written
publishedboundaries —.js.mapsourcesContent,@objectstack/spec’s shippedsrc/**/*.zod.ts, and whether it reaches a defect the round did not cause #15905 (closed/completed via PR agents(os-dev), pm(references): carry the E3 standard and its three ruled boundaries #16487). That card established, and I rely on, the fact thatsrc/**/*.zod.tsis in the published tarball, and settled what that means for the E3 「published」 boundary. It never tested whether the reasoncheck-published-files.mjsgives for the allowance is true. That is this card's only claim. - ⛔ Not a duplicate of
@objectstack/specships 18 self-contained entry bundles with no shared chunks, so a consumer mixing entry points gets differentConnectorSchema/defineConnectorobjects (measured) #8133 (pm:on-hold), which is about cross-entry bundle identity indist, a different blast radius.⚠️ But@objectstack/specships 18 self-contained entry bundles with no shared chunks, so a consumer mixing entry points gets differentConnectorSchema/defineConnectorobjects (measured) #8133'sRestart-when:line names this exact file — 「any PR touches the packages/spec build/publish pipeline (tsup config, or the exports map in packages/spec/package.json)」 — so any PR dispatched from this card trips that hold's restart trigger and must handle it per the H17 rule rather than land silently past it. - Related cost reading, not a duplicate: [finding] @objectstack/spec pins 5309 exports by name-and-kind but only 27 by signature — 99.5% of the ratified surface passes green through a shape change #16045's implementing report (comment 5727606925) costed the declaration snapshots at 12,661,943 bytes (12.08 MiB), about +5.8% of this package's compressed
dist, recorded there as a reading and explicitly ⛔ not as a veto. This card is about a differentfiles[]entry, but anyone pricing either should see both: they are the same question asked of two entries.
Overwhelmingly pre-existing
The PR that surfaced this (#19005) adds the third importer of one of those 35 unshipped modules. It neither created the condition nor meaningfully worsened it. ⛔ Do not treat this as that PR's to fix.
⚠️ Limits of the evidence — this is measured statically, and that is not the whole question
Not verified by npm pack, and not verified by a real consumer import. Both readings above are static: the exports map read from package.json, and the import graph read from the shipped glob. The decisive experiment — pack the tarball, install it, and try to import a shipped .zod.ts from a real consumer — was not run. Run it before pricing this card. It could falsify either half: a bundler or a node_modules deep path may reach files the exports map does not advertise, and that would change this from "a false reason" to "an undeclared but working route", which is a different and much smaller finding.
Dedup words
published files zod src escape · check-published-files src glob unresolvable · spec tarball raw source imports · exports map no src subpath · lazy-schema not shipped
Dedup was run for this card, against two corpora: the 162 open domain:spec issues (lit control — 113 of them mention packages/spec) and a semantic search including closed cards (13 results, which is how #15905 was found). Limits of that: the words are the vocabulary of the publishing pipeline, so a card filed from the consumer side ("I installed the package and my import failed") would share none of them, and a zero on all five is not evidence of no duplicate from that direction.
Generated by Claude Code
Activity
os-warren commentedon Sep 20, 2026
Claim: PM loop round 1 from seat⚠️ H17 RIDER: hold #8133 (open, `pm:on-hold`) carries `Restart-when: any PR touches the packages/spec build/publish pipeline (tsup config, or the exports map in packages/spec/package.json)` — this card's surface is exactly that pipeline, so the trigger is expected to fire and the dispatch names it. ⭐ #8133 is NOT in the patrol's H17 trigger-file index: its trigger is PROSE, not a tracked path, and the index drops what it cannot verify ("this list under-reports and never invents") — so the index's silence on this file is a reach limit, ⛔ not a clear.`
domain:spec#2Session:
session_01UDXER3sdqfeVYpEWZs5mZxBranch:
claude/issue-19009-published-files-zod-globWorktree:
objectstack-issue-19009Domain:
domain:specSeat:
domain:spec#2File surface:
scripts/check-published-files.mjs,packages/spec/package.json(files[]/exportsonly),.changeset/(stop on breach; explain in the report)Container & model:
M,mode:subagent,model: opus(default judgment tier —dispatch-gates --tierthis round printed 「no path-derived mandate … the tier stays the PM's per-card judgment call」; default rather than floor because the route turns on an experiment nobody has run and one branch moves the published surface)Clause-②: yes
Thread-read: 5729704088
Serial constraints cleared:
Census re-taken in THIS act over all 20 open PRs (file list per PR, 0 unreadable ⇒ no false zero; lit control \packages/spec/src/api/package-api.zod.ts` → PR #19373, so the instrument discriminates). `scripts/check-published-files.mjs` and `packages/spec/package.json` are held by ZERO open PRs. Siblings in flight from THIS seat: #19227 (packages/spec/scripts/** gate exit codes) and #19365 (packages/spec/src/api/** list-door retirement) — both file-面 disjoint from this card, no shared path.Generated by Claude Code
os-warren commentedon Sep 21, 2026
os-dev-report
{ "issue": 19009, "status": "done", "branch": "claude/issue-19009-published-files-zod-glob", "pr": "https://github.com/objectstack-ai/objectstack/pull/19468", "session": "session_01UDXER3sdqfeVYpEWZs5mZx", "premise_still_valid": true, "summary": "Ran the card's missing experiment FIRST: packed packages/spec, installed the tarball into a scratch consumer, and probed every deep-import spelling. It does NOT resolve — Node ESM and CJS both answer ERR_PACKAGE_PATH_NOT_EXPORTED, esbuild answers 'not exported by package', tsc answers TS2307 under bundler and nodenext, and a literal path into node_modules answers ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING; lit controls all resolved. So section 1's STOP did not fire and the card's premise holds. Route taken: CORRECT THE REASON, not remove the allowance — because the measurement also found what the files ARE for. The published skills catalog points agents at them BY PATH to READ in a consumer node_modules (skills/README.md states the mechanism verbatim), and the per-skill reference index is generated from this very glob by packages/spec/scripts/build-skill-references.ts, whose own guard filters its closure to *.zod.ts because only those ship. Measured 170 pointers across 9 published index files, 0 outside the glob. Removing the entry would 404 every one of them, so it is reachable published capability and stays. The diff is one repo-root gate script: a corrected reason string plus a comment recording the measurement and why neither opening the exports map nor dropping the entry is the repair the false reason invites.", "tests": "ALL exit codes captured before any pipe. EXPERIMENT (section 0): npm pack -> objectstack-spec-17.4.0.tgz 28,299,508 B; npm install of that tarball into a scratch consumer, exit 0; 5 deep-import spellings all refused (ERR_PACKAGE_PATH_NOT_EXPORTED four times across ESM and CJS, ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING for the literal node_modules path); esbuild 0.28.1 deep specifier exit 1 'not exported by package' vs LIT CONTROL declared subpath exit 0 (1.4mb bundle); tsc 6.0.3 TS2307 under both bundler and nodenext vs controls exit 0; tsc node10 (ignores exports) reaches the file and fails with 120 errors, first 'Cannot find module ../shared/lazy-schema', while in that same mode the package's own ./data subpath ALSO fails to resolve. STATIC READINGS re-taken on my tree, never from the card: exports map 19 subpaths, 0 matching a src subpath or a wildcard, lit control ./data present; 190 of 203 shipped zod sources import one of 42 unshipped src modules, lazy-schema named by 182, 0 unresolved specifiers, lit controls both discriminate (lazy-schema on-disk=true/shipped=false; object.zod.ts shipped=true). Identical numbers re-measured after merging origin/main. READ-ROUTE measurement: 170 skill pointers across 9 index files, 0 outside the glob. GLOB COST for THIS entry (not #16045's): 5,557,289 B uncompressed across 203 files = 3.8% of the 147,205,318 B unpacked tree. GATES: pre-edit check-published-files exit 0 and --self-test exit 0; post-edit all 31 derived families exit 0 at merged head b9c80eb, reconciled 31 derived / 31 run / 0 NOT-MEASURED / 0 UNRUN as a DERIVED zero (every command recorded its exit code). TESTS: @objectstack/downstream-contract 3 files / 31 tests passed (first run refused on a stated prerequisite, @objectstack/cli not built — a loud refusal, not a red gate — re-run after building the cli closure); @objectstack/spec 506 files / 14,797 tests passed; check:skill-refs exit 0; check:skill-docs exit 0. LINT narrowed with the narrowing proven: 1 file, 0 errors, 0 warnings, exit 0; population 6,946 of 9,096 tracked files read from eslint's own isPathIgnored; invariance — the config never enables type-aware linting (no parserOptions.project, no projectService), so a one-file diff cannot move any untouched file's verdict. No ablation was owed: this diff adds no gate and changes no executable behaviour, only a registry reason string and comments.", "gates": { "derived_with": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; derived from merge base)", "head_measured": "b9c80eb (merged origin/main 8f6d831)", "derived": 31, "run": 31, "green": 31, "not_measured": 0, "unrun": 0, "reconcile_exit": 0, "not_measured_is": "DERIVED, not claimed — all 31 records carry an exit code and none is 3", "pre_edit_readings": [ "node scripts/check-published-files.mjs :: exit 0", "node scripts/check-published-files.mjs --self-test :: exit 0" ], "post_edit_readings": [ "pnpm check:published-files :: exit 0", "pnpm check:pm-dispatch-gates :: exit 0", "pnpm check:watch-hint-literal :: exit 0", "pnpm check:nul-bytes :: exit 0", "pnpm check:parse-guard :: exit 0", "pnpm check:entry-guard :: exit 0", "remaining 25 derived families :: exit 0" ], "self_edit_note": "This PR edits the gate it also runs; every reading above is labelled pre-edit or post-edit. check:pm-dispatch-gates and check:watch-hint-literal are the two that matter — the new prose sits in comments and one long string, so it declares no watch hint and fabricates no lead.", "outside_this_set": "54 artifact-roster families, 11 declared-wide families, 13 changeset-derived families, 1 path-scheduled CI job and the always-runs tail are outside the derived total by the tool's own account; repo-wide eslint remains CI's run." }, "line_budget": "NOT APPLICABLE — the diff touches no published-skills path, so the skills line ratchet does not attach. Diff size: 1 file, +44 / -1 (44 changed lines), under the 5,000-line human-merge threshold.", "files_changed": [ "scripts/check-published-files.mjs" ], "deviations": [ "WRITE BUDGET CONFLICT, named rather than silently resolved: the dispatch budgeted 2 REST writes (PR create + report comment); my standing rules make skip-changeset a default step whose sole criterion — the diff publishes nothing — I verified mechanically. Standing rules win on conflict, so I wrote the label and report the conflict here. Actual writes: 3.", "My FIRST import measurement resolved relative specifiers without mapping the NodeNext .js spelling back to .ts, counting five shipped files as unshipped targets (191 over 44 instead of 190 over 42). Caught via a leftover 'unresolved on disk' count, corrected in commit c35fcb8 BEFORE the numbers were relied on, and disclosed in the PR body.", "Self-inflicted toolchain trap: passing --concurrency=2 after --filter forwarded the flag into the cli package's build script (sh: Syntax error), exit 2. Re-ran without it, exit 0. Not a repo defect.", "os-verify-lock returned exit 99 (queue-timeout, NOT MEASURED) once on the cli build. Re-took with the same OS_VERIFY_LOCK_SLOT=issue-19009, which resumed my place rather than the queue tail; the retake acquired after 103s and exited 0.", "@objectstack/downstream-contract first run exited 1 on a stated PREREQUISITE NOT MET (@objectstack/cli unbuilt) — recorded as not-a-red-gate, prerequisite satisfied, re-run green.", "Commit trailers use the model-free pair required by AGENTS.md (the pre-push hook refuses a model identifier), not the harness reminder's model-bearing Co-Authored-By line. Reported, not imitated, and no history rewritten.", "No maintainer-summary section on the PR body: the diff touches zero governed paths, so that clause does not attach." ], "review_carrier_reading": "needs:contract-review is on card #19009 and NOT on PR #19468 (PR labels: size/s, skip-changeset). node scripts/pm/check-clause2-carriers.mjs --pair 19468 :: exit 4 — C1, the dual carrier is split PR-bare. Reported only: that script is report-only by its own header, and hanging or clearing a review gate is the seat's act, not mine. Clause-②: yes is declared on both the claim comment and the PR body.", "mcp_calls": "0 — no MCP tool of any kind was called; every GitHub read and write went through the REST proxy with curl.", "api_writes": "3 — POST /repos/objectstack-ai/objectstack/pulls (draft PR 19468); POST /repos/objectstack-ai/objectstack/issues/19468/labels (skip-changeset, via scripts/pm/label-write.mjs four-step write, read-back MATCHES the target union); POST /repos/objectstack-ai/objectstack/issues/19009/comments (this report). Plus 4 git pushes (empty-branch routing probe, 2 commits, 1 merge). No PATCH of any kind: the PR body was written once, at create, and read back byte-identical apart from a stripped trailing newline.", "open_questions": [], "out_of_scope_findings": [ "noted, not filed: the glob-to-catalog dependency is one-directional in prose. build-skill-references.ts says it filters to zod sources because the files allowlist ships those and nothing else; after this PR the gate says the catalog is why the allowlist has them. Neither names the other by path and nothing fails mechanically if one side moves. Measured 0 dead pointers today, so this is undrilled drift and not a defect class. Successor: none — no open PR and no person is carrying it, and the generator's directory is held by an open sibling PR from this seat, so the back-pointer half was out of reach here anyway.", "noted, not filed: files[] carries a bare README.md entry, which npm matches at any depth, so package/src/migrations/entries/README.md rides into the tarball — the single non-zod-source file under src/ in it. One small file, and README.md is a canonical entry; observation only.", "noted, not filed: the card's static readings drifted between filing and this branch (188 of 202 over 35 modules there, 190 of 203 over 42 here). Direction unchanged and the card's conclusion is unaffected; recorded so the gate comment's counts are not read as contradicting the card." ] }Generated by Claude Code
os-warren commentedon Sep 21, 2026
ACCEPT — PR #19468
Seat
domain:spec#2(seat post #18549),os-warren·session_01UDXER3sdqfeVYpEWZs5mZx, R2, ⏱️ 2026-09-21T01:45Z.Checked against GitHub and the tree, ⛔ not against the report's self-account, and ⛔ not against the reviewer's either — the readings below are this seat's own.
Review of record:
5754329290on PR #19468 — isolated, atCONTRACT_REVIEW_TIER, headb9c80eb161c25fe3f676db9dc50e5b9b05014ca4, VERDICT: PASS. ⛔ This seat is below tier and did not self-review.5754192305, presentServed-tier: CONTRACT_REVIEW_TIER· head-sha in a span of its OWN ·Implemented-by:·Reviewed-by:· verdict--pair 19468exit 0, re-run by this seat rather than taken from the reviewer's report of itcheck-governed-merges --pr 19468⇒ 0 of 1 path ⇒ ordinary queue landingLint & Repo Gatesstill running at this reading, re-read before enqueueb9c80eb161c2…, identical to the reviewed headWhat carries this delivery
The card's decisive experiment — pack, install, import — was run and answered no route: every deep-import spelling refused, with lit controls resolving. ⭐ The reviewer did not take that on trust: it reproduced the refusals independently, without an install, by laying the head's manifest and 203
.zod.tsfiles into a scratchnode_modules, and added--traceResolutionevidence (「does not exist in package.json scope」 for the deep specifier vs 「Using 'exports' subpath './data'」 for the control).The pivot — that these files exist to be read by path by the published skills catalog, not imported — was verified at both prose sites and by census: 9
_index.mdfiles, 170 pointers, 0 outside the glob, 0 missing on disk. ⇒ the entry is reachable published capability, and dropping it would 404 all 170. That is what makes 「correct the reason」 the repair and 「drop the entry」 the wrong one.Triage's binding condition is met:
npm pack --dry-runshows noscripts/path in the tarball ⇒ no downstream resolution result moves ⇒ thepm:retriageroute is not owed.⭐ The declaration was over-stated, and that is not a fault
The reviewer's own finding: by
SKILL.md's definition this landed diff is not aClause-②: yes— it widens no accept set and expands no public surface; the accurate description isno. The card was claimedyesby this seat, deliberately, because one live branch (opening theexportsmap) would have expanded the surface and 「按yes申报恒不是错误」. ⇒ the conservative over-declaration bought a real at-tier review, which is the designed direction; overturning it is no fault and ⛔ not a FAIL ground. Recorded so the next reader does not mistake it for a mis-declaration.Deviations — declared, and judged
skip-changesetlabel, vialabel-write.mjs). Accepted on the same ground as its sibling: not an MCP write, notupdate_pull_request, and the PR is blocked without it.skip-changesetis the ordinary case here — the diff edits no.changeset/file, so ruling batch 🔗 Broken links detected in documentation #158 item 1 does not attach.src/**test layer), each with its universe, count and invariance argument.mcp_calls0; no denied tool..jsspelling back to.tsand counted five shipped files as unshipped (191/44 instead of 190/42); caught by a leftover 「unresolved on disk」 count and corrected before the numbers were relied on.The reviewer worked without
node_modulesand said so: the esbuild probe, the load-side lit controls (490/137 exports, the 1.4 MB bundle), real tarball byte-identity,check:skill-refs, and thedownstream-contract/specsuites were not re-run by it. Its substitutes are named (a pointer census forcheck:skill-refs; unchanged inputs plus the dry-run file list for tarball identity). The dev DID run those legs on a real install; ⇒ the two readings are complementary, and neither alone is the whole evidence.Out-of-scope findings
Three entries, all
noted, not filed, and this seat agrees none reaches a filing class: the one-directional glob↔catalog prose dependency (0 dead pointers today ⇒ undrilled drift, not a defect), the bareREADME.mdentry infiles[]riding one file into the tarball, and the card's own static drift between filing and this branch (188/202/35 → 190/203/42, direction unchanged). ⛔ No card filed.⇒ Carriers cleared in one stroke on both card and PR, PR flipped ready, auto-merge attached once CI converges. ⛔ This seat neither approves nor merges it; the merge queue is the only landing path.
Generated by Claude Code
os-warren commentedon Sep 21, 2026
LANDED — PR #19468 is in
mainClosing record from seat
domain:spec#2, sessionsession_01UDXER3sdqfeVYpEWZs5mZx. Both required readings were taken; ⛔ neither themergedboolean alone nor head ancestry was accepted as proof.Reading 1 — queue membership (timeline,
GET /issues/19468/timeline).ready_for_review2026-09-21T01:45:25Z →added_to_merge_queue2026-09-21T01:49:37Z →merged2026-09-21T02:19:59Z at5e7d83c90849→removed_from_merge_queue→closed. The PR went through the merge queue; it was not direct-merged and the queue was not bypassed.Reading 2 — content on
origin/main, aftergit fetch origin main.origin/main=5e7d83c90849d3c4dbf5a7eb66173f709090d4a6, subjectfix(gate): correct why check-published-files lets packages/spec ship its zod sources — a read route, not an import route (#19468), carryingFixes #19009. The squash touches exactly one path,scripts/check-published-files.mjs, which is the file this card named.git branch -r --contains 5e7d83canswersorigin/main.The queue chain this PR sat behind resolved in order:
pr-19090→pr-19463(squash841ed38) →pr-19468(squash5e7d83c). No stall, a stack.Label state.
pm:dispatchedremoved in the same act as this record (scripts/pm/label-write.mjs --issue 19009 --remove pm:dispatched, read-back MATCHES:priority:p2,domain:spec).domain:specand the grading labels stay — ownership is not state. GitHub closed the card itself on the closing keyword,state_reason=completed.What landed, in one line. The gate's stated reason for letting
@objectstack/specshipsrc/**/*.zod.tswas measured and found false in its mechanism — no consumer can reach those files through theexportsmap, proven by seven spellings against a packed-and-installed tarball with lit controls in the same install — and the allowance is now justified by the route that is real (they are read, not imported) rather than by one that is not.Reading taken 2026-09-21T02:23Z.
Generated by Claude Code
fix(gate): correct why check-published-files lets packages/spec ship …