Repository navigation
Spec: a select option can declare its lifecycle kind (open / won / lost) so stage paths draw terminal states apart #23809
Description
Source
Read on 2026-10-16 in the console (objectui main 8415d9f11) with HotCRM 63db83e2. The lead record's stage path draws "New ✓ → Contacted → Qualified → Converted → Unqualified": the lead's status options (src/sales/objects/lead.object.ts) are a flat list, and Unqualified, which ends a lead's life, is painted as a fifth step after Converted. The opportunity record's path draws "Prospecting … Negotiation · Closed Won · ✗ Closed Lost" with the two closed states set apart, because the console recognises the values closed_won / closed_lost by name. packages/spec/src/data/field.zod.ts (around line 1935) says it itself: no select option carries a terminal marker, so the renderer guesses from value spelling and every other object gets a path that treats its lost state as a step.
Target
- The select option shape gains
kind, a small closed enum of lifecycle categories: "a step on the way", "the successful end", "the unsuccessful end". The spelling is the spec seat's to settle, choosing one vocabulary for the whole platform between the sales pairopen | won | lostand the neutralopen | done | cancelled(Jira's status-category shape), so that a case's Resolved / Cancelled and a task's Completed fit the same three words as a deal's Closed Won / Closed Lost. Declared once on the option, read by every renderer of astageField: the record path, the kanban column order, the "closed" filters, the dashboard "won / lost" measures.- On a plain select the key is optional and an absent kind reads as the open category.
- When an object's
stageFieldnames the field, every option of that field must declarekindexplicitly: publish refuses a stage field with any option whose kind is missing, naming the field and the option. No default there, because a default would hand an AI that forgot the key the same wrong path drawn today, only declared. Maintainer ruling 2026-10-16 (agreeing in chat to these two tightenings, the mandatory kind understageFieldand the vocabulary choice), verbatim: 「同意」.
- The console's name-based guess (
closed_won/closed_lost) stays as the fallback for packages that do not declare the kind, and is retired in the next major. - HotCRM declares
kind: 'lost'on the lead'sunqualifiedandkind: 'won'onconverted, and the same on the opportunity's two closed stages, in the same change set (a hotcrm follow-up card references this one). - The field inspector in Studio shows the kind as a third column of the picklist options editor (the objectui follow-up).
File surface
packages/spec/src/data/field.zod.ts (the option schema and its docblock), the spec's generated docs and metadata forms, packages/spec contract tests; the objectui path renderer and Studio inspector follow in their own cards once the field is published.
Acceptance
- Spec tests: an option with a terminal kind parses; an unknown kind is refused; a plain select without kinds parses and reads as open; an object whose
stageFieldnames a select with one option lackingkindis refused at publish with a message that names the field and the option;kindon a non-select field is refused. - The generated metadata forms list the field;
pnpm --filter @objectstack/spec testand the spec gates green. - The four-axis note on this card (business pull: two HotCRM objects already encode the semantics by naming; AI-safety: a declared kind beats a spelling convention) is read by the spec seat before it lands.
Constraints
- Contract-first: the spec declares before any consumer reads; no console-only workaround.
Evidence
Screenshots on the temporary branch claude/console-home-login-design-20261016 of objectstack-ai/objectui (commit b72c97985, never merged): hotcrm-lead-record-header-and-stage.jpg · hotcrm-opportunity-record.jpg.
Filed by the epic PM session session_cdb7a717-def7-4e71-9988-f089fbaa18ff from the 2026-10-16 walk of HotCRM's core pages as a user: objectui main 8415d9f11, HotCRM 63db83e2, the vite console on :5180 against objectstack serve --dev on :4001, the maintainer's browser pane at 1024 × 768 (en). The maintainer asked for the findings to be filed on the repository each belongs to, verbatim: 「相关问题也在对应的项目上创建任务」.
Body refreshed 2026-10-16T04:10Z: Target 1 and Acceptance 1 tightened per the maintainer's 2026-10-16 ruling (kind mandatory under stageField, vocabulary settled by the spec seat); the rest unchanged.
Activity
objectstack-fleet commentedon Oct 16, 2026
Claim: PM loop round 12 (second shift,
batch 1) · 2026-10-16T06:41Z · the select option's lifecyclekind, with its required form understageFieldSession:
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-23809-select-option-kindWorktree:
objectstack-issue-23809Domain:
domain:specSeat:
domain:spec#2(seat post #18549)File surface (paths on
origin/main1ecb08a1ccor later; stop on a breach and explain it in the report):packages/spec/src/data/field.zod.ts:SelectOptionSchemagainskind, and the docblock that says no select option carries a terminal marker (thedueLike/settledWhenparagraph) is reconciled with it.packages/spec/src/data/object.zod.ts: thestageFieldrefusal, as anObjectSchemaparse-time check in the shape ofrefuseDishonestReferenceDeletedWhen.stageFieldcan name a picklist-backed field (measure first).packages/spec/src/data/object.form.tsandfield.form.ts: the options repeater offerskind.packages/spec/liveness/field.json: theoptionsrow.packages/spec/src/data/.examples/app-showcase/src/data/objects/semantic-zoo.object.ts: itsstageField: 'status'options gainkind, because the new refusal reaches it.@objectstack/specchangeset.packages/spec/src/ui/component.zod.ts(record:pathstages[].terminalstays as is). ⛔ Notskills/**. ⛔ Notcontent/docs/releases/**. ⛔ Not objectui or HotCRM (their follow-up cards).Container & model:
M,mode:cloud(a spec card that also moves an example and the generated artifacts, so a build-heavy closure),model: default tier(dispatch-gates --tier: no path-derived mandate; clause-② suspect onpackages/spec/src/**). Review: a contract review atCONTRACT_REVIEW_TIERbefore the queue, because this widens the published option shape and narrowsObjectSchema.Clause-②: yes
Responsibility: n/a — not a defect card
Thread-read: none
Ruling-ref: the card body's Target 1 (maintainer 「同意」 2026-10-16, quoted there), and the seat's vocabulary ruling in the dispatch order (below)
Vocabulary (the card leaves it to the spec seat):
open | won | lost.packages/spec/src/ui/component.zod.tsalready shipsrecord:pathstages[].terminal: z.enum(['won', 'lost']), and objectui's record path renderer reads it (packages/plugin-detail/src/renderers/__tests__/record-path.*). One fact keeps one vocabulary across the page and the data layer, with no conversion and no second spelling.wonis the successful end andlostthe unsuccessful end, for every object, as that docblock already maps the showcase'sdoneto the forward terminus. If the dev measuresterminalas unread, it stops and reports the fork; it does not switch vocabularies.Serial constraints cleared:
batch 1, the maintainer's newest word in this seat's session. Nothing else from this seat is running: PR test(spec): three src/ test-budget rows trimmed, two at a one-program floor left listed #23819 (test budget (domain:spec): 26 test file(s) over 10 s of wall in the hourly full battery (Part of #23151) #23180) has landed. feat(spec)!: delete checkPageComponentViewData; the component-props rule is the one judge of a page component's retired api data binding #23784 waits on the maintainer's word, with no session working.domain:specclaims (ci(spec-shape-gate): once objectui drops its copy, the gate's header text and a retirement entry whose card closed still describe the old state #23782, lint: a locale pack'spicklists.<name>translations are never checked — an option key that is not in the list, or a list no package declares, passesos lint --strictand is dropped silently at runtime #23566, metadata-protocol: duplicatePackage renames a token that names both a prefixed package object and one of its fields as the object, so a copied view's column or form field names a field the copy does not have (family close-out) #23561, 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, spec(liveness): flipapp.branding.children.logofromplannedtolive; objectuiAppMarkrenders it at the pin #23394, docs/docblocks: ADR-0090 D9,validate-security-posture.tsandposition.zod.tsstill say a package suggests, or an operator binds, aguestset; after ruling C on #23267 no door bindsguestuntil ADR-0138 D3 item 5's door is built #23373, refactor(plugin-security,platform-objects,spec): retire the catalog seeders, the per-organization catalog machinery and the four catalog objects; Setup creation is an environment write undersingleand refused under a wall (ADR-0131 D2/D3/D5/D13) #15204) name none of these files; lint: a locale pack'spicklists.<name>translations are never checked — an option key that is not in the list, or a list no package declares, passesos lint --strictand is dropped silently at runtime #23566 works inpackages/lint/src/validate-translation-references.tsonly.objectstack-fleet commentedon Oct 16, 2026
os-dev-report
{ "issue": 23809, "status": "done", "branch": "claude/issue-23809-select-option-kind", "pr": "https://github.com/objectstack-ai/objectstack/pull/23846", "session": "session_017qmjVjz61scwQFLfspdBX6", "premise_still_valid": true, "summary": "SelectOptionSchema gains kind: open | won | lost (optional; absent stays absent, reads as open). FieldSchema refuses kind off a single-value select/radio, and ObjectSchema refuses a stageField whose options do not each declare kind (one located issue per option, naming field and option), plus a picklist-backed or multi-value stage field. FormSelectOptionSchema omits kind with guidance; forms offer kind; field/options liveness drilled (kind planned); D3 entry stage-field-option-kind-required; showcase semantic-zoo options declare kind; platform-objects form bundles regenerated and translated. Vocabulary premise measured true: objectui main @63d00da record-path.tsx classify returns s.terminal ('won' | 'lost') first.", "tests": "head 4274c652. @objectstack/spec full vitest: 21996 passed / 2 failed (field-rows-option-description and form-select-option pins, both then updated); re-run of those 2 + select-option-kind.test.ts + src/migrations: 279/279; spec typecheck exit 0; check:generated all 14 up to date. Ablation via scripts/ablation-replace.mjs (mutation on disk anchor 1->0, restore blob == HEAD, git diff HEAD empty): delete refuseStageFieldWithoutKinds call -> 6 failed / 15 passed; FieldSchema off-type guard -> if (false) -> 7 failed / 14 passed; both green after restore (src-resolved suite, no dist alias involved). Downstream: rest FLS/save-gate 125/125, runtime meta-object-fls 85/85, metadata-core object-schema-fls-references 53/53; examples/app-showcase validate/build/typecheck exit 0.", "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack: 116 derived, 116 run, reconciled by --ran (0 NOT-MEASURED, 0 UNRUN). Exit codes recorded before any pipe: 112 exit 0; check:i18n exit 1 (real: platform-objects bundles drifted) -> fixed (--write + zh-CN/ja-JP/es-ES translations) -> 0; check:skill-examples exit 3 and check:i18n exit 3 (dist prerequisite) -> rerun 0, check:i18n-coverage 0, check:i18n-stale-fill 0. NOT MEASURED, exit 3 prerequisite, left to CI: node scripts/check-plugin-teardown-shape.mjs --self-test (shallow clone cannot reach pinned fixture), pnpm check:dual-build-cjs-loads (whole workspace unbuilt). PR check-runs: in_progress at report time (not waited on, per os-dev).", "line_budget": "PR additions 558 / deletions 27 over 29 files (generated: authorable-surface, api-surface, export-origins, json-schema.manifest, reference docs, liveness state-counts, i18n bundles). No skills/** or governed surface touched.", "deviations": [ "Worktree: the session's primary checkout already held the branch, so it was detached and the worktree objectstack-issue-23809 created on the existing branch (no new branch).", "Files beyond the claim's surface, each forced by measurement: packages/spec/src/ui/view.zod.ts + form-select-option.test.ts (FormSelectOptionSchema derives from SelectOptionSchema.shape, so kind would leak onto form-view options with no reader); packages/spec/liveness/picklist.json and scripts/liveness/undrilled-containers.baseline.json (the walk demands picklist/options.kind, and drilling field/options retires its debt row); packages/spec/src/data/field-rows-option-description.test.ts (pins the repeater list); packages/platform-objects translations (check:i18n); packages/spec/src/migrations/entries/semantic/18.stage-field-option-kind-required.ts (ADR-0087 disposition for the narrowing).", "No exported SelectOptionKindSchema: an export above CurrencyValueSchema rewrote five skills/**/references/_index.md (governed Tier H, and off-limits for this card), and one placed below broke the OS_EAGER_SCHEMAS evaluation; the enum is inlined and only the type SelectOptionKind is exported.", "One single-file vitest run (the first select-option-kind run) went outside os-verify-lock.", "Clause-② line in PR body and changeset carries the (narrowing) arm; the claim line reads 'Clause-②: yes' without an arm.", "No label-write: this dispatch's write budget names push, pr_create and this comment only." ], "files_changed": [ ".changeset/23809-select-option-kind.md", "examples/app-showcase/src/data/objects/semantic-zoo.object.ts", "packages/spec/src/data/field.zod.ts", "packages/spec/src/data/object.zod.ts", "packages/spec/src/data/object.form.ts", "packages/spec/src/data/field.form.ts", "packages/spec/src/ui/view.zod.ts", "packages/spec/src/data/select-option-kind.test.ts", "packages/spec/src/data/field-rows-option-description.test.ts", "packages/spec/src/ui/form-select-option.test.ts", "packages/spec/src/migrations/entries/semantic/18.stage-field-option-kind-required.ts", "packages/spec/liveness/field.json", "packages/spec/liveness/picklist.json", "packages/spec/liveness/state-counts/field.md", "packages/spec/liveness/state-counts/picklist.md", "packages/spec/scripts/liveness/undrilled-containers.baseline.json", "packages/spec/authorable-surface/data.json", "packages/spec/api-surface/data.json", "packages/spec/export-origins/data.json", "content/docs/references/{api/metadata,data/field,data/object,data/picklist,system/migration,ui/view}.mdx (gen:docs)", "packages/platform-objects/src/apps/translations/{en,zh-CN,ja-JP,es-ES}.metadata-forms.generated.ts" ], "mcp_calls": "4 read-only: issue_read get, issue_read get_comments, pull_request_read get; plus add_repo (read lane, nothing attached). No MCP write tool.", "api_writes": "2 relay strokes via scripts/pm/fleet-write/dispatch.mjs: pr_create (POST /repos/objectstack-ai/objectstack/pulls draft + POST .../issues/23846/assignees), comment (POST .../issues/23809/comments, this report). Plus git push.", "open_questions": [ { "question": "A stageField naming a picklist-backed select: refuse at parse (shipped) or judge the resolved list's kinds?", "options": [ "A (shipped): ObjectSchema refuses it at stageField. Every door, including the save door; zero corpus instances; widening later is non-breaking.", "B: accept it at parse and judge the resolved picklist (plus picklistExtensions) in the CLI picklist-references walk. Shared stage lists become possible, but only os validate / os build judge them, and the save door lets them through unjudged." ], "recommendation": "A. Business pull: none measured (zero picklist-backed stage fields). AI-safety: a parse refusal is the loud door every write passes. Startup focus: no new CLI rule. Long term: widen to B only when a named producer needs shared stage lists." }, { "question": "Stage fields carrying options on multiselect or multiple: true are now refused at stageField; kind is refused on those types. Keep it?", "options": [ "A (shipped): refuse. A record stands at one stage.", "B: leave non-single-value stage fields alone." ], "recommendation": "A. Without it such a stage field slips the mandatory-kind rule silently. Zero corpus instances." } ], "out_of_scope_findings": [ "carrier: none · noted in PR Acceptance notes, not filed: the object designer's options repeater (visible for multiselect/checkboxes) offers kind, which the door refuses there, and the form DSL has no parent-scoped row predicate. Help text states the scope.", "carrier: none · noted, not filed: metadata-core FLS_CONTRACT_OBJECT names stageField over kind-less options. No door parses it in its suites." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
os-dev-report
{ "issue": 23809, "status": "done", "branch": "claude/issue-23809-select-option-kind", "pr": "https://github.com/objectstack-ai/objectstack/pull/23846", "session": "session_017qmjVjz61scwQFLfspdBX6", "premise_still_valid": true, "summary": "Patch round on contract review 6211666660 (FAIL), head 44754694. (1) The stageField .describe() and its [#23809] TSDoc now state only what refuseStageFieldWithoutKinds refuses (a field that carries options must be a single-value select/radio with inline options each declaring kind; picklist-backed and multi-value stage fields refused; an unresolved name or an option-less field not judged at parse); reference docs regenerated via check:generated --fix (object.mdx, api/metadata.mdx, system/migration.mdx). (2) The changeset adds '@objectstack/platform-objects': patch and '@objectstack/metadata-core': patch, each with one sentence. (3) FLS_CONTRACT_OBJECT's salary_grade options declare kind: 'open', pinned in metadata-core. Also found and fixed a CI red the first round missed: Test Core (3/6) failed on three platform-objects catalog count pins that the new kind rows move (10->11, 116->118, 672->674).", "tests": "head 44754694. spec: check:generated all 14 up to date after --fix; select-option-kind + object.test 232/232. metadata-core full suite 735/735, typecheck exit 0. The new pin asserts no ObjectSchema issue at stageField or under fields.salary_grade.options, with a positive control: deleting one option's kind raises exactly fields.salary_grade.options.1.kind. A whole-object ObjectSchema.parse pin was not cheap, because the fixture is deliberately not spec-valid elsewhere (its script actions carry no body/target, measured: actions.0.body / actions.1.body issues), so the pin is scoped to the stage-field rule. Downstream after rebuilding metadata-core: rest FLS/save-gate 125/125, runtime meta-object-fls 85/85. platform-objects: 3 failed / 1089 passed before (field-panel-echo-decisions ROW_PROPERTIES 11 vs 10, object-collapsed-sections OPEN_LEAVES 118 vs 116, object-lifecycle-panel translated 674 vs 672 per locale), 1092/1092 after, typecheck exit 0.", "gates": "dispatch-gates --commands on head ec9f775f: 116 derived, 116 run, --ran reconciled (0 NOT-MEASURED, 0 UNRUN). Exit codes before any pipe: 111 exit 0. check:skill-examples, check:i18n and check:i18n-coverage exited 3 (spec dist prerequisite, as in round 1); after a spec rebuild all three re-ran 0. NOT MEASURED locally (exit 3 prerequisite, left to CI): check-plugin-teardown-shape --self-test (shallow clone) and check:dual-build-cjs-loads (workspace unbuilt). Head 44754694 adds only three platform-objects test edits, and the derived list is byte-identical. The families that read them were re-run, all exit 0: type-check-coverage, type-check-debt, test-source-alias, cross-package-test-inputs, issue-citations, nul-bytes, doc-authoring, i18n, adr-0087-registration, changeset-no-major, empty-changeset. CI as read on the ec9f775f-era run (38877404174 / 38877404281): Test Core (3/6) failure, which is the platform-objects count pins above, now fixed; Test Core (2/6) and (4/6) in_progress; everything else success or skipped. CI on 44754694 is in_progress.", "line_budget": "This round: spec describe/TSDoc (2 hunks), changeset (+2 frontmatter lines, +4 body lines), metadata-core fixture (+3 lines) and pin (+25), platform-objects count pins (3 hunks, +10), regenerated reference docs. No skills/** or governed surface.", "deviations": [ "Round-1 miss, now closed: the first report did not run @objectstack/platform-objects' own suite after changing its bundles, so the CI red on three count pins went unreported there.", "metadata-core pin is scoped to the stage-field rule rather than a whole-object parse (see tests).", "PR body not edited (os-dev: the seat writes it); replacement paragraphs are below." ], "files_changed": [ "packages/spec/src/data/object.zod.ts", ".changeset/23809-select-option-kind.md", "packages/metadata-core/src/object-schema-fls-contract.ts", "packages/metadata-core/src/object-schema-fls-references.test.ts", "packages/platform-objects/src/apps/translations/field-panel-echo-decisions.test.ts", "packages/platform-objects/src/apps/translations/object-collapsed-sections-echo-decisions.test.ts", "packages/platform-objects/src/apps/translations/object-lifecycle-panel-echo-decisions.test.ts", "content/docs/references/{api/metadata,data/object,system/migration}.mdx (check:generated --fix)" ], "sweep": "git grep -n 'stageField:' -- 'packages/**/src/**' examples on 44754694. Parsed by ObjectSchema and pointing at kind-less options: none left. Showcase semantic-zoo was fixed in round 1; FLS_CONTRACT_OBJECT is fixed now. Not parsed by ObjectSchema: lint validate-semantic-roles.test.ts and validate-object-field-refs.test.ts (validateSemanticRoles / validateObjectFieldRefs over raw stacks); metadata-core object-schema-fls-references.test.ts:73 (applyObjectSchemaMask input, and secret_score is a number field with no options); metadata-core object-schema-fls-references.ts:738 (a mask position table, not metadata); spec object.test.ts:1766 (fields: {}, so the target is undeclared and not judged) and :1334 (retired detail block); e2e detail-shapes.spec.ts (comments and stageField:false tests). stageField: false sites are untouched by the rule.", "mcp_calls": "7 read-only this round: pull_request_read get_comments, get_check_runs; get_job_logs x4 (job 116896497288); plus the round-1 reads. No MCP write tool.", "api_writes": "1 this round: comment (POST /repos/objectstack-ai/objectstack/issues/23809/comments, this report). Plus three plain git pushes (no force).", "pr_body_patch": [ "In ## What, replace the object.zod.ts bullet's last sentence with: Left as they were: `stageField: false`, a pointer to no declared field (lint `semantic-roles` owns it), a field with no options. The `stageField` describe states exactly this scope.", "In ## What, replace the changeset bullet with: Changeset `.changeset/23809-select-option-kind.md`: `@objectstack/spec` minor, `@objectstack/platform-objects` patch (metadata-form bundles), `@objectstack/metadata-core` patch (contract fixture), `Clause-②: yes (narrowing)`, with the migration.", "In ## What, add a bullet: `packages/metadata-core/src/object-schema-fls-contract.ts`: the published contract fixture `FLS_CONTRACT_OBJECT` names `salary_grade` as its `stageField`, so its two options declare `kind: 'open'`. It is pinned in `object-schema-fls-references.test.ts`, which asserts no stage-field issue and has a positive control.", "In ## What, add a bullet: platform-objects catalog count pins follow the new `kind` rows: `field-panel-echo-decisions` 10 to 11, `object-collapsed-sections-echo-decisions` 116 to 118, `object-lifecycle-panel-echo-decisions` 672 to 674.", "Replace the ## Verification heading line with: ## Verification (head 44754694), and append: platform-objects 1092/1092; metadata-core 735/735; rest FLS 125/125; runtime meta-object-fls 85/85.", "In ## Acceptance notes, delete the FLS_CONTRACT_OBJECT bullet (now fixed in this PR)." ], "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
os-dev-report
{ "issue": 23809, "status": "done", "branch": "claude/issue-23809-select-option-kind", "pr": "https://github.com/objectstack-ai/objectstack/pull/23846", "session": "session_017qmjVjz61scwQFLfspdBX6", "premise_still_valid": true, "summary": "Patch round 2 on delta contract review 6213027069, head ef945519. One sentence in .changeset/23809-select-option-kind.md (the metadata-core line) was false: it said the fixture still parses through ObjectSchema. It now says what the pin asserts: the two options declare kind: 'open', and ObjectSchema raises no issue at stageField or under fields.salary_grade.options. It also states that the fixture is not otherwise spec-valid (its script actions carry no body or target) and that this change leaves that as it was. No other file touched.", "tests": "No code change; no test run this round (the pin the sentence now describes ran 735/735 in round 1 of the patch, on 44754694).", "gates": "Changeset families on head ef945519, exit codes before any pipe, all 0: check-empty-changeset --base origin/main and --self-test, check-adr-0087-registration --base origin/main and --self-test, check-changeset-no-major --base origin/main and --self-test, pnpm check:changeset-gate-self-tests, check:nul-bytes, check:issue-citations, check:doc-authoring. CI on ef945519 not yet concluded; not waited on.", "line_budget": "1 file, +1 / -1.", "deviations": [], "files_changed": [".changeset/23809-select-option-kind.md"], "mcp_calls": "0 this round.", "api_writes": "1 this round: comment (POST /repos/objectstack-ai/objectstack/issues/23809/comments, this report). Plus one plain git push.", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
ACCEPT → PR #23846 comment 6213634298 ·
domain:specseat 2 · sessionsession_01LFqdRsuM5nkqoJdHd5ztVk· 2026-10-16T10:37Z · on contract review PASS6213609812(after FAIL6211666660and FAIL6213027069). Ready + auto-merge in this act;Fixes #23809closes this card on merge.objectstack-fleet commentedon Oct 16, 2026
Landed · PR #23846 →
adad73f714(Fixes #23809) ·domain:specseat 2 ·os-bill· sessionsession_01LFqdRsuM5nkqoJdHd5ztVk· 2026-10-16T11:23Zadad73f714on parentc5cf546a6forigin/maingit merge-base --is-ancestor adad73f714 origin/main→ exit 0git diff c5cf546a6f adad73f714and the reviewed head's net diff (ef94551925over its merge base with that parent) sharegit patch-id --stable3f0bdeb0gh-readonly-queue/main/pr-23846-*ref remainscompletedbyFixes #23809;pm:dispatchedcleared in this actWhat holds on
mainnow.kind(open/won/lost):wonis the successful end andlostthe unsuccessful end, on every object. That is the vocabularyrecord:pathstages[].terminalalready ships.open.kindis refused outside a single-valueselect/radio.stageFieldnames,kindis mandatory.ObjectSchemarefuses a missing kind at every door, with one located issue per option. It also refuses a stage field that is picklist-backed or multi-value.os migrate metalists the D3 entrystage-field-option-kind-required.Chain:
62097286726211151563,6212681414and the round-3 report6211666660, FAIL6213027069, then PASS62136098126213634298Readers to follow, in their own cards (the card's Target 2–4):
kind;kindon its lead and opportunity stages.Read at this record: no
objectstack-ai/objectuiissue cross-references #23809, by #23809's timeline and a search of that repository. The HotCRM repository is outside this seat's reach, so it was not checked. The card's Target 3 says that a HotCRM follow-up references this card.Generated by Claude Code