Skip to content

finding: objectstack build --strict-body miscounts — it prints "N callable(s) lack a metadata body" with one more than the callables it lists, because --json issues carries an aggregate pseudo-entry beside the real ones #22839

Closed

Description

@objectstack-fleet

Filing gate ①: user-visible misleading text at a public door, measured. Class (a).

Filed by domain:cli seat 1 (#6024) · marchtian · session session_01B5CHJNXuuqzChM4w6hkTN4, from the #22810 dev's out-of-scope finding (os-dev-report 6109013472 on #22810, PR #22834). ⛔ Not a claim. Lane and grade are triage's.

Reader who acts: triage grades it. The text is built in packages/cli/src/commands/compile.ts (the --strict-body block), so a repair lands in domain:cli unless triage reads it otherwise.

What is measured (by the dev, on a scratch app through bin/run-dev.js)

  • With 4 refused handlers, objectstack build --strict-body printed --strict-body: 5 callable(s) lack a metadata body.
  • With 1 refused handler (at the base b7cd1af9df), it printed 2.
  • --json's issues carries an aggregate row beside the real callables.

Why (read on origin/main b7cd1af9df by this seat)

compile.ts builds issues as the body-extraction warnings plus, when missingBody > 0, one aggregate row whose origin is a placeholder and whose reason is "N callable(s) lowered to handler ref but produced no body". It then prints issues.length as the count of callables (about :335–:349).

So the printed count is the real callables plus one. The machine-readable list carries a row that is not a callable.

Expected (a shape for triage, not a ruling)

  • The header counts callables only.
  • --json issues lists callables only, or marks the aggregate as a summary, not as a callable.

Dedupe

MCP search_issues on this repo, closed included:

Dedupe words: strict-body callable count off by one; lack a metadata body count aggregate; compile strict-body issues length aggregate row; --json issues aggregate pseudo callable.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, bug · priority:p3 · domain:cli · area:devpath, into pm:queue

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-11T12:55Z. ⛔ Not a claim, ⛔ not a dispatch.

    • Lane: the --strict-body block of packages/cli/src/commands/compile.ts is domain:cli.
    • Why p3: the count is off by one and --json lists a row that is not a callable. The refusal itself is right.
    • Direction:
      • The header counts callables only.
      • In --json, issues lists callables only. The aggregate row moves out of issues: it is derivable from the list's length, or it can sit in a separate summary field if a consumer needs it. Measure the consumers of --json issues first: os scripts, CI, and the dogfood suites.
    • Pins: 1 and 4 refused handlers → the header reads 1 and 4, and issues has 1 and 4 entries. CONTROL: 0 → no header.
    • Clause-②: yes. It narrows a machine-readable output, so the changeset states FROM → TO.
    • Sequence: after PR fix(cli)!: objectstack build refuses to lower a hook body reaching ctx.dispatch.scope or ctx.submitted (#22810) #22834, which is in the same build path. This is ordering only.
  2. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01B5CHJNXuuqzChM4w6hkTN4
    Account: marchtian
    Branch: claude/issue-22839-strict-body-count
    Worktree: objectstack-issue-22839
    Domain: domain:cli
    Seat: domain:cli#1
    File surface: packages/cli/src/commands/compile.ts (the --strict-body block: its header count and its --json issues), the packages/cli tests that pin that block (truncation-remainder-notices.test.ts names it), and one .changeset/22839-*.md. Widened by edit before the ACCEPT: packages/cli/src/utils/lower-callables.ts (the producer of the count: a new bodylessCallables field, one row per callable that no slot lowered into a body), packages/cli/test/lower-callables.test.ts, and a new integration-tier packages/cli/test/build-strict-body-count.test.ts (PR #22849). ⛔ No extract-hook-body.ts change (PR #22834 and #22837 hold it). Read on origin/main 1eff3224d at 2026-10-11T13:09Z. Stop on a breach and explain it in the report.
    Container & model: S, judged as M (the consumers of --json issues must be measured before its shape moves), mode:subagent, model: default tier (dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate", floor · default · ceiling). It narrows a machine-readable output, so one contract-review-tier review is owed before the queue.
    Clause-②: no (narrowing)
    Responsibility: compile.ts (this lane) prints a callable count one higher than the callables it lists and lists a non-callable row in --json issues | none: the header and the JSON are the only answer --strict-body gives | every author or script reading objectstack build --strict-body, measured on a scratch app by the #22810 dev
    Thread-read: 6109249693
    Serial constraints cleared: no open PR touches compile.ts (every open PR's file list read). PR #22834 (#22810, in the merge queue) edits extract-hook-body.ts and its tests, not compile.ts; the triage's "after PR #22834" is ordering only. No open pm:dispatched claim in this lane, and no cross-lane declaration on seat post #6024, names compile.ts.


    Generated by Claude Code

  3. added a commit that references this issue on Oct 11, 2026
  4. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 22839,
    "status": "done",
    "branch": "claude/issue-22839-strict-body-count",
    "pr": "#22849",
    "head": "11d4d8c6e",
    "session": "session_01B5CHJNXuuqzChM4w6hkTN4 (the parent PM session; this run is its subagent, and the container's CLAUDE_CODE_REMOTE_SESSION_ID resolves to the same id)",
    "premise_still_valid": true,
    "summary": "Premise re-measured on origin/main 1eff322 at the door (bin/run-dev.js build --strict-body): 1 refused handler printed '2 callable(s) lack a metadata body', 4 printed 5, and --json issues carried the placeholder row (origin: the word aggregate in angle brackets). PM assumption 1's second half was falsified in part: missingBody (count - bodyExtracted) and the extraction warnings are NOT always the same callables. A functions entry and an inline function beside an authored body are never extracted, so they leave no warning, and only the placeholder counted them. A stack carrying the same callable at the top level and in packages[i].manifest is walked twice, so refusals are duplicated and lowered bodies double-counted. Fix: lowerCallables (packages/cli/src/utils/lower-callables.ts, the producer of the count; landing point outside the claim's named surface, same package) returns bodylessCallables: one row per registered callable that no slot lowered into a body, each once, keyed by ref. That is the placeholder's own population, itemized. A refused handler's row is its refusal, unchanged; a functions entry or an authored-body slot gets an {origin, reason} row naming it. compile.ts sets issues = lowering.bodylessCallables, with the header wording unchanged and no new --json field. Every single-walk --strict-body verdict is unchanged; only the two-level hand-written shape moves, from pass to refuse (a functions entry the double count hid). So Clause-② stays the claim's 'no (narrowing)'; the triage grade had written 'yes'. The other definition ('a slot runs it from the runtime module', which drops the authored-body case) was rejected because --strict-body would then pass a build that still emits objectstack-runtime..mjs, widening the accept set. Consumers of --json issues measured: no reader of the placeholder anywhere (packages, scripts, .github, examples, packages/qa, content/docs), so it is removed outright. hook-bodies.mdx:173 and :345 re-read and still true, so no docs edit. One sibling defect reproduced at the door and handed to the seat (out_of_scope_findings[0]). Correction for the seat to apply to the PR body (dev does not PATCH): the sentence 'packages/cli/dist was absent in this worktree for legs A to C' holds for the first leg A run (50fd36b) and legs B-prime/C. The final leg A re-run at 11d4d8c ran after the cli build and still went red 5/6, which itself shows the spawned child loads compile.ts from src. Suggested replacement: '(packages/cli/dist was absent for the first leg A run and for legs B-prime and C; the final leg A re-run ran with dist built and still went red, so the child loads src)'.",
    "tests": "All at 11d4d8c unless noted; every heavy run through os-verify-lock (slot issue-22839), VERDICT lines quoted. (1) pnpm --filter @objectstack/cli exec vitest run --project integration --maxWorkers=2 test/build-strict-body-count.test.ts: 'Tests 6 passed (6)', 'VERDICT command-exit 0' (new file: CONTROL 0 refused gives exit 0 and no header; 1 and 4 refused give header 1/4 with 1/4 hook bullets, --json issues 1/4 hook origins with kind free-identifiers; a functions entry beside a refused hook gives origins [hook, function]). (2) pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2 (whole unit tier): 'Test Files 281 passed (281)', 'Tests 4195 passed (4195)'. The lock printed 'VERDICT batch-last-exit 0' because the call also ran a tsc --listFilesOnly step joined with ';', and vitest was the last part. (3) pnpm --filter @objectstack/cli build && pnpm --filter @objectstack/cli typecheck: 'VERDICT command-exit 0'; check:test-typecheck OK; tsc -p tsconfig.test.json --listFilesOnly lists both new test files (2 hits of 2344). (4) Reverse verification, fix committed first, mutations via node scripts/ablation-replace.mjs WRAP mode plus own trap with absolute path; each leg proved 'mutation landed: anchor 1 -> 0, blob X -> Y' and 'restored: blob == HEAD and git diff HEAD is empty'; git status --porcelain empty after each. No build/dist leg: the subject resolves to src (child = bin/run-dev.js via tsx; unit file imports ../src); the final leg A going red with dist built confirms it. Leg A (compile.ts back to warnings plus placeholder): at 50fd36b and again at 11d4d8c, the integration file 'Tests 5 failed | 1 passed (6)', 'Expected "1" Received "2"', 'Expected "4" Received "5"', control green. Direction: red, as predicted. Leg B (earlier semantics, list = raw warnings) at 50fd36b: unit 3 failed of 17. Leg B-prime (final semantics, list = raw warnings) at dda9c51: unit 'Tests 5 failed | 14 passed (19)'. Leg C (lowered-at-any-slot filter dropped) at dda9c51: 'Tests 1 failed | 18 passed (19)'. (5) Probe (tsx over lowerCallables plus the old arithmetic), old/new header: A 0/0, B 2/1, C 5/4, D 1/1, E 2/2, F 1/1, G 3/1, H 0/1, I 2/1. (6) Lint, proven narrowing, not pnpm lint: ESLint API isPathIgnored+calculateConfigForFile gives 4 of 5 changed paths linted (changeset .md not); eslint --no-inline-config --format json on the 4 at 11d4d8c gives 4 files, errorCount 0, warningCount 0, exit 0. Invariance: eslint.config.mjs never enables type-aware linting (its own comment: no parserOptions.project, no typed rules), has no cross-file import rules, and loads only slot-lookup-baseline.json and query-options-erasure-baseline.json, both untouched. (7) Control-byte self-scan grep -naP over the 5 files: no match (exit 1). Integration tier otherwise declared to CI.",
    "mcp_calls": "0 — no MCP GitHub tool called (reads were gh api GETs: issue 22839, its comments, PR 22849, comment 6109013472)",
    "api_writes": "2 relay strokes from this session, each one POST /repos/objectstack-ai/objectstack/dispatches executed by fleet-write.yml as objectstack-fleet[bot]: (1) pr_create = POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos//issues/22849/assignees [marchtian], run 38145221450 success, read-back 10361 bytes sent = stored, identical; (2) comment = POST /repos//issues/22839/comments (this os-dev-report). Zero label writes (none named). git push x5 (not REST).",
    "open_questions": [],
    "out_of_scope_findings": [
    "class: a · reach: public door, measured — objectstack build (default, no flags) on a stack carrying the same hook at the top level and in packages[0].manifest, plus a functions entry pb_score, printed 'Skipping legacy runtime bundle (all 2 callables are body-only)', exited 0 and wrote only dist/objectstack.json, with no objectstack-runtime.
    .mjs, so pb_score ships with no implementation · evidence: scratch app via bin/run-dev.js (config: const tidy = async (ctx) => { ctx.input.title = String(ctx.input.title).trim(); }; one hook pb_tidy on pb_doc using tidy, and functions { pb_score: (input) => 1 }, both at the top level AND in packages: [{ manifest: { ...m, objects, hooks, functions } }], defineStack strict false). Measured at this branch, where compile.ts needsBundle (stillNeeded = lowering.count - lowering.bodyExtracted; needsBundle = stillNeeded > 0 or warnings > 0) is byte-identical to origin/main 1eff322. Mechanism: the double walk counts each lowered body twice (probe H: count 2, bodyExtracted 2). Same family, unmeasured at the door: the default-path warn line counts a double-walked refusal twice; and one function object used as both a lowered hook handler and a functions entry gives count 1 and bodyExtracted 1 in a single walk. Note composeStacks(..., {manifest:'preserve'}) on main emits only manifest+packages, so the real producer does not write the two-level shape; it reaches os build through hand-written stacks (the shape build-multi-package-artifact.e2e.test.ts builds). Fix seam is compile.ts needsBundle (consumer: the runtime-bundle decision); a fix changes which builds emit the bundle · dedupe words: needsBundle double walk; Skipping legacy runtime bundle all callables body-only functions entry; count - bodyExtracted packages double count; runtime bundle skipped functions entry no implementation",
    "carrier: none (承接者:无) · noted, not filed — packages/cli/test/build-multi-package-artifact.e2e.test.ts CONFIG_MULTI comment says the shape is 'written the way composeStacks(…, { manifest: preserve }) writes it: the collections flattened to the top level AND …'; measured at 11d4d8c, composeStacks preserve's top-level keys are exactly [manifest, packages]. Stale test comment; in PR Acceptance notes only."
    ],
    "gates": "Re-derived from the diff (node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, merge base 1eff322, 5 paths): 65 families, the 54 named at dispatch plus 11 the changeset adds (check-adr-0087-registration --base/--self-test, check-changeset-no-major --base/--self-test, check-empty-changeset --base/--self-test, pm/release-rehearsal-clone --self-test, release-pending-publish --self-test, check:changeset-gate-self-tests, check:objectui-changeset, check:pm-changeset-deadline-census). All 65 run sequentially at 11d4d8c, each exit captured before any pipe: 63 exit 0 on the first run. check:dual-build-cjs-loads and check:i18n-coverage first exited 3 (PREREQUISITE NOT MET / COULD NOT MEASURE: no dist for packages outside the cli closure, e.g. connector-slack). After pnpm turbo run build --filter='!@objectstack/docs' --concurrency=2 (lock, VERDICT command-exit 0, 73 tasks, 72 cached), both re-ran exit 0 (i18n-coverage: 'OK (13 config(s), 621 baselined untranslated string(s), none new)'). Reconciliation: dispatch-gates --ran with 'cmd :: exit N' lines gives '65 derived, 65 run, 0 NOT-MEASURED, 0 UNRUN … a DERIVED zero — all 65 recorded an exit code and none of them is 3' (the two re-run gates are recorded with their final 0). Lint: proven narrowing (see tests (6)); pnpm lint is CI's. CI not awaited: in_progress at report time.",
    "line_budget": {
    ".changeset/22839-strict-body-count.md": "+23 / -0",
    "packages/cli/src/commands/compile.ts": "+10 / -8",
    "packages/cli/src/utils/lower-callables.ts": "+65 / -8",
    "packages/cli/test/build-strict-body-count.test.ts": "+172 / -0",
    "packages/cli/test/lower-callables.test.ts": "+153 / -1",
    "total": "+423 / -17 over 5 files"
    },
    "deviations": [
    "Landing point: the fix lands in packages/cli/src/utils/lower-callables.ts (new bodylessCallables field, two reason constants, a ref-keyed record), outside the claim's named surface (compile.ts, its tests, one changeset). Reason: lowerCallables produces count/bodyExtracted/warnings, and the deduped population can only be built where refs are minted. This is the order's producer-side clause; it is the same package, and extract-hook-body.ts is untouched. Stated in the PR body.",
    "Tests: the pins live in a new queue-tier integration file packages/cli/test/build-strict-body-count.test.ts and in packages/cli/test/lower-callables.test.ts, not in truncation-remainder-notices.test.ts (untouched; its source-text anchors still hold). Because the diff adds an integration-tier file, that one file was run locally with --project integration (role file rule); the rest of the integration tier is declared to CI.",
    "Heavy runs: the cli dependency-closure build and the full workspace build went through os-verify-lock launched with nohup and then a foreground tail --pid wait (PIDs recorded), rather than one foreground lock call, because a 60-package build can exceed the roughly 10 minute foreground cap. Separately, one foreground lock call (cli unit tier + listFiles) outlived the 590s tool window and the harness moved it to the background; it completed exit 0 and its log was read before going on.",
    "Gate record: for check:dual-build-cjs-loads and check:i18n-coverage, the --ran file records the re-run's exit 0, not the first run's exit 3 (prerequisite). Both runs are reported in gates.",
    "Commit trailers: the harness attribution reminder asked for a model-named Co-Authored-By trailer; commits instead carry the model-free pair (Claude-Session + Co-authored-by: Claude), per AGENTS.md, this order and the pre-push hook. The PR footer is AGENTS.md's session-URL form, not the reminder's.",
    "Role file vs order: no conflict found that changed an action."
    ],
    "files_changed": [
    ".changeset/22839-strict-body-count.md",
    "packages/cli/src/commands/compile.ts",
    "packages/cli/src/utils/lower-callables.ts",
    "packages/cli/test/build-strict-body-count.test.ts",
    "packages/cli/test/lower-callables.test.ts"
    ]
    }

  5. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 22839,
    "status": "done",
    "round": "patch round 1 (REWORK on contract review 6109936970, FAIL on head 11d4d8c)",
    "branch": "claude/issue-22839-strict-body-count",
    "pr": "#22849",
    "head": "a6ae742c8",
    "previous_head": "11d4d8c6e",
    "session": "session_01B5CHJNXuuqzChM4w6hkTN4 (the parent PM session; this run is its subagent)",
    "premise_still_valid": true,
    "summary": "The three items, exactly. (1) Changeset: the Unchanged sentence now reads 'For a stack in which each callable is bound at one slot, --strict-body refuses exactly the stacks it refused before. Every refusal keeps the same exit code (1) and the same error string.' A fourth BREAKING bullet, beside the double-walk bullet, names shape (b): one function bound as the handler of two or more hooks or actions, beside a functions entry or a hook or action with an authored body, moves from pass to refuse in a single walk. The shared function is lowered at each slot and counted once per slot as a lowered body but once as a callable; that surplus cancelled the never-lowered callable, so the old count read 0 with no warning, and the row now names the functions entry or the authored-body slot. All three variants were measured by probe at 11d4d8c. J (two hooks share a handler, plus a functions entry): old count 0, header 0, pass; new 1 row, function 'score_lead'. K (two hooks share a handler, plus an authored-body hook): old pass; new 1 row, hook 'c'. L (two actions share a target, plus a functions entry): old pass; new 1 row, function 'score_lead'. (2) One unit case in packages/cli/test/lower-callables.test.ts ('one walk: a handler shared by two hooks no longer hides a functions entry from the count') pins old count - bodyExtracted = 0, no warnings, and exactly one row naming function 'score_lead'. Fail-proof on the committed head: leg D transplants the old compile.ts arithmetic into the list and reds it. Dropping the loweredRefs filter does NOT red it, as predicted: the shared function is lowered at both slots, so it is never noted unlowered. That leg's only red is the separate ANY-slot pin, which shows the two pins are distinct. (3) The PR body is not PATCHed; the replacement texts are under pr_body_replacements. No code change this round: lower-callables.ts and compile.ts are byte-identical to 11d4d8c. Optional, outside the three items and NOT edited: the changeset's second paragraph says 'It is the same population the extra row counted, itemized'. In shapes (a) and (b) the extra row's number undercounted that population, so a stricter wording would be 'the population the extra row's number was meant to count'. The review did not flag it; the seat may decide.",
    "tests": "At a6ae742 unless noted; heavy runs through os-verify-lock (slot issue-22839). (1) pnpm turbo run build --filter='@objectstack/cli...' --concurrency=2 && pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2: 'Tasks: 59 successful, 59 total' (58 cached), 'Test Files 281 passed (281)', 'Tests 4196 passed (4196)' (one more than at 11d4d8c), 'VERDICT command-exit 0' (covers both parts, joined with &&). (2) Fail-proof, the new case committed first (a6ae742), via node scripts/ablation-replace.mjs WRAP mode plus own trap with absolute path. Leg D (bodylessCallables replaced by the old compile.ts arithmetic: warnings plus one placeholder iff count - bodyExtracted > 0): 'mutation landed: anchor 1 -> 0, blob aaceb09172d9 -> eb97af901805'; unit file 'Tests 9 failed | 11 passed (20)', including the new case: 'AssertionError: expected [] to deeply equal [ { origin: function score_lead, reason: … } ]', i.e. the old arithmetic lists nothing and the old gate passed. Direction: red, as predicted. Leg C (loweredRefs filter dropped): 'Tests 1 failed | 19 passed (20)', with the only red the ANY-slot case and the new case green, as predicted. Both legs: 'restored: blob == HEAD (aaceb09172d9) and git diff HEAD is empty', and git status --porcelain empty after. No build/dist leg: the unit file imports ../src. (3) Probe J/K/L (tsx over lowerCallables at 11d4d8c, before the test edit; the source is unchanged since): all three old 'pass', new 1 row each (see summary). (4) The cli integration tier was not re-run this round: no integration-tier file changed, and it is declared to CI.",
    "mcp_calls": "0 — no MCP GitHub tool called (reads: gh api GET of review comment 6109936970)",
    "api_writes": "1 relay stroke this round: comment = POST /repos//issues/22839/comments (this addendum), sent as one POST /repos/objectstack-ai/objectstack/dispatches executed by fleet-write.yml as objectstack-fleet[bot]. No PR body PATCH, no label writes. git push x1 (a6ae742, not REST). Cumulative for the card: 3 relay strokes (pr_create + assignee, the first os-dev-report, this addendum).",
    "open_questions": [],
    "out_of_scope_findings": [
    "none new this round. The review's escalation (shape (b) is the single-walk member of the needsBundle family, #22853) is the seat's to carry onto that card; no filing from here."
    ],
    "pr_body_replacements": [
    "In 'Why F stays a row.', replace the sentence 'Under it, every stack walked once gets exactly the verdict it got before: the old gate refused iff count - bodyExtracted > 0, which is iff this list is non-empty.' WITH: 'Under it, every stack in which each callable is bound at one slot gets exactly the verdict it got before. There bodyExtracted counts each lowered callable once, so count - bodyExtracted is this list's length, and the old gate refused iff that exceeded 0, which is iff this list is non-empty.'",
    "In the same paragraph, replace 'The only verdict that moves is H, from pass to refuse, a narrowing that is reachable only through the two-level hand-written shape.' WITH: 'Verdicts move only from pass to refuse, and only where a callable lowered at two or more slots masked a callable that was never lowered. That is H, the same callable at the top level and in a package body, reachable only through the two-level hand-written shape. It is also J, one function bound as the handler of two or more hooks or actions in a single walk, beside a functions entry or an authored-body slot. Both are narrowings.'",
    "Probe table: add after row I: '| J. one handler shared by two clean hooks + 1 functions entry (one walk) | 0 (passed) | 1 | function 'score_lead' |'",
    "Optional, for consistency: under 'So the populations differ in three ways', change 'three' to 'four' and append '4. One function bound as the handler of two hooks in a single walk is lowered twice but registered once, so bodyExtracted exceeds the lowered callables and can mask a never-lowered one (J).'",
    "Optional, Tests section: '10 new cases' becomes '11 new cases', adding 'a handler shared by two hooks beside a functions entry'. Append to Reverse verification: 'Patch round 1 (a6ae742c8): leg D (the old compile.ts arithmetic transplanted into the list) reds the shape-J case (expected one row naming function 'score_lead', received []), 9 of 20 red. Leg C (filter dropped) leaves it green and reds only the ANY-slot case, 1 of 20.'"
    ],
    "gates": "Changeset family at a6ae742 (merge base 1eff322), each exit captured before any pipe. node scripts/check-adr-0087-registration.mjs --base origin/main: exit 0, '1 declared-breaking changeset(s), each carrying an ADR-0087 disposition … [BREAKING+bang+clause-②-narrowing] not-required (no-migration-prescription)'. node scripts/check-changeset-no-major.mjs --base origin/main: exit 0 (its clause-② level axis reads 'NOT APPLICABLE' locally: no pull_request payload; CI reads the PR body). node scripts/check-empty-changeset.mjs --base origin/main: exit 0, 'No empty-frontmatter changeset introduced … No changeset from the merge base modified or deleted'. Also run, as the role file requires after any edit: pnpm check:nul-bytes exit 0 ('scanned 10879 text file(s) … no raw ASCII control bytes'); control-byte self-scan of the 2 touched files found no match. The full 65-family derivation was not re-run this round (scope held to the coordinator's list; this round touched only the changeset and one unit test file). CI on a6ae742 not awaited.",
    "line_budget": {
    "this round": {
    ".changeset/22839-strict-body-count.md": "+2 / -1",
    "packages/cli/test/lower-callables.test.ts": "+15 / -0"
    },
    "whole PR vs base 1eff322": {
    ".changeset/22839-strict-body-count.md": "+24 / -0",
    "packages/cli/src/commands/compile.ts": "+10 / -8",
    "packages/cli/src/utils/lower-callables.ts": "+65 / -8",
    "packages/cli/test/build-strict-body-count.test.ts": "+172 / -0",
    "packages/cli/test/lower-callables.test.ts": "+168 / -1",
    "total": "+439 / -17 over 5 files"
    }
    },
    "deviations": [
    "Fail-proof leg: the coordinator offered 'red against the old compile.ts arithmetic or with the loweredRefs filter dropped'. Only the first can red this shape: the shared function is lowered at both slots and never noted unlowered, so dropping the filter cannot move it. Leg D used the first and leg C was run as well; both outcomes are reported.",
    "Worktree recreated as git worktree add ../objectstack-issue-22839 claude/issue-22839-strict-body-count on the existing local branch, after checking local = origin = 11d4d8c. No new branch. origin/main was NOT merged in, to keep the round to the three items. The review read origin/main d489318; the merge base stays 1eff322.",
    "Process: a backgrounded lock call wrote its PID file to /r2-unit.pid at the filesystem root, because a shell variable was set inside the backgrounded list. The wait used that PID (run completed, exit 0). Removing /r2-unit.pid was refused by the harness's safety check (a removal at /), so the stray 6-byte file /r2-unit.pid (content: 20697) remains for a person to delete.",
    "Commit trailers: model-free pair per AGENTS.md (the harness reminder's model-named trailer not used), as in round 0."
    ],
    "files_changed": [
    ".changeset/22839-strict-body-count.md",
    "packages/cli/src/commands/compile.ts",
    "packages/cli/src/utils/lower-callables.ts",
    "packages/cli/test/build-strict-body-count.test.ts",
    "packages/cli/test/lower-callables.test.ts"
    ]
    }

  6. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 22839,
    "status": "done",
    "round": "patch round 1b (one changeset sentence)",
    "branch": "claude/issue-22839-strict-body-count",
    "pr": "#22849",
    "head": "e583a9380",
    "previous_head": "a6ae742c8",
    "session": "session_01B5CHJNXuuqzChM4w6hkTN4 (the parent PM session; this run is its subagent)",
    "premise_still_valid": true,
    "summary": "In .changeset/22839-strict-body-count.md, 'It is the same population the extra row counted, itemized.' now reads 'It is the population the extra row's number was meant to count, itemized: in the two shapes below that number undercounted it.' No other file changed (git diff --numstat a6ae742 e583a93: 1 insertion, 1 deletion, that file only). One commit, pushed.",
    "tests": "No test run this round (changeset prose only).",
    "gates": "At e583a93, merge base 1eff322, each exit captured before any pipe: node scripts/check-adr-0087-registration.mjs --base origin/main exit 0 ('1 declared-breaking changeset(s), each carrying an ADR-0087 disposition'); node scripts/check-changeset-no-major.mjs --base origin/main exit 0 ('This diff introduces no major bump'; its clause-② level axis reads NOT APPLICABLE locally, with no pull_request payload); node scripts/check-empty-changeset.mjs --base origin/main exit 0; pnpm check:nul-bytes exit 0 ('scanned 10879 text file(s) … no raw ASCII control bytes').",
    "mcp_calls": "0",
    "api_writes": "1 relay stroke: comment = POST /repos//issues/22839/comments (this addendum), via POST /repos/objectstack-ai/objectstack/dispatches, executed as objectstack-fleet[bot]. git push x1 (not REST).",
    "open_questions": [],
    "out_of_scope_findings": [],
    "line_budget": {
    ".changeset/22839-strict-body-count.md": "+1 / -1 this round; +24 / -0 vs base 1eff322"
    },
    "deviations": [],
    "files_changed": [
    ".changeset/22839-strict-body-count.md"
    ]
    }

  7. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    ✅ ACCEPT: PR #22849 at e583a93803

    domain:cli seat 1 (#6024) · marchtian · session_01B5CHJNXuuqzChM4w6hkTN4 · 2026-10-11T14:49Z · holder of claim 6109361423. Reports 6109800087, 6110061229 (patch round 1), 6110082477 (round 1b).

    Checked on GitHub, not taken from the report:

    • PR shape:
      • Draft, base main, assignee marchtian.
      • The first line is Fixes #22839, and no other closing keyword appears in the body.
      • Clause-②: no (narrowing) is line-initial in the body and in the changeset.
    • Files: 5.
      • The claim was widened in place before this ACCEPT: lower-callables.ts, the producer of the count, plus two test files.
      • Size: +439 / −17.
      • check-governed-merges --pr 22849: NOT governed.
    • Diff, read:
      • compile.ts's --strict-body block now reads lowering.bodylessCallables: one row per callable that no slot lowered into a body, each once, keyed by ref.
      • The placeholder row is gone. No new --json field, and no published export (lower-callables.ts is reachable from none of @objectstack/cli's four entries).
    • Contract review:
      • The first CONTRACT_REVIEW_TIER record, 6109936970, read FAIL on 11d4d8c6e: the changeset said every single-walk stack keeps its verdict, and the one-walk shared-handler shape was unpinned.
      • Patch round 1 and 1b fixed both.
      • The record 6110206259 on this head reads PASS. Both records were rendered by isolated reviewers and adopted by this seat.
    • CI on e583a93803, latest run per name: 31 success, 3 skipped by design. mergeable_state: clean.
    • Prose, sentence by sentence against the diff:
      • The changeset: the opening paragraph and the population sentence.
      • The four BREAKING bullets: issues, the header, the double walk, and the one-walk shared handler.
      • The one-line fix, and the Unchanged paragraph scoped to "each callable bound at one slot".
      • The PR body's verdict paragraph and probe table: the seat wrote the dev's replacement sentences and row J.
      • All true against the diff.
    • Tests:
      • New door pins: 1 and 4 refused handlers give headers 1 and 4; 0 gives no header.
      • A unit case for each moved shape.
      • Reverse legs A, B′, C and D each turned exactly the expected pins red.

    Deviations, accepted:

    • The fix lands in lower-callables.ts: the deduplicated population can only be built where refs are minted.
    • The pins live in two new files.
    • Shape (b) was added in patch round 1.

    Findings:

    Landing next: ready, then auto-merge through the queue. PR #22859 (#22837) edits the same four lines of lower-callables.ts; it merges main after this lands.


    Generated by Claude Code

  8. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #22849 → 6364375b51

    domain:cli seat 1 (#6024) · marchtian · session_01B5CHJNXuuqzChM4w6hkTN4 · 2026-10-11T15:19Z


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratebugSomething isn't workingdomain:clipriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions