@sentropic/track 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +6 -0
  2. package/dist/branch/index.d.ts +2 -0
  3. package/dist/branch/index.d.ts.map +1 -1
  4. package/dist/branch/index.js +2 -0
  5. package/dist/branch/index.js.map +1 -1
  6. package/dist/branch/parse.d.ts +6 -0
  7. package/dist/branch/parse.d.ts.map +1 -1
  8. package/dist/branch/parse.js +8 -0
  9. package/dist/branch/parse.js.map +1 -1
  10. package/dist/branch/signature.d.ts +10 -0
  11. package/dist/branch/signature.d.ts.map +1 -0
  12. package/dist/branch/signature.js +41 -0
  13. package/dist/branch/signature.js.map +1 -0
  14. package/dist/cli/bin.d.ts +3 -0
  15. package/dist/cli/bin.d.ts.map +1 -0
  16. package/dist/cli/bin.js +13 -0
  17. package/dist/cli/bin.js.map +1 -0
  18. package/dist/cli/desync.d.ts +3 -0
  19. package/dist/cli/desync.d.ts.map +1 -1
  20. package/dist/cli/desync.js +14 -2
  21. package/dist/cli/desync.js.map +1 -1
  22. package/dist/cli/index.d.ts +0 -1
  23. package/dist/cli/index.d.ts.map +1 -1
  24. package/dist/cli/index.js +50 -27
  25. package/dist/cli/index.js.map +1 -1
  26. package/dist/events/types.d.ts +21 -0
  27. package/dist/events/types.d.ts.map +1 -1
  28. package/dist/index.d.ts +1 -0
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +2 -0
  31. package/dist/index.js.map +1 -1
  32. package/dist/mcp/cli.d.ts +3 -0
  33. package/dist/mcp/cli.d.ts.map +1 -0
  34. package/dist/mcp/cli.js +12 -0
  35. package/dist/mcp/cli.js.map +1 -0
  36. package/dist/mcp/server.d.ts +99 -0
  37. package/dist/mcp/server.d.ts.map +1 -0
  38. package/dist/mcp/server.js +168 -0
  39. package/dist/mcp/server.js.map +1 -0
  40. package/dist/read/commands.d.ts +8 -0
  41. package/dist/read/commands.d.ts.map +1 -0
  42. package/dist/read/commands.js +15 -0
  43. package/dist/read/commands.js.map +1 -0
  44. package/dist/read/contract.d.ts +77 -0
  45. package/dist/read/contract.d.ts.map +1 -0
  46. package/dist/read/contract.js +127 -0
  47. package/dist/read/contract.js.map +1 -0
  48. package/dist/read/index.d.ts +2 -0
  49. package/dist/read/index.d.ts.map +1 -0
  50. package/dist/read/index.js +3 -0
  51. package/dist/read/index.js.map +1 -0
  52. package/dist/report/blocker-status.d.ts +12 -0
  53. package/dist/report/blocker-status.d.ts.map +1 -0
  54. package/dist/report/blocker-status.js +34 -0
  55. package/dist/report/blocker-status.js.map +1 -0
  56. package/dist/report/buckets.d.ts +1 -1
  57. package/dist/report/buckets.d.ts.map +1 -1
  58. package/dist/report/buckets.js +4 -2
  59. package/dist/report/buckets.js.map +1 -1
  60. package/dist/report/index.d.ts +1 -0
  61. package/dist/report/index.d.ts.map +1 -1
  62. package/dist/report/index.js +2 -0
  63. package/dist/report/index.js.map +1 -1
  64. package/dist/state/fold.d.ts +8 -2
  65. package/dist/state/fold.d.ts.map +1 -1
  66. package/dist/state/fold.js +15 -5
  67. package/dist/state/fold.js.map +1 -1
  68. package/dist/track.d.ts +4 -1
  69. package/dist/track.d.ts.map +1 -1
  70. package/dist/track.js +55 -17
  71. package/dist/track.js.map +1 -1
  72. package/dist/version.d.ts +1 -1
  73. package/dist/version.d.ts.map +1 -1
  74. package/dist/version.js +17 -1
  75. package/dist/version.js.map +1 -1
  76. package/package.json +6 -3
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/mcp/cli.ts"],"names":[],"mappings":""}
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ // `track-mcp` bin — a stdio read-only MCP server over the current dir's `.track/events.jsonl`.
3
+ import { join } from 'node:path';
4
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
5
+ import { createTrackMcpServer } from './server.js';
6
+ const eventsPath = join(process.cwd(), '.track', 'events.jsonl');
7
+ const server = createTrackMcpServer(eventsPath);
8
+ server.connect(new StdioServerTransport()).catch((error) => {
9
+ process.stderr.write(`track-mcp failed: ${error instanceof Error ? error.message : String(error)}\n`);
10
+ process.exit(1);
11
+ });
12
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/mcp/cli.ts"],"names":[],"mappings":";AACA,+FAA+F;AAC/F,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAEhF,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAElD,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAA;AAChE,MAAM,MAAM,GAAG,oBAAoB,CAAC,UAAU,CAAC,CAAA;AAE/C,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAClE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACrG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
@@ -0,0 +1,99 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { TrackReader } from '../read/contract.js';
3
+ /** Read-only tools, declared as plain JSON Schema (no zod) — the curated read contract over MCP. */
4
+ export declare const READ_TOOLS: readonly [{
5
+ readonly name: "track_report";
6
+ readonly description: "Bucketed backlog report (AWAITED/DROPPED/DONE/TO-DO) as JSON. AWAITED is relative to baselineCommit.";
7
+ readonly inputSchema: {
8
+ readonly type: "object";
9
+ readonly properties: {
10
+ readonly baselineCommit: {
11
+ readonly type: "string";
12
+ readonly description: "Commit the report is evaluated against (acceptance/staleness).";
13
+ };
14
+ readonly requireAccepted: {
15
+ readonly type: "boolean";
16
+ readonly description: "A done item counts as DONE only if acceptance=pass.";
17
+ };
18
+ readonly decisions: {
19
+ readonly type: "boolean";
20
+ readonly description: "Include the decisions view.";
21
+ };
22
+ };
23
+ readonly required: readonly ["baselineCommit"];
24
+ };
25
+ }, {
26
+ readonly name: "track_query";
27
+ readonly description: "Flat, filtered query over the non-decision report rows, as JSON.";
28
+ readonly inputSchema: {
29
+ readonly type: "object";
30
+ readonly properties: {
31
+ readonly baselineCommit: {
32
+ readonly type: "string";
33
+ };
34
+ readonly kind: {
35
+ readonly type: "string";
36
+ readonly enum: readonly ["feature", "bug", "chore"];
37
+ };
38
+ readonly workspace: {
39
+ readonly type: "string";
40
+ };
41
+ readonly bucket: {
42
+ readonly type: "string";
43
+ readonly enum: readonly ["AWAITED", "DROPPED", "DONE", "TO-DO"];
44
+ };
45
+ readonly realization: {
46
+ readonly type: "string";
47
+ readonly enum: readonly ["to-do", "in-progress", "done", "cancelled", "rejected"];
48
+ };
49
+ readonly acceptance: {
50
+ readonly type: "string";
51
+ readonly enum: readonly ["pass", "fail", "unknown", "stale", "waived"];
52
+ };
53
+ };
54
+ readonly required: readonly ["baselineCommit"];
55
+ };
56
+ }, {
57
+ readonly name: "track_validate";
58
+ readonly description: "Recompute the append-only integrity chain (detect-only). Returns {ok, findings}. NOTE: integrity only — unlike the CLI `validate`, it does NOT include prose↔log desync (that would read arbitrary cwd files, breaking the read-only contract).";
59
+ readonly inputSchema: {
60
+ readonly type: "object";
61
+ readonly properties: {};
62
+ };
63
+ }, {
64
+ readonly name: "track_branch_provenance";
65
+ readonly description: "Latest branch.imported provenance for a BRANCH.md locator (or null).";
66
+ readonly inputSchema: {
67
+ readonly type: "object";
68
+ readonly properties: {
69
+ readonly locator: {
70
+ readonly type: "string";
71
+ };
72
+ };
73
+ readonly required: readonly ["locator"];
74
+ };
75
+ }, {
76
+ readonly name: "track_freshness";
77
+ readonly description: "Is the sidecar structurally current with the given BRANCH.md content for a locator?";
78
+ readonly inputSchema: {
79
+ readonly type: "object";
80
+ readonly properties: {
81
+ readonly locator: {
82
+ readonly type: "string";
83
+ };
84
+ readonly content: {
85
+ readonly type: "string";
86
+ };
87
+ };
88
+ readonly required: readonly ["locator", "content"];
89
+ };
90
+ }];
91
+ /**
92
+ * Pure tool dispatch — used by both the server handler and the parity test. Returns the tool's
93
+ * text payload (JSON), identical to the CLI's `--format json` output for report/query. Throws on a
94
+ * missing/invalid argument (the handler turns the throw into an MCP `isError` result).
95
+ */
96
+ export declare function dispatchReadTool(reader: TrackReader, name: string, args: Record<string, unknown>): string;
97
+ /** Build a read-only MCP server bound to one `.track/events.jsonl`. */
98
+ export declare function createTrackMcpServer(eventsPath: string): Server;
99
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAA;AAIlE,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAUjD,oGAAoG;AACpG,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqDb,CAAA;AAmCV;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,WAAW,EACnB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,CAiCR;AAED,uEAAuE;AACvE,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAuB/D"}
@@ -0,0 +1,168 @@
1
+ // Lot v2.3a — read-only MCP server over the shared read command layer (read/commands.ts +
2
+ // TrackReader). Thin adapter: every tool maps 1:1 to a read on a baseline the CALLER supplies
3
+ // (no git in this layer). Read tools are side-effect-free — they never append to the event log.
4
+ //
5
+ // The low-level Server does NOT auto-validate args against the advertised inputSchema, so this
6
+ // module validates every arg itself (enum + type) and surfaces violations as `isError` — matching
7
+ // the CLI's `oneOf` strictness so the two transports never diverge on bad input.
8
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
9
+ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
10
+ import { queryText, reportText } from '../read/commands.js';
11
+ import { TrackReader } from '../read/contract.js';
12
+ import { VERSION } from '../version.js';
13
+ // Allowed enum values — the single source for BOTH the advertised schema and runtime validation.
14
+ const KINDS = ['feature', 'bug', 'chore'];
15
+ const BUCKETS = ['AWAITED', 'DROPPED', 'DONE', 'TO-DO'];
16
+ const REALIZATIONS = ['to-do', 'in-progress', 'done', 'cancelled', 'rejected'];
17
+ const ACCEPTANCES = ['pass', 'fail', 'unknown', 'stale', 'waived'];
18
+ /** Read-only tools, declared as plain JSON Schema (no zod) — the curated read contract over MCP. */
19
+ export const READ_TOOLS = [
20
+ {
21
+ name: 'track_report',
22
+ description: 'Bucketed backlog report (AWAITED/DROPPED/DONE/TO-DO) as JSON. AWAITED is relative to baselineCommit.',
23
+ inputSchema: {
24
+ type: 'object',
25
+ properties: {
26
+ baselineCommit: { type: 'string', description: 'Commit the report is evaluated against (acceptance/staleness).' },
27
+ requireAccepted: { type: 'boolean', description: 'A done item counts as DONE only if acceptance=pass.' },
28
+ decisions: { type: 'boolean', description: 'Include the decisions view.' },
29
+ },
30
+ required: ['baselineCommit'],
31
+ },
32
+ },
33
+ {
34
+ name: 'track_query',
35
+ description: 'Flat, filtered query over the non-decision report rows, as JSON.',
36
+ inputSchema: {
37
+ type: 'object',
38
+ properties: {
39
+ baselineCommit: { type: 'string' },
40
+ kind: { type: 'string', enum: KINDS },
41
+ workspace: { type: 'string' },
42
+ bucket: { type: 'string', enum: BUCKETS },
43
+ realization: { type: 'string', enum: REALIZATIONS },
44
+ acceptance: { type: 'string', enum: ACCEPTANCES },
45
+ },
46
+ required: ['baselineCommit'],
47
+ },
48
+ },
49
+ {
50
+ name: 'track_validate',
51
+ description: 'Recompute the append-only integrity chain (detect-only). Returns {ok, findings}. NOTE: integrity only — unlike the CLI `validate`, it does NOT include prose↔log desync (that would read arbitrary cwd files, breaking the read-only contract).',
52
+ inputSchema: { type: 'object', properties: {} },
53
+ },
54
+ {
55
+ name: 'track_branch_provenance',
56
+ description: 'Latest branch.imported provenance for a BRANCH.md locator (or null).',
57
+ inputSchema: {
58
+ type: 'object',
59
+ properties: { locator: { type: 'string' } },
60
+ required: ['locator'],
61
+ },
62
+ },
63
+ {
64
+ name: 'track_freshness',
65
+ description: 'Is the sidecar structurally current with the given BRANCH.md content for a locator?',
66
+ inputSchema: {
67
+ type: 'object',
68
+ properties: { locator: { type: 'string' }, content: { type: 'string' } },
69
+ required: ['locator', 'content'],
70
+ },
71
+ },
72
+ ];
73
+ function reqStr(args, key) {
74
+ const v = args[key];
75
+ if (typeof v !== 'string')
76
+ throw new Error(`tool argument "${key}" must be a string`);
77
+ return v;
78
+ }
79
+ function optBool(args, key) {
80
+ const v = args[key];
81
+ if (v === undefined)
82
+ return undefined;
83
+ if (typeof v !== 'boolean')
84
+ throw new Error(`tool argument "${key}" must be a boolean`);
85
+ return v;
86
+ }
87
+ function optStr(args, key) {
88
+ const v = args[key];
89
+ if (v === undefined)
90
+ return undefined;
91
+ if (typeof v !== 'string')
92
+ throw new Error(`tool argument "${key}" must be a string`);
93
+ return v;
94
+ }
95
+ function optEnum(args, key, allowed) {
96
+ const v = args[key];
97
+ if (v === undefined)
98
+ return undefined;
99
+ if (typeof v !== 'string' || !allowed.includes(v)) {
100
+ throw new Error(`tool argument "${key}" must be one of: ${allowed.join(', ')}`);
101
+ }
102
+ return v;
103
+ }
104
+ /**
105
+ * Pure tool dispatch — used by both the server handler and the parity test. Returns the tool's
106
+ * text payload (JSON), identical to the CLI's `--format json` output for report/query. Throws on a
107
+ * missing/invalid argument (the handler turns the throw into an MCP `isError` result).
108
+ */
109
+ export function dispatchReadTool(reader, name, args) {
110
+ switch (name) {
111
+ case 'track_report': {
112
+ const options = {
113
+ baselineCommit: reqStr(args, 'baselineCommit'),
114
+ requireAccepted: optBool(args, 'requireAccepted') ?? false,
115
+ decisions: optBool(args, 'decisions') ?? false,
116
+ };
117
+ return reportText(reader, options, 'json');
118
+ }
119
+ case 'track_query': {
120
+ const filter = {};
121
+ const kind = optEnum(args, 'kind', KINDS);
122
+ if (kind !== undefined)
123
+ filter.kind = kind;
124
+ const workspace = optStr(args, 'workspace');
125
+ if (workspace !== undefined)
126
+ filter.workspace = workspace;
127
+ const bucket = optEnum(args, 'bucket', BUCKETS);
128
+ if (bucket !== undefined)
129
+ filter.bucket = bucket;
130
+ const realization = optEnum(args, 'realization', REALIZATIONS);
131
+ if (realization !== undefined)
132
+ filter.realization = realization;
133
+ const acceptance = optEnum(args, 'acceptance', ACCEPTANCES);
134
+ if (acceptance !== undefined)
135
+ filter.acceptance = acceptance;
136
+ return queryText(reader, filter, { baselineCommit: reqStr(args, 'baselineCommit') }, 'json');
137
+ }
138
+ case 'track_validate':
139
+ return JSON.stringify(reader.validate(), null, 2);
140
+ case 'track_branch_provenance':
141
+ return JSON.stringify(reader.branchProvenance(reqStr(args, 'locator')) ?? null, null, 2);
142
+ case 'track_freshness':
143
+ return JSON.stringify(reader.freshness(reqStr(args, 'content'), reqStr(args, 'locator')), null, 2);
144
+ default:
145
+ throw new Error(`unknown tool: ${name}`);
146
+ }
147
+ }
148
+ /** Build a read-only MCP server bound to one `.track/events.jsonl`. */
149
+ export function createTrackMcpServer(eventsPath) {
150
+ const reader = new TrackReader(eventsPath);
151
+ const server = new Server({ name: '@sentropic/track', version: VERSION }, { capabilities: { tools: {} } });
152
+ server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: READ_TOOLS }));
153
+ server.setRequestHandler(CallToolRequestSchema, (request) => {
154
+ const { name, arguments: args } = request.params;
155
+ try {
156
+ const text = dispatchReadTool(reader, name, (args ?? {}));
157
+ return { content: [{ type: 'text', text }] };
158
+ }
159
+ catch (error) {
160
+ return {
161
+ content: [{ type: 'text', text: error instanceof Error ? error.message : String(error) }],
162
+ isError: true,
163
+ };
164
+ }
165
+ });
166
+ return server;
167
+ }
168
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { type Format } from '../report/format.js';
2
+ import type { QueryFilter, ReportOptions } from '../report/build.js';
3
+ import type { TrackReader } from './contract.js';
4
+ /** `report` rendered exactly as the CLI renders it (SPEC §7). */
5
+ export declare function reportText(reader: TrackReader, options: ReportOptions, format: Format): string;
6
+ /** `query` rendered exactly as the CLI renders it: raw JSON for `json`, else the row formatter. */
7
+ export declare function queryText(reader: TrackReader, filter: QueryFilter, options: ReportOptions, format: Format): string;
8
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/read/commands.ts"],"names":[],"mappings":"AAKA,OAAO,EAA4B,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAC3E,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAEhD,iEAAiE;AACjE,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE9F;AAED,mGAAmG;AACnG,wBAAgB,SAAS,CACvB,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,MAAM,GACb,MAAM,CAGR"}
@@ -0,0 +1,15 @@
1
+ // Lot v2.3a — transport-agnostic READ command layer. CLI and the MCP server are thin adapters over
2
+ // these pure functions: a `TrackReader` (no git/fs beyond the event log) + the SAME formatters, with
3
+ // the adapter supplying `baselineCommit` (CLI from git HEAD, MCP from a tool argument). This is what
4
+ // makes CLI≡MCP parity STRUCTURAL (one layer), not coincidental.
5
+ import { formatReport, formatRows } from '../report/format.js';
6
+ /** `report` rendered exactly as the CLI renders it (SPEC §7). */
7
+ export function reportText(reader, options, format) {
8
+ return formatReport(reader.report(options), format);
9
+ }
10
+ /** `query` rendered exactly as the CLI renders it: raw JSON for `json`, else the row formatter. */
11
+ export function queryText(reader, filter, options, format) {
12
+ const rows = reader.query(filter, options);
13
+ return format === 'json' ? `${JSON.stringify(rows, null, 2)}\n` : formatRows(rows, format);
14
+ }
15
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/read/commands.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,qGAAqG;AACrG,qGAAqG;AACrG,iEAAiE;AAEjE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAe,MAAM,qBAAqB,CAAA;AAI3E,iEAAiE;AACjE,MAAM,UAAU,UAAU,CAAC,MAAmB,EAAE,OAAsB,EAAE,MAAc;IACpF,OAAO,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAA;AACrD,CAAC;AAED,mGAAmG;AACnG,MAAM,UAAU,SAAS,CACvB,MAAmB,EACnB,MAAmB,EACnB,OAAsB,EACtB,MAAc;IAEd,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC1C,OAAO,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;AAC5F,CAAC"}
@@ -0,0 +1,77 @@
1
+ import type { Sha256 } from '../events/types.js';
2
+ import { type IntegrityResult } from '../events/validate.js';
3
+ import { type QueryFilter, type Report, type ReportOptions, type ReportRow } from '../report/build.js';
4
+ /**
5
+ * Semver of the skill-facing READ contract.
6
+ *
7
+ * **Policy (PLAN-v2 D7 — additive-only):** within a major, the `TrackReader` surface and the
8
+ * shapes it returns may only GROW (new methods / new optional fields); nothing is removed or
9
+ * repurposed without a major bump. Consumers gate on `reader.contractVersion`.
10
+ */
11
+ export declare const READ_CONTRACT_VERSION = "1.0.0";
12
+ /** Provenance of the last `branch.imported` for a locator (drawn from the raw event log). */
13
+ export interface BranchProvenance {
14
+ locator: string;
15
+ branchSlug: string;
16
+ /** sha256 of the raw BRANCH.md bytes at import (audit). */
17
+ sourceHash: Sha256;
18
+ /** sha256 of the reconciled structural projection — drives freshness. */
19
+ structureHash: Sha256;
20
+ at: string;
21
+ }
22
+ /** Result of comparing live BRANCH.md content against the last imported structure. */
23
+ export type Freshness = {
24
+ status: 'fresh';
25
+ structureHash: Sha256;
26
+ } | {
27
+ status: 'stale';
28
+ expected: Sha256;
29
+ actual: Sha256;
30
+ } | {
31
+ status: 'absent';
32
+ };
33
+ /** Thrown by `requireFresh` when the sidecar is unsafe to consume (stale, absent, or tampered). */
34
+ export declare class StaleSidecarError extends Error {
35
+ readonly detail: {
36
+ locator: string;
37
+ freshness: Freshness;
38
+ integrityOk: boolean;
39
+ };
40
+ constructor(message: string, detail: {
41
+ locator: string;
42
+ freshness: Freshness;
43
+ integrityOk: boolean;
44
+ });
45
+ }
46
+ /**
47
+ * Read-only, versioned consumption surface over a frozen track log. Holds NO `git` and only reads
48
+ * the event file/head via `fs` — a baseline commit is supplied by the caller via `ReportOptions`
49
+ * (the adapter owns `git`, not this layer — PLAN-v2 stack note).
50
+ */
51
+ export declare class TrackReader {
52
+ private readonly eventsPath;
53
+ readonly contractVersion = "1.0.0";
54
+ private readonly store;
55
+ constructor(eventsPath: string);
56
+ private events;
57
+ /** Bucketed backlog report (SPEC §7). */
58
+ report(options: ReportOptions): Report;
59
+ /** Flat, filtered query over report rows (SPEC §6). */
60
+ query(filter: QueryFilter, options: ReportOptions): ReportRow[];
61
+ /** Recompute the integrity chain (SPEC §3) — pure detector, never repairs. */
62
+ validate(): IntegrityResult;
63
+ /** Latest VALID `branch.imported` provenance for `locator`, or `undefined`. */
64
+ branchProvenance(locator: string): BranchProvenance | undefined;
65
+ /** Compare current BRANCH.md `content` against the last imported structure for `locator`. */
66
+ freshness(content: string, locator: string): Freshness;
67
+ /**
68
+ * Fail-closed guard for skill consumers (`scope-check`/`lot-gate`): throws `StaleSidecarError`
69
+ * unless the sidecar is FRESH against the given BRANCH.md AND the log integrity is intact.
70
+ * BRANCH.md stays master — a stale or tampered sidecar must NEVER be trusted as the backlog.
71
+ * Reads the log ONCE for both checks.
72
+ */
73
+ requireFresh(content: string, locator: string): void;
74
+ private provenanceFrom;
75
+ private freshnessFrom;
76
+ }
77
+ //# sourceMappingURL=contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../src/read/contract.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,oBAAoB,CAAA;AAC5D,OAAO,EAAY,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACtE,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,MAAM,EACX,KAAK,aAAa,EAClB,KAAK,SAAS,EACf,MAAM,oBAAoB,CAAA;AAG3B;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,UAAU,CAAA;AAE5C,6FAA6F;AAC7F,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAA;IAClB,yEAAyE;IACzE,aAAa,EAAE,MAAM,CAAA;IACrB,EAAE,EAAE,MAAM,CAAA;CACX;AAED,sFAAsF;AACtF,MAAM,MAAM,SAAS,GACjB;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,MAAM,EAAE,QAAQ,CAAA;CAAE,CAAA;AAExB,mGAAmG;AACnG,qBAAa,iBAAkB,SAAQ,KAAK;IAGxC,QAAQ,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,SAAS,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE;gBADhF,OAAO,EAAE,MAAM,EACN,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,SAAS,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE;CAKnF;AAID;;;;GAIG;AACH,qBAAa,WAAW;IAIV,OAAO,CAAC,QAAQ,CAAC,UAAU;IAHvC,QAAQ,CAAC,eAAe,WAAwB;IAChD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAY;gBAEL,UAAU,EAAE,MAAM;IAI/C,OAAO,CAAC,MAAM;IAId,yCAAyC;IACzC,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM;IAItC,uDAAuD;IACvD,KAAK,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,GAAG,SAAS,EAAE;IAI/D,8EAA8E;IAC9E,QAAQ,IAAI,eAAe;IAI3B,+EAA+E;IAC/E,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAI/D,6FAA6F;IAC7F,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS;IAItD;;;;;OAKG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAcpD,OAAO,CAAC,cAAc;IA6BtB,OAAO,CAAC,aAAa;CAatB"}
@@ -0,0 +1,127 @@
1
+ // Lot v2.0 — curated, VERSIONED read contract (PLAN-v2 M2a).
2
+ //
3
+ // The MVP barrel (`src/index.ts`) still `export *`s internals; that is the LIBRARY surface. THIS
4
+ // module is the *skill-facing read contract* (`scope-check` / `lot-gate` consume it via the
5
+ // `@sentropic/track/read` subpath) — reads only (report / query / validate / branch provenance /
6
+ // freshness), never mutations, plus a fail-closed `requireFresh` guard so a stale OR tampered
7
+ // sidecar can never become de-facto master over BRANCH.md (the source of truth — SPEC §5,
8
+ // INTENTION §9 pin).
9
+ import { branchId } from '../branch/parse.js';
10
+ import { branchSignature } from '../branch/signature.js';
11
+ import { readHead } from '../events/head.js';
12
+ import { EventStore } from '../events/store.js';
13
+ import { validate } from '../events/validate.js';
14
+ import { buildReport, query as runQuery, } from '../report/build.js';
15
+ import { fold } from '../state/fold.js';
16
+ /**
17
+ * Semver of the skill-facing READ contract.
18
+ *
19
+ * **Policy (PLAN-v2 D7 — additive-only):** within a major, the `TrackReader` surface and the
20
+ * shapes it returns may only GROW (new methods / new optional fields); nothing is removed or
21
+ * repurposed without a major bump. Consumers gate on `reader.contractVersion`.
22
+ */
23
+ export const READ_CONTRACT_VERSION = '1.0.0';
24
+ /** Thrown by `requireFresh` when the sidecar is unsafe to consume (stale, absent, or tampered). */
25
+ export class StaleSidecarError extends Error {
26
+ detail;
27
+ constructor(message, detail) {
28
+ super(message);
29
+ this.detail = detail;
30
+ this.name = 'StaleSidecarError';
31
+ }
32
+ }
33
+ const isSha256 = (v) => typeof v === 'string' && v.startsWith('sha256:');
34
+ /**
35
+ * Read-only, versioned consumption surface over a frozen track log. Holds NO `git` and only reads
36
+ * the event file/head via `fs` — a baseline commit is supplied by the caller via `ReportOptions`
37
+ * (the adapter owns `git`, not this layer — PLAN-v2 stack note).
38
+ */
39
+ export class TrackReader {
40
+ eventsPath;
41
+ contractVersion = READ_CONTRACT_VERSION;
42
+ store;
43
+ constructor(eventsPath) {
44
+ this.eventsPath = eventsPath;
45
+ this.store = new EventStore(eventsPath);
46
+ }
47
+ events() {
48
+ return this.store.readAll();
49
+ }
50
+ /** Bucketed backlog report (SPEC §7). */
51
+ report(options) {
52
+ return buildReport(fold(this.events()), options);
53
+ }
54
+ /** Flat, filtered query over report rows (SPEC §6). */
55
+ query(filter, options) {
56
+ return runQuery(fold(this.events()), filter, options);
57
+ }
58
+ /** Recompute the integrity chain (SPEC §3) — pure detector, never repairs. */
59
+ validate() {
60
+ return validate(this.events(), readHead(this.eventsPath));
61
+ }
62
+ /** Latest VALID `branch.imported` provenance for `locator`, or `undefined`. */
63
+ branchProvenance(locator) {
64
+ return this.provenanceFrom(this.events(), locator);
65
+ }
66
+ /** Compare current BRANCH.md `content` against the last imported structure for `locator`. */
67
+ freshness(content, locator) {
68
+ return this.freshnessFrom(this.events(), content, locator);
69
+ }
70
+ /**
71
+ * Fail-closed guard for skill consumers (`scope-check`/`lot-gate`): throws `StaleSidecarError`
72
+ * unless the sidecar is FRESH against the given BRANCH.md AND the log integrity is intact.
73
+ * BRANCH.md stays master — a stale or tampered sidecar must NEVER be trusted as the backlog.
74
+ * Reads the log ONCE for both checks.
75
+ */
76
+ requireFresh(content, locator) {
77
+ const events = this.events();
78
+ const freshness = this.freshnessFrom(events, content, locator);
79
+ const integrity = validate(events, readHead(this.eventsPath));
80
+ if (freshness.status !== 'fresh' || !integrity.ok) {
81
+ throw new StaleSidecarError(`track sidecar unsafe to consume for "${locator}": freshness=${freshness.status}, integrity=${integrity.ok ? 'ok' : 'broken'} — BRANCH.md stays master`, { locator, freshness, integrityOk: integrity.ok });
82
+ }
83
+ }
84
+ // ---- internals (single-read helpers) ----
85
+ provenanceFrom(events, locator) {
86
+ // The LATEST `branch.imported` for the locator is authoritative. Find it regardless of shape,
87
+ // THEN validate its payload. A malformed latest stamp fails CLOSED (→ undefined → `absent` →
88
+ // requireFresh throws); we must NOT fall back to an older valid stamp, or a re-pointed/tampered
89
+ // latest import could read as an earlier "fresh" state even though validate passes.
90
+ let latest;
91
+ for (const e of events) {
92
+ if (e.type !== 'branch.imported')
93
+ continue;
94
+ if (e.payload.locator !== locator)
95
+ continue;
96
+ latest = e;
97
+ }
98
+ if (!latest)
99
+ return undefined;
100
+ const p = latest.payload;
101
+ if (typeof p.branchSlug !== 'string' || !isSha256(p.sourceHash) || !isSha256(p.structureHash)) {
102
+ return undefined;
103
+ }
104
+ return {
105
+ locator,
106
+ branchSlug: p.branchSlug,
107
+ sourceHash: p.sourceHash,
108
+ structureHash: p.structureHash,
109
+ at: latest.at,
110
+ };
111
+ }
112
+ freshnessFrom(events, content, locator) {
113
+ const prov = this.provenanceFrom(events, locator);
114
+ if (!prov)
115
+ return { status: 'absent' };
116
+ const actual = branchSignature(content);
117
+ // Freshness is only AUTHORITATIVE when the content carries a `BR-NN` id — the one identity
118
+ // import and reader provably agree on. Without it, the import's branchSlug may derive from a
119
+ // fileSlug (or title) the reader cannot reproduce, so even an exact structureHash match could be
120
+ // coincidental → fail closed (stale), never fresh.
121
+ if (branchId(content) === undefined || actual !== prov.structureHash) {
122
+ return { status: 'stale', expected: prov.structureHash, actual };
123
+ }
124
+ return { status: 'fresh', structureHash: actual };
125
+ }
126
+ }
127
+ //# sourceMappingURL=contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../../src/read/contract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { READ_CONTRACT_VERSION, StaleSidecarError, TrackReader, type BranchProvenance, type Freshness, } from './contract.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/read/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,WAAW,EACX,KAAK,gBAAgB,EACrB,KAAK,SAAS,GACf,MAAM,eAAe,CAAA"}
@@ -0,0 +1,3 @@
1
+ // Read contract barrel (Lot v2.0) — the skill-facing, versioned, read-only surface.
2
+ export { READ_CONTRACT_VERSION, StaleSidecarError, TrackReader, } from './contract.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/read/index.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,WAAW,GAGZ,MAAM,eAAe,CAAA"}
@@ -0,0 +1,12 @@
1
+ import type { BlockerState } from '../model/blocker.js';
2
+ import type { ItemId } from '../model/item.js';
3
+ import type { State } from '../state/fold.js';
4
+ /**
5
+ * Authoritative openness of a blocker at a given `baselineCommit`. `resolvedByEvent` hard-closes
6
+ * first (manual/decision). For `linked-accepted` (dependency), openness = ref is not accepted at
7
+ * the baseline (revocable). All other rules fall back to the fold scalar `blocker.open`.
8
+ */
9
+ export declare function effectiveBlockerOpen(state: State, blocker: BlockerState, baselineCommit: string): boolean;
10
+ /** Open blockers targeting `itemId`, evaluated against `baselineCommit` (commit-relative). */
11
+ export declare function effectiveOpenBlockersForItem(state: State, itemId: ItemId, baselineCommit: string): BlockerState[];
12
+ //# sourceMappingURL=blocker-status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocker-status.d.ts","sourceRoot":"","sources":["../../src/report/blocker-status.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AAK7C;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,YAAY,EACrB,cAAc,EAAE,MAAM,GACrB,OAAO,CAMT;AAED,8FAA8F;AAC9F,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,GACrB,YAAY,EAAE,CAIhB"}
@@ -0,0 +1,34 @@
1
+ // Lot v2.2b — commit-relative ("hybrid-A") blocker openness for the `linked-accepted` rule.
2
+ //
3
+ // `fold()` is baseline-free, so it cannot evaluate acceptance (which is a function of
4
+ // (state, baselineCommit) and is REVOCABLE). It therefore folds a `linked-accepted` blocker as
5
+ // conservatively OPEN (never falsely closed). The AUTHORITATIVE openness is DERIVED HERE, at
6
+ // report/query time, against the caller's `baselineCommit` — so a `linked-accepted` gate closes
7
+ // only while its ref is accepted and RE-OPENS for free (no event) when the ref regresses.
8
+ // Never call this from `fold` — it would pin the commit axis and break determinism.
9
+ //
10
+ // Settle-once rules (`decision`, `manual`, `linked-done`) keep their fold-scalar `blocker.open`.
11
+ // Decision blockers ("blocked until a decision settles") are already covered by `kind:"decision"`
12
+ // + the go/no-go batch that resolves them (`resolvedByEvent`), so no separate `decision-settled`
13
+ // dependency rule is introduced here (see docs/plan/v2.2a-linked-accepted-DESIGN.md §4).
14
+ import { acceptanceStatus } from '../accept/status.js';
15
+ /** Acceptance statuses that CLOSE a `linked-accepted` gate — strict pass-only (owner policy P3). */
16
+ const ACCEPTED_CLOSES = new Set(['pass']);
17
+ /**
18
+ * Authoritative openness of a blocker at a given `baselineCommit`. `resolvedByEvent` hard-closes
19
+ * first (manual/decision). For `linked-accepted` (dependency), openness = ref is not accepted at
20
+ * the baseline (revocable). All other rules fall back to the fold scalar `blocker.open`.
21
+ */
22
+ export function effectiveBlockerOpen(state, blocker, baselineCommit) {
23
+ if (blocker.resolvedByEvent)
24
+ return false;
25
+ if (blocker.kind === 'dependency' && blocker.resolutionRule === 'linked-accepted') {
26
+ return !ACCEPTED_CLOSES.has(acceptanceStatus(state, blocker.ref, baselineCommit));
27
+ }
28
+ return blocker.open;
29
+ }
30
+ /** Open blockers targeting `itemId`, evaluated against `baselineCommit` (commit-relative). */
31
+ export function effectiveOpenBlockersForItem(state, itemId, baselineCommit) {
32
+ return [...state.blockers.values()].filter((b) => b.targetId === itemId && effectiveBlockerOpen(state, b, baselineCommit));
33
+ }
34
+ //# sourceMappingURL=blocker-status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocker-status.js","sourceRoot":"","sources":["../../src/report/blocker-status.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,EAAE;AACF,sFAAsF;AACtF,+FAA+F;AAC/F,6FAA6F;AAC7F,gGAAgG;AAChG,0FAA0F;AAC1F,oFAAoF;AACpF,EAAE;AACF,iGAAiG;AACjG,kGAAkG;AAClG,iGAAiG;AACjG,yFAAyF;AAEzF,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAMtD,oGAAoG;AACpG,MAAM,eAAe,GAAkC,IAAI,GAAG,CAAmB,CAAC,MAAM,CAAC,CAAC,CAAA;AAE1F;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAClC,KAAY,EACZ,OAAqB,EACrB,cAAsB;IAEtB,IAAI,OAAO,CAAC,eAAe;QAAE,OAAO,KAAK,CAAA;IACzC,IAAI,OAAO,CAAC,IAAI,KAAK,YAAY,IAAI,OAAO,CAAC,cAAc,KAAK,iBAAiB,EAAE,CAAC;QAClF,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAA;IACnF,CAAC;IACD,OAAO,OAAO,CAAC,IAAI,CAAA;AACrB,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,4BAA4B,CAC1C,KAAY,EACZ,MAAc,EACd,cAAsB;IAEtB,OAAO,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CACxC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,IAAI,oBAAoB,CAAC,KAAK,EAAE,CAAC,EAAE,cAAc,CAAC,CAC/E,CAAA;AACH,CAAC"}