Skip to content

Import the existing duty list through the platform's standard Import - #110

Merged
os-warren merged 1 commit into
mainfrom
claude/issue-19-standard-import
Sep 2, 2026
Merged

Import the existing duty list through the platform's standard Import#110
os-warren merged 1 commit into
mainfrom
claude/issue-19-standard-import

Conversation

@os-warren

Copy link
Copy Markdown
Collaborator

Fixes #19

Duly writes no import code. The Import button already on every object list
is the interface; what was missing was the material that makes "import the list
you already have" demoable — samples shaped to go straight through it, a record
of what it was measured to do, and a guard so the samples cannot rot.

Gates green at 04cab67 (the tree that is HEAD of this branch):
pnpm validate✓ Validation passed (472ms) · pnpm typecheck → clean ·
pnpm testTest Files 30 passed (30) · Tests 749 passed (749) ·
pnpm build✓ Build complete (839ms).

What landed

samples/catalog-items.csv 21 rows → duly_catalog_item. All three forms (recurring 18, standing 2, one-off 1), all seven frequencies, grace 0–21.
samples/duties.csv 19 rows → duly_duty. owner, business_unit and catalog_item written as natural keys.
samples/business-units.csv, samples/people.csv 6 + 12 rows → sys_business_unit, sys_user. The prerequisite the duty file resolves against — see the scope note below.
docs/import/walkthrough.md + 10 screenshots The wizard walked on a clean pnpm dev database.
README.md → "Import your existing list" Three steps, the column rules, the sample links.
test/import-samples.test.ts Every sample header held to the target object's own schema.

All four files describe Ardenline Group — the same fictional manufacturer
pnpm demo seeds, not a second one.

The browser walk

Clean pnpm dev database (rm -rf .objectstack/data), not pnpm demo, so
the walk is a customer's first hour rather than a demo of a demo. Signed in as
the seeded dev admin, driven through the Console's own three-screen wizard.

6 + 12 + 21 + 19 rows created, 0 skipped. Row counts read back through
the API as well as off the screen, and each duty's three lookups checked
against the row they should point at, not merely against a row:

Lifting equipment check — Line C   owner=Yuki Tanabe   bu=Northgate Operations   catalog_item=Lifting equipment check
Monthly site performance note      owner=Tomas Bergh   bu=Northgate Plant        catalog_item=null

How the platform resolves lookup natural keys — the question this card set out to settle

Measured on @objectstack/connector-rest 17.2.0, not assumed:

Column Written as Result
duly_duty.owner Priya Raman (a sys_user name) resolves
duly_duty.owner priya.raman@ardenline.example resolves
duly_duty.business_unit Northgate Quality (the unit's name) resolves
duly_duty.business_unit NGP-QA (the unit's code) does not resolve
duly_duty.catalog_item the item's name resolves
sys_business_unit.parent_business_unit_id the parent's name resolves, within the same file

It is the same rule the seed loader uses. src/data/org.seed.ts resolves
duly_task.owner against sys_user.name; the Import UI matches it, so there is
one format for both paths and nothing to file upstream on this point.

An unresolvable name skips that row and names it, with a Download failed
rows
file to fix and re-import — nothing is linked to a best guess, and an
ambiguous name is reported rather than matched to the first hit.

Two more measured behaviours, both in the walkthrough because both are quiet:

  • A read-only column is never written, and says so in the mapping step.
    sys_user.manager_id shows — Skip —; duly_duty.last_dispatched_period
    shows (match only), the import reports 1 created, and the column reads
    back null.
  • A blank cell leaves the field unset, so the object's conditional cadence
    defaults resolve — which is what lets one flat CSV carry all three duty forms
    without tripping standing_no_frequency.

Scope note — four sample files, not two

#19 asks for two CSVs. There are four, and the extra two are load-bearing
rather than extra scope: on a genuinely clean pnpm dev database there is one
sys_user (Dev Admin) and zero sys_business_unit rows, so duties.csv
imports 0 of 19, one Owner: No matching record per row. That failure is
captured in the walkthrough (08-unresolved-lookup.png) because it is what an
evaluator hits if they skip the ordering — but the card's own acceptance
criterion ("both samples land, row counts match") cannot be met without the
people and units existing first. business-units.csv and people.csv create
them through the same standard Import, so nothing here steps outside the
platform's interface. Happy to drop them and reword the README to "your
directory already has these" if you would rather the repo shipped only the two.

Two smaller judgement calls, both easy to reverse:

  • The README section is titled "Import your existing list" rather than the
    card's 「导入现有清单」 — AGENTS.md rule 8 makes English the source language
    and the README is entirely English. One word to change if the literal
    heading was meant.
  • test/node-builtins.d.ts declares the single readFileSync overload the
    test needs instead of adding @types/node. That follows the precedent
    src/data/index.ts sets for its process.env read; the package's deliberate
    no-@types/node posture is unchanged.

The guard

test/import-samples.test.ts reads the field list off the ObjectSchema
objects themselves — never a hand-copied column list — in two tiers, because
ownership differs. Objects this app declares are held to what the import will
actually write (declared minus readonly, plus: every required writable
field must be present); sys_user and sys_business_unit are held to the
weaker "names a declared field", because the platform's own readonly flag
does not predict what its import writes there (email is readonly and is
written; manager_id is readonly and is dropped).

Reverse-verified rather than assumed. Renaming position_code
position_kode in src/objects/catalog-item.object.ts — mutation confirmed on
disk before reading anything (injected = 1, removed = 0) and restored by an
EXIT trap — turns the suite red in the predicted direction:

FAIL  test/import-samples.test.ts > 'catalog-items.csv' → 'duly_catalog_item'
AssertionError: expected [ 'position_kode' ] to deeply equal []
 Tests  2 failed | 4 passed (6)

No changeset: this repo has none.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SqkTcrxUFci7nqXdbBSe2p


Generated by Claude Code

Duly writes no import code. The Import button already on every object
list is the interface; what was missing was the material that makes it
demoable — sample files shaped to go straight through it, a record of
what it was measured to do, and a guard so the samples cannot rot.

- samples/: four CSVs for Ardenline Group, the same fictional
  manufacturer `pnpm demo` seeds. catalog-items.csv (21 rows, all three
  forms, seven frequencies, grace 0..21) and duties.csv (19 rows, owner
  and business_unit as natural keys) are the list itself;
  business-units.csv and people.csv are the prerequisite the duty file
  resolves against.
- docs/import/walkthrough.md + screenshots: the wizard walked on a clean
  `pnpm dev` database, 6 + 12 + 21 + 19 rows created, 0 skipped.
- README "Import your existing list": three steps, the column rules, the
  sample links.
- test/import-samples.test.ts: every sample header held to the target
  object's own schema, so a renamed field fails the build instead of
  quietly importing a blank column.

Measured, on @objectstack/connector-rest 17.2.0:

- `duly_duty.owner` resolves against a `sys_user` name OR email;
  `business_unit` against a unit's name (its code does not resolve);
  `catalog_item` against the item's name. The same natural-key rule the
  seed loader uses, which is what this card set out to settle.
- An unresolvable name skips that row, names it, and offers the failed
  rows back. On a genuinely clean database duties.csv imports 0 of 19,
  one `Owner: No matching record` per row — hence the ordering the
  README leads with.
- A read-only column is never written and says so in the mapping step:
  `sys_user.manager_id` shows `— Skip —`,
  `duly_duty.last_dispatched_period` shows `(match only)` and reads back
  null after an import reporting success.
- A blank cell leaves the field unset, so the conditional cadence
  defaults resolve: standing rows land with all five null, one-off keeps
  its grace.

`@types/node` stays out of the package; the one function the test needs
is declared narrowly in test/node-builtins.d.ts, the remedy
src/data/index.ts already sets the precedent for.

Part of #19

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqkTcrxUFci7nqXdbBSe2p
@os-warren
os-warren marked this pull request as ready for review September 2, 2026 04:57

Copy link
Copy Markdown
Collaborator Author

Reviewed — merging. Both open questions: A.

Gates, re-run by me on the head merged with current main: validate 0, typecheck 0, test 0 (Test Files 30 passed, Tests 749 passed), build 0. Sample row counts on disk match the report (6 / 12 / 21 / 19); test/import-samples.test.ts reads CatalogItem / Duty off src/objects rather than a hand-copied field list; README carries no IM connector and no import code — the maintainer's constraints hold.

Four CSVs, not two — correct. The card's own acceptance was "both named samples land with matching row counts on a clean database", and on a clean pnpm dev there is one user and zero business units, so duties.csv cannot land at all — the 0 imported / 19 skipped screen you kept is the proof, and it is the honest reason the README's step order is what it is. The two org files add no interface (same Import button, same wizard) and create directory rows, not logins. Keep them.

English heading — correct. AGENTS.md rule 8; the zh-CN bundle is where display text localizes, and a lone Chinese heading in an otherwise English README would be the only one.

On the screenshots being in-repo. Earlier cards were told to keep PR evidence on a throwaway branch. These are different: docs/import/walkthrough.md is a reader-facing document the README links to, and a walkthrough without its images is not a walkthrough. Accepting the ~0.5 MB deliberately; a PR-evidence-only screenshot would still belong off-tree.

The card's open question is settled, and settled the useful way: the Import UI resolves natural keys the same way the seed loader does — owner by sys_user.name (and email), business_unit by name (not by code — measured, NGP-QA refused by name), catalog_item by name, parent_business_unit_id by a parent's name within the same file — and an unresolvable name skips its row, names it, and offers the failed rows for download rather than guessing. Nothing to file upstream. The readonly last_dispatched_period mapping as "(match only)" and reading back null is exactly right and worth having recorded.


Generated by Claude Code

@os-warren
os-warren merged commit 56b6aae into main Sep 2, 2026
1 check passed
os-warren added a commit that referenced this pull request Sep 2, 2026
…numbers

#110 landed `test/node-builtins.d.ts` — a narrow `declare module 'node:fs'`
exposing `readFileSync`, with the reasoning that `@types/node` stays out of a
metadata package. That is the same problem `test/raw-import.d.ts` solved here
with Vite's `?raw`, and one way to read a file in a test beats two, so this
drops the `?raw` declaration and uses the merged shim. Its `URL` overload is
what makes `node:url` unnecessary.

Also stamps every gap in the page header with the issue it was filed as, so a
gap without a number reads as a workaround wearing a comment:

  §1 no record-context filter token            objectui#7297
  §2 related list cannot group                 objectui#7301
  §3 no page-level read-only for a related list objectui#7300
  §4 related-list columns cannot cross a lookup objectui#7301
  §5 `priority` read but not declared           objectui#7298
  §6 related list cannot bind a multi-value field objectui#7299
  §7 `disableDiscussion` read but not declared  objectui#7298

Gates re-run on the merge: validate 0, typecheck 0, test 0 (775 passed /
31 files), build 0.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqkTcrxUFci7nqXdbBSe2p
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

清单导入走平台标准 Import:提供样例 CSV,浏览器走通目录项与职责的导入并留档

1 participant