Skip to content

[Decision] The contract list still offers CSV import — a bulk door that skips the launch wizard; keep it, keep it for admins, or hide it #148

Closed

Description

@objectstack-fleet

Ruled: 6422708158 · letter A · 2026-10-26T17:19:58Z

Filing class: ③ needs a ruling — reach: public door, observed on 18.1.0 by the #136 dev (os-dev-report on #136, PR #146). Reader: the maintainer through the decision box, then the hotclm lane.

Situation

Since PR #146 the contract list no longer offers the platform's generic 新建: contracts are created only through 发起合同 (F1), 发起续签 (F12) and 补录已签合同 (F16), as DESIGN.md §06 lays out. The same list header still offers 导入 (CSV import) to whoever may create contracts — a bulk write door into clm_contract that skips F1 the same way: no first version, no routing, no type-driven fields, no numbering by F1.

What needs deciding

  • A — hide import for contracts (userActions.import: false), like create. Legacy contracts come in through F16 one by one, or through a seed/migration an administrator runs.
  • B — keep import for the CLM administrator only (if 18.1.0 can gate it per role — the lane measures), as a migration door for an existing contract register.
  • C — keep it as is.

The seat has no product reading; the server-side guards (draft-only create, FLS) hold either way.

Dedupe: hotclm issues listed and grepped for import, 导入, bulk — none.


Filed by the repo:hotclm PM seat from the #136 dev report.

Activity

  1. zhuangjianguo commented on Oct 26, 2026

    @zhuangjianguo
    Collaborator

    Ruling: batch #9 item 3 · letter A · maintainer 「同意」 2026-10-26T17:19:58Z

    A — hide CSV import on contracts, as #146 hid the generic New.

    What the director checked before recording (hotclm main 4485ea1, objectstack main):

    • The platform gates import by the create grant (permission.zod.ts: import → allowCreate, a maintainer ruling; no allowImport bit). Every member holds clm_requester with create on clm_contract, so the import API stays open to every member whatever the UI shows; userActions.import: false hides the entry, the same kind of change “新建” on the contract list and on the contract page's Renewals list opens the raw contract form, not the launch wizard — status, legal fields and six child grids for a requester #146 made for create.
    • Application objects (platform bucket) carry the import entry by default; the contract object declares only create: false, which is why import is still offered.
    • contract.hook.ts refuses a non-system insert with any status but draft, so an import can only produce a batch of version-less drafts. Option B's stated purpose (migrating an existing register of signed contracts) cannot be served by it; that migration is a system-run seed or script, the F16 path.

    Execution after the ruling (lane):

    1. userActions: { create: false, import: false } on clm_contract.
    2. Check the list header as a requester and as the CLM administrator: no 新建, no 导入.
    3. No §06 change, no platform card.

    Doc: https://claude.ai/artifact/VnrFu3yXCg8ynfh1rXtzYk


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 16
    Ruling-ref: 6422708158
    Session: session_017nzWFRUo2U7evMkPdVniAn
    Account: zhuangjianguo (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-148-no-contract-import
    Worktree: hotclm-issue-148
    Domain: repo:hotclm (single-lane sister repo — no domain:*)
    Seat: repo:hotclm#1
    File surface: src/objects/contract.object.ts (userActions.import: false beside create: false) — stop on breach; explain in the report. Governed DESIGN.md §04 text rides the seat's design-doc PR #120
    Container & model: S, mode:subagent, model: opus (TIER_DEFAULT; hand-derived for a sister repo), effort: medium
    Clause-②: no
    Metadata-protocol: none
    Responsibility: this app's contract create doors (F1, F12, F16 only) | none known | every member on the contract list
    Thread-read: 6422708158
    Premise: measured in the 2026-10-26 browser regression pass with real personas; re-measured by the dev on origin/main first
    Serial constraints cleared: batch 9 ruled 2026-10-26T17:19Z; #133, #134 and #148 dispatched together on disjoint files.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 148,
      "status": "done",
      "branch": "claude/issue-148-no-contract-import",
      "pr": "https://github.com/objectstack-ai/hotclm/pull/151",
      "session": "session_017nzWFRUo2U7evMkPdVniAn",
      "premise_still_valid": true,
      "summary": "Ruling 6422708158 letter A is implemented. src/objects/contract.object.ts now declares userActions: { create: false, import: false }, and the comment above it says why import goes too. Import is a bulk door that skips F1, and the contract hook only lets it make version-less drafts. Contracts come in through F1, F12 and F16, and a legacy register through F16 or an administrator's seed or migration. One file, 7 lines added, inside the claimed file surface. The key was checked against the installed @objectstack/spec 18.1.0: userActions is a strictObject with an import key ('Show CSV import wizard entry'). resolveCrudAffordances narrows the managedBy bucket default with it, and platform, this object's bucket, is the only bucket that grants import by default. 18.1.0 honours the flag in the UI, and the data API import endpoint is unchanged. That is reported, not altered: the platform gates import by the create grant, which every member holds through clm_requester. Premise re-measured on eace613 first: 导入 was on the contract list header for both personas.",
      "tests": "Metadata-only app with no unit suite (package.json has no test script), so the evidence is the browser and the API. Setup: one throwaway DB, pnpm demo --port 3148, Chromium /opt/pw-browsers/chromium at 1440x1000, zh-CN. Before = eace613's contract.object.ts (blob e4ecb2d, hash-checked on disk). After = f81629b (blob 4b5c1b6, restored with git checkout HEAD and hash-checked), on the same DB. Boot: '✓ Server is ready', no 'degraded capabilities', no 'no such table'. Personas: Business Requester 1 made the README way (admin create-user 200 with mustChangePassword true, then change-password 200, sign-in 200), plus the dev admin. Served metadata, GET /api/v1/meta/objects/clm_contract: item.userActions {\"create\":false} before, {\"create\":false,\"import\":false} after. Contract list header (我发起的 and 全部合同): 导入 · 发起合同 before, 发起合同 only after, for both personas. The overflow 更多操作 holds only 添加到收藏 before and after, so import did not move there. Other objects keep import as the dev admin: clm_party, clm_obligation and clm_clause show 新建 · 导入 before and after. Business Requester 1 had neither on those three lists before or after (no create grant there). 发起合同 still launches, as Business Requester 1 after the change: the dialog opens (合同类型*, 相对方, 我方签约主体*, 发起部门); POST /api/v1/actions/clm_contract/launch_contract answered 200; F1's 合同信息 screen opened; submitting it sent POST /api/v1/automation/contract_intake/runs/…/resume, which answered 200, and the flow moved on to 新建相对方 (no party picked). The before run was the same. Import API, POST /api/v1/data/clm_contract/import with format json and one row: 200 ok 1 · created 1 for dryRun true and false, for both personas, before and after. Rows written: before SOW-2026-0025 (admin) and SOW-2026-0026 (requester); after SOW-2026-0027 and SOW-2026-0028; all draft, read back by id. After the change, a status:active row imported as the requester answered 200 with ok 0 · errors 1, and the row reads INVALID_STATE 'A contract is created as a draft, not as active. …'. Console: one 404 on the dev admin's pages before and after; none for the requester. NOT MEASURED: a holder of the clm_admin position (the ruling's 'CLM administrator'). The dispatch named the dev admin, who holds no position; userActions has no per-role form here. Legal Counsel was not in this pass. No ablation: no test pins this behaviour, and the repo has no suite.",
      "gates": "All on HEAD f81629b, exit code captured before any pipe. pnpm install: EXIT 0, 'Done in 781ms using pnpm v10.28.0'. pnpm validate && pnpm lint && pnpm typecheck && pnpm lint:i18n-gate: chain EXIT 0. validate: '✓ Validation passed (909ms)'. lint: '6 suggestion(s) (948ms)', the six existing approval-approvers-may-resolve-empty notes, 0 warnings. typecheck: tsc --noEmit, no output. lint:i18n-gate: 'COVERAGE : 0 missing keys across 2 locale(s)'. CLM_DEMO_SEED=1 pnpm validate: EXIT 0, '✓ Validation passed (963ms)'. CI on head f81629b, read once: 'Validate' completed/success (two check-runs of that name, both success). dispatch-gates.mjs --commands is objectstack-only and was not run against this sister repo; the local set is the one AGENTS.md and the dispatch name.",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "mcp_calls": "0",
      "api_writes": "2. (1) pr_create through the fleet-write relay: one repository_dispatch POST /repos/objectstack-ai/objectstack/dispatches, run 40371138283 success. Inside the run: POST /repos/objectstack-ai/hotclm/pulls (draft, #151) and POST /repos/objectstack-ai/hotclm/issues/151/assignees (zhuangjianguo). Read back identical: 6825 bytes sent, 6825 stored. (2) This os-dev-report comment through post-stamped.mjs: POST /repos/objectstack-ai/hotclm/issues/148/comments. Also git push -u origin claude/issue-148-no-contract-import, which is not a REST write.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: ruling 6422708158 (it already records this) — noted in the PR's Acceptance notes, not filed. POST /api/v1/data/clm_contract/import stays open to every member who holds C on clm_contract, which is every member through clm_requester. After the change, a json import answers 200 and creates a draft for both the dev admin and Business Requester 1. The hook still refuses any non-draft row (INVALID_STATE). Platform design on 18.1.0: import is gated by allowCreate, and there is no import bit.",
        "carrier: none — observation, not filed. The dev admin's console run logs one 'Failed to load resource: 404', before and after this change. The URL was not identified, and Business Requester 1's run logs nothing."
      ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruling-ref: 6422708158

    Landed — PR #151 · 2026-10-26

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

    Release: the contract list no longer offers CSV import; contracts come in through F1, F12 and F16. 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