Skip to content

Seed datasets: extend the import door's applyOn: 'create' (#25577 / PR #25664) to seed-dataset columns — a re-run of an app's seed reverts columns its own jobs wrote #25761

Closed

Description

@zhuangjianguo

Filing class: ③ platform gap, filed at destination from an app ruling. Reader: domain:server / area:records triage. Unlabelled, no assignee. Origin: objectstack-ai/hotclm#41, ruled A by the maintainer on 2026-10-25 (ruling comment objectstack-ai/hotclm#41 (comment)): "job-written columns are written on first seed only — via the platform".

Context

hotclm's demo corpus (src/data, 820 rows, every dataset mode: 'upsert') is documented as re-runnable: "create the accounts and run pnpm demo again and their contracts are handed over". Its daily jobs (F11 payment_overdue, F12 renewal notice, F13 expiration_sweep) are the declared single writers of status, is_expiring and the stage timestamps.

Measured (17.7.0, hotclm#41 comments 6092962474 / 6093795791, hotclm#50)

A re-seed after the jobs have run:

  • silently moves 18 instalments overdue → partial (a legal edge, so 0 errors) — the jobs' work is undone with nothing in the log;
  • resets is_expiring to 0, so the next F12 run sends the same renewal notice again;
  • is refused by the state machines for terminated → active and expired → active (1 row each, loud) — and a refused seed row is dropped whole, every other column of that row with it.

The gap

The maintainer already ruled this need for the import door: #25577 → letter B, FieldMappingEntry.applyOn: 'create' | 'always', shipped in PR #25664 (packages/core/src/utils/import-runner.ts, packages/rest/src/import-prepare.ts). The seed loader (packages/runtime/src/seed-loader.ts) has no per-column policy: a seed dataset's upsert re-asserts every column on every boot, and the only modes are upsert / insert / ignore (row-level, not column-level). An app that seeds a lifecycle it also automates has no declarative way to say "this column is the job's after the first write".

What is asked

The same applyOn: 'create' | 'always' (default always) on a seed dataset's column declaration, honoured by the seed loader's upsert path exactly as the import runner honours it: on an existing row, columns marked 'create' are left out of the merge set. Same name, same semantics, same ruling — one concept for both doors.

App side after it ships: hotclm marks status, is_expiring and the job-stamped timestamps applyOn: 'create' and restores the README's re-run promise. Until then its README states the current truth.

Dedupe

search_issues on this repo: #25577 / #25475 (closed completed — import door only), #24562 (seed replay rewrites unchanged rows; a different problem, equality not ownership), #8740 (open; driver upsert merge-set fallback internals — adjacent, not this), #21646 (seed created_at). 0 cards about a column-level create-only policy for seed datasets.

Not asked

Activity

  1. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    Contributor

    Triage: first grade — enhancement · priority:p1 · domain:spec · area:records · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-25T09:59Z

    ⛔ Not a claim, ⛔ not a dispatch.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 2
    Session: session_018HE4VY52U3L5Bfzz51dsFv (the card thread; seat session session_01K2Tat8JEt4AhLASV4V4QWE posts this claim)
    Account: marchtian
    Branch: claude/project-thread-6djfn0
    Worktree: objectstack-issue-25761
    Domain: domain:spec
    Seat: domain:spec#4 (seat post #18917)
    File surface: packages/spec/src/data/** (seed dataset column schema) + packages/runtime/src/seed-loader.ts + tests + changeset
    Container & model: M, mode:thread, model: default tier (dispatch-gates --tier: "no path-derived mandate", floor sonnet · default opus · ceiling fable), effort: xhigh (p1)
    Clause-②: yes
    Metadata-protocol: added
    Responsibility: n/a — not a defect card
    Thread-read: 6396236242
    Premise: holds (at main 5f5f0b42: no applyOn on a seed dataset column (only packages/spec/src/api/export.zod.ts, the import door); the seed loader has no per-column policy; no open PR names #25761)
    Ruling-ref: hotclm#41 comment 6395830052 (letter A)
    Serial constraints cleared: #25779 (same loader and dataset schema) is serialised behind this card — answered serial, not fold: different defect shape and fix.

    Order: /mnt/project-files/spec-seat/orders/25761.md with _common.md (project files). Batch of six on the project owner's order 「你应该并发6」 (2026-10-25). Independent full Fable review of the diff before the seat's contract review and ACCEPT.

  3. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 25761,
      "status": "done",
      "branch": "claude/project-thread-6djfn0",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/25813",
      "session": "session_018HE4VY52U3L5Bfzz51dsFv",
      "premise_still_valid": true,
      "summary": "Spec: one ColumnApplyOnSchema enum ('create' | 'always') in packages/spec/src/data/column-apply-on.zod.ts, read by the import door's FieldMappingEntry.applyOn and by a new closed SeedColumnSchema; SeedSchema gains an optional `columns` map keyed by field name, and defineSeed refuses a columns key naming no column (compile time and run time). Seed loader (packages/metadata-protocol/src/seed-loader.ts, the real body; the runtime file is a shim): on a row the load matched by its externalId, create-only columns minus the key fields leave the record before the no-op check and the write, on both the batched and the sequential path; a create-only reference on a matched row is neither resolved nor deferred, so pass 2 cannot write it back; a loaded seed whose columns names no column is logged at error once per dataset. #8740: the seed path never calls driver.upsert, and a key-only patch is a no-op skip, pinned by test. PR is a draft, assigned marchtian; Tier H (skills/** generator rows) so it waits for the seat's contract review and maintainer approval; the auto-requested reviewers are hotlong and os-zhuang.",
      "tests": "Final head 0a22461b, under os-verify-lock (VERDICT command-exit 0): pnpm --filter @objectstack/spec typecheck and pnpm --filter @objectstack/metadata-protocol typecheck green; @objectstack/spec test: Test Files 721 passed (721), Tests 21844 passed, 1 todo; @objectstack/metadata-protocol test: Test Files 263 passed, 3 skipped (266), Tests 21511 passed, 19 skipped. aea8b0d3 (PR head) adds only the changeset title text. Ablations via scripts/ablation-replace.mjs, each red, each restored with blob == HEAD and git diff HEAD empty (the test imports ./seed-loader.js from src, no dist leg): strip of create-only columns removed 9 failed/45; in-loop create-only reference skip removed 2 failed/45; create-only references in declared order 1 failed/46; warnOnUndeclaredSeedColumns call removed 1 failed/46; defineSeed columns-key check removed 3 failed/13; report at warn instead of error 1 failed/46. Rejection tests assert code + path: invalid_value at columns.status.applyOn, unrecognized_keys at columns.status.",
      "gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 0a22461b: 120 commands, all 120 run on that head, exit codes captured before any pipe: 120 x exit 0. --ran over the codes: '120 derived famil(ies) accounted for — 120 run, 0 NOT-MEASURED (a DERIVED zero — all 120 recorded an exit code and none of them is 3)'. Local-only: check:dev-prereqs exit 0, check:skill-frame-freshness exit 0, check:nul-bytes exit 0. PR head aea8b0d3 (changeset text only): derived list unchanged, the 7 changeset families rerun, 7 x exit 0. First pass before the merge and last fixes: check:llms-txt 1, check:quick-reference-counts 1, check:spec-parsed-alias 1 (all fixed in this PR); check:liveness 1 and check:generated 1 (main-red translation/flows.screens row, green after merging main with #25796); check:plugin-teardown-shape --self-test 3 (shallow clone, pinned fixture fetched), check:skill-examples 3, check:dual-build-cjs-loads 3, check:lean-entry-closure 3 (no dist, green once built). CI on aea8b0d3 at report time: 45 check runs, 11 success, 10 skipped, 24 in_progress, 0 failure — in_progress.",
      "dogfood_battery": "n/a — no `!` in the PR title and Clause-② is `yes` with no (narrowing) arm",
      "fable_review": "4 rounds by an isolated Agent on model fable, read-only: round 1 full diff PASS-WITH-NOTES (0 must-fix), round 2 delta PASS-WITH-NOTES (0 must-fix), round 3 delta + merge PASS-WITH-NOTES (0 must-fix, 0 should-fix), round 4 gate-driven delta PASS. Verdict comment: objectstack-ai/objectstack#25813 comment 6399359190.",
      "mcp_calls": "0 GitHub MCP calls. 1 non-GitHub MCP call: claude-code-remote subscribe_pr_activity on #25813 (no write to GitHub content).",
      "api_writes": "3 — POST /repos/objectstack-ai/objectstack/dispatches x3 (fleet-write relay, as objectstack-fleet[bot]): pr_create (PR #25813, body read back identical, 13484 bytes), post-stamped comment on #25813 (6399359190, read back identical), this report on #25761. git push is not a REST write.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: none · noted, not filed — packages/rest/src/import-prepare.ts:620 validates a mapping entry's applyOn with hand-spelled 'always' / 'create' instead of ColumnApplyOnSchema; unexercised drift, the schema has one spelling (PR Acceptance notes)",
        "carrier: none · noted, not filed — a stored seed whose columns key names no column passes SeedSchema and is only logged at error by the loader; result.errors (ReferenceResolutionError, recordIndex int min 0) has no dataset-level shape, so carrying it or refusing it at the stored-seed publish door changes a contract beyond this card (PR Acceptance notes)",
        "carrier: #25796 (landed on main) · noted, not filed — check:liveness red on main 5f5f0b42 (translation/flows.screens -> service-automation engine.ts) made 23 check-liveness.test.ts cases red; fixed on main by #25796 and green here after the merge"
      ],
      "deviations": [
        "File surface: the claim names packages/runtime/src/seed-loader.ts; it is a re-export shim, so the loader change is in packages/metadata-protocol/src/seed-loader.ts (PM assumption 1 refuted).",
        "The import door's enum was inline in packages/spec/src/api/export.zod.ts; it is extracted to packages/spec/src/data/column-apply-on.zod.ts because data/ never imports api/; export.zod.ts changes one import, its emitted JSON schema is unchanged.",
        "Hand-kept counts moved with the new module: packages/spec/llms.txt (206 -> 207, data 30 -> 31), content/docs/getting-started/quick-reference.mdx (data 29 -> 30), and the ADR-0122 pin file packages/spec/src/type-alias-convention.pin.test.ts (790 -> 791).",
        "skills/** touched only by generator-owned rows; the order carried no skills net-add budget.",
        "Main moved 3 commits (#25748, #25789, #25804) after the one merge; git merge-tree is clean, no overlapping file, no sibling card landed, so no second merge.",
        "PR left as the relay's draft: no pr_ready stroke, per os-dev (draft PR is the deliverable; the seat owns the Tier H handoff)."
      ],
      "files_changed": {
        "count": 36,
        "paths": [
          ".changeset/25761-seed-column-apply-on.md",
          "content/docs/data-modeling/seed-data.mdx",
          "content/docs/getting-started/quick-reference.mdx",
          "content/docs/references/api/package-api-assembled.mdx",
          "content/docs/references/api/package-api.mdx",
          "content/docs/references/api/protocol.mdx",
          "content/docs/references/data/column-apply-on.mdx",
          "content/docs/references/data/index.mdx",
          "content/docs/references/data/meta.json",
          "content/docs/references/data/seed-loader.mdx",
          "content/docs/references/data/seed.mdx",
          "content/docs/references/index.mdx",
          "content/docs/references/kernel/manifest.mdx",
          "content/docs/references/kernel/package-registry.mdx",
          "content/docs/references/kernel/package-upgrade.mdx",
          "docs/audits/2026-07-unknown-key-strictness-ledger.counts/data.md",
          "packages/metadata-protocol/src/seed-loader-replay.test.ts",
          "packages/metadata-protocol/src/seed-loader.ts",
          "packages/spec/api-surface/data.json",
          "packages/spec/authorable-defaults/data.json",
          "packages/spec/authorable-surface/data.json",
          "packages/spec/declaration-map/data.json",
          "packages/spec/export-origins/data.json",
          "packages/spec/json-schema.manifest/data.json",
          "packages/spec/liveness/seed.json",
          "packages/spec/liveness/state-counts/seed.md",
          "packages/spec/llms.txt",
          "packages/spec/src/api/export.zod.ts",
          "packages/spec/src/data/column-apply-on.zod.ts",
          "packages/spec/src/data/define-seed-record-keys.test.ts",
          "packages/spec/src/data/index.ts",
          "packages/spec/src/data/seed.test.ts",
          "packages/spec/src/data/seed.zod.ts",
          "packages/spec/src/type-alias-convention.pin.test.ts",
          "skills/objectstack-data/references/_index.md",
          "skills/objectstack-platform/references/_index.md"
        ]
      },
      "line_budget": {
        "total": "+877 / -62 over 36 files",
        "hand_written": "+352 / -25 over 10 files",
        "tests": "+411 / -4 over 4 files",
        "generated": "+114 / -33 over 22 files",
        "skills": "objectstack-data references/_index.md 72 -> 73, objectstack-platform references/_index.md 41 -> 41, all SKILL.md 4383 -> 4383, skills/ tree 13350 -> 13351; no budget in the order"
      }
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    Contributor

    ACCEPT → PR #25813 comment 6399673281 at head aea8b0d3c1ca · seat domain:spec#4 · 2026-10-25T15:43Z. Independent full review PASS (6399359190); contract review PASS on this head (6399466171); CI green. Tier H (skills/** index rows): draft with needs-user-decision until the maintainer merges or an authorized approval lands it.

  5. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    Contributor

    ACCEPT → PR #25813 comment 6400166735 at head b2e758728a04 (supersedes 6399689260) · seat domain:spec#4 · 2026-10-25T16:28Z. Contract review PASS at aea8b0d3 (6399466171) and on the merged head (6399908619); CI green. Tier H: draft with needs-user-decision until the maintainer merges or an authorized approval lands it.

  6. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    Contributor

    Landed · seat domain:spec#4 · 2026-10-26T05:29Z. PR #25813 merged through the queue as merge commit 493a6c4a5f5b. Before the merge it had os-zhuang's governed approval (review 5647036589), seat ACCEPT 6408411003 and delta contract review 6408397262. This card closed completed by Fixes; pm:dispatched is removed. One item goes to the seat's clock-out brief: packages/rest/src/import-prepare.ts:620 hand-spells 'always' / 'create' instead of reading ColumnApplyOnSchema.options. #25779, serialised behind this card, can now be dispatched; this shift does not dispatch it.

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

Metadata

Metadata

Assignees

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchdomain:specenhancementNew feature or requestpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions