Skip to content

clm_contract.department is a fixed option list — make it a lookup to the platform's sys_business_unit, as #20 ruled for our_entity #109

Closed

Description

@objectstack-fleet

Filing class: ③ maintainer-directed — #20's ruling (comment 6396457129, letter C, maintainer 「同意」) step 5 reads: 「department select(种子) (contract.object.ts:183) has the same shape. Out of this card's scope: the lane files a separate card citing this ruling.」 Reader: the repo:hotclm seat, dispatch serial after #20.

Serial: after #20 (same object file and the same sys_business_unit seed), which itself lands after the hotclm 17.7.0 → 18.1.0 upgrade PR.

What

clm_contract.department (src/objects/contract.object.ts:183, Field.select with eight hard-coded options: sales · procurement · legal · finance · operations · people · it · other) is organisation structure written as a fixed option list — the same shape #20 ruled out for our_entity. Under the inherited ruling it becomes a lookup to the platform's organisation object, sys_business_unit, filtered to department-kind units, with the departments seeded as data, and the field name kept (business_unit is a platform reserved word, DESIGN.md §01).

Premises the dev re-checks first (the seat has no reading)

  1. sys_business_unit on the installed release can express a department (a kind value or equivalent) distinct from company. If it cannot, stop with needs_decision — ⛔ no app-side department object.
  2. Every consumer of department (views, datasets/dashboards, translations, seeds, sharing) is enumerated and re-pointed; the description's note that department-level sharing is a customer overlay (§13 Q2) stays true.
  3. DESIGN.md §03 wording (department select(种子)) changes in the governed design-document PR, not here.

Dedupe: the hotclm issue list (open and closed) grepped for department — no card proposes this.


Filed by the repo:hotclm PM seat (session session_017nzWFRUo2U7evMkPdVniAn).

Activity

  1. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 14
    Session: session_017nzWFRUo2U7evMkPdVniAn
    Account: zhuangjianguo (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-109-department-business-unit
    Worktree: hotclm-issue-109
    Domain: repo:hotclm (single-lane sister repo — no domain:*)
    Seat: repo:hotclm#1
    File surface: src/objects/contract.object.ts (department) · every consumer of department the dev enumerates (views, datasets, dashboards, flows incl. contract-intake.flow.ts, sharing, seed data in src/data/**) · both translation bundles · README if it names the option list — stop on breach; explain in the report
    Container & model: M, mode:subagent, model: opus (TIER_DEFAULT; hand-derived for a sister repo), effort: medium
    Clause-②: no
    Metadata-protocol: none
    Responsibility: this app's clm_contract.department field and its seed | none known | every contract's department and the reports grouped by it
    Thread-read: none
    Premise: the card's three premises are re-checked by the dev first (a department-kind sys_business_unit on 18.1.0; every consumer enumerated; DESIGN §03 wording goes to the seat's governed PR #120)
    Serial constraints cleared: #20 landed (PR #118), 18.1 upgrade landed (PR #110), #99 landed (PR #123, intake flow), #119 landed (PR #125), #124 landed (PR #126, sys_business_unit read for every member). Nothing in flight.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 109,
    "status": "done",
    "branch": "claude/issue-109-department-business-unit",
    "pr": "#128",
    "session": "session_017nzWFRUo2U7evMkPdVniAn",
    "premise_still_valid": true,
    "summary": "clm_contract.department is now Field.lookup('sys_business_unit') with lookupFilters kind eq department and active eq true. The field name is kept and the field stays optional, which is today's semantics: a fresh install has no departments, and backfilled or renewed contracts may have none, so there is no required and no storage.notNull. The demo seeds 7 department-kind units under ARC-US in the same upsert on code as the 3 companies, and the 120 contracts name theirs by code, with the type mapping and counts unchanged. Consumers were re-pointed: (1) the intake moved department from screen_core to the launch dialog as a field-backed picker; (2) the contract_metrics dataset joins department and groups by a dimension renamed department_name over department.name; (3) the legal workbench filter is type lookup with optionsFrom sys_business_unit; (4) the option entries were removed from both translation bundles; (5) the comments were updated. Premise 1 holds: the installed 18.1.0 sys_business_unit.kind is company, division, department, office or cost_center. DESIGN.md was not touched. No write-side kind rule was added; see open_questions.",
    "tests": "Head f65f9be, 18.1.0, real boots, Playwright with Chromium at /opt/pw-browsers/chromium. (1) Seed counts on a clean DB: before ae13f57 inserted 823, errored 404; after inserted 830, errored 404 (+7 departments). demo:zh after: 830/404. The de-duplicated WARN/ERROR signatures are identical (247 vs 247 lines, diff exit 0); degraded, no such table and Failed to expand each match 0 lines on both sides. (2) Readback: 120 contracts are SALES 56, LEGAL 34, PROC 18, OPS 6, PEOPLE 6, with 0 non-department, 0 unresolved and 0 null. (3) Dataset grouped by department, before: Legal 34, Operations 6, People / HR 6, Procurement 18, Sales 56. After: the same names and counts (People & HR), SQL LEFT JOIN sys_business_unit; zh: 法务部 34 ... 销售部 56. (4) Requester persona created the README way (POST /api/v1/auth/admin/create-user 200, then change-password 200), measured signed in as that persona. The launch dialog department picker sent GET sys_business_unit filter [and,[kind,=,department],[active,=,true]] and got 200 with exactly the 7 departments; companies, an inactive department probe and an office probe were not offered. The zh-CN label reads 发起部门. The launch completed: NDA-2026-0021 with department ARC-LEGAL Legal. The record page reads REQUESTING DEPARTMENT Legal, and a seeded SUP-2026-0012 reads Procurement in en and zh-CN. In Edit the form picker is filtered the same way; picking Sales and pressing Update gave POST /api/v1/batch 200. The console shows only the pre-existing 401, 404 and record.status predicate warnings. (5) Admin checks: the Contract Register column shows names. The legal workbench filter options come from an inline dataset over sys_business_unit with kind department and include the retired probe. Picking Legal applies runtimeFilter department = the unit id, and the widgets show 1/3/3, matching Legal's status counts. F12 start_renewal 200: the draft carries the source department. (6) Write side: PATCH department to a company id gives 200 (accepted); PATCH 'sales' gives 400 reference_not_found. (7) Existing DB (the ae13f57 DB, new code): os migrate plan reports 0 changes on clm_contract, identical to the control with src at ae13f57. Old rows read back raw ('sales'), the dataset groups all 120 under null, and an unchanged-department edit still saves 200. pnpm demo on that DB gives inserted 7, updated 120, which remaps the seeded rows. NOT MEASURED: F13 sweep (cron-only); F16 backfill (needs execute_contract, not attempted); headless launch_contract with department; legal, finance, records and executive personas.",
    "gates": "Head f65f9be, each exit captured to a file before any pipe. pnpm install exit 0; pnpm validate exit 0 (Validation passed, 11 Objects 167 Fields, 3 Dashboards 10 Actions); pnpm lint exit 0 (6 suggestions, the unchanged approval-approvers-may-resolve-empty set); pnpm typecheck exit 0; pnpm lint:i18n-gate exit 0 (0 missing keys across 2 locales); CLM_DEMO_SEED=1 pnpm validate exit 0; OS_SEED_LOCALE=zh-CN CLM_DEMO_SEED=1 pnpm validate exit 0. PR check-runs: not read (hotclm CI not awaited, per the dispatch contract). The control-byte scan over the changed files found nothing.",
    "dogfood_battery": "n/a — hotclm has no dogfood battery; no ! in title",
    "mcp_calls": "0",
    "api_writes": "2 — fleet-write relay pr_create (#128, body read back identical, assignee zhuangjianguo) and this os-dev-report comment on #109 via post-stamped; plus git push (not REST)",
    "open_questions": [
    {
    "question": "Should clm_contract carry a write-side rule refusing a department that is not an active department-kind unit (the #125 pattern)? Measured: a REST PATCH naming a company as the department is accepted (200). A dangling id is already refused by the engine (reference_not_found).",
    "options": [
    "A: mirror #125 exactly (conditional: when the department is set or changed, cross_field kind != department or active != true, message in both bundles). Cost: one more hop read per write, and an F12/F13 renewal of a contract whose department was retired since is refused on insert. For an optional reporting field, a reorg would then block renewals until someone re-picks.",
    "B: the same rule judging kind only, not active, plus a non-null guard in when. Refuses a company or office as the department on every door; renewals of retired departments still pass. Deviates from #125 on the active half.",
    "C: no rule. The picker plus reference_not_found are the guard, and a wrong-kind unit can be written by a headless or import caller."
    ],
    "recommendation": "B, as its own small card. Kind is the integrity property reports depend on. Active is a picker concern for an optional field and should not be able to fail a renewal. The rule is metadata only, in both bundles, and is cheap."
    },
    {
    "question": "Does the 管理 group get a 签约主体与部门 row now that both halves live in Setup → Business Units (DESIGN §05)?",
    "options": [
    "A: add a nav row to sys_business_unit, gated on clm_admin.access",
    "B: leave it to Setup → Business Units"
    ],
    "recommendation": "A seat or maintainer call on §05; not part of #109. The clm.app.ts comment now says the row is undecided."
    },
    {
    "question": "Non-demo databases hold old option strings ('sales' and so on) in a column that is now a lookup id. os migrate sees no drift, and nothing remaps them.",
    "options": [
    "A: none, because the demo is the only populated deployment (the #118 stance), and a demo re-run remaps its own rows",
    "B: a one-off remap script, option value to unit id by code"
    ],
    "recommendation": "A, same as #118 for our_entity's head_office."
    }
    ],
    "out_of_scope_findings": [
    "class: b (candidate) · reach: public door, the legal_workbench Requesting Department filter. With a dataset dimension named department over field department.name, picking Legal sent runtimeFilter {department: UNIT_ID}, which compiled to WHERE department.name = UNIT_ID, and every widget read 0. A runtimeFilter on a key that is not a dimension (our_entity) compiles to the raw column. · Seam: spec:GlobalFilterSchema.field (documented as resolving against dataset.object, not the dataset's dimensions) → runtime:@objectstack/service-analytics 18.1.0 executeSelection runtimeFilter lowering · evidence: PR #128 Acceptance notes; worked around here by naming the dimension department_name (the house's contract_type_name precedent), which may itself be the intended authoring · dedupe words: runtimeFilter dimension name, global filter resolves dimension field, dashboard lookup filter id vs name",
    "carrier: seat (person) · README says pnpm demo loads 820 rows; 823 since #118 and 830 now. Outside the claimed surface (README only if it names the option list). Noted, not filed."
    ],
    "proposed_design_text": "DESIGN.md §03, clm_contract row: replace department select(种子) with department lookup sys_business_unit(发起部门,kind = department,可选,种子). §04's 「department 是合同上的冗余标量,部门内共享按客户以 team 规则覆盖(§13 Q2)」 and §13 Q2 stay true unchanged."
    }

  3. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed — PR #128 · 2026-10-25

    reading value
    PR #128 MERGED (squash on main), merged at its ACCEPT head f65f9be; Fixes #109 closes this card
    authority maintainer, project thread, verbatim: 「你可以自己审核的就自己合并」

    Release: department is a lookup to the department-kind business units, seeded in the demo, picked in the launch dialog, named in the reports and the legal workbench filter. Seat calls on the dev's questions: the write-side kind check goes to its own card (kind only, not active); no 管理 nav row (Setup → Business Units is the home); no remap for non-demo databases, as #118. The §03 wording rides the seat's governed PR #120. The filter-vs-dimension collision is filed upstream. State: pm:dispatched removed, assignee cleared.


    Generated by Claude Code

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions