pm-csv 2026.10.23 → 2026.10.24

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.
@@ -0,0 +1,260 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFileSync, writeFileSync } from "node:fs";
3
+ import { getWorkspaceContracts, PmCliError } from "@unbrained/pm-cli/sdk";
4
+ import { IMPORT_COLUMNS, parseCSV, stripBOM, resolveDelimiter, parseFieldMap, resolveImportFieldMap, validateFieldMapTargets, checkMapSourcesPresent, validateParsedCSV, strictValidationIssues, normalizeStatus, importParsedCSV, readBoolOption } from "./index.js";
5
+ /** Expected SDK/CLI refusal with an actionable remedy and semantic usage exit code. */
6
+ export class TableSchemaRefusal extends PmCliError {
7
+ /** Stable category for automated refusal handling. */
8
+ code;
9
+ /** Concrete recovery action required before retrying. */
10
+ remedy;
11
+ constructor(code, remedy) {
12
+ super(`${code}: ${remedy}`, 2, { code, required: remedy, nextSteps: [remedy] });
13
+ this.code = code;
14
+ this.remedy = remedy;
15
+ }
16
+ }
17
+ /** Reject malformed quoting before using the existing parser; inspect only record separators outside quotes. */
18
+ function readTable(text, delimiter) {
19
+ if (Buffer.byteLength(text) > 8 * 1024 * 1024 || delimiter.length !== 1 || /["\r\n\uFEFF]/.test(delimiter)) {
20
+ throw new TableSchemaRefusal("csv_table_invalid", "Use at most 8 MiB of UTF-8 CSV and a single delimiter other than quotes or newlines.");
21
+ }
22
+ const source = stripBOM(text);
23
+ let state = "start";
24
+ const endings = new Set();
25
+ for (let index = 0; index < source.length; index++) {
26
+ const char = source[index];
27
+ if (state === "quoted") {
28
+ if (char === '"') {
29
+ if (source[index + 1] === '"')
30
+ index++;
31
+ else
32
+ state = "after";
33
+ }
34
+ continue;
35
+ }
36
+ if (char === delimiter)
37
+ state = "start";
38
+ else if (char === "\n" || char === "\r") {
39
+ if (char === "\r") {
40
+ if (source[++index] !== "\n")
41
+ throw new TableSchemaRefusal("csv_table_invalid", "Use LF or CRLF record separators; bare CR is unsupported.");
42
+ endings.add("crlf");
43
+ }
44
+ else
45
+ endings.add("lf");
46
+ state = "start";
47
+ }
48
+ else if (char === '"' && state === "start")
49
+ state = "quoted";
50
+ else if (char === '"' || state === "after") {
51
+ throw new TableSchemaRefusal("csv_table_invalid", "Quote complete fields and double embedded quotes; remove characters after closing quotes.");
52
+ }
53
+ else
54
+ state = "plain";
55
+ }
56
+ if (state === "quoted" || endings.size > 1)
57
+ throw new TableSchemaRefusal("csv_table_invalid", "Close every quoted field and use one consistent record separator.");
58
+ const table = parseCSV(source, delimiter);
59
+ const headers = table[0];
60
+ if (!headers?.length || headers.length > 128 || table.length > 10001 ||
61
+ headers.some(header => !header.trim() || header.length > 128) ||
62
+ new Set(headers.map(header => header.trim().toLowerCase())).size !== headers.length ||
63
+ table.some(row => row.length !== headers.length || row.every(cell => !cell.trim()))) {
64
+ throw new TableSchemaRefusal("csv_table_invalid", "Provide 1-128 distinct nonempty headers, at most 10000 data rows, matching row widths and no empty records.");
65
+ }
66
+ return { table, dialect: { delimiter, quote: '"', bom: text.startsWith("\uFEFF"), eol: [...endings][0] ?? "none" } };
67
+ }
68
+ /** Infer a conservative lexical domain over the complete column, never a sample. */
69
+ function inferDomain(values) {
70
+ const nonempty = values.map(value => value.trim()).filter(Boolean);
71
+ const kind = nonempty.length === 0 ? "empty" : nonempty.every(value => /^[+-]?\d+$/.test(value)) ? "integer"
72
+ : nonempty.every(value => /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(value)) ? "decimal"
73
+ : nonempty.every(value => /^(true|false)$/i.test(value)) ? "boolean" : "text";
74
+ return { kind, nullable: nonempty.length < values.length };
75
+ }
76
+ /** Canonical SHA-256 contract identity. Key ordering is reconstructed by the schema reader. */
77
+ function revision(base) {
78
+ return createHash("sha256").update(JSON.stringify(base)).digest("hex");
79
+ }
80
+ /** Read native target enums without activating extensions or invoking git. */
81
+ async function targetContract(pmRoot) {
82
+ const contracts = await getWorkspaceContracts(pmRoot, { noExtensions: true });
83
+ return { types: contracts.types.sort(), statuses: contracts.statuses.sort() };
84
+ }
85
+ /** Project only explicitly mapped columns; ignored canonical-looking headers cannot acquire meaning. */
86
+ function mappedTable(table, columns) {
87
+ const selected = columns.map((column, index) => ({ ...column, index })).filter(column => column.field !== null);
88
+ return [selected.map(column => column.field), ...table.slice(1).map(row => selected.map(column => row[column.index]))];
89
+ }
90
+ /** Reuse strict import checks, then check mapped type/status destinations against the SDK contract. */
91
+ function validateMapping(table, columns, target) {
92
+ const projected = mappedTable(table, columns);
93
+ const report = validateParsedCSV(projected[0], projected.slice(1), {});
94
+ if (strictValidationIssues(report).length || table.slice(1).some(row => columns.some((column, index) => {
95
+ const value = row[index].trim();
96
+ return value !== "" && ((column.field === "type" && !target.types.includes(value)) ||
97
+ (column.field === "status" && !target.statuses.includes(normalizeStatus(value))));
98
+ })))
99
+ throw new TableSchemaRefusal("csv_table_invalid", "Correct missing titles, unsupported types/statuses, priorities outside 0-4 or ambiguous pm mappings, then describe again.");
100
+ }
101
+ /** Produce bounded summary counts without returning table contents. */
102
+ function receipt(schema, rows) {
103
+ const mapped = schema.columns.filter(column => column.field !== null).length;
104
+ return { version: 1, revision: schema.revision, rows, columns: schema.columns.length, mapped, ignored: schema.columns.length - mapped };
105
+ }
106
+ /** Describe and propose a reusable schema without writing tracker state or artifacts. */
107
+ export async function describeTable(pmRoot, text, options = {}) {
108
+ const delimiter = resolveDelimiter(options.delimiter);
109
+ const { table, dialect } = readTable(text, delimiter);
110
+ const headers = table[0].map(header => header.trim().toLowerCase());
111
+ const explicit = options.fieldMap ?? {};
112
+ if (validateFieldMapTargets(explicit).length || checkMapSourcesPresent(headers, explicit).length) {
113
+ throw new TableSchemaRefusal("csv_table_invalid", "Map existing normalized source headers to supported CSV import fields.");
114
+ }
115
+ const { fieldMap } = resolveImportFieldMap(headers, explicit, options.autoMap ?? false);
116
+ const columns = table[0].map((name, index) => {
117
+ const field = fieldMap[headers[index]] ?? headers[index];
118
+ return { name, domain: inferDomain(table.slice(1).map(row => row[index])), field: IMPORT_COLUMNS.includes(field) ? field : null };
119
+ });
120
+ const target = await targetContract(pmRoot);
121
+ validateMapping(table, columns, target);
122
+ const base = { version: 1, dialect, columns, target };
123
+ const schema = { ...base, revision: revision(base) };
124
+ return { schema, receipt: receipt(schema, table.length - 1) };
125
+ }
126
+ /** Narrow an unknown JSON artifact before accessing nested properties. */
127
+ function record(value) {
128
+ return value !== null && typeof value === "object" && !Array.isArray(value);
129
+ }
130
+ /** Validate artifact version, bounds, mapping shape and canonical identity. Unknown versions fail closed. */
131
+ export function parseTableSchema(value) {
132
+ const invalid = new TableSchemaRefusal("csv_schema_invalid", "Use an unchanged version 1 schema produced by csv schema describe; create a new artifact for intentional changes.");
133
+ if (!record(value) || value.version !== 1 || typeof value.revision !== "string" || !record(value.dialect) ||
134
+ !record(value.target) || !Array.isArray(value.columns) || value.columns.length < 1 || value.columns.length > 128)
135
+ throw invalid;
136
+ const { dialect, target } = value;
137
+ if (typeof dialect.delimiter !== "string" || dialect.delimiter.length !== 1 || /["\r\n\uFEFF]/.test(dialect.delimiter) ||
138
+ dialect.quote !== '"' || typeof dialect.bom !== "boolean" || !["lf", "crlf", "none"].includes(String(dialect.eol)) ||
139
+ !Array.isArray(target.types) || !Array.isArray(target.statuses) ||
140
+ [...target.types, ...target.statuses].some(entry => typeof entry !== "string"))
141
+ throw invalid;
142
+ const columns = value.columns.map((column) => {
143
+ if (!record(column) || typeof column.name !== "string" || !column.name.trim() || column.name.length > 128 ||
144
+ !record(column.domain) || !["empty", "integer", "decimal", "boolean", "text"].includes(String(column.domain.kind)) ||
145
+ typeof column.domain.nullable !== "boolean" || (column.field !== null && !IMPORT_COLUMNS.includes(column.field)))
146
+ throw invalid;
147
+ return { name: column.name, domain: { kind: column.domain.kind, nullable: column.domain.nullable }, field: column.field };
148
+ });
149
+ const fields = columns.flatMap(column => column.field === null ? [] : [column.field]);
150
+ if (new Set(columns.map(column => column.name.trim().toLowerCase())).size !== columns.length ||
151
+ new Set(fields).size !== fields.length || !fields.includes("title"))
152
+ throw invalid;
153
+ const base = { version: 1, dialect: { delimiter: dialect.delimiter, quote: '"', bom: dialect.bom, eol: dialect.eol },
154
+ columns, target: { types: [...target.types], statuses: [...target.statuses] } };
155
+ if (revision(base) !== value.revision)
156
+ throw invalid;
157
+ return { ...base, revision: value.revision };
158
+ }
159
+ /** Enforce a saved mapping against the full new table before any tracker mutation. */
160
+ export async function previewTable(pmRoot, text, saved) {
161
+ const schema = parseTableSchema(saved);
162
+ const { table, dialect } = readTable(text, schema.dialect.delimiter);
163
+ if (JSON.stringify(dialect) !== JSON.stringify(schema.dialect) || JSON.stringify(table[0]) !== JSON.stringify(schema.columns.map(column => column.name))) {
164
+ throw new TableSchemaRefusal("csv_schema_changed", "Review changed headers, order or dialect; run csv schema describe into a new artifact and explicitly approve its mappings.");
165
+ }
166
+ for (const [index, column] of schema.columns.entries()) {
167
+ const next = inferDomain(table.slice(1).map(row => row[index]));
168
+ if ((!column.domain.nullable && next.nullable) || (next.kind !== "empty" && column.domain.kind !== "text" && next.kind !== column.domain.kind &&
169
+ !(column.domain.kind === "decimal" && next.kind === "integer"))) {
170
+ throw new TableSchemaRefusal("csv_domain_changed", "Review changed value domains or new empty cells; describe the revised table into a new schema before importing.");
171
+ }
172
+ }
173
+ if (JSON.stringify(await targetContract(pmRoot)) !== JSON.stringify(schema.target)) {
174
+ throw new TableSchemaRefusal("csv_target_changed", "Review tracker type/status changes and describe a new table schema against the current tracker.");
175
+ }
176
+ validateMapping(table, schema.columns, schema.target);
177
+ return receipt(schema, table.length - 1);
178
+ }
179
+ /** Negotiate a captured UTF-8 table then use the existing import mutation/upsert core.
180
+ * Preview never runs mutation hooks. Import retains legacy partial-write semantics on row failures.
181
+ */
182
+ export async function importNegotiatedTable(pmRoot, text, saved, options = {}) {
183
+ const schema = parseTableSchema(saved);
184
+ const { dryRun, keyField } = options;
185
+ const summary = await previewTable(pmRoot, text, schema);
186
+ if (dryRun)
187
+ return { ...summary, imported: 0, updated: 0, skipped: 0 };
188
+ const { table } = readTable(text, schema.dialect.delimiter);
189
+ const result = importParsedCSV(pmRoot, mappedTable(table, schema.columns), { delimiter: schema.dialect.delimiter, fieldMap: {}, dryRun: false, keyField });
190
+ if (result.errors.length)
191
+ throw new TableSchemaRefusal("csv_table_import_failed", `Inspect tracker state and import policies before retrying: imported=${result.imported}, updated=${result.updated}, skipped=${result.skipped}, errors=${result.errors.length}. Use csv import --atomic when rollback is required.`);
192
+ return { ...summary, imported: result.imported, updated: result.updated, skipped: result.skipped };
193
+ }
194
+ /** Read UTF-8 input and optional JSON contract without leaking IO paths in refusals. */
195
+ function readInputs(file, schemaFile) {
196
+ try {
197
+ if (!file)
198
+ throw new Error("missing input");
199
+ const bytes = readFileSync(file);
200
+ const text = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
201
+ return { text, saved: schemaFile === undefined ? undefined : JSON.parse(readFileSync(schemaFile, "utf8")) };
202
+ }
203
+ catch {
204
+ throw new TableSchemaRefusal("csv_schema_io", "Provide readable UTF-8 CSV and, for preview/import, a readable versioned JSON schema.");
205
+ }
206
+ }
207
+ /** Register native host-rendered TOON/JSON commands; full artifacts are written only on explicit request. */
208
+ export function registerTableSchemaCommands(api) {
209
+ api.registerCommand({
210
+ name: "csv schema describe", description: "Describe a bounded UTF-8 table and propose a versioned pm mapping without tracker writes.",
211
+ arguments: [{ name: "file", required: true, description: "UTF-8 CSV input" }],
212
+ flags: [
213
+ { long: "--delimiter", value_name: "char", description: "Delimiter or comma|semicolon|tab|pipe (default comma)" },
214
+ { long: "--map", value_name: "mapping", description: "Normalized Source=field pairs" },
215
+ { long: "--auto-map", description: "Propose existing unambiguous import aliases" },
216
+ { long: "--output", value_name: "file", description: "Exclusively create reusable JSON schema; never overwrite" },
217
+ ],
218
+ async run(ctx) {
219
+ const { text } = readInputs(ctx.args[0]);
220
+ let fieldMap;
221
+ try {
222
+ fieldMap = parseFieldMap(ctx.options["map"]);
223
+ }
224
+ catch {
225
+ throw new TableSchemaRefusal("csv_table_invalid", "Use --map Source=title,Rank=priority with supported import fields.");
226
+ }
227
+ const described = await describeTable(ctx.pm_root, text, { delimiter: ctx.options["delimiter"], fieldMap, autoMap: readBoolOption(ctx.options, "auto-map", "autoMap") });
228
+ const output = ctx.options["output"];
229
+ if (output) {
230
+ try {
231
+ writeFileSync(output, `${JSON.stringify(described.schema, null, 2)}\n`, { flag: "wx", mode: 0o600 });
232
+ }
233
+ catch {
234
+ throw new TableSchemaRefusal("csv_schema_io", "Choose a new writable schema filename; existing artifacts are preserved.");
235
+ }
236
+ }
237
+ return { ...described.receipt, saved: Boolean(output) };
238
+ },
239
+ });
240
+ for (const action of ["preview", "import"])
241
+ api.registerCommand({
242
+ name: `csv schema ${action}`, description: `${action === "preview" ? "Check" : "Import"} a table using an unchanged saved schema; refuse drift before writes.`,
243
+ arguments: [{ name: "file", required: true, description: "UTF-8 table matching the saved dialect" }],
244
+ flags: [
245
+ { long: "--schema", value_name: "file", description: "Required version 1 JSON schema" },
246
+ { long: "--dry-run", description: "Read-only negotiation; no item or history writes" },
247
+ { long: "--key", value_name: "field", description: "Existing import upsert key (import only)" },
248
+ ],
249
+ async run(ctx) {
250
+ const schemaFile = ctx.options["schema"];
251
+ const keyField = ctx.options["key"];
252
+ if (!schemaFile || (keyField !== undefined && !IMPORT_COLUMNS.includes(keyField))) {
253
+ throw new TableSchemaRefusal("csv_schema_invalid", "Provide --schema saved.json and, optionally, a supported import field for --key.");
254
+ }
255
+ const { text, saved } = readInputs(ctx.args[0], schemaFile);
256
+ return { ...await importNegotiatedTable(ctx.pm_root, text, saved, { dryRun: action === "preview" || readBoolOption(ctx.options, "dry-run", "dryRun"), keyField }) };
257
+ },
258
+ });
259
+ }
260
+ //# sourceMappingURL=table-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-schema.js","sourceRoot":"","sources":["../table-schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ # pm-cli 2026.10.20 compatibility
2
+
3
+ Dependabot [PR 177](https://github.com/unbraind/pm-csv/pull/177) updates the
4
+ development host from 2026.10.15 to 2026.10.20. Its original Node 22 and Node 26
5
+ CI jobs failed three tests. The priority-99 row errors in those logs are
6
+ deliberately injected invalid inputs, not unexpected update failures.
7
+
8
+ | Failure | Root cause | Adaptation |
9
+ | --- | --- | --- |
10
+ | Malformed SDK journal identities | The SDK now distinguishes `transaction_journal_invalid` from a valid journal that mismatches the plan. pm-csv depended on the older combined diagnostic and decoded bindings before validating the schema and transaction identity. | Validate both identities before using bindings or entering the coordinator; throw a host-recognized `CommandError`. Keep the original plan-mismatch refusal and prove every fixture byte stays unchanged. |
11
+ | Partial create/update command and importer failures | The tests read `priority` from default brief list rows. `--all` selects statuses, independently of projection, on the new host. | Request `--full` in the verification reads. Keep the exact error counts, continuation, priority and absence assertions. Production complete-list readers already request `--output-include full` and certify completeness. |
12
+ | Legacy CLI parity without git | The same brief-row assumption in a real CLI verification read. | Request `--full`; retain both command spellings, streaming and buffered imports, a PATH without git, dry-run snapshots and nonzero failure assertions. |
13
+
14
+ ## Version evidence
15
+
16
+ Read release notes for 2026.10.17 through 2026.10.20. There is no v2026.10.16
17
+ release; v2026.10.17 covers the source range starting at v2026.10.15.
18
+
19
+ - [2026.10.17](https://github.com/unbraind/pm-cli/releases/tag/v2026.10.17)
20
+ adds SDK bulk creation. Its
21
+ [implementation](https://github.com/unbraind/pm-cli/commit/94757af3016420bfda6b6fc1905be7814ee2ea1d)
22
+ separates invalid journals from plan mismatches in
23
+ `src/sdk/workspace-transaction.ts`.
24
+ - [2026.10.18](https://github.com/unbraind/pm-cli/releases/tag/v2026.10.18)
25
+ documents GH-1489, keeping all-status selection independent of projection.
26
+ - [2026.10.19](https://github.com/unbraind/pm-cli/releases/tag/v2026.10.19)
27
+ adds encoding aliases; [2026.10.20](https://github.com/unbraind/pm-cli/releases/tag/v2026.10.20)
28
+ preserves JSON row facts and compacts default brief TOON envelopes. Neither
29
+ promises priority in a brief list row.
30
+
31
+ Real installed-host probes in fresh non-git trackers confirmed:
32
+
33
+ | Host | Default `list --all --json` priority | With `--full` | Bad journal schema |
34
+ | --- | --- | --- | --- |
35
+ | 2026.10.15 | 2 | 2 | journal does not match the supplied plan |
36
+ | 2026.10.20 | omitted | 2 | journal is invalid (schema version or transaction id does not match the journal file) |
37
+
38
+ Both hosts rejected priority 99 with exit 2 and accepted priority 2. No upstream
39
+ regression was established by these failures, so no new upstream issue draft
40
+ is needed.
41
+
42
+ ## Regression proof
43
+
44
+ The focused command is:
45
+
46
+ ```sh
47
+ npm run build && npm run build:test && node --test --test-name-pattern="malformed SDK journal|partial create/update|legacy CSV CLI" test/atomic-update.test.ts test/commands.test.ts test/legacy-import-cli.test.ts
48
+ ```
49
+
50
+ It passes 3/3 with the pinned host on PATH. The journal test covers malformed
51
+ step shapes, missing/unsupported schemas and missing/foreign transaction ids in
52
+ source and built code. It asserts refusal before coordinator entry and compares
53
+ all fixture file bytes, including item history and the journal itself.
54
+
55
+ Reverting only `loadAtomicBindings` to the PR's original implementation and
56
+ rebuilding makes the journal regression fail 0/1 at the behavioral assertion
57
+ `invalid journal identity must refuse before coordinator entry` (`true !== false`).
58
+ Restoring the function makes it pass. No refusal expectation was weakened.
59
+
60
+ ## Lease fencing assessment (no pin or workaround change)
61
+
62
+ [pm-cli 2026.10.23](https://github.com/unbraind/pm-cli/releases/tag/v2026.10.23)
63
+ includes [GH-1549](https://github.com/unbraind/pm-cli/issues/1549), implemented in
64
+ [PR 1550](https://github.com/unbraind/pm-cli/pull/1550). Native item mutations
65
+ fence their commit against lease takeover and changed item bytes. The public
66
+ `acquireLockLease` primitive also lets extensions fence their own commits.
67
+
68
+ A real unguarded SDK probe with a one-second lease and a newer writer committing
69
+ after expiry overwrote the newer title on 2026.10.20. The same probe on
70
+ 2026.10.23 refused the stale writer and preserved the newer title. This supports
71
+ removing the callback-only `csv_lease_expired` mitigation for native mutation
72
+ paths once the supported host floor is raised and SDK/CLI takeover regressions
73
+ are migrated to the host's typed refusal. Extension-provided lock overrides
74
+ still own their fencing contract; the upstream implementation cannot fence
75
+ those providers automatically. The current peer floor and development pin still
76
+ need the mitigation, so `lease-guard.ts` remains unchanged.
@@ -0,0 +1,123 @@
1
+ # Versioned table schema negotiation
2
+
3
+ A saved version 1 schema binds a UTF-8 table's delimiter, doubled-double-quote
4
+ escaping, BOM presence, record separator, exact ordered headers, full-column
5
+ value domains and proposed pm mappings. It also binds the target types and
6
+ statuses resolved by the native pm SDK. A renamed, reordered, added or removed
7
+ column cannot reuse that mapping. Describe the changed input into a **new**
8
+ artifact and review the mapping before using it.
9
+
10
+ ```csv
11
+ Summary;priority;body
12
+ "Document; negotiation";2;"Quote ""safely"""
13
+ ```
14
+
15
+ ```bash
16
+ pm csv schema describe backlog.csv --delimiter semicolon --auto-map --output backlog.schema.json --json
17
+ pm csv schema preview backlog.csv --schema backlog.schema.json --json
18
+ pm csv schema import backlog.csv --schema backlog.schema.json --dry-run --json
19
+ pm csv schema import backlog.csv --schema backlog.schema.json --key title --json
20
+ ```
21
+
22
+ `describe` proposes the existing import aliases only when `--auto-map` is
23
+ explicit. Alternatively use `--map Summary=title,Rank=priority`. Input map keys
24
+ follow the existing lowercase/trimmed header convention. The saved artifact
25
+ preserves the original exact header spelling, whitespace and order. A field
26
+ outside the supported CSV import surface has `field: null` and is excluded
27
+ from import. Renaming an ignored column still requires review.
28
+
29
+ Without `--output`, describe returns a compact count/revision receipt. With
30
+ `--output`, it exclusively creates a private JSON artifact (mode 0600), never
31
+ overwriting an existing file. Review its `columns[].field` proposals in that
32
+ file; cell values and input paths are absent. The host renders receipts in
33
+ TOON by default and JSON with global `--json`. Receipts stay constant in size
34
+ as row counts grow: version, revision, row/column/mapped/ignored counts, plus
35
+ save or import counts. No row previews are returned.
36
+
37
+ ## Typed SDK
38
+
39
+ ```ts
40
+ import { readFileSync } from "node:fs";
41
+ import {
42
+ describeTable, previewTable, importNegotiatedTable, TableSchemaRefusal,
43
+ } from "pm-csv";
44
+ import type { TableSchema, TableReceipt } from "pm-csv";
45
+
46
+ const tracker = ".agents/pm";
47
+ const text = readFileSync("backlog.csv", "utf8");
48
+ const { schema } = await describeTable(tracker, text, {
49
+ delimiter: ";", fieldMap: { summary: "title" },
50
+ });
51
+ const saved: TableSchema = schema;
52
+ const preview: TableReceipt = await previewTable(tracker, text, saved);
53
+ try {
54
+ await importNegotiatedTable(tracker, text, saved, { keyField: "title" });
55
+ } catch (error) {
56
+ if (error instanceof TableSchemaRefusal) {
57
+ console.error(error.code, error.remedy);
58
+ } else throw error;
59
+ }
60
+ ```
61
+
62
+ Use `parseTableSchema(unknown)` to validate an artifact before reuse. Unsupported
63
+ versions, malformed shapes and changed revisions refuse. The SHA-256 revision
64
+ hashes canonical JSON in the order `version,dialect,columns,target`, with
65
+ dialect keys `delimiter,quote,bom,eol`, column keys `name,domain,field`, domain
66
+ keys `kind,nullable` and target keys `types,statuses`. Top-level JSON key order
67
+ does not matter. This checksum detects accidental edits; it is not an
68
+ authentication signature. Only use reviewed artifacts from trusted sources.
69
+
70
+ ## Domains and refusals
71
+
72
+ Domains inspect **all** cells, never a sample. After trimming, nonempty values
73
+ are classified as integer, decimal, boolean (`true`/`false`, case insensitive),
74
+ text or empty. `nullable` records whether empty cells were observed. Text allows
75
+ arbitrary new strings; decimal permits an integer subset. New empty cells in a
76
+ nonnullable column and other domain widening require a new schema. An all-empty
77
+ column cannot silently become populated. Domains describe lexical input and
78
+ do not promise preservation of whitespace or numerical formatting: negotiated
79
+ import retains existing CSV coercion and trimming semantics.
80
+
81
+ | Code | Remedy |
82
+ |---|---|
83
+ | `csv_table_invalid` | Correct quoting, row widths, mappings, title, priority, type or status. |
84
+ | `csv_schema_invalid` | Restore a valid unchanged version 1 artifact or describe a new one. |
85
+ | `csv_schema_changed` | Review dialect/header/order changes and explicitly adopt a new schema. |
86
+ | `csv_domain_changed` | Review changed domains/nullability and describe again. |
87
+ | `csv_target_changed` | Review changed target types/statuses and describe again. |
88
+ | `csv_schema_io` | Supply readable UTF-8 inputs or a new writable output filename. |
89
+ | `csv_table_import_failed` | Inspect reported counts and tracker policies before retrying. |
90
+
91
+ All package refusals extend the SDK's `PmCliError`, expose `code` and `remedy`,
92
+ and exit 2 with structured context/next steps. Normal SDK tracker-read errors
93
+ retain their native SDK error contract. Schema refusal messages never echo raw
94
+ cell values; mutation diagnostics retain the existing importer's behavior.
95
+
96
+ ## Boundaries
97
+
98
+ The negotiated protocol accepts at most 8 MiB, 128 columns (distinct normalized
99
+ headers of at most 128 characters) and 10000 data rows. It rejects malformed
100
+ quotes, mixed LF/CRLF record separators, bare CR, blank records, ragged rows,
101
+ unknown statuses and invalid priorities. Embedded line endings inside quoted
102
+ cells are preserved by parsing/serialization and do not define record dialect.
103
+ The shared import parser retains an explicit final `""` record even without a
104
+ trailing newline. Quoted and unquoted empty records are validated and refused;
105
+ a single terminating LF or CRLF does not invent an additional record.
106
+ Delimiter aliases are comma, semicolon, tab and pipe. Quoting style may change
107
+ between equivalent valid encodings; escaping rules remain fixed.
108
+
109
+ Describe, preview and import dry-run do not mutate tracker documents, history
110
+ or settings and need no git. Preview checks the table contract and pm field
111
+ values; it does not plan dedup outcomes or execute per-item mutation hooks.
112
+ Import negotiates the complete captured text before calling the existing CSV
113
+ create/update core, so changing the source file afterward cannot change that
114
+ operation's input. The SDK also captures caller-owned schemas and options before
115
+ asynchronous tracker reads. Native history, locking, schema validation, key semantics and
116
+ mutation policies remain authoritative. Negotiation is not a target-schema
117
+ lock: a concurrent policy change can still refuse a row during import.
118
+
119
+ Negotiated import retains legacy partial-write semantics on mutation failures;
120
+ its refusal reports counts. It does not offer atomic resume or compensation.
121
+ The separate `csv import --atomic` command remains available for that workflow.
122
+ Core schema evolution changes tracker contracts; this protocol describes input
123
+ tables. Spreadsheet edit sessions and cell lineage remain separate protocols.
package/manifest.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pm-csv",
3
- "version": "2026.10.23",
3
+ "version": "2026.10.24",
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.23",
3
+ "version": "2026.10.24",
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.15",
71
+ "@unbrained/pm-cli": "2026.10.20",
72
72
  "eslint": "10.13.0",
73
73
  "jiti": "2.7.0",
74
- "jscpd": "5.4.1",
75
- "pm-changelog": "2026.10.16",
76
- "pm-ops": "2026.10.16",
74
+ "jscpd": "5.4.2",
75
+ "pm-changelog": "2026.10.20",
76
+ "pm-ops": "2026.10.20",
77
77
  "typescript": "7.0.2",
78
78
  "yaml": "2.9.1"
79
79
  },
@@ -118,10 +118,12 @@
118
118
  "docs/atomic-compensation.md",
119
119
  "docs/spreadsheet-roundtrip.md",
120
120
  "docs/row-lineage.md",
121
+ "docs/table-schema.md",
121
122
  "CHANGELOG.md"
122
123
  ],
123
124
  "examples": [
124
- "README.md"
125
+ "README.md",
126
+ "docs/table-schema.md"
125
127
  ]
126
128
  },
127
129
  "publishConfig": {