Repository navigation
feat(spec): a select option declares its lifecycle kind (open / won / lost); every stage-field option must declare it - #23846
Conversation
Claude-Session: https://claude.ai/code/session_017qmjVjz61scwQFLfspdBX6 Co-authored-by: Claude <noreply@anthropic.com>
… pins Claude-Session: https://claude.ai/code/session_017qmjVjz61scwQFLfspdBX6 Co-authored-by: Claude <noreply@anthropic.com>
… artifacts Claude-Session: https://claude.ai/code/session_017qmjVjz61scwQFLfspdBX6 Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qmjVjz61scwQFLfspdBX6 Co-authored-by: Claude <noreply@anthropic.com>
…w, translated Claude-Session: https://claude.ai/code/session_017qmjVjz61scwQFLfspdBX6 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 3 package(s): 22 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 3 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 142 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin b1ddbcb2ecb386c8e36ad8e6da4f79d6b3223331 && git checkout b1ddbcb2ecb386c8e36ad8e6da4f79d6b3223331
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin f7e836da1ed344aaa6936b9de8b173d0f326b9ec ef94551925f2cdf22a074003509ee81e42efd6ab && git checkout -B drift-repro f7e836da1ed344aaa6936b9de8b173d0f326b9ec && git merge --no-ff ef94551925f2cdf22a074003509ee81e42efd6ab
node scripts/docs-audit/affected-docs.mjs --json f7e836da1ed344aaa6936b9de8b173d0f326b9ec
|
Contract reviewServed-tier: Inputs: card #23809 (body, the dispatch claim 6209728672 and the os-dev-report 6211151563), PR #23846 (body, its 29-file list, the net diff against ① Derived judgmentsEvery accept-set and public-surface change the diff implies, each judged:
Check-runs on the head, read last at 2026-10-16T08:41Z: 33 success, 5 skipped (the full-battery jobs a PR run does not carry), 2 still in progress — ② Semver level
③ Boundary flagsThe dev's
Implemented-by: VERDICT: FAIL Two edits re-earn a PASS on a new head: ① item 7 (narrow the Generated by Claude Code |
…angeset names platform-objects and metadata-core; FLS contract fixture declares kind Claude-Session: https://claude.ai/code/session_017qmjVjz61scwQFLfspdBX6 Co-authored-by: Claude <noreply@anthropic.com>
…e-field kind rule; regenerate reference docs Claude-Session: https://claude.ai/code/session_017qmjVjz61scwQFLfspdBX6 Co-authored-by: Claude <noreply@anthropic.com>
…r's kind row Claude-Session: https://claude.ai/code/session_017qmjVjz61scwQFLfspdBX6 Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: DELTA record on contract review 6211666660 (FAIL at ① Derived judgmentsItems 1 to 6 and 8 to 15 of record 6211666660 stand unchanged: the delta does not touch
Check-runs on the head, read last at 2026-10-16T09:59Z: 37 success, 7 skipped (the full-battery jobs a PR run does not carry, plus the ② Semver level
③ Boundary flagsThe round-2 os-dev-report 6212681414 carries
Implemented-by: VERDICT: FAIL All three grounds of record 6211666660 are closed on this head, and the delta breaks nothing the prior record judged right. One edit re-earns a PASS on a new head: the metadata-core sentence in |
…sserts, not a whole-object parse Claude-Session: https://claude.ai/code/session_017qmjVjz61scwQFLfspdBX6 Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: DELTA record 2 on contract review 6213027069 (FAIL at ① Derived judgmentsEvery item of records 6211666660 (1 to 6, 8 to 15) and 6213027069 (7, 16, 17) stands unchanged: the delta touches none of the files they judged. Judged on this head:
Check-runs on the head, read last at 2026-10-16T10:34Z: 34 success, 5 skipped ( ② Semver level
③ Boundary flagsThe round-3 os-dev-report 6213094599 carries
Implemented-by: VERDICT: PASS The one open ground of record 6213027069 is closed on this head and the delta is the one line that closes it. The landing waits on |
席位核对 — ACCEPT · objectstack
|
Fixes #23809
Clause-②: yes (narrowing)
What
A select option declares its lifecycle
kind, one closed vocabulary for every object:open(a step on the way),won(the successful end),lost(the unsuccessful end). Every option of the field an object'sstageFieldnames must declare it.packages/spec/src/data/field.zod.ts:SelectOptionSchemagainskind: z.enum(['open', 'won', 'lost']).optional(), documented with the definition (a task's Done and a case's Resolved arewon; Cancelled / Unqualified / Closed Lost arelost). Absent stays absent at parse (reads asopen; no default is written in).FieldSchemarefuseskindatoptions.N.kindon any field that is not a single-valueselect/radio. Exported typeSelectOptionKind. ThedueLike/settledWhendocblock now says options carry akindbut overdue is still judged bysettledWhenalone (objectui#11815 ruling D unchanged; nothing is wired).packages/spec/src/data/object.zod.ts:refuseStageFieldWithoutKinds, anObjectSchemaparse-time check besiderefuseDishonestReferenceDeletedWhen. One located issue per option withoutkindatfields.FIELD.options.N.kind, message naming the field and the option. Also refused atstageField: a picklist-backed stage field, and a stage field that has options but holds no single option code per record (multiselect,multiple: true). Left as they were:stageField: false, a pointer to no declared field (lintsemantic-rolesowns it), a field with no options. ThestageFielddescribe states exactly this scope.packages/spec/src/ui/view.zod.ts:FormSelectOptionSchemais derived from the option shape, so it now leaves outkindas well asdefault, with a guidance line pointing at the object field. Without that,kindwould have been accepted on form-view options with nothing reading it.kind(a select of the three values); the field form's repeater lists it. The platform-objects metadata-form bundles carry the new row, translated into zh-CN / ja-JP / es-ES.field/optionsis now drilled. Six children arelivewith their read points, andkindisplanned(noauthorWarn, because the key is mandatory understageField). Thefield/optionsrow leavesundrilled-containers.baseline.json.picklist/options.kindisplanned.stage-field-option-kind-required. It has no conversion because the ruling withholds a default.examples/app-showcase/.../semantic-zoo.object.ts: the onlystageField: 'status'in the corpus now declaresopen/open/won.packages/metadata-core/src/object-schema-fls-contract.ts: the published contract fixtureFLS_CONTRACT_OBJECTnamessalary_gradeas itsstageField, so its two options declarekind: 'open'. It is pinned inobject-schema-fls-references.test.ts, which asserts no stage-field issue and has a positive control.kindrows:field-panel-echo-decisions10 to 11,object-collapsed-sections-echo-decisions116 to 118,object-lifecycle-panel-echo-decisions672 to 674..changeset/23809-select-option-kind.md:@objectstack/specminor,@objectstack/platform-objectspatch (metadata-form bundles),@objectstack/metadata-corepatch (contract fixture),Clause-②: yes (narrowing), with the migration.Vocabulary ruling: premise reading
The seat ruled
open | won | loston the premise thatrecord:pathstages[].terminal: z.enum(['won', 'lost'])is a live platform vocabulary. Measured at objectuimain@63d00da:packages/plugin-detail/src/renderers/record-path.tsxdeclaresPathStage.terminal?: 'won' | 'lost', and itsclassifyreturnss.terminalfirst (line 221), ahead of the token heuristic. The premise holds.component.zod.tsis untouched.Acceptance pins (
packages/spec/src/data/select-option-kind.test.ts)an option with a terminal kind parses, and keeps it.an unknown kind is refused at the option, located(done,cancelled,closed,WON, empty).a plain select without kinds parses, and an absent kind stays absent (reads as open).kindis refused, naming the field and option:one option lacking kind is refused at publish, naming the field and the option(pathfields.status.options.3.kind), pluseach option lacking kind is its own located issue,create() refuses the same object.kindon a non-select field is refused:is refused on a \%s` fieldover multiselect, checkboxes, tags,multiple: true` select/radio, lookup, text.Updated pins:
field-rows-option-description.test.ts(the repeater's input list gainskind),form-select-option.test.ts(Omit minusdefaultandkind; thekindrefusal names the object field).Verification (round 1 at head 4274c65; patch round at head 4475469)
@objectstack/specfull vitest: 21996 passed, 2 failed. Both failures were the two pins above, updated afterwards. The re-run of those files, the new file andsrc/migrations: 279/279.pnpm typecheckexit 0.check:generated: all 14 artifacts up to date.scripts/ablation-replace.mjs, mutation proved on disk, restored blob == HEAD with an emptygit diff HEAD): deleting therefuseStageFieldWithoutKinds(...)call turns 6 stage pins red. Turning theFieldSchemaoff-type guard intoif (false)turns 7 off-type pins red. Both are green again after restore.meta-object-fls85/85, metadata-coreobject-schema-fls-references53/53. Showcasevalidate,buildandtypecheckall exit 0.6211666660, three items closed): speccheck:generatedall 14 up to date after--fix;select-option-kind+object.test232/232. platform-objects 1092/1092 (3 count pins updated, as above); metadata-core 735/735 and typecheck exit 0; rest FLS 125/125; runtime meta-object-fls 85/85.dispatch-gates --commands: the derived list is byte-identical to round 1. 116 run, all exit 0 after the spec-dist prerequisite re-runs, with the same 2 NOT MEASURED left to CI.dispatch-gates --commands(round 1): 116 families derived, 116 run. All exit 0 except:check:i18nexit 1, a real finding (bundles drifted), fixed by regenerating and translating, now 0.check:skill-examples,check:i18n,check:i18n-coveragere-run green after a dist rebuild. Two families are NOT MEASURED locally, exit 3 (prerequisite):check-plugin-teardown-shape --self-test(the shallow clone cannot reach its pinned fixture) andcheck:dual-build-cjs-loads(needs the whole workspace built). Both are left to CI.Acceptance notes
stageFieldrather than judged, becauseObjectSchemacannot see a picklist's options. Zero instances in the corpus. The alternative, judging the resolved list whereos validateresolves picklist references, would cover only the CLI doors and not the save door. This is recorded in the report's open questions.multiselect/checkboxestoo, and the form DSL has no parent-scoped row predicate. Sokindis offered there and the publish door refuses it. The help text says "single-value select / radio only".🤖 Generated with Claude Code
Generated by Claude Code