@davidbalzan/groundwork-seam 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 David Balzan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # @davidbalzan/groundwork-seam
2
+
3
+ Schema owner of `docs/QUEUE.md`, `docs/DONE.md`, `docs/WORKSTREAMS.md`, `docs/FACTS.md`, plus typed protocol records.
4
+
5
+ Markdown stays authoritative. This package is a library, not a daemon.
6
+
7
+ PR2 parse contract: any markdown table is a board, arity 5. Header-cell detection is PR3.
package/dist/docs.d.ts ADDED
@@ -0,0 +1,125 @@
1
+ import { EM_DASH_SEP, MIDDOT_SEP } from "./glyphs.js";
2
+ import { SCHEMA, type WorkstreamsExtId, type WorkstreamsExtKey } from "./schema.js";
3
+ export type Priority = "P1" | "P2" | "P3";
4
+ export type QueueItem = {
5
+ id: string;
6
+ priority: Priority;
7
+ text: string;
8
+ done: boolean;
9
+ section?: string;
10
+ };
11
+ export type DoneEntry = {
12
+ id: string;
13
+ text: string;
14
+ ref?: string;
15
+ date?: string;
16
+ section?: string;
17
+ };
18
+ export type BoardRow = {
19
+ id: string;
20
+ lane: string;
21
+ owner: string;
22
+ state: string;
23
+ currentSlice: string;
24
+ nextGo: string;
25
+ raw?: string;
26
+ };
27
+ export declare const BOARD_ARITY = 5;
28
+ export type MalformedRow = {
29
+ malformed: true;
30
+ verbatim: string;
31
+ issue: string;
32
+ };
33
+ export type AnyBoardRow = BoardRow | WorkstreamsV1Row | ExtensionRow | MalformedRow;
34
+ export declare function isMalformedRow(r: AnyBoardRow): r is MalformedRow;
35
+ export type WorkstreamsV1Row = {
36
+ id: string;
37
+ stream: string;
38
+ owner: string;
39
+ branchWorktree: string;
40
+ status: string;
41
+ blocker: string;
42
+ lastNote: string;
43
+ raw?: string;
44
+ };
45
+ export declare function isWorkstreamsV1Row(r: AnyBoardRow): r is WorkstreamsV1Row;
46
+ export type BoardSchema = typeof SCHEMA.workstreams | typeof SCHEMA.workstreamsLanesV0 | WorkstreamsExtId;
47
+ export type ExtensionRow = {
48
+ extension: WorkstreamsExtKey;
49
+ cells: string[];
50
+ raw?: string;
51
+ };
52
+ export declare function isExtensionRow(r: AnyBoardRow): r is ExtensionRow;
53
+ export type Block = {
54
+ kind: "text";
55
+ lines: string[];
56
+ } | {
57
+ kind: "queue";
58
+ items: QueueItem[];
59
+ } | {
60
+ kind: "done";
61
+ entries: DoneEntry[];
62
+ } | {
63
+ kind: "board";
64
+ header: string;
65
+ align: string;
66
+ schema?: BoardSchema;
67
+ rows: (BoardRow | WorkstreamsV1Row | ExtensionRow | MalformedRow)[];
68
+ } | {
69
+ kind: "decisions";
70
+ items: string[];
71
+ };
72
+ export type WorkDoc = {
73
+ trailingNewline: boolean;
74
+ blocks: Block[];
75
+ warnings?: string[];
76
+ };
77
+ export { EM_DASH_SEP, MIDDOT_SEP };
78
+ export declare function parseQueueLine(line: string, section?: string): QueueItem | null;
79
+ export declare function parseDoneLine(line: string, section?: string): DoneEntry | null;
80
+ export declare function renderQueueLine(item: QueueItem): string;
81
+ export declare function renderDoneLine(entry: DoneEntry): string;
82
+ export declare function renderBoardRow(r: BoardRow | WorkstreamsV1Row | ExtensionRow | MalformedRow): string;
83
+ export declare function parseWorkDoc(source: string): WorkDoc;
84
+ export declare function renderWorkDoc(doc: WorkDoc): string;
85
+ export declare function queueItemsOf(doc: WorkDoc): QueueItem[];
86
+ export declare function doneEntriesOf(doc: WorkDoc): DoneEntry[];
87
+ export declare function boardRowsOf(doc: WorkDoc): BoardRow[];
88
+ export declare function workstreamsV1RowsOf(doc: WorkDoc): WorkstreamsV1Row[];
89
+ export type WorkstreamsExtensions = {
90
+ openPrs: Record<string, string>[];
91
+ cutoverGates: Record<string, string>[];
92
+ needsDavid: Record<string, string>[];
93
+ risks: Record<string, string>[];
94
+ rooms: Record<string, string>[];
95
+ decisions: string[];
96
+ };
97
+ export declare function workstreamsExtensionsOf(doc: WorkDoc): WorkstreamsExtensions;
98
+ export type ListWorkBoardRow = {
99
+ schema: typeof SCHEMA.workstreams;
100
+ id: string;
101
+ stream: string;
102
+ owner: string;
103
+ branchWorktree: string;
104
+ status: string;
105
+ blocker: string;
106
+ lastNote: string;
107
+ } | {
108
+ schema: typeof SCHEMA.workstreamsLanesV0;
109
+ id: string;
110
+ lane: string;
111
+ owner: string;
112
+ state: string;
113
+ currentSlice: string;
114
+ nextGo: string;
115
+ };
116
+ export declare function listWorkBoardOf(doc: WorkDoc): ListWorkBoardRow[];
117
+ export declare function projectV1ToLanes(row: ListWorkBoardRow): Extract<ListWorkBoardRow, {
118
+ schema: typeof SCHEMA.workstreamsLanesV0;
119
+ }>;
120
+ export declare function workDocIssues(doc: WorkDoc): string[];
121
+ export declare const LANES_V0_WRITE_ISSUE = "workstreams.lanes-v0 is parse-only \u2014 new writes must use workstreams.v1 (6-col Active Streams)";
122
+ /** 5-col Lanes still parses; this is the write-grammar refusal for doctor / export. */
123
+ export declare function workDocLegacyWriteIssues(doc: WorkDoc): string[];
124
+ /** Alias used in the design: MCP BoardRow is lanes-v0. */
125
+ export type LanesV0Row = BoardRow;
package/dist/docs.js ADDED
@@ -0,0 +1,494 @@
1
+ // Work state as data (Phase 8 Task 5): queue items, done entries and board
2
+ // rows as typed records, with markdown as a first-class EXPORT rather than the
3
+ // datastore.
4
+ //
5
+ // WHY THIS SHAPE. `docs/QUEUE.md` / `docs/DONE.md` were parsed under a contract
6
+ // requiring exact `—` (U+2014) and ` · ` (U+00B7). A parser once returned ZERO
7
+ // items on the real file while passing every synthetic test, and the consuming
8
+ // UI rendered an empty panel — no error, no signal. The files themselves are
9
+ // good: David edits them, git diffs them, they grep. So markdown stays the
10
+ // INTERFACE and stops being the DATASTORE.
11
+ //
12
+ // MARKDOWN REMAINS AUTHORITATIVE. Delete the store and the documents still
13
+ // stand alone: every field here is recoverable from the file it came from, the
14
+ // exporter is byte-exact, and nothing downstream is required to read records.
15
+ // The store is a derived index until a consumer chooses otherwise.
16
+ //
17
+ // The round-trip guarantee is structural, not incidental: everything this
18
+ // module does NOT model — prose, the write-rule paragraphs, the Cross-project
19
+ // section, fenced examples — is captured verbatim as a `text` block and
20
+ // replayed in place. Only lines it genuinely understands are re-rendered, so
21
+ // an unmodelled construct can never be silently dropped.
22
+ import { createHash } from "node:crypto";
23
+ import { EM_DASH_SEP, MIDDOT_SEP } from "./glyphs.js";
24
+ import { LANES_V0_HEADERS, SCHEMA, WORKSTREAMS_EXT, WORKSTREAMS_V1_ARITY, WORKSTREAMS_V1_HEADERS, } from "./schema.js";
25
+ // The one arity BoardRow can hold. boardCells() and the parser's arity guard
26
+ // both reference this constant so the two sides cannot drift; a test pins
27
+ // boardCells().length === BOARD_ARITY.
28
+ export const BOARD_ARITY = 5;
29
+ export function isMalformedRow(r) {
30
+ return r.malformed === true;
31
+ }
32
+ export function isWorkstreamsV1Row(r) {
33
+ return !isMalformedRow(r) && "stream" in r;
34
+ }
35
+ export function isExtensionRow(r) {
36
+ return !isMalformedRow(r) && "extension" in r && "cells" in r;
37
+ }
38
+ // ---------- the glyph contract ----------
39
+ //
40
+ // Pinned in docs/DONE.md and consumed by external parsers: the ref splits on
41
+ // the LAST ` — ` (space, U+2014, space) and the date is a trailing ` · `
42
+ // (space, U+00B7, space) + ISO date. Written as explicit escapes so a source
43
+ // file normalization or a copy-paste through an ASCII-mangling tool can never
44
+ // change them silently — the constants are the contract.
45
+ export { EM_DASH_SEP, MIDDOT_SEP };
46
+ const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
47
+ // Deterministic ids: same content in → same id out, so import→export→import is
48
+ // stable and no id has to be written into the markdown (which would change the
49
+ // bytes the round trip is measured on).
50
+ function idFor(kind, seed) {
51
+ return `${kind}-${createHash("sha1").update(seed).digest("hex").slice(0, 8)}`;
52
+ }
53
+ // ---------- line-level recognizers ----------
54
+ const QUEUE_LINE = /^- \[( |x)\] \((P1|P2|P3)\) (.*)$/;
55
+ const DONE_LINE = /^- \[( |x)\] (.*)$/;
56
+ export function parseQueueLine(line, section) {
57
+ const m = QUEUE_LINE.exec(line);
58
+ if (!m)
59
+ return null;
60
+ const [, mark, priority, text] = m;
61
+ return {
62
+ id: idFor("q", text ?? ""),
63
+ priority: priority,
64
+ text: text ?? "",
65
+ done: mark === "x",
66
+ ...(section ? { section } : {}),
67
+ };
68
+ }
69
+ // Split on the LAST ` — `, then peel a trailing ` · YYYY-MM-DD`. Both parts are
70
+ // optional: an entry with neither is still a done entry, it just carries no
71
+ // verifiable ref — which is a fact about the entry, not a parse failure. The
72
+ // old parser treated the same line as nothing at all.
73
+ export function parseDoneLine(line, section) {
74
+ const m = DONE_LINE.exec(line);
75
+ if (!m)
76
+ return null;
77
+ let body = m[2] ?? "";
78
+ let ref;
79
+ let date;
80
+ const cut = body.lastIndexOf(EM_DASH_SEP);
81
+ if (cut !== -1) {
82
+ const tail = body.slice(cut + EM_DASH_SEP.length);
83
+ const dot = tail.lastIndexOf(MIDDOT_SEP);
84
+ if (dot !== -1) {
85
+ const maybeDate = tail.slice(dot + MIDDOT_SEP.length);
86
+ if (ISO_DATE.test(maybeDate)) {
87
+ date = maybeDate;
88
+ ref = tail.slice(0, dot);
89
+ }
90
+ }
91
+ // A tail with no date is still a ref, as long as it isn't prose with
92
+ // spaces — refs are `owner/repo#N` or `owner/repo@sha`.
93
+ if (date === undefined && /^\S+$/.test(tail))
94
+ ref = tail;
95
+ if (ref !== undefined)
96
+ body = body.slice(0, cut);
97
+ }
98
+ return {
99
+ id: idFor("d", `${body}|${ref ?? ""}|${date ?? ""}`),
100
+ text: body,
101
+ ...(ref !== undefined ? { ref } : {}),
102
+ ...(date !== undefined ? { date } : {}),
103
+ ...(section ? { section } : {}),
104
+ };
105
+ }
106
+ export function renderQueueLine(item) {
107
+ return `- [${item.done ? "x" : " "}] (${item.priority}) ${item.text}`;
108
+ }
109
+ export function renderDoneLine(entry) {
110
+ let line = `- [x] ${entry.text}`;
111
+ if (entry.ref !== undefined)
112
+ line += `${EM_DASH_SEP}${entry.ref}`;
113
+ if (entry.date !== undefined)
114
+ line += `${MIDDOT_SEP}${entry.date}`;
115
+ return line;
116
+ }
117
+ // ---------- board table ----------
118
+ const TABLE_ROW = /^\|(.*)\|\s*$/;
119
+ const TABLE_ALIGN = /^\|[\s:|-]+\|\s*$/;
120
+ function splitRow(line) {
121
+ const m = TABLE_ROW.exec(line);
122
+ if (!m)
123
+ return [];
124
+ return (m[1] ?? "").split("|").map((c) => c.trim());
125
+ }
126
+ function renderRow(cells) {
127
+ return `| ${cells.join(" | ")} |`;
128
+ }
129
+ function boardCells(r) {
130
+ return [r.lane, r.owner, r.state, r.currentSlice, r.nextGo];
131
+ }
132
+ function v1Cells(r) {
133
+ return [r.stream, r.owner, r.branchWorktree, r.status, r.blocker, r.lastNote];
134
+ }
135
+ function normHeaderCell(s) {
136
+ return s.trim().toLowerCase().replace(/\s+/g, " ").replace(/[·•]/g, "\u00b7");
137
+ }
138
+ function headersEqual(cells, expected) {
139
+ if (cells.length !== expected.length)
140
+ return false;
141
+ return cells.every((c, i) => normHeaderCell(c) === normHeaderCell(expected[i] ?? ""));
142
+ }
143
+ /** Lanes-v0 even if extra columns were inserted (the #38 Pane-column shape). */
144
+ function looksLikeLanesHeader(cells) {
145
+ const n = new Set(cells.map(normHeaderCell));
146
+ return LANES_V0_HEADERS.every((h) => n.has(normHeaderCell(h)));
147
+ }
148
+ function classifyBoardHeader(cells) {
149
+ if (headersEqual(cells, WORKSTREAMS_V1_HEADERS))
150
+ return SCHEMA.workstreams;
151
+ if (headersEqual(cells, LANES_V0_HEADERS) || looksLikeLanesHeader(cells))
152
+ return SCHEMA.workstreamsLanesV0;
153
+ for (const ext of Object.values(WORKSTREAMS_EXT)) {
154
+ if (headersEqual(cells, ext.headers))
155
+ return ext.id;
156
+ }
157
+ return "unknown";
158
+ }
159
+ // Replay the original line when the fields still say what it said; re-render
160
+ // once anything actually changed. A malformed row has no fields to have
161
+ // changed — it replays its bytes, always.
162
+ export function renderBoardRow(r) {
163
+ if (isMalformedRow(r))
164
+ return r.verbatim;
165
+ if (isExtensionRow(r)) {
166
+ if (r.raw !== undefined) {
167
+ const cells = splitRow(r.raw);
168
+ if (cells.length === r.cells.length && cells.every((c, i) => c === r.cells[i]))
169
+ return r.raw;
170
+ }
171
+ return renderRow(r.cells);
172
+ }
173
+ if (isWorkstreamsV1Row(r)) {
174
+ if (r.raw !== undefined) {
175
+ const cells = splitRow(r.raw);
176
+ if (cells.length === WORKSTREAMS_V1_ARITY && cells.every((c, i) => c === v1Cells(r)[i]))
177
+ return r.raw;
178
+ }
179
+ return renderRow(v1Cells(r));
180
+ }
181
+ if (r.raw !== undefined) {
182
+ const cells = splitRow(r.raw);
183
+ if (cells.length === BOARD_ARITY && cells.every((c, i) => c === boardCells(r)[i]))
184
+ return r.raw;
185
+ }
186
+ return renderRow(boardCells(r));
187
+ }
188
+ // Which record kind a `## Heading` opens. `## Queue` → queue items,
189
+ // `## Done` → done entries; anything else closes the record region, so the
190
+ // Cross-project section's bullets stay verbatim prose.
191
+ function sectionKindOf(heading) {
192
+ const h = heading.replace(/^#+\s*/, "").trim().toLowerCase();
193
+ if (h === "queue")
194
+ return "queue";
195
+ if (h === "done")
196
+ return "done";
197
+ if (h === "decisions recorded")
198
+ return "decisions";
199
+ return "none";
200
+ }
201
+ export function parseWorkDoc(source) {
202
+ const trailingNewline = source.endsWith("\n");
203
+ const lines = source.split("\n");
204
+ if (trailingNewline)
205
+ lines.pop(); // the split's empty tail, restored on render
206
+ const blocks = [];
207
+ const warnings = [];
208
+ let text = [];
209
+ let kind = "none";
210
+ let subsection;
211
+ let inFence = false;
212
+ let inHtmlComment = false;
213
+ const flushText = () => {
214
+ if (text.length)
215
+ blocks.push({ kind: "text", lines: text });
216
+ text = [];
217
+ };
218
+ // Consecutive item lines coalesce into one block; anything in between (a
219
+ // blank line, a subsection heading) flushes as text first and therefore
220
+ // starts a new one, which is what keeps rendering order faithful.
221
+ const pushItem = (item) => {
222
+ flushText();
223
+ const last = blocks[blocks.length - 1];
224
+ if (last?.kind === "queue")
225
+ last.items.push(item);
226
+ else
227
+ blocks.push({ kind: "queue", items: [item] });
228
+ };
229
+ const pushEntry = (entry) => {
230
+ flushText();
231
+ const last = blocks[blocks.length - 1];
232
+ if (last?.kind === "done")
233
+ last.entries.push(entry);
234
+ else
235
+ blocks.push({ kind: "done", entries: [entry] });
236
+ };
237
+ const pushDecision = (item) => {
238
+ flushText();
239
+ const last = blocks[blocks.length - 1];
240
+ if (last?.kind === "decisions")
241
+ last.items.push(item);
242
+ else
243
+ blocks.push({ kind: "decisions", items: [item] });
244
+ };
245
+ for (let i = 0; i < lines.length; i++) {
246
+ const line = lines[i] ?? "";
247
+ // Fenced blocks are never interpreted — docs/DONE.md pins the done-line
248
+ // FORMAT inside a fence, and parsing that example as an entry would
249
+ // invent a record out of documentation.
250
+ if (/^\s*```/.test(line)) {
251
+ inFence = !inFence;
252
+ text.push(line);
253
+ continue;
254
+ }
255
+ if (inFence) {
256
+ text.push(line);
257
+ continue;
258
+ }
259
+ // Template examples live inside the coordinator-extensions HTML comment.
260
+ // Live sections sit *after* a one-line fence comment (deepblackjack).
261
+ if (inHtmlComment) {
262
+ text.push(line);
263
+ if (/-->/.test(line))
264
+ inHtmlComment = false;
265
+ continue;
266
+ }
267
+ if (/<!--/.test(line) && !/-->/.test(line)) {
268
+ inHtmlComment = true;
269
+ text.push(line);
270
+ continue;
271
+ }
272
+ if (/^#{1,6}\s/.test(line)) {
273
+ const level = (line.match(/^#+/) ?? [""])[0].length;
274
+ if (level <= 2) {
275
+ kind = sectionKindOf(line);
276
+ subsection = undefined;
277
+ }
278
+ else if (kind !== "none") {
279
+ subsection = line.replace(/^#+\s*/, "").trim();
280
+ }
281
+ text.push(line);
282
+ continue;
283
+ }
284
+ if (kind === "queue") {
285
+ const item = parseQueueLine(line, subsection);
286
+ if (item) {
287
+ pushItem(item);
288
+ continue;
289
+ }
290
+ }
291
+ else if (kind === "done") {
292
+ const entry = parseDoneLine(line, subsection);
293
+ if (entry) {
294
+ pushEntry(entry);
295
+ continue;
296
+ }
297
+ }
298
+ else if (kind === "decisions") {
299
+ const m = /^-\s+(.+)$/.exec(line);
300
+ if (m) {
301
+ pushDecision(m[1] ?? "");
302
+ continue;
303
+ }
304
+ }
305
+ // Board table: header + alignment. PR3 classifies by header cells.
306
+ if (TABLE_ROW.test(line) && TABLE_ALIGN.test(lines[i + 1] ?? "")) {
307
+ const headerCells = splitRow(line);
308
+ const schema = classifyBoardHeader(headerCells);
309
+ let j = i + 2;
310
+ while (j < lines.length && TABLE_ROW.test(lines[j] ?? ""))
311
+ j++;
312
+ if (schema === "unknown") {
313
+ warnings.push(`unknown workstreams grammar at line ${i + 1}; expected workstreams.v1 (6-col Active Streams).`);
314
+ for (let k = i; k < j; k++)
315
+ text.push(lines[k] ?? "");
316
+ i = j - 1;
317
+ continue;
318
+ }
319
+ const ext = Object.values(WORKSTREAMS_EXT).find((e) => e.id === schema);
320
+ const expected = schema === SCHEMA.workstreams
321
+ ? WORKSTREAMS_V1_ARITY
322
+ : ext
323
+ ? ext.headers.length
324
+ : BOARD_ARITY;
325
+ const rows = [];
326
+ for (let k = i + 2; k < j; k++) {
327
+ const raw = lines[k] ?? "";
328
+ const cells = splitRow(raw);
329
+ if (cells.length !== expected) {
330
+ rows.push({
331
+ malformed: true,
332
+ verbatim: raw,
333
+ issue: `board row at line ${k + 1} has ${cells.length} column(s), expected ${expected} — ` +
334
+ `refused (kept verbatim, excluded from board records). Fix the row, or widen the schema ` +
335
+ `deliberately. Row: ${raw}`,
336
+ });
337
+ continue;
338
+ }
339
+ if (ext) {
340
+ rows.push({ extension: ext.key, cells, raw });
341
+ continue;
342
+ }
343
+ if (schema === SCHEMA.workstreams) {
344
+ const [stream = "", owner = "", branchWorktree = "", status = "", blocker = "", lastNote = ""] = cells;
345
+ rows.push({
346
+ id: idFor("s", stream),
347
+ stream,
348
+ owner,
349
+ branchWorktree,
350
+ status,
351
+ blocker,
352
+ lastNote,
353
+ raw,
354
+ });
355
+ }
356
+ else {
357
+ const [lane = "", owner = "", state = "", currentSlice = "", nextGo = ""] = cells;
358
+ rows.push({ id: idFor("b", lane), lane, owner, state, currentSlice, nextGo, raw });
359
+ }
360
+ }
361
+ flushText();
362
+ blocks.push({ kind: "board", header: line, align: lines[i + 1] ?? "", schema, rows });
363
+ i = j - 1;
364
+ continue;
365
+ }
366
+ text.push(line);
367
+ }
368
+ flushText();
369
+ return { trailingNewline, blocks, ...(warnings.length ? { warnings } : {}) };
370
+ }
371
+ export function renderWorkDoc(doc) {
372
+ const out = [];
373
+ for (const block of doc.blocks) {
374
+ if (block.kind === "text")
375
+ out.push(...block.lines);
376
+ else if (block.kind === "queue")
377
+ out.push(...block.items.map(renderQueueLine));
378
+ else if (block.kind === "done")
379
+ out.push(...block.entries.map(renderDoneLine));
380
+ else if (block.kind === "decisions")
381
+ out.push(...block.items.map((item) => `- ${item}`));
382
+ else {
383
+ out.push(block.header);
384
+ out.push(block.align);
385
+ for (const r of block.rows)
386
+ out.push(renderBoardRow(r));
387
+ }
388
+ }
389
+ return out.join("\n") + (doc.trailingNewline ? "\n" : "");
390
+ }
391
+ // ---------- record extraction ----------
392
+ export function queueItemsOf(doc) {
393
+ return doc.blocks.flatMap((b) => (b.kind === "queue" ? b.items : []));
394
+ }
395
+ export function doneEntriesOf(doc) {
396
+ return doc.blocks.flatMap((b) => (b.kind === "done" ? b.entries : []));
397
+ }
398
+ export function boardRowsOf(doc) {
399
+ return doc.blocks.flatMap((b) => b.kind === "board" && b.schema === SCHEMA.workstreamsLanesV0
400
+ ? b.rows.filter((r) => !isMalformedRow(r) && !isWorkstreamsV1Row(r) && !isExtensionRow(r))
401
+ : []);
402
+ }
403
+ export function workstreamsV1RowsOf(doc) {
404
+ return doc.blocks.flatMap((b) => b.kind === "board" && b.schema === SCHEMA.workstreams
405
+ ? b.rows.filter((r) => isWorkstreamsV1Row(r))
406
+ : []);
407
+ }
408
+ function namedExtRow(ext, cells) {
409
+ const out = {};
410
+ ext.fields.forEach((field, i) => {
411
+ out[field] = cells[i] ?? "";
412
+ });
413
+ return out;
414
+ }
415
+ export function workstreamsExtensionsOf(doc) {
416
+ const out = {
417
+ openPrs: [],
418
+ cutoverGates: [],
419
+ needsDavid: [],
420
+ risks: [],
421
+ rooms: [],
422
+ decisions: [],
423
+ };
424
+ for (const b of doc.blocks) {
425
+ if (b.kind === "decisions") {
426
+ out.decisions.push(...b.items);
427
+ continue;
428
+ }
429
+ if (b.kind !== "board" || !b.schema)
430
+ continue;
431
+ const ext = Object.values(WORKSTREAMS_EXT).find((e) => e.id === b.schema);
432
+ if (!ext)
433
+ continue;
434
+ for (const r of b.rows) {
435
+ if (isMalformedRow(r) || !isExtensionRow(r))
436
+ continue;
437
+ out[ext.key].push(namedExtRow(ext, r.cells));
438
+ }
439
+ }
440
+ return out;
441
+ }
442
+ export function listWorkBoardOf(doc) {
443
+ const out = [];
444
+ for (const r of workstreamsV1RowsOf(doc)) {
445
+ out.push({
446
+ schema: SCHEMA.workstreams,
447
+ id: r.id,
448
+ stream: r.stream,
449
+ owner: r.owner,
450
+ branchWorktree: r.branchWorktree,
451
+ status: r.status,
452
+ blocker: r.blocker,
453
+ lastNote: r.lastNote,
454
+ });
455
+ }
456
+ for (const r of boardRowsOf(doc)) {
457
+ out.push({
458
+ schema: SCHEMA.workstreamsLanesV0,
459
+ id: r.id,
460
+ lane: r.lane,
461
+ owner: r.owner,
462
+ state: r.state,
463
+ currentSlice: r.currentSlice,
464
+ nextGo: r.nextGo,
465
+ });
466
+ }
467
+ return out;
468
+ }
469
+ export function projectV1ToLanes(row) {
470
+ if (row.schema === SCHEMA.workstreamsLanesV0)
471
+ return row;
472
+ return {
473
+ schema: SCHEMA.workstreamsLanesV0,
474
+ id: row.id,
475
+ lane: row.stream,
476
+ owner: row.owner,
477
+ state: row.status,
478
+ currentSlice: row.lastNote,
479
+ nextGo: "",
480
+ };
481
+ }
482
+ // Every refused row's issue, in document order — plus unknown-grammar warnings.
483
+ export function workDocIssues(doc) {
484
+ const refused = doc.blocks.flatMap((b) => b.kind === "board" ? b.rows.filter(isMalformedRow).map((r) => r.issue) : []);
485
+ return [...(doc.warnings ?? []), ...refused];
486
+ }
487
+ export const LANES_V0_WRITE_ISSUE = "workstreams.lanes-v0 is parse-only — new writes must use workstreams.v1 (6-col Active Streams)";
488
+ /** 5-col Lanes still parses; this is the write-grammar refusal for doctor / export. */
489
+ export function workDocLegacyWriteIssues(doc) {
490
+ return doc.blocks.some((b) => b.kind === "board" && b.schema === SCHEMA.workstreamsLanesV0)
491
+ ? [LANES_V0_WRITE_ISSUE]
492
+ : [];
493
+ }
494
+ //# sourceMappingURL=docs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs.js","sourceRoot":"","sources":["../src/docs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export type FactEntry = {
2
+ id: string;
3
+ claim: string;
4
+ verified: string;
5
+ by: string;
6
+ method: string;
7
+ };
8
+ export declare function parseFactEntry(lines: [string, string?]): FactEntry | null;
9
+ /** Skip ``` fences; only the `## Facts` section. */
10
+ export declare function parseFactsDoc(source: string): {
11
+ entries: FactEntry[];
12
+ issues: string[];
13
+ };
14
+ export declare function renderFactEntry(entry: FactEntry): string;
15
+ /** Returns the new file string. Does not write disk. Twin of set-fact.mjs. */
16
+ export declare function upsertFact(source: string, entry: Omit<FactEntry, "verified"> & {
17
+ verified?: string;
18
+ }): string;
package/dist/facts.js ADDED
@@ -0,0 +1,95 @@
1
+ import { EM_DASH_SEP, MIDDOT_SEP } from "./glyphs.js";
2
+ import { FACT_STALE_DAYS } from "./schema.js";
3
+ const ENTRY_RE = /^- `([^`]+)` — /;
4
+ const META_RE = /^ {2}verified: (\d{4}-\d{2}-\d{2}T\d{2}:\d{2}Z) · by: (\S+) · method: (.+)$/;
5
+ const KEBAB = /^[a-z0-9][a-z0-9-]*$/;
6
+ export function parseFactEntry(lines) {
7
+ const m = lines[0]?.match(ENTRY_RE);
8
+ if (!m)
9
+ return null;
10
+ const meta = (lines[1] || "").match(META_RE);
11
+ if (!meta)
12
+ return null;
13
+ return {
14
+ id: m[1] ?? "",
15
+ claim: lines[0].slice(m[0].length),
16
+ verified: meta[1] ?? "",
17
+ by: meta[2] ?? "",
18
+ method: meta[3] ?? "",
19
+ };
20
+ }
21
+ function daysSinceIsoDate(iso) {
22
+ const m = iso.match(/^(\d{4})-(\d{2})-(\d{2})/);
23
+ if (!m)
24
+ return 0;
25
+ const then = new Date(+m[1], +m[2] - 1, +m[3]).getTime();
26
+ return Math.floor((Date.now() - then) / 86400000);
27
+ }
28
+ /** Skip ``` fences; only the `## Facts` section. */
29
+ export function parseFactsDoc(source) {
30
+ const lines = source.split("\n");
31
+ const entries = [];
32
+ const issues = [];
33
+ const ids = new Set();
34
+ let inSection = false;
35
+ let inFence = false;
36
+ for (let i = 0; i < lines.length; i++) {
37
+ if (/^```/.test(lines[i] ?? ""))
38
+ inFence = !inFence;
39
+ if (/^## /.test(lines[i] ?? ""))
40
+ inSection = /^## Facts\s*$/.test(lines[i] ?? "");
41
+ if (!inSection || inFence)
42
+ continue;
43
+ const m = (lines[i] ?? "").match(ENTRY_RE);
44
+ if (!m)
45
+ continue;
46
+ const id = m[1] ?? "";
47
+ if (!KEBAB.test(id))
48
+ issues.push(`FACTS: \`${id}\` — id is not kebab-case`);
49
+ if (ids.has(id))
50
+ issues.push(`FACTS: \`${id}\` — duplicate id (one writer per fact; replace, don't append)`);
51
+ ids.add(id);
52
+ const parsed = parseFactEntry([lines[i] ?? "", lines[i + 1]]);
53
+ if (!parsed) {
54
+ issues.push(`FACTS: \`${id}\` — missing/malformed \`verified: … · by: … · method: …\` line`);
55
+ continue;
56
+ }
57
+ entries.push(parsed);
58
+ const days = daysSinceIsoDate(parsed.verified);
59
+ if (days > FACT_STALE_DAYS)
60
+ issues.push(`FACTS: \`${id}\` — verified ${days} days ago (> ${FACT_STALE_DAYS}) — re-verify or delete`);
61
+ }
62
+ return { entries, issues };
63
+ }
64
+ export function renderFactEntry(entry) {
65
+ return (`- \`${entry.id}\`${EM_DASH_SEP}${entry.claim}\n` +
66
+ ` verified: ${entry.verified}${MIDDOT_SEP}by: ${entry.by}${MIDDOT_SEP}method: ${entry.method}`);
67
+ }
68
+ /** Returns the new file string. Does not write disk. Twin of set-fact.mjs. */
69
+ export function upsertFact(source, entry) {
70
+ const ts = entry.verified ?? new Date().toISOString().slice(0, 16) + "Z";
71
+ const block = `- \`${entry.id}\`${EM_DASH_SEP}${entry.claim}\n verified: ${ts}${MIDDOT_SEP}by: ${entry.by}${MIDDOT_SEP}method: ${entry.method}`;
72
+ const lines = source.split("\n");
73
+ const entryRe = new RegExp(`^- \`${entry.id}\` — `);
74
+ const start = lines.findIndex((l) => entryRe.test(l));
75
+ if (start >= 0) {
76
+ const hasMeta = /^ {2}verified:/.test(lines[start + 1] || "");
77
+ lines.splice(start, hasMeta ? 2 : 1, ...block.split("\n"));
78
+ return lines.join("\n");
79
+ }
80
+ const facts = lines.findIndex((l) => /^## Facts\s*$/.test(l));
81
+ if (facts < 0)
82
+ throw new Error("no `## Facts` section in FACTS.md — file is malformed");
83
+ let end = lines.length;
84
+ for (let i = facts + 1; i < lines.length; i++) {
85
+ if (/^## /.test(lines[i] ?? "")) {
86
+ end = i;
87
+ break;
88
+ }
89
+ }
90
+ while (end > facts + 1 && (lines[end - 1] ?? "").trim() === "")
91
+ end--;
92
+ lines.splice(end, 0, "", ...block.split("\n"));
93
+ return lines.join("\n");
94
+ }
95
+ //# sourceMappingURL=facts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"facts.js","sourceRoot":"","sources":["../src/facts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ /** Pinned. A source-file normalization must not turn these into ASCII. */
2
+ export declare const EM_DASH_SEP = " \u2014 ";
3
+ export declare const MIDDOT_SEP = " \u00B7 ";
package/dist/glyphs.js ADDED
@@ -0,0 +1,4 @@
1
+ /** Pinned. A source-file normalization must not turn these into ASCII. */
2
+ export const EM_DASH_SEP = " \u2014 "; // " — "
3
+ export const MIDDOT_SEP = " \u00b7 "; // " · "
4
+ //# sourceMappingURL=glyphs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glyphs.js","sourceRoot":"","sources":["../src/glyphs.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,MAAM,CAAC,MAAM,WAAW,GAAG,UAAU,CAAC,CAAC,QAAQ;AAC/C,MAAM,CAAC,MAAM,UAAU,GAAG,UAAU,CAAC,CAAC,QAAQ"}
package/dist/ids.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ /** Internal. Not a public export — work.ts never exported it. */
2
+ export declare function idFor(kind: string, seed: string): string;
package/dist/ids.js ADDED
@@ -0,0 +1,6 @@
1
+ import { createHash } from "node:crypto";
2
+ /** Internal. Not a public export — work.ts never exported it. */
3
+ export function idFor(kind, seed) {
4
+ return `${kind}-${createHash("sha1").update(seed).digest("hex").slice(0, 8)}`;
5
+ }
6
+ //# sourceMappingURL=ids.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ids.js","sourceRoot":"","sources":["../src/ids.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,iEAAiE;AACjE,MAAM,UAAU,KAAK,CAAC,IAAY,EAAE,IAAY;IAC9C,OAAO,GAAG,IAAI,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;AAChF,CAAC"}
@@ -0,0 +1,6 @@
1
+ export * from "./docs.js";
2
+ export * from "./glyphs.js";
3
+ export * from "./schema.js";
4
+ export * from "./facts.js";
5
+ export * from "./protocol/index.js";
6
+ export * from "./skills-mirror.js";
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export * from "./docs.js";
2
+ export * from "./glyphs.js";
3
+ export * from "./schema.js";
4
+ export * from "./facts.js";
5
+ export * from "./protocol/index.js";
6
+ export * from "./skills-mirror.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export type { Citation, MessageRecordType, DecisionPayload, VerdictPayload, SummaryPayload, SummaryRecordType, MessageRecord, } from "./records.js";
2
+ export { isDecision } from "./records.js";
3
+ export { PREFIX, DECISION_PREFIX } from "./prefixes.js";
4
+ export { renderRecord } from "./render.js";
@@ -0,0 +1,4 @@
1
+ export { isDecision } from "./records.js";
2
+ export { PREFIX, DECISION_PREFIX } from "./prefixes.js";
3
+ export { renderRecord } from "./render.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/protocol/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const PREFIX: Record<string, string>;
2
+ export declare const DECISION_PREFIX = "DAVID_DECISION";
@@ -0,0 +1,12 @@
1
+ export const PREFIX = {
2
+ blocker: "BLOCKER",
3
+ risk: "RISK",
4
+ done: "DONE",
5
+ fyi: "FYI",
6
+ action: "AGENT_ACTION",
7
+ go: "GO",
8
+ scope: "SCOPE CHANGE",
9
+ verdict: "VERDICT",
10
+ };
11
+ export const DECISION_PREFIX = "DAVID_DECISION";
12
+ //# sourceMappingURL=prefixes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prefixes.js","sourceRoot":"","sources":["../../src/protocol/prefixes.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,MAAM,GAA2B;IAC5C,OAAO,EAAE,SAAS;IAClB,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,MAAM;IACZ,GAAG,EAAE,KAAK;IACV,MAAM,EAAE,cAAc;IACtB,EAAE,EAAE,IAAI;IACR,KAAK,EAAE,cAAc;IACrB,OAAO,EAAE,SAAS;CACnB,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG,gBAAgB,CAAC"}
@@ -0,0 +1,45 @@
1
+ export type Citation = {
2
+ kind: "pr" | "file" | "commit" | "url";
3
+ ref: string;
4
+ };
5
+ export type MessageRecordType = "blocker" | "decision" | "risk" | "done" | "fyi" | "action" | "go" | "scope" | "verdict";
6
+ export type DecisionPayload = {
7
+ title: string;
8
+ context: string;
9
+ options: string[];
10
+ recommendation: string;
11
+ ifNoAction: string;
12
+ };
13
+ export type VerdictPayload = {
14
+ result: "pass" | "fail";
15
+ headRefOid: string;
16
+ notes?: string;
17
+ };
18
+ export type SummaryPayload = {
19
+ summary: string;
20
+ };
21
+ export type SummaryRecordType = "blocker" | "risk" | "fyi" | "action" | "go" | "scope";
22
+ export type MessageRecord = {
23
+ type: "decision";
24
+ payload?: DecisionPayload;
25
+ cites?: Citation[];
26
+ } | {
27
+ type: "verdict";
28
+ payload?: VerdictPayload;
29
+ cites?: Citation[];
30
+ } | {
31
+ type: "done";
32
+ payload?: SummaryPayload;
33
+ cites?: Citation[];
34
+ } | {
35
+ type: SummaryRecordType;
36
+ payload?: SummaryPayload;
37
+ cites?: Citation[];
38
+ };
39
+ /** Monotone: can only GRANT long retention, never remove it. */
40
+ export declare function isDecision(e: {
41
+ kind?: string;
42
+ record?: {
43
+ type?: string;
44
+ };
45
+ } | null | undefined): boolean;
@@ -0,0 +1,5 @@
1
+ /** Monotone: can only GRANT long retention, never remove it. */
2
+ export function isDecision(e) {
3
+ return e?.kind === "decision" || e?.record?.type === "decision";
4
+ }
5
+ //# sourceMappingURL=records.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"records.js","sourceRoot":"","sources":["../../src/protocol/records.ts"],"names":[],"mappings":"AA+BA,gEAAgE;AAChE,MAAM,UAAU,UAAU,CACxB,CAAmE;IAEnE,OAAO,CAAC,EAAE,IAAI,KAAK,UAAU,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,KAAK,UAAU,CAAC;AAClE,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { MessageRecord } from "./records.js";
2
+ export declare function renderRecord(record: MessageRecord): string | null;
@@ -0,0 +1,49 @@
1
+ import { DECISION_PREFIX, PREFIX } from "./prefixes.js";
2
+ function isNonEmpty(v) {
3
+ return typeof v === "string" && v.trim().length > 0;
4
+ }
5
+ function renderDecision(p) {
6
+ if (!isNonEmpty(p.title) || !isNonEmpty(p.context))
7
+ return null;
8
+ if (!isNonEmpty(p.recommendation) || !isNonEmpty(p.ifNoAction))
9
+ return null;
10
+ if (!Array.isArray(p.options) || p.options.length === 0)
11
+ return null;
12
+ if (!p.options.every(isNonEmpty))
13
+ return null;
14
+ return [
15
+ `${DECISION_PREFIX}: ${p.title}`,
16
+ `Context: ${p.context}`,
17
+ "Options:",
18
+ ...p.options.map((o, i) => `${i + 1}. ${o}`),
19
+ `Recommendation: ${p.recommendation}`,
20
+ `If no action: ${p.ifNoAction}`,
21
+ ].join("\n");
22
+ }
23
+ function renderVerdict(p) {
24
+ if (p.result !== "pass" && p.result !== "fail")
25
+ return null;
26
+ if (!isNonEmpty(p.headRefOid))
27
+ return null;
28
+ const head = `${PREFIX.verdict}: ${p.result.toUpperCase()} ${p.headRefOid}`;
29
+ return isNonEmpty(p.notes) ? `${head} — ${p.notes}` : head;
30
+ }
31
+ export function renderRecord(record) {
32
+ if (!record || typeof record.type !== "string")
33
+ return null;
34
+ const payload = record.payload;
35
+ if (payload === undefined || payload === null)
36
+ return null;
37
+ if (typeof payload !== "object" || Array.isArray(payload))
38
+ return null;
39
+ if (record.type === "decision")
40
+ return renderDecision(payload);
41
+ if (record.type === "verdict")
42
+ return renderVerdict(payload);
43
+ const prefix = PREFIX[record.type];
44
+ if (!prefix)
45
+ return null;
46
+ const { summary } = payload;
47
+ return isNonEmpty(summary) ? `${prefix}: ${summary}` : null;
48
+ }
49
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/protocol/render.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ export declare const SCHEMA: {
2
+ readonly queue: "queue.v1";
3
+ readonly done: "done.v1";
4
+ readonly facts: "facts.v1";
5
+ readonly workstreams: "workstreams.v1";
6
+ readonly workstreamsLanesV0: "workstreams.lanes-v0";
7
+ };
8
+ export type SchemaId = (typeof SCHEMA)[keyof typeof SCHEMA];
9
+ export declare const FACT_STALE_DAYS = 14;
10
+ export declare const WORKSTREAMS_V1_ARITY = 6;
11
+ export declare const WORKSTREAMS_V1_HEADERS: readonly ["Stream", "Owner / Agent", "Branch · Worktree", "Status", "Blocker", "Last note"];
12
+ export declare const LANES_V0_ARITY = 5;
13
+ export declare const LANES_V0_HEADERS: readonly ["Lane", "Owner", "State", "Current slice", "Next GO"];
14
+ /** Coordinator-only write region under the WORKSTREAMS fence. Header-cell detected. */
15
+ export declare const WORKSTREAMS_EXT: {
16
+ readonly openPrs: {
17
+ readonly id: "workstreams.ext.open-prs";
18
+ readonly key: "openPrs";
19
+ readonly headers: readonly ["Repo", "PR", "Owner", "Status", "Merge Gate"];
20
+ readonly fields: readonly ["repo", "pr", "owner", "status", "mergeGate"];
21
+ };
22
+ readonly cutoverGates: {
23
+ readonly id: "workstreams.ext.cutover-gates";
24
+ readonly key: "cutoverGates";
25
+ readonly headers: readonly ["Gate", "Owner", "State", "Notes"];
26
+ readonly fields: readonly ["gate", "owner", "state", "notes"];
27
+ };
28
+ readonly needsDavid: {
29
+ readonly id: "workstreams.ext.needs-david";
30
+ readonly key: "needsDavid";
31
+ readonly headers: readonly ["Decision", "Context", "Recommendation"];
32
+ readonly fields: readonly ["decision", "context", "recommendation"];
33
+ };
34
+ readonly risks: {
35
+ readonly id: "workstreams.ext.risks";
36
+ readonly key: "risks";
37
+ readonly headers: readonly ["Risk", "Impact", "Mitigation"];
38
+ readonly fields: readonly ["risk", "impact", "mitigation"];
39
+ };
40
+ readonly rooms: {
41
+ readonly id: "workstreams.ext.rooms";
42
+ readonly key: "rooms";
43
+ readonly headers: readonly ["Room", "Purpose", "Owner", "State"];
44
+ readonly fields: readonly ["room", "purpose", "owner", "state"];
45
+ };
46
+ };
47
+ export type WorkstreamsExtKey = keyof typeof WORKSTREAMS_EXT;
48
+ export type WorkstreamsExtId = (typeof WORKSTREAMS_EXT)[WorkstreamsExtKey]["id"];
package/dist/schema.js ADDED
@@ -0,0 +1,53 @@
1
+ export const SCHEMA = {
2
+ queue: "queue.v1",
3
+ done: "done.v1",
4
+ facts: "facts.v1",
5
+ workstreams: "workstreams.v1",
6
+ workstreamsLanesV0: "workstreams.lanes-v0",
7
+ };
8
+ export const FACT_STALE_DAYS = 14;
9
+ export const WORKSTREAMS_V1_ARITY = 6;
10
+ export const WORKSTREAMS_V1_HEADERS = [
11
+ "Stream",
12
+ "Owner / Agent",
13
+ "Branch · Worktree",
14
+ "Status",
15
+ "Blocker",
16
+ "Last note",
17
+ ];
18
+ export const LANES_V0_ARITY = 5;
19
+ export const LANES_V0_HEADERS = ["Lane", "Owner", "State", "Current slice", "Next GO"];
20
+ /** Coordinator-only write region under the WORKSTREAMS fence. Header-cell detected. */
21
+ export const WORKSTREAMS_EXT = {
22
+ openPrs: {
23
+ id: "workstreams.ext.open-prs",
24
+ key: "openPrs",
25
+ headers: ["Repo", "PR", "Owner", "Status", "Merge Gate"],
26
+ fields: ["repo", "pr", "owner", "status", "mergeGate"],
27
+ },
28
+ cutoverGates: {
29
+ id: "workstreams.ext.cutover-gates",
30
+ key: "cutoverGates",
31
+ headers: ["Gate", "Owner", "State", "Notes"],
32
+ fields: ["gate", "owner", "state", "notes"],
33
+ },
34
+ needsDavid: {
35
+ id: "workstreams.ext.needs-david",
36
+ key: "needsDavid",
37
+ headers: ["Decision", "Context", "Recommendation"],
38
+ fields: ["decision", "context", "recommendation"],
39
+ },
40
+ risks: {
41
+ id: "workstreams.ext.risks",
42
+ key: "risks",
43
+ headers: ["Risk", "Impact", "Mitigation"],
44
+ fields: ["risk", "impact", "mitigation"],
45
+ },
46
+ rooms: {
47
+ id: "workstreams.ext.rooms",
48
+ key: "rooms",
49
+ headers: ["Room", "Purpose", "Owner", "State"],
50
+ fields: ["room", "purpose", "owner", "state"],
51
+ },
52
+ };
53
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,KAAK,EAAE,UAAU;IACjB,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,UAAU;IACjB,WAAW,EAAE,gBAAgB;IAC7B,kBAAkB,EAAE,sBAAsB;CAClC,CAAC;AAGX,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAC;AAElC,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC;AACtC,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,QAAQ;IACR,eAAe;IACf,mBAAmB;IACnB,QAAQ;IACR,SAAS;IACT,WAAW;CACH,CAAC;AAEX,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC;AAChC,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,SAAS,CAAU,CAAC;AAEhG,uFAAuF;AACvF,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,OAAO,EAAE;QACP,EAAE,EAAE,0BAA0B;QAC9B,GAAG,EAAE,SAAS;QACd,OAAO,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,CAAC;QACxD,MAAM,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,CAAC;KACvD;IACD,YAAY,EAAE;QACZ,EAAE,EAAE,+BAA+B;QACnC,GAAG,EAAE,cAAc;QACnB,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;QAC5C,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;KAC5C;IACD,UAAU,EAAE;QACV,EAAE,EAAE,6BAA6B;QACjC,GAAG,EAAE,YAAY;QACjB,OAAO,EAAE,CAAC,UAAU,EAAE,SAAS,EAAE,gBAAgB,CAAC;QAClD,MAAM,EAAE,CAAC,UAAU,EAAE,SAAS,EAAE,gBAAgB,CAAC;KAClD;IACD,KAAK,EAAE;QACL,EAAE,EAAE,uBAAuB;QAC3B,GAAG,EAAE,OAAO;QACZ,OAAO,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,YAAY,CAAC;QACzC,MAAM,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,YAAY,CAAC;KACzC;IACD,KAAK,EAAE;QACL,EAAE,EAAE,uBAAuB;QAC3B,GAAG,EAAE,OAAO;QACZ,OAAO,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC;QAC9C,MAAM,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC;KAC9C;CACO,CAAC"}
@@ -0,0 +1,13 @@
1
+ export type SkillFront = {
2
+ name: string;
3
+ data: Record<string, string>;
4
+ body: string;
5
+ };
6
+ export declare function parseFrontmatter(raw: string): {
7
+ data: Record<string, string>;
8
+ body: string;
9
+ };
10
+ export declare function deArgument(body: string): string;
11
+ export declare function isReadOnly(skill: SkillFront): boolean;
12
+ export declare function toCursor(skill: SkillFront): string;
13
+ export declare function toVscode(skill: SkillFront): string;
@@ -0,0 +1,48 @@
1
+ const READ_ONLY_TOOLS = new Set(["read", "glob", "grep"]);
2
+ export function parseFrontmatter(raw) {
3
+ const m = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
4
+ if (!m)
5
+ return { data: {}, body: raw };
6
+ const data = {};
7
+ for (const line of (m[1] ?? "").split("\n")) {
8
+ const kv = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
9
+ if (kv)
10
+ data[kv[1] ?? ""] = (kv[2] ?? "").replace(/^["']|["']$/g, "").trim();
11
+ }
12
+ return { data, body: m[2] ?? "" };
13
+ }
14
+ export function deArgument(body) {
15
+ return body
16
+ .replace(/:\s*\$ARGUMENTS\s*$/gm, ": (the input you provide)")
17
+ .replace(/\$ARGUMENTS/g, "the input you provide");
18
+ }
19
+ export function isReadOnly(skill) {
20
+ const tools = skill.data["allowed-tools"];
21
+ if (!tools)
22
+ return false;
23
+ const list = tools
24
+ .split(",")
25
+ .map((t) => t.trim().toLowerCase())
26
+ .filter(Boolean);
27
+ return list.length > 0 && list.every((t) => READ_ONLY_TOOLS.has(t));
28
+ }
29
+ const GENERATED_NOTE = "<!-- GENERATED from .claude/skills/<name>/SKILL.md by `groundwork`. Do not edit by hand. -->";
30
+ export function toCursor(skill) {
31
+ return `${GENERATED_NOTE.replace("<name>", skill.name)}\n\n${deArgument(skill.body).trim()}\n`;
32
+ }
33
+ export function toVscode(skill) {
34
+ const mode = isReadOnly(skill) ? "ask" : "edit";
35
+ const desc = (skill.data.description || skill.name).replace(/"/g, "'");
36
+ return [
37
+ "---",
38
+ `mode: ${mode}`,
39
+ `description: ${desc}`,
40
+ "---",
41
+ "",
42
+ GENERATED_NOTE.replace("<name>", skill.name),
43
+ "",
44
+ deArgument(skill.body).trim(),
45
+ "",
46
+ ].join("\n");
47
+ }
48
+ //# sourceMappingURL=skills-mirror.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills-mirror.js","sourceRoot":"","sources":["../src/skills-mirror.ts"],"names":[],"mappings":"AAAA,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAI1D,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;IAC1D,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;IACvC,MAAM,IAAI,GAA2B,EAAE,CAAC;IACxC,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5C,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;QACpD,IAAI,EAAE;YAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/E,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI;SACR,OAAO,CAAC,uBAAuB,EAAE,2BAA2B,CAAC;SAC7D,OAAO,CAAC,cAAc,EAAE,uBAAuB,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAiB;IAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC1C,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,MAAM,IAAI,GAAG,KAAK;SACf,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;SAClC,MAAM,CAAC,OAAO,CAAC,CAAC;IACnB,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,cAAc,GAClB,8FAA8F,CAAC;AAEjG,MAAM,UAAU,QAAQ,CAAC,KAAiB;IACxC,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,OAAO,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC;AACjG,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,KAAiB;IACxC,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;IAChD,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACvE,OAAO;QACL,KAAK;QACL,SAAS,IAAI,EAAE;QACf,gBAAgB,IAAI,EAAE;QACtB,KAAK;QACL,EAAE;QACF,cAAc,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC;QAC5C,EAAE;QACF,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE;QAC7B,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@davidbalzan/groundwork-seam",
3
+ "version": "0.1.0",
4
+ "description": "Schema owner of QUEUE / DONE / WORKSTREAMS / FACTS plus protocol record types.",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.js"
10
+ },
11
+ "./docs": {
12
+ "types": "./dist/docs.d.ts",
13
+ "import": "./dist/docs.js"
14
+ },
15
+ "./protocol": {
16
+ "types": "./dist/protocol/index.d.ts",
17
+ "import": "./dist/protocol/index.js"
18
+ },
19
+ "./skills-mirror": {
20
+ "types": "./dist/skills-mirror.d.ts",
21
+ "import": "./dist/skills-mirror.js"
22
+ }
23
+ },
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "engines": {
28
+ "node": ">=20"
29
+ },
30
+ "license": "MIT",
31
+ "devDependencies": {
32
+ "@types/node": "^26.2.0",
33
+ "typescript": "^7.0.2"
34
+ },
35
+ "scripts": {
36
+ "build": "tsc",
37
+ "test": "node --test test/*.test.mjs"
38
+ }
39
+ }