pm-csv 2026.10.22 → 2026.10.23
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.
- package/CHANGELOG.md +21 -0
- package/README.md +43 -0
- package/dist/atomic-update.d.ts +3 -2
- package/dist/atomic-update.d.ts.map +1 -1
- package/dist/atomic-update.js +10 -4
- package/dist/atomic-update.js.map +1 -1
- package/dist/edit-refusal.d.ts +21 -0
- package/dist/edit-refusal.d.ts.map +1 -0
- package/dist/edit-refusal.js +21 -0
- package/dist/edit-refusal.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +105 -2
- package/dist/index.js.map +1 -1
- package/dist/lease-guard.d.ts +14 -0
- package/dist/lease-guard.d.ts.map +1 -0
- package/dist/lease-guard.js +25 -0
- package/dist/lease-guard.js.map +1 -0
- package/dist/lineage.d.ts +72 -0
- package/dist/lineage.d.ts.map +1 -0
- package/dist/lineage.js +137 -0
- package/dist/lineage.js.map +1 -0
- package/dist/roundtrip.d.ts +54 -0
- package/dist/roundtrip.d.ts.map +1 -0
- package/dist/roundtrip.js +255 -0
- package/dist/roundtrip.js.map +1 -0
- package/docs/row-lineage-validation.md +49 -0
- package/docs/row-lineage.md +87 -0
- package/docs/spreadsheet-roundtrip.md +146 -0
- package/docs/spreadsheet-validation.md +47 -0
- package/manifest.json +1 -1
- package/package.json +3 -1
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Edit a CSV checkout safely
|
|
2
|
+
|
|
3
|
+
`csv export` and `csv import --key` remain format conversion and keyed upsert.
|
|
4
|
+
For a spreadsheet edit session, use the separate checkout/apply protocol:
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
pm csv checkout --output planning.csv --json
|
|
8
|
+
# Edit title, body, priority, assignee, sprint or release cells in a spreadsheet.
|
|
9
|
+
pm csv apply planning.csv --dry-run --json
|
|
10
|
+
pm csv apply planning.csv --json
|
|
11
|
+
# Identical retry: applied=0, with no additional item/history writes.
|
|
12
|
+
pm csv apply planning.csv
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
For a selection, pass `--ids item-a,item-b` to checkout. The default is a certified
|
|
16
|
+
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
|
|
18
|
+
tracker. Files are UTF-8 comma-separated CSV, with quoted multiline cells, and
|
|
19
|
+
can be saved with CRLF or a BOM. Newline-only LF/CRLF conversions in editable
|
|
20
|
+
cells count as unchanged and retain the exact baseline value, including when a
|
|
21
|
+
concurrent writer has changed that field. Genuine content edits retain their
|
|
22
|
+
normal conflict checks. Keep column names/order and digest cells intact.
|
|
23
|
+
Rows may be reordered or omitted. Omission never deletes an item. Blank optional
|
|
24
|
+
scalar cells unset that metadata; blank body clears it. Priority must be 0..4,
|
|
25
|
+
and title must be nonblank. Scalar edits must be trimmed. Body uses the host's
|
|
26
|
+
canonical trailing-whitespace normalization. Checkout prefixes editable values
|
|
27
|
+
that start with `=`, `+`, `-`, `@`, tab, carriage return or an apostrophe
|
|
28
|
+
with an apostrophe. Apply removes one recognized display prefix before comparison
|
|
29
|
+
and storage, preserving raw baseline values and digests. Keep these prefixes when
|
|
30
|
+
saving the CSV. For new formula-leading or apostrophe-leading values, use the
|
|
31
|
+
same escape convention (for example, `'=SUM(1,2)` or `''literal`). Import all
|
|
32
|
+
columns as text to preserve the literal CSV strings and digests; spreadsheet
|
|
33
|
+
applications can otherwise change values or strip the display escape.
|
|
34
|
+
|
|
35
|
+
Checkout writes `planning.csv` and `planning.csv.session.json` exclusively, with
|
|
36
|
+
owner-only file permissions. Existing files are never overwritten. If CSV
|
|
37
|
+
creation fails after the baseline is written, the refusal explains that an
|
|
38
|
+
incomplete baseline may remain; choose new filenames after checking the files.
|
|
39
|
+
Keep the JSON baseline unchanged, or specify it with `--session baseline.json`.
|
|
40
|
+
The baseline contains exported values, so protect it like the CSV. Digests detect
|
|
41
|
+
accidental alteration and bind the CSV to its baseline; they are not signatures
|
|
42
|
+
or authorization credentials. Tracker permissions and SDK ownership policy still
|
|
43
|
+
apply. Do not edit digest columns to override a conflict.
|
|
44
|
+
|
|
45
|
+
Each row has `_session`, stable `id`, `_revision` (verified history digest),
|
|
46
|
+
`_item_digest` (full document digest), read-only `status`, and a `_digest_<field>`
|
|
47
|
+
column for every editable field. Custom fields, relationships, collections, type,
|
|
48
|
+
and lifecycle transitions are outside this scalar protocol. Use native commands
|
|
49
|
+
for those operations, then export again. In particular, use `pm close` / `pm
|
|
50
|
+
reopen` for status changes. Editing fields on an already terminal keyed item
|
|
51
|
+
preserves its identity, status, completion timestamp and closure evidence.
|
|
52
|
+
This does not change legacy `csv import --key` retry behavior.
|
|
53
|
+
|
|
54
|
+
For each edited field, apply compares **baseline / edited CSV / current tracker**:
|
|
55
|
+
|
|
56
|
+
| Current tracker | Result |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| Equals baseline | Apply the edited value |
|
|
59
|
+
| Equals edited value | Already converged; no item/history write |
|
|
60
|
+
| Differs from both | Conflict; refuse the whole table during preflight |
|
|
61
|
+
| Changed only in an unedited field | Preserve that change and count the row as concurrent |
|
|
62
|
+
|
|
63
|
+
Preflight validates the complete table and all selected items before starting a
|
|
64
|
+
transaction. A malformed, changed or conflicting digest causes zero item/history
|
|
65
|
+
writes and creates no transaction journal. A successful dry run is a preview,
|
|
66
|
+
not a reservation: a native writer can change state immediately afterward.
|
|
67
|
+
The patch rechecks each item while holding the SDK item lock. Every native
|
|
68
|
+
mutation callback used by apply and compensation records a monotonic start time
|
|
69
|
+
and the effective `locks.ttl_seconds`. Before returning (including no-op returns),
|
|
70
|
+
it refuses with typed `csv_lease_expired` when elapsed time reaches the TTL minus
|
|
71
|
+
ten percent, capped at a one-second safety margin. Checkout, prior-document
|
|
72
|
+
capture and representation restoration also check this budget before returning
|
|
73
|
+
or writing. The remedy is: re-run apply; another writer may have changed the item.
|
|
74
|
+
|
|
75
|
+
Run `node --test test/repros/sdk-lease-overrun.ts` for two passing real-SDK
|
|
76
|
+
regressions. A disposable non-git tracker uses a real one-second lease; a newer
|
|
77
|
+
native SDK writer or separate CLI writer takes over while the first callback is
|
|
78
|
+
suspended. The package guard refuses the stale callback and preserves the newer
|
|
79
|
+
value. These cases also run in the normal release suite through
|
|
80
|
+
`test/lease-guard.test.ts`, alongside actual apply and compensation wiring cases.
|
|
81
|
+
|
|
82
|
+
The deadline is a package mitigation, not atomic persistence fencing. The SDK
|
|
83
|
+
reads before entering the callback and persists after its return; arbitrary host
|
|
84
|
+
delays in those windows can exhaust the lease despite the margin. A callback
|
|
85
|
+
check cannot atomically close that later write window. The root fix belongs
|
|
86
|
+
upstream in [pm-cli issue 1549](https://github.com/unbraind/pm-cli/issues/1549),
|
|
87
|
+
verified by the owner on SDK 2026.10.22; the package dependency pin is unchanged.
|
|
88
|
+
|
|
89
|
+
Ordinary failures trigger the existing SDK transaction coordinator and CSV
|
|
90
|
+
full-document compensation; compensation checks for intervening writers before
|
|
91
|
+
restoring and depends on the same host lease contract. Such a late
|
|
92
|
+
failure can append history or leave mutations pending reconciliation: it is not a
|
|
93
|
+
database rollback or a promise of zero history writes after application starts.
|
|
94
|
+
Respect the `csv_edit_apply_failed` remedy and inspect the named transaction.
|
|
95
|
+
Interrupted runs use stable session/table/row identities and can resume without
|
|
96
|
+
retargeting already applied rows. Reusing an old committed session after a newer
|
|
97
|
+
writer changes an edited value is refused; export again.
|
|
98
|
+
|
|
99
|
+
Successful command results are compact TOON by default or JSON with `--json`:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{"rows":2,"changed":1,"unchanged":1,"concurrent":1,"applied":0}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Counts are per row. `changed` is the preflight plan and `applied` counts actual
|
|
106
|
+
mutations in this attempt; convergence after preflight can reduce `applied`.
|
|
107
|
+
`concurrent` includes unedited and convergent concurrent
|
|
108
|
+
changes. An applied batch also returns its transaction ID. Refusals have stable
|
|
109
|
+
codes (`csv_session_invalid`, `csv_edit_invalid`, `csv_edit_conflict`,
|
|
110
|
+
`csv_edit_apply_failed`, `csv_lease_expired`) and remedies. SDK `EditRefusal` exposes `code`, `remedy`,
|
|
111
|
+
`conflicts` and `conflictCount`. Transaction refusals retain nested conflict keys
|
|
112
|
+
and expose the original error as `cause`, including lease expiration, interruption
|
|
113
|
+
and compensation failures. Checkout/apply IO and snapshot read refusals also retain their causes.
|
|
114
|
+
Diagnostics show at most twenty item/field keys,
|
|
115
|
+
with the total count, and never include cell values. Command error messages carry
|
|
116
|
+
the same code, remedy and bounded conflicts; the host controls error rendering.
|
|
117
|
+
|
|
118
|
+
## Typed SDK example
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import { exportEditSession, applyEditSession } from "pm-csv";
|
|
122
|
+
|
|
123
|
+
const { session, table } = await exportEditSession(pmRoot, itemIds);
|
|
124
|
+
const titleColumn = table[0].indexOf("title");
|
|
125
|
+
table[1][titleColumn] = "Reviewed title";
|
|
126
|
+
const preview = await applyEditSession(pmRoot, session, table, {
|
|
127
|
+
author: "spreadsheet-editor", dryRun: true,
|
|
128
|
+
});
|
|
129
|
+
const result = await applyEditSession(pmRoot, session, table, {
|
|
130
|
+
author: "spreadsheet-editor",
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`parseEditSession` validates untrusted JSON before use. `EditSession`,
|
|
135
|
+
`EditSessionRow`, `EditField`, `EditReceipt`, `EditConflict` and `EditRefusalCode`
|
|
136
|
+
are exported types. SDK tables are arrays of CSV string cells; use a CSV codec
|
|
137
|
+
when serializing, rather than joining cells with commas. The SDK accepts an
|
|
138
|
+
optional transaction `onTransition` observer for durable execution diagnostics.
|
|
139
|
+
|
|
140
|
+
## Without git
|
|
141
|
+
|
|
142
|
+
Checkout, conflict checking and application work in a plain tracker initialized
|
|
143
|
+
with `pm init`, including with no `git` executable on PATH. This protocol neither
|
|
144
|
+
invokes Git nor needs branches. It uses the SDK's existing tracker storage,
|
|
145
|
+
ownership checks, item locks, immutable history and transaction journal. Tests
|
|
146
|
+
use real non-git trackers; hosted backend acceptance is separate evidence.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Public tracker copy validation
|
|
2
|
+
|
|
3
|
+
Source: public `unbraind/pm-csv`, main commit
|
|
4
|
+
`1f7e523b6d5539264d3316362665506486eeea3a` (2026.10.21).
|
|
5
|
+
Repository visibility was checked before copying. Only the committed `.agents/pm`
|
|
6
|
+
subtree was archived to a newly created non-git scratch directory. The source
|
|
7
|
+
tracker was read-only throughout; all test mutations targeted the disposable
|
|
8
|
+
copy. No exported tracker content is committed as a fixture.
|
|
9
|
+
|
|
10
|
+
Procedure: `git archive origin/main .agents/pm` into a scratch directory, enumerate
|
|
11
|
+
items with the pinned SDK and explicit settings/type registry, exercise the built
|
|
12
|
+
`dist` SDK, then install the built extension into the scratch tracker's extensions
|
|
13
|
+
directory and invoke the pinned CLI. CLI PATH contained only Node and pm, and
|
|
14
|
+
`GIT_CEILING_DIRECTORIES` excluded ancestor discovery.
|
|
15
|
+
|
|
16
|
+
| Measurement | Result |
|
|
17
|
+
| --- | ---: |
|
|
18
|
+
| Public tracker items exported by SDK and actual CLI | 118 |
|
|
19
|
+
| SDK enumeration warnings | 0 |
|
|
20
|
+
| CSV columns / editable fields | 17 / 6 |
|
|
21
|
+
| Baseline JSON bytes / CSV bytes | 114257 / 97461 |
|
|
22
|
+
| Unedited full-table apply | 118 unchanged, 0 applied |
|
|
23
|
+
| Edited rows (open, in progress, terminal) | 3 |
|
|
24
|
+
| Concurrent unedited-field change | 1 preserved |
|
|
25
|
+
| Dry run | 3 changed, 0 applied |
|
|
26
|
+
| Apply | 3 applied |
|
|
27
|
+
| Identical retry | 3 unchanged, 0 applied |
|
|
28
|
+
| Second editor changing an already edited title | `csv_edit_conflict` |
|
|
29
|
+
| Altered revision digest | `csv_edit_invalid` |
|
|
30
|
+
| Whole-corpus byte/hash comparisons showing zero content changes | 4 |
|
|
31
|
+
| Full-table preview JSON / no-op TOON receipt bytes | 89 / 61 |
|
|
32
|
+
| Measured validation duration (single sample) | 31.32 seconds |
|
|
33
|
+
|
|
34
|
+
The four content comparisons cover unedited import, identical replay,
|
|
35
|
+
conflicting second editor and altered revision digest. Each comparison hashes
|
|
36
|
+
all 118 raw item documents and their immutable history streams, rather than
|
|
37
|
+
trusting command counters. Matching bytes do not prove that no physical write occurred;
|
|
38
|
+
an identical-content rewrite would remain invisible to these comparisons.
|
|
39
|
+
Separate targeted regression tests check that stale preflight creates no
|
|
40
|
+
transaction journal. The original public snapshot and the
|
|
41
|
+
copy contain real heterogeneous lifecycle states; the three edited rows were
|
|
42
|
+
selected by status rather than fabricated as replacement tracker data.
|
|
43
|
+
|
|
44
|
+
The compact receipts contain counts and transaction IDs, not exported values.
|
|
45
|
+
This is bounded filesystem-backend validation, not hosted Postgres/MCP or a
|
|
46
|
+
throughput benchmark. The full npm/Bun release-gate evidence is recorded in the
|
|
47
|
+
PR, using CI to avoid heavy local gates on the saturated host.
|
package/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pm-csv",
|
|
3
|
-
"version": "2026.10.
|
|
3
|
+
"version": "2026.10.23",
|
|
4
4
|
"description": "CSV importer and exporter for pm-cli",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -116,6 +116,8 @@
|
|
|
116
116
|
"docs": [
|
|
117
117
|
"README.md",
|
|
118
118
|
"docs/atomic-compensation.md",
|
|
119
|
+
"docs/spreadsheet-roundtrip.md",
|
|
120
|
+
"docs/row-lineage.md",
|
|
119
121
|
"CHANGELOG.md"
|
|
120
122
|
],
|
|
121
123
|
"examples": [
|