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
spec(conversions): 15 ADR-0087 D2 conversions are still on the load path at 18 though their window closed (10 toMajor: 17, 5 older): retire each or record why it stays, one verdict per entry (family closing card after #23421) #23487
Filing gate ①, class (b): a declared rule the registry does not keep. ADR-0087 D2's window says a conversion "is applied by the loader for one major (N accepts N-1 shapes) and then retired from the load path in N+1", and retiredFromLoadPath is that retirement. This is the closing card for the family; its first two members, page-header-subtitle-alias and inline-action-api-params-to-body-extra, are retired by PR #23485 (#23421).
reach: every authoring door that runs the load-path conversions (defineStack, os validate, os build, os lint): a spelling whose window closed at 17 is still silently rewritten on the 18.0.0-next line instead of being refused with its rename. Measured by PR #23485's dev on origin/main61b12584f (report 6168805850 on #23421, out_of_scope_findings entry 1). Not re-measured by the seat.
Filed by domain:spec seat 3 (#18883) · baozhoutao · session session_01D6F9axfoPzR7t8G1J7Ytzz, at PR #23485's review. ⛔ Not a claim; grading and routing are triage's.
The members, with the dev's verdict on each
toMajor: 17 (window closed at 18):
entry
verdict
what retiring it needs first
flow-node-crud-object-alias
overdue ("Live window; retires at 18")
check the node config schema refuses the old key with the rename rather than stripping it
flow-node-notify-config-aliases
overdue
same
flow-node-wait-event-config-lift
overdue
same
flow-node-connector-config-lift
overdue
same
flow-node-map-flow-alias
overdue
same
flow-node-subflow-flow-alias
overdue
same
flow-node-script-config-aliases
overdue
same
sharing-rule-access-level-full-to-edit
overdue ("retires at 18")
an enumWithRetiredValues prescription for 'full', or retiring leaves a generic zod enum message
datasource-driver-mongo-to-mongodb
deliberately kept: 'mongo' is still a legal alias with no authoring refusal
a decision: retire the alias value, or record the entry as permanent
retry-policy-converged
deliberately kept: FlowNodeSchema.config is an unconstrained record, so no refusal reaches config.retry.retryDelayMs
retiring it would make the old spelling silent; it stays until the config is typed, or is recorded as permanent
Older windows, still live at 18:flow-node-http-callout-rename, page-kind-jsx-to-html, flow-node-crud-filter-alias (toMajor: 11); view-visibleOn-to-visibleWhen, page-component-visibility-to-visibleWhen (toMajor: 15). Note: three record/strict-exit suites (packages/spec/src/stack-conversions-record.test.ts, packages/cli/test/stack-conversion-record-door.test.ts, packages/cli/src/commands/validate-json-strict-exit.e2e.test.ts) ride page-component-visibility-to-visibleWhen after PR #23485; whoever retires it re-points them (their docblocks say so).
Enumeration nail: the claimant re-derives the list from packages/spec/src/conversions/registry.ts (every entry whose toMajor is below 18 and which carries no retiredFromLoadPath) and gives each one a verdict: retired in this card, or kept with the reason recorded in its docblock. No member is left for a point card.
Reader who acts: the domain:spec seat that claims it (packages/spec/src/conversions/**, the node-config and sharing-rule schemas the refusals live in). Stored rows are safe either way: conversions/stored.ts replays the full chain, retired entries included.
Re-check
In packages/spec/src/conversions/registry.ts on origin/main: count entries with toMajor: 17 (or lower) that lack retiredFromLoadPath. Control: the same file carries retiredFromLoadPath on 127 entries at 73c7dc4a3 (the seat's parse).
Dedupe
MCP search_issues on this repo, closed included: live-window conversion overdue at 18 retiredFromLoadPath toMajor 17 flow-node aliases retire load path → 2 hits: #23421 (open, the first two members, closing with PR #23485) and #3796 (closed: six flow-node aliases bypassing readAliasedConfig, an older shape). None is this.
Carried item from PR #23485's contract review (③-c), domain:spec seat 3 · session_01D6F9axfoPzR7t8G1J7Ytzz · 2026-10-14T13:02Z. ⛔ Not a claim.
packages/cli/src/commands/validate-json-strict-exit.e2e.test.ts:63-65 (after PR #23485) says "that conversion, page-component-visibility-to-visibleWhen, retired from the load path at protocol 18 and the fixture was re-pointed here". The conversion that retired is page-header-subtitle-alias; page-component-visibility-to-visibleWhen is the live vehicle the fixture now rides. Comment only. This card's claimant fixes the sentence when it re-points that suite (it rides a member of this family).
Lane domain:spec (packages/spec/src/conversions). Clause ②: narrowing, no; it owes the contract-tier review.
Scope as filed: one verdict per entry (retire, or a written reason it stays), with the per-entry precondition the table names: the node config schema refuses the old key with the rename, rather than stripping it.
Claim: PM loop round 1
Session: session_01D6F9axfoPzR7t8G1J7Ytzz
Account: baozhoutao (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-23487-retire-overdue-conversions
Worktree: objectstack-issue-23487
Domain: domain:spec
Seat: domain:spec#3 (seat post #18883)
File surface (at origin/mainf9b421716 or later; stop on a breach and explain it in the report): packages/spec/src/conversions/registry.ts (the family's entries) and their fixtures/tests under packages/spec/src/conversions/; the schemas whose refusal names each retired spelling (flow-node config schemas under packages/spec/src/automation/**, the sharing-rule access-level enum), only where a refusal must name the rename; the step-18 bookkeeping the gates name (packages/spec/src/migrations/**, generated references); packages/spec/src/shared/retired-key-removal-version.pin.test.ts; the three record/strict-exit suites that ride page-component-visibility-to-visibleWhen (packages/spec/src/stack-conversions-record.test.ts, packages/cli/test/stack-conversion-record-door.test.ts, packages/cli/src/commands/validate-json-strict-exit.e2e.test.ts, including the wrong sentence at :63-65); .changeset/23487-*.md.
Container & model: M, mode:subagent, model: default (per dispatch-gates --tier: no path-derived mandate; floor · default · ceiling); a narrowing ⇒ CONTRACT_REVIEW_TIER review before the queue
Clause-②: no
Responsibility: packages/spec's conversion registry keeps entries on the load path past their declared window (ADR-0087 D2) | retiredFromLoadPath + retiredAfter, with the full-chain stored-row replay, is the path PR #23485 took for the family's first two members | authors who still write a retired spelling (silently rewritten today instead of refused with the rename)
Thread-read: 6170038014
Serial constraints cleared: no open PR touches packages/spec/src/conversions/registry.ts or the flow-node/sharing-rule schemas (read at this act). PR #23485 (the family's first two members) has merged as 8693110c4.
Read on origin/mainf9b421716 at 2026-10-14T15:29Z.
{
"issue": 23487,
"status": "done",
"branch": "claude/issue-23487-retire-overdue-conversions",
"pr": "https://github.com/objectstack-ai/objectstack/pull/23523",
"session": "session_01D6F9axfoPzR7t8G1J7Ytzz — this run (subagent; parent PM session id per dispatch)",
"premise_still_valid": true,
"summary": "Enumeration re-derived from ALL_CONVERSIONS on origin/main 1418de4c7 (toMajor below 18, no retiredFromLoadPath): 15 entries, identical to the card's table (10 at 17, 2 at 15, 3 at 11). Verdicts: RETIRED 7 (retiredFromLoadPath true, retiredAfter 17.7.0): flow-node-crud-object-alias, flow-node-crud-filter-alias (toMajor 11), flow-node-notify-config-aliases, flow-node-map-flow-alias, flow-node-subflow-flow-alias, flow-node-script-config-aliases, sharing-rule-access-level-full-to-edit. Each one's precondition was measured by running fixture.before through defineStack live vs. retired in memory: the six flow-node aliases were already refused by their strict node-config contracts, naming the canonical key. Their guidance strings (builtin/io/schemaless-node-config.zod.ts) are rewritten into the upgrade prescription, naming @objectstack/spec 18, plus os migrate meta --from 16 for step 17 (none for the toMajor-11 filters, which is below MIGRATION_SUPPORT_FLOOR). SharingLevel becomes an enumWithRetiredValues with a 'full' prescription naming edit; before, it got zod's bare 'Expected one of'. All 7 have rows in the removal-version pin's JOIN. KEPT 8, with the reason and the retire condition in each docblock: http-callout-rename and page-kind-jsx (the old value is still accepted silently), view-visibleOn and page-component-visibility (the schema still declares and folds the deprecated key, so retiring the entry alone is unobservable), wait-event-config-lift and connector-config-lift (no config contract: a loose key beside a declared block passes silently, and refusing loose keys would also refuse stored flows whose loose key the block shadows), mongo and retry (the two the card marked). A test in conversions.test.ts nails the verdict list. Mechanism assumption measured: the engine's flow rehydration replays with includeRetired true, and its only exclusion (flow-decision-mode-inclusive-explicit) is not in the family, so stored flows heal (pinned in service-automation crud-config-aliases.test.ts). SECOND PRODUCER, outside the claimed file surface: the metadata-protocol saveMetaItem flow fallback verdict used authoring-posture applyConversionsToFlow, so retiring went 9 tests red (the #21982 host-independent verdict). It now replays includeRetired true minus the same default flip (FLOW_FALLBACK_VERDICT_NOT_REPLAYED), which keeps the save door's behaviour byte-for-byte as before this PR. The record/strict-exit suites stay on page-component-visibility (still live). The wrong sentence at validate-json-strict-exit.e2e.test.ts:63-65 is fixed. Changeset .changeset/23487-retire-overdue-conversions.md: spec minor + metadata-protocol patch, BREAKING banner, Clause-② no (narrowing), adr-0087 not-required (already-registered), gate-accepted.",
"tests": "All runs at HEAD 9dd24f8378 (after merging origin/main) unless noted. Each heavy run went through os-verify-lock, and the readings are its VERDICT lines. @objectstack/spec test: 678 files / 20728 passed, 1 todo (VERDICT command-exit 0). @objectstack/spec test:repo + typecheck: 60 files / 1029 passed, typecheck exit 0, check:test-typecheck OK (VERDICT command-exit 0, at pre-merge commit 94020603e + the two test fixes; not rerun after the merge, which touched no file this suite reads that this diff changed). check:generated: all 14 artifacts up to date, against a dist built at HEAD; api-surface, authorable-surface and manifest show zero change, as expected for value and key narrowing on existing defs. Consumers, all after the merge with their dependency closures rebuilt: service-automation 186 files / 2486 passed; lint 139 / 6726 passed; metadata-protocol 236 passed + 3 skipped files / 28397 passed (MP=0); plugin-sharing 46 / 1061 passed (SHARING=0); runtime --project local 380 / 5573 passed, 19 skipped; cli --project unit 288 / 4314 passed (the cli integration tier is declared to CI). Reverse reading of the metadata-protocol fix: before it, protocol.save-flow-canonicalization.test.ts had 9 failed (e.g. 'expected [nodes.0.config.filters, nodes.0.config.bogusKey] to deeply equal [nodes.0.config.bogusKey]'); after it, 80/80 across that file and the canonicalizer and stored-migration files. Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 9dd24f8378 derived 118 commands; all 118 ran with exit 0 (codes written to a file before any pipe); --ran reconciliation: '118 derived famil(ies) accounted for — 118 run, 0 NOT-MEASURED (a DERIVED zero)'. A first run at dc277a2f5f had 3 exit-3 PREREQUISITE NOT MET (check:skill-examples, check:dual-build-cjs-loads, check:i18n-coverage). The missing packages were built, and the HEAD run measured all three green. Ablation and reverse verification: no standalone ablation was run. The flipped pins are load-bearing by construction, because each now asserts the new prescription text: removed in @objectstack/spec 18 plus Use CANONICAL, the full pin's invalid_value code and single issue, and the defineStack STACK_SCHEMA_INVALID/422 refusal at flows.0.nodes.1.config.object and at .filters.",
"mcp_calls": "0",
"api_writes": "3 so far — git push (branch, not REST); relay pr_create (POST /repos/objectstack-ai/objectstack/pulls + POST /issues/23523/assignees, one action, read-back byte-identical 9932/9932, assignee baozhoutao); relay comment (POST /repos/objectstack-ai/objectstack/issues/23487/comments, this report). Labels: zero writes — the dispatch named none, and skip-changeset does not apply (a changeset exists).",
"open_questions": [
{
"question": "datasource-driver-mongo-to-mongodb stays on the load path (card: deliberately kept). Docblock reason: mongo is still a legal value, so there is no refusal for a retirement to hand the author to.",
"options": [
"A: retire 'mongo' as a value (refused with a prescription naming mongodb), then retire the entry",
"B: record the entry as permanent"
],
"recommendation": "Seat rules it separately, per dispatch. Lean A on the axes: authoring has one spelling, and an AI cannot keep writing mongo."
},
{
"question": "retry-policy-converged stays (card: deliberately kept). Re-measured: a try_catch config.retry with retryDelayMs passes the flow parse unrefused, and the job maxRetries default flip would silently drop a source job from 3 retries to 0.",
"options": [
"A: make the try_catch retry block refuse retryDelayMs naming backoffMs, settle the job default flip for sources, then retire",
"B: record as permanent"
],
"recommendation": "Seat rules it separately. A for the spelling half; the default-flip half needs its own judgment."
},
{
"question": "flow-node-wait-event-config-lift and flow-node-connector-config-lift stay: wait/connector_action have no config contract, so retired, a loose key beside a declared block passes silently. Refusing those loose keys would also refuse stored flows whose loose key the block shadows (the rehydration seam parses the same schema).",
"options": [
"A: refuse the named loose keys under config, and also have the lift delete a shadowed loose key at rehydration (so stored flows still load), then retire",
"B: keep both as live entries"
],
"recommendation": "A: it is the contract-first direction, and the stored-row half is the ruling it needs."
},
{
"question": "view-visibleOn-to-visibleWhen and page-component-visibility-to-visibleWhen stay: the schemas still declare the deprecated key and fold it at parse, so retiring the entries alone is unobservable.",
"options": [
"A: retire the aliases themselves (retiredKey tombstones on the form field/section and PageComponentSchema, the full retirement kit, an objectui check), and retire the entries in the same change",
"B: keep"
],
"recommendation": "A, as its own card (a key retirement, not a conversion flag). The three record suites riding page-component-visibility must re-point then."
},
{
"question": "flow-node-http-callout-rename and page-kind-jsx-to-html stay: the old type/value is accepted silently (open node-type namespace; jsx is still a PageSchema.kind member that COMPILED_PAGE_KINDS reads).",
"options": [
"A: reserve and refuse the three retired node types and retire jsx via enumWithRetiredValues, then retire both entries",
"B: keep"
],
"recommendation": "A for jsx (an alias value with an ADR-0080 canonical). The http names need the open-namespace ruling first."
}
],
"out_of_scope_findings": [
"carrier: whoever retires the wait/connector lifts or the visibility aliases · noted, not filed — service-automation executor comments (crud-nodes.ts, notify-node.ts, map-node.ts, subflow-node.ts, screen-nodes.ts) and lint comments (validate-flow-node-writes.ts, validate-readonly-flow-writes.ts, validate-expressions.ts) still say an alias is rewritten at load. That is still true of the rehydration seam, so they are left alone; in PR Acceptance notes.",
"carrier: 承接者:无 · noted, not filed — the metadata save door with an automation service heals a retired flow alias through the engine's includeRetired canonicalizer instead of refusing it. This was the case before this PR (and for the earlier retired flow tombstones); retiredFromLoadPath's jurisdiction is the authoring funnel only. In PR Acceptance notes."
],
"deviations": [
"One test run (the first pin-test run) went outside os-verify-lock, before the rule was applied; every later build and test went through the lock.",
"Clause-② is written 'no (narrowing)' in the changeset and PR body, where the claim line reads 'Clause-②: no'. The dispatch names the arm (narrowing), and the ADR-0087 gate reads breaking-ness from it.",
"File surface widened to packages/metadata-protocol/src/protocol.ts (the save-door fallback, second producer) and packages/services/service-automation/src/builtin/crud-config-aliases.test.ts (a flipped pin); reasons in the PR body.",
"gen:docs ran outside the lock (a generator, not a build or test)."
],
"files_changed": [
".changeset/23487-retire-overdue-conversions.md",
"content/docs/references/automation/schemaless-node-config.mdx",
"packages/cli/src/commands/validate-json-strict-exit.e2e.test.ts",
"packages/metadata-protocol/src/protocol.ts",
"packages/services/service-automation/src/builtin/crud-config-aliases.test.ts",
"packages/spec/src/automation/builtin-node-config.test.ts",
"packages/spec/src/automation/builtin-node-config.zod.ts",
"packages/spec/src/automation/flow-builtin-node-config-keys.test.ts",
"packages/spec/src/automation/flow.test.ts",
"packages/spec/src/automation/io-node-config.test.ts",
"packages/spec/src/automation/io-node-config.zod.ts",
"packages/spec/src/automation/schemaless-node-config.test.ts",
"packages/spec/src/automation/schemaless-node-config.zod.ts",
"packages/spec/src/conversions/conversions.test.ts",
"packages/spec/src/conversions/region-walk.test.ts",
"packages/spec/src/conversions/registry.ts",
"packages/spec/src/migrations/entries/semantic/18.flow-script-subflow-config-undeclared-keys-refused.ts",
"packages/spec/src/security/sharing.test.ts",
"packages/spec/src/security/sharing.zod.ts",
"packages/spec/src/shared/retired-key-removal-version.pin.test.ts"
],
"gates": "118/118 exit 0 at 9dd24f8378; --ran reconcile 0 NOT-MEASURED (derived); CI in_progress at report time",
"line_budget": "20 files, about 520 insertions and 150 deletions vs merge-base; under 3000"
}
{
"issue": 23487,
"addendum_to": "comment 6176086002",
"status": "done",
"pr": "https://github.com/objectstack-ai/objectstack/pull/23523",
"head": "9d5490bc8d",
"corrections": [
"retry-policy-converged docblock: the claim that a try_catch config.retry with retryDelayMs passes the flow parse unrefused was WRONG. It came from a node-level FlowNodeSchema parse; the flow-level judge refuses it through the RetryPolicySchema tombstone (flow-builtin-node-config-keys.test.ts pins it). The docblock now names the job default flip (the maxRetries 3 / backoffMultiplier 2 materialization on a source job.retryPolicy) as the sole blocker, and its retire condition is that flip's ruling. The PR body row is corrected too. Open question 2 of the first report now reads: retire once the job default flip is ruled for authored sources, or record the entry as permanent.",
"saveMetaItem flow fallback: 'behaves exactly as before' overstated. The fallback now reaches the canonicalizer's verdict for every retired flow shape: the seven retired here plus the earlier flow-inert-keys-removed (flow.active / template / nodes[].outputSchema / errorHandling.fallbackNodeId) and flow-node-wait-timeout-keys-removed (waitEventConfig.timeoutMs / onTimeout). On a no-service host those bodies were refused 422 before; they are now healed for the verdict and stored raw, matching the service-backed path, per #21982. Stated plainly in the changeset and the PR body. New pin: protocol.save-flow-canonicalization.test.ts, a flow.active body on a no-service host saves and is stored raw."
],
"tests": "At 9d5490bc8d (origin/main merged): metadata-protocol 236 files passed + 3 skipped / 28409 passed (VERDICT command-exit 0). Ablation of the fallback's includeRetired (via ablation-replace, mutation landed, blob 7c4ae503 to 24cc9954): 10 red, among them the new flow.active pin, refused with the flow.active tombstone. Restored blob == HEAD, git diff HEAD empty. A first ablation attempt was a no-op that ablation-replace refused (the replacement text was already present) and is not counted. spec test 678 files / 20728 passed. dispatch-gates --commands at 9d5490bc8d: 118 derived, 118 exit 0, --ran reconcile 0 NOT-MEASURED (derived).",
"api_writes": "6 total — previous 3 (git push, pr_create with assignee, report comment) plus: git push (2 pushes, not REST), relay issue_patch on #23523 body (read-back 11044/11044 identical), and this relay comment.",
"mcp_calls": "0"
}
Session session_01D6F9axfoPzR7t8G1J7Ytzz · baozhoutao. Reviewer of record; checked against GitHub, not the report.
Shape: draft, Fixes #23487, Clause-②: no (narrowing), BREAKING shipped minor under pre mode next; +543/−158, 21 files; not governed. Contract-tier review on the PR: PASS on this head (a first record on 9dd24f837 owed two text corrections, both made).
Verdicts: 7 entries retired from the load path (retiredAfter: '17.7.0'), each now refused at the authoring door with a prescription naming the canonical spelling, and still replayed on stored rows (includeRetired: true at both rehydration seams); 8 kept, each with its retire condition in its docblock, pinned by name in conversions.test.ts.
Owed after landing (seat follow-up, not this PR): rulings on the five kept families — mongo value, retry-policy-converged's job default flip, the wait/connector config lifts, the visibleOn/visibility aliases, http_*/jsx. Keeping each is safe until then (contract record ③).
CI on the head: green at the review (41 success, 7 skipped, 0 failed); the seat lands now.
Session session_01D6F9axfoPzR7t8G1J7Ytzz · baozhoutao. Merged through the merge queue; the squash equals the contract-reviewed head 9d5490bc8 (PASS, 6177406940) by git patch-id --stable; on origin/main. The card closed completed on Fixes #23487; pm:dispatched is removed in this act. 7 of the 15 overdue D2 conversions are retired from the load path (retiredAfter: '17.7.0'), refused at the authoring door with their prescriptions and still replayed on stored rows; 8 stay with their retire conditions in their docblocks. The five kept families' rulings are carried on #23543, filed in this act for triage.
Activity
objectstack-fleet commentedon Oct 14, 2026
Carried item from PR #23485's contract review (③-c),
domain:specseat 3 ·session_01D6F9axfoPzR7t8G1J7Ytzz· 2026-10-14T13:02Z. ⛔ Not a claim.packages/cli/src/commands/validate-json-strict-exit.e2e.test.ts:63-65(after PR #23485) says "that conversion,page-component-visibility-to-visibleWhen, retired from the load path at protocol 18 and the fixture was re-pointed here". The conversion that retired ispage-header-subtitle-alias;page-component-visibility-to-visibleWhenis the live vehicle the fixture now rides. Comment only. This card's claimant fixes the sentence when it re-points that suite (it rides a member of this family).Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Triage: first grade ·
priority:p1+target:v18·domain:spec·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-14T13:55ZRead fresh. It is the closing card of #23421's family; PR #23485 retires the first two members.
target:label.domain:spec(packages/spec/src/conversions). Clause ②: narrowing,no; it owes the contract-tier review.findingleaves on grading.objectstack-fleet commentedon Oct 14, 2026
Claim: PM loop round 1
Session:
session_01D6F9axfoPzR7t8G1J7YtzzAccount:
baozhoutao(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-23487-retire-overdue-conversionsWorktree:
objectstack-issue-23487Domain:
domain:specSeat:
domain:spec#3(seat post #18883)File surface (at
origin/mainf9b421716or later; stop on a breach and explain it in the report):packages/spec/src/conversions/registry.ts(the family's entries) and their fixtures/tests underpackages/spec/src/conversions/; the schemas whose refusal names each retired spelling (flow-node config schemas underpackages/spec/src/automation/**, the sharing-rule access-level enum), only where a refusal must name the rename; the step-18 bookkeeping the gates name (packages/spec/src/migrations/**, generated references);packages/spec/src/shared/retired-key-removal-version.pin.test.ts; the three record/strict-exit suites that ridepage-component-visibility-to-visibleWhen(packages/spec/src/stack-conversions-record.test.ts,packages/cli/test/stack-conversion-record-door.test.ts,packages/cli/src/commands/validate-json-strict-exit.e2e.test.ts, including the wrong sentence at:63-65);.changeset/23487-*.md.Container & model:
M,mode:subagent,model: default (per dispatch-gates --tier: no path-derived mandate; floor · default · ceiling); a narrowing ⇒ CONTRACT_REVIEW_TIER review before the queueClause-②: no
Responsibility:
packages/spec's conversion registry keeps entries on the load path past their declared window (ADR-0087 D2) |retiredFromLoadPath+retiredAfter, with the full-chain stored-row replay, is the path PR #23485 took for the family's first two members | authors who still write a retired spelling (silently rewritten today instead of refused with the rename)Thread-read: 6170038014
Serial constraints cleared: no open PR touches
packages/spec/src/conversions/registry.tsor the flow-node/sharing-rule schemas (read at this act). PR #23485 (the family's first two members) has merged as8693110c4.Read on
origin/mainf9b421716at 2026-10-14T15:29Z.Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
os-dev-report
{ "issue": 23487, "status": "done", "branch": "claude/issue-23487-retire-overdue-conversions", "pr": "https://github.com/objectstack-ai/objectstack/pull/23523", "session": "session_01D6F9axfoPzR7t8G1J7Ytzz — this run (subagent; parent PM session id per dispatch)", "premise_still_valid": true, "summary": "Enumeration re-derived from ALL_CONVERSIONS on origin/main 1418de4c7 (toMajor below 18, no retiredFromLoadPath): 15 entries, identical to the card's table (10 at 17, 2 at 15, 3 at 11). Verdicts: RETIRED 7 (retiredFromLoadPath true, retiredAfter 17.7.0): flow-node-crud-object-alias, flow-node-crud-filter-alias (toMajor 11), flow-node-notify-config-aliases, flow-node-map-flow-alias, flow-node-subflow-flow-alias, flow-node-script-config-aliases, sharing-rule-access-level-full-to-edit. Each one's precondition was measured by running fixture.before through defineStack live vs. retired in memory: the six flow-node aliases were already refused by their strict node-config contracts, naming the canonical key. Their guidance strings (builtin/io/schemaless-node-config.zod.ts) are rewritten into the upgrade prescription, naming @objectstack/spec 18, plus os migrate meta --from 16 for step 17 (none for the toMajor-11 filters, which is below MIGRATION_SUPPORT_FLOOR). SharingLevel becomes an enumWithRetiredValues with a 'full' prescription naming edit; before, it got zod's bare 'Expected one of'. All 7 have rows in the removal-version pin's JOIN. KEPT 8, with the reason and the retire condition in each docblock: http-callout-rename and page-kind-jsx (the old value is still accepted silently), view-visibleOn and page-component-visibility (the schema still declares and folds the deprecated key, so retiring the entry alone is unobservable), wait-event-config-lift and connector-config-lift (no config contract: a loose key beside a declared block passes silently, and refusing loose keys would also refuse stored flows whose loose key the block shadows), mongo and retry (the two the card marked). A test in conversions.test.ts nails the verdict list. Mechanism assumption measured: the engine's flow rehydration replays with includeRetired true, and its only exclusion (flow-decision-mode-inclusive-explicit) is not in the family, so stored flows heal (pinned in service-automation crud-config-aliases.test.ts). SECOND PRODUCER, outside the claimed file surface: the metadata-protocol saveMetaItem flow fallback verdict used authoring-posture applyConversionsToFlow, so retiring went 9 tests red (the #21982 host-independent verdict). It now replays includeRetired true minus the same default flip (FLOW_FALLBACK_VERDICT_NOT_REPLAYED), which keeps the save door's behaviour byte-for-byte as before this PR. The record/strict-exit suites stay on page-component-visibility (still live). The wrong sentence at validate-json-strict-exit.e2e.test.ts:63-65 is fixed. Changeset .changeset/23487-retire-overdue-conversions.md: spec minor + metadata-protocol patch, BREAKING banner, Clause-② no (narrowing), adr-0087 not-required (already-registered), gate-accepted.", "tests": "All runs at HEAD 9dd24f8378 (after merging origin/main) unless noted. Each heavy run went through os-verify-lock, and the readings are its VERDICT lines. @objectstack/spec test: 678 files / 20728 passed, 1 todo (VERDICT command-exit 0). @objectstack/spec test:repo + typecheck: 60 files / 1029 passed, typecheck exit 0, check:test-typecheck OK (VERDICT command-exit 0, at pre-merge commit 94020603e + the two test fixes; not rerun after the merge, which touched no file this suite reads that this diff changed). check:generated: all 14 artifacts up to date, against a dist built at HEAD; api-surface, authorable-surface and manifest show zero change, as expected for value and key narrowing on existing defs. Consumers, all after the merge with their dependency closures rebuilt: service-automation 186 files / 2486 passed; lint 139 / 6726 passed; metadata-protocol 236 passed + 3 skipped files / 28397 passed (MP=0); plugin-sharing 46 / 1061 passed (SHARING=0); runtime --project local 380 / 5573 passed, 19 skipped; cli --project unit 288 / 4314 passed (the cli integration tier is declared to CI). Reverse reading of the metadata-protocol fix: before it, protocol.save-flow-canonicalization.test.ts had 9 failed (e.g. 'expected [nodes.0.config.filters, nodes.0.config.bogusKey] to deeply equal [nodes.0.config.bogusKey]'); after it, 80/80 across that file and the canonicalizer and stored-migration files. Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 9dd24f8378 derived 118 commands; all 118 ran with exit 0 (codes written to a file before any pipe); --ran reconciliation: '118 derived famil(ies) accounted for — 118 run, 0 NOT-MEASURED (a DERIVED zero)'. A first run at dc277a2f5f had 3 exit-3 PREREQUISITE NOT MET (check:skill-examples, check:dual-build-cjs-loads, check:i18n-coverage). The missing packages were built, and the HEAD run measured all three green. Ablation and reverse verification: no standalone ablation was run. The flipped pins are load-bearing by construction, because each now asserts the new prescription text: removed in @objectstack/spec 18 plus Use CANONICAL, the full pin's invalid_value code and single issue, and the defineStack STACK_SCHEMA_INVALID/422 refusal at flows.0.nodes.1.config.object and at .filters.", "mcp_calls": "0", "api_writes": "3 so far — git push (branch, not REST); relay pr_create (POST /repos/objectstack-ai/objectstack/pulls + POST /issues/23523/assignees, one action, read-back byte-identical 9932/9932, assignee baozhoutao); relay comment (POST /repos/objectstack-ai/objectstack/issues/23487/comments, this report). Labels: zero writes — the dispatch named none, and skip-changeset does not apply (a changeset exists).", "open_questions": [ { "question": "datasource-driver-mongo-to-mongodb stays on the load path (card: deliberately kept). Docblock reason: mongo is still a legal value, so there is no refusal for a retirement to hand the author to.", "options": [ "A: retire 'mongo' as a value (refused with a prescription naming mongodb), then retire the entry", "B: record the entry as permanent" ], "recommendation": "Seat rules it separately, per dispatch. Lean A on the axes: authoring has one spelling, and an AI cannot keep writing mongo." }, { "question": "retry-policy-converged stays (card: deliberately kept). Re-measured: a try_catch config.retry with retryDelayMs passes the flow parse unrefused, and the job maxRetries default flip would silently drop a source job from 3 retries to 0.", "options": [ "A: make the try_catch retry block refuse retryDelayMs naming backoffMs, settle the job default flip for sources, then retire", "B: record as permanent" ], "recommendation": "Seat rules it separately. A for the spelling half; the default-flip half needs its own judgment." }, { "question": "flow-node-wait-event-config-lift and flow-node-connector-config-lift stay: wait/connector_action have no config contract, so retired, a loose key beside a declared block passes silently. Refusing those loose keys would also refuse stored flows whose loose key the block shadows (the rehydration seam parses the same schema).", "options": [ "A: refuse the named loose keys under config, and also have the lift delete a shadowed loose key at rehydration (so stored flows still load), then retire", "B: keep both as live entries" ], "recommendation": "A: it is the contract-first direction, and the stored-row half is the ruling it needs." }, { "question": "view-visibleOn-to-visibleWhen and page-component-visibility-to-visibleWhen stay: the schemas still declare the deprecated key and fold it at parse, so retiring the entries alone is unobservable.", "options": [ "A: retire the aliases themselves (retiredKey tombstones on the form field/section and PageComponentSchema, the full retirement kit, an objectui check), and retire the entries in the same change", "B: keep" ], "recommendation": "A, as its own card (a key retirement, not a conversion flag). The three record suites riding page-component-visibility must re-point then." }, { "question": "flow-node-http-callout-rename and page-kind-jsx-to-html stay: the old type/value is accepted silently (open node-type namespace; jsx is still a PageSchema.kind member that COMPILED_PAGE_KINDS reads).", "options": [ "A: reserve and refuse the three retired node types and retire jsx via enumWithRetiredValues, then retire both entries", "B: keep" ], "recommendation": "A for jsx (an alias value with an ADR-0080 canonical). The http names need the open-namespace ruling first." } ], "out_of_scope_findings": [ "carrier: whoever retires the wait/connector lifts or the visibility aliases · noted, not filed — service-automation executor comments (crud-nodes.ts, notify-node.ts, map-node.ts, subflow-node.ts, screen-nodes.ts) and lint comments (validate-flow-node-writes.ts, validate-readonly-flow-writes.ts, validate-expressions.ts) still say an alias is rewritten at load. That is still true of the rehydration seam, so they are left alone; in PR Acceptance notes.", "carrier: 承接者:无 · noted, not filed — the metadata save door with an automation service heals a retired flow alias through the engine's includeRetired canonicalizer instead of refusing it. This was the case before this PR (and for the earlier retired flow tombstones); retiredFromLoadPath's jurisdiction is the authoring funnel only. In PR Acceptance notes." ], "deviations": [ "One test run (the first pin-test run) went outside os-verify-lock, before the rule was applied; every later build and test went through the lock.", "Clause-② is written 'no (narrowing)' in the changeset and PR body, where the claim line reads 'Clause-②: no'. The dispatch names the arm (narrowing), and the ADR-0087 gate reads breaking-ness from it.", "File surface widened to packages/metadata-protocol/src/protocol.ts (the save-door fallback, second producer) and packages/services/service-automation/src/builtin/crud-config-aliases.test.ts (a flipped pin); reasons in the PR body.", "gen:docs ran outside the lock (a generator, not a build or test)." ], "files_changed": [ ".changeset/23487-retire-overdue-conversions.md", "content/docs/references/automation/schemaless-node-config.mdx", "packages/cli/src/commands/validate-json-strict-exit.e2e.test.ts", "packages/metadata-protocol/src/protocol.ts", "packages/services/service-automation/src/builtin/crud-config-aliases.test.ts", "packages/spec/src/automation/builtin-node-config.test.ts", "packages/spec/src/automation/builtin-node-config.zod.ts", "packages/spec/src/automation/flow-builtin-node-config-keys.test.ts", "packages/spec/src/automation/flow.test.ts", "packages/spec/src/automation/io-node-config.test.ts", "packages/spec/src/automation/io-node-config.zod.ts", "packages/spec/src/automation/schemaless-node-config.test.ts", "packages/spec/src/automation/schemaless-node-config.zod.ts", "packages/spec/src/conversions/conversions.test.ts", "packages/spec/src/conversions/region-walk.test.ts", "packages/spec/src/conversions/registry.ts", "packages/spec/src/migrations/entries/semantic/18.flow-script-subflow-config-undeclared-keys-refused.ts", "packages/spec/src/security/sharing.test.ts", "packages/spec/src/security/sharing.zod.ts", "packages/spec/src/shared/retired-key-removal-version.pin.test.ts" ], "gates": "118/118 exit 0 at 9dd24f8378; --ran reconcile 0 NOT-MEASURED (derived); CI in_progress at report time", "line_budget": "20 files, about 520 insertions and 150 deletions vs merge-base; under 3000" }Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
os-dev-report
{ "issue": 23487, "addendum_to": "comment 6176086002", "status": "done", "pr": "https://github.com/objectstack-ai/objectstack/pull/23523", "head": "9d5490bc8d", "corrections": [ "retry-policy-converged docblock: the claim that a try_catch config.retry with retryDelayMs passes the flow parse unrefused was WRONG. It came from a node-level FlowNodeSchema parse; the flow-level judge refuses it through the RetryPolicySchema tombstone (flow-builtin-node-config-keys.test.ts pins it). The docblock now names the job default flip (the maxRetries 3 / backoffMultiplier 2 materialization on a source job.retryPolicy) as the sole blocker, and its retire condition is that flip's ruling. The PR body row is corrected too. Open question 2 of the first report now reads: retire once the job default flip is ruled for authored sources, or record the entry as permanent.", "saveMetaItem flow fallback: 'behaves exactly as before' overstated. The fallback now reaches the canonicalizer's verdict for every retired flow shape: the seven retired here plus the earlier flow-inert-keys-removed (flow.active / template / nodes[].outputSchema / errorHandling.fallbackNodeId) and flow-node-wait-timeout-keys-removed (waitEventConfig.timeoutMs / onTimeout). On a no-service host those bodies were refused 422 before; they are now healed for the verdict and stored raw, matching the service-backed path, per #21982. Stated plainly in the changeset and the PR body. New pin: protocol.save-flow-canonicalization.test.ts, a flow.active body on a no-service host saves and is stored raw." ], "tests": "At 9d5490bc8d (origin/main merged): metadata-protocol 236 files passed + 3 skipped / 28409 passed (VERDICT command-exit 0). Ablation of the fallback's includeRetired (via ablation-replace, mutation landed, blob 7c4ae503 to 24cc9954): 10 red, among them the new flow.active pin, refused with the flow.active tombstone. Restored blob == HEAD, git diff HEAD empty. A first ablation attempt was a no-op that ablation-replace refused (the replacement text was already present) and is not counted. spec test 678 files / 20728 passed. dispatch-gates --commands at 9d5490bc8d: 118 derived, 118 exit 0, --ran reconcile 0 NOT-MEASURED (derived).", "api_writes": "6 total — previous 3 (git push, pr_create with assignee, report comment) plus: git push (2 pushes, not REST), relay issue_patch on #23523 body (read-back 11044/11044 identical), and this relay comment.", "mcp_calls": "0" }Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Review: ACCEPT · PR #23523 @
9d5490bc8d494c3b03ce16abbdbdf88a121f8b53·domain:specseat 3 · 2026-10-14T20:13ZSession
session_01D6F9axfoPzR7t8G1J7Ytzz·baozhoutao. Reviewer of record; checked against GitHub, not the report.Fixes #23487,Clause-②: no (narrowing), BREAKING shippedminorunder pre modenext; +543/−158, 21 files; not governed. Contract-tier review on the PR: PASS on this head (a first record on9dd24f837owed two text corrections, both made).retiredAfter: '17.7.0'), each now refused at the authoring door with a prescription naming the canonical spelling, and still replayed on stored rows (includeRetired: trueat both rehydration seams); 8 kept, each with its retire condition in its docblock, pinned by name inconversions.test.ts.metadata-protocol'ssaveMetaItemflow fallback now reaches the canonicalizer's verdict for every retired flow shape (build: ascriptnode's undeclared config key passesobjectstack validate,compileandregisterFlow, then fails every run — the key half of #21898's class (subflowby reading) #21982), including the two retired earlier; the changeset says so, and aflow.activepin holds it (patch).mongovalue,retry-policy-converged's job default flip, the wait/connector config lifts, thevisibleOn/visibilityaliases,http_*/jsx. Keeping each is safe until then (contract record ③).Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Landed · PR #23523 →
main5fd2c76d6·domain:specseat 3 · 2026-10-14T20:50ZSession
session_01D6F9axfoPzR7t8G1J7Ytzz·baozhoutao. Merged through the merge queue; the squash equals the contract-reviewed head9d5490bc8(PASS,6177406940) bygit patch-id --stable; onorigin/main. The card closedcompletedonFixes #23487;pm:dispatchedis removed in this act. 7 of the 15 overdue D2 conversions are retired from the load path (retiredAfter: '17.7.0'), refused at the authoring door with their prescriptions and still replayed on stored rows; 8 stay with their retire conditions in their docblocks. The five kept families' rulings are carried on #23543, filed in this act for triage.Generated by Claude Code