openplanr 1.17.0 → 1.19.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 (84) hide show
  1. package/dist/cli/commands/operate.d.ts +10 -1
  2. package/dist/cli/commands/operate.d.ts.map +1 -1
  3. package/dist/cli/commands/operate.js +55 -7
  4. package/dist/cli/commands/operate.js.map +1 -1
  5. package/dist/services/ai-service.d.ts +25 -0
  6. package/dist/services/ai-service.d.ts.map +1 -1
  7. package/dist/services/ai-service.js +38 -0
  8. package/dist/services/ai-service.js.map +1 -1
  9. package/dist/services/operate/advisors.d.ts +100 -4
  10. package/dist/services/operate/advisors.d.ts.map +1 -1
  11. package/dist/services/operate/advisors.js +450 -14
  12. package/dist/services/operate/advisors.js.map +1 -1
  13. package/dist/services/operate/config.d.ts +36 -0
  14. package/dist/services/operate/config.d.ts.map +1 -1
  15. package/dist/services/operate/config.js +94 -6
  16. package/dist/services/operate/config.js.map +1 -1
  17. package/dist/services/operate/doctor.d.ts.map +1 -1
  18. package/dist/services/operate/doctor.js +121 -1
  19. package/dist/services/operate/doctor.js.map +1 -1
  20. package/dist/services/operate/engine.d.ts +22 -6
  21. package/dist/services/operate/engine.d.ts.map +1 -1
  22. package/dist/services/operate/engine.js +20 -9
  23. package/dist/services/operate/engine.js.map +1 -1
  24. package/dist/services/operate/evidence-readiness.d.ts +11 -1
  25. package/dist/services/operate/evidence-readiness.d.ts.map +1 -1
  26. package/dist/services/operate/evidence-readiness.js +12 -2
  27. package/dist/services/operate/evidence-readiness.js.map +1 -1
  28. package/dist/services/operate/evidence.d.ts +30 -1
  29. package/dist/services/operate/evidence.d.ts.map +1 -1
  30. package/dist/services/operate/evidence.js +248 -10
  31. package/dist/services/operate/evidence.js.map +1 -1
  32. package/dist/services/operate/index.d.ts +2 -0
  33. package/dist/services/operate/index.d.ts.map +1 -1
  34. package/dist/services/operate/index.js +242 -41
  35. package/dist/services/operate/index.js.map +1 -1
  36. package/dist/services/operate/interaction/answer-service.d.ts +17 -0
  37. package/dist/services/operate/interaction/answer-service.d.ts.map +1 -1
  38. package/dist/services/operate/interaction/answer-service.js +49 -7
  39. package/dist/services/operate/interaction/answer-service.js.map +1 -1
  40. package/dist/services/operate/interaction/question-engine.d.ts.map +1 -1
  41. package/dist/services/operate/interaction/question-engine.js +9 -3
  42. package/dist/services/operate/interaction/question-engine.js.map +1 -1
  43. package/dist/services/operate/interaction/question-registry.d.ts +13 -0
  44. package/dist/services/operate/interaction/question-registry.d.ts.map +1 -1
  45. package/dist/services/operate/interaction/question-registry.js +116 -26
  46. package/dist/services/operate/interaction/question-registry.js.map +1 -1
  47. package/dist/services/operate/interaction/terminal-renderer.d.ts +12 -1
  48. package/dist/services/operate/interaction/terminal-renderer.d.ts.map +1 -1
  49. package/dist/services/operate/interaction/terminal-renderer.js +26 -15
  50. package/dist/services/operate/interaction/terminal-renderer.js.map +1 -1
  51. package/dist/services/operate/lifecycle.d.ts +8 -0
  52. package/dist/services/operate/lifecycle.d.ts.map +1 -1
  53. package/dist/services/operate/lifecycle.js +36 -2
  54. package/dist/services/operate/lifecycle.js.map +1 -1
  55. package/dist/services/operate/maintenance.d.ts +33 -1
  56. package/dist/services/operate/maintenance.d.ts.map +1 -1
  57. package/dist/services/operate/maintenance.js +467 -47
  58. package/dist/services/operate/maintenance.js.map +1 -1
  59. package/dist/services/operate/projection-persistence.d.ts +35 -7
  60. package/dist/services/operate/projection-persistence.d.ts.map +1 -1
  61. package/dist/services/operate/projection-persistence.js +122 -53
  62. package/dist/services/operate/projection-persistence.js.map +1 -1
  63. package/dist/services/operate/projection.d.ts +9 -1
  64. package/dist/services/operate/projection.d.ts.map +1 -1
  65. package/dist/services/operate/projection.js +12 -9
  66. package/dist/services/operate/projection.js.map +1 -1
  67. package/dist/services/operate/protocol.d.ts +4 -1
  68. package/dist/services/operate/protocol.d.ts.map +1 -1
  69. package/dist/services/operate/protocol.js.map +1 -1
  70. package/dist/services/operate/reports.d.ts +18 -0
  71. package/dist/services/operate/reports.d.ts.map +1 -1
  72. package/dist/services/operate/reports.js +29 -1
  73. package/dist/services/operate/reports.js.map +1 -1
  74. package/dist/services/operate/routes.d.ts +84 -0
  75. package/dist/services/operate/routes.d.ts.map +1 -1
  76. package/dist/services/operate/routes.js +519 -10
  77. package/dist/services/operate/routes.js.map +1 -1
  78. package/dist/services/operate/types.d.ts +25 -5
  79. package/dist/services/operate/types.d.ts.map +1 -1
  80. package/dist/services/operate/types.js.map +1 -1
  81. package/dist/services/runtime-manager-service.d.ts.map +1 -1
  82. package/dist/services/runtime-manager-service.js +25 -5
  83. package/dist/services/runtime-manager-service.js.map +1 -1
  84. package/package.json +2 -2
@@ -8,6 +8,9 @@ function ageHours(iso, now) {
8
8
  export async function evaluateEvidenceReadiness(input) {
9
9
  const now = input.now ?? new Date();
10
10
  const registry = (await loadOperatingProtocol()).listOperatingRoles();
11
+ const postIndexRepositoryCount = input.missionEvidenceIndex
12
+ ? input.missionEvidenceIndex.filter((item) => item.source === 'repository').length
13
+ : null;
11
14
  const eligibleItems = input.evidence.items.filter((item) => !prepareAdvisorEvidenceText({
12
15
  evidenceId: item.id,
13
16
  digest: item.digest,
@@ -33,7 +36,14 @@ export async function evaluateEvidenceReadiness(input) {
33
36
  const oldestAgeHours = matching.length > 0
34
37
  ? Math.max(...matching.map((item) => ageHours(item.collectedAt, now)))
35
38
  : null;
36
- const satisfied = matching.length >= requirement.minimumItems &&
39
+ // FR2: for a mission-mode evaluation, a `repository` requirement can be
40
+ // satisfied only by repository items the mission index actually retained —
41
+ // dot-prefixed/pattern-dropped repository items are unreachable by a bounded
42
+ // mission packet, so they cannot count toward readiness.
43
+ const observedItems = postIndexRepositoryCount !== null && requirement.source === 'repository'
44
+ ? Math.min(matching.length, postIndexRepositoryCount)
45
+ : matching.length;
46
+ const satisfied = observedItems >= requirement.minimumItems &&
37
47
  oldestAgeHours !== null &&
38
48
  oldestAgeHours <= requirement.maxAgeHours &&
39
49
  (requirement.observationWindow === 'current-state' ||
@@ -41,7 +51,7 @@ export async function evaluateEvidenceReadiness(input) {
41
51
  matching.some((item) => item.observedFrom && item.observedTo));
42
52
  return {
43
53
  ...structuredClone(requirement),
44
- observedItems: matching.length,
54
+ observedItems,
45
55
  oldestAgeHours,
46
56
  satisfied,
47
57
  };
@@ -1 +1 @@
1
- {"version":3,"file":"evidence-readiness.js","sourceRoot":"","sources":["../../../src/services/operate/evidence-readiness.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"evidence-readiness.js","sourceRoot":"","sources":["../../../src/services/operate/evidence-readiness.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { type ReadOnlyFetch } from './read-only-providers.js';
2
- import { type CollectedEvidenceItem, type EvidenceBudget, type OperatingDataGap, type OperatingEvidence, type OperatingEvidenceIndexItem, type OperatingEvidenceItem, type OperatingSensitivity, type OperatingWorkspaceManifest } from './types.js';
2
+ import { type CollectedEvidenceItem, type EvidenceBudget, type OperatingDataGap, type OperatingEvidence, type OperatingEvidenceIndexItem, type OperatingEvidenceItem, type OperatingRoleId, type OperatingSensitivity, type OperatingWorkspaceManifest } from './types.js';
3
3
  export interface EvidenceCollectionInput {
4
4
  projectRoot: string;
5
5
  cycleId: string;
@@ -62,6 +62,21 @@ export declare function unqualifiedCommercialEvidenceGaps(input: {
62
62
  owner: string;
63
63
  now: string;
64
64
  }): Promise<OperatingDataGap[]>;
65
+ /**
66
+ * FR2: open a governed evidence gap for every mission role whose POST-index
67
+ * evidence retained zero `source: 'repository'` items. A role that requires
68
+ * repository code must never dispatch "ready" on an empty evidence base — the
69
+ * pre-index readiness gate can pass while the mission index built moments later
70
+ * loses every repository item to the dot-prefixed/pattern filter. Each gap names
71
+ * the affected role and the reason so the loss is visible instead of silent.
72
+ */
73
+ export declare function starvedRoleEvidenceGaps(input: {
74
+ cycleId: string;
75
+ roleIds: readonly OperatingRoleId[];
76
+ owner: string;
77
+ now: string;
78
+ startingGapNumber?: number;
79
+ }): Promise<OperatingDataGap[]>;
65
80
  export declare function evidenceProjectionSources(evidence: OperatingEvidence): Array<{
66
81
  id: string;
67
82
  freshness: OperatingEvidenceItem['freshness'];
@@ -81,6 +96,20 @@ export declare function evidenceProjectionSources(evidence: OperatingEvidence):
81
96
  export declare function buildOperatingEvidenceIndex(evidence: OperatingEvidence, options?: {
82
97
  sensitivityCeiling?: OperatingSensitivity;
83
98
  }): OperatingEvidenceIndexItem[];
99
+ /**
100
+ * FR2: count — using the exact predicates `buildOperatingEvidenceIndex` filters
101
+ * on — the path-referenced evidence items a mission evidence index would drop:
102
+ * `pathPattern` for dot-prefixed / pattern-rejected paths, and `sensitivity` for
103
+ * items above a supplied ceiling. Integration records (github/linear) and
104
+ * git-by-revision items are never a "loss" (they are not path pointers), so they
105
+ * are not counted. The caller surfaces the counts as evidence-bundle warnings.
106
+ */
107
+ export declare function missionEvidenceIndexDrops(items: readonly OperatingEvidenceItem[], options?: {
108
+ sensitivityCeiling?: OperatingSensitivity;
109
+ }): {
110
+ pathPattern: number;
111
+ sensitivity: number;
112
+ };
84
113
  /**
85
114
  * The read roots a mission agent may traverse, derived from the top-level
86
115
  * directory segment of every path-referenced index item. Schema-valid and
@@ -1 +1 @@
1
- {"version":3,"file":"evidence.d.ts","sourceRoot":"","sources":["../../../src/services/operate/evidence.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"evidence.d.ts","sourceRoot":"","sources":["../../../src/services/operate/evidence.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,7 @@ import path from 'node:path';
3
3
  import { loadConfig } from '../config-service.js';
4
4
  import { resolveApiKey } from '../credentials-service.js';
5
5
  import { canonicalDigest, canonicalize } from './canonical.js';
6
+ import { OperatingEventStore } from './event-store.js';
6
7
  import { createEvidenceDiagnostic } from './evidence-diagnostics.js';
7
8
  import { readImportedEvidenceFile } from './evidence-import.js';
8
9
  import { assertOperatingArtifact } from './protocol.js';
@@ -70,15 +71,112 @@ function descriptorById(workspace) {
70
71
  entry,
71
72
  ]));
72
73
  }
74
+ /**
75
+ * The first path segment of a repository-relative path (its top-level
76
+ * directory), or null for a root-level file. Drives per-directory round-robin
77
+ * fairness and the truncation diagnostics.
78
+ */
79
+ function topLevelSegment(relativePath) {
80
+ const slash = relativePath.indexOf('/');
81
+ if (slash < 0)
82
+ return null;
83
+ const top = relativePath.slice(0, slash);
84
+ return top.length > 0 ? top : null;
85
+ }
86
+ /**
87
+ * Map each tracked path to the Unix-second timestamp of the most recent commit
88
+ * that touched it, from a single `git log --name-only` walk (newest-first, so a
89
+ * path's first occurrence is its latest change). A leading NUL marks the
90
+ * timestamp header line so a filename can never be mistaken for a timestamp.
91
+ * Missing or empty history yields an empty map; callers then fall back to a
92
+ * path-stable order, keeping the evidence digest reproducible.
93
+ */
94
+ async function gitRecencyMap(root, deadline) {
95
+ const remaining = deadline - Date.now();
96
+ if (remaining <= 0)
97
+ return new Map();
98
+ const output = await executeGitReadOnly(root, ['-c', 'core.quotePath=false', 'log', '--no-renames', '--pretty=format:%x00%ct', '--name-only'], { timeoutMs: remaining }).catch(() => '');
99
+ const recency = new Map();
100
+ let current = 0;
101
+ for (const line of output.split('\n')) {
102
+ if (line.startsWith('\0')) {
103
+ current = Number.parseInt(line.slice(1), 10) || 0;
104
+ continue;
105
+ }
106
+ if (line.length === 0)
107
+ continue;
108
+ if (!recency.has(line))
109
+ recency.set(line, current);
110
+ }
111
+ return recency;
112
+ }
113
+ /**
114
+ * FR3: order candidate paths for a fair, prioritized capped walk. Files are
115
+ * grouped by top-level directory; within a group the most recently changed come
116
+ * first (git recency, stable path tiebreak); product (non-dot) directories are
117
+ * round-robined to exhaustion BEFORE any dot-prefixed/planning tree, so a capped
118
+ * walk samples every product directory and dot-config never consumes the cap
119
+ * before product code. Deterministic: identical inputs yield an identical order.
120
+ */
121
+ function orderCandidatesFairly(names, recency) {
122
+ const groups = new Map();
123
+ for (const name of names) {
124
+ const key = topLevelSegment(name) ?? '';
125
+ const bucket = groups.get(key);
126
+ if (bucket)
127
+ bucket.push(name);
128
+ else
129
+ groups.set(key, [name]);
130
+ }
131
+ for (const bucket of groups.values()) {
132
+ bucket.sort((left, right) => (recency.get(right) ?? 0) - (recency.get(left) ?? 0) ||
133
+ (left < right ? -1 : left > right ? 1 : 0));
134
+ }
135
+ const isPlanningGroup = (key) => key.startsWith('.');
136
+ const productGroups = [...groups.keys()].filter((key) => !isPlanningGroup(key)).sort();
137
+ const planningGroups = [...groups.keys()].filter(isPlanningGroup).sort();
138
+ const roundRobin = (keys) => {
139
+ const ordered = [];
140
+ const cursors = new Map(keys.map((key) => [key, 0]));
141
+ for (let active = keys.length; active > 0;) {
142
+ active = 0;
143
+ for (const key of keys) {
144
+ const bucket = groups.get(key) ?? [];
145
+ const cursor = cursors.get(key) ?? 0;
146
+ if (cursor < bucket.length) {
147
+ ordered.push(bucket[cursor]);
148
+ cursors.set(key, cursor + 1);
149
+ active += 1;
150
+ }
151
+ }
152
+ }
153
+ return ordered;
154
+ };
155
+ return [...roundRobin(productGroups), ...roundRobin(planningGroups)];
156
+ }
73
157
  async function repositoryItems(roots, workspace, budgets, now, options) {
74
158
  const descriptors = descriptorById(workspace);
75
159
  const results = [];
76
- let files = 0;
160
+ // FR3: the repository (product code) and planr (planning tree) providers no
161
+ // longer share one undifferentiated `maxFiles` counter. Product code draws the
162
+ // full file budget; the `.planr/` tree draws an independent, smaller allotment
163
+ // so a large planning tree can never consume the cap before product code.
164
+ const repositoryFileBudget = budgets.maxFiles;
165
+ const planrFileBudget = Math.max(1, Math.floor(budgets.maxFiles / 4));
166
+ let repositoryFiles = 0;
167
+ let planrFiles = 0;
77
168
  let bytes = 0;
78
169
  let truncated = false;
79
- for (const [componentId, root] of Object.entries(roots.roots).sort()) {
170
+ let lastPathReached = null;
171
+ const unscannedTopLevel = new Set();
172
+ const componentIds = Object.keys(roots.roots).sort();
173
+ for (let componentIndex = 0; componentIndex < componentIds.length; componentIndex += 1) {
174
+ const componentId = componentIds[componentIndex];
175
+ const root = roots.roots[componentId];
80
176
  if (Date.now() >= options.deadline) {
81
177
  truncated = true;
178
+ for (const remaining of componentIds.slice(componentIndex))
179
+ unscannedTopLevel.add(remaining);
82
180
  break;
83
181
  }
84
182
  const descriptor = descriptors.get(componentId);
@@ -88,20 +186,42 @@ async function repositoryItems(roots, workspace, budgets, now, options) {
88
186
  const names = (await executeGitReadOnly(canonicalRoot, ['ls-files', '-z']))
89
187
  .split('\0')
90
188
  .filter(Boolean)
91
- .filter((name) => TEXT_EXTENSIONS.has(path.extname(name).toLowerCase()))
92
- .sort();
93
- for (const relativePath of names) {
94
- if (Date.now() >= options.deadline ||
95
- files >= budgets.maxFiles ||
96
- results.length >= budgets.maxItems) {
189
+ .filter((name) => TEXT_EXTENSIONS.has(path.extname(name).toLowerCase()));
190
+ const recency = await gitRecencyMap(canonicalRoot, options.deadline);
191
+ const ordered = orderCandidatesFairly(names, recency);
192
+ const pendingTop = new Set(ordered.map((name) => topLevelSegment(name)).filter((top) => top !== null));
193
+ let stopped = false;
194
+ for (const relativePath of ordered) {
195
+ if (Date.now() >= options.deadline || results.length >= budgets.maxItems) {
97
196
  truncated = true;
197
+ stopped = true;
198
+ for (const top of pendingTop)
199
+ unscannedTopLevel.add(top);
98
200
  break;
99
201
  }
202
+ const reachedTop = topLevelSegment(relativePath);
203
+ if (reachedTop)
204
+ pendingTop.delete(reachedTop);
100
205
  const isPlanr = relativePath.startsWith('.planr/');
101
206
  if ((!options.includeRepository && !isPlanr) ||
102
207
  (isPlanr && !options.includePlanr && !options.includeRepository)) {
103
208
  continue;
104
209
  }
210
+ // FR3: enforce the split file allotments. A planr file over the planr
211
+ // allotment, or a product file over the repository allotment, is skipped
212
+ // (and the walk marked truncated) rather than consuming the other
213
+ // provider's budget. Product code is ordered first, so it claims its cap
214
+ // before any planning tree is reached.
215
+ if (isPlanr) {
216
+ if (planrFiles >= planrFileBudget) {
217
+ truncated = true;
218
+ continue;
219
+ }
220
+ }
221
+ else if (repositoryFiles >= repositoryFileBudget) {
222
+ truncated = true;
223
+ continue;
224
+ }
105
225
  const target = path.join(canonicalRoot, relativePath);
106
226
  const directInfo = await lstat(target).catch(() => null);
107
227
  if (!directInfo)
@@ -119,13 +239,20 @@ async function repositoryItems(roots, workspace, budgets, now, options) {
119
239
  continue;
120
240
  if (bytes + info.size > budgets.maxBytes) {
121
241
  truncated = true;
242
+ stopped = true;
243
+ for (const top of pendingTop)
244
+ unscannedTopLevel.add(top);
122
245
  break;
123
246
  }
124
247
  const content = await readFile(resolvedTarget, 'utf8').catch(() => null);
125
248
  if (content === null || content.includes('\0'))
126
249
  continue;
127
- files += 1;
250
+ if (isPlanr)
251
+ planrFiles += 1;
252
+ else
253
+ repositoryFiles += 1;
128
254
  bytes += Buffer.byteLength(content);
255
+ lastPathReached = relativePath;
129
256
  const collectedAt = now.toISOString();
130
257
  const repository = {
131
258
  componentId,
@@ -184,8 +311,15 @@ async function repositoryItems(roots, workspace, budgets, now, options) {
184
311
  });
185
312
  }
186
313
  }
314
+ if (stopped)
315
+ break;
187
316
  }
188
- return { items: results, truncated };
317
+ return {
318
+ items: results,
319
+ truncated,
320
+ lastPathReached,
321
+ unscannedTopLevel: [...unscannedTopLevel].sort(),
322
+ };
189
323
  }
190
324
  async function gitItems(roots, workspace, now, deadline) {
191
325
  const descriptors = descriptorById(workspace);
@@ -749,6 +883,26 @@ function inferImportedCommercialMetric(content, format) {
749
883
  function incrementalEvidencePath(input, key) {
750
884
  return path.join(resolveOperatingPaths(input.projectRoot, { localRoot: input.localRoot }).evidence, 'incremental', `${key.slice('sha256:'.length)}.json`);
751
885
  }
886
+ /**
887
+ * FR4: the board's identity is the hash of its event-chain genesis event. The
888
+ * incremental cache key incorporates it so a board re-inited at the same path —
889
+ * whose workspace components (remote/branch) are unchanged and would otherwise
890
+ * compute an identical key — never reuses a prior generation's baseline. Returns
891
+ * '' when no committed chain exists yet or it cannot be read (the collection then
892
+ * behaves exactly as before this change).
893
+ */
894
+ async function evidenceBoardIdentity(input) {
895
+ try {
896
+ const { events } = await new OperatingEventStore(input.projectRoot, {
897
+ localRoot: input.localRoot,
898
+ }).replay();
899
+ const genesis = events.find((event) => event.previousEventHash === null) ?? events[0];
900
+ return genesis?.eventHash ?? '';
901
+ }
902
+ catch {
903
+ return '';
904
+ }
905
+ }
752
906
  async function readIncrementalEvidence(input, key) {
753
907
  if (!input.incremental)
754
908
  return null;
@@ -757,6 +911,13 @@ async function readIncrementalEvidence(input, key) {
757
911
  if (record.implementation !== 'openplanr-operate-incremental-evidence' || record.key !== key) {
758
912
  return null;
759
913
  }
914
+ // FR4: honor the baseline's own already-written workspaceDigest. A record
915
+ // whose captured workspace no longer matches the current one is treated as
916
+ // no-baseline so the cycle recollects deep, rather than trusting a snapshot
917
+ // bound to a superseded workspace (for example, a prior board generation).
918
+ if (record.workspaceDigest !== input.workspace.workspaceDigest) {
919
+ return null;
920
+ }
760
921
  await assertOperatingArtifact('operating-evidence', record.evidence);
761
922
  return record;
762
923
  }
@@ -829,6 +990,7 @@ export async function collectOperatingEvidence(input) {
829
990
  const now = input.now ?? new Date();
830
991
  const deadline = started + input.budgets.maxDurationMs;
831
992
  const incrementalKey = canonicalDigest({
993
+ boardIdentity: await evidenceBoardIdentity(input),
832
994
  workspaceComponents: [input.workspace.controlRepository, ...input.workspace.components]
833
995
  .map((component) => ({
834
996
  componentId: component.componentId,
@@ -853,6 +1015,13 @@ export async function collectOperatingEvidence(input) {
853
1015
  });
854
1016
  raw.push(...repository.items);
855
1017
  truncated ||= repository.truncated;
1018
+ // FR2: a `maxFiles`/duration/byte truncation is no longer a silent boolean.
1019
+ // Name the last path reached and the top-level directories the capped walk
1020
+ // never scanned so starvation is visible from the cycle warnings alone.
1021
+ if (repository.truncated &&
1022
+ (repository.lastPathReached || repository.unscannedTopLevel.length > 0)) {
1023
+ warnings.push(`Repository evidence collection was truncated at the configured caps; last path reached: ${repository.lastPathReached ?? 'none'}; top-level directories left unscanned: ${repository.unscannedTopLevel.length > 0 ? repository.unscannedTopLevel.join(', ') : 'none'}.`);
1024
+ }
856
1025
  }
857
1026
  if (input.providers.includes('git') && Date.now() < deadline) {
858
1027
  const git = await gitItems(roots, input.workspace, now, deadline);
@@ -961,6 +1130,18 @@ export async function collectOperatingEvidence(input) {
961
1130
  truncated = true;
962
1131
  warnings.push('Evidence collection reached the configured duration budget.');
963
1132
  }
1133
+ // FR2: report the mission-index path-pattern loss up front. The mission
1134
+ // evidence index forbids dot-prefixed/pattern-rejected paths, so those items —
1135
+ // though retained in the full evidence set — can never be referenced by a
1136
+ // bounded mission packet. Counting the loss here (rather than dropping it
1137
+ // silently inside `buildOperatingEvidenceIndex`) is what makes a starved
1138
+ // mission collection observable from the cycle warnings.
1139
+ const missionIndexDrops = missionEvidenceIndexDrops(flattened, {
1140
+ sensitivityCeiling: input.sensitivityCeiling,
1141
+ });
1142
+ if (missionIndexDrops.pathPattern > 0) {
1143
+ warnings.push(`${missionIndexDrops.pathPattern} evidence item(s) cannot be represented in the mission evidence index because their paths are dot-prefixed or pattern-rejected; they remain in the full evidence set but no bounded mission packet can reference them.`);
1144
+ }
964
1145
  const sourceIds = [...new Set(input.providers)].sort();
965
1146
  const delta = buildEvidenceDelta({
966
1147
  incremental: Boolean(input.incremental),
@@ -1051,6 +1232,36 @@ export async function unqualifiedCommercialEvidenceGaps(input) {
1051
1232
  await Promise.all(gaps.map((gap) => assertOperatingArtifact('operating-data-gap', gap)));
1052
1233
  return gaps;
1053
1234
  }
1235
+ /**
1236
+ * FR2: open a governed evidence gap for every mission role whose POST-index
1237
+ * evidence retained zero `source: 'repository'` items. A role that requires
1238
+ * repository code must never dispatch "ready" on an empty evidence base — the
1239
+ * pre-index readiness gate can pass while the mission index built moments later
1240
+ * loses every repository item to the dot-prefixed/pattern filter. Each gap names
1241
+ * the affected role and the reason so the loss is visible instead of silent.
1242
+ */
1243
+ export async function starvedRoleEvidenceGaps(input) {
1244
+ const roleIds = [...new Set(input.roleIds)].sort();
1245
+ const start = input.startingGapNumber ?? 1;
1246
+ const gaps = roleIds.map((roleId, offset) => ({
1247
+ kind: 'operating-data-gap',
1248
+ schemaVersion: OPERATE_SCHEMA_VERSION,
1249
+ protocolVersion: OPERATE_PROTOCOL_VERSION,
1250
+ id: `GAP-${String(start + offset).padStart(3, '0')}`,
1251
+ cycleId: input.cycleId,
1252
+ question: `What repository evidence can ${roleId} read after mission-index filtering removed every repository item?`,
1253
+ reason: 'The mission evidence index retained zero repository items after dot-prefixed/pattern and sensitivity filtering, so this repository-dependent role was gated not-ready instead of dispatching on an empty evidence base.',
1254
+ unblocks: [],
1255
+ affectedRoles: [roleId],
1256
+ status: 'open',
1257
+ owner: input.owner,
1258
+ evidenceRefs: [],
1259
+ createdAt: input.now,
1260
+ updatedAt: input.now,
1261
+ }));
1262
+ await Promise.all(gaps.map((gap) => assertOperatingArtifact('operating-data-gap', gap)));
1263
+ return gaps;
1264
+ }
1054
1265
  export function evidenceProjectionSources(evidence) {
1055
1266
  return evidence.sources.map((source) => ({
1056
1267
  id: source.id,
@@ -1166,6 +1377,33 @@ export function buildOperatingEvidenceIndex(evidence, options = {}) {
1166
1377
  return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
1167
1378
  });
1168
1379
  }
1380
+ /**
1381
+ * FR2: count — using the exact predicates `buildOperatingEvidenceIndex` filters
1382
+ * on — the path-referenced evidence items a mission evidence index would drop:
1383
+ * `pathPattern` for dot-prefixed / pattern-rejected paths, and `sensitivity` for
1384
+ * items above a supplied ceiling. Integration records (github/linear) and
1385
+ * git-by-revision items are never a "loss" (they are not path pointers), so they
1386
+ * are not counted. The caller surfaces the counts as evidence-bundle warnings.
1387
+ */
1388
+ export function missionEvidenceIndexDrops(items, options = {}) {
1389
+ let pathPattern = 0;
1390
+ let sensitivity = 0;
1391
+ for (const item of items) {
1392
+ if (options.sensitivityCeiling &&
1393
+ compareSensitivity(item.sensitivity, options.sensitivityCeiling) > 0) {
1394
+ sensitivity += 1;
1395
+ continue;
1396
+ }
1397
+ const source = item.source;
1398
+ if (!INDEX_SOURCE_CLASSIFICATION[source])
1399
+ continue;
1400
+ if (source === 'repository' || source === 'planr' || source === 'file-import') {
1401
+ if (evidenceIndexPath(item.location) === null)
1402
+ pathPattern += 1;
1403
+ }
1404
+ }
1405
+ return { pathPattern, sensitivity };
1406
+ }
1169
1407
  /**
1170
1408
  * The read roots a mission agent may traverse, derived from the top-level
1171
1409
  * directory segment of every path-referenced index item. Schema-valid and