pm-csv 2026.10.28 → 2026.10.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +86 -2
  2. package/dist/admission.d.ts +93 -0
  3. package/dist/admission.d.ts.map +1 -0
  4. package/dist/admission.js +362 -0
  5. package/dist/admission.js.map +1 -0
  6. package/dist/atomic-update.d.ts.map +1 -1
  7. package/dist/atomic-update.js +16 -14
  8. package/dist/atomic-update.js.map +1 -1
  9. package/dist/edit-refusal.d.ts +1 -1
  10. package/dist/edit-refusal.d.ts.map +1 -1
  11. package/dist/index.d.ts +46 -8
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +364 -117
  14. package/dist/index.js.map +1 -1
  15. package/dist/join-plans.d.ts +110 -0
  16. package/dist/join-plans.d.ts.map +1 -0
  17. package/dist/join-plans.js +557 -0
  18. package/dist/join-plans.js.map +1 -0
  19. package/dist/lineage.d.ts +3 -6
  20. package/dist/lineage.d.ts.map +1 -1
  21. package/dist/lineage.js +32 -11
  22. package/dist/lineage.js.map +1 -1
  23. package/dist/mutation-intent.d.ts +57 -0
  24. package/dist/mutation-intent.d.ts.map +1 -0
  25. package/dist/mutation-intent.js +145 -0
  26. package/dist/mutation-intent.js.map +1 -0
  27. package/dist/roundtrip.d.ts +2 -8
  28. package/dist/roundtrip.d.ts.map +1 -1
  29. package/dist/roundtrip.js +78 -24
  30. package/dist/roundtrip.js.map +1 -1
  31. package/dist/settings-snapshot.d.ts +37 -0
  32. package/dist/settings-snapshot.d.ts.map +1 -0
  33. package/dist/settings-snapshot.js +128 -0
  34. package/dist/settings-snapshot.js.map +1 -0
  35. package/dist/table-schema.d.ts +10 -7
  36. package/dist/table-schema.d.ts.map +1 -1
  37. package/dist/table-schema.js +36 -12
  38. package/dist/table-schema.js.map +1 -1
  39. package/docs/import-admission.md +199 -0
  40. package/docs/join-plans.md +201 -0
  41. package/docs/join-validation.md +240 -0
  42. package/docs/spreadsheet-roundtrip.md +17 -2
  43. package/manifest.json +1 -1
  44. package/package.json +10 -6
@@ -0,0 +1,240 @@
1
+ # Join-plan validation
2
+
3
+ ## Reproducible targeted checks
4
+
5
+ - `npm run build && npm run build:test`
6
+ - `node --test test/join-plans.test.ts`
7
+ - `node --test --test-concurrency=1 test/keyed-atomic.test.ts`
8
+ - `npm run typecheck`
9
+ - `npm run lint`
10
+ - `npm run duplicates`
11
+ - `npm run docstring`
12
+ - `node scripts/validate-join-plans.ts <read-only-public-tracker-copy>`
13
+
14
+ The join state-model test uses seed IDs 1–500 and 4,000 operations over sixteen
15
+ persistent target/reference tracker pairs. The native reference transition is
16
+ create-once at an absent identity, preserve current state on replay, and apply
17
+ explicit native edits through the SDK. All supported mapped scalar fields, body,
18
+ parent and dependency values are read back through the SDK on both sides. No
19
+ hand-written whitespace or field normalization defines the expected state.
20
+ Random custom status registries include active, blocked, done and canceled roles.
21
+ CSV fixtures vary surrounding whitespace and quoted CRLF/LF content. Older plans,
22
+ changed source bytes, native edits and repeated inputs are included.
23
+
24
+ Interleaving tests use one real non-git tracker and native competing writers at
25
+ every emitted forward SDK transaction transition and internal inspected,
26
+ before-dependency and before-create boundary. They cover identical peer success,
27
+ foreign collisions, dependent skips, removed targets, missing durable state,
28
+ interruption, reverse compensation, retained tombstone history and refusal to overwrite peer edits. No SDK mocks are used.
29
+ The built CLI is also run with only Node and pm on PATH, using real JSON and TOON
30
+ rendering. Input and output fuzz cases include sparse arrays, extra keys, wrong
31
+ primitive types, size bounds, nonfinite numbers and fractional/out-of-range values.
32
+
33
+ Behavioral revert: disable only the external-key-to-native-edge resolution in
34
+ `compile`. The three-table regression fails because its SDK-read parent is absent.
35
+ Restoring the resolver restores the pass. This checks persisted behavior rather
36
+ than a parser snapshot or an implementation mirror.
37
+
38
+ ## Public-data measurement
39
+
40
+ Source: public `unbraind/pm-csv` main commit
41
+ `ec5b50f55e68a6b4be95ef50640fdfe158f8273c`, copied to an immutable
42
+ non-git directory. The source repository's public visibility was checked before
43
+ copying. The validator removes Git from PATH, checks complete-list certification,
44
+ checks read-only permissions and hashes every source file before and after.
45
+ Only aggregate results are retained here.
46
+
47
+ | Measurement | Result |
48
+ | --- | ---: |
49
+ | Public tracker items / selected rows | 127 |
50
+ | Selected tables | 3 |
51
+ | Resolved native parent target keys | 19 |
52
+ | Resolved native dependency target keys | 0 |
53
+ | CSV bytes | 40,833 |
54
+ | Compact JSON receipt bytes | 114 |
55
+ | Full source-bearing plan bytes | 46,193 |
56
+ | Coordinates for an injected duplicate | 2 |
57
+ | Tracker writes | 0 |
58
+ | Source hash changed | No |
59
+ | Git executable available | No |
60
+
61
+ For this read-only measurement, parent targets are expressed as explicit
62
+ cross-table dependency lookup columns, allowing each sparse column to name its
63
+ target table. Exact parent semantics and native dependency kinds are verified
64
+ separately by the three-table mutation fixture and native model comparisons.
65
+ This measurement is a preview/import-plan validation; it does not mutate the
66
+ public tracker or certify every older CSV workflow.
67
+
68
+ ## Host boundaries
69
+
70
+ The package retains its pinned pm SDK. Its create lifecycle currently accepts
71
+ `completedAt` only for the configured close status, even when another configured
72
+ status has a terminal role. The package delegates native lifecycle validation to
73
+ that SDK and returns an actionable apply refusal on unsupported native policies.
74
+ The pre-existing item-write lease-fencing limitation is tracked by
75
+ `pm-csv-sdk-write-fence`. Local global-extension health can also report the
76
+ pre-existing pm-github host-version skew (`pm-csv-sdyz`); isolated-global health
77
+ passes. No global installation or dependency pin is changed by this feature.
78
+
79
+ Full release acceptance is supplied by the unchanged npm and Bun CI release
80
+ checks, including every coverage dimension and packed-package acceptance.
81
+
82
+ ## Ambient-state review regressions
83
+
84
+ The shared mutation builder is exercised through joins, lineage, standard CSV,
85
+ streaming CSV, atomic CSV, schema imports, native spreadsheet patches and
86
+ compensation. Creates explicitly suppress ambient defaults; absent or empty keyed-update cells preserve existing state. Native
87
+ patch/restore callbacks preserve unselected current scope under the item lock.
88
+
89
+ The 500-sequence model now randomizes target focus before preview, changes it
90
+ between preview and apply, and changes focus and working directory during retry.
91
+ Each of sixteen non-git tracker/model pairs uses three ambient items and two
92
+ working directories. Source assignment and planning cells alternate between
93
+ empty and explicit values. SDK readback matches the independent reference;
94
+ changing ambient state adds no parent or dependency links. Previously created
95
+ owned records containing unreviewed parent, assignment, planning or resource
96
+ links refuse replay.
97
+
98
+ Refusal regressions cover unterminated quotes, ragged rows, bare/mixed line
99
+ endings, invalid UTF-8/JSON and arbitrary lower-layer failures. Both public join
100
+ APIs always expose `JoinRefusal`, retaining lower refusal codes/remedies and causes.
101
+
102
+ Two behavioral revert proofs were run independently: removing the builder's
103
+ parent unset makes the focus-change regression fail, and removing only the
104
+ public preview refusal wrapper makes the malformed-CSV instance assertion fail.
105
+ Both tests pass when their changed functions are restored. Targeted native
106
+ recovery tests also pass, including compensation and a competing writer.
107
+
108
+ ## Keyed-update column ownership re-review
109
+
110
+ Legacy keyed updates now own only nonempty supported CSV cells. Absent headers
111
+ and empty cells preserve current values, native links and dependency edges;
112
+ `none`/`null` retain native explicit-clear semantics for supported nullable scope fields.
113
+ Creates still unset absent scope through the shared builder. Body cells retain
114
+ source whitespace until the SDK applies its native normalization. README and
115
+ import command help describe this ownership contract, additive tags/blockers,
116
+ and export-only custom fields.
117
+
118
+ `test/column-ownership.test.ts` reproduces the title-only review case and tests
119
+ all twelve supported import headers as keys, plus native ID, foreign and custom
120
+ keys. Its deterministic seeds 1–500 each perform four transitions: a random
121
+ subset import, an unchanged native/custom-field export/import, explicit edits,
122
+ and replay of an older input. Standard, streaming and atomic mutations use real
123
+ non-git trackers with only Node and the pinned pm executable on PATH. Reference
124
+ state evolves independently through native SDK updates. The model reads complete
125
+ SDK documents, including custom objects and resource links; comparisons exclude
126
+ only generated mutation/edge clocks. Silent dependency records also receive a
127
+ byte-for-byte assertion, including their original provenance and timestamps.
128
+ Atomic transaction ownership tags are modeled explicitly with the public
129
+ transaction-ID API. No source-value normalization is implemented in the oracle.
130
+
131
+ The behavioral revert replaces only `appendRelationalArgs` with the clearing
132
+ implementation from the previous head. The fixed review case fails the silence
133
+ law, losing parent, assignment, planning values and the dependency edge. Restoring
134
+ the changed function makes that regression pass.
135
+
136
+ ## Native clear-alias review
137
+
138
+ The intent builder recognizes both native `none` and `null` command aliases for
139
+ all eight scope scalars and dependency/file/doc/test collections, including case
140
+ and surrounding-whitespace variants. It emits explicit SDK unsets/clear flags;
141
+ stored snapshots preserve literal strings and omitted patch fields remain silent.
142
+
143
+ `test/scope-aliases.test.ts` exhaustively compares every scalar and linked
144
+ collection against an independent native SDK create/update oracle in non-git
145
+ trackers. It checks snapshots and silent patches, joins and retries, lineage
146
+ replay, standard/streaming/atomic/schema imports, and real JSON/TOON CLI receipts.
147
+ The existing 500-seed ambient-state model also varies assignment/planning clear
148
+ aliases. Packed npm-current, Bun-current and minimum-host acceptance exercise
149
+ join apply/retry and lineage clear aliases, then read scope through each installed
150
+ host's SDK. Mixed alias/concrete collection inputs retain native refusal behavior.
151
+
152
+
153
+ Native input replay uses the public SDK parent-reference and relationship-ID
154
+ normalizers, sprint/release validation and document canonicalization. Every shared
155
+ scope comparison receives the tracker settings explicitly. Raw parent aliases are
156
+ never compared directly with persisted IDs, and expected dependency IDs use the
157
+ same SDK reference normalizer. Older supported hosts that store trimmed parent
158
+ spellings use their public parent-value normalizer; dependency IDs still follow
159
+ that host's ID normalization. Packed oracles compare against native create/get
160
+ on each installed host, including the minimum version. Edit sessions canonicalize their desired stored
161
+ values with `canonicalDocument`, including leading blank body lines; they do not
162
+ interpret command clear aliases.
163
+
164
+ `test/native-replay.test.ts` covers the exact bare-parent atomic retry, all twelve
165
+ legacy import fields across atomic/standard/stream/schema paths, native remote
166
+ locators, hash/case/prefix variants, whitespace, Unicode and sentinels. Seeds
167
+ 1..500 import and replay both joins and lineage (1000 successful replayed rows),
168
+ comparing full SDK documents and immutable histories with their post-import model.
169
+ The installed npm/Bun/minimum-host acceptance also repeats an atomic bare-parent
170
+ import in JSON and TOON and replays a body edit, checking native SDK reads/history.
171
+ `test/keyed-atomic.test.ts` exercises 500 deterministic prototype-name mapping
172
+ permutations and real SDK paths for type/status/field refusals, headers, external
173
+ keys, references, lineage, table contracts and export. A valid first row followed
174
+ by an inherited type or status refuses before transaction writes, including a
175
+ validly rehashed adversarial plan. Failure injection before and after persistence
176
+ at each row proves no owned live items remain. Recovery covers each compensation
177
+ phase, native item-lock contention, peer revisions and foreign replacements.
178
+ SDK-derived transaction identities also match the minimum-host fallback; legacy
179
+ journals keep their original identities. Serialized mapping snapshots retain their
180
+ public plain-record shape, with own-property reads at every consumption site.
181
+
182
+ ## Settings snapshot review regression
183
+
184
+ `test/settings-snapshot.test.ts` changes a custom type's default status at every
185
+ boundary from before row one through after row four. The join API must return
186
+ `csv_join_settings_changed`, remove every unchanged owned materialization, and
187
+ retain native tombstones. A fresh plan's defaults are compared with native SDK
188
+ creates read back through the SDK. Five hundred deterministic type/status cases
189
+ also compare snapshot resolution against native SDK oracles.
190
+
191
+ The tests cover all reviewed API refusal families, edits and atomic imports at
192
+ each following-row boundary, concurrent/nested snapshot scopes, settings writers
193
+ contending with standalone batches, post-persistence settings changes, cause
194
+ graphs and input ownership before SDK reads. Relative tracker roots stay fixed
195
+ across ambient working-directory changes. Existing SIGKILL recovery and caller-
196
+ mutated table-schema tests remain intact; these caught and verified fixes to an
197
+ overlong auxiliary lease and deferred input capture during implementation.
198
+
199
+ Reproduce with `node --test --test-concurrency=1 test/settings-snapshot.test.ts`.
200
+ Installed npm, Bun and minimum-host acceptance also changes settings between
201
+ join rows, checks compensation and stale-plan refusal, previews fresh defaults,
202
+ and verifies real JSON/TOON `csv_join_settings_changed` CLI output.
203
+
204
+ ## Canonical identity review regression
205
+
206
+ `test/native-identities.test.ts` uses the SDK normalizer for 500 identity families
207
+ across configured prefixes. Validly rehashed plans with bare/canonical collisions
208
+ at every row position refuse before any target lease or journal, including replay;
209
+ the complete tracker byte inventory stays identical. Canonical plans still import
210
+ and replay, and native items written before the change remain readable. Checkout
211
+ selection and rehashed edit sidecars enforce the same shared identity guard.
212
+
213
+ Every row also fails before and after persistence in one SDK transaction, followed
214
+ by replay. All SDK-visible live documents remain byte-identical to the baseline,
215
+ foreign history is unchanged, and no owned live item remains. Native transaction
216
+ audit/delete history is intentionally retained: the SDK documents each step as an
217
+ append-only compensation and does not erase histories for physical directory
218
+ rollback. Existing tombstone/history regressions remain unchanged.
219
+
220
+ Installed npm, Bun and minimum-SDK consumers exercise colliding plans through
221
+ both the SDK and real JSON/TOON CLI, and fail before/after each row's persistence.
222
+ Reproduce with `node --test --test-concurrency=1 test/native-identities.test.ts`.
223
+
224
+ ## Stable recovery identity
225
+
226
+ `test/plan-recovery.test.ts` interrupts every cleanup boundary, then changes each
227
+ future target's eligibility and the all-foreign subset. The retry uses the same
228
+ native transaction, completes compensation, preserves peer records/history and
229
+ does not create a second committed journal or report owned leftovers unchanged.
230
+ Both historical subset-key formats recover their recorded step lists. A seeded
231
+ committed fork cannot supersede pending cleanup, and a receipt appearing between
232
+ preflight and SDK writer coordination refuses before forward rows.
233
+
234
+ Spreadsheet tests cover convergent cells over applying and compensating journals,
235
+ dry-run and compensated retries. Atomic imports refuse empty, different and wider
236
+ filter selections over the recorded row domain; recovery stops after rollback
237
+ instead of adopting a cached pre-compensation item ID. Native audit history is
238
+ retained throughout. Unrelated/malformed journal fixtures remain untouched.
239
+ Installed npm, Bun and minimum-SDK acceptance repeats the join recovery scenario
240
+ through real JSON/TOON CLI commands and verifies the same journal is compensated.
@@ -1,5 +1,17 @@
1
1
  # Edit a CSV checkout safely
2
2
 
3
+ Each export/apply attempt captures one native settings/schema snapshot. Changes
4
+ during apply refuse with `csv_edit_settings_changed`; earlier patches use the
5
+ existing SDK transaction compensation. Inputs are copied before asynchronous
6
+ reads, and each mutation batch revalidates the settings digest inside writer
7
+ coordination. Inspect pending cleanup before exporting a fresh session.
8
+
9
+ A table whose cells already match the desired values still enters native journal
10
+ recovery when an attempt is unfinished. Pending compensation restores the earlier
11
+ state and returns `csv_edit_apply_failed`; it cannot be bypassed by an unchanged
12
+ receipt. A compensated edit requires a fresh checkout. Dry-run preserves pending
13
+ cleanup and reports the same refusal rather than claiming recovery.
14
+
3
15
  `csv export` and `csv import --key` remain format conversion and keyed upsert.
4
16
  For a spreadsheet edit session, use the separate checkout/apply protocol:
5
17
 
@@ -14,7 +26,10 @@ pm csv apply planning.csv
14
26
 
15
27
  For a selection, pass `--ids item-a,item-b` to checkout. The default is a certified
16
28
  complete tracker read. A checkout accepts up to 1000 distinct canonical item IDs;
17
- split larger selections. These examples use illustrative IDs; use IDs from your
29
+ split larger selections. Checkout and apply validate all IDs with the SDK prefix
30
+ before reading targets: bare, case or whitespace aliases and normalized duplicates
31
+ refuse as `csv_session_invalid`. Saved identities are never silently rewritten.
32
+ These examples use illustrative IDs; use IDs from your
18
33
  tracker. Files are UTF-8 comma-separated CSV, with quoted multiline cells, and
19
34
  can be saved with CRLF or a BOM. Newline-only LF/CRLF conversions in editable
20
35
  cells count as unchanged and retain the exact baseline value, including when a
@@ -23,7 +38,7 @@ normal conflict checks. Keep column names/order and digest cells intact.
23
38
  Rows may be reordered or omitted. Omission never deletes an item. Blank optional
24
39
  scalar cells unset that metadata; blank body clears it. Priority must be 0..4,
25
40
  and title must be nonblank. Scalar edits must be trimmed. Body uses the host's
26
- canonical trailing-whitespace normalization. Checkout prefixes editable values
41
+ canonical document normalization, which removes leading blank body lines and trailing whitespace. Checkout prefixes editable values
27
42
  that start with `=`, `+`, `-`, `@`, tab, carriage return or an apostrophe
28
43
  with an apostrophe. Apply removes one recognized display prefix before comparison
29
44
  and storage, preserving raw baseline values and digests. Keep these prefixes when
package/manifest.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pm-csv",
3
- "version": "2026.10.28",
3
+ "version": "2026.10.30",
4
4
  "description": "CSV importer and exporter for pm-cli",
5
5
  "author": "@unbraind",
6
6
  "entry": "./dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pm-csv",
3
- "version": "2026.10.28",
3
+ "version": "2026.10.30",
4
4
  "description": "CSV importer and exporter for pm-cli",
5
5
  "repository": {
6
6
  "type": "git",
@@ -68,12 +68,12 @@
68
68
  "@babel/eslint-parser": "8.0.7",
69
69
  "@babel/plugin-syntax-typescript": "8.0.3",
70
70
  "@types/node": "26.6.5",
71
- "@unbrained/pm-cli": "2026.10.24",
71
+ "@unbrained/pm-cli": "2026.10.25",
72
72
  "eslint": "10.13.0",
73
73
  "jiti": "2.7.0",
74
74
  "jscpd": "5.4.2",
75
- "pm-changelog": "2026.10.24",
76
- "pm-ops": "2026.10.24",
75
+ "pm-changelog": "2026.10.25",
76
+ "pm-ops": "2026.10.25",
77
77
  "typescript": "7.0.2",
78
78
  "yaml": "2.9.1"
79
79
  },
@@ -122,11 +122,15 @@
122
122
  "docs/spreadsheet-roundtrip.md",
123
123
  "docs/row-lineage.md",
124
124
  "docs/table-schema.md",
125
- "CHANGELOG.md"
125
+ "CHANGELOG.md",
126
+ "docs/join-plans.md",
127
+ "docs/import-admission.md"
126
128
  ],
127
129
  "examples": [
128
130
  "README.md",
129
- "docs/table-schema.md"
131
+ "docs/table-schema.md",
132
+ "docs/join-plans.md",
133
+ "docs/import-admission.md"
130
134
  ]
131
135
  },
132
136
  "publishConfig": {