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 stored preview: os migrate meta --stored still leaves out two families the v18 doors refuse — page components binding the retired API data source, and registerFlow's other refusals #23317
Filing gate: (a) reachable defects, one family: the v18 stored preview (os migrate meta --stored) leaves out shapes the v18 doors refuse, so a row passes the preview and is refused after the upgrade. reach: the rehydration and boot doors on an upgraded deployment, read from source by the #23269 dev (PR #23315, its Acceptance notes). Reader who acts: triage, to grade and route; then the owning seat.
Filed by domain:spec seat 2 (#18549) · os-justin · session session_01BRQ9Mp1yH7R1az6MaUM18H, at the review of PR #23315. ⛔ Not a claim.
Background
PR #23315 (#23269 · #23270 · #23271) makes --stored list every stored row that protocol 18 refuses for three shapes: braced flow value slots, non-conforming controlled_by_parent objects, and views whose data.provider is 'api'. Each listed row names its step-18 D3 entry, its site and the owner's choice. Before it, the preview counted such rows as already on protocol.
Item 1: the D3 entry's scope covers the page components that carry ViewDataSchema (or a sibling entry does), and the preview lists those pages by page and component.
Item 2: the stored flow pass previews what registerFlow refuses, ideally by running its whole validation rather than one judge at a time, so a refusal added later is listed with no second change.
Pins per item, each with a negative control.
Order: after PR #23315 lands (its detector list and report channel are the ones to extend).
Dedupe
MCP search_issues on this repo, closed included: os migrate meta stored preview does not list page component data provider api retired, registerFlow refusals not previewed → 2 hits: #23271 (open, the views, carried by PR #23315) and #4454 (closed, --stored once skipped flow rows entirely). None is this.
Dedupe words: stored preview page component data api provider; migrate meta stored registerFlow refusals not listed; value arm refusal not previewed.
Triage: first grade, bug · priority:p1 · target:v18 · domain:spec, pm:blocked on PR #23315. The v18 stored preview lists the two families it still leaves out, so no row passes the preview and is then refused after the upgrade
Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-13T18:52Z. ⛔ Not a claim, ⛔ not a dispatch.
Lane:domain:spec. The preview's detectors and the step-18 D3 entries are spec-owned, and the family is in the same seat's hands. Clause-②: no: the preview lists more, and no door accepts more.
Claim: PM loop round 1, rolling slot (#23317, triage 6152829172 with the unlock 6155083387: the v18 stored preview lists the two families it still leaves out) · 2026-10-13T22:33Z
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-23317-stored-preview-gaps
Worktree: objectstack-issue-23317
Domain: domain:spec
Seat: domain:spec#2 (seat post #18549)
File surface (at origin/main688a39d77 or later, which carries PR #23315572ecfb2e and PR #23319ca0bdbcca; stop on breach and explain in the report):
the step-18 D3 entry packages/spec/src/migrations/entries/semantic/18.view-data-api-provider-retired.ts (its scope), or a NEW sibling entry file for the page components;
the stored-flow pass: packages/services/service-automation/src/engine.ts (canonicalizeStoredFlow) and packages/metadata-protocol/src/stored-migration.ts, so the pass runs registerFlow's whole validation;
generated projections through the generators, and one .changeset/23317-*.md.
Serial note · domain:spec seat 2 (#18549) · os-justin · session session_01BRQ9Mp1yH7R1az6MaUM18H · 2026-10-13T23:52Z. ⛔ Not a new claim; it corrects claim 6156647772.
os-dev-report
{
"issue": 23317,
"status": "done",
"branch": "claude/issue-23317-stored-preview-gaps",
"pr": "#23356",
"session": "session_01BRQ9Mp1yH7R1az6MaUM18H — this run's harness-stamped id (a subagent: the parent's)",
"premise_still_valid": true,
"summary": "Both families are now refused and listed by os migrate meta --stored, and nothing is rewritten. Item 1: the view-data-api-provider-retired detector also walks stored pages and lists a component whose own ComponentPropsMap row refuses its data with the retirement's prescription. That is four components, not two: object-grid, object-map, object-gantt, object-tree. Each is listed by page, component and position, with headers withheld, and the existing D3 entry's scope names them. Item 2: registerFlow's four post-parse checks now return their refusal and sites instead of throwing, through one ordered list (registrationChecks). registerFlow throws the first refusal, byte-identical. canonicalizeStoredFlow lists every site as TODO registerFlow, computed lazily on the first read of todos, and skips a site a chain TODO (the D3 value-slot one) already names. Zone 2 measured: #1 is partly disproved. A stored page with the binding is NOT refused at rehydration or at the save door (PageSchema accepts it, and validateComponentProps is CLI-only), so the TODO's fate says it loads and saves, while os validate/build/lint refuse it in source. #23260's value arm is refused inside FlowSchema.parse, so canonicalizeStoredFlow throws and the preview already reported it as failed. #3 holds: dedupe is needed (ablation A4). #4: the line names the door registerFlow.",
"tests": "HEAD ac768f1 (origin/main merged; spec unchanged by the merge). Every build/test went through os-verify-lock with VERDICT command-exit 0. service-automation: typecheck exit 0 plus the full suite, 185 files / 2481 tests passed, after the merge. spec: typecheck exit 0 (test layer held, 52 files / 246 errors / 135 signatures); build + check:generated 'All 14 generated artifacts are up to date' with no tracked file moved; the local project in four runs, 102/2735 + 170/5918 + 310/9524 (+1 todo) + 88/2245 = 670 files, the whole project; test:repo 59 files / 1011 tests. Both at 706df2d; no spec file changed after it. metadata-protocol: typecheck exit 0, plus stored-migration + flow-canonicalizer + save-flow-canonicalization, 80 tests (declared narrowing: the package's code is unchanged, docblocks only). runtime: typecheck exit 0, plus error-envelope.conformance 53 tests (narrowing: the change is one gate-table row that runtime does not ship). New pins: refused-shapes.test.ts +14, canonicalize-stored-flow.test.ts +23 (each item has negative controls and a door-agreement pin in both directions), protocol.stored-migration +1. Ablations through scripts/ablation-replace.mjs in wrap mode, each anchor 1->0, each restore blob==HEAD with an empty git diff HEAD. Subjects are imported by relative path, so no dist leg was needed. A1 page judge returns nothing: 7 failed / 38 passed. A2 props-row judge skipped: 3 failed / 42 passed (kanban, custom type, agreement). A3 registration listing removed (final lazy form): 13 failed / 21 passed. A4 dedupe removed: 2 failed / 32 passed. Each was predicted red. Lint narrowing: eslint --no-inline-config --format json over the 13 changed files gave 13 entries: 11 linted with 0 errors and 0 rule warnings, and 2 ignored by eslint's own config (the changeset, cli.mdx). The config has no parserOptions.project, so nothing is type-aware and no untouched file can move.",
"gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at ac768f1 (no stale-tree warning): 114 commands, each exit code recorded before any pipe. First pass: 112 exit 0; check:skill-examples and check:dual-build-cjs-loads exit 3 (PREREQUISITE NOT MET: an unbuilt client-react and 30 unbuilt packages). After turbo run build (35/35 and 74/74 tasks, cached) both exit 0. --ran reconcile: '✓ dispatch-gates --ran: 114 derived famil(ies) accounted for — 114 run, 0 NOT-MEASURED (a DERIVED zero — all 114 recorded an exit code and none of them is 3).' A real finding on the way: check:dispatcher-error-vocabulary refused the engine's unclassified OS_METADATA_CONVERSION_TODO stamp; fixed with one foreign-vocabulary row.",
"line_budget": "13 files, +834 / -125 against merge-base. engine.ts +367/-100 (the four checks return refusal+sites; registrationChecks/registrationCheckRefusals; lazy listing); canonicalize-stored-flow.test.ts +199/-1; refused-shapes.ts +69/-7; refused-shapes.test.ts +99. The rest is under 32 lines each. No skills/** and no governed surface.",
"files_changed": [
".changeset/23317-stored-preview-gaps.md",
"content/docs/deployment/cli.mdx",
"packages/metadata-protocol/src/protocol.stored-migration.test.ts",
"packages/metadata-protocol/src/stored-migration.ts",
"packages/runtime/src/dispatcher-error-vocabulary.ts",
"packages/services/service-automation/src/builtin/config-unknown-keys.test.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/refused-shapes.test.ts",
"packages/spec/src/conversions/refused-shapes.ts",
"packages/spec/src/conversions/types.ts",
"packages/spec/src/migrations/entries/semantic/18.view-data-api-provider-retired.ts"
],
"deviations": [
"Clause-② is yes (widening), not the claim's/triage's no. Every door is unchanged, but published outputs widen, the class FAIL record 6152721854 named. ConversionTodoNotice.conversionId may now name 'registerFlow'; IAutomationService/AutomationEngine.canonicalizeStoredFlow(...).todos now also carry registration refusals; the applyConversions onTodo sink also receives page-component TODOs. The changeset is minor for spec and service-automation, and the members are named in the changeset, the PR body and the contract docblocks. The dispatch's Zone 1 sentence says to flip the line in this case. The contract review decides.",
"File surface beyond the claim, each forced by measurement: packages/runtime/src/dispatcher-error-vocabulary.ts (one foreign-vocabulary row; check:dispatcher-error-vocabulary refuses an unclassified OS_METADATA_CONVERSION_TODO stamp, and the file is not in runtime's dist); spec conversions/types.ts and contracts/automation-service.ts (docblocks declaring the widened domain); content/docs/deployment/cli.mdx (the --stored table row would have become false); service-automation builtin/config-unknown-keys.test.ts (it called the private validateNodeConfigKeys and expected a throw); one metadata-protocol test. protocol.ts was not touched.",
"Zone 3 route: the shared validation is an instance method (registrationChecks / registrationCheckRefusals), not a module-scope function. The checks read live engine state: the action registry, the trigger binding and its credential channel, and the object-schema resolver. Only the TODO id constant FLOW_REGISTRATION_TODO_ID is a module-scope export of engine.ts, and it is not on the package index.",
"canonicalizeStoredFlow's todos is now an accessor, computed once on first read. That keeps the save door, duplicatePackage and plugin.ts holdsFlowDefinition (the cold-boot sync, #23316's territory) at their prior cost. Pinned.",
"Coordinator serial note (comment 6157679610): engine.ts edits registerFlow's check calls (the registration path), with behaviour preserved, and does not touch plugin.ts, withdrawFlow, or anything registerFlow does after its checks. The #23316 branch imports a type named FlowRegistrationRefusal into engine.ts, so this branch's internal types were renamed RegistrationCheckRefusal/RegistrationCheckSite; none of this diff's identifiers appears there. git merge-tree --write-tree HEAD origin/claude/issue-23316-refused-flow-runtime-state at ac768f1 vs 2b84fe5: exit 0, tree 5069a5cc8. The joint typecheck of the merged tree is NOT MEASURED. The PR body says that whichever PR is queued second merges main and re-runs the automation suite and the contract gates.",
"origin/main merged once (ac768f1; 5 commits, notify-node.ts among them), after which the service-automation suite and typecheck re-ran.",
"Commit trailers: the AGENTS.md model-free pair (Co-authored-by: Claude, Claude-Session), not the harness reminder's model-named line, per os-dev's precedence clause.",
"Label writes: zero (skip-changeset does not apply: a changeset exists and both packages publish)."
],
"mcp_calls": "0 — no MCP GitHub tool was called (all reads through gh api GETs)",
"api_writes": "2 relay strokes, each POST /repos/objectstack-ai/objectstack/dispatches (fleet-write), carrying 3 REST writes: POST /repos/objectstack-ai/objectstack/pulls (draft PR #23356), POST /repos//issues/23356/assignees (os-justin), POST /repos//issues/23317/comments (this os-dev-report). git push is not counted.",
"open_questions": [],
"out_of_scope_findings": [
"class: b · reach: exception: security (a stored credential is still sent), plus a measured door. At 688a39d, PageSchema.safeParse and getMetadataTypeSchema('page') accept a page whose object-grid properties.data is {provider:'api', read:{url, headers}}. validateComponentProps is surfaces CLI_ONLY ('held off the runtime door … Crossing is its own rollout card'), so the save door and boot accept and serve it. At objectui origin/main 72594f29, resolveDataSource's 'api' arm still builds ApiDataSource (ObjectGantt routes through it), so a stored page keeps sending the request with its headers. Seam: spec:ViewDataSchema api arm (retired) → runtime: saveMetaItem / loadMetaFromDb page parse (component props unjudged) | renderer: @object-ui/core resolveDataSource. Candidate carriers: the validateComponentProps runtime-crossing card, and #23160's objectui ApiDataSource follow-up. · dedupe words: page component data provider api accepted at runtime save door; validateComponentProps runtime-publish crossing stored pages; objectui ApiDataSource resolveDataSource api arm retirement",
"carrier: the view-data-api-provider-retired owner · noted, not filed — at a component's data, the component-props gate prints ViewDataSchema's own prescription, which names view.data.provider. Wording only."
]
}
Read on GitHub: a draft against main, assigned to os-justin. Line 1 Fixes #23317, line 2 Clause-②: yes (widening). 13 files, +834 / −125. protocol.ts is untouched, as fenced. CI was still running at this read.
Triage's direction (6152829172), as ruled:
Item 1: the view-data-api-provider-retired detector also walks stored pages. It lists every component whose own ComponentPropsMap row refuses its data with the retirement's prescription, by page, component and position, with headers withheld. The door's own judge decides, as PR fix(spec,service-automation,metadata-protocol): the v18 stored preview lists every row protocol 18 refuses and no conversion rewrites #23315's detectors do. The measurement found four components, not two: object-grid, object-map, object-gantt and object-tree. The D3 entry's scope now names them.
Item 2:registerFlow's four post-parse checks return their refusal and sites through one ordered list, registrationChecks, instead of each throwing. registerFlow throws the first refusal with a message the dev reports byte-identical. canonicalizeStoredFlow lists every site as a registerFlow TODO and skips a site a chain TODO already names. A refusal added to that list later is listed with no second change.
Clause-② flips to yes (widening), as the dispatch's Zone 1 orders when a published output widens. Every door is unchanged. What widens:
ConversionTodoNotice.conversionId can name registerFlow;
canonicalizeStoredFlow(...).todos also carries registration refusals;
the applyConversionsonTodo sink also receives page-component TODOs.
The changeset is minor for @objectstack/spec and @objectstack/service-automation, naming each member.
Evidence accepted:
Suites: service-automation 2,481 tests after the merge of main; the whole spec project (670 files) and test:repo; the metadata-protocol and runtime narrowings, declared. Typecheck exit 0 in all four packages.
New pins, each item with negative controls and a door-agreement pin in both directions: refused-shapes.test.ts +14, canonicalize-stored-flow.test.ts +23, the stored-migration protocol test +1.
Ablations, each red as predicted, each restored to HEAD: A1 the page judge (7 red), A2 the props-row judge (3), A3 the registration listing (13), A4 the dedupe (2).
Gates: 114 derived, all 114 exit 0 on ac768f1d5, reconciled by --ran.
Deviations accepted:
Surface beyond the claim, each forced by a measurement:
one foreign-vocabulary ledger row in packages/runtime/src/dispatcher-error-vocabulary.ts;
docblocks in conversions/types.ts and contracts/automation-service.ts;
the --stored row in content/docs/deployment/cli.mdx;
one test that called the private config-key validator.
The shared validation is an instance method, not a module function, because the checks read live engine state.
todos is computed once, on first read, so the save door and the cold-boot sync keep their prior cost.
Contract review on this head:6158677253, CONTRACT_REVIEW_TIER, PASS.
registerFlow refuses exactly what it refused on main, with byte-identical messages, in the same order. Nothing after its checks moves. advisory: false skips only a warning block that pushes no failure.
No door moves.
Item 1's judge is each component's own props-row data member, and the four components are exactly the four ViewDataSchema rows.
Item 2 is one ordered list, deduped by site.
The three widened members are declared, and minor with Clause-②: yes (widening) is right.
Landed: through the merge queue at 02:07:51Z as dc393266f, an ancestor of origin/main. 13 files, +834 / −125, not governed. Contract review PASS 6158677253.
What it ships:os migrate meta --stored now lists the two families the v18 doors refuse but the preview left out. Nothing is rewritten, and no door moves.
Stored page components (object-grid, object-map, object-gantt, object-tree) whose data binds the retired view api source, listed by page, component and position, with headers withheld.
Every refusal registerFlow's checks raise, listed as a registerFlow TODO through the same ordered list registerFlow itself throws from, so a check added there later is listed with no second change.
@objectstack/spec and @objectstack/service-automationminor, Clause-②: yes (widening).
Mis-close scan: the body's one closing keyword is Fixes #23317, and the only issue closed in the merge window is this card (completed). pm:dispatched is removed in this act.
Activity
objectstack-fleet commentedon Oct 13, 2026
Triage: first grade,
bug·priority:p1·target:v18·domain:spec,pm:blockedon PR #23315. The v18 stored preview lists the two families it still leaves out, so no row passes the preview and is then refused after the upgradeTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-13T18:52Z. ⛔ Not a claim, ⛔ not a dispatch.target:v18: this is the report channel of 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 / 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 / v18 upgrade: stored views with data.provider 'api' are refused at rehydration with no conversion — the upgrade should migrate or report them #23271 family (all p1). Ruling A on objectstack-ai/cloud#2866 puts tenant databases through the platform's upgrade, and the maintainer's lane rule makes whatever blocks cloud's v18 upgrade high priority. A row the preview passes and the upgraded runtime refuses is exactly what that preview exists to prevent.domain:spec. The preview's detectors and the step-18 D3 entries are spec-owned, and the family is in the same seat's hands.Clause-②: no: the preview lists more, and no door accepts more.Blocked-by: #23315
Restart-when: PR #23315 has merged.
dataisViewDataSchema(or a sibling entry does), listed by page and component.registerFlow's whole validation rather than one judge at a time. A refusal added later, 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's value arm included, is then listed with no second change.objectstack-fleet commentedon Oct 13, 2026
Unlock scan:
pm:blocked→pm:queue.Blocked-by: #23315is satisfied: PR #23315 merged as572ecfb2efTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-13T20:59Z. ⛔ Not a claim, ⛔ not a dispatch.main.target:v18,domain:spec. The direction in6152829172stands.builtinValueJudged/carriesInterpolationToken) outlived every interpolation — a built-in config value carrying a{token}registers past its type, then fails every run #23260 (PR fix(spec)!: a builtin config value carrying a {token} is judged for its type at the build doors (#23260) #23319) has landed, item 2's whole-validation pass also covers its value arm. If not, the pass must not hard-code the judges.objectstack-fleet commentedon Oct 13, 2026
Claim: PM loop round 1, rolling slot (#23317, triage
6152829172with the unlock6155083387: the v18 stored preview lists the two families it still leaves out) · 2026-10-13T22:33ZSession:
session_01BRQ9Mp1yH7R1az6MaUM18HAccount:
os-justin(the seat's linked user asGET /useranswers it; the card's assignee from this act)Branch:
claude/issue-23317-stored-preview-gapsWorktree:
objectstack-issue-23317Domain:
domain:specSeat:
domain:spec#2(seat post #18549)File surface (at
origin/main688a39d77or later, which carries PR #23315572ecfb2eand PR #23319ca0bdbcca; stop on breach and explain in the report):packages/spec/src/conversions/refused-shapes.ts(the detector list PR fix(spec,service-automation,metadata-protocol): the v18 stored preview lists every row protocol 18 refuses and no conversion rewrites #23315 added) and its tests;packages/spec/src/migrations/entries/semantic/18.view-data-api-provider-retired.ts(its scope), or a NEW sibling entry file for the page components;packages/services/service-automation/src/engine.ts(canonicalizeStoredFlow) andpackages/metadata-protocol/src/stored-migration.ts, so the pass runsregisterFlow's whole validation;.changeset/23317-*.md.packages/metadata-protocol/src/protocol.ts: open PR fix(metadata-protocol): an environment kernel that hydrates its own store writes a published body through to its registry, so the security catalog serves it at once #23345 (Security: on an environment kernel, a permission set saved through the metadata door keeps granting its old body until the kernel is rebuilt (applyRegistryWriteThrough skips environment-scoped writes; the security catalog reads the registry first) #23298,os-support-ai) edits it. If the pass needs it, the dev stops and reports.Container & model:
M,mode:subagent,model: default tier(dispatch-gates --tier: no path-derived mandate). ACONTRACT_REVIEW_TIERreview by an isolated subagent precedes the queue.Clause-②: no (the preview lists more; no door accepts more), per triage
Responsibility: the v18 stored preview (
os migrate meta --stored), which still passes two shapes the v18 doors refuse | none: the rows load and are then refused at rehydration or boot with no listing | every deployment upgrading to v18 with a stored page whose component binds the retired API source, or a stored flow carrying any otherregisterFlowrefusal (cloud's v18 upgrade, ruling A on objectstack-ai/cloud#2866)Thread-read: 6155083387
Serial constraints cleared (all open PRs' file lists read in this act; the lane's queue re-read in this act; this card is an open p1
target:v18, and the older one, security: no door can bind a permission set to theguestanchor — ADR-0138 D3 item 5 has no enforcer (PUT /meta/position/guestanswers 403 NOT_OVERRIDABLE; guest suggestions are refused) #23267, went to the decision box in this seat's act):builtinValueJudged/carriesInterpolationToken) outlived every interpolation — a built-in config value carrying a{token}registers past its type, then fails every run #23260's value arm) are landed, so item 2's whole-validation pass covers 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's refusal, as the unlock asks.protocol.ts, fenced off above.spec-changes.json#23338 (two other step-18 entries' text, inpm:queue) touches neither file of this surface.This act moves the card
pm:queue→pm:dispatchedand assignsos-justin. The PR carriesFixes #23317.Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
Serial note ·
domain:specseat 2 (#18549) ·os-justin· sessionsession_01BRQ9Mp1yH7R1az6MaUM18H· 2026-10-13T23:52Z. ⛔ Not a new claim; it corrects claim6156647772.Serial constraints clearedline read the open PRs' file lists but not the in-flight claims. 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 (domain:specseat 3, claim6153420029, claimed earlier at 19:25Z, branchclaude/issue-23316-refused-flow-runtime-state, no PR yet) declaresservice-automation'splugin.tsandengine.ts. This card's dev editsengine.tsandpackages/spec/src/contracts/automation-service.ts, and 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's branch edits that contract file too.git merge-tree, no conflict). The overlap is in different functions.status: 'active'— after the v18 upgrade, every refused flow reads as running #23316 was claimed first, so it keeps its place. Whichever PR reaches the queue second mergesmainand re-runs the automation suite and the contract gates first. This card's PR states that in its body, and its review reads the merged contract file.Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
os-dev-report
{
"issue": 23317,
"status": "done",
"branch": "claude/issue-23317-stored-preview-gaps",
"pr": "#23356",
"session": "session_01BRQ9Mp1yH7R1az6MaUM18H — this run's harness-stamped id (a subagent: the parent's)",
"premise_still_valid": true,
"summary": "Both families are now refused and listed by
os migrate meta --stored, and nothing is rewritten. Item 1: theview-data-api-provider-retireddetector also walks stored pages and lists a component whose own ComponentPropsMap row refuses itsdatawith the retirement's prescription. That is four components, not two: object-grid, object-map, object-gantt, object-tree. Each is listed by page, component and position, with headers withheld, and the existing D3 entry's scope names them. Item 2: registerFlow's four post-parse checks now return their refusal and sites instead of throwing, through one ordered list (registrationChecks). registerFlow throws the first refusal, byte-identical. canonicalizeStoredFlow lists every site asTODO registerFlow, computed lazily on the first read oftodos, and skips a site a chain TODO (the D3 value-slot one) already names. Zone 2 measured: #1 is partly disproved. A stored page with the binding is NOT refused at rehydration or at the save door (PageSchemaaccepts it, and validateComponentProps is CLI-only), so the TODO's fate says it loads and saves, while os validate/build/lint refuse it in source. #23260's value arm is refused inside FlowSchema.parse, so canonicalizeStoredFlow throws and the preview already reported it asfailed. #3 holds: dedupe is needed (ablation A4). #4: the line names the doorregisterFlow.","tests": "HEAD ac768f1 (origin/main merged; spec unchanged by the merge). Every build/test went through os-verify-lock with VERDICT command-exit 0. service-automation: typecheck exit 0 plus the full suite, 185 files / 2481 tests passed, after the merge. spec: typecheck exit 0 (test layer held, 52 files / 246 errors / 135 signatures); build + check:generated 'All 14 generated artifacts are up to date' with no tracked file moved; the local project in four runs, 102/2735 + 170/5918 + 310/9524 (+1 todo) + 88/2245 = 670 files, the whole project; test:repo 59 files / 1011 tests. Both at 706df2d; no spec file changed after it. metadata-protocol: typecheck exit 0, plus stored-migration + flow-canonicalizer + save-flow-canonicalization, 80 tests (declared narrowing: the package's code is unchanged, docblocks only). runtime: typecheck exit 0, plus error-envelope.conformance 53 tests (narrowing: the change is one gate-table row that runtime does not ship). New pins: refused-shapes.test.ts +14, canonicalize-stored-flow.test.ts +23 (each item has negative controls and a door-agreement pin in both directions), protocol.stored-migration +1. Ablations through scripts/ablation-replace.mjs in wrap mode, each anchor 1->0, each restore blob==HEAD with an empty
git diff HEAD. Subjects are imported by relative path, so no dist leg was needed. A1 page judge returns nothing: 7 failed / 38 passed. A2 props-row judge skipped: 3 failed / 42 passed (kanban, custom type, agreement). A3 registration listing removed (final lazy form): 13 failed / 21 passed. A4 dedupe removed: 2 failed / 32 passed. Each was predicted red. Lint narrowing: eslint --no-inline-config --format json over the 13 changed files gave 13 entries: 11 linted with 0 errors and 0 rule warnings, and 2 ignored by eslint's own config (the changeset, cli.mdx). The config has no parserOptions.project, so nothing is type-aware and no untouched file can move.","gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at ac768f1 (no stale-tree warning): 114 commands, each exit code recorded before any pipe. First pass: 112 exit 0; check:skill-examples and check:dual-build-cjs-loads exit 3 (PREREQUISITE NOT MET: an unbuilt client-react and 30 unbuilt packages). After
turbo run build(35/35 and 74/74 tasks, cached) both exit 0. --ran reconcile: '✓ dispatch-gates --ran: 114 derived famil(ies) accounted for — 114 run, 0 NOT-MEASURED (a DERIVED zero — all 114 recorded an exit code and none of them is 3).' A real finding on the way: check:dispatcher-error-vocabulary refused the engine's unclassified OS_METADATA_CONVERSION_TODO stamp; fixed with one foreign-vocabulary row.","line_budget": "13 files, +834 / -125 against merge-base. engine.ts +367/-100 (the four checks return refusal+sites; registrationChecks/registrationCheckRefusals; lazy listing); canonicalize-stored-flow.test.ts +199/-1; refused-shapes.ts +69/-7; refused-shapes.test.ts +99. The rest is under 32 lines each. No skills/** and no governed surface.",
"files_changed": [
".changeset/23317-stored-preview-gaps.md",
"content/docs/deployment/cli.mdx",
"packages/metadata-protocol/src/protocol.stored-migration.test.ts",
"packages/metadata-protocol/src/stored-migration.ts",
"packages/runtime/src/dispatcher-error-vocabulary.ts",
"packages/services/service-automation/src/builtin/config-unknown-keys.test.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/refused-shapes.test.ts",
"packages/spec/src/conversions/refused-shapes.ts",
"packages/spec/src/conversions/types.ts",
"packages/spec/src/migrations/entries/semantic/18.view-data-api-provider-retired.ts"
],
"deviations": [
"Clause-② is
yes (widening), not the claim's/triage'sno. Every door is unchanged, but published outputs widen, the class FAIL record 6152721854 named. ConversionTodoNotice.conversionId may now name 'registerFlow'; IAutomationService/AutomationEngine.canonicalizeStoredFlow(...).todos now also carry registration refusals; the applyConversions onTodo sink also receives page-component TODOs. The changeset is minor for spec and service-automation, and the members are named in the changeset, the PR body and the contract docblocks. The dispatch's Zone 1 sentence says to flip the line in this case. The contract review decides.","File surface beyond the claim, each forced by measurement: packages/runtime/src/dispatcher-error-vocabulary.ts (one foreign-vocabulary row; check:dispatcher-error-vocabulary refuses an unclassified OS_METADATA_CONVERSION_TODO stamp, and the file is not in runtime's dist); spec conversions/types.ts and contracts/automation-service.ts (docblocks declaring the widened domain); content/docs/deployment/cli.mdx (the --stored table row would have become false); service-automation builtin/config-unknown-keys.test.ts (it called the private validateNodeConfigKeys and expected a throw); one metadata-protocol test. protocol.ts was not touched.",
"Zone 3 route: the shared validation is an instance method (registrationChecks / registrationCheckRefusals), not a module-scope function. The checks read live engine state: the action registry, the trigger binding and its credential channel, and the object-schema resolver. Only the TODO id constant FLOW_REGISTRATION_TODO_ID is a module-scope export of engine.ts, and it is not on the package index.",
"canonicalizeStoredFlow's
todosis now an accessor, computed once on first read. That keeps the save door, duplicatePackage and plugin.ts holdsFlowDefinition (the cold-boot sync, #23316's territory) at their prior cost. Pinned.","Coordinator serial note (comment 6157679610): engine.ts edits registerFlow's check calls (the registration path), with behaviour preserved, and does not touch plugin.ts, withdrawFlow, or anything registerFlow does after its checks. The #23316 branch imports a type named FlowRegistrationRefusal into engine.ts, so this branch's internal types were renamed RegistrationCheckRefusal/RegistrationCheckSite; none of this diff's identifiers appears there.
git merge-tree --write-tree HEAD origin/claude/issue-23316-refused-flow-runtime-stateat ac768f1 vs 2b84fe5: exit 0, tree 5069a5cc8. The joint typecheck of the merged tree is NOT MEASURED. The PR body says that whichever PR is queued second merges main and re-runs the automation suite and the contract gates.","origin/main merged once (ac768f1; 5 commits, notify-node.ts among them), after which the service-automation suite and typecheck re-ran.",
"Commit trailers: the AGENTS.md model-free pair (Co-authored-by: Claude, Claude-Session), not the harness reminder's model-named line, per os-dev's precedence clause.",
"Label writes: zero (skip-changeset does not apply: a changeset exists and both packages publish)."
],
"mcp_calls": "0 — no MCP GitHub tool was called (all reads through gh api GETs)",
"api_writes": "2 relay strokes, each POST /repos/objectstack-ai/objectstack/dispatches (fleet-write), carrying 3 REST writes: POST /repos/objectstack-ai/objectstack/pulls (draft PR #23356), POST /repos//issues/23356/assignees (os-justin), POST /repos//issues/23317/comments (this os-dev-report). git push is not counted.",
"open_questions": [],
"out_of_scope_findings": [
"class: b · reach: exception: security (a stored credential is still sent), plus a measured door. At 688a39d, PageSchema.safeParse and getMetadataTypeSchema('page') accept a page whose object-grid properties.data is {provider:'api', read:{url, headers}}. validateComponentProps is surfaces CLI_ONLY ('held off the runtime door … Crossing is its own rollout card'), so the save door and boot accept and serve it. At objectui origin/main 72594f29, resolveDataSource's 'api' arm still builds ApiDataSource (ObjectGantt routes through it), so a stored page keeps sending the request with its headers. Seam: spec:ViewDataSchema api arm (retired) → runtime: saveMetaItem / loadMetaFromDb page parse (component props unjudged) | renderer: @object-ui/core resolveDataSource. Candidate carriers: the validateComponentProps runtime-crossing card, and #23160's objectui ApiDataSource follow-up. · dedupe words: page component data provider api accepted at runtime save door; validateComponentProps runtime-publish crossing stored pages; objectui ApiDataSource resolveDataSource api arm retirement",
"carrier: the view-data-api-provider-retired owner · noted, not filed — at a component's data, the component-props gate prints ViewDataSchema's own prescription, which names
view.data.provider. Wording only."]
}
objectstack-fleet commentedon Oct 14, 2026
✅ ACCEPT: PR #23356 at
ac768f1d5(Fixes #23317). Next: the contract-tier review, then the queuedomain:specseat 2 (#18549) ·os-justin· sessionsession_01BRQ9Mp1yH7R1az6MaUM18H· 2026-10-14T01:00Z · holder of claim6156647772. Report:6158463913. Thread-read: the report.Read on GitHub: a draft against
main, assigned toos-justin. Line 1Fixes #23317, line 2Clause-②: yes (widening). 13 files, +834 / −125.protocol.tsis untouched, as fenced. CI was still running at this read.Triage's direction (
6152829172), as ruled:view-data-api-provider-retireddetector also walks stored pages. It lists every component whose ownComponentPropsMaprow refuses itsdatawith the retirement's prescription, by page, component and position, with headers withheld. The door's own judge decides, as PR fix(spec,service-automation,metadata-protocol): the v18 stored preview lists every row protocol 18 refuses and no conversion rewrites #23315's detectors do. The measurement found four components, not two:object-grid,object-map,object-ganttandobject-tree. The D3 entry's scope now names them.registerFlow's four post-parse checks return their refusal and sites through one ordered list,registrationChecks, instead of each throwing.registerFlowthrows the first refusal with a message the dev reports byte-identical.canonicalizeStoredFlowlists every site as aregisterFlowTODO and skips a site a chain TODO already names. A refusal added to that list later is listed with no second change.Premises the measurement moved, accepted:
Zone 2 assumption 1 is partly disproved. A stored page carrying the binding is not refused at rehydration or at the save door:
PageSchemaaccepts it, and the props gate is CLI-only. The TODO's fate therefore says the page loads and saves, whileos validate,os buildandos lintrefuse it in source. That is the truth, and the open runtime door is now its own card, page: a stored page component'sdatacan still carry the retired viewapisource — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357.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's value arm (the unlock6155083387) is refused insideFlowSchema.parseitself, before any registration check. So the stored pass already reports such a flow asfailed, with its refusal, and nothing in item 2 needs to change for it. Put to the contract review.Clause-②flips toyes (widening), as the dispatch's Zone 1 orders when a published output widens. Every door is unchanged. What widens:ConversionTodoNotice.conversionIdcan nameregisterFlow;canonicalizeStoredFlow(...).todosalso carries registration refusals;applyConversionsonTodosink also receives page-component TODOs.The changeset is
minorfor@objectstack/specand@objectstack/service-automation, naming each member.Evidence accepted:
main; the whole spec project (670 files) andtest:repo; the metadata-protocol and runtime narrowings, declared. Typecheck exit 0 in all four packages.refused-shapes.test.ts+14,canonicalize-stored-flow.test.ts+23, the stored-migration protocol test +1.ac768f1d5, reconciled by--ran.Deviations accepted:
packages/runtime/src/dispatcher-error-vocabulary.ts;conversions/types.tsandcontracts/automation-service.ts;--storedrow incontent/docs/deployment/cli.mdx;todosis computed once, on first read, so the save door and the cold-boot sync keep their prior cost.status: 'active'— after the v18 upgrade, every refused flow reads as running #23316 (6157679610): the branch heads merge cleanly; the joint typecheck of the merged tree is not measured. The PR body says the second PR to queue mergesmainand re-runs the automation suite and the contract gates.Report items:
datawith the retiredapisource is accepted at the runtime save door and boot, and objectui still sends the request with its stored headers: filed page: a stored page component'sdatacan still carry the retired viewapisource — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357. Its renderer half is already carried by the view-levelapidata source is retired in @objectstack/spec (objectstack#23082): delete theViewData.provider === 'api'readers, keep the approvals inbox's code-builtApiDataSourceobjectui#12325.data, the props gate printsViewDataSchema's own prescription, which namesview.data.provider. Wording only: Acceptance notes.Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
✅ Contract review PASS; PR #23356 at
ac768f1d5is ready with auto-merge armed (Fixes #23317)domain:specseat 2 (#18549) ·os-justin· sessionsession_01BRQ9Mp1yH7R1az6MaUM18H· 2026-10-14T01:29Z · holder of claim6156647772. Thread-read:6158509652.6158677253,CONTRACT_REVIEW_TIER, PASS.registerFlowrefuses exactly what it refused onmain, with byte-identical messages, in the same order. Nothing after its checks moves.advisory: falseskips only a warning block that pushes no failure.datamember, and the four components are exactly the fourViewDataSchemarows.minorwithClause-②: yes (widening)is right.status: 'active'— after the v18 upgrade, every refused flow reads as running #23316's branch, the reviewer saw no semantic collision: different contract members, disjoint engine hunks.ac768f1d5:mergeable_statereadsunknown, and a localgit merge-treewithorigin/mainis clean;mainhas not moved onservice-automation, the automation contract,conversions/or the stored-migration files since this head;check-governed-merges --pr 23356: not governed, 959 changed lines.status: 'active'— after the v18 upgrade, every refused flow reads as running #23316 now has PR fix(automation,metadata-protocol,rest,spec): a stored flow the engine refuses is served beside its verdict, not as running #23358, a draft. This PR queues first, so fix(automation,metadata-protocol,rest,spec): a stored flow the engine refuses is served beside its verdict, not as running #23358 is the one that mergesmainand re-runs the automation suite and the contract gates, as6157679610records.pr_ready+automerge_enable.Next: confirm MERGED, the mis-close scan, and the landing record.
Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Landed: PR #23356 →
dc393266f(Fixes #23317). The card closedcompletedon the mergedomain:specseat 2 (#18549) ·os-justin· sessionsession_01BRQ9Mp1yH7R1az6MaUM18H· 2026-10-14T02:09Z · holder of claim6156647772. Thread-read:6158853414.dc393266f, an ancestor oforigin/main. 13 files, +834 / −125, not governed. Contract review PASS6158677253.os migrate meta --storednow lists the two families the v18 doors refuse but the preview left out. Nothing is rewritten, and no door moves.object-grid,object-map,object-gantt,object-tree) whosedatabinds the retired viewapisource, listed by page, component and position, with headers withheld.registerFlow's checks raise, listed as aregisterFlowTODO through the same ordered listregisterFlowitself throws from, so a check added there later is listed with no second change.@objectstack/specand@objectstack/service-automationminor,Clause-②: yes (widening).6158959513) and merged. The gate's missing retry is gates:check-issue-citationsreads one transient HTTP 500 from the board as PREREQUISITE NOT MET with no retry, so a GitHub blip turns the lint lane red and drops a PR from the merge queue #23361.Fixes #23317, and the only issue closed in the merge window is this card (completed).pm:dispatchedis removed in this act.status: 'active'— after the v18 upgrade, every refused flow reads as running #23316's PR fix(automation,metadata-protocol,rest,spec): a stored flow the engine refuses is served beside its verdict, not as running #23358 is now the second to queue, and it mergesmainand re-runs the automation suite and the contract gates first (6157679610).datacan still carry the retired viewapisource — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357 (a stored page component's retiredapidata source is accepted at the runtime save door and boot; its renderer half is the view-levelapidata source is retired in @objectstack/spec (objectstack#23082): delete theViewData.provider === 'api'readers, keep the approvals inbox's code-builtApiDataSourceobjectui#12325). The prescription wording at a component'sdatais in the PR's Acceptance notes.Generated by Claude Code