frontier-mcp 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.
Files changed (115) hide show
  1. package/README.md +104 -0
  2. package/dist/bin.d.ts +3 -0
  3. package/dist/bin.d.ts.map +1 -0
  4. package/dist/bin.js +10 -0
  5. package/dist/bin.js.map +1 -0
  6. package/dist/domain.d.ts +230 -0
  7. package/dist/domain.d.ts.map +1 -0
  8. package/dist/domain.js +23 -0
  9. package/dist/domain.js.map +1 -0
  10. package/dist/edges.d.ts +35 -0
  11. package/dist/edges.d.ts.map +1 -0
  12. package/dist/edges.js +61 -0
  13. package/dist/edges.js.map +1 -0
  14. package/dist/frontier.d.ts +12 -0
  15. package/dist/frontier.d.ts.map +1 -0
  16. package/dist/frontier.js +34 -0
  17. package/dist/frontier.js.map +1 -0
  18. package/dist/index.d.ts +5 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +2 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/server.d.ts +29 -0
  23. package/dist/server.d.ts.map +1 -0
  24. package/dist/server.js +210 -0
  25. package/dist/server.js.map +1 -0
  26. package/dist/storage/driver.d.ts +131 -0
  27. package/dist/storage/driver.d.ts.map +1 -0
  28. package/dist/storage/driver.js +43 -0
  29. package/dist/storage/driver.js.map +1 -0
  30. package/dist/storage/markdown/create.d.ts +52 -0
  31. package/dist/storage/markdown/create.d.ts.map +1 -0
  32. package/dist/storage/markdown/create.js +298 -0
  33. package/dist/storage/markdown/create.js.map +1 -0
  34. package/dist/storage/markdown/driver.d.ts +7 -0
  35. package/dist/storage/markdown/driver.d.ts.map +1 -0
  36. package/dist/storage/markdown/driver.js +492 -0
  37. package/dist/storage/markdown/driver.js.map +1 -0
  38. package/dist/storage/markdown/frontmatter.d.ts +17 -0
  39. package/dist/storage/markdown/frontmatter.d.ts.map +1 -0
  40. package/dist/storage/markdown/frontmatter.js +36 -0
  41. package/dist/storage/markdown/frontmatter.js.map +1 -0
  42. package/dist/storage/markdown/header-doc.d.ts +56 -0
  43. package/dist/storage/markdown/header-doc.d.ts.map +1 -0
  44. package/dist/storage/markdown/header-doc.js +287 -0
  45. package/dist/storage/markdown/header-doc.js.map +1 -0
  46. package/dist/storage/markdown/legacy.d.ts +16 -0
  47. package/dist/storage/markdown/legacy.d.ts.map +1 -0
  48. package/dist/storage/markdown/legacy.js +152 -0
  49. package/dist/storage/markdown/legacy.js.map +1 -0
  50. package/dist/storage/markdown/migrate.d.ts +27 -0
  51. package/dist/storage/markdown/migrate.d.ts.map +1 -0
  52. package/dist/storage/markdown/migrate.js +158 -0
  53. package/dist/storage/markdown/migrate.js.map +1 -0
  54. package/dist/storage/markdown/serialize.d.ts +37 -0
  55. package/dist/storage/markdown/serialize.d.ts.map +1 -0
  56. package/dist/storage/markdown/serialize.js +321 -0
  57. package/dist/storage/markdown/serialize.js.map +1 -0
  58. package/dist/storage/markdown/ticket.d.ts +12 -0
  59. package/dist/storage/markdown/ticket.d.ts.map +1 -0
  60. package/dist/storage/markdown/ticket.js +97 -0
  61. package/dist/storage/markdown/ticket.js.map +1 -0
  62. package/dist/storage/markdown/write.d.ts +42 -0
  63. package/dist/storage/markdown/write.d.ts.map +1 -0
  64. package/dist/storage/markdown/write.js +134 -0
  65. package/dist/storage/markdown/write.js.map +1 -0
  66. package/dist/tools/create-tickets.d.ts +63 -0
  67. package/dist/tools/create-tickets.d.ts.map +1 -0
  68. package/dist/tools/create-tickets.js +161 -0
  69. package/dist/tools/create-tickets.js.map +1 -0
  70. package/dist/tools/edit-map.d.ts +29 -0
  71. package/dist/tools/edit-map.d.ts.map +1 -0
  72. package/dist/tools/edit-map.js +81 -0
  73. package/dist/tools/edit-map.js.map +1 -0
  74. package/dist/tools/get-board.d.ts +29 -0
  75. package/dist/tools/get-board.d.ts.map +1 -0
  76. package/dist/tools/get-board.js +190 -0
  77. package/dist/tools/get-board.js.map +1 -0
  78. package/dist/tools/get-tickets.d.ts +14 -0
  79. package/dist/tools/get-tickets.d.ts.map +1 -0
  80. package/dist/tools/get-tickets.js +56 -0
  81. package/dist/tools/get-tickets.js.map +1 -0
  82. package/dist/tools/list-efforts.d.ts +16 -0
  83. package/dist/tools/list-efforts.d.ts.map +1 -0
  84. package/dist/tools/list-efforts.js +38 -0
  85. package/dist/tools/list-efforts.js.map +1 -0
  86. package/dist/tools/migrate-effort.d.ts +11 -0
  87. package/dist/tools/migrate-effort.d.ts.map +1 -0
  88. package/dist/tools/migrate-effort.js +54 -0
  89. package/dist/tools/migrate-effort.js.map +1 -0
  90. package/dist/tools/spec.d.ts +12 -0
  91. package/dist/tools/spec.d.ts.map +1 -0
  92. package/dist/tools/spec.js +27 -0
  93. package/dist/tools/spec.js.map +1 -0
  94. package/dist/tools/update-ticket.d.ts +62 -0
  95. package/dist/tools/update-ticket.d.ts.map +1 -0
  96. package/dist/tools/update-ticket.js +164 -0
  97. package/dist/tools/update-ticket.js.map +1 -0
  98. package/dist/tracker-doc.d.ts +10 -0
  99. package/dist/tracker-doc.d.ts.map +1 -0
  100. package/dist/tracker-doc.js +16 -0
  101. package/dist/tracker-doc.js.map +1 -0
  102. package/dist/workspace-index.d.ts +39 -0
  103. package/dist/workspace-index.d.ts.map +1 -0
  104. package/dist/workspace-index.js +83 -0
  105. package/dist/workspace-index.js.map +1 -0
  106. package/dist/workspace-watcher.d.ts +24 -0
  107. package/dist/workspace-watcher.d.ts.map +1 -0
  108. package/dist/workspace-watcher.js +87 -0
  109. package/dist/workspace-watcher.js.map +1 -0
  110. package/dist/workspace.d.ts +23 -0
  111. package/dist/workspace.d.ts.map +1 -0
  112. package/dist/workspace.js +70 -0
  113. package/dist/workspace.js.map +1 -0
  114. package/docs/agents/issue-tracker.md +141 -0
  115. package/package.json +56 -0
package/dist/server.js ADDED
@@ -0,0 +1,210 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { frontierOf } from './frontier.js';
3
+ import { createIndexRegistry } from './workspace-index.js';
4
+ import { createMarkdownDriver } from './storage/markdown/driver.js';
5
+ import { NoSuchMap, RevisionMismatch } from './storage/driver.js';
6
+ import { createTicketsDescription, createTicketsInputSchema, planBatch, renderCreated, } from './tools/create-tickets.js';
7
+ import { editMapDescription, editMapInputSchema, mapEditFor, renderMap } from './tools/edit-map.js';
8
+ import { boardFor, getBoardDescription, getBoardInputSchema, renderBoard, } from './tools/get-board.js';
9
+ import { getTicketsDescription, getTicketsInputSchema, renderTickets, } from './tools/get-tickets.js';
10
+ import { listEffortsDescription, listEffortsInputSchema, renderEfforts, } from './tools/list-efforts.js';
11
+ import { migrateEffortDescription, migrateEffortInputSchema, renderMigration, } from './tools/migrate-effort.js';
12
+ import { renderSpec, specDescription, specInputSchema } from './tools/spec.js';
13
+ import { editFor, renderUpdate, updateTicketDescription, updateTicketInputSchema, } from './tools/update-ticket.js';
14
+ import { readTrackerDoc, TRACKER_DOC_URI } from './tracker-doc.js';
15
+ import { resolveWorkspace } from './workspace.js';
16
+ export const SERVER_NAME = 'frontier';
17
+ export const SERVER_VERSION = '0.1.0';
18
+ /** Injectable so a test can pin the moment a claim was taken. */
19
+ function now() {
20
+ return new Date().toISOString();
21
+ }
22
+ export function createFrontierMCP(options = {}) {
23
+ const context = {
24
+ cwd: options.cwd ?? process.cwd(),
25
+ env: options.env ?? process.env,
26
+ };
27
+ const registry = createIndexRegistry(options.createDriver ?? createMarkdownDriver, options.watcherDebounceMs === undefined ? {} : { watcherDebounceMs: options.watcherDebounceMs });
28
+ const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION }, { capabilities: { tools: {}, resources: {} } });
29
+ server.registerResource('tracker-doc', TRACKER_DOC_URI, {
30
+ title: 'Issue tracker configuration',
31
+ description: 'Tracker conventions and FrontierMCP tool mapping. Read once at setup; file conventions work as fallback without the server.',
32
+ mimeType: 'text/markdown',
33
+ }, async () => ({
34
+ contents: [{ uri: TRACKER_DOC_URI, mimeType: 'text/markdown', text: readTrackerDoc() }],
35
+ }));
36
+ server.registerTool('list_efforts', {
37
+ title: 'List Efforts',
38
+ description: listEffortsDescription,
39
+ inputSchema: listEffortsInputSchema,
40
+ annotations: { readOnlyHint: true },
41
+ }, async ({ root }) => {
42
+ const workspace = resolveWorkspace(root, context);
43
+ const index = registry.forWorkspace(workspace);
44
+ const [efforts, tickets] = await Promise.all([index.efforts(), index.tickets()]);
45
+ return {
46
+ content: [{ type: 'text', text: renderEfforts(workspace, efforts, frontierOf(tickets)) }],
47
+ };
48
+ });
49
+ server.registerTool('get_board', {
50
+ title: 'Get Board',
51
+ description: getBoardDescription,
52
+ inputSchema: getBoardInputSchema,
53
+ annotations: { readOnlyHint: true },
54
+ }, async ({ effort: slug, root }) => {
55
+ const workspace = resolveWorkspace(root, context);
56
+ const index = registry.forWorkspace(workspace);
57
+ const [efforts, tickets] = await Promise.all([index.efforts(), index.tickets()]);
58
+ const effort = efforts.find(candidate => candidate.slug === slug);
59
+ if (effort === undefined) {
60
+ throw new Error(`No Effort '${slug}' in ${workspace}. Known: ${efforts.map(e => e.slug).join(', ') || '(none)'}`);
61
+ }
62
+ return { content: [{ type: 'text', text: renderBoard(boardFor(effort, tickets)) }] };
63
+ });
64
+ server.registerTool('get_tickets', {
65
+ title: 'Get Tickets',
66
+ description: getTicketsDescription,
67
+ inputSchema: getTicketsInputSchema,
68
+ annotations: { readOnlyHint: true },
69
+ }, async ({ ids, root }) => {
70
+ const workspace = resolveWorkspace(root, context);
71
+ const tickets = await registry.forWorkspace(workspace).tickets();
72
+ const wanted = new Set(ids);
73
+ return {
74
+ content: [
75
+ {
76
+ type: 'text',
77
+ text: renderTickets(ids, tickets.filter(ticket => wanted.has(ticket.handle) || (ticket.id !== undefined && wanted.has(ticket.id)))),
78
+ },
79
+ ],
80
+ };
81
+ });
82
+ server.registerTool('create_tickets', {
83
+ title: 'Create Tickets',
84
+ description: createTicketsDescription,
85
+ inputSchema: createTicketsInputSchema,
86
+ annotations: { readOnlyHint: false, idempotentHint: false },
87
+ }, async ({ effort, create, tickets, root }) => {
88
+ const workspace = resolveWorkspace(root, context);
89
+ const index = registry.forWorkspace(workspace);
90
+ // Every reference resolves before anything is written, so a refusal —
91
+ // an unknown key, a cycle — leaves the workspace exactly as it was. The
92
+ // one rule that needs real ids runs under the driver's reservations,
93
+ // still before any file lands.
94
+ const { drafts, validate } = planBatch(tickets, await index.tickets());
95
+ const created = await index.create(effort, drafts, {
96
+ createEffort: create === true,
97
+ validate,
98
+ });
99
+ return { content: [{ type: 'text', text: renderCreated(effort, created) }] };
100
+ });
101
+ server.registerTool('update_ticket', {
102
+ title: 'Update Ticket',
103
+ description: updateTicketDescription,
104
+ inputSchema: updateTicketInputSchema,
105
+ annotations: { readOnlyHint: false, idempotentHint: false },
106
+ }, async ({ id, claim, resolve, drop, status, triage, blocked_by, comment, tick, root }) => {
107
+ const workspace = resolveWorkspace(root, context);
108
+ const index = registry.forWorkspace(workspace);
109
+ const tickets = await index.tickets();
110
+ const request = { claim, resolve, drop, status, triage, blocked_by, comment, tick };
111
+ const find = (from) => {
112
+ const ticket = from.find(entry => entry.id === id || entry.handle === id);
113
+ if (ticket === undefined)
114
+ throw new Error(`No Ticket '${id}' in ${workspace}.`);
115
+ return ticket;
116
+ };
117
+ const ticket = find(tickets);
118
+ let written;
119
+ try {
120
+ written = await index.update(ticket.handle, editFor(ticket, tickets, request, now()), ticket.revision);
121
+ }
122
+ catch (error) {
123
+ if (!(error instanceof RevisionMismatch))
124
+ throw error;
125
+ // The Ticket moved under us. Re-read and re-validate, but only to
126
+ // explain why: most often a parallel session claimed it, and "already
127
+ // claimed by agent-3" is the useful message. The write is never
128
+ // retried — a mismatch may equally be somebody's hand edit, and
129
+ // overwriting that is exactly what the check exists to prevent.
130
+ const fresh = await index.tickets();
131
+ editFor(find(fresh), fresh, request, now());
132
+ throw error;
133
+ }
134
+ return {
135
+ content: [{ type: 'text', text: renderUpdate(written.ticket, written.warnings) }],
136
+ };
137
+ });
138
+ server.registerTool('edit_map', {
139
+ title: 'Edit Map',
140
+ description: editMapDescription,
141
+ inputSchema: editMapInputSchema,
142
+ annotations: { readOnlyHint: false, idempotentHint: false },
143
+ }, async ({ effort, create, destination, notes, add_fog, graduate_fog, rule_out, expected_revision, root, }) => {
144
+ const workspace = resolveWorkspace(root, context);
145
+ const index = registry.forWorkspace(workspace);
146
+ const edit = mapEditFor({ destination, notes, add_fog, graduate_fog, rule_out });
147
+ const mutating = Object.keys(edit).length > 0;
148
+ // create alone is for starting a missing Effort/Map — not a flag to send
149
+ // on every call. With no section fields, an existing Map is a plain read
150
+ // (no expected_revision); only a missing Map takes the write path.
151
+ let document;
152
+ if (!mutating) {
153
+ try {
154
+ document = await index.readMap(effort);
155
+ }
156
+ catch (error) {
157
+ if (!(create === true && error instanceof NoSuchMap))
158
+ throw error;
159
+ document = await index.editMap(effort, edit, undefined, { createEffort: true });
160
+ }
161
+ }
162
+ else {
163
+ document = await index.editMap(effort, edit, expected_revision, {
164
+ createEffort: create === true,
165
+ });
166
+ }
167
+ return { content: [{ type: 'text', text: renderMap(effort, document) }] };
168
+ });
169
+ server.registerTool('spec', {
170
+ title: 'Spec',
171
+ description: specDescription,
172
+ inputSchema: specInputSchema,
173
+ annotations: { readOnlyHint: false, idempotentHint: false },
174
+ }, async ({ effort, create, content, expected_revision, root }) => {
175
+ const workspace = resolveWorkspace(root, context);
176
+ const index = registry.forWorkspace(workspace);
177
+ const document = content === undefined
178
+ ? await index.readSpec(effort)
179
+ : await index.putSpec(effort, content, expected_revision, {
180
+ createEffort: create === true,
181
+ });
182
+ return { content: [{ type: 'text', text: renderSpec(effort, document) }] };
183
+ });
184
+ server.registerTool('migrate_effort', {
185
+ title: 'Migrate Effort',
186
+ description: migrateEffortDescription,
187
+ inputSchema: migrateEffortInputSchema,
188
+ annotations: { readOnlyHint: false, idempotentHint: false },
189
+ }, async ({ effort, preview, rename, root }) => {
190
+ const workspace = resolveWorkspace(root, context);
191
+ const index = registry.forWorkspace(workspace);
192
+ const report = await index.migrate(effort, {
193
+ preview: preview === true,
194
+ rename: rename === true,
195
+ });
196
+ return { content: [{ type: 'text', text: renderMigration(report) }] };
197
+ });
198
+ return {
199
+ server,
200
+ async warmUp() {
201
+ const workspace = resolveWorkspace(undefined, context);
202
+ await registry.forWorkspace(workspace).efforts();
203
+ },
204
+ async close() {
205
+ registry.closeAll();
206
+ await server.close();
207
+ },
208
+ };
209
+ }
210
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,131 @@
1
+ import type { Effort, MapDocument, MapEdit, MigrateOptions, MigrationReport, SpecDocument, Ticket, TicketDraft, TicketEdit } from '../domain.ts';
2
+ export type { MapDocument, MapEdit, MigrateOptions, MigrationReport, SpecDocument, TicketDraft, TicketEdit, };
3
+ export interface CreateOptions {
4
+ /**
5
+ * Create the Effort rather than failing when the workspace does not hold it.
6
+ * Off by default, so starting work is one deliberate call while a mistyped
7
+ * slug never silently forks an Effort.
8
+ */
9
+ readonly createEffort: boolean;
10
+ /**
11
+ * Called with the ids about to be written, in draft order, and the workspace
12
+ * as it looks under those reservations. Runs before anything lands, so
13
+ * throwing abandons the batch and nothing is created — not the Tickets, and
14
+ * not the Effort either.
15
+ *
16
+ * It exists for the rules that cannot be checked until the ids are real. A
17
+ * cycle is the one that matters: a Ticket already on disk may declare an Edge
18
+ * on an id nobody has minted yet, which the Board reports as dangling — and
19
+ * minting exactly that id is the moment it becomes a loop. Nothing above the
20
+ * seam can see that coming, because nothing above the seam knows what the
21
+ * next id will be.
22
+ */
23
+ readonly validate?: (ids: readonly string[], tickets: readonly Ticket[]) => void;
24
+ }
25
+ /**
26
+ * Whether a Header-doc write may start an Effort that does not yet exist.
27
+ * Off by default — same reason as {@link CreateOptions.createEffort}.
28
+ */
29
+ export interface HeaderDocOptions {
30
+ readonly createEffort: boolean;
31
+ }
32
+ /**
33
+ * The seam from ADR 0001. Every read and write reaches storage through this
34
+ * interface, and the tool layer never touches a filesystem.
35
+ *
36
+ * Nothing markdown-shaped may cross it — no file paths, no frontmatter, no
37
+ * section names. A driver is constructed already bound to its workspace (the
38
+ * markdown driver takes a directory, a SQLite driver would take a connection),
39
+ * so no method ever takes or returns a location. The planned SQLite driver, and
40
+ * the `md <-> db` conversion either side of it, are only writable if this holds.
41
+ */
42
+ export interface StorageDriver {
43
+ /**
44
+ * Every Effort in the workspace, ordered by slug. A workspace holding no
45
+ * Efforts yields an empty list; that is not an error.
46
+ */
47
+ listEfforts(): Promise<readonly Effort[]>;
48
+ /**
49
+ * Every Ticket in the workspace, ordered by Effort then by sort order. Ids
50
+ * resolve repo-wide, so callers that need one Effort filter this rather than
51
+ * asking for a subset — at these volumes the whole set is the cheap answer.
52
+ */
53
+ listTickets(): Promise<readonly Ticket[]>;
54
+ /**
55
+ * Apply an edit to one Ticket and return it as written.
56
+ *
57
+ * `expectedRevision` is the {@link TicketSummary.revision} the caller last
58
+ * read. The driver refuses the write if the stored Ticket has moved on since,
59
+ * so a concurrent edit fails loudly instead of being clobbered. The write is
60
+ * atomic: it either lands whole or not at all.
61
+ *
62
+ * Resolving or dropping also refreshes the Map's derived blocks. That refresh
63
+ * is best-effort: a failure after the Ticket write still returns success, with
64
+ * a warning that Decisions-so-far / dropped Out-of-scope may be stale.
65
+ */
66
+ updateTicket(handle: string, edit: TicketEdit, expectedRevision: string): Promise<TicketWriteResult>;
67
+ /**
68
+ * Create a whole set of Tickets in one Effort and return them as written, in
69
+ * the order asked for.
70
+ *
71
+ * The batch is the unit for a reason: the driver mints every id, resolves
72
+ * every {@link TicketDraft.key} an Edge names into the id it was given, and
73
+ * either creates all of them or none. Splitting this into a create call and a
74
+ * wire-them-up call is the second pass this exists to delete.
75
+ *
76
+ * Ids are unique across the whole workspace, and stay unique when two sessions
77
+ * in separate processes create at the same instant.
78
+ */
79
+ createTickets(effort: string, drafts: readonly TicketDraft[], options: CreateOptions): Promise<readonly Ticket[]>;
80
+ /**
81
+ * The Map's typed sections for one Effort. Decisions-so-far is not here — it
82
+ * is derived from Tickets and never read from the file.
83
+ */
84
+ readMap(effort: string): Promise<MapDocument>;
85
+ /**
86
+ * Apply a section edit to a Map and regenerate its derived blocks. Reads go
87
+ * through {@link readMap} — the tool layer never uses an empty edit as a
88
+ * refresh; resolve/drop refresh the derived blocks on their own path.
89
+ */
90
+ editMap(effort: string, edit: MapEdit, expectedRevision: string | undefined, options: HeaderDocOptions): Promise<MapDocument>;
91
+ /** A Spec as an opaque document. Absent when the Effort has none. */
92
+ readSpec(effort: string): Promise<SpecDocument>;
93
+ /** Replace a Spec wholesale with opaque document bytes. */
94
+ putSpec(effort: string, body: string, expectedRevision: string | undefined, options: HeaderDocOptions): Promise<SpecDocument>;
95
+ /**
96
+ * Normalize every Legacy Ticket in one Effort, mint ids for Tickets that have
97
+ * none, and optionally rename files to the schema convention. Preview writes
98
+ * nothing. Unrecognized files in the Effort directory are ignored.
99
+ */
100
+ migrateEffort(effort: string, options: MigrateOptions): Promise<MigrationReport>;
101
+ }
102
+ /** A Ticket write, plus any non-fatal warnings that rode along. */
103
+ export interface TicketWriteResult {
104
+ readonly ticket: Ticket;
105
+ readonly warnings: readonly string[];
106
+ }
107
+ /** Thrown when the stored Ticket moved on since the caller read it. */
108
+ export declare class RevisionMismatch extends Error {
109
+ constructor(handle: string);
110
+ }
111
+ /** Thrown when the edit names a Ticket the workspace does not hold. */
112
+ export declare class NoSuchTicket extends Error {
113
+ constructor(handle: string);
114
+ }
115
+ /** Thrown when a creation names an Effort that does not exist and may not be made. */
116
+ export declare class NoSuchEffort extends Error {
117
+ constructor(slug: string);
118
+ }
119
+ /** Thrown when a Map operation names an Effort that holds no Map. */
120
+ export declare class NoSuchMap extends Error {
121
+ constructor(slug: string);
122
+ }
123
+ /** Thrown when a Spec operation names an Effort that holds no Spec. */
124
+ export declare class NoSuchSpec extends Error {
125
+ constructor(slug: string);
126
+ }
127
+ /** Thrown when graduating a fog patch that the Map does not hold. */
128
+ export declare class NoSuchFogPatch extends Error {
129
+ constructor(patch: string);
130
+ }
131
+ //# sourceMappingURL=driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../../src/storage/driver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /** Thrown when the stored Ticket moved on since the caller read it. */
2
+ export class RevisionMismatch extends Error {
3
+ constructor(handle) {
4
+ super(`${handle} changed on disk since it was read. Nothing was written — re-read it and retry.`);
5
+ this.name = 'RevisionMismatch';
6
+ }
7
+ }
8
+ /** Thrown when the edit names a Ticket the workspace does not hold. */
9
+ export class NoSuchTicket extends Error {
10
+ constructor(handle) {
11
+ super(`No Ticket '${handle}' in this workspace.`);
12
+ this.name = 'NoSuchTicket';
13
+ }
14
+ }
15
+ /** Thrown when a creation names an Effort that does not exist and may not be made. */
16
+ export class NoSuchEffort extends Error {
17
+ constructor(slug) {
18
+ super(`No Effort '${slug}' in this workspace. Pass create to start it.`);
19
+ this.name = 'NoSuchEffort';
20
+ }
21
+ }
22
+ /** Thrown when a Map operation names an Effort that holds no Map. */
23
+ export class NoSuchMap extends Error {
24
+ constructor(slug) {
25
+ super(`No Map for Effort '${slug}'.`);
26
+ this.name = 'NoSuchMap';
27
+ }
28
+ }
29
+ /** Thrown when a Spec operation names an Effort that holds no Spec. */
30
+ export class NoSuchSpec extends Error {
31
+ constructor(slug) {
32
+ super(`No Spec for Effort '${slug}'.`);
33
+ this.name = 'NoSuchSpec';
34
+ }
35
+ }
36
+ /** Thrown when graduating a fog patch that the Map does not hold. */
37
+ export class NoSuchFogPatch extends Error {
38
+ constructor(patch) {
39
+ super(`No fog patch matching '${patch}' under Not yet specified.`);
40
+ this.name = 'NoSuchFogPatch';
41
+ }
42
+ }
43
+ //# sourceMappingURL=driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.js","sourceRoot":"","sources":["../../src/storage/driver.ts"],"names":[],"mappings":"AA4JA,uEAAuE;AACvE,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACzC,YAAY,MAAc;QACxB,KAAK,CACH,GAAG,MAAM,iFAAiF,CAC3F,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AAED,uEAAuE;AACvE,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,YAAY,MAAc;QACxB,KAAK,CAAC,cAAc,MAAM,sBAAsB,CAAC,CAAC;QAClD,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAED,sFAAsF;AACtF,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,YAAY,IAAY;QACtB,KAAK,CAAC,cAAc,IAAI,+CAA+C,CAAC,CAAC;QACzE,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAED,qEAAqE;AACrE,MAAM,OAAO,SAAU,SAAQ,KAAK;IAClC,YAAY,IAAY;QACtB,KAAK,CAAC,sBAAsB,IAAI,IAAI,CAAC,CAAC;QACtC,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;IAC1B,CAAC;CACF;AAED,uEAAuE;AACvE,MAAM,OAAO,UAAW,SAAQ,KAAK;IACnC,YAAY,IAAY;QACtB,KAAK,CAAC,uBAAuB,IAAI,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;IAC3B,CAAC;CACF;AAED,qEAAqE;AACrE,MAAM,OAAO,cAAe,SAAQ,KAAK;IACvC,YAAY,KAAa;QACvB,KAAK,CAAC,0BAA0B,KAAK,4BAA4B,CAAC,CAAC;QACnE,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF"}
@@ -0,0 +1,52 @@
1
+ import type { Ticket, TicketDraft } from '../../domain.ts';
2
+ /** Every Ticket in the workspace, re-read rather than served from any cache. */
3
+ export type Rescan = () => Promise<readonly Ticket[]>;
4
+ export interface CreateRequest {
5
+ /** Where the id guards live: one namespace for the whole workspace. */
6
+ readonly scratch: string;
7
+ readonly effort: string;
8
+ /** The Effort's directory, and the directory its Tickets go in. Both owned by the driver. */
9
+ readonly dir: string;
10
+ readonly issues: string;
11
+ readonly drafts: readonly TicketDraft[];
12
+ /** Re-read the workspace. Called under the guards, so it must not be cached. */
13
+ readonly rescan: Rescan;
14
+ /**
15
+ * The last check, run with the reserved ids and the workspace as it looks
16
+ * under the guards. Throwing abandons the batch before anything is written.
17
+ */
18
+ readonly validate: (ids: readonly string[], tickets: readonly Ticket[]) => void;
19
+ /** Whether the Effort's directory has to be made, and unmade if nothing lands. */
20
+ readonly createEffort: boolean;
21
+ }
22
+ /**
23
+ * Create every Ticket in the batch, or none, and return the filenames written in
24
+ * the order asked for.
25
+ *
26
+ * Ids are `max + 1` over the whole workspace — derived, so there is no counter
27
+ * file to drift — and made safe against a parallel session by an exclusive
28
+ * create: `open(..., 'wx')` on a guard named for the candidate id, bumping to the
29
+ * next candidate on collision. The filesystem provides the mutual exclusion.
30
+ *
31
+ * The guard alone is not compare-and-set. A session that scanned before ours and
32
+ * finished writing after would hand us an id it has already used, so the batch
33
+ * re-scans *while holding every guard* and starts over if any candidate has
34
+ * turned up on disk. That is what makes the invariant hold: a file carrying id X
35
+ * is only ever written while X's guard is held, and the guard is only kept when a
36
+ * scan taken after acquiring it shows X unused — so two sessions can never both
37
+ * write X. See ADR 0005.
38
+ */
39
+ export declare function createTicketFiles(request: CreateRequest): Promise<readonly string[]>;
40
+ /**
41
+ * Hold exclusive id guards for `count` fresh ids, run `use`, then release.
42
+ * Migration mints through this so a parallel create_tickets cannot collide.
43
+ */
44
+ export declare function withIdReservations<T>(scratch: string, count: number, rescan: Rescan, use: (ids: readonly string[]) => Promise<T>): Promise<T>;
45
+ /**
46
+ * The next `count` minted ids from a scan, without taking guards. Preview uses
47
+ * this so a dry run never leaves `.frontier-id-*.guard` files behind.
48
+ */
49
+ export declare function peekMintedIds(used: ReadonlySet<string>, count: number): readonly string[];
50
+ /** The cosmetic filename convention: `<NN>-T<n>-<slug>.md`. */
51
+ export declare function ticketFilename(order: number, id: string, title: string): string;
52
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/storage/markdown/create.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAyB3D,gFAAgF;AAChF,MAAM,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;AAEtD,MAAM,WAAW,aAAa;IAC5B,uEAAuE;IACvE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,6FAA6F;IAC7F,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC;IACxC,gFAAgF;IAChF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,KAAK,IAAI,CAAC;IAChF,kFAAkF;IAClF,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CA4B1F;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,EACxC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,GAC1C,OAAO,CAAC,CAAC,CAAC,CASZ;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAWzF;AAED,+DAA+D;AAC/D,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/E"}