Skip to content

Decision: DESIGN.md §10 asks for 300 seeded contract versions, but §03 makes file required and a seed cannot mint one #19

Description

@hotlong

Raised by card 08 (#18, seed data), by Claude Code session session_01KcrVDXSptwDukFsHPHPR1V, rather than editing the governed surface. DESIGN.md §01–§04 is a governed surface; §10 is the seed table.

The conflict

  • §03 declares clm_contract_version with file* — required. src/objects/contract-version.object.ts implements it as Field.file({ required: true, accept: ['application/pdf', '.docx'] }).
  • §10 asks for 300 seeded clm_contract_version rows ("谈判中的合同有 3 到 5 版,含对方红线").
  • A file value is an opaque sys_file id minted by an upload. sys_file is a fileId-to-storage-key mapping; the bytes live in the storage backend under key. A declarative seed has no upload and cannot mint one.

Measured, three ways (@objectstack/* 17.3.0, sqlite, objectstack dev)

One seed load, three version rows differing only in the spelling of file:

  1. file omitted — refused, and the whole row is lost:

    ERROR [SeedLoader] Failed to write clm_contract_version record #2
          (contract+version_no=... 1): File is required
    
  2. file is a URL (https://example.invalid/probe.pdf) — the row inserts, and the platform then refuses to attest its own migration:

    WARN [migration] NOT attesting 'adr-0104-file-references' on this new datastore:
         this boot already wrote 1 value(s) that the migration's own contract rejects
         (clm_contract_version.file: Expected an opaque sys_file id).
    
  3. file is an id-shaped token (probe-file-token-0001) — the row inserts silently, no warning, and reads back verbatim because no sys_file matches it. It is a download that 404s.

Positive control: the other 12 rows of the same seed load inserted without complaint, so the refusal in (1) is about the value, not the seed path.

Why card 08 shipped zero versions

Option 3 is the one a seed could get away with, and it is the one AGENTS.md forbids: "Honest capabilities. No AI output that is a stub; no seeded number that looks computed. A capability the runtime does not deliver is hidden, not faked." So #18 seeds no versions at all, version_count reads an honest 0 on every one of the 120 contracts, and this card carries the decision.

What that costs today

  • The version timeline of §05 is empty, and the counterparty-redline story of §10 cannot be demonstrated from the seed.
  • version_count is one of the five roll-ups §10 wants shown; it is the only one that reads 0 everywhere. (The other four are verified correct against their seeded children in Seed data: demo-en (default) and demo-zh (card 08, M2) #18.)
  • A seeded draft cannot be submitted through the UI. The draft to submitted guard wants "至少一个版本文件或类型带模板", and the seed can supply neither, so the ten seeded drafts refuse submission with Upload a first version, or choose a contract type that carries a template, before submitting. Driving intake in the demo means creating a new contract and uploading a real file.
  • The same wall applies to clm_contract_type.template_file and clm_signature.executed_file; both are optional, so Seed data: demo-en (default) and demo-zh (card 08, M2) #18 simply leaves them empty.

Options

A. Leave §03 as it is; the seed ships no versions. What #18 does today. Zero cost, zero risk, and the demo keeps a visible hole. The file* requirement is defensible on its own terms: a version without its document is not a version.

B. Have pnpm demo upload one real document and let the fixture reference it. The priming boot in scripts/demo.mjs already signs in; it could POST a small PDF, capture the returned sys_file id, and pass it to the demo boot as an environment variable the fixture stamps on all 300 rows. sys_file ownership is exclusive per (object, record, field), and the platform copies the bytes into a fresh row rather than sharing one, so 300 references are legal. Cost: the declarative seed gains a runtime dependency, and the fixture's row count becomes conditional on a variable — if the upload fails, the version dataset has to be dropped, silently changing what pnpm demo produces.

C. Relax clm_contract_version.file to optional in §03 and in the object, and move "a version must carry a document" into the state-machine guards that already read versions (approved to signing wants a current clean version; signing to active wants a final_signed one). Cost: an edit to the governed surface, and a window where a version row can exist with nothing attached — which is arguably what a "by template" version already is.

Recommendation: A now, C when a maintainer next opens §03. B buys the demo one artefact at the price of making the seed non-declarative and its row count conditional, which is a bad trade for a fixture whose whole design is that both locales produce provably identical rows. C is the honest long-term shape, but it is a §03 decision and #18 has no mandate to take it.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions