Skip to content

feat(picklists): the shared option lists become picklist metadata — *.picklist.ts and Field.select({ picklist }) replace _picklists.ts (acceptance of objectstack#18164) #2000

Closed

Description

@objectstack-fleet

Unblocked: #2031 landed as 2f3d898d (PR #2091, 2026-10-13); see the unlock-scan comment.
Re-derived 2026-10-09T14:13Z by the repo:hotcrm seat (session_018Mk4tab2eCyY41UTWK7y7V): the previous blocker objectstack-ai/objectstack#22178 closed completed at 2026-10-08T08:00:12Z, but no installable release carries it yet (npm view @objectstack/cli version answers 17.7.0, which is this repo's pin). So this card waits on #2031, the upgrade card, whose Restart-when: is that release; re-derive again when #2031 closes.
Unlock: a PUBLISHED @objectstack/* release that carries the objectstack#22178 fix, pinned in this repo's package.json (merged upstream is not installable). The migration is done and green on claude/issue-2000-picklist-metadata (c8f331cf); on unlock: merge main, re-run pnpm verify and the live-boot probe (including the zh-CN analytics dimension reading), then open the PR. Ruled 6051357805 (batch hotcrm-R74 item 2, letter A); blocked 2026-10-08T04:13Z.

Sub-issue of objectstack-ai/objectstack#18164 (shared picklist / global value set), the acceptance step. Filed by the #18164 epic PM: the director seat, summon #32, session_016tKoy8NJa35Yih1FdzrVmn. It was taken over on the maintainer's order in the live director chat, verbatim 「你负责接手,并派发处理所有所有相关的」.

Filing gate: ② a ruling's execution. objectstack-ai/objectstack#18164 ruling batch #209 item 1 (5755653853, maintainer 「18164 同意」) states: "hotcrm's migration of its shared lists is filed as a hotcrm card by the epic PM when this lands." The runtime half (objectstack-ai/objectstack#19519, PR #21047, 88b484e00) landed on 2026-10-01 and ships in @objectstack/spec / @objectstack/runtime 17.6.0, which hotcrm already pins. The card was never filed; this is it.

What the platform now offers (17.6.0)

  • definePicklist({ name, label, description?, options }) (packages/spec/src/data/picklist.zod.ts). The kind is picklist, authored as *.picklist.ts or defineStack({ picklists }). options is the field option shape, reused verbatim.
  • A select field references a list: Field.select({ picklist: '<name>' }), mutually exclusive with options.
  • The server resolves the reference and serves the field with the resolved options. Writes are validated against the resolved set.
  • picklistExtensions: [{ extend, options }]: additive, package-level.
  • Translation face: TranslationData.picklists.<name>.{ label?, options: { value: label } }. A field that references a picklist inherits it.
  • Not in 17.6.0: a per-organization overlay (deferred phase 2).

The reading (hotcrm main f44ab6429c)

src/sales/objects/_picklists.ts (286 lines) exports 13 option lists and a plainOptions helper. It is imported by 21 files across src/sales, src/revenue and test/.

Lists used in two or more files:

List Files
LEAD_SOURCE_OPTIONS 5
INDUSTRY_OPTIONS 3
PAYMENT_TERMS_OPTIONS 3
SALUTATION_OPTIONS 2
TASK_TYPE_OPTIONS 2
OPPORTUNITY_STAGE_OPTIONS 2
RELATED_TO_TYPE_OPTIONS 2

The rest are each used by one file.

Scope

  1. Every option list that more than one select field uses becomes a picklist in the owning package's *.picklist.ts. Each referencing field switches to Field.select({ picklist: '<name>' }).
    • Names follow the platform's snake_case rule and the app's prefix convention; the claim measures which applies to a picklist name.
    • A list used by exactly one field returns to that field as inline options. A shared kind with one user is the special case the kind exists to remove.
    • Whether a list is "shared" is measured by its field references, not by import count.
  2. Non-field consumers (views, flows, actions, the dashboard, the import mapping, the lead hook, tests) read the values from the picklist definition, or keep a local constant if the value list is not a field's option set. Each consumer gets a disposition in the PR body; none is left silently pointing at a deleted constant.
  3. Translations: option labels for a picklist-bound field move to picklists.<name> in each locale bundle that carries them (the ja-JP shared file included). Field-level option translations remain only for inline options.
  4. Special lists: DUPLICATE_OF_TYPE_* (authorable vs. erased) and plainOptions are kept or retired on measurement, with the reason in the PR body.
  5. _picklists.ts is deleted if nothing is left in it. Otherwise it keeps only what is not a field option list, and is renamed for what it is.

Acceptance

  • pnpm verify green: validate, typecheck, lint, hygiene, build and test.
  • Live boot, the checklist item records-forms.picklist-shared-across-objects: one shared picklist on two objects is served the same resolved options, a value outside the set is refused, and a locale switch relabels. Run it against the app booted on 17.6.0, and record the run in the PR body.
  • Community-edition rule: picklists are core spec and runtime, and no enterprise capability is needed.
  • A changeset: hotcrm's changeset-check gate needs one; this changes shipped app metadata.

Not in scope

The per-organization overlay (records-forms.picklist-org-append), and the Studio picklist page (objectstack-ai/objectui#10202).


Generated by Claude Code

Activity

  1. added
    enhancementNew feature or request
    pm:epicParent delegated to a dedicated epic PM — other PMs never dispatch into its subtree
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Oct 3, 2026
  2. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: epic PM round 1 (objectstack-ai/objectstack#18164)
    Session: session_016tKoy8NJa35Yih1FdzrVmn
    Account: hotlong (the session's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-2000-picklist-metadata
    Worktree: hotcrm-issue-2000
    Domain: repo:hotcrm (a single-lane repository), under epic:#18164
    Seat: the epic PM of objectstack-ai/objectstack#18164, which is the director seat, summon #32. It was taken over on the maintainer's order 「你负责接手,并派发处理所有所有相关的」; the checklist is objectstack-ai/objectstack#18164 5967887779.
    File surface:

    • src/sales/objects/_picklists.ts (deleted, or reduced to what is not a field option list and renamed).
    • New *.picklist.ts files in the owning package, plus the barrel or defineStack registration they need.
    • The select fields that use a shared list: src/sales/objects/{account,contact,lead,opportunity,task,event,event_attendee}.object.ts and src/revenue/objects/{contract,quote}.object.ts.
    • The non-field consumers that import the lists:
      • src/sales/views/{lead,opportunity}.view.ts
      • src/sales/flows/schedule-followup.flow.ts
      • src/sales/actions/opportunity.actions.ts
      • src/sales/dashboards/executive.dashboard.ts
      • src/sales/mappings/_shared.ts
      • src/sales/objects/lead.hook.ts
    • Locale bundles under src/**/translations/** that carry those options' labels.
    • objectstack.config.ts only if registration requires it.
    • The tests that import _picklists: test/actions-flows-integrity, test/lead-duplicate-management, test/quote-accepted-payment-terms, test/view-references and test/placeholder-picklist-options.
    • One .changeset/2000-*.md.

    Stop on breach; explain in the report.
    Container & model: M, mode:subagent, model: opus
    Clause-②: no
    Thread-read: none
    Serial constraints cleared: zero open hotcrm PRs at this write. The upstream halves (objectstack-ai/objectstack#19518 and #19519) landed and ship in 17.6.0, which hotcrm main pins.

  3. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    Release: the epic PM of objectstack-ai/objectstack#18164, session_016tKoy8NJa35Yih1FdzrVmn · 2026-10-03T09:58Z · cause: the maintainer's order in the live director chat, verbatim 「hotcrm延后,先处理平台」 · destination: pm:on-hold


    Generated by Claude Code

  4. added
    pm:queueReady for the PM dispatch loop
    and removed
    pm:epicParent delegated to a dedicated epic PM — other PMs never dispatch into its subtree
    on Oct 8, 2026
  5. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruling: batch hotcrm-R74 item 2 · letter A · maintainer 「同意」 2026-10-08T03:08Z

    repo:hotcrm seat, session_012zh91QzFgePbkmuHnugLN3. The maintainer answered 「同意」 in this session's chat to the batch presented there with the recommendations 1A 2A 3A 4B.

    The decision: A. Migrate now, without waiting for objectstack-ai/objectui#10202 (the Studio picklist page). The run is serial after hotcrm#1992, because both edit src/sales/translations. The fallback was B, wait for #10202; C was not to do the migration.

    Transition: pm:on-hold → pm:queue · reason: this ruling schedules the card sooner, which is the second arm of its Restart-when: line · pm:epic is removed in the same write, because the card leaves objectstack-ai/objectstack#18164's subtree and goes to the repo:hotcrm lane · source: the maintainer's 「同意」 above, in this session's chat.

    Premises re-read on origin/main c967803. Each one is still a claim for the dev to falsify:

    • src/sales/objects/_picklists.ts has 286 lines and 13 exported *_OPTIONS lists. 21 files under src and test import it.
    • @objectstack/spec@17.7.0, the pin, carries definePicklist and PicklistSchema (packages/spec/src/data/picklist.zod.ts).
    • The upstream ruling is objectstack#18164 5755653853 (Ruling: batch #209 item 1 · letter A · maintainer 「18164 同意」): "hotcrm's migration of its shared lists is filed as a hotcrm card by the epic PM when this lands."
    • The serial predecessor, hotcrm#1992, merged as 1e88edc1 (PR fix(service): retire crm_case.created_date — cases use the platform's created_at #2009).

    The scope and acceptance in the body are unchanged. The empty branch claude/issue-2000-picklist-metadata (at f44ab64, no commits) is reused by the next claim.


    Generated by Claude Code

  6. 7 remaining items

  7. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    Verdict: os-dev report 6052029866 — ACCEPT as blocked on the platform, pr: null. repo:hotcrm seat, session_012zh91QzFgePbkmuHnugLN3, 2026-10-08T04:15Z.

    Release: session session_012zh91QzFgePbkmuHnugLN3 (claim 6051525236) · reason: a platform defect blocks landing (AGENTS.md §2: wait for the platform fix; no route-around, no half landing) · destination: pm:blocked, unassigned in the same act. The branch claude/issue-2000-picklist-metadata at c8f331cf keeps the finished work, ⛔ as a resume point, not a live claim.
    Blocked-by: objectstack-ai/objectstack#22178

    The blocker, verified by the seat in source, not taken from the report. On objectstack origin/main 959c209d:

    • packages/services/service-analytics/src/plugin.ts translateSelectOptions builds its synthetic field as { name: fieldName, options }.
    • packages/spec/src/system/i18n-resolver.ts (~3063) reaches lookupPicklistOption only through def.picklist.

    So a chart or report dimension over a picklist-bound field keeps its English labels in every locale. The dev measured it on POST /api/v1/analytics/dataset/query (zh-CN):

    • On base 1e88edc1, industry reads 教育 and lead_source reads 广告.
    • On the branch they read Education and Advertisement.
    • The inline-options control type reads 正式客户 on both.

    Landing now would put English category labels on 7 widgets across 4 dashboards, and on 3 reports, in zh-CN, ja-JP and es-ES. Filed upstream as objectstack-ai/objectstack#22178, with its duplicate check.

    What is verified done on the branch (read from the report; the seat re-ran nothing on it):

    • pnpm verify passed at c8f331cf: validate shows 5 picklists, lint --strict reads 0 / 0 / 18, and 3803 tests passed.
    • The live-boot checklist item records-forms.picklist-shared-across-objects passed all three clauses: the same resolved options in 20 of 20 cells, invalid_option refused, and the four locales relabelled.
    • The migration covers five shared picklists, single-field lists back inline, translations moved to picklists.NAME with 0 label mismatches, and _picklists.ts deleted.
    • Branch = main + 5 commits, 75 files (REST compare: behind 0).

    The dev's open questions, answered by the seat:

    1. Hold behind the upstream card (A). AGENTS.md §2 decides this, so it is not a choice: B ships a visible regression, and C is the consumer-side compensation that §2 forbids.
    2. The four inline copies of not_required / pending / approved / rejected (approval status) are ⛔ not folded in (A). This card's reading, and the ruling, cover the _picklists.ts lists. The approval-status vocabulary is a separate question with approval-flow semantics and gets its own card only if someone pulls for it.

    Unlock and resume: this card is unblocked when #22178's fix is INSTALLABLE, meaning a published @objectstack/* release carries it and this repo's pin moves to it. Merged upstream is not enough. Then:

    1. Merge main into the branch.
    2. Re-run pnpm verify and the live-boot probe, including the zh-CN analytics dimension reading as the regression check.
    3. Open the PR, with one pr_create.

    hotcrm#1907 and #1595, queued behind this card for file overlap, are not blocked by the platform. Their order is re-decided at the next dispatch.

    Implemented-by: os-dev subagent of this session · Reviewed-by: repo:hotcrm seat


    Generated by Claude Code

  8. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    Resume note for this card (repo:hotcrm seat, session_012zh91QzFgePbkmuHnugLN3, 2026-10-08T13:40Z). ⛔ Not a state change: this card stays pm:blocked on objectstack-ai/objectstack#22178.

    PR #2013 (#1595), awaiting the maintainer, deletes the five hand-built test stand-ins (test/helpers/{hook-harness,flow-harness,action-sandbox,metadata-fixtures,tenancy-probe}.ts). The tests now run on @objectstack/verify's handle, through test/helpers/verify-stack.ts.

    This card's branch claude/issue-2000-picklist-metadata (c8f331cf) edits seven of the same files, and its versions still import a deleted stand-in: global-actions, hook-org-inheritance, i18n-references, opportunity-line-item-add-picker, placeholder-picklist-options, quote-accepted-payment-terms, view-references.

    On resume, after merging main, move this branch's additions onto the handle (metadata / hooks.run / actions.run / rows) rather than restoring a helper. A re-grown stand-in is refused under #1595's rule ("⛔ Never re-grow a local stand-in").


    Generated by Claude Code

  9. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    Unlock scan by the repo:hotcrm seat (session_01BCDqr1DqnQjEh5jA9gGkUG), 2026-10-13T12:50Z. ⛔ Not a claim.


    Generated by Claude Code

  10. added
    pm:queueReady for the PM dispatch loop
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Oct 13, 2026
  11. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round R87 (resume of the finished migration branch)
    Session: session_01P8mZuzuyZAHSrgQKmv6ASv
    Account: sunhaolin (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-2000-picklist-metadata (resumes c8f331cf; main is merged in, never rebased)
    Worktree: hotcrm-issue-2000
    Domain: repo:hotcrm (single-lane repo, no domain:* taxonomy)
    Seat: repo:hotcrm#1
    File surface: as claim 6051525236: src/sales/objects/_picklists.ts (deleted, or reduced and renamed), new *.picklist.ts files plus their registration (objectstack.composition.ts / objectstack.config.ts only if registration needs it), the select fields in src/sales/objects/{account,contact,lead,opportunity,task,event,event_attendee}.object.ts and src/revenue/objects/{contract,quote}.object.ts, the non-field consumers (src/sales/views/{lead,opportunity}.view.ts, src/sales/flows/schedule-followup.flow.ts, src/sales/actions/opportunity.actions.ts, src/sales/dashboards/executive.dashboard.ts, src/sales/mappings/_shared.ts, src/sales/objects/lead.hook.ts), the locale bundles under src/**/translations/** carrying those options' labels, the tests importing _picklists (moved onto the @objectstack/verify handle per 6061167800, ⛔ no re-grown stand-in), one .changeset/2000-*.md (stop on breach; explain in the report)
    Container & model: L (a ~50-commit merge onto v18, then a live-boot locale probe), mode:subagent, model: default (dispatch-gates --tier --repo objectstack-ai/hotcrm: "no path-derived mandate"; the tier stays the PM's per-card call)
    Clause-②: no
    Responsibility: n/a — not a defect card
    Thread-read: 6145948198
    Serial constraints cleared: src/sales/translations has no other writer in flight; #2083 (this round) writes src/revenue/objects/quote.hook.ts only, and this card writes quote.object.ts / contract.object.ts (disjoint files; the later lander re-reads the src/revenue token ratchet on the merged tree). #2057 (this round) writes no file.


    Generated by Claude Code

  12. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 2000,
    "status": "done",
    "branch": "claude/issue-2000-picklist-metadata",
    "pr": "#2111",
    "session": "session_01P8mZuzuyZAHSrgQKmv6ASv (subagent; the parent's harness id)",
    "premise_still_valid": true,
    "summary": "Resumed the finished 17.x branch (c8f331c), merged origin/main 4de5343 (46 commits, @objectstack 18.0.0-next.1) as merge commit 346951a with no rebase and no force-push, re-verified, and opened draft PR #2111 (head 05f36d1). H1 holds: the migration survives the merge. Picklists now register with the app package stack (objectstack.composition.ts appPicklists into createHotCrmAppStack); objectstack.config.ts is main's. Every conflict was resolved toward main's behaviour plus the migration: 22 files (21 content conflicts plus the _picklists.ts modify/delete), listed in deviations (e) and in the PR body. The branch's test additions moved onto the @objectstack/verify handle (verify.metadata.object, runtime.metadata.items('picklist')), and no stand-in was re-grown. test/helpers/stack-picklists.ts now resolves offered options through the platform's SchemaRegistry.resolvePicklistOptions instead of a local lookup. Equivalence with main, measured mechanically: (1) all 18 objects' offered options and field properties equal main's, 0 diffs; (2) 384 locale-pack option labels equal main's served labels, and the rest of each pack is identical, 0 mismatches; (3) every other composed collection is identical, and picklists is the only new key. H2 holds: definePicklist, Field.select({ picklist }), picklistExtensions and TranslationData.picklists.NAME are all present in the installed 18.0.0-next.1 dist. H3 holds: the analytics blocker objectstack#22178 is fixed on the pin. H4 holds on all three clauses, plus the form enumeration. H5 holds: the token ratchet is clean. Two comments that the migration made false were repointed. lead-conversion.flow.ts arrived from main via #2068 and still cited _picklists.ts. do-not-call-enforcement.test.ts still cited the retired plainOptions.",
    "tests": "FINAL HEAD 05f36d1: pnpm verify through os-verify-lock (slot hotcrm-2000) printed os-verify-lock: VERDICT command-exit 0. Step verdict lines: validate ✓ Validation passed (2250ms) / Data: 18 Objects 357 Fields 5 Picklists; typecheck tsc --noEmit passed in the && chain (--listFiles shows 190 test/ files inside it); lint 18 suggestion(s), 0 errors / 0 warnings; lint:i18n-gate ✓ i18n lint gate: 0 i18n/missing-* issues (18 total lint issue(s) reported, unaffected by this gate); hygiene ✓ source hygiene clean; hygiene:tokens ✓ source token ratchet clean; build ✓ Build complete (2427ms) / 5 Picklists; test Test Files 179 passed (179) / Tests 3660 passed | 1 skipped (3661). The same split chain also passed at 2b4eb38: 179 files and 3660 tests; validate, lint, i18n-gate, build, hygiene and tokens each exit 0. TARGETED at 2b4eb38: the 17 touched suites gave Test Files 17 passed (17) / Tests 471 passed (471), VERDICT command-exit 0. LINT VS MAIN: objectstack lint --json on an export of origin/main and on the branch gave 0/0/18 both, and the issue-set diff is empty both ways. H3 LIVE (objectstack dev -p 4120, fresh DB, tree 2b4eb38), POST /api/v1/analytics/dataset/query: zh-CN account_metrics x industry reads 教育 / 能源公用事业, lead_metrics x lead_source reads 广告 / 陌生拜访, control account_metrics x type reads 正式客户. ja-JP reads 教育 / エネルギー・公益, 広告 / コールドコール, control 顧客. en reads Education / Energy & Utilities, Advertisement / Cold Call, Customer. drillRawRows keeps the stored values. H4 LIVE (records-forms.picklist-shared-across-objects). Clause 1: GET /api/v1/meta/picklist lists industry, lead_source, payment_terms, related_to_type, salutation; served options are identical with picklist kept in 20/20 cells (5 lists x 4 locales); PicklistServedFieldSchema.safeParse succeeds 44/44 (11 fields x 4 locales); Chromium form enumeration on the create forms of crm_account, crm_lead, crm_contact and crm_opportunity in zh-CN and en has native options and the opened listbox equal to the served set in 14/14. Clause 2: crm_lead with industry=logistics, lead_source=webinar, salutation=prof returned 201; crm_account with industry=logistics returned 201; re-reads in 4 locales return the same values. industry=not_an_industry on crm_lead and crm_account, and lead_source=carrier_pigeon on crm_contact, each return 400 with code VALIDATION_FAILED, fields[].code invalid_option, and a message naming the picklist (zh-CN: 行业必须是选项列表“industry”中的值之一). Clause 3: industry=logistics reads Logistics / 物流 / 物流 / Logística, and payment_terms=due_on_receipt reads Due on Receipt / 货到付款 / 請求書受領時払い / Pago a la Recepción. Extension sub-clause: NOT MEASURED, reason: hotcrm authors no picklistExtensions (outside the card). ABLATION (the picklist translation-key guard; the HEAD with the implementation was committed first; each leg was mutated with objectstack scripts/ablation-replace.mjs and restored to the HEAD blob d07e1406bae1 with git diff HEAD empty). Leg A: bogus key mister under zh-CN picklists.salutation.options. Strict lint gave lint-exit=0 with 0 picklist findings; vitest i18n-references gave 1 failed / 21 passed. Leg B: an undeclared picklists.not_a_list entry. Strict lint gave lint-exit=0 with 0 findings; i18n-references gave 1 failed / 21 passed. Leg B's first two attempts were voided: one was refused by the tool (the anchor reappeared in the replacement); the other stripped salutation.options, so the stack schema refused the pack before either check ran. The direction was the expected one: red at the guard, while lint stayed silent.",
    "gates": "LOCAL (hand-derived from hotcrm's package.json verify script + ci.yml, as the dispatch states; dispatch-gates.mjs answers objectstack only, so no --ran reconciliation exists for this repo). Each exit was captured before any pipe, via redirect then $?. On 2b4eb38: validate exit 0, typecheck exit 0, lint exit 0, lint:i18n-gate exit 0, hygiene exit 0, hygiene:tokens exit 0, build exit 0, test exit 0. Final, on 05f36d1: pnpm verify exit 0 (VERDICT command-exit 0). CI on PR #2111 head 05f36d1, one read after opening: Vercel Preview Comments success; Label Pull Request success; in_progress: Check Changeset, Typecheck and Build, Quality Checks, link-check, Build and Test (22.x), Playwright, Analyze Code (javascript). The Playwright e2e is CI's, not run locally.",
    "mcp_calls": "0",
    "api_writes": "2 — (1) pr_create via node scripts/pm/fleet-write/dispatch.mjs: POST /repos/objectstack-ai/objectstack/dispatches (HTTP 204); the relay run 38661232113 executed POST /repos/objectstack-ai/hotcrm/pulls, and the read-back found 12168 bytes sent and stored identical, assignee sunhaolin, draft true. (2) this os-dev-report: POST /repos//issues/2000/comments via post-stamped.mjs. git push is not REST. label-write: zero, as dispatched. Reads were single-card REST GETs: issue 2000 and its comments, pulls/2111, and check-runs.",
    "line_budget": "PR #2111 vs main: +945 / -744 = 1689 changed lines across 75 files (9 added, 1 deleted, 65 modified). This is under the ~3,000 stop line. It counts the branch's own diff, the merge resolutions, the test moves and 3 small follow-up commits (2 comment repoints, 1 changeset sentence).",
    "files_changed": {
    "count": 75,
    "added": [
    ".changeset/2000-shared-picklists.md",
    "src/revenue/picklists/index.ts",
    "src/revenue/picklists/payment_terms.picklist.ts",
    "src/sales/picklists/index.ts",
    "src/sales/picklists/industry.picklist.ts",
    "src/sales/picklists/lead_source.picklist.ts",
    "src/sales/picklists/related_to_type.picklist.ts",
    "src/sales/picklists/salutation.picklist.ts",
    "test/helpers/stack-picklists.ts"
    ],
    "deleted": [
    "src/sales/objects/_picklists.ts"
    ],
    "modified": [
    "content/docs/administration/setup.mdx",
    "content/docs/administration/setup.zh-Hans.mdx",
    "content/docs/administration/setup.zh-Hant.mdx",
    "content/docs/sales/opportunities.mdx",
    "content/docs/sales/opportunities.zh-Hans.mdx",
    "content/docs/sales/opportunities.zh-Hant.mdx",
    "content/docs/sales/pipeline-management.mdx",
    "content/docs/sales/pipeline-management.zh-Hans.mdx",
    "content/docs/sales/pipeline-management.zh-Hant.mdx",
    "objectstack.composition.ts",
    "src/revenue/objects/contract.object.ts",
    "src/revenue/objects/quote.object.ts",
    "src/sales/actions/activity-actions.ts",
    "src/sales/actions/opportunity.actions.ts",
    "src/sales/dashboards/executive.dashboard.ts",
    "src/sales/flows/lead-conversion.flow.ts",
    "src/sales/flows/schedule-followup.flow.ts",
    "src/sales/mappings/_shared.ts",
    "src/sales/objects/account.object.ts",
    "src/sales/objects/contact.object.ts",
    "src/sales/objects/event.object.ts",
    "src/sales/objects/event_attendee.object.ts",
    "src/sales/objects/lead.hook.ts",
    "src/sales/objects/lead.object.ts",
    "src/sales/objects/opportunity.object.ts",
    "src/sales/objects/task.object.ts",
    "src/sales/translations/en/app.ts",
    "src/sales/translations/en/objects.activity.ts",
    "src/sales/translations/en/objects.commerce.ts",
    "src/sales/translations/en/objects.customer.ts",
    "src/sales/translations/en/objects.pipeline.ts",
    "src/sales/translations/es-ES/app.ts",
    "src/sales/translations/es-ES/objects.activity.ts",
    "src/sales/translations/es-ES/objects.commerce.ts",
    "src/sales/translations/es-ES/objects.customer.ts",
    "src/sales/translations/es-ES/objects.pipeline.ts",
    "src/sales/translations/ja-JP/_shared.ts",
    "src/sales/translations/ja-JP/app.ts",
    "src/sales/translations/ja-JP/objects.activity.ts",
    "src/sales/translations/ja-JP/objects.commerce.ts",
    "src/sales/translations/ja-JP/objects.customer.ts",
    "src/sales/translations/ja-JP/objects.pipeline.ts",
    "src/sales/translations/zh-CN/app.ts",
    "src/sales/translations/zh-CN/objects.activity.ts",
    "src/sales/translations/zh-CN/objects.commerce.ts",
    "src/sales/translations/zh-CN/objects.customer.ts",
    "src/sales/translations/zh-CN/objects.pipeline.ts",
    "src/sales/views/lead.view.ts",
    "test/actions-flows-integrity.test.ts",
    "test/attendee-type-resolution.test.ts",
    "test/campaign-member-cascade.test.ts",
    "test/do-not-call-enforcement.test.ts",
    "test/event-attendee-cascade.test.ts",
    "test/flow-variable-conditions.test.ts",
    "test/global-actions.test.ts",
    "test/hook-org-inheritance.test.ts",
    "test/i18n-references.test.ts",
    "test/import-mappings.test.ts",
    "test/lead-duplicate-management.test.ts",
    "test/line-item-cascade.test.ts",
    "test/opportunity-line-item-add-picker.test.ts",
    "test/placeholder-picklist-options.test.ts",
    "test/quote-accepted-payment-terms.test.ts",
    "test/view-references.test.ts",
    "test/win-loss-capture.test.ts"
    ]
    },
    "deviations": [
    "(a) File surface beyond claim 6180895114. Every item is comment-only, follows from deleting _picklists.ts, or follows from fields becoming picklist-bound. No platform file was touched. NEW this round: src/sales/flows/lead-conversion.flow.ts (comment; the text arrived from main via #2068 and still cited _picklists.ts) and test/do-not-call-enforcement.test.ts (comment naming the retired plainOptions). CARRIED from the 17.x round and reported in 6052029866: src/sales/actions/activity-actions.ts (comment), 9 content/docs pages, test/helpers/stack-picklists.ts, and the test suites that do not import _picklists. Those suites are the 8 ObjectQL.create engine suites, global-actions, i18n-references and import-mappings.",
    "(b) The changeset keeps its existing name, .changeset/2000-shared-picklists.md, not 2000-picklist-metadata.md. The dispatch says 'the branch may already carry one; keep one', so it stays the one changeset. One sentence on chart and report labels was added after H3 measured them.",
    "(c) PR body footer: the os-dev session-URL form _Generated by [Claude Code](https://claude.ai/code/session_01P8mZuzuyZAHSrgQKmv6ASv)_, following the agent definition rather than the harness reminder's two-line attribution. Commits carry the harness's Co-Authored-By and Claude-Session trailers. No card trailers were used.",
    "(d) For module resolution, four short-lived probe scripts were placed in the worktree root. They were the served-field parse and three Playwright DOM probes, named .probe-served-2000.mts and .pw-*-2000.mjs. Each was deleted in the same command that ran it, and none was committed; git status stayed clean. Everything else lived in the scratchpad issue-2000/ directory.",
    "(e) H1 conflicted files (21 content conflicts plus 1 modify/delete), each resolved toward main's behaviour plus the migration: objectstack.config.ts (main's composeStacks; registration moved to the composition), objectstack.composition.ts (main's two stacks + appPicklists), src/sales/objects/_picklists.ts (modify/delete: stays deleted, main's #2035/#2040 comment edits carried into payment_terms.picklist.ts), src/revenue/objects/contract.object.ts (main's hook name + picklist), src/sales/views/opportunity.view.ts (main's: rowColor gone, #2108), src/sales/dashboards/executive.dashboard.ts and src/sales/mappings/_shared.ts (branch's comments), test/{attendee-type-resolution,campaign-member-cascade,event-attendee-cascade,flow-variable-conditions,hook-org-inheritance,line-item-cascade,opportunity-line-item-add-picker,win-loss-capture} (main's drivers/imports/syncSchemas + registerStackPicklists), test/{global-actions,i18n-references,quote-accepted-payment-terms,view-references} (main's verify-handle versions + the branch's additions moved onto the handle; view-references drops the retired rowColor pins), test/actions-flows-integrity (both non-vacuity guards kept), test/{import-mappings,placeholder-picklist-options} (main's imports + the branch's picklist reads)."
    ],
    "open_questions": [],
    "out_of_scope_findings": [
    "class: c · reach: public door — objectstack lint --strict (os lint) on hotcrm 2b4eb38, @objectstack 18.0.0-next.1. A zh-CN locale pack carrying picklists.salutation.options.mister (not a value of the list) exits 0 with 0 findings. So does a picklists.not_a_list entry that no package declares. The runtime drops both translations silently. The same mistake under a field's own options is reported as translation-option-key-unknown, and an orphan object or field key as translation-target-unknown. Named producer: hotcrm's locale packs, src/sales/translations/LOCALE/app.ts picklists. Evidence: ablation legs A and B in tests. In @objectstack/lint 18.0.0-next.1 dist/index.js, checkOptionKeys walks field options only, and no translation rule reads picklists. hotcrm keeps its local guard (test/i18n-references) until the platform covers it. · Seam: spec:TranslationData.picklists → runtime:@objectstack/lint translation-option-key-unknown / translation-target-unknown (do not walk picklists) | consumer: packages/spec/src/system/i18n-resolver.ts lookupPicklistOption (misses silently) · dedupe words: translation-option-key-unknown picklists, lint picklist translation key, picklists.NAME unknown option key, orphan picklist translation, translation-target-unknown picklist",
    "carrier: none (承接者:无) · noted, not filed · ObjectQL.create({ datasources, objects, hooks }) still takes no picklists on 18.0.0-next.1. An engine built from an app's objects therefore refuses every value on a picklist-bound field until registry.registerItem('picklist', …) runs, and hotcrm's 8 engine suites do that via test/helpers/stack-picklists.ts. It is a harness factory, not an authoring surface, so there is no production reach for class a/b/c.",
    "carrier: none (承接者:无) · noted, not filed · internal maintainer docs still name _picklists.ts in dated or planning prose. docs/architecture/module-split-plan.md (also says 'no global picklist metadata kind', now false), docs/architecture/psa-module-plan.md (plans a _psa-picklists.ts beside it), docs/architecture/module-split-inventory.json, and docs/requirements/0004, 0005 and 0006. They are outside the claim and were not edited (dated records stay, AGENTS.md upgrade rule 5)."
    ]
    }

  13. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #2111 comment 6181823445 · 2026-10-15T00:52Z

  14. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    落地 · 2026-10-15T00:53Z

    读数 探针 结果
    队列分支 git ls-remote origin 'refs/heads/gh-readonly-queue/*' 空(已出队;merged 2026-10-15T00:53Z)
    origin/main git merge-base --is-ancestor 8dfed905 origin/main 是;origin/main = 8dfed905 feat(picklists): the shared option lists become picklist metadata (#2000) (#2111),父提交 9e064b3d(#2109)

    状态变更:Fixes #2000 已代关(completed);摘 pm:dispatched、unassign,收口。合并前席位已在本地把 head 与 9e064b3d 合并:无冲突,token ratchet 干净(src/revenue ~19,162 / ~28,500)。随此解锁:#2110(报价总价只读,p3)不再被 quote.object.ts 卡住。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions