openplanr 1.16.2 → 1.17.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 (99) hide show
  1. package/dist/cli/commands/operate.d.ts.map +1 -1
  2. package/dist/cli/commands/operate.js +185 -6
  3. package/dist/cli/commands/operate.js.map +1 -1
  4. package/dist/services/operate/advisors.d.ts +46 -1
  5. package/dist/services/operate/advisors.d.ts.map +1 -1
  6. package/dist/services/operate/advisors.js +124 -13
  7. package/dist/services/operate/advisors.js.map +1 -1
  8. package/dist/services/operate/artifacts.d.ts +27 -1
  9. package/dist/services/operate/artifacts.d.ts.map +1 -1
  10. package/dist/services/operate/artifacts.js +103 -1
  11. package/dist/services/operate/artifacts.js.map +1 -1
  12. package/dist/services/operate/cadence.d.ts +50 -0
  13. package/dist/services/operate/cadence.d.ts.map +1 -0
  14. package/dist/services/operate/cadence.js +56 -0
  15. package/dist/services/operate/cadence.js.map +1 -0
  16. package/dist/services/operate/citation-resolution.d.ts +101 -0
  17. package/dist/services/operate/citation-resolution.d.ts.map +1 -0
  18. package/dist/services/operate/citation-resolution.js +308 -0
  19. package/dist/services/operate/citation-resolution.js.map +1 -0
  20. package/dist/services/operate/config.d.ts +67 -1
  21. package/dist/services/operate/config.d.ts.map +1 -1
  22. package/dist/services/operate/config.js +158 -9
  23. package/dist/services/operate/config.js.map +1 -1
  24. package/dist/services/operate/decision-brief.d.ts +148 -0
  25. package/dist/services/operate/decision-brief.d.ts.map +1 -0
  26. package/dist/services/operate/decision-brief.js +194 -0
  27. package/dist/services/operate/decision-brief.js.map +1 -0
  28. package/dist/services/operate/doctor.d.ts.map +1 -1
  29. package/dist/services/operate/doctor.js +151 -3
  30. package/dist/services/operate/doctor.js.map +1 -1
  31. package/dist/services/operate/engine.d.ts +66 -2
  32. package/dist/services/operate/engine.d.ts.map +1 -1
  33. package/dist/services/operate/engine.js +219 -7
  34. package/dist/services/operate/engine.js.map +1 -1
  35. package/dist/services/operate/event-store.d.ts +35 -1
  36. package/dist/services/operate/event-store.d.ts.map +1 -1
  37. package/dist/services/operate/event-store.js +142 -29
  38. package/dist/services/operate/event-store.js.map +1 -1
  39. package/dist/services/operate/evidence-cache.d.ts +25 -1
  40. package/dist/services/operate/evidence-cache.d.ts.map +1 -1
  41. package/dist/services/operate/evidence-cache.js +62 -1
  42. package/dist/services/operate/evidence-cache.js.map +1 -1
  43. package/dist/services/operate/evidence.d.ts +20 -1
  44. package/dist/services/operate/evidence.d.ts.map +1 -1
  45. package/dist/services/operate/evidence.js +119 -0
  46. package/dist/services/operate/evidence.js.map +1 -1
  47. package/dist/services/operate/index.d.ts.map +1 -1
  48. package/dist/services/operate/index.js +72 -5
  49. package/dist/services/operate/index.js.map +1 -1
  50. package/dist/services/operate/interaction/action-service.d.ts +17 -0
  51. package/dist/services/operate/interaction/action-service.d.ts.map +1 -1
  52. package/dist/services/operate/interaction/action-service.js +19 -0
  53. package/dist/services/operate/interaction/action-service.js.map +1 -1
  54. package/dist/services/operate/journal.d.ts.map +1 -1
  55. package/dist/services/operate/journal.js +11 -5
  56. package/dist/services/operate/journal.js.map +1 -1
  57. package/dist/services/operate/maintenance.d.ts.map +1 -1
  58. package/dist/services/operate/maintenance.js +9 -2
  59. package/dist/services/operate/maintenance.js.map +1 -1
  60. package/dist/services/operate/migration.d.ts +68 -0
  61. package/dist/services/operate/migration.d.ts.map +1 -0
  62. package/dist/services/operate/migration.js +235 -0
  63. package/dist/services/operate/migration.js.map +1 -0
  64. package/dist/services/operate/mission-dispatch.d.ts +193 -0
  65. package/dist/services/operate/mission-dispatch.d.ts.map +1 -0
  66. package/dist/services/operate/mission-dispatch.js +444 -0
  67. package/dist/services/operate/mission-dispatch.js.map +1 -0
  68. package/dist/services/operate/projection-persistence.d.ts +6 -0
  69. package/dist/services/operate/projection-persistence.d.ts.map +1 -1
  70. package/dist/services/operate/projection-persistence.js +87 -11
  71. package/dist/services/operate/projection-persistence.js.map +1 -1
  72. package/dist/services/operate/projection.d.ts +26 -1
  73. package/dist/services/operate/projection.d.ts.map +1 -1
  74. package/dist/services/operate/projection.js +62 -0
  75. package/dist/services/operate/projection.js.map +1 -1
  76. package/dist/services/operate/protocol.d.ts +46 -2
  77. package/dist/services/operate/protocol.d.ts.map +1 -1
  78. package/dist/services/operate/protocol.js +57 -5
  79. package/dist/services/operate/protocol.js.map +1 -1
  80. package/dist/services/operate/read-only-providers.d.ts +37 -0
  81. package/dist/services/operate/read-only-providers.d.ts.map +1 -1
  82. package/dist/services/operate/read-only-providers.js +130 -0
  83. package/dist/services/operate/read-only-providers.js.map +1 -1
  84. package/dist/services/operate/reports.d.ts +13 -0
  85. package/dist/services/operate/reports.d.ts.map +1 -1
  86. package/dist/services/operate/reports.js +217 -1
  87. package/dist/services/operate/reports.js.map +1 -1
  88. package/dist/services/operate/routes.d.ts.map +1 -1
  89. package/dist/services/operate/routes.js +79 -5
  90. package/dist/services/operate/routes.js.map +1 -1
  91. package/dist/services/operate/types.d.ts +120 -4
  92. package/dist/services/operate/types.d.ts.map +1 -1
  93. package/dist/services/operate/types.js +13 -0
  94. package/dist/services/operate/types.js.map +1 -1
  95. package/dist/services/operate/workspace.d.ts +7 -0
  96. package/dist/services/operate/workspace.d.ts.map +1 -1
  97. package/dist/services/operate/workspace.js +14 -5
  98. package/dist/services/operate/workspace.js.map +1 -1
  99. package/package.json +2 -2
@@ -0,0 +1,444 @@
1
+ import { readdir, readFile, realpath, stat } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
+ import { resolveOperatingPipelineRoot } from './protocol.js';
5
+ import { executeGitReadOnly } from './read-only-providers.js';
6
+ import { compareSensitivity } from './redaction.js';
7
+ import { OperateError, } from './types.js';
8
+ /**
9
+ * The bounded read-only capability set for a mission-mode lens. Kept in lockstep
10
+ * with the pipeline's `MISSION_READ_ONLY_TOOLS`: no write, execute, network, or
11
+ * environment tool is present, so a grant assembled from it can never authorize
12
+ * a mutating or escaping action.
13
+ */
14
+ export const MISSION_READ_ONLY_TOOLS = Object.freeze([
15
+ 'file-read',
16
+ 'glob',
17
+ 'content-search',
18
+ 'git-log',
19
+ 'git-show',
20
+ 'git-diff',
21
+ 'git-blame',
22
+ ]);
23
+ function isReadOnlyTool(tool) {
24
+ return MISSION_READ_ONLY_TOOLS.includes(tool);
25
+ }
26
+ // ---------------------------------------------------------------------------
27
+ // Dispatch-mode resolution (FR4 / E-004) + runtime enforceability (FR2)
28
+ // ---------------------------------------------------------------------------
29
+ /**
30
+ * The registry default dispatch mode for a role. The v1.3 role registry carries
31
+ * a `dispatchMode` field; while it is still published without one every role
32
+ * defaults to `mission`, and only a role explicitly marked `pack` opts out —
33
+ * exactly the pipeline's own `role?.dispatchMode ?? 'mission'` convention.
34
+ */
35
+ export function operatingRegistryDispatchMode(role) {
36
+ return role?.dispatchMode === 'pack' ? 'pack' : 'mission';
37
+ }
38
+ let cachedRuntimeEnforcement = null;
39
+ /**
40
+ * Load the pipeline's runtime registry and derive, per runtime, whether it
41
+ * natively enforces the bounded read-only boundary. The classification is the
42
+ * published `capabilities.toolIsolation === 'enforced'` bit — the same one the
43
+ * pipeline's adapter handoff uses — so it stays a single source of truth rather
44
+ * than a field the registry schema cannot carry.
45
+ *
46
+ * Fails CLOSED: if the mission-capable pipeline root or its runtime module
47
+ * cannot be resolved, nothing is treated as enforcing, so a runtime whose
48
+ * isolation cannot be verified never receives a native lens (DoD).
49
+ */
50
+ async function loadRuntimeEnforcement() {
51
+ cachedRuntimeEnforcement ??= (async () => {
52
+ try {
53
+ const root = resolveOperatingPipelineRoot({ requireMission: true });
54
+ if (!root)
55
+ return () => false;
56
+ const module = (await import(pathToFileURL(path.join(root, 'lib', 'pipeline', 'runtime.mjs')).href));
57
+ const adapters = module.listRuntimeAdapters();
58
+ return (runtime) => {
59
+ if (!runtime || runtime === 'auto')
60
+ return false;
61
+ let id;
62
+ try {
63
+ id = module.normalizeRuntime(runtime);
64
+ }
65
+ catch {
66
+ return false;
67
+ }
68
+ const adapter = adapters.find((entry) => entry.id === id);
69
+ return adapter?.capabilities?.toolIsolation === 'enforced';
70
+ };
71
+ }
72
+ catch {
73
+ return () => false;
74
+ }
75
+ })();
76
+ return cachedRuntimeEnforcement;
77
+ }
78
+ /**
79
+ * Whether the given runtime natively enforces the mission read-only boundary.
80
+ * `claude-code` enforces; `codex` and `cursor` are advisory and do not.
81
+ */
82
+ export async function operatingRuntimeEnforcesBoundedReadOnly(runtime) {
83
+ return (await loadRuntimeEnforcement())(runtime);
84
+ }
85
+ /**
86
+ * Resolve one role's effective dispatch isolation.
87
+ *
88
+ * FR4 states Codex SHOULD dispatch natively, but FR2's fail-closed rule
89
+ * overrides it: a runtime whose tool isolation is only advisory (`codex`,
90
+ * `cursor`) cannot guarantee the bounded read-only boundary, so it routes to the
91
+ * structured provider path until its isolation is enforceable. Only a runtime
92
+ * that natively enforces tool isolation (`toolIsolation === 'enforced'`, i.e.
93
+ * `claude-code`) hosting a native-isolated adapter receives a native mission
94
+ * lens; every other combination fails closed here. This is the exact FR2/FR4
95
+ * reconciliation the spec calls out, recorded in the returned `reconciliation`
96
+ * so it appears in the dispatch provenance.
97
+ */
98
+ export function resolveOperatingDispatchMode(input) {
99
+ const mode = input.override ?? input.registryDefault;
100
+ if (mode === 'pack') {
101
+ return {
102
+ roleId: input.roleId,
103
+ mode,
104
+ isolation: 'enforced-empty-tools',
105
+ native: false,
106
+ reconciliation: input.override === 'pack'
107
+ ? 'dispatchModeOverrides rolled this role back to the v1.2 empty-tool pack path'
108
+ : 'registry default selects the v1.2 empty-tool pack path',
109
+ };
110
+ }
111
+ if (input.runtimeEnforcesBoundedReadOnly && input.adapterNativeCapable) {
112
+ return {
113
+ roleId: input.roleId,
114
+ mode,
115
+ isolation: 'enforced-read-only-bounded',
116
+ native: true,
117
+ reconciliation: 'runtime natively enforces tool isolation; a bounded read-only mission lens is dispatched',
118
+ };
119
+ }
120
+ return {
121
+ roleId: input.roleId,
122
+ mode,
123
+ isolation: 'fail-closed-structured-provider',
124
+ native: false,
125
+ reconciliation: input.runtimeEnforcesBoundedReadOnly
126
+ ? 'adapter cannot host a bounded native lens; falling back to the structured provider path'
127
+ : 'runtime tool isolation is advisory or unverifiable (FR2 fail-closed overrides FR4); routing to the structured provider path',
128
+ };
129
+ }
130
+ // ---------------------------------------------------------------------------
131
+ // Sensitivity-ceiling root narrowing (FR2)
132
+ // ---------------------------------------------------------------------------
133
+ /**
134
+ * The top-level path segment a declared read root is derived from. Mission roots
135
+ * are single top-level directory segments (see `deriveOperatingDeclaredRoots`).
136
+ */
137
+ function topSegment(relativePath) {
138
+ const [top] = relativePath.split('/');
139
+ return top && top.length > 0 ? top : null;
140
+ }
141
+ /**
142
+ * Narrow a role's declared read roots by DENY-LISTING any root that contains an
143
+ * evidence item above the role's sensitivity ceiling, so no above-ceiling file
144
+ * is readable even inside a granted root. Pass the FULL (pre-ceiling-filter)
145
+ * evidence index: the narrowing needs to see the above-ceiling items the packet
146
+ * index itself excludes. Combined with the read-time ceiling check on the
147
+ * bounded reader, this is the belt-and-suspenders that preserves the pack-era
148
+ * filter-before-handoff guarantee under agent-driven reads.
149
+ */
150
+ export function narrowMissionRootsToCeiling(input) {
151
+ const denied = new Set();
152
+ for (const item of input.evidenceIndex) {
153
+ if (!item.path)
154
+ continue;
155
+ if (compareSensitivity(item.sensitivity, input.ceiling) <= 0)
156
+ continue;
157
+ const top = topSegment(item.path);
158
+ if (top)
159
+ denied.add(top);
160
+ }
161
+ return input.declaredRoots.filter((root) => !denied.has(root)).sort();
162
+ }
163
+ const MAX_MISSION_READ_BYTES = 256 * 1024;
164
+ const MAX_MISSION_WALK_ENTRIES = 5_000;
165
+ function refuseCapability(tool) {
166
+ throw new OperateError('E_OPERATE_PROVIDER_READ_ONLY', `Mission tool "${tool}" is not in the bounded read-only grant; write, execute, ` +
167
+ 'network, and environment access are refused.', { tool, allowed: [...MISSION_READ_ONLY_TOOLS] });
168
+ }
169
+ function resolvedRoots(boundary) {
170
+ return boundary.roots.map((root) => path.resolve(root));
171
+ }
172
+ function isContained(roots, absolute) {
173
+ return roots.some((root) => absolute === root || absolute.startsWith(`${root}${path.sep}`));
174
+ }
175
+ function resolveWithinRoots(boundary, requestPath) {
176
+ if (typeof requestPath !== 'string' || requestPath.length === 0) {
177
+ throw new OperateError('E_OPERATE_PATH_ESCAPE', 'Mission read requires a non-empty path.');
178
+ }
179
+ const roots = resolvedRoots(boundary);
180
+ const candidates = path.isAbsolute(requestPath)
181
+ ? [path.resolve(requestPath)]
182
+ : roots.map((root) => path.resolve(root, requestPath));
183
+ for (const candidate of candidates) {
184
+ if (isContained(roots, candidate))
185
+ return candidate;
186
+ }
187
+ throw new OperateError('E_OPERATE_PATH_ESCAPE', `Mission read target "${requestPath}" resolves outside the declared read roots.`, { roots: [...boundary.roots] });
188
+ }
189
+ /**
190
+ * Defeat symlink escapes: after path-based containment, resolve the real target
191
+ * and re-check it against the real roots. Both sides are realpath-resolved so a
192
+ * platform-level symlink on the temp namespace (e.g. macOS `/var`→`/private/var`)
193
+ * does not produce a false escape.
194
+ */
195
+ async function assertNoSymlinkEscape(boundary, absolute) {
196
+ let real;
197
+ try {
198
+ real = await realpath(absolute);
199
+ }
200
+ catch {
201
+ // Target does not exist yet; the caller's read/stat surfaces the real error.
202
+ return;
203
+ }
204
+ const realRoots = await Promise.all(resolvedRoots(boundary).map((root) => realpath(root).catch(() => root)));
205
+ if (!isContained(realRoots, real)) {
206
+ throw new OperateError('E_OPERATE_PATH_ESCAPE', `Mission read target "${absolute}" escapes the declared read roots through a symlink.`);
207
+ }
208
+ }
209
+ function classify(boundary, absolute) {
210
+ return (boundary.sensitivityByPath?.get(absolute) ?? boundary.defaultSensitivity ?? boundary.ceiling);
211
+ }
212
+ function assertBelowCeiling(boundary, absolute) {
213
+ const sensitivity = classify(boundary, absolute);
214
+ if (compareSensitivity(sensitivity, boundary.ceiling) > 0) {
215
+ throw new OperateError('E_OPERATE_EVIDENCE_REJECTED', `Mission read of "${absolute}" is ${sensitivity}, above the ${boundary.ceiling} ceiling.`, { sensitivity, ceiling: boundary.ceiling });
216
+ }
217
+ }
218
+ async function readBoundedFile(boundary, requestPath) {
219
+ const absolute = resolveWithinRoots(boundary, requestPath);
220
+ await assertNoSymlinkEscape(boundary, absolute);
221
+ assertBelowCeiling(boundary, absolute);
222
+ const info = await stat(absolute);
223
+ if (!info.isFile()) {
224
+ throw new OperateError('E_OPERATE_EVIDENCE_REJECTED', `Mission read target "${absolute}" is not a file.`);
225
+ }
226
+ if (info.size > MAX_MISSION_READ_BYTES) {
227
+ throw new OperateError('E_OPERATE_EVIDENCE_REJECTED', `Mission read target "${absolute}" exceeds the ${MAX_MISSION_READ_BYTES}-byte read bound.`);
228
+ }
229
+ return readFile(absolute, 'utf8');
230
+ }
231
+ async function* walkRoot(root) {
232
+ let visited = 0;
233
+ const stack = [root];
234
+ while (stack.length > 0) {
235
+ const current = stack.pop();
236
+ const entries = await readdir(current, { withFileTypes: true }).catch(() => null);
237
+ if (!entries)
238
+ continue;
239
+ for (const entry of entries) {
240
+ if (entry.name === '.git' || entry.name === 'node_modules')
241
+ continue;
242
+ const child = path.join(current, entry.name);
243
+ if (entry.isDirectory()) {
244
+ stack.push(child);
245
+ }
246
+ else if (entry.isFile()) {
247
+ visited += 1;
248
+ if (visited > MAX_MISSION_WALK_ENTRIES)
249
+ return;
250
+ yield child;
251
+ }
252
+ }
253
+ }
254
+ }
255
+ /**
256
+ * Render a filesystem path with forward-slash separators so a glob pattern —
257
+ * always authored with `/` — matches identically on every platform. `path.join`
258
+ * yields `\` separators on Windows, which the pattern's `/` can never match, so
259
+ * a recursive `.ts` glob would otherwise return nothing on Windows. Normalizing
260
+ * only the candidate fed to the matcher (never the stored/returned path) keeps
261
+ * the emitted matches in native form for the caller's own containment checks. On
262
+ * POSIX `path.sep` is `/`, so this is a no-op.
263
+ */
264
+ function toGlobMatchPath(target) {
265
+ return target.split(path.sep).join('/');
266
+ }
267
+ function simplePatternToRegExp(pattern) {
268
+ let out = '';
269
+ let index = 0;
270
+ while (index < pattern.length) {
271
+ const char = pattern[index];
272
+ if (char === '*') {
273
+ if (pattern[index + 1] === '*') {
274
+ out += '.*';
275
+ index += 2;
276
+ }
277
+ else {
278
+ out += '[^/]*';
279
+ index += 1;
280
+ }
281
+ }
282
+ else {
283
+ out += char.replace(/[.+^${}()|[\]\\]/g, '\\$&');
284
+ index += 1;
285
+ }
286
+ }
287
+ return new RegExp(`${out}$`);
288
+ }
289
+ async function globWithinRoots(boundary, pattern, root) {
290
+ const roots = root ? [resolveWithinRoots(boundary, root)] : resolvedRoots(boundary);
291
+ const matcher = simplePatternToRegExp(pattern || '**/*');
292
+ const matches = [];
293
+ for (const searchRoot of roots) {
294
+ for await (const file of walkRoot(searchRoot)) {
295
+ if (!matcher.test(toGlobMatchPath(file)))
296
+ continue;
297
+ try {
298
+ assertBelowCeiling(boundary, file);
299
+ }
300
+ catch {
301
+ continue;
302
+ }
303
+ matches.push(file);
304
+ }
305
+ }
306
+ return [...new Set(matches)].sort();
307
+ }
308
+ async function contentSearchWithinRoots(boundary, query, root) {
309
+ if (typeof query !== 'string' || query.length === 0) {
310
+ throw new OperateError('E_OPERATE_EVIDENCE_REJECTED', 'Mission content-search requires a query.');
311
+ }
312
+ const roots = root ? [resolveWithinRoots(boundary, root)] : resolvedRoots(boundary);
313
+ const hits = [];
314
+ for (const searchRoot of roots) {
315
+ for await (const file of walkRoot(searchRoot)) {
316
+ try {
317
+ assertBelowCeiling(boundary, file);
318
+ }
319
+ catch {
320
+ continue;
321
+ }
322
+ const info = await stat(file).catch(() => null);
323
+ if (!info?.isFile() || info.size > MAX_MISSION_READ_BYTES)
324
+ continue;
325
+ const content = await readFile(file, 'utf8').catch(() => null);
326
+ if (content === null)
327
+ continue;
328
+ content.split(/\r?\n/).forEach((text, index) => {
329
+ if (text.includes(query))
330
+ hits.push({ path: file, line: index + 1, text: text.slice(0, 512) });
331
+ });
332
+ }
333
+ }
334
+ return hits;
335
+ }
336
+ function assertGitArgsBounded(args) {
337
+ for (const argument of args) {
338
+ if (typeof argument !== 'string') {
339
+ throw new OperateError('E_OPERATE_PROVIDER_READ_ONLY', 'Mission git arguments must be strings.');
340
+ }
341
+ if (argument.startsWith('--output') || argument === '-o' || argument.includes('../..')) {
342
+ throw new OperateError('E_OPERATE_PROVIDER_READ_ONLY', 'Mission git arguments cannot redirect output.');
343
+ }
344
+ }
345
+ }
346
+ async function runBoundedGit(boundary, subcommand, request) {
347
+ const repositoryRoot = boundary.repositoryRoot
348
+ ? path.resolve(boundary.repositoryRoot)
349
+ : resolvedRoots(boundary)[0];
350
+ if (!repositoryRoot) {
351
+ throw new OperateError('E_OPERATE_PROVIDER_READ_ONLY', 'Mission git tools require a repository root.');
352
+ }
353
+ const extra = request.args ?? [];
354
+ assertGitArgsBounded(extra);
355
+ const args = [subcommand, ...extra];
356
+ if (request.path) {
357
+ // Any path argument stays confined to the declared roots.
358
+ const confined = resolveWithinRoots(boundary, request.path);
359
+ args.push('--', path.relative(repositoryRoot, confined) || '.');
360
+ }
361
+ // `executeGitReadOnly` re-asserts the read-only git allowlist (no mutation,
362
+ // no repository/config overrides) as a second, independent gate.
363
+ return executeGitReadOnly(repositoryRoot, args);
364
+ }
365
+ /**
366
+ * The single audited entry point for a mission lens's tool use. Every request is
367
+ * checked against the bounded read-only grant BEFORE any filesystem or git
368
+ * access: a tool outside the grant (write, execute, network, environment, or any
369
+ * unknown surface) is refused, a target outside the declared roots is refused as
370
+ * a path escape, and a target above the sensitivity ceiling is refused at read
371
+ * time. There is deliberately no write/execute/network/environment tool to
372
+ * invoke — those channels do not exist on this surface at all.
373
+ */
374
+ export async function invokeMissionTool(boundary, request) {
375
+ const tool = String(request.tool ?? '');
376
+ if (!isReadOnlyTool(tool))
377
+ refuseCapability(tool);
378
+ switch (tool) {
379
+ case 'file-read': {
380
+ const requestPath = String(request.path ?? '');
381
+ return {
382
+ tool: 'file-read',
383
+ path: requestPath,
384
+ content: await readBoundedFile(boundary, requestPath),
385
+ };
386
+ }
387
+ case 'glob': {
388
+ const pattern = request.pattern ?? '**/*';
389
+ const root = request.root;
390
+ return { tool: 'glob', matches: await globWithinRoots(boundary, pattern, root) };
391
+ }
392
+ case 'content-search': {
393
+ const query = String(request.query ?? '');
394
+ const root = request.root;
395
+ return {
396
+ tool: 'content-search',
397
+ matches: await contentSearchWithinRoots(boundary, query, root),
398
+ };
399
+ }
400
+ case 'git-log':
401
+ case 'git-show':
402
+ case 'git-diff':
403
+ case 'git-blame': {
404
+ const subcommand = tool.slice('git-'.length);
405
+ const typed = request;
406
+ return { tool, output: await runBoundedGit(boundary, subcommand, typed) };
407
+ }
408
+ default:
409
+ return refuseCapability(tool);
410
+ }
411
+ }
412
+ /**
413
+ * The concrete callable tool surface handed to an in-process native harness:
414
+ * exactly the read-only tools, each bound to this boundary. There is no write,
415
+ * execute, network, or environment callable on the returned object, so a harness
416
+ * that walks it can never reach a mutating or escaping capability.
417
+ */
418
+ export function createMissionToolset(boundary) {
419
+ const toolset = {};
420
+ for (const tool of MISSION_READ_ONLY_TOOLS) {
421
+ toolset[tool] = (request) => invokeMissionTool(boundary, { ...request, tool });
422
+ }
423
+ return toolset;
424
+ }
425
+ // ---------------------------------------------------------------------------
426
+ // Fan-out orchestration (FR2 parallel/sequential, FR4 determinism)
427
+ // ---------------------------------------------------------------------------
428
+ /**
429
+ * Fan a per-item dispatch out in parallel where the adapter reports
430
+ * `parallelDispatch: true`, sequentially otherwise. Results are always returned
431
+ * in the SAME order as `items`, so the caller can restore registry order and the
432
+ * reduced events are byte-identical across parallel and sequential dispatch.
433
+ */
434
+ export async function runMissionDispatchFanOut(input) {
435
+ if (input.parallel) {
436
+ return Promise.all(input.items.map((item) => input.run(item)));
437
+ }
438
+ const results = [];
439
+ for (const item of input.items) {
440
+ results.push(await input.run(item));
441
+ }
442
+ return results;
443
+ }
444
+ //# sourceMappingURL=mission-dispatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mission-dispatch.js","sourceRoot":"","sources":["../../../src/services/operate/mission-dispatch.ts"],"names":[],"mappings":"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"}
@@ -51,5 +51,11 @@ export declare const OPERATING_PROJECTION_PATHS: {
51
51
  readonly decisions: ".planr/operate/projections/decisions.md";
52
52
  readonly dataGaps: ".planr/operate/projections/data-gaps.md";
53
53
  readonly backlog: ".planr/operate/projections/backlog.md";
54
+ readonly evidenceIndex: ".planr/operate/evidence-index.json";
55
+ readonly brief: ".planr/operate/brief.md";
56
+ readonly findings: ".planr/operate/findings.md";
57
+ readonly readableDecisions: ".planr/operate/decisions.md";
58
+ readonly gaps: ".planr/operate/gaps.md";
59
+ readonly routes: ".planr/operate/routes.md";
54
60
  };
55
61
  //# sourceMappingURL=projection-persistence.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"projection-persistence.d.ts","sourceRoot":"","sources":["../../../src/services/operate/projection-persistence.ts"],"names":[],"mappings":"AAWA,OAAO,EAAgB,KAAK,kBAAkB,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AA4BxF,MAAM,MAAM,8BAA8B,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;AAE5E,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,8BAA8B,CAAC;IACvC,cAAc,EAAE,UAAU,MAAM,EAAE,CAAC;IACnC,YAAY,EAAE,UAAU,MAAM,EAAE,GAAG,IAAI,CAAC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,UAAU,MAAM,EAAE,CAAC;IAChC,SAAS,EAAE,kBAAkB,CAAC;IAC9B,aAAa,EAAE,UAAU,MAAM,EAAE,CAAC;IAClC,KAAK,EAAE,uBAAuB,EAAE,CAAC;IACjC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,KAAK,EAAE,wBAAwB,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,gCAAiC,SAAQ,0BAA0B;IAClF,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAwQD,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,cAAc,GAAG,uBAAuB,EAAE,CAY/F;AAkED,wBAAsB,+BAA+B,CAAC,KAAK,EAAE;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,cAAc,CAAC;CACvB,GAAG,OAAO,CAAC,wBAAwB,EAAE,CAAC,CAItC;AAED,wBAAsB,iCAAiC,CAAC,KAAK,EAAE;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,cAAc,CAAC;CACvB,GAAG,OAAO,CAAC,IAAI,CAAC,CAUhB;AAED,wBAAsB,qCAAqC,CAAC,KAAK,EAAE;IACjE,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,cAAc,CAAC;CACvB,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAsDtC;AAED,wBAAsB,2BAA2B,CAAC,KAAK,EAAE;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,cAAc,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,mBAAmB,CAAC,EAAE,MAAM,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACzD,GAAG,OAAO,CAAC,gCAAgC,CAAC,CA6B5C;AAED,eAAO,MAAM,0BAA0B;;;;;;CAM7B,CAAC"}
1
+ {"version":3,"file":"projection-persistence.d.ts","sourceRoot":"","sources":["../../../src/services/operate/projection-persistence.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAgB,KAAK,kBAAkB,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AA8DxF,MAAM,MAAM,8BAA8B,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;AAE5E,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,8BAA8B,CAAC;IACvC,cAAc,EAAE,UAAU,MAAM,EAAE,CAAC;IACnC,YAAY,EAAE,UAAU,MAAM,EAAE,GAAG,IAAI,CAAC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,UAAU,MAAM,EAAE,CAAC;IAChC,SAAS,EAAE,kBAAkB,CAAC;IAC9B,aAAa,EAAE,UAAU,MAAM,EAAE,CAAC;IAClC,KAAK,EAAE,uBAAuB,EAAE,CAAC;IACjC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,KAAK,EAAE,wBAAwB,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,gCAAiC,SAAQ,0BAA0B;IAClF,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAgTD,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,cAAc,GAAG,uBAAuB,EAAE,CAgB/F;AAkED,wBAAsB,+BAA+B,CAAC,KAAK,EAAE;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,cAAc,CAAC;CACvB,GAAG,OAAO,CAAC,wBAAwB,EAAE,CAAC,CAItC;AAED,wBAAsB,iCAAiC,CAAC,KAAK,EAAE;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,cAAc,CAAC;CACvB,GAAG,OAAO,CAAC,IAAI,CAAC,CAUhB;AAED,wBAAsB,qCAAqC,CAAC,KAAK,EAAE;IACjE,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,cAAc,CAAC;CACvB,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAsDtC;AAED,wBAAsB,2BAA2B,CAAC,KAAK,EAAE;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,cAAc,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,mBAAmB,CAAC,EAAE,MAAM,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACzD,GAAG,OAAO,CAAC,gCAAgC,CAAC,CA6B5C;AAED,eAAO,MAAM,0BAA0B;;;;;;;;;;;;CAa7B,CAAC"}
@@ -2,12 +2,14 @@ import { readFile } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { canonicalDigest, canonicalize, sha256Digest } from './canonical.js';
4
4
  import { applyJournalTransaction, prepareJournalTransaction, } from './journal.js';
5
- import { renderOperatingBrief, selectCycleState } from './projection.js';
5
+ import { OPERATING_BOARD_ROLES, renderOperatingBoardReport, renderOperatingBrief, renderOperatingEvidenceIndex, selectCycleState, } from './projection.js';
6
6
  import { assertOperatingArtifact } from './protocol.js';
7
7
  import { operatingStalledItems } from './stalled-item-service.js';
8
8
  import { OperateError } from './types.js';
9
- const PROJECTION_ROOT = '.planr/operate/projections';
9
+ const OPERATE_ROOT = '.planr/operate';
10
+ const PROJECTION_ROOT = `${OPERATE_ROOT}/projections`;
10
11
  const STATE_PATH = `${PROJECTION_ROOT}/state.json`;
12
+ const EVIDENCE_INDEX_PATH = `${OPERATE_ROOT}/evidence-index.json`;
11
13
  const MARKDOWN_PROJECTIONS = [
12
14
  {
13
15
  relativePath: `${PROJECTION_ROOT}/register.md`,
@@ -30,7 +32,39 @@ const MARKDOWN_PROJECTIONS = [
30
32
  render: renderBacklogRegister,
31
33
  },
32
34
  ];
35
+ // FR5 readable tree: one consolidated Markdown file per register rendered at the
36
+ // top level (the workspace path getters T-001 added), above the `.state/`
37
+ // internals. `findings.md`/`decisions.md`/`gaps.md` reuse the register renderers
38
+ // so the readable tree and the projections directory stay byte-consistent.
39
+ const READABLE_TREE_PROJECTIONS = [
40
+ {
41
+ relativePath: `${OPERATE_ROOT}/brief.md`,
42
+ markerName: 'operate-brief',
43
+ render: renderOperatingBrief,
44
+ },
45
+ {
46
+ relativePath: `${OPERATE_ROOT}/findings.md`,
47
+ markerName: 'operate-findings-register',
48
+ render: renderFindingRegister,
49
+ },
50
+ {
51
+ relativePath: `${OPERATE_ROOT}/decisions.md`,
52
+ markerName: 'operate-decisions-register',
53
+ render: renderDecisionRegister,
54
+ },
55
+ {
56
+ relativePath: `${OPERATE_ROOT}/gaps.md`,
57
+ markerName: 'operate-data-gaps-register',
58
+ render: renderDataGapRegister,
59
+ },
60
+ {
61
+ relativePath: `${OPERATE_ROOT}/routes.md`,
62
+ markerName: 'operate-routes-register',
63
+ render: renderRouteRegister,
64
+ },
65
+ ];
33
66
  const CYCLE_BRIEF_MARKER = 'operate-cycle-brief';
67
+ const BOARD_MARKER = 'operate-board';
34
68
  function value(record, key, fallback = '') {
35
69
  const candidate = record[key];
36
70
  if (typeof candidate === 'string' && candidate.trim()) {
@@ -137,6 +171,30 @@ function renderBacklogRegister(state) {
137
171
  : ['| — | — | — | 0 | No parked findings. | — | — |']),
138
172
  ].join('\n');
139
173
  }
174
+ function renderRouteRegister(state) {
175
+ const routes = [...state.routes].sort((left, right) => left.id.localeCompare(right.id));
176
+ return [
177
+ ...generatedHeader(state, 'Operating Routes'),
178
+ '| Route | Cycle | State | Findings | Actions | Route digest |',
179
+ '|---|---|---|---|---:|---|',
180
+ ...(routes.length > 0
181
+ ? routes.map((route) => {
182
+ const actions = Array.isArray(route.actions)
183
+ ? route.actions
184
+ : [];
185
+ const findingIds = Array.isArray(route.findingIds)
186
+ ? route.findingIds.filter((entry) => typeof entry === 'string')
187
+ : [
188
+ ...new Set(actions
189
+ .map((action) => action.findingId)
190
+ .filter((entry) => typeof entry === 'string')),
191
+ ];
192
+ const actionCount = typeof route.actionCount === 'number' ? route.actionCount : actions.length;
193
+ return `| ${markdownCell(route.id)} | ${markdownCell(route.cycleId)} | ${markdownCell(route.state)} | ${markdownCell(findingIds.sort().join(', '))} | ${markdownCell(actionCount, '0')} | ${markdownCell(route.routeDigest)} |`;
194
+ })
195
+ : ['| — | — | — | — | 0 | No routes recorded. |']),
196
+ ].join('\n');
197
+ }
140
198
  function renderCycleBrief(state, cycleId) {
141
199
  const selected = selectCycleState(state, cycleId);
142
200
  const brief = renderOperatingBrief(selected);
@@ -201,33 +259,44 @@ function spliceProjectionBlock(existing, markerName, managedContent) {
201
259
  return `${existing.slice(0, beginStart)}${block}${existing.slice(afterStart)}`;
202
260
  }
203
261
  function markdownProjectionSpecs(state) {
204
- const registers = MARKDOWN_PROJECTIONS.map((projection) => ({
262
+ const registers = [...MARKDOWN_PROJECTIONS, ...READABLE_TREE_PROJECTIONS].map((projection) => ({
205
263
  relativePath: projection.relativePath,
206
264
  markerName: projection.markerName,
207
265
  managedContent: projection.render(state),
208
266
  }));
209
- const briefs = [...state.cycles]
267
+ const cycleArtifacts = [...state.cycles]
210
268
  .filter((cycle) => cycle.state === 'reviewable' || cycle.state === 'closed')
211
269
  .sort((left, right) => left.id.localeCompare(right.id))
212
- .map((cycle) => ({
213
- relativePath: `.planr/operate/cycles/${cycle.id}/brief.md`,
214
- markerName: CYCLE_BRIEF_MARKER,
215
- managedContent: renderCycleBrief(state, cycle.id),
216
- }));
217
- return [...registers, ...briefs];
270
+ .flatMap((cycle) => [
271
+ {
272
+ relativePath: `${OPERATE_ROOT}/cycles/${cycle.id}/brief.md`,
273
+ markerName: CYCLE_BRIEF_MARKER,
274
+ managedContent: renderCycleBrief(state, cycle.id),
275
+ },
276
+ ...OPERATING_BOARD_ROLES.map((role) => ({
277
+ relativePath: `${OPERATE_ROOT}/cycles/${cycle.id}/board/${role.id}.md`,
278
+ markerName: BOARD_MARKER,
279
+ managedContent: renderOperatingBoardReport(state, cycle.id, role),
280
+ })),
281
+ ]);
282
+ return [...registers, ...cycleArtifacts];
218
283
  }
219
284
  export function renderOperatingProjectionFiles(state) {
220
285
  const stateFile = {
221
286
  relativePath: STATE_PATH,
222
287
  content: `${canonicalize(state)}\n`,
223
288
  };
289
+ const evidenceIndexFile = {
290
+ relativePath: EVIDENCE_INDEX_PATH,
291
+ content: renderOperatingEvidenceIndex(state),
292
+ };
224
293
  const markdownFiles = markdownProjectionSpecs(state).map((projection) => ({
225
294
  relativePath: projection.relativePath,
226
295
  markerName: projection.markerName,
227
296
  managedContent: projection.managedContent,
228
297
  content: spliceProjectionBlock('', projection.markerName, projection.managedContent),
229
298
  }));
230
- return [stateFile, ...markdownFiles];
299
+ return [stateFile, evidenceIndexFile, ...markdownFiles];
231
300
  }
232
301
  async function inspectProjectionFile(projectRoot, expected) {
233
302
  const expectedBytes = expected.markerName ? (expected.managedContent ?? '') : expected.content;
@@ -389,5 +458,12 @@ export const OPERATING_PROJECTION_PATHS = {
389
458
  decisions: `${PROJECTION_ROOT}/decisions.md`,
390
459
  dataGaps: `${PROJECTION_ROOT}/data-gaps.md`,
391
460
  backlog: `${PROJECTION_ROOT}/backlog.md`,
461
+ // FR5 readable tree at the workspace path getters (T-001).
462
+ evidenceIndex: EVIDENCE_INDEX_PATH,
463
+ brief: `${OPERATE_ROOT}/brief.md`,
464
+ findings: `${OPERATE_ROOT}/findings.md`,
465
+ readableDecisions: `${OPERATE_ROOT}/decisions.md`,
466
+ gaps: `${OPERATE_ROOT}/gaps.md`,
467
+ routes: `${OPERATE_ROOT}/routes.md`,
392
468
  };
393
469
  //# sourceMappingURL=projection-persistence.js.map