Import the existing duty list through the platform's standard Import - #110
Conversation
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
Reviewed — merging. Both open questions: A.Gates, re-run by me on the head merged with current 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 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: 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 — Generated by Claude Code |
…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
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 test→Test Files 30 passed (30) · Tests 749 passed (749)·pnpm build→✓ Build complete (839ms).What landed
samples/catalog-items.csvduly_catalog_item. All three forms (recurring 18, standing 2, one-off 1), all seven frequencies, grace 0–21.samples/duties.csvduly_duty.owner,business_unitandcatalog_itemwritten as natural keys.samples/business-units.csv,samples/people.csvsys_business_unit,sys_user. The prerequisite the duty file resolves against — see the scope note below.docs/import/walkthrough.md+ 10 screenshotspnpm devdatabase.README.md→ "Import your existing list"test/import-samples.test.tsAll four files describe Ardenline Group — the same fictional manufacturer
pnpm demoseeds, not a second one.The browser walk
Clean
pnpm devdatabase (rm -rf .objectstack/data), notpnpm demo, sothe 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 throughthe 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:
How the platform resolves lookup natural keys — the question this card set out to settle
Measured on
@objectstack/connector-rest17.2.0, not assumed:duly_duty.ownerPriya Raman(asys_username)duly_duty.ownerpriya.raman@ardenline.exampleduly_duty.business_unitNorthgate Quality(the unit's name)duly_duty.business_unitNGP-QA(the unit's code)duly_duty.catalog_itemsys_business_unit.parent_business_unit_idIt is the same rule the seed loader uses.
src/data/org.seed.tsresolvesduly_task.owneragainstsys_user.name; the Import UI matches it, so there isone 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:
sys_user.manager_idshows— Skip —;duly_duty.last_dispatched_periodshows
(match only), the import reports1 created, and the column readsback
null.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 devdatabase there is onesys_user(Dev Admin) and zerosys_business_unitrows, soduties.csvimports 0 of 19, one
Owner: No matching recordper row. That failure iscaptured in the walkthrough (
08-unresolved-lookup.png) because it is what anevaluator 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.csvandpeople.csvcreatethem 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:
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.tsdeclares the singlereadFileSyncoverload thetest needs instead of adding
@types/node. That follows the precedentsrc/data/index.tssets for itsprocess.envread; the package's deliberateno-
@types/nodeposture is unchanged.The guard
test/import-samples.test.tsreads the field list off theObjectSchemaobjects 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 writablefield must be present);
sys_userandsys_business_unitare held to theweaker "names a declared field", because the platform's own
readonlyflagdoes not predict what its import writes there (
emailisreadonlyand iswritten;
manager_idisreadonlyand is dropped).Reverse-verified rather than assumed. Renaming
position_code→position_kodeinsrc/objects/catalog-item.object.ts— mutation confirmed ondisk before reading anything (
injected = 1,removed = 0) and restored by anEXITtrap — turns the suite red in the predicted direction:No changeset: this repo has none.
🤖 Generated with Claude Code
https://claude.ai/code/session_01SqkTcrxUFci7nqXdbBSe2p
Generated by Claude Code