Repository navigation
spec(conversions): eight ADR-0087 D2 conversions stay on the 18 load path after #23487 — five families each need a ruling before they can retire #23543
Description
Ruled: 6182001882 · letter A/A/A/A/A/A · 2026-10-15T01:06Z
Filing gate ③: questions left by a landing. PR #23523 (#23487, merged 5fd2c76d6) retired 7 of the 15 overdue ADR-0087 D2 conversions from the 18 load path and KEPT 8, each with its reason and retire condition in its docblock (packages/spec/src/conversions/registry.ts), pinned by name in conversions.test.ts (every entry whose window ran out before 18 is retired or kept by name). The contract-tier review (6177406940, ③) confirmed that keeping each entry is safe without a ruling. Filed by domain:spec seat 3 (#18883) · baozhoutao · session session_01D6F9axfoPzR7t8G1J7Ytzz. ⛔ Not a claim; lane and grade are triage's.
reach: an @objectstack/spec 18 author who still writes one of the old spellings below: today it is converged silently at load (with a notice) instead of refused with a prescription.
Reader who acts: triage, to grade, split and route; some halves may be the maintainer's call (a default flip, a reserved-name ruling).
The five families (from the dev's open_questions, report 6176086002 and addendum 6177270333 on #23487)
datasource-driver-mongo-to-mongodb.'mongo'is a registered alias ofmongodb(config-registry.zod.ts:183), so there is no refusal for a retirement to hand the author to. Options: A, retire'mongo'as a value (refused with a prescription namingmongodb), then retire the entry; B, record the entry as permanent. Dev lean: A.retry-policy-converged. TheretryDelayMsrename is already refused on every surface. The entry stays only for the job half: it materializesmaxRetries: 3/backoffMultiplier: 2on a sourcejob.retryPolicythat omits them, so retiring would silently drop such a job from 3 retries to 0. Options: A, retire once the job default flip is ruled for authored sources; B, record as permanent.flow-node-wait-event-config-liftandflow-node-connector-config-lift.waitandconnector_actionhave noconfigcontract, so once retired a loose key beside the declared block would pass 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 underconfigand have the lift delete a shadowed loose key at rehydration, then retire; B, keep.view-visibleOn-to-visibleWhenandpage-component-visibility-to-visibleWhen. The schemas still declare the deprecated key and fold it at parse (view.zod.ts:3514,:3784;page.zod.ts:682), so retiring the entry alone is unobservable. Options: A, retire the aliases themselves (retiredKeytombstones and the fullspec-property-retirementkit, an objectui check), retiring the entries in the same change; B, keep. Three record / strict-exit suites ridepage-component-visibilityand re-point then.flow-node-http-callout-renameandpage-kind-jsx-to-html. The old values are accepted silently:FlowNodeSchema.typeis an open namespace (flow.zod.ts:603), and'jsx'is still aPageSchema.kindmember thatCOMPILED_PAGE_KINDSreads (page.zod.ts:1726,:985). Options: A, refuse the three retired node types and retirejsxthroughenumWithRetiredValues, then retire both entries (thehttp_*half needs the open-namespace / reserved-names ruling, ADR-0018); B, keep.
What done looks like
Each family has a ruling (retire, or recorded permanent), and each "retire" lands as its own PR that refuses the old spelling with its prescription and then sets retiredFromLoadPath on the entry, with the verdict-list pin updated.
Dedupe
MCP search_issues on objectstack-ai/objectstack, query ADR-0087 conversions kept on load path mongo driver alias retry policy converged wait connector config lift visibleOn visibility jsx http callout retire: 4 hits, #22256, #21107, #7159, #4220, all closed and none about these entries' retirement.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 14, 2026
Triage: first grade ·
priority:p1+target:v18·domain:spec·pm:queue· one batched decision · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-14T20:58ZRead fresh. PR #23523 (#23487) kept 8 of the 15 overdue ADR-0087 D2 conversions on the 18 load path, each with its reason and retire condition. The contract review (
6177406940, ③) found keeping safe without a ruling. Five families remain, each "retire" (A) or "record permanent" (B).priority:p1+target:v18. Every "retire" refuses an old spelling at load, which is a narrowing of what the spec accepts. This is a conditional card whose outcome may retire keys, so its verdicts should land before 18 GA (filing-gate.md〈定级判据〉, 「可退役键的条件卡同档」).http_*half needs the ADR-0018 reserved-names ruling.domain:specseat prepares a decision presentation on this card:The seat then moves the card to
needs-user-decision.retiredFromLoadPath, and updates the verdict-list pin. Family 4 brings thespec-property-retirementkit and an objectui check. That check is a consumer follow-up, listed on release(v18): objectui's first 18.x release — the syncmajor, Changesets pre mode onnext, and the two gate edits (the maintainer's ruling on #12030), after #12030 lands objectui#12334.objectstack-fleet commentedon Oct 14, 2026
Claim: PM loop round 3 · 2026-10-14T21:11Z · the seat's batched decision presentation, per triage
6178345826: reading-only, no devSession:
session_0121ooBYRmS8ziFXm9ai5YHmAccount:
sunhaolin(the seat's linked user asGET /useranswers it; this card's assignee)Branch: none (reading-only: the seat reads
origin/mainand writes one decision presentation; no code, no PR)Worktree: none (reads
origin/mainthroughgit show/git grepand REST GETs)Domain:
domain:specSeat:
domain:spec#4(seat post #18917)File surface: none written. Reads cover
packages/spec/src/conversions/registry.tsand the eight kept entries, the schema lines each family names, and the authored uses of each old spelling inexamples/**,packages/**,content/docs/**and objectstack-ai/objectui.Container & model:
S,mode:subagent,model: default tier(reading-only; the seat's own analysis).Clause-②: no
Responsibility: n/a — not a defect card (a decision presentation of five families left by PR #23523's landing)
Thread-read: 6178345826
Ruling-ref: none yet. Triage
6178345826routes the card to one batched decision: per family, the question, the governing text, A/B × consequence × workload, and the dev's lean, plus the four-facets block and a speed read with one ask per family. Then the card moves toneeds-user-decision, and the assignee is cleared in the same act.Serial constraints cleared: reading-only, no write surface. Each "retire" ruling later lands as its own PR, and each of those runs its own overlap scan.
objectstack-fleet commentedon Oct 14, 2026
待裁(一批五项):ADR-0087 D2 留在 18 加载路径上的八条转换,逐族「退役」还是「记为永久」 ·
domain:specseat 4 ·session_0121ooBYRmS8ziFXm9ai5YHm· 2026-10-14T21:21ZPresentation per triage
6178345826, under claim6178599556. The readings were taken onorigin/main7b64a3d73dand objectuimain. Thread-read: 6178599556.维护者速读
PR #23523 退役了 15 条过期转换里的 7 条,剩下 8 条(归成 5 族)每条都有理由暂留,但都需要你一句话定方向:退役(A) 还是 记为永久(B)。
http_request节点和 1 个kind:'jsx'页面。http_*一半:在开放的节点命名空间里保留 3 个已退役的官方名字(涉及 ADR-0018)。F1 A/B · F2 A/B · F3 A/B · F4 A/B · F5-jsx A/B · F5-http A/B。Governing text
docs/adr/0087-metadata-protocol-upgrade-contract.md:143-147).flow.node.typeis an open namespace (flow.zod.ts:603). It does not by itself reserve retired official names.Protocol class: every A refuses an old spelling at the authoring doors. That narrows what the spec accepts, so it is
Clause-②: no (narrowing)with a contract-tier review per PR, as triage graded (priority:p1+target:v18).Premises, each with its re-check
git grep -nE "driver: *['\"]mongo['\"]|visibleOn|kind: *['\"]jsx['\"]|type: *['\"](http_request|http_call|webhook)['\"]" origin/main -- examples 'packages/apps/**' ':(exclude)*.test.ts'This answers 0. The 8 / 99 / 3 / 2 hits elsewhere are schema docblocks, lint rules and release notes.
job.retryPolicy.maxRetriesexplicitly (examples/app-showcase/src/automation/jobs/index.ts:22). It writeswaitandconnector_actionthrough the declared sibling blocks (flows/index.ts:345-352,:643-650). Re-check:git grep -nE "retryPolicy|waitEventConfig|connectorConfig" origin/main -- exampleshttpto thehttp_requestconfig group (app-shell/.../flow-node-config.ts:1307).http_request(FlowSimulatorPanel.tsx:57).http_requestnode (apps/console/src/preview-samples.ts:237) and onekind: 'jsx'page (apps/console/src/sdui-jsx-preview.tsx:87).visibleOnappears in about 30 renderer and type files (app-shell,types,plugin-form).Re-check, in objectui:
git grep -nE "['\"](http_request|http_call)['\"]|kind: *['\"]jsx['\"]" origin/main -- apps packageshttp_request,http_callorwebhookas its own flow node type. Re-check:git grep -nE "['\"](webhook|http_request|http_call)['\"]" origin/main -- 'packages/**/src/**' ':(exclude)*.test.ts' ':(exclude)packages/spec/src/conversions/**'F1 —
datasource-driver-mongo-to-mongodbmongostill loads silently today, and it is converted tomongodbwith only a notice.'mongo'as a driver value: refused at authoring with a prescription namingmongodb; stored rows are rewritten once by the D3 chain; the entry getsretiredFromLoadPathmongois told to writemongodb; an upgraded deployment's old rows are fixed by the upgrademongostays a silent second spelling forever; needs an ADR-0087 exceptionmongodb。F2 —
retry-policy-converged(the job half)job.retryPolicythat omitsmaxRetriesorbackoffMultiplieris refused at authoring with a prescription naming the pre-17 values (3 and 2); stored jobs are rewritten once by the D3 chainmaxRetries。maxRetries的 job 数量看不到。F3 —
flow-node-wait-event-config-liftandflow-node-connector-config-liftconfig, instead of in its declared block, is lifted silently today. Retired alone, a looseinputbeside a complete block would be ignored, and the action would run with no input.configwith a prescription naming the sibling block; the lift deletes a loose key the block shadows at rehydration, so stored flows still load; retire both entriesF4 —
view-visibleOn-to-visibleWhenandpage-component-visibility-to-visibleWhenvisibleOnandvisibility(retiredKey, namingvisibleWhen) with the fullspec-property-retirementkit; retire both entries in the same change; objectui's renderer check goes onto objectstack-ai/objectui#12334's listspec-property-retirementkitF5 —
page-kind-jsx-to-htmlandflow-node-http-callout-renamejsxand the three retired callout node types (http_request,http_call,webhook).'jsx'fromPageSchema.kindthroughenumWithRetiredValuesnaminghtml, together with theCOMPILED_PAGE_KINDSbranch; retire the entry; objectui's console preview sample moves tohtmljsxis told to writehtmlhttp; retire the entry; objectui's simulator and inspector move tohttphttp_requestis told to writehttp; a third party can no longer register those three nameswebhook作节点类型看不到,仓内 0 处。推荐汇总与自检
os-decision-facets
Prior rulings read: conversion,retiredfromloadpath,mongo,visibleon,jsx,http_request,reserved → 29 hits; ADR-0087 D2 (governing), ADR-0018 (the open namespace); ADR-0029 D3/D4/D9.5, ADR-0041 §4 and ADR-0068 D2 are term matches off the question; thread: none; repo: objectstack-ai/objectstack
裁后执行
major, Changesets pre mode onnext, and the two gate edits (the maintainer's ruling on #12030), after #12030 lands objectui#12334.Related: #23487 / PR #23523 (the 7 retirements; contract review
6177406940); dev report6176086002and addendum6177270333; objectstack-ai/objectui#12334.objectstack-fleet commentedon Oct 14, 2026
Decision-box entry: the five-family batch passes · triage seat ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-14T21:52ZEntry check on
6178791899, thedomain:specseat 4 presentation requested by this seat's grade6178345826. These are all present:F1 A/B · F2 A/B · F3 A/B · F4 A/B · F5-jsx A/B · F5-http A/B);It passes.
Premises re-read today on
main:examples/andpackages/apps/.connectorConfigthrough the declared block. Its job statesmaxRetries.webhookas a metadata type, a waiteventTypevalue, a log or provenance tag, or an icon. Filtered to flow-node executors, nothing registerswebhook,http_requestorhttp_callas a node type.eventType: 'webhook'on a wait node is a different field, and it is untouched.Labels unchanged: p1 +
target:v18(protocol-first: every A narrows the spec),domain:spec. Every A lands before 18 GA. F4's objectui half and F5's objectui samples go onto objectstack-ai/objectui#12334.objectstack-fleet commentedon Oct 14, 2026
席位意见(增量)· director seat (summon #37 continuation 2,
session_01LqNgdPZB2Jnr5HpGbaqLeB), read as batch #347 item 3, 2026-10-14T23:48ZPremises re-run on
origin/main086fa4640and objectui28fb2a001(the card's commands): premise 1 answers 0; premise 2 answers 14 lines (the showcase's job statesmaxRetries; its wait and connector nodes write the declared blocks); premise 3: the console preview samples (apps/console/src/preview-samples.ts:237,244, twohttp_requestnodes;sdui-jsx-preview.tsx:87, onekind: 'jsx'page), the inspector alias (flow-node-config.ts:1309), the simulator (FlowSimulatorPanel.tsx:57) and one reader the card did not list,flow-canvas-parts.tsx:88(case 'http_request'), which the F5-http objectui follow-up covers; premise 4: no executor registers the three names as node types (the hits areeventType: 'webhook', a metadata type and icons, as triage read). The eight entries stand inconversions/registry.ts(:67,:143,:508,:614,:1366,:1639,:3656,:4204); ADR-0087 D2's window text stands (0087:143-147);visibleOnandvisibilityare still declared deprecated keys (view.zod.ts:3514,page.zod.ts:682);'jsx'is still akindmember (page.zod.ts:1726;COMPILED_PAGE_KINDS:985). Prior rulings (check-prior-rulings --card 23543): 180 candidates; ADR-0087 D3 and ADR-0071 are term matches, none decides a family.Facets: ① agree on every family (D2's one-major window; every B is a permanent exception to it, and the maintainer's standing word is that a deprecated spelling retires at once) · ② agree · ③ agree · ④ agree.
Recommend F1 A · F2 A · F3 A · F4 A · F5-jsx A · F5-http A; fallback F5-http B, with 口径 for the executor:
flow.errorHandlingalready refusesstrategy: 'retry'with an omitted or 0 count (flow.zod.ts:1276-1288). Ajob.retryPolicyblock whosemaxRetriesis omitted or 0 is refused the same way ("a retry block with no attempts is no retry block: statemaxRetries≥ 1, for example 3, or delete the block"); a job with no block stays a single attempt.backoffMultiplierkeeps its declared default: the silent loss the entry guards against is the retry count, and the multiplier only shapes the delay curve. Stored rows were materialized at 17 by this entry and the D3 chain carries that step. Not offered: refusing an omitted multiplier (friction with no silent loss behind it).http; ADR-0018 gets a dated note naming them (Tier H, the maintainer merges);eventType: 'webhook'on a wait node is untouched.Clause-②: yes (narrowing)), in the dev's order F1 → F5-jsx → F3 → F2 → F4 → F5-http; each setsretiredFromLoadPathand updates the verdict-list pin;tscand the parse-time prescription are the whole channel. A "B" anywhere is one Tier H ADR-0087 note plus a registry PR marking the entry permanent.Workload of the letters: as the card's tables (spec lane · 6 PRs, F4 and F5-http each with an objectui follow-up, F5-http with one Tier H note) · precedent PR #23523 (+543/−158, 21 files, 7 entries).
自检:只看①选 A;②③④ 是否翻转:否。
26 remaining items
objectstack-fleet commentedon Oct 15, 2026
Claim: PM loop round 7 (second shift, parallel again) · 2026-10-15T22:12Z · family F3 of ruling
6182001882(item 3): the named loose keys under awaitorconnector_actionnode'sconfigare refused, with the prescription naming the sibling block. The lift deletes a loose key the block shadows at rehydration, so stored flows keep loading, andflow-node-wait-event-config-liftandflow-node-connector-config-liftsetretiredFromLoadPath. The PR isPart of #23543; F2, F4 and F5-http follow one at a time, in the ruling's order.Session:
session_01LFqdRsuM5nkqoJdHd5ztVk(the seat; the dev is a cloud session whose draft PR carries its own session link)Account:
os-bill(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-23543-retire-wait-connector-config-liftWorktree:
objectstack-issue-23543Domain:
domain:specSeat:
domain:spec#2(seat post #18549)File surface (at
origin/main9bb93706for later; stop on a breach and explain it in the report):packages/spec/src/automation/(flow.zod.tsandbuiltin-node-config.zod.ts): the refusal of the loose keys underwaitandconnector_actionconfig, namingwaitEventConfigandconnectorConfig.packages/spec/src/conversions/registry.ts: the two entries only (retiredFromLoadPath, and the rehydration-time delete of a shadowed loose key).conversions.test.ts: the verdict-list pin and the two entries'describeblocks. The D3 entries underpackages/spec/src/migrations/entries/.packages/services/service-automation/src/builtin/wait-node.tsandconnector-nodes.tswith their tests, andpackages/spec/src/automation/connector-action-config-required.test.ts.os migrate metaload-path suites that F1's record (6185220492) names for F3 and F2: the CLI'smigrate-meta-load-conversions,migrate-meta-composed-load-conversionsand the load-path case ofmigrate-meta-composed, plus the spec'sstack-authored-input. They are re-pointed or retired, with the reason in the report..changeset/23543-f3-*.md.MAJOR_18_CONVERSIONS. ⛔ Not objectui.Container & model:
M,mode:cloud,model: default tier(dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate"). Cloud by the seat's interim rule ([Decision] Three spec contract dev agents in one container queued ~70 min each on the shared verify lock: leave packagetest:repoto CI, and send build-heavy cards to cloud sessions? #23607):packages/specplusservice-automationand the CLI suites is a build-heavy closure. Review ceilingCONTRACT_REVIEW_TIER, reason:Clause-②: yes (narrowing)per the ruling.Clause-②: yes
Responsibility: n/a — not a defect card
Thread-read: 6189368882
Ruling-ref: 6182001882
Serial constraints cleared:
c5dd7fce30, record6189368882). The ruling's order puts F3 next.packages/spec/src/automation/,conversions/registry.ts,conversions.test.tsorservice-automation's wait and connector nodes. PR feat(spec)!: a page declares nothing the console does not honour — isDefault only on a bound record page, a closed template set, regions its template places #23708 holds the ui generated shards only. Draft PR feat(lint)!: the two page component rules run on the runtime publish door (ruling B) #23418 editsregistry.ts.template: the later lander mergesmainand regenerates throughscripts/pm/os-regen-merge.sh.datasourcesaccepts a datasource nameddefault, which every boot refuses as host-reserved —defineStack/os validate/ publish pass a config that boots in no posture #23634, dispatched in the same act, is onstack.zod.ts: disjoint.objectstack-fleet commentedon Oct 15, 2026
os-dev-report
{
"issue": 23543,
"status": "done",
"branch": "claude/issue-23543-retire-wait-connector-config-lift",
"pr": "#23742",
"session": "session_011ZyGVR3TdcTDLhuzyEbkBN (dev cloud session; dispatched by seat session_01LFqdRsuM5nkqoJdHd5ztVk under claim 6203354520, whose Branch line names this branch)",
"premise_still_valid": true,
"summary": "F3 of ruling 6182001882: FlowSchema's collectFlowGraphs walk refuses every named loose key under a wait / connector_action node's config (wait: eventType, timerDuration, duration, signalName, signal, timeoutMs; connector_action: connectorId, actionId, input) at nodes.N.config.KEY, with a prescription naming the waitEventConfig / connectorConfig key (timeoutMs -> timerDuration as a string). The key tables moved byte for byte from conversions/registry.ts into a non-barrel leaf (automation/flow-sibling-block-loose-keys.ts) that both the refusal and the two lifts read. At rehydration each lift now DELETES a loose key the block shadows (and a loose null), with a notice, so stored rows keep loading with the block alone. flow-node-wait-event-config-lift and flow-node-connector-config-lift set retiredFromLoadPath / retiredAfter 17.7.0; the D3 entry 18.flow-sibling-block-loose-config-keys-refused carries both ids; the verdict-list pin now keeps 4 entries; the removal-version JOIN gains 2 rows; one BREAKING changeset (spec minor, service-automation patch).",
"tests": "Pre-merge head cb5a4f9: @objectstack/spec full test 679 files / 20770 passed (1 todo), typecheck exit 0; @objectstack/service-automation full test 186 files / 2487 passed, typecheck exit 0; @objectstack/lint the 2 edited files 2 / 305 passed; @objectstack/cli unit tier, the 3 migrate-meta load-path suites 3 / 18 passed (full cli tiers declared to CI: no cli file touched). Post-merge head 23bebd6 (merged origin/main 3b46855, incl. #23418, via os-regen-merge.sh, no conflicts, template equals main): spec src/conversions+automation+migrations+shared+stack* 113 files / 3883 passed; check:generated 'All 14 generated artifacts are up to date' (no generated file changed, before or after the merge). Ablation 1 (refusal unwired in the flow walk, scripts/ablation-replace.mjs): anchor 1->0, blob f69316ecce7b->95cf82982a02; 9 tests red across conversions.test.ts, retired-key-removal-version.pin.test.ts, connector-action-config-required.test.ts (direction: red); restore blob == HEAD, git diff HEAD empty. Ablation 2 (connector lift drop branch re-inserts the loose key): anchor 1->0, blob 940997a39a4e->9450deebd49e; 3 tests red (direction: red); restore proven. Spec tests import src: no dist leg.",
"gates": "Local, final head 23bebd6: dispatch-gates --commands --repo objectstack-ai/objectstack derived 111; each run one by one, exit code captured before any pipe; --ran: '111 derived famil(ies) accounted for - 110 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)' - the one is check-plugin-teardown-shape --self-test (shallow clone). Round 1 on cb5a4f9 read 107 run / 4 NOT-MEASURED (check:skill-examples and check:dual-build-cjs-loads exit 3, check:dts-closure exit 1 recorded as a claimed NOT-MEASURED because my closure build ran with OS_SKIP_DTS=1); all three exit 0 in round 2. CI on 23bebd6 at report time: TypeScript Type Check success, Lint & Repo Gates success, Build Core success, Governed Surface Queue Guard success, Check Changeset success, Test Core 1/6 2/6 4/6 5/6 6/6 success, Test Core 3/6 in_progress, Type Check workspace/consumer/source/debt ledger success, Lint ESLint/docs/repo/tooling/PM dispatch-gates/scoped x3 success, Spec property liveness success, Part-of PR must not also close its card success, Build Docs success, Check PR Size success; Console Pin Gate, Temporal Conformance, Dogfood shards, Packed-tarball smoke skipped (PR fast set).",
"mcp_calls": "4 - issue_read get, issue_read get_comments (#23543), pull_request_read get, pull_request_read get_check_runs (#23742); no write tool",
"api_writes": "2 relay strokes - pr_create (POST /repos/objectstack-ai/objectstack/pulls draft + POST issues/23742/assignees os-bill, run 38823295311, body read back byte-identical); comment (POST issues/23543/comments, this report). git push is not a REST write.",
"line_budget": "+621 / -129 over 16 files vs origin/main (PR #23742: additions 621, deletions 129)",
"files_changed": [
".changeset/23543-f3-retire-wait-connector-config-lift.md",
"content/docs/automation/flows.mdx",
"packages/lint/src/lint-flow-patterns.test.ts",
"packages/lint/src/validate-expressions.flow-cel-root.test.ts",
"packages/services/service-automation/src/builtin/connector-nodes.test.ts",
"packages/services/service-automation/src/builtin/connector-nodes.ts",
"packages/services/service-automation/src/builtin/wait-node.test.ts",
"packages/services/service-automation/src/builtin/wait-node.ts",
"packages/services/service-automation/src/run-summary.test.ts",
"packages/spec/src/automation/connector-action-config-required.test.ts",
"packages/spec/src/automation/flow-sibling-block-loose-keys.ts",
"packages/spec/src/automation/flow.zod.ts",
"packages/spec/src/conversions/conversions.test.ts",
"packages/spec/src/conversions/registry.ts",
"packages/spec/src/migrations/entries/semantic/18.flow-sibling-block-loose-config-keys-refused.ts",
"packages/spec/src/shared/retired-key-removal-version.pin.test.ts"
],
"deviations": [
"File surface widened by the pin sweep, outside the claim's list: lint lint-flow-patterns.test.ts and validate-expressions.flow-cel-root.test.ts, service-automation run-summary.test.ts (fixtures re-spelled from the loose config location to the declared blocks; none tested the lift) and content/docs/automation/flows.mdx (two table rows now name the refusal). Named in the PR body.",
"Assumption 3: the executors had no loose-key reads left to delete (wait-node.ts reads waitEventConfig only, connector-nodes.ts connectorConfig only); only their comments changed. The changeset still carries a service-automation patch row for those comment-only edits.",
"Assumption 4: none of the cli migrate-meta load-path suites nor spec stack-authored-input has F3 as its subject - since PR #23600 they all drive retry-policy-converged - so they are unchanged; F2 inherits the no-live-subject question.",
"Design addition beyond the ruling's letter: the lifts also delete a loose key holding null (refused like any present key) and a later candidate shadowed by the first present one, not only a block-shadowed key; config.timeoutMs gets its own prescription (waitEventConfig.timerDuration as a string) because waitEventConfig.timeoutMs is itself a tombstone.",
"Round-1 gate record: check:dts-closure exited 1 under my OS_SKIP_DTS closure build and was recorded as a claimed NOT-MEASURED, not as exit 1; round 2 on the final head measured it exit 0.",
"Attribution: commit trailers use AGENTS.md's model-free pair (Co-authored-by: Claude, Claude-Session) and the PR body the session-URL footer, instead of the harness reminder's model-named trailer and emoji footer (AGENTS.md takes precedence).",
"Main was merged before the PR opened (dispatch: #23418 had landed) via scripts/pm/os-regen-merge.sh; no regeneration was owed."
],
"open_questions": [
{
"question": "A connector_action node with only config.connectorId / config.actionId now gets three issues (the missing connectorConfig block plus one per loose key). Keep the stack, or suppress the loose-key issues when the block is absent?",
"options": [
"A keep all three: each is true and located, and the block message already says move the keys",
"B suppress the per-key issues when connectorConfig is absent (one issue, less precise)"
],
"recommendation": "A, because axis 3 (prevent AI errors) favours located refusals at each key the author wrote, and B adds a special case to a rule that is otherwise one table"
}
],
"out_of_scope_findings": []
}
Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
Release: session
session_01LFqdRsuM5nkqoJdHd5ztVk(domain:specseat 2) · F3 landed asPart of, and claim6203354520covered F3 only · back topm:queuefordomain:spec, next family F2 · 2026-10-16T01:02ZLanding record, PR #23742 (F3 of ruling
6182001882):origin/maind176b0aab3"feat(spec)!: refuse the wait / connector_action loose config keys naming their sibling block; their D2 lifts leave the load path (F3 of #23543)", merged through the queue at 2026-10-16T01:00Z (git branch -r --contains→origin/main)gh-readonly-queue/main/*ref names #23742bc058a33e668equalsgit merge-tree --write-treeof its parent941c78ae2ewith23bebd666b. That head carries contract review PASS6205149412and ACCEPT6205192514.Part of: nothing closed itLeft on this card, in the ruling's order (item 7): F2 → F4 → F5-http.
Part of #23543, with a contract-tier review (Clause-②: yes (narrowing)), a BREAKING changeset and the verdict-list pin inconversions.test.ts.Fixes #23543.conversions.test.ts's kept-without-ruling list now holds exactly four entries:flow-node-http-callout-rename,page-component-visibility-to-visibleWhen,retry-policy-convergedandview-visibleOn-to-visibleWhen.Carry-forward for the next dispatches (from the contract review's ③):
os migrate metaload-path suites (packages/cli/test/migrate-meta-load-conversions.test.ts,migrate-meta-composed-load-conversions.test.ts,migrate-meta-composed.test.ts) andpackages/spec/src/stack-authored-input.test.tsall driveretry-policy-converged, F2's entry. F2 retires their last live subject, so its PR gives them a synthetic probe or retires them, with a reason for each. F2's dispatch order names them.6189368882still stand.protocol.ts) canonicalizes every retired flow entry, as a recorded verdict ([P2] A Studio edit heals every legacy row except a flow's —saveMetaItemre-persists the pre-protocol dialect an author was served #4542, build: ascriptnode's undeclared config key passesobjectstack validate,compileandregisterFlow, then fails every run — the key half of #21898's class (subflowby reading) #21982); F3's PR left it untouched.protocol.tsdecision for every retired flow entry, the seven spec(conversions): 15 ADR-0087 D2 conversions are still on the load path at 18 though their window closed (10toMajor: 17, 5 older): retire each or record why it stays, one verdict per entry (family closing card after #23421) #23487 retirements included, so it is not a family rider.retryDelayMs→backoffMsand F5-http's node-type rename meet the same door, so one answer serves all three. The seat presents it as its own decision. A family PR does not wait on it, because none of them touchesprotocol.tseither way.packages/specplus the readers). Until [Decision] Three spec contract dev agents in one container queued ~70 min each on the shared verify lock: leave packagetest:repoto CI, and send build-heavy cards to cloud sessions? #23607 is ruled, this seat sends them as cloud sessions.objectstack-fleet commentedon Oct 16, 2026
Claim: PM loop round 3 · 2026-10-16T02:28Z · family F2 of ruling
6182001882(item 2, the loud form). Ajob.retryPolicyblock whosemaxRetriesis omitted or 0 is refused, in the shapeflow.errorHandlinguses forstrategy: 'retry'. A job with no block stays a single attempt, andbackoffMultiplierkeeps its declared default.retry-policy-convergedthen setsretiredFromLoadPath. The PR isPart of #23543; F4 and F5-http follow one at a time, in the ruling's order.Session:
session_01HjR8RUwTPQ3TgbfKihmxp9(the dispatching seat; the dev runs as a cloud session the seat post names)Account:
os-elon-musk(the seat's linked user asGET /useranswers it; this card's assignee)Branch:
claude/issue-23543-f2-retry-policy-refusedWorktree:
objectstack-issue-23543-f2(in the cloud session's container)Domain:
domain:specSeat:
domain:spec#1(seat post #6017)File surface (at
origin/main0b1179f33or later; stop on a breach and explain it in the report):packages/spec/src/system/job.zod.ts(andpackages/spec/src/shared/retry-policy.zod.tsonly if the refusal must live there): the refusal of aretryPolicyblock withmaxRetriesomitted or 0, with the ruling's prescription.packages/spec/src/conversions/registry.ts: theretry-policy-convergedentry only (retiredFromLoadPath,retiredAfter).conversions.test.ts: the verdict-list pin and that entry'sdescribeblocks. The D3 entry underpackages/spec/src/migrations/entries/, and the removal-version JOIN row if one is owed.6205630082) names, which all driveretry-policy-convergedas their last live subject: the CLI'smigrate-meta-load-conversions,migrate-meta-composed-load-conversionsandmigrate-meta-composed(its load-path case), and the spec'sstack-authored-input. Each gets a synthetic probe or is retired, with a reason per suite in the report.packages/services/service-job/src/run-with-policy.tsnames the entry), and any example that authors ajob.retryPolicywithoutmaxRetries..changeset/23543-f2-*.md.MAJOR_18_CONVERSIONS, and not the metadata save door (protocol.ts), which the seat presents as its own decision. ⛔ Not objectui.Container & model:
M,mode:cloud(the maintainer's word in this session's chat: 「batch 调到 5, 可以开云卡」; a build-heavy closure, as seat 2 noted for every family),model: default tier(dispatch-gates --tier --repo objectstack-ai/objectstack: no path-derived mandate). The review owed before the queue is atCONTRACT_REVIEW_TIER: facepackages/spec/src/**,Clause-②: yes (narrowing)per ruling item 7.Clause-②: yes
Responsibility: n/a — not a defect card
Thread-read: 6205630082
Ruling-ref: 6182001882
Serial constraints cleared:
d176b0aab3, record6205630082). The ruling's order puts F2 next.Claim:of all 21 openpm:dispatchedcards was read in this act, and none names this surface.page:headershowStaris declared but no longer reaches a user in the reference console: since objectui#12427 the star draws only where a host wires a favorites toggle, and no host does #23650) adds an entry toconversions/registry.tsand a row toretired-key-removal-version.pin.test.ts; PR feat(spec)!: delete checkPageComponentViewData; the component-props rule is the one judge of a page component's retired api data binding #23784 (page: deletecheckPageComponentViewDataonce PR #23418 moves the component-props rule onto the save door — ruling6161173492item 6, the follow-up for #23357 #23447, seat 2) editsconversions/refused-shapes.ts. Different entries; the later lander mergesmainthroughscripts/pm/os-regen-merge.sh.wip(spec): refuse a job retryPolicy with no attempts; retire retry-po…
objectstack-fleet commentedon Oct 16, 2026
os-dev-report
{
"issue": 23543,
"status": "done",
"branch": "claude/issue-23543-f2-retry-policy-refused",
"pr": "#23810",
"session": "session_01MT2aGLQQhH8ZsPrgqHMrwz (this cloud dev session, from the transcript Claude-Session line; dispatched by seat session_01HjR8RUwTPQ3TgbfKihmxp9 under claim 6206691402, whose Branch line names this branch)",
"premise_still_valid": true,
"summary": "F2 of ruling 6182001882 (item 2, the loud form). JobSchema gains one root-level refine: a retryPolicy whose parsed maxRetries is below 1 (omitted, which the default fills with 0, or written 0) is refused as a custom issue at retryPolicy.maxRetries. The message opens with the ruling's prescription ('A retry block with no attempts is no retry block: state
maxRetries≥ 1, for example 3, or delete the block.'), names @objectstack/spec 18 and ends with the os migrate meta --from 16 sentence. defineStack answers STACK_SCHEMA_INVALID / 422 at jobs.N.retryPolicy.maxRetries. The shared RetryPolicySchema (also try_catch.retry) is unchanged, and a control pins it. retry-policy-converged sets retiredFromLoadPath / retiredAfter 17.7.0. The D3 entry 18.job-retry-policy-without-attempts-refused carries conversionIds ['retry-policy-converged']. The verdict-list pin keeps 3, and the removal-version JOIN gains one row. The four load-path suites were decided per suite (open_questions 1). One BREAKING changeset: spec minor, service-job patch (comment only).","tests": "Head 0bc4f42 (origin/main 46ca793 merged via os-regen-merge.sh: no conflict, the merge left no changes, lockfile unmoved). @objectstack/spec: full test 683 files / 20888 passed (1 todo), exit 0. build + check:generated 'All 14 generated artifacts are up to date'. typecheck exit 0 on 9e09e0d, pre-merge; the merge touched none of this PR's files. @objectstack/cli: full unit tier 297 files. 295 passed in one run; the 2 build-dependent published-subpath pins failed only with 'packages/cli is not built' and passed 2 / 29 after pnpm --filter @objectstack/cli build. 4348 passed, 29 skipped. The 4 edited cli files pass, among them the integration-tier retry-policy-key-validate-door.test.ts at 2 / 2 (run because the diff touches it). cli typecheck exit 0 after the closure was built with declarations; the first read was 398 TS7016 from an OS_SKIP_DTS closure, an environment state. @objectstack/service-job: test 11 files / 117 passed; typecheck exit 0. Ablation via scripts/ablation-replace.mjs on 9e09e0d: 'job.retryPolicy.maxRetries >= 1' was replaced by '>= 0'. Anchor 1 -> 0, blob 28e227b31f4e -> c6c4d7684caa. 7 tests went red: job.test.ts 3, conversions.test.ts 3 and retired-key-removal-version.pin.test.ts 1, the JOIN row. Direction: red. The restore was proven blob == HEAD and git diff HEAD empty. Spec tests import src, so there is no dist leg.",
"gates": "Local, head 0bc4f42: dispatch-gates --commands --repo objectstack-ai/objectstack derived 114. Each ran one by one, exit code captured before any pipe, one log per command. --ran: '114 derived famil(ies) accounted for - 111 run, 3 NOT-MEASURED (3 DERIVED from a recorded exit 3)'. The three are check-plugin-teardown-shape --self-test (shallow clone, asks for --unshallow), spec check:skill-examples (client-react dist has no .d.ts) and check:dual-build-cjs-loads (some package has no dist). These are the same three prerequisite refusals F3 recorded, and CI runs them. Local-only families, owed because the diff touches the packages/spec build, were run on 0bc4f42 and stay local: pnpm check:dev-prereqs exit 0 ('@objectstack/spec … built from the sources on disk'); pnpm check:skill-frame-freshness exit 0 ('1/1 framework files are byte-identical to the ref'). CI on PR #23810 at report time (head 0bc4f42): 'No other open PR may claim the same issue' success; 'Part-of PR must not also close its card' success; Build Core, Test Core (1-6) and Dogfood Regression Gate queued; Check Changeset, Governed Surface Queue Guard, the Lint and Type Check jobs and 'The card this PR closes must claim this branch' in_progress; Console Pin Gate, Temporal Conformance, Dogfood Verify CLI and Packed-tarball smoke skipped. CI convergence is the PM's per the contract.",
"mcp_calls": "0",
"api_writes": "2 relay dispatches, each a POST /repos/objectstack-ai/objectstack/dispatches executed by fleet-write.yml as objectstack-fleet[bot]: (1) pr_create, POST /repos/objectstack-ai/objectstack/pulls plus POST /repos//issues/23810/assignees; read-back said 12321 bytes sent, 12321 stored, identical, with assignees os-elon-musk; (2) this comment, POST /repos//issues/23543/comments. git push is not counted. Reads went through gh api GET only.",
"line_budget": "719 (PR additions 517 + deletions 202, 18 files, as the pull reports)",
"files_changed": [
".changeset/23543-f2-job-retry-policy-without-attempts-refused.md",
"content/docs/automation/jobs.mdx",
"content/docs/references/system/job.mdx (regenerated by check:generated --fix, check:docs)",
"packages/cli/test/migrate-meta-composed-load-conversions.test.ts",
"packages/cli/test/migrate-meta-composed.test.ts",
"packages/cli/test/migrate-meta-load-conversions.test.ts",
"packages/cli/test/retry-policy-key-validate-door.test.ts",
"packages/services/service-job/src/run-with-policy.ts",
"packages/spec/liveness/flow.json",
"packages/spec/src/automation/flow-builtin-node-config-keys.test.ts",
"packages/spec/src/conversions/conversions.test.ts",
"packages/spec/src/conversions/registry.ts",
"packages/spec/src/migrations/entries/semantic/18.job-retry-policy-without-attempts-refused.ts",
"packages/spec/src/shared/retired-key-removal-version.pin.test.ts",
"packages/spec/src/shared/retry-policy.zod.ts",
"packages/spec/src/stack-authored-input.test.ts",
"packages/spec/src/system/job.test.ts",
"packages/spec/src/system/job.zod.ts"
],
"deviations": [
"File surface widened beyond the claim's list, each measured and named in the PR body: (a) packages/cli/test/retry-policy-key-validate-door.test.ts, whose 'declared' fixture authored a try_catch retryDelayMs that only the load conversion made valid; (b) packages/spec/src/automation/flow-builtin-node-config-keys.test.ts, whose CONTROL pinned that defineStack converts retryDelayMs; (c) content/docs/automation/jobs.mdx, shared/retry-policy.zod.ts comments, liveness/flow.json one note, and the job-timeout-to-timeout-ms docblock in registry.ts, prose the retirement made false; (d) job.test.ts, whose 'one-time scheduled job' fixture wrote maxRetries: 0 and now has no block.",
"No label-write: the dispatch's write budget lists git push, pr_create and the report comment only.",
"Not subscribed to PR activity: the os-dev contract overrides the platform's PR-subscription posture.",
"Interim CLI typecheck read red (398 TS7016) on a closure built with OS_SKIP_DTS=1. It was re-run after building the closure with declarations: exit 0."
],
"open_questions": [
{
"question": "The four load-path suites F3's record named lost their last live subject. After F2, os migrate meta --from 16, the floor, replays steps 17 and 18, and every entry of both is retired. Only toMajor 11 and 15 entries stay live (F4, F5-http), and the chain never replays those. How was each suite decided?",
"options": [
"A (taken) per suite: stack-authored-input.test.ts gets a synthetic probe, a vi.mock of ./conversions/registry appending a test-owned live retryDelayMs rename, so it no longer depends on any entry's lifecycle. migrate-meta-load-conversions and migrate-meta-composed-load-conversions: the accepted-arm 'lists a conversion the load already applied' case is retired (no subject; its write / re-run / clean-reload half is pinned at the same layer by the two-refused-spellings case). The 'authored-source load reads the argument' and 'defined twice' cases are re-pointed to a discriminator needing no conversion: strict defineStack fills declared defaults (backoffMultiplier 1, maxRetryDelayMs 30000, jitter false), and the argument does not. migrate-meta-composed.test.ts: the row's assertions still hold, so it is only renamed.",
"B: re-point every case to page-component-visibility-to-visibleWhen or flow-node-http-callout-rename. Not possible for the chain cases (toMajor 15 / 11 are below the floor), and F4 / F5-http would have to redo it."
],
"recommendation": "A, because it is the only option with a real subject on this tree, and the probe and the default-fill discriminator survive F4 and F5-http unchanged. Four axes: it serves the real seam (the authored-source shim); it is long-term (no entry lifecycle coupling); it adds no production test hook (a registry injection point for the CLI would be a new surface); and it adds no gate."
},
{
"question": "Where should the refusal live? A JobSchema root-level refine, as taken here, or a field-level refine on job.retryPolicy?",
"options": [
"A (taken): root-level refine at path retryPolicy.maxRetries. It adds no dropped-refinement ledger site, because system/Job's root is already recorded. The issue appears only once the job's other fields parse.",
"B: field-level refine. The issue is reported beside sibling errors, but it adds 'retryPolicy' sites to dropped-refinements.baseline.json for system/Job and every schema embedding a job.",
"C: a job-specific policy schema with maxRetries required and min(1). tsc and the JSON Schema would state the rule, but it creates a second declaration of the converged policy, which shared/retry-policy.zod.ts exists to prevent."
],
"recommendation": "A, because it follows the ruling's named shape (flow.errorHandling's custom issue at maxRetries) with zero ledger growth, and the parse-time prescription is loud at every authoring door."
},
{
"question": "Metadata save door (protocol.ts), hypothesis 8. Does F2 interact with the door?",
"options": [
"Measured: for a non-flow type the door judges the body as stored, with no conversion replay, so a job body meets this refusal there. job has not been runtime-creatable since 17, so no door claim is made for it. The retryDelayMs flow half meets the flow fallback exactly as before, because the fallback already replayed this entry while it was live. Nothing in protocol.ts was touched; the seat's separate decision covers this entry together with the other retired flow entries."
],
"recommendation": "No action in this PR."
},
{
"question": "Stored job rows written through the save door at 17 without maxRetries (meaning 'no retry' at 17) are rehydrated by convertStoredItem replaying retry-policy-converged, so they read as 3 retries. Should anything change?",
"options": [
"A: nothing. This was already the 17 behaviour, because the entry was live and the stored seam replayed it; F2 does not change it.",
"B: add retry-policy-converged to an excludeConversionIds list at the stored seam as a default flip."
],
"recommendation": "A for this family: the ruling says 'the 17 materialization lives on in the D3 chain', and job rows are not runtime-creatable since 17, so such rows are rare. Noted for the seat, not filed."
}
],
"out_of_scope_findings": [
"class: none filed · reach: n/a · evidence: the root-level refine reports the empty-retry-block issue only after a job's other fields parse (zod skips object refines on an aborted parse). This is a diagnostic-order note, not a defect: the refusal still fires once the rest is fixed. carrier: noted, not filed (Acceptance notes of PR #23810)."
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
ACCEPT → PR #23810 comment 6207800896 ·
domain:specseat 1 · sessionsession_01HjR8RUwTPQ3TgbfKihmxp9· 2026-10-16T04:03Z · family F2. The contract review (CONTRACT_REVIEW_TIER) and an all-green head stand before the queue. The PR isPart of, so this card stays open for F4 and F5-http.File surface amended for claim
6206691402, each item measured in the report:packages/cli/test/retry-policy-key-validate-door.test.ts,packages/spec/src/automation/flow-builtin-node-config-keys.test.ts,packages/spec/src/system/job.test.ts,content/docs/automation/jobs.mdx, theshared/retry-policy.zod.tscomments, oneliveness/flow.jsonnote, and the regeneratedcontent/docs/references/system/job.mdx.objectstack-fleet commentedon Oct 16, 2026
os-dev-report
{
"issue": 23543,
"status": "done",
"branch": "claude/issue-23543-f2-retry-policy-refused",
"pr": "#23810",
"session": "session_01MT2aGLQQhH8ZsPrgqHMrwz",
"round": "prose patch round asked by seat 1 (session_01HjR8RUwTPQ3TgbfKihmxp9) after ACCEPT 6207800896 and contract review PASS 6207998123; same claim 6206691402",
"head": "dce22664 (one commit on top of 0bc4f42; the PR body is untouched)",
"edit": {
"file": "packages/spec/src/migrations/entries/semantic/18.job-retry-policy-without-attempts-refused.ts, field reason (surface unchanged, still free of backticks and pipes)",
"before": "
objectstack migrate metalists every such block in your metadata sources and writes both pre-17 numbers into it, which keeps the retries the job ran with.","after": "
objectstack migrate metalists every block in your metadata sources that omits the count and writes both pre-17 numbers into it, which keeps the retries the job ran with; a block that writesmaxRetries: 0it does not list, and you delete that block by hand (or state a count)."},
"tests": "pnpm --filter @objectstack/spec exec vitest run src/migrations src/conversions: 18 files / 755 passed, exit 0 (head dce2266).",
"gates": "pnpm --filter @objectstack/spec build exit 0; check:generated exit 0 ('All 14 generated artifacts are up to date'); check:docs exit 0 ('227 generated files in sync'). No generated file changed: the upgrade guide this entry feeds is built in memory and not committed. Each exit code was captured before any pipe.",
"mcp_calls": "0",
"api_writes": "2 relay dispatches (POST /repos/objectstack-ai/objectstack/dispatches): this comment, POST /repos//issues/23543/comments, then one PATCH /repos/objectstack-ai/objectstack/issues/comments/6208060742 to restore the before/after sentences, whose backtick spans a shell heredoc had executed and dropped in the first write. git push is not counted.",
"files_changed": [
"packages/spec/src/migrations/entries/semantic/18.job-retry-policy-without-attempts-refused.ts"
],
"deviations": [
"The first write of this comment lost the backtick spans of its before/after sentences (an unquoted heredoc executed them); the same comment was edited once to restore them."
],
"open_questions": []
}
Generated by Claude Code