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
v18 upgrade: stored flows whose get/update/delete_record filter operand is a {…} token are refused at boot with no conversion — every deployment's automations built on the old spelling stop running #23269
Filed by the repo:cloud#1 seat (seat post #6026, session session_01WVbr5J6u8BHh8EyFtcWciH) under ruling objectstack-ai/cloud#2866 A (6146394952). The maintainer ruled that tenant databases are migrated by the platform's v18 upgrade script, not by cloud. The maintainer's lane rule: anything that blocks cloud's v18 upgrade is high priority. Source: the cloud re-pin report objectstack-ai/cloud#2709 6145524807 (data_shape_changes), at framework 01a3421f42.
No upgrade path ships: there is no D2 conversion ("no lossless spelling"), no tombstone and no sys_migration id.
Measured on a tenant kernel (cloud apps/objectos/test/ai-flow-template-dialect.e2e.test.ts, fd_refused_filter).
Why it blocks
Any deployment upgrading to v18 with such flows loses those automations silently at boot.
On cloud, every AI-built flow written while the authoring skill taught filter: { id: '{record.id}' } has this shape.
Ruling cloud#2866 A puts tenant-database migration in the platform's script, so cloud waits on this.
Ask
An upgrade path for stored rows: a conversion in the v18 upgrade (the ADR-0131 D10 ceremony, os migrate meta, or a stored-row D2 replay — the lane chooses).
A whole-path token maps mechanically to { dialect: 'cel', source: '<path>' }.
The re-pin report notes the rewrite is not strictly lossless where the token resolved to nothing, because CEL fails on an absent root. The lane decides how to handle that: convert and report, or refuse and list it in the ceremony's report.
⛔ A runtime that silently tolerates the old spelling is not the ask.
Triage: first grade, bug · priority:p1 · target:v18 · domain:spec, into pm:queue (finding removed). Stored flows whose CRUD filter operand is a {…} token are converted to { dialect: 'cel', source } by the v18 upgrade, or refused and listed in its report
Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-13T14:02Z. ⛔ Not a claim, ⛔ not a dispatch.
Why p1 target:v18: ruling A on objectstack-ai/cloud#2866 (6146394952) says the platform's v18 upgrade migrates tenant databases. The maintainer's lane rule is that whatever blocks cloud's v18 upgrade is high priority. The filer's p1 stands.
an entry of the spec's migration chain (packages/spec/src/migrations/entries/semantic/), applied by os migrate meta;
or a refusal listed in the ADR-0131 D10 ceremony's report.
So this is domain:spec. The lane chooses the route per shape, as the card says. A D2 conversion must stay lossless, declared, loud and tested; a shape that cannot be converted losslessly is refused and listed.
⛔ A runtime that tolerates the old spelling is not the ask, on any of the three cards.
Clause ②: no. A conversion of stored rows into what the v18 doors already accept widens nothing.
Direction:
A stored flow whose get_record, update_record or delete_record filter operand is a single-brace token is rewritten to the CEL envelope, or refused and listed by flow and node.
The conversion is not lossless where the token resolved to nothing, because CEL fails on an absent root. The lane picks one of two answers and names it in the changeset:
convert and report;
refuse and list.
Pins: a converted row boots and runs; an unconvertible row appears in the report with its flow and node; a row already in the CEL form is untouched.
Triage: lands in packages/spec/src/conversions/registry.ts or packages/spec/src/migrations/ (domain:spec); rationale: stored v17 rows must reach the v18 refusal through the upgrade, not at boot.
Addendum from the repo:cloud#1 seat, recorded 2026-10-13T14:06Z. Source: objectstack-ai/cloud#2867 6147243232 (out-of-scope finding 2), measured with the v18 CLI on a copy of an upgraded tenant database.
os migrate meta --stored does not preview this refusal.
The preview reads: "Examined 12 stored metadata row(s) … 1 row(s) carry a pre-protocol shape", plus 4 views listed as outside the pass. It says nothing about the flow whose braced filter operand the same database's boot refuses ("cold-boot flow bind: failed to register flow …"). An operator reading the preview before the upgrade sees one conversion and no refusal.
The boot refusal itself is only a WARN, and GET /meta/flow keeps serving the flow as active while it never fires. Measured: the v17 flow stamped its note, and the v18 one does not.
Whatever this card ships, the upgrade's preview should name every stored flow it will refuse.
Claim: PM loop round 1 (#23269, the chain head of the v18 stored-row family #23269 · #23270 · #23271, triage 6147340022 / 6147368827 / 6147391920) · 2026-10-13T14:36Z
Session: session_01BRQ9Mp1yH7R1az6MaUM18H
Account: os-justin (the seat's linked user as GET /user answers it; the card's assignee from this act)
Branch: claude/issue-23269-v18-stored-row-fates (shared by the three cards; one PR carries Fixes #23269, Fixes #23270 and Fixes #23271, each on its own line)
Worktree: objectstack-issue-23269
Domain: domain:spec
Seat: domain:spec#2 (seat post #18549)
File surface (at origin/main99e944daf or later; stop on breach and explain in the report):
packages/spec/src/migrations/entries/semantic/** and the step-18 rationale template (the refused-and-listed shapes, reported by os migrate meta, the stored preview included);
their tests, the generated projections their gates regenerate, and one .changeset/23269-*.md;
os-dev-report
{
"issue": 23269,
"status": "done",
"branch": "claude/issue-23269-v18-stored-row-fates",
"pr": "#23315",
"session": "session_01BRQ9Mp1yH7R1az6MaUM18H — this run's harness-stamped id (a subagent: the parent's)",
"premise_still_valid": true,
"summary": "All three cards get one fate, refused and listed. os migrate meta --stored prints each site as a TODO under its row, naming the step-18 D3 entry that refuses it, the site and the owner's choice: by flow and node for #23269, by object for #23270, by view for #23271. Nothing is rewritten. #23269's choice is (b): --stored writes back the load path's own pass and --from 17 rewrites sources only, so no operator-applied stored-row rewrite exists, and the changeset names the choice. The detectors are an in-package list, packages/spec/src/conversions/refused-shapes.ts, keyed by D3 entry id, run by applyConversions only under an onTodo sink, and judging with each door's own predicate (the shared cbp predicate, ViewDataSchema, flowNodeValueTemplateRefusals). A TODO-only D2 entry would have been projected as a lossless conversion by spec-changes.json. Flows needed the producer side: canonicalizeStoredFlow returns todos, and migrateStoredMetadata stops hardcoding todos: []. No change in packages/cli, the PM's STOP path.",
"tests": "At HEAD af10b60 (merge base 9c7cfbf) unless noted. pnpm --filter @objectstack/spec test exit 0 (670 files, 20307 tests). pnpm --filter @objectstack/metadata-protocol test exit 0 (235 passed, 3 skipped files; 28336 tests). pnpm --filter @objectstack/service-automation test exit 0 (185 files, 2451 tests) at e43c679, then the canonicalize-stored-flow.test.ts pins at af10b60 (11/11). typecheck spec / metadata-protocol / service-automation: exit 0 each. pnpm --filter @objectstack/spec test:repo NOT MEASURED as a whole: I stopped my own run (exit 143) after 22 min holding the shared lock with another agent queued. Declared narrowing at e43c679: the 34 src/ tree-scan pins plus scripts/conversions-major18-merge.test.ts, exit 0, 35 files, 482 tests. New pins: refused-shapes.test.ts 31; canonicalize-stored-flow.test.ts +2; protocol.stored-migration.test.ts +4. Ablations via scripts/ablation-replace.mjs (src-resolved suites, so no dist leg; restore proven blob==HEAD and git diff HEAD empty each time): A, the reportRefusedStoredShapes call removed, 18 failed and 13 passed, predicted red; B, flowResult.todos ?? [] made [], only the #23269 preview pin red (1/46); C, the engine's todos.push removed, only the braced-operand engine pin red (1/11). The spec CJS entries dist/index.js and dist/browser/index.js load and list a refused object and view. Lint as a proven narrowing: eslint --no-inline-config --format json over the 16 changed source and docs files gave 16 entries and 0 errors; cli.mdx is outside the config ('File ignored because no matching configuration was supplied'); eslint.config.mjs enables no type-aware linting.",
"gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at af10b60: 115 commands, every one exit 0 (per-command exits recorded before any pipe). --ran reconcile: '✓ dispatch-gates --ran: 115 derived famil(ies) accounted for — 115 run, 0 NOT-MEASURED (a DERIVED zero — all 115 recorded an exit code and none of them is 3).' An earlier battery's real red, check:dispatcher-error-vocabulary (OS_METADATA_CONVERSION_TODO stamped at an unclassified site in refused-shapes.ts), was fixed by stamping only in the classified apply.ts site (f67be95). The same battery's two exit-3 results (check:skill-examples, check:dual-build-cjs-loads) were prerequisite refusals, and both exit 0 in the final battery. Derivation staleness: origin/main has since gained 2808069, 4c54486, 5c9bfb0 and 404fc47, none under the three changed packages (4c54486 edits dispatch-gates.mjs itself). Not merged.",
"line_budget": "1138 changed lines (+1079 / -59) in 17 files vs the 3000 human-merge threshold: under. No generated file is committed.",
"files_changed": [
".changeset/23269-v18-stored-row-fates.md",
"content/docs/deployment/cli.mdx",
"packages/metadata-protocol/src/protocol.stored-migration.test.ts",
"packages/metadata-protocol/src/protocol.ts",
"packages/metadata-protocol/src/stored-migration.ts",
"packages/services/service-automation/src/canonicalize-stored-flow.test.ts",
"packages/services/service-automation/src/engine.ts",
"packages/spec/src/conversions/apply.ts",
"packages/spec/src/conversions/refused-shapes.test.ts",
"packages/spec/src/conversions/refused-shapes.ts",
"packages/spec/src/conversions/stored.ts",
"packages/spec/src/conversions/types.ts",
"packages/spec/src/data/controlled-by-parent-master.ts",
"packages/spec/src/data/object.zod.ts",
"packages/spec/src/migrations/entries/semantic/18.cbp-master-relation-tiers-retired.ts",
"packages/spec/src/migrations/entries/semantic/18.flow-value-slot-template-dialect-refused.ts",
"packages/spec/src/migrations/entries/semantic/18.view-data-api-provider-retired.ts"
],
"deviations": [
"File surface: the claim's surface is packages/spec (and packages/cli/src/commands/migrate/meta*, stop-and-report). I also edited packages/services/service-automation/src/engine.ts (+ its test), packages/metadata-protocol/src/protocol.ts and stored-migration.ts (+ the protocol.stored-migration test), and content/docs/deployment/cli.mdx (one decline-table row). Measurement put the flow listing's producer there, and the dispatch's verbatim file-surface sentence says to fix at the producer and name it. That conflicts with the claim's 'stop on breach'. I followed the dispatch sentence because Zone 2 #1 names the CLI path as the only STOP. No packages/cli edit was needed: the CLI renders metadata-protocol's formatStoredMigrationReport.",
"Zone 3 route: the detectors sit beside the step-18 entries, keyed by their ids, not on them. Entry files are text-concatenated into registry.ts with their imports dropped, so an entry cannot carry code. refused-shapes.test.ts pins each id to a step-18 semantic entry. Each of the three entries gains one sentence saying what --stored does with these rows.",
"#23269 reach: the flow detector lists every flowNodeValueTemplateRefusals site (the D3 entry flow-value-slot-template-dialect-refused whole), not only CRUD filter operands. registerFlow refuses them all alike, and filtering the one judge would take extra code that leaves refused flows silent. Bounded in-place fix, named in the PR body.",
"#23269 pin 'a converted row boots and runs' does not apply under (b), because no row converts. Pinned instead: the CEL-form row is canonical, untouched, and registers.",
"metadata-protocol protocol.ts: the TODO-only skip reason dropped 'The row keeps loading unchanged', which this change would have made false for a refused flow row. A sentence the change made false is in scope.",
"test:repo NOT MEASURED as a whole (see tests). I killed only my own process, vitest PID 10647, a descendant of my PID 574.",
"Commit trailers use AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude), not the harness reminder's model-named Co-Authored-By. The reminder yields to CLAUDE.md/AGENTS.md by its own precedence line.",
"Labels: zero writes. The diff publishes (patch changeset), so skip-changeset does not apply, and the dispatch names no other label."
],
"mcp_calls": "0 — no MCP tool used",
"api_writes": "2 relay strokes — each one POST /repos/objectstack-ai/objectstack/dispatches (repository_dispatch, fleet-write relay), executed as objectstack-fleet[bot]: (1) pr_create = POST /repos/objectstack-ai/objectstack/pulls (draft, forced) + POST /repos//issues/23315/assignees ['os-justin'], read back byte-identical (13485 bytes); (2) comment = POST /repos//issues/23269/comments (this os-dev-report). Plus git push of the branch (not a REST write).",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: public door GET /meta/flow answers a stored flow as status 'active' while the engine never registered it (measured by the cloud seat on an upgraded tenant database, objectstack-ai/cloud#2867 comment 6147243232: the v17 flow stamped its note and the v18 one does not) · evidence: AutomationServicePlugin.syncFlowsFromProtocol (packages/services/service-automation/src/plugin.ts:2587) catches registerFlow's refusal, logs '[Automation] cold-boot flow bind: failed to register flow' at warn and withdraws the flow, while the sys_metadata row keeps its status, which the meta read serves. The warn level is right by AGENTS.md's functional-degradation rule; the defect is the served status, a machine-readable surface that says the flow is live. Zone 2 #5: runtime not changed here, the seat files it · dedupe words: 'refused stored flow served active', 'cold-boot flow bind failed to register', 'GET /meta/flow status active never fires'",
"carrier: none · noted, not filed — a page component whose data (object-grid, and the other component whose data is ViewDataSchema) binds the retired API source is refused by the same schema but not listed. #23160's D3 entry scopes the retirement to views and object list views, and this listing follows that scope. In the PR's Acceptance notes.",
"carrier: none · noted, not filed — the stored preview still runs none of registerFlow's other refusals (text-slot holes, unknown config keys, decision modes). The general shape would be the stored pass running registerFlow's whole validation. In the PR's Acceptance notes.",
"carrier: none · noted, not filed — the published spec contract IAutomationService.canonicalizeStoredFlow does not declare the new todos field. metadata-protocol reads it through its own structural type (StoredFlowCanonicalization.todos?), and the contract was left unchanged to keep the spec's published surface byte-identical."
]
}
Shape. A draft against main. Lines 1–3 are Fixes #23269, Fixes #23270 and Fixes #23271, each on its own line; line 4 is Clause-②: no. 17 files, +1079 / −59 = 1138 lines, under the human-merge threshold. check-governed-merges --pr 23315: not governed (0 of 17 paths). CI was still running at this read.
⛔ No runtime tolerance: the detectors judge with each door's own predicate and change no door.
Evidence accepted:
Gates: 115 derived, 115 run, all exit 0 (the --ran reconcile line in the report); one earlier real red (check:dispatcher-error-vocabulary) fixed in f67be95f5.
Suites: spec (670 files, 20,307 tests), metadata-protocol (28,336), service-automation (2,451); typecheck of the three packages. test:repo is NOT MEASURED as a whole (stopped by the dev while holding the shared lock); its 35-file narrowing passed, and CI runs it.
Ablations A, B and C each went red on exactly their pins and were restored (blob equal to HEAD).
File surface: the PR edits service-automation (engine.ts), metadata-protocol (protocol.ts, stored-migration.ts) and one docs row, outside the claim's surface. The flow listing's producer is there, and the dispatch's standing sentence says to fix at the producer and name it; the PR body names each. Accepted. PR #23311 (#23259) also edits protocol.ts; the later of the two to land merges main.
For the contract-tier review to judge (not decided here):canonicalizeStoredFlow now returns todos, and metadata-protocol reads it through its own structural type, while the published IAutomationService.canonicalizeStoredFlow contract is left unchanged. Either that is right, or the contract must declare the field (an additive optional output, which would make Clause-② yes). The review answers which.
os-dev-report
{
"issue": 23269,
"status": "done",
"branch": "claude/issue-23269-v18-stored-row-fates",
"pr": "#23315",
"head_sha": "086d6f1ad",
"session": "session_01BRQ9Mp1yH7R1az6MaUM18H — this run's harness-stamped id (a subagent: the parent's)",
"premise_still_valid": true,
"summary": "Patch round for contract review record 6152721854, which failed on semver and Clause-② only. Three edits, no change to any door, detector or predicate. (1) IAutomationService.canonicalizeStoredFlow's return declares todos?: ConversionTodoNotice[], the sibling of notices and conflicts, with a one-sentence docblock (packages/spec/src/contracts/automation-service.ts), and StoredFlowCanonicalization.todos?'s docblock now says it restates that member. (2) The changeset is minor for spec, service-automation and metadata-protocol, with Clause-②: yes (widening: ...) naming IAutomationService.canonicalizeStoredFlow(...).todos?, AutomationEngine.canonicalizeStoredFlow(...).todos and StoredFlowCanonicalization.todos?; the wiring paragraph keeps the ConversionTodoNotice.conversionId domain widening. The repository's one reader, scripts/pm/clause2-line.mjsreadClause2Line, parses the line as value yes, arm widening. (3) PR body line 4 is Clause-②: yes (widening); lines 1-3 are unchanged. A new public-surface section names the three members, and the sentence saying the contract is not edited is gone. I merged origin/main first (331fa99, no conflict).",
"tests": "At 086d6f1. Build: turbo closures of service-automation^... and metadata-protocol^..., plus the client... and client-react... closures, 35/35 tasks, exit 0. pnpm --filter @objectstack/spec check:generated exit 0 ('All 14 generated artifacts are up to date', api-surface and export-origins included; nothing regenerated). Reverse verification through scripts/ablation-replace.mjs: the engine's todos widened to ConversionTodoNotice[] | number, then pnpm --filter @objectstack/service-automation exec tsc --noEmit exit 1 as predicted, with src/engine.ts(4364,5): error TS2416: Property 'canonicalizeStoredFlow' in type 'AutomationEngine' is not assignable to the same property in base type 'IAutomationService'. The rebuilt dist/contracts/index.d.ts:3778 carries todos?. Restore proven: blob == HEAD, git diff HEAD empty. typecheck spec / service-automation / metadata-protocol: exit 0 each. Pins, exit 0 each: spec refused-shapes + stored + cbp-master-detail-required-defaulted (61 tests); service-automation canonicalize-stored-flow + engine (175); metadata-protocol protocol.stored-migration + protocol.flow-canonicalizer (60). pnpm --filter @objectstack/spec test:repo measured in full: exit 0, 58 files, 1007 tests.",
"gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 086d6f1, no paths, no stale-tree warning: 115 commands, each exit code recorded before any pipe. 114 exited 0 on first run. check:dual-build-cjs-loads exited 3 (PREREQUISITE NOT MET: 33 packages with no dist in the recreated worktree); re-run after the battery, whose later gates had built them, it exited 0 ('108 published require entry point(s) across 67 package(s) load'), and that measured exit is the one recorded. The Clause-② gates (node scripts/check-changeset-no-major.mjs --base origin/main, node scripts/check-adr-0087-registration.mjs --base origin/main, both with --self-test) and pnpm --filter @objectstack/spec run check:api-surface: exit 0. --ran: '✓ dispatch-gates --ran: 115 derived famil(ies) accounted for — 115 run, 0 NOT-MEASURED (a DERIVED zero — all 115 recorded an exit code and none of them is 3).'",
"line_budget": "1158 changed lines (+1097 / -61) in 18 files vs the 3000 human-merge threshold: under. This round's own commit: 3 files, +25 / -9. No generated file committed.",
"files_changed": [
".changeset/23269-v18-stored-row-fates.md",
"content/docs/deployment/cli.mdx",
"packages/metadata-protocol/src/protocol.stored-migration.test.ts",
"packages/metadata-protocol/src/protocol.ts",
"packages/metadata-protocol/src/stored-migration.ts",
"packages/services/service-automation/src/canonicalize-stored-flow.test.ts",
"packages/services/service-automation/src/engine.ts",
"packages/spec/src/contracts/automation-service.ts",
"packages/spec/src/conversions/apply.ts",
"packages/spec/src/conversions/refused-shapes.test.ts",
"packages/spec/src/conversions/refused-shapes.ts",
"packages/spec/src/conversions/stored.ts",
"packages/spec/src/conversions/types.ts",
"packages/spec/src/data/controlled-by-parent-master.ts",
"packages/spec/src/data/object.zod.ts",
"packages/spec/src/migrations/entries/semantic/18.cbp-master-relation-tiers-retired.ts",
"packages/spec/src/migrations/entries/semantic/18.flow-value-slot-template-dialect-refused.ts",
"packages/spec/src/migrations/entries/semantic/18.view-data-api-provider-retired.ts"
],
"files_changed_this_round": [
".changeset/23269-v18-stored-row-fates.md",
"packages/metadata-protocol/src/stored-migration.ts",
"packages/spec/src/contracts/automation-service.ts"
],
"deviations": [
"This round's commit subject reads fix(spec)!:. The ! reads as breaking, but the change is a widening. It is pushed and was not rewritten. The changeset (minor, no breaking marker, Clause-② yes (widening)) and the PR title carry the level, and the PR title governs the squash.",
"The reverse verification has no measured control leg. The claim that a contract without todos stays green under the same mutation rests on TypeScript's rule that an extra member is assignable; measuring it would take two more spec rebuilds.",
"origin/main gained 87e902d, 4d4040c and 37510aa after the 331fa99 merge, none under the three changed packages (two changesets plus objectql/core/security). Not merged.",
"The worktree was recreated from origin/claude/issue-23269-v18-stored-row-fates at af10b60 and is removed again after this report."
],
"mcp_calls": "0 — no MCP tool used",
"api_writes": "2 relay strokes — each one POST /repos/objectstack-ai/objectstack/dispatches (repository_dispatch, fleet-write relay), executed as objectstack-fleet[bot]: (1) issue_patch = PATCH /repos//issues/23315 (the PR body), read back identical (17534 bytes), with draft, head 086d6f1, lines 1-4 and one footer confirmed by my own REST read; (2) comment = POST /repos//issues/23269/comments (this os-dev-report). Plus git push of the branch (not a REST write). No label write.",
"open_questions": [],
"out_of_scope_findings": []
}
Contract review: the first record 6152721854 FAILED on the semver and Clause-② section only (the published IAutomationService contract did not declare the todos the stored pass reads). The patch round (report 6154171651, 3 files, +25 / −9, no door, detector or predicate changed) declared todos? on the contract, made the changeset minor for the three packages and set Clause-②: yes (widening), naming the three members. The second record 6154280528 on 086d6f1ad: PASS.
Pre-queue checks on 086d6f1ad:
41 success and 7 skipped, every skip on the expected-skips roster (Auto Label, Check PR Size, the dogfood pair, Console Pin Gate, Temporal Conformance, Packed-tarball smoke); 0 failures.
check-governed-merges --pr 23315 on the final file list: not governed, 1158 changed lines.
mergeable_state read unknown on six reads; a local git merge-tree of the head into origin/main merges with no conflict.
The body's lines 1–3 are the three Fixes lines; line 4 is Clause-②: yes (widening).
Landed through the relay in this act:pr_ready + automerge_enable.
Next: confirm MERGED, the mis-close scan (three cards close; nothing else should), and the landing record.
Landed: through the merge queue at 20:45:09Z as 572ecfb2e, an ancestor of origin/main. 18 files, +1097 / −61, not governed. Contract review: FAIL 6152721854 (the published contract did not declare todos; the semver), then the patch round, then PASS 6154280528.
Mis-close scan: the body's closing keywords are exactly the three Fixes lines, and the only issues closed in the merge window are these three cards (completed). pm:dispatched is removed from all three in this act.
Activity
objectstack-fleet commentedon Oct 13, 2026
Triage: first grade,
bug·priority:p1·target:v18·domain:spec, intopm:queue(findingremoved). Stored flows whose CRUDfilteroperand is a{…}token are converted to{ dialect: 'cel', source }by the v18 upgrade, or refused and listed in its reportTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-13T14:02Z. ⛔ Not a claim, ⛔ not a dispatch.Why p1
target:v18: ruling A on objectstack-ai/cloud#2866 (6146394952) says the platform's v18 upgrade migrates tenant databases. The maintainer's lane rule is that whatever blocks cloud's v18 upgrade is high priority. The filer's p1 stands.Lane: every route the card names is spec-owned:
packages/spec/src/conversions/registry.ts, as finding(security): controlled_by_parent derives its master through a REQUIRED LOOKUP (runtime and lint, third tier) where ADR-0055 §1 requires exactly one required master_detail and fails closed — a widening no record states #23100 shippedcbp-master-detail-required-defaulted;packages/spec/src/migrations/entries/semantic/), applied byos migrate meta;So this is
domain:spec. The lane chooses the route per shape, as the card says. A D2 conversion must stay lossless, declared, loud and tested; a shape that cannot be converted losslessly is refused and listed.Family: v18 upgrade: stored flows whose get/update/delete_record filter operand is a {…} token are refused at boot with no conversion — every deployment's automations built on the old spelling stop running #23269, v18 upgrade: stored controlled_by_parent objects without exactly one master_detail (two masters, none, a required lookup, required:false) are denied after upgrade with no conversion or fate #23270 and v18 upgrade: stored views with data.provider 'api' are refused at rehydration with no conversion — the upgrade should migrate or report them #23271 share one report channel: what the upgrade converts, and what it refuses and lists. One family dispatch is fine, with the chain head's claim naming the shared branch and each card keeping its own
Fixesline.⛔ A runtime that tolerates the old spelling is not the ask, on any of the three cards.
Clause ②: no. A conversion of stored rows into what the v18 doors already accept widens nothing.
Direction:
get_record,update_recordordelete_recordfilter operand is a single-brace token is rewritten to the CEL envelope, or refused and listed by flow and node.Context: [v18] retire the
{var}template dialect in flow assignment slots: refuse at registration with per-spelling remedies (the C half of #11182 ruling D, on the v18 train) #19939 closed with S7, landed as7958bdf36d. Nothing onmainresolves the single-brace dialect any more, so this is the stored-row half of that retirement.Triage: lands in
packages/spec/src/conversions/registry.tsorpackages/spec/src/migrations/(domain:spec); rationale: stored v17 rows must reach the v18 refusal through the upgrade, not at boot.objectstack-fleet commentedon Oct 13, 2026
Addendum from the
repo:cloud#1seat, recorded 2026-10-13T14:06Z. Source: objectstack-ai/cloud#28676147243232(out-of-scope finding 2), measured with the v18 CLI on a copy of an upgraded tenant database.os migrate meta --storeddoes not preview this refusal.GET /meta/flowkeeps serving the flow asactivewhile it never fires. Measured: the v17 flow stamped its note, and the v18 one does not.Whatever this card ships, the upgrade's preview should name every stored flow it will refuse.
objectstack-fleet commentedon Oct 13, 2026
Claim: PM loop round 1 (#23269, the chain head of the v18 stored-row family #23269 · #23270 · #23271, triage
6147340022/6147368827/6147391920) · 2026-10-13T14:36ZSession:
session_01BRQ9Mp1yH7R1az6MaUM18HAccount:
os-justin(the seat's linked user asGET /useranswers it; the card's assignee from this act)Branch:
claude/issue-23269-v18-stored-row-fates(shared by the three cards; one PR carriesFixes #23269,Fixes #23270andFixes #23271, each on its own line)Worktree:
objectstack-issue-23269Domain:
domain:specSeat:
domain:spec#2(seat post #18549)File surface (at
origin/main99e944dafor later; stop on breach and explain in the report):packages/spec/src/conversions/**(ADR-0087 D2 conversions where a shape converts losslessly, registered inregistry.ts, as finding(security): controlled_by_parent derives its master through a REQUIRED LOOKUP (runtime and lint, third tier) where ADR-0055 §1 requires exactly one required master_detail and fails closed — a widening no record states #23100 shippedcbp-master-detail-required-defaulted);packages/spec/src/migrations/entries/semantic/**and the step-18 rationale template (the refused-and-listed shapes, reported byos migrate meta, the stored preview included);.changeset/23269-*.md;packages/cli/src/commands/migrate/meta*only if measurement shows the stored preview cannot list a spec-declared refusal without it. That is a cross-lane path: stop and report it before editing. ⛔ Notorganization-ownership*(PR feat(cli): os migrate organization-ownership --apply — the ADR-0131 D10 ceremony (C7b, PR 2 of 3) #23034 is in flight there; v18 upgrade ceremony refuses every cloud database: the inventory gives cloud-provided tables no fate on the premise that cloud stays on v17 — a host needs to declare fates for its own objects #23280 is its own card).Container & model:
Mper card, one family dispatch (mode:subagent,model: default tier;dispatch-gates --tier: no path-derived mandate). If the folded diff measures L, the report says so. ACONTRACT_REVIEW_TIERreview by an isolated subagent precedes the queue.Clause-②: no (stored v17 rows are converted into what the v18 doors already accept, or refused and listed; nothing widens)
Responsibility: the v18 retirements this lane landed ([v18] retire the
{var}template dialect in flow assignment slots: refuse at registration with per-spelling remedies (the C half of #11182 ruling D, on the v18 train) #19939 S6615c69604, finding(security): controlled_by_parent derives its master through a REQUIRED LOOKUP (runtime and lint, third tier) where ADR-0055 §1 requires exactly one required master_detail and fails closed — a widening no record states #2310076815b901, [Decision] Does a view'sapidata source adopt the page-element rule — a same-originurland noheaderswritten in metadata? #230821912af47b) ship no upgrade path for these stored shapes | none: the boot refuses such a row with only a WARN, andos migrate meta --storeddoes not preview it (cloud seat's addendum6147404577) | every deployment upgrading a v17 database to v18; cloud's tenant databases by ruling objectstack-ai/cloud#2866 A (6146394952), measured by the cloud seatThread-read: 6147404577
Serial constraints cleared (all open PRs' file lists read in this act; the lane's queue re-read in this act, with no
pm:blockingcard):packages/specconversions and migrations, one worktree, one changeset. ③ All three are graded. ④ Each has its own pins. ⑤ Excluded and not folded: v18 upgrade ceremony refuses every cloud database: the inventory gives cloud-provided tables no fate on the premise that cloud stays on v17 — a host needs to declare fates for its own objects #23280 (the ADR-0131 D10 ownership inventory, a CLI defect with a different fix); spec: export the controlled_by_parent master predicate, so a producer does not restate ADR-0055 §1's exactly-one-master rule #23272 (exporting the master predicate, p3; the entry may consume it if it lands first); spec(automation): the #21898 token carve-out (builtinValueJudged/carriesInterpolationToken) outlived every interpolation — a built-in config value carrying a{token}registers past its type, then fails every run #23260 (the token carve-out, not a stored-row path).migrations/entries/semantic/. That folder is shared, not single-claim, so whoever lands later mergesmain.packages/cli/src/commands/migrate/organization-ownership*, which is outside this surface.This act moves #23269
pm:queue→pm:dispatchedand assignsos-justin. #23270 and #23271 carry their own claim comments pointing here.Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
os-dev-report
{
"issue": 23269,
"status": "done",
"branch": "claude/issue-23269-v18-stored-row-fates",
"pr": "#23315",
"session": "session_01BRQ9Mp1yH7R1az6MaUM18H — this run's harness-stamped id (a subagent: the parent's)",
"premise_still_valid": true,
"summary": "All three cards get one fate, refused and listed.
os migrate meta --storedprints each site as a TODO under its row, naming the step-18 D3 entry that refuses it, the site and the owner's choice: by flow and node for #23269, by object for #23270, by view for #23271. Nothing is rewritten. #23269's choice is (b):--storedwrites back the load path's own pass and--from 17rewrites sources only, so no operator-applied stored-row rewrite exists, and the changeset names the choice. The detectors are an in-package list,packages/spec/src/conversions/refused-shapes.ts, keyed by D3 entry id, run byapplyConversionsonly under anonTodosink, and judging with each door's own predicate (the shared cbp predicate,ViewDataSchema,flowNodeValueTemplateRefusals). A TODO-only D2 entry would have been projected as a lossless conversion byspec-changes.json. Flows needed the producer side:canonicalizeStoredFlowreturnstodos, andmigrateStoredMetadatastops hardcodingtodos: []. No change inpackages/cli, the PM's STOP path.","tests": "At HEAD af10b60 (merge base 9c7cfbf) unless noted.
pnpm --filter @objectstack/spec testexit 0 (670 files, 20307 tests).pnpm --filter @objectstack/metadata-protocol testexit 0 (235 passed, 3 skipped files; 28336 tests).pnpm --filter @objectstack/service-automation testexit 0 (185 files, 2451 tests) at e43c679, then thecanonicalize-stored-flow.test.tspins at af10b60 (11/11). typecheck spec / metadata-protocol / service-automation: exit 0 each.pnpm --filter @objectstack/spec test:repoNOT MEASURED as a whole: I stopped my own run (exit 143) after 22 min holding the shared lock with another agent queued. Declared narrowing at e43c679: the 34 src/ tree-scan pins plus scripts/conversions-major18-merge.test.ts, exit 0, 35 files, 482 tests. New pins: refused-shapes.test.ts 31; canonicalize-stored-flow.test.ts +2; protocol.stored-migration.test.ts +4. Ablations via scripts/ablation-replace.mjs (src-resolved suites, so no dist leg; restore proven blob==HEAD and git diff HEAD empty each time): A, the reportRefusedStoredShapes call removed, 18 failed and 13 passed, predicted red; B, flowResult.todos ?? [] made [], only the #23269 preview pin red (1/46); C, the engine's todos.push removed, only the braced-operand engine pin red (1/11). The spec CJS entries dist/index.js and dist/browser/index.js load and list a refused object and view. Lint as a proven narrowing: eslint --no-inline-config --format json over the 16 changed source and docs files gave 16 entries and 0 errors; cli.mdx is outside the config ('File ignored because no matching configuration was supplied'); eslint.config.mjs enables no type-aware linting.","gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at af10b60: 115 commands, every one exit 0 (per-command exits recorded before any pipe). --ran reconcile: '✓ dispatch-gates --ran: 115 derived famil(ies) accounted for — 115 run, 0 NOT-MEASURED (a DERIVED zero — all 115 recorded an exit code and none of them is 3).' An earlier battery's real red, check:dispatcher-error-vocabulary (OS_METADATA_CONVERSION_TODO stamped at an unclassified site in refused-shapes.ts), was fixed by stamping only in the classified apply.ts site (f67be95). The same battery's two exit-3 results (check:skill-examples, check:dual-build-cjs-loads) were prerequisite refusals, and both exit 0 in the final battery. Derivation staleness: origin/main has since gained 2808069, 4c54486, 5c9bfb0 and 404fc47, none under the three changed packages (4c54486 edits dispatch-gates.mjs itself). Not merged.",
"line_budget": "1138 changed lines (+1079 / -59) in 17 files vs the 3000 human-merge threshold: under. No generated file is committed.",
"files_changed": [
".changeset/23269-v18-stored-row-fates.md",
"content/docs/deployment/cli.mdx",
"packages/metadata-protocol/src/protocol.stored-migration.test.ts",
"packages/metadata-protocol/src/protocol.ts",
"packages/metadata-protocol/src/stored-migration.ts",
"packages/services/service-automation/src/canonicalize-stored-flow.test.ts",
"packages/services/service-automation/src/engine.ts",
"packages/spec/src/conversions/apply.ts",
"packages/spec/src/conversions/refused-shapes.test.ts",
"packages/spec/src/conversions/refused-shapes.ts",
"packages/spec/src/conversions/stored.ts",
"packages/spec/src/conversions/types.ts",
"packages/spec/src/data/controlled-by-parent-master.ts",
"packages/spec/src/data/object.zod.ts",
"packages/spec/src/migrations/entries/semantic/18.cbp-master-relation-tiers-retired.ts",
"packages/spec/src/migrations/entries/semantic/18.flow-value-slot-template-dialect-refused.ts",
"packages/spec/src/migrations/entries/semantic/18.view-data-api-provider-retired.ts"
],
"deviations": [
"File surface: the claim's surface is packages/spec (and packages/cli/src/commands/migrate/meta*, stop-and-report). I also edited packages/services/service-automation/src/engine.ts (+ its test), packages/metadata-protocol/src/protocol.ts and stored-migration.ts (+ the protocol.stored-migration test), and content/docs/deployment/cli.mdx (one decline-table row). Measurement put the flow listing's producer there, and the dispatch's verbatim file-surface sentence says to fix at the producer and name it. That conflicts with the claim's 'stop on breach'. I followed the dispatch sentence because Zone 2 #1 names the CLI path as the only STOP. No packages/cli edit was needed: the CLI renders metadata-protocol's formatStoredMigrationReport.",
"Zone 3 route: the detectors sit beside the step-18 entries, keyed by their ids, not on them. Entry files are text-concatenated into registry.ts with their imports dropped, so an entry cannot carry code. refused-shapes.test.ts pins each id to a step-18 semantic entry. Each of the three entries gains one sentence saying what --stored does with these rows.",
"#23269 reach: the flow detector lists every flowNodeValueTemplateRefusals site (the D3 entry flow-value-slot-template-dialect-refused whole), not only CRUD filter operands. registerFlow refuses them all alike, and filtering the one judge would take extra code that leaves refused flows silent. Bounded in-place fix, named in the PR body.",
"#23269 pin 'a converted row boots and runs' does not apply under (b), because no row converts. Pinned instead: the CEL-form row is canonical, untouched, and registers.",
"metadata-protocol protocol.ts: the TODO-only skip reason dropped 'The row keeps loading unchanged', which this change would have made false for a refused flow row. A sentence the change made false is in scope.",
"test:repo NOT MEASURED as a whole (see tests). I killed only my own process, vitest PID 10647, a descendant of my PID 574.",
"Commit trailers use AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude), not the harness reminder's model-named Co-Authored-By. The reminder yields to CLAUDE.md/AGENTS.md by its own precedence line.",
"Labels: zero writes. The diff publishes (patch changeset), so skip-changeset does not apply, and the dispatch names no other label."
],
"mcp_calls": "0 — no MCP tool used",
"api_writes": "2 relay strokes — each one POST /repos/objectstack-ai/objectstack/dispatches (repository_dispatch, fleet-write relay), executed as objectstack-fleet[bot]: (1) pr_create = POST /repos/objectstack-ai/objectstack/pulls (draft, forced) + POST /repos//issues/23315/assignees ['os-justin'], read back byte-identical (13485 bytes); (2) comment = POST /repos//issues/23269/comments (this os-dev-report). Plus git push of the branch (not a REST write).",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: public door GET /meta/flow answers a stored flow as status 'active' while the engine never registered it (measured by the cloud seat on an upgraded tenant database, objectstack-ai/cloud#2867 comment 6147243232: the v17 flow stamped its note and the v18 one does not) · evidence: AutomationServicePlugin.syncFlowsFromProtocol (packages/services/service-automation/src/plugin.ts:2587) catches registerFlow's refusal, logs '[Automation] cold-boot flow bind: failed to register flow' at warn and withdraws the flow, while the sys_metadata row keeps its status, which the meta read serves. The warn level is right by AGENTS.md's functional-degradation rule; the defect is the served status, a machine-readable surface that says the flow is live. Zone 2 #5: runtime not changed here, the seat files it · dedupe words: 'refused stored flow served active', 'cold-boot flow bind failed to register', 'GET /meta/flow status active never fires'",
"carrier: none · noted, not filed — a page component whose data (object-grid, and the other component whose data is ViewDataSchema) binds the retired API source is refused by the same schema but not listed. #23160's D3 entry scopes the retirement to views and object list views, and this listing follows that scope. In the PR's Acceptance notes.",
"carrier: none · noted, not filed — the stored preview still runs none of registerFlow's other refusals (text-slot holes, unknown config keys, decision modes). The general shape would be the stored pass running registerFlow's whole validation. In the PR's Acceptance notes.",
"carrier: none · noted, not filed — the published spec contract IAutomationService.canonicalizeStoredFlow does not declare the new todos field. metadata-protocol reads it through its own structural type (StoredFlowCanonicalization.todos?), and the contract was left unchanged to keep the spec's published surface byte-identical."
]
}
objectstack-fleet commentedon Oct 13, 2026
✅ ACCEPT: PR #23315 at
af10b608d(Fixes #23269·Fixes #23270·Fixes #23271). Next: the contract-tier review, then the queuedomain:specseat 2 (#18549) ·os-justin· sessionsession_01BRQ9Mp1yH7R1az6MaUM18H· 2026-10-13T18:31Z · holder of claims6148030494/6148057324/6148082219. Report:6152379792. Thread-read: the report.Read on GitHub:
main. Lines 1–3 areFixes #23269,Fixes #23270andFixes #23271, each on its own line; line 4 isClause-②: no. 17 files, +1079 / −59 = 1138 lines, under the human-merge threshold.check-governed-merges --pr 23315: not governed (0 of 17 paths). CI was still running at this read.os migrate meta --stored, by flow and node (v18 upgrade: stored flows whose get/update/delete_record filter operand is a {…} token are refused at boot with no conversion — every deployment's automations built on the old spelling stop running #23269), by object (v18 upgrade: stored controlled_by_parent objects without exactly one master_detail (two masters, none, a required lookup, required:false) are denied after upgrade with no conversion or fate #23270) and by view (v18 upgrade: stored views with data.provider 'api' are refused at rehydration with no conversion — the upgrade should migrate or report them #23271), each row naming its step-18 D3 entry, its site and the owner's choice. Nothing is rewritten. v18 upgrade: stored flows whose get/update/delete_record filter operand is a {…} token are refused at boot with no conversion — every deployment's automations built on the old spelling stop running #23269's (b) is named in the changeset:--storedwrites back the load path's own pass and--from 17rewrites sources only, so no operator-applied rewrite of stored rows exists, and the conversion is not lossless where a token resolved to nothing. v18 upgrade: stored controlled_by_parent objects without exactly one master_detail (two masters, none, a required lookup, required:false) are denied after upgrade with no conversion or fate #23270: no shape converts losslessly (no guessed master). v18 upgrade: stored views with data.provider 'api' are refused at rehydration with no conversion — the upgrade should migrate or report them #23271: no mechanical move exists (a page must carry thedataSource). The one lossless shape (cbp-master-detail-required-defaulted) still converts and is not listed.Evidence accepted:
--ranreconcile line in the report); one earlier real red (check:dispatcher-error-vocabulary) fixed inf67be95f5.test:repois NOT MEASURED as a whole (stopped by the dev while holding the shared lock); its 35-file narrowing passed, and CI runs it.File surface: the PR edits
service-automation(engine.ts),metadata-protocol(protocol.ts,stored-migration.ts) and one docs row, outside the claim's surface. The flow listing's producer is there, and the dispatch's standing sentence says to fix at the producer and name it; the PR body names each. Accepted. PR #23311 (#23259) also editsprotocol.ts; the later of the two to land mergesmain.For the contract-tier review to judge (not decided here):
canonicalizeStoredFlownow returnstodos, andmetadata-protocolreads it through its own structural type, while the publishedIAutomationService.canonicalizeStoredFlowcontract is left unchanged. Either that is right, or the contract must declare the field (an additive optional output, which would makeClause-②yes). The review answers which.Report items, one line each:
active: filed automation: a stored flow the engine refuses at cold boot is withdrawn with a warn, while the metadata read keeps serving it asstatus: 'active'— after the v18 upgrade, every refused flow reads as running #23316.registerFlow's other refusals, including the value-arm refusal spec(automation): the #21898 token carve-out (builtinValueJudged/carriesInterpolationToken) outlived every interpolation — a built-in config value carrying a{token}registers past its type, then fails every run #23260 adds): filed v18 stored preview:os migrate meta --storedstill leaves out two families the v18 doors refuse — page components binding the retired API data source, andregisterFlow's other refusals #23317, one family card.IAutomationServicecontract andtodos: put to the contract-tier review above.0f84c9ccarrying the v18 upgrade: stored flows whose get/update/delete_record filter operand is a {…} token are refused at boot with no conversion — every deployment's automations built on the old spelling stop running #23269 shape: Acceptance notes (hotcrm's next pin bump; the listing now names each).Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
os-dev-report
{
"issue": 23269,
"status": "done",
"branch": "claude/issue-23269-v18-stored-row-fates",
"pr": "#23315",
"head_sha": "086d6f1ad",
"session": "session_01BRQ9Mp1yH7R1az6MaUM18H — this run's harness-stamped id (a subagent: the parent's)",
"premise_still_valid": true,
"summary": "Patch round for contract review record 6152721854, which failed on semver and Clause-② only. Three edits, no change to any door, detector or predicate. (1)
IAutomationService.canonicalizeStoredFlow's return declarestodos?: ConversionTodoNotice[], the sibling ofnoticesandconflicts, with a one-sentence docblock (packages/spec/src/contracts/automation-service.ts), andStoredFlowCanonicalization.todos?'s docblock now says it restates that member. (2) The changeset isminorfor spec, service-automation and metadata-protocol, withClause-②: yes (widening: ...)namingIAutomationService.canonicalizeStoredFlow(...).todos?,AutomationEngine.canonicalizeStoredFlow(...).todosandStoredFlowCanonicalization.todos?; the wiring paragraph keeps theConversionTodoNotice.conversionIddomain widening. The repository's one reader,scripts/pm/clause2-line.mjsreadClause2Line, parses the line as value yes, arm widening. (3) PR body line 4 isClause-②: yes (widening); lines 1-3 are unchanged. A new public-surface section names the three members, and the sentence saying the contract is not edited is gone. I mergedorigin/mainfirst (331fa99, no conflict).","tests": "At 086d6f1. Build: turbo closures of service-automation^... and metadata-protocol^..., plus the client... and client-react... closures, 35/35 tasks, exit 0.
pnpm --filter @objectstack/spec check:generatedexit 0 ('All 14 generated artifacts are up to date', api-surface and export-origins included; nothing regenerated). Reverse verification through scripts/ablation-replace.mjs: the engine'stodoswidened toConversionTodoNotice[] | number, thenpnpm --filter @objectstack/service-automation exec tsc --noEmitexit 1 as predicted, withsrc/engine.ts(4364,5): error TS2416: Property 'canonicalizeStoredFlow' in type 'AutomationEngine' is not assignable to the same property in base type 'IAutomationService'. The rebuilt dist/contracts/index.d.ts:3778 carriestodos?. Restore proven: blob == HEAD, git diff HEAD empty. typecheck spec / service-automation / metadata-protocol: exit 0 each. Pins, exit 0 each: spec refused-shapes + stored + cbp-master-detail-required-defaulted (61 tests); service-automation canonicalize-stored-flow + engine (175); metadata-protocol protocol.stored-migration + protocol.flow-canonicalizer (60).pnpm --filter @objectstack/spec test:repomeasured in full: exit 0, 58 files, 1007 tests.","gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 086d6f1, no paths, no stale-tree warning: 115 commands, each exit code recorded before any pipe. 114 exited 0 on first run. check:dual-build-cjs-loads exited 3 (PREREQUISITE NOT MET: 33 packages with no dist in the recreated worktree); re-run after the battery, whose later gates had built them, it exited 0 ('108 published require entry point(s) across 67 package(s) load'), and that measured exit is the one recorded. The Clause-② gates (node scripts/check-changeset-no-major.mjs --base origin/main, node scripts/check-adr-0087-registration.mjs --base origin/main, both with --self-test) and pnpm --filter @objectstack/spec run check:api-surface: exit 0. --ran: '✓ dispatch-gates --ran: 115 derived famil(ies) accounted for — 115 run, 0 NOT-MEASURED (a DERIVED zero — all 115 recorded an exit code and none of them is 3).'",
"line_budget": "1158 changed lines (+1097 / -61) in 18 files vs the 3000 human-merge threshold: under. This round's own commit: 3 files, +25 / -9. No generated file committed.",
"files_changed": [
".changeset/23269-v18-stored-row-fates.md",
"content/docs/deployment/cli.mdx",
"packages/metadata-protocol/src/protocol.stored-migration.test.ts",
"packages/metadata-protocol/src/protocol.ts",
"packages/metadata-protocol/src/stored-migration.ts",
"packages/services/service-automation/src/canonicalize-stored-flow.test.ts",
"packages/services/service-automation/src/engine.ts",
"packages/spec/src/contracts/automation-service.ts",
"packages/spec/src/conversions/apply.ts",
"packages/spec/src/conversions/refused-shapes.test.ts",
"packages/spec/src/conversions/refused-shapes.ts",
"packages/spec/src/conversions/stored.ts",
"packages/spec/src/conversions/types.ts",
"packages/spec/src/data/controlled-by-parent-master.ts",
"packages/spec/src/data/object.zod.ts",
"packages/spec/src/migrations/entries/semantic/18.cbp-master-relation-tiers-retired.ts",
"packages/spec/src/migrations/entries/semantic/18.flow-value-slot-template-dialect-refused.ts",
"packages/spec/src/migrations/entries/semantic/18.view-data-api-provider-retired.ts"
],
"files_changed_this_round": [
".changeset/23269-v18-stored-row-fates.md",
"packages/metadata-protocol/src/stored-migration.ts",
"packages/spec/src/contracts/automation-service.ts"
],
"deviations": [
"This round's commit subject reads
fix(spec)!:. The!reads as breaking, but the change is a widening. It is pushed and was not rewritten. The changeset (minor, no breaking marker, Clause-② yes (widening)) and the PR title carry the level, and the PR title governs the squash.","The reverse verification has no measured control leg. The claim that a contract without
todosstays green under the same mutation rests on TypeScript's rule that an extra member is assignable; measuring it would take two more spec rebuilds.","origin/main gained 87e902d, 4d4040c and 37510aa after the 331fa99 merge, none under the three changed packages (two changesets plus objectql/core/security). Not merged.",
"The worktree was recreated from origin/claude/issue-23269-v18-stored-row-fates at af10b60 and is removed again after this report."
],
"mcp_calls": "0 — no MCP tool used",
"api_writes": "2 relay strokes — each one POST /repos/objectstack-ai/objectstack/dispatches (repository_dispatch, fleet-write relay), executed as objectstack-fleet[bot]: (1) issue_patch = PATCH /repos//issues/23315 (the PR body), read back identical (17534 bytes), with draft, head 086d6f1, lines 1-4 and one footer confirmed by my own REST read; (2) comment = POST /repos//issues/23269/comments (this os-dev-report). Plus git push of the branch (not a REST write). No label write.",
"open_questions": [],
"out_of_scope_findings": []
}
objectstack-fleet commentedon Oct 13, 2026
✅ Contract review PASS on the patched head; PR #23315 at
086d6f1adis ready with auto-merge armed (Fixes #23269·Fixes #23270·Fixes #23271)domain:specseat 2 (#18549) ·os-justin· sessionsession_01BRQ9Mp1yH7R1az6MaUM18H· 2026-10-13T20:14Z · holder of claims6148030494/6148057324/6148082219. Thread-read:6154171651.6152721854FAILED on the semver andClause-②section only (the publishedIAutomationServicecontract did not declare thetodosthe stored pass reads). The patch round (report6154171651, 3 files, +25 / −9, no door, detector or predicate changed) declaredtodos?on the contract, made the changesetminorfor the three packages and setClause-②: yes (widening), naming the three members. The second record6154280528on086d6f1ad: PASS.086d6f1ad:check-governed-merges --pr 23315on the final file list: not governed, 1158 changed lines.mergeable_statereadunknownon six reads; a localgit merge-treeof the head intoorigin/mainmerges with no conflict.Fixeslines; line 4 isClause-②: yes (widening).pr_ready+automerge_enable.Next: confirm MERGED, the mis-close scan (three cards close; nothing else should), and the landing record.
Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
Landed: PR #23315 →
572ecfb2e(Fixes #23269·Fixes #23270·Fixes #23271). The three cards closedcompletedon the mergedomain:specseat 2 (#18549) ·os-justin· sessionsession_01BRQ9Mp1yH7R1az6MaUM18H· 2026-10-13T20:45Z · holder of claims6148030494/6148057324/6148082219. Thread-read:6154319725.572ecfb2e, an ancestor oforigin/main. 18 files, +1097 / −61, not governed. Contract review: FAIL6152721854(the published contract did not declaretodos; the semver), then the patch round, then PASS6154280528.os migrate meta --storedlists every stored row that protocol 18 refuses and no conversion rewrites, by flow and node (v18 upgrade: stored flows whose get/update/delete_record filter operand is a {…} token are refused at boot with no conversion — every deployment's automations built on the old spelling stop running #23269), by object (v18 upgrade: stored controlled_by_parent objects without exactly one master_detail (two masters, none, a required lookup, required:false) are denied after upgrade with no conversion or fate #23270) and by view (v18 upgrade: stored views with data.provider 'api' are refused at rehydration with no conversion — the upgrade should migrate or report them #23271), each naming its step-18 D3 entry, its site and the owner's choice. Nothing is rewritten.IAutomationService.canonicalizeStoredFlowdeclarestodos?;@objectstack/spec,@objectstack/service-automationand@objectstack/metadata-protocolareminor(Clause-②: yes (widening)).Fixeslines, and the only issues closed in the merge window are these three cards (completed).pm:dispatchedis removed from all three in this act.status: 'active'— after the v18 upgrade, every refused flow reads as running #23316 (a stored flow refused at cold boot is served asactive), v18 stored preview:os migrate meta --storedstill leaves out two families the v18 doors refuse — page components binding the retired API data source, andregisterFlow's other refusals #23317 (the preview's two remaining gaps: page components binding the retired API source, andregisterFlow's other refusals); both named this PR's landing as their order.0f84c9c) are listed the same way.mainpublishes nothing; the release is the Version Packages PR, a human act.Generated by Claude Code