frontend-project-context 1.8.0 → 1.10.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 (69) hide show
  1. package/CHANGELOG.md +29 -1
  2. package/README.md +26 -7
  3. package/UPGRADING.md +24 -0
  4. package/docs/00-PRODUCT-CONSTITUTION.md +44 -12
  5. package/docs/08-INSTALLATION-AND-DISTRIBUTION.md +9 -3
  6. package/docs/14-FORMAL-RELEASE-READINESS.md +14 -0
  7. package/docs/28-REAL-PROJECT-ONBOARDING-CLOSURE-DESIGN.md +10 -0
  8. package/docs/29-REAL-PROJECT-1.8.0-INITIALIZATION-OBSERVATIONS.md +228 -0
  9. package/docs/30-TASK-CONTEXT-CONSUMPTION-CLOSURE-DESIGN.md +563 -0
  10. package/docs/31-TASK-CONTEXT-INTEGRITY-REPAIR-DESIGN.md +281 -0
  11. package/docs/32-PROJECT-TO-TASK-INTERACTION-PROPOSAL.md +173 -0
  12. package/docs/33-BOUNDED-TASK-HANDOFF-REDESIGN.md +207 -0
  13. package/docs/34-A0-CODEX-HOST-FEASIBILITY.md +61 -0
  14. package/docs/35-CONTEXT-FIRST-TASK-HANDOFF-DESIGN.md +248 -0
  15. package/docs/36-PRODUCT-VALUE-AND-USABILITY-REVIEW.md +158 -0
  16. package/docs/37-EVERYDAY-HANDOFF-FLOW-DESIGN.md +271 -0
  17. package/docs/38-EVERYDAY-HANDOFF-OPERATION.md +147 -0
  18. package/docs/39-SELF-HOST-RELEASE-ACCEPTANCE.md +39 -0
  19. package/docs/AI-PROJECT-INITIALIZATION.md +8 -3
  20. package/docs/PRODUCT-SHARING-AND-ADOPTION-GUIDE.md +111 -0
  21. package/docs/README.md +26 -2
  22. package/docs/USER-AND-AI-OPERATION-MANUAL.md +9 -3
  23. package/docs/assets/product-sharing-01-overview.svg +32 -0
  24. package/docs/assets/product-sharing-02-how-it-works.svg +17 -0
  25. package/docs/assets/product-sharing-03-example.svg +19 -0
  26. package/examples/package.json +1 -1
  27. package/migration-manifest.json +38 -12
  28. package/package.json +2 -2
  29. package/schemas/capabilities.schema.json +26 -12
  30. package/schemas/context-bundle.schema.json +32 -0
  31. package/schemas/coverage-audit.schema.json +6 -4
  32. package/schemas/evidence-bundle.schema.json +1 -1
  33. package/schemas/initialization-instruction.schema.json +17 -5
  34. package/schemas/migration-manifest.schema.json +3 -3
  35. package/schemas/migration-plan.schema.json +1 -1
  36. package/schemas/project-brief.schema.json +400 -0
  37. package/schemas/projection-lock.schema.json +1 -1
  38. package/schemas/task-context-record.schema.json +27 -0
  39. package/schemas/task-handoff-input.schema.json +23 -0
  40. package/schemas/task-handoff-result.schema.json +320 -0
  41. package/schemas/upgrade-assessment.schema.json +1 -1
  42. package/schemas/upgrade-result-bundle.schema.json +1 -1
  43. package/schemas/work-view.schema.json +541 -0
  44. package/src/project-context/adaptive-context-schema.mjs +1 -1
  45. package/src/project-context/adaptive-context.mjs +16 -29
  46. package/src/project-context/ai-entry.mjs +28 -12
  47. package/src/project-context/approver.mjs +6 -2
  48. package/src/project-context/authoring.mjs +28 -8
  49. package/src/project-context/capabilities.mjs +13 -1
  50. package/src/project-context/checker.mjs +1 -1
  51. package/src/project-context/cli.mjs +74 -17
  52. package/src/project-context/context-bundle.mjs +379 -0
  53. package/src/project-context/contract-schema.mjs +62 -9
  54. package/src/project-context/coverage-profile.mjs +127 -0
  55. package/src/project-context/exchange-schema.mjs +24 -6
  56. package/src/project-context/exchange.mjs +3 -0
  57. package/src/project-context/initialization-instruction.mjs +20 -2
  58. package/src/project-context/maintenance.mjs +5 -4
  59. package/src/project-context/migration-manifest.mjs +3 -3
  60. package/src/project-context/path-policy.mjs +13 -0
  61. package/src/project-context/renderer.mjs +11 -4
  62. package/src/project-context/source-reader.mjs +27 -2
  63. package/src/project-context/task-context.mjs +4 -14
  64. package/src/project-context/task-handoff-schema.mjs +185 -0
  65. package/src/project-context/task-handoff.mjs +259 -0
  66. package/src/project-context/upgrade-schema.mjs +1 -1
  67. package/src/project-context/upgrade.mjs +1 -0
  68. package/src/project-context/work-view-schema.mjs +125 -0
  69. package/src/project-context/work-view.mjs +135 -0
@@ -0,0 +1,541 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "urn:frontend-project-context:schema:work-view:2",
4
+ "$defs": {
5
+ "presentation": {
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "headline",
10
+ "constraints",
11
+ "remainingConstraints",
12
+ "basis",
13
+ "remainingBasis",
14
+ "progress",
15
+ "issues",
16
+ "questions",
17
+ "nextStep"
18
+ ],
19
+ "properties": {
20
+ "headline": {
21
+ "type": "string",
22
+ "minLength": 1
23
+ },
24
+ "constraints": {
25
+ "type": "array",
26
+ "maxItems": 5,
27
+ "items": {
28
+ "type": "object",
29
+ "additionalProperties": false,
30
+ "required": [
31
+ "text",
32
+ "itemIds",
33
+ "scope"
34
+ ],
35
+ "properties": {
36
+ "text": {
37
+ "type": "string",
38
+ "minLength": 1
39
+ },
40
+ "itemIds": {
41
+ "type": "array",
42
+ "items": {
43
+ "type": "string",
44
+ "minLength": 1
45
+ },
46
+ "minItems": 1
47
+ },
48
+ "scope": {
49
+ "oneOf": [
50
+ {
51
+ "type": "object",
52
+ "additionalProperties": false,
53
+ "required": [
54
+ "kind"
55
+ ],
56
+ "properties": {
57
+ "kind": {
58
+ "const": "project"
59
+ }
60
+ }
61
+ },
62
+ {
63
+ "type": "object",
64
+ "additionalProperties": false,
65
+ "required": [
66
+ "kind",
67
+ "path"
68
+ ],
69
+ "properties": {
70
+ "kind": {
71
+ "enum": [
72
+ "path-prefix",
73
+ "file"
74
+ ]
75
+ },
76
+ "path": {
77
+ "type": "string",
78
+ "minLength": 1
79
+ }
80
+ }
81
+ }
82
+ ]
83
+ }
84
+ }
85
+ }
86
+ },
87
+ "remainingConstraints": {
88
+ "type": "integer",
89
+ "minimum": 0
90
+ },
91
+ "basis": {
92
+ "type": "array",
93
+ "maxItems": 3,
94
+ "items": {
95
+ "type": "object",
96
+ "additionalProperties": false,
97
+ "required": [
98
+ "text",
99
+ "evidenceIds"
100
+ ],
101
+ "properties": {
102
+ "text": {
103
+ "type": "string",
104
+ "minLength": 1
105
+ },
106
+ "evidenceIds": {
107
+ "type": "array",
108
+ "items": {
109
+ "type": "string",
110
+ "minLength": 1
111
+ }
112
+ },
113
+ "requirementIds": {
114
+ "type": "array",
115
+ "items": {
116
+ "type": "string",
117
+ "minLength": 1
118
+ }
119
+ },
120
+ "decisionIds": {
121
+ "type": "array",
122
+ "items": {
123
+ "type": "string",
124
+ "minLength": 1
125
+ }
126
+ }
127
+ }
128
+ }
129
+ },
130
+ "remainingBasis": {
131
+ "type": "integer",
132
+ "minimum": 0
133
+ },
134
+ "progress": {
135
+ "anyOf": [
136
+ {
137
+ "type": "object",
138
+ "additionalProperties": false,
139
+ "required": [
140
+ "revision",
141
+ "identity",
142
+ "completed",
143
+ "remaining",
144
+ "implementation",
145
+ "nextAction"
146
+ ],
147
+ "properties": {
148
+ "revision": {
149
+ "type": "integer",
150
+ "minimum": 1
151
+ },
152
+ "identity": {
153
+ "const": "host-reported"
154
+ },
155
+ "completed": {
156
+ "type": "array",
157
+ "items": {
158
+ "type": "string",
159
+ "minLength": 1
160
+ }
161
+ },
162
+ "remaining": {
163
+ "type": "array",
164
+ "items": {
165
+ "type": "string",
166
+ "minLength": 1
167
+ }
168
+ },
169
+ "implementation": {
170
+ "$ref": "task-context-record.schema.json#/properties/progress/properties/implementation"
171
+ },
172
+ "nextAction": {
173
+ "type": "string",
174
+ "minLength": 1
175
+ }
176
+ }
177
+ },
178
+ {
179
+ "type": "null"
180
+ }
181
+ ]
182
+ },
183
+ "issues": {
184
+ "type": "array",
185
+ "items": {
186
+ "type": "object",
187
+ "additionalProperties": false,
188
+ "required": [
189
+ "code",
190
+ "object",
191
+ "explanation",
192
+ "actor",
193
+ "nextStep"
194
+ ],
195
+ "properties": {
196
+ "code": {
197
+ "type": "string",
198
+ "minLength": 1
199
+ },
200
+ "object": {
201
+ "type": "string",
202
+ "minLength": 1
203
+ },
204
+ "explanation": {
205
+ "type": "string",
206
+ "minLength": 1
207
+ },
208
+ "actor": {
209
+ "type": "string",
210
+ "minLength": 1
211
+ },
212
+ "nextStep": {
213
+ "type": "string",
214
+ "minLength": 1
215
+ }
216
+ }
217
+ }
218
+ },
219
+ "questions": {
220
+ "type": "array",
221
+ "items": {
222
+ "type": "object",
223
+ "additionalProperties": false,
224
+ "required": [
225
+ "decisionId",
226
+ "question"
227
+ ],
228
+ "properties": {
229
+ "decisionId": {
230
+ "type": "string",
231
+ "minLength": 1
232
+ },
233
+ "question": {
234
+ "type": "string",
235
+ "minLength": 1
236
+ }
237
+ }
238
+ }
239
+ },
240
+ "nextStep": {
241
+ "type": "string",
242
+ "minLength": 1
243
+ }
244
+ }
245
+ },
246
+ "delivery": {
247
+ "type": "object",
248
+ "additionalProperties": false,
249
+ "required": [
250
+ "status",
251
+ "reasonCodes",
252
+ "bytes",
253
+ "maxBytes"
254
+ ],
255
+ "properties": {
256
+ "status": {
257
+ "enum": [
258
+ "ready",
259
+ "withheld"
260
+ ]
261
+ },
262
+ "reasonCodes": {
263
+ "type": "array",
264
+ "items": {
265
+ "type": "string",
266
+ "minLength": 1
267
+ }
268
+ },
269
+ "bytes": {
270
+ "type": "integer",
271
+ "minimum": 1,
272
+ "maximum": 1048576
273
+ },
274
+ "maxBytes": {
275
+ "const": 1048576
276
+ }
277
+ }
278
+ },
279
+ "progress": {
280
+ "type": "object",
281
+ "additionalProperties": false,
282
+ "required": [
283
+ "revision",
284
+ "identity",
285
+ "completed",
286
+ "remaining",
287
+ "implementation",
288
+ "nextAction"
289
+ ],
290
+ "properties": {
291
+ "revision": {
292
+ "type": "integer",
293
+ "minimum": 1
294
+ },
295
+ "identity": {
296
+ "const": "host-reported"
297
+ },
298
+ "completed": {
299
+ "type": "array",
300
+ "items": {
301
+ "type": "string",
302
+ "minLength": 1
303
+ }
304
+ },
305
+ "remaining": {
306
+ "type": "array",
307
+ "items": {
308
+ "type": "string",
309
+ "minLength": 1
310
+ }
311
+ },
312
+ "implementation": {
313
+ "$ref": "task-context-record.schema.json#/properties/progress/properties/implementation"
314
+ },
315
+ "nextAction": {
316
+ "type": "string",
317
+ "minLength": 1
318
+ }
319
+ }
320
+ },
321
+ "item": {
322
+ "type": "object",
323
+ "additionalProperties": false,
324
+ "required": [
325
+ "id",
326
+ "kind",
327
+ "subject",
328
+ "statement",
329
+ "value",
330
+ "scope",
331
+ "sourceIds"
332
+ ],
333
+ "properties": {
334
+ "id": {
335
+ "type": "string",
336
+ "minLength": 1
337
+ },
338
+ "kind": {
339
+ "enum": [
340
+ "fact",
341
+ "policy",
342
+ "reference",
343
+ "validation-description"
344
+ ]
345
+ },
346
+ "subject": {
347
+ "type": "string",
348
+ "minLength": 1
349
+ },
350
+ "statement": {
351
+ "type": "string",
352
+ "minLength": 1
353
+ },
354
+ "value": {},
355
+ "scope": {
356
+ "oneOf": [
357
+ {
358
+ "type": "object",
359
+ "additionalProperties": false,
360
+ "required": [
361
+ "kind"
362
+ ],
363
+ "properties": {
364
+ "kind": {
365
+ "const": "project"
366
+ }
367
+ }
368
+ },
369
+ {
370
+ "type": "object",
371
+ "additionalProperties": false,
372
+ "required": [
373
+ "kind",
374
+ "path"
375
+ ],
376
+ "properties": {
377
+ "kind": {
378
+ "enum": [
379
+ "path-prefix",
380
+ "file"
381
+ ]
382
+ },
383
+ "path": {
384
+ "type": "string",
385
+ "minLength": 1
386
+ }
387
+ }
388
+ }
389
+ ]
390
+ },
391
+ "sourceIds": {
392
+ "type": "array",
393
+ "items": {
394
+ "type": "string",
395
+ "minLength": 1
396
+ }
397
+ }
398
+ }
399
+ },
400
+ "taskSnapshot": {
401
+ "type": "object",
402
+ "additionalProperties": false,
403
+ "required": [
404
+ "origin",
405
+ "scope",
406
+ "requirements",
407
+ "decisions",
408
+ "evidence",
409
+ "progress"
410
+ ],
411
+ "properties": {
412
+ "origin": {
413
+ "$ref": "task-context-record.schema.json#/properties/origin"
414
+ },
415
+ "scope": {
416
+ "$ref": "task-context-record.schema.json#/properties/scope"
417
+ },
418
+ "requirements": {
419
+ "$ref": "task-context-record.schema.json#/properties/requirements"
420
+ },
421
+ "decisions": {
422
+ "$ref": "task-context-record.schema.json#/properties/decisions"
423
+ },
424
+ "evidence": {
425
+ "type": "array",
426
+ "items": {
427
+ "allOf": [
428
+ {
429
+ "type": "object",
430
+ "required": [
431
+ "id",
432
+ "purpose",
433
+ "kind",
434
+ "digest",
435
+ "identity",
436
+ "status"
437
+ ],
438
+ "properties": {
439
+ "id": {
440
+ "type": "string",
441
+ "minLength": 1
442
+ },
443
+ "purpose": {
444
+ "enum": [
445
+ "requirement",
446
+ "design",
447
+ "decision",
448
+ "implementation",
449
+ "reference"
450
+ ]
451
+ },
452
+ "kind": {
453
+ "enum": [
454
+ "local",
455
+ "inline",
456
+ "external"
457
+ ]
458
+ },
459
+ "path": {
460
+ "type": "string",
461
+ "minLength": 1
462
+ },
463
+ "text": {
464
+ "type": "string",
465
+ "minLength": 1
466
+ },
467
+ "reference": {
468
+ "type": "string",
469
+ "minLength": 1
470
+ },
471
+ "digest": {
472
+ "type": "string",
473
+ "pattern": "^sha256:[a-f0-9]{64}$"
474
+ },
475
+ "identity": {
476
+ "enum": [
477
+ "local-verified",
478
+ "user-statement",
479
+ "host-asserted"
480
+ ]
481
+ },
482
+ "status": {
483
+ "enum": [
484
+ "current",
485
+ "changed",
486
+ "missing",
487
+ "not-checked"
488
+ ]
489
+ }
490
+ },
491
+ "additionalProperties": false
492
+ }
493
+ ]
494
+ }
495
+ },
496
+ "progress": {
497
+ "type": "object",
498
+ "additionalProperties": false,
499
+ "required": [
500
+ "revision",
501
+ "identity",
502
+ "completed",
503
+ "remaining",
504
+ "implementation",
505
+ "nextAction"
506
+ ],
507
+ "properties": {
508
+ "revision": {
509
+ "type": "integer",
510
+ "minimum": 1
511
+ },
512
+ "identity": {
513
+ "const": "host-reported"
514
+ },
515
+ "completed": {
516
+ "type": "array",
517
+ "items": {
518
+ "type": "string",
519
+ "minLength": 1
520
+ }
521
+ },
522
+ "remaining": {
523
+ "type": "array",
524
+ "items": {
525
+ "type": "string",
526
+ "minLength": 1
527
+ }
528
+ },
529
+ "implementation": {
530
+ "$ref": "task-context-record.schema.json#/properties/progress/properties/implementation"
531
+ },
532
+ "nextAction": {
533
+ "type": "string",
534
+ "minLength": 1
535
+ }
536
+ }
537
+ }
538
+ }
539
+ }
540
+ }
541
+ }
@@ -5,7 +5,7 @@ import { normalizeRelativePath } from "./path-policy.mjs";
5
5
  export const CONTEXT_QUERY_SCHEMA_VERSION = 2;
6
6
  export const ADAPTIVE_CONTEXT_BUNDLE_SCHEMA_VERSION = 2;
7
7
  export const ROUTING_INDEX_SCHEMA_VERSION = 2;
8
- export const COVERAGE_AUDIT_SCHEMA_VERSION = 1;
8
+ export const COVERAGE_AUDIT_SCHEMA_VERSION = 2;
9
9
  export const ADAPTIVE_SELECTOR_VERSION = 2;
10
10
  export const ROUTING_INDEX_PATH = ".project-context/derived/routing-index.json";
11
11
 
@@ -1,12 +1,11 @@
1
- import { access } from "node:fs/promises";
2
- import path from "node:path";
3
1
  import { canonicalJson, digestJson, prettyCanonicalJson, sha256 } from "./canonical-json.mjs";
4
2
  import { blockingContextFindings, checkProject } from "./checker.mjs";
5
3
  import { sourceStatus } from "./contract-schema.mjs";
4
+ import { coverageClass, coverageProfile } from "./coverage-profile.mjs";
6
5
  import { discoverProject } from "./discovery.mjs";
7
6
  import { ProjectContextError, fail } from "./errors.mjs";
8
7
  import { atomicWriteFile, readJsonFile } from "./io.mjs";
9
- import { normalizeRelativePath, resolveExistingInside, resolveWritableInside } from "./path-policy.mjs";
8
+ import { inspectContextTarget, normalizeRelativePath, resolveExistingInside, resolveWritableInside } from "./path-policy.mjs";
10
9
  import { loadProject } from "./project-store.mjs";
11
10
  import { renderTaskContextBundle } from "./renderer.mjs";
12
11
  import { effectiveItems, findConflicts, scopeApplies, validateOverrides } from "./scope-compiler.mjs";
@@ -26,7 +25,6 @@ import {
26
25
  const LOCAL_SOURCE_KINDS = new Set(["file", "path", "json-pointer"]);
27
26
  const MANDATORY_KINDS = new Set(["policy", "validation-description"]);
28
27
  const ROUTED_KINDS = new Set(["fact", "reference"]);
29
- const COVERAGE_SUBJECT = "project.registration-coverage";
30
28
  const RETRIEVAL_SUBJECT = "project.context-retrieval";
31
29
  const EXCLUDED_BODIES = Object.freeze(["chat-history", "git-diffs", "source-bodies", "task-path-bodies", "verification-logs"]);
32
30
  const ROUTING_STOP_TERMS = new Set(["a", "an", "and", "apply", "change", "for", "in", "of", "on", "project", "rule", "the", "to", "update", "use"]);
@@ -145,24 +143,7 @@ export async function indexContext(root, project, options = {}) {
145
143
  return index;
146
144
  }
147
145
 
148
- function pathWithin(target, parent) { return parent === "." || target === parent || target.startsWith(`${parent}/`); }
149
- function coverageProfile(contract) {
150
- const candidates = contract.items.filter((item) => item.status === "approved" && item.kind === "policy" && item.subject === COVERAGE_SUBJECT);
151
- if (candidates.length === 0) return null;
152
- if (candidates.length > 1) fail("coverage-profile-conflict", `multiple approved ${COVERAGE_SUBJECT} items are active`);
153
- const item = candidates[0];
154
- const value = item.value;
155
- if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).some((key) => !["excludedPaths", "roots"].includes(key)) || !Array.isArray(value.roots) || !Array.isArray(value.excludedPaths)) fail("coverage-profile-invalid", `${item.id} coverage value is invalid`);
156
- const normalize = (values, label) => uniqueSorted(values.map((entry) => normalizeRelativePath(entry, { allowRoot: true, label })));
157
- return { itemId: item.id, itemDigest: digestJson(item), sourceIds: [...item.sources].sort(), roots: normalize(value.roots, "coverage root"), excludedPaths: normalize(value.excludedPaths, "coverage excluded path") };
158
- }
159
- function coverageClass(profile, registeredPaths, candidatePath) {
160
- if (registeredPaths.has(candidatePath)) return "registered";
161
- if (!profile) return "outside-declared-coverage";
162
- if (profile.excludedPaths.some((entry) => pathWithin(candidatePath, entry))) return "excluded-approved";
163
- if (profile.roots.some((entry) => pathWithin(candidatePath, entry))) return "review-required";
164
- return "outside-declared-coverage";
165
- }
146
+ export { coverageClass, coverageProfile } from "./coverage-profile.mjs";
166
147
  export async function buildCoverageAudit(root, project, changedPaths = []) {
167
148
  const profile = coverageProfile(project.contract);
168
149
  const proposal = await discoverProject(root, project.contract);
@@ -175,10 +156,16 @@ export async function buildCoverageAudit(root, project, changedPaths = []) {
175
156
  if (!byPath.has(normalized)) byPath.set(normalized, { path: normalized, origin: "host-changed-path-signal" });
176
157
  }
177
158
  for (const registeredPath of registered) if (!byPath.has(registeredPath)) byPath.set(registeredPath, { path: registeredPath, origin: "registered-source" });
178
- const categories = { registered: [], "excluded-approved": [], "review-required": [], "outside-declared-coverage": [] };
159
+ const categories = { registered: [], "dynamic-investigation": [], "excluded-approved": [], "review-required": [], unresolved: [], "outside-declared-coverage": [] };
179
160
  for (const candidate of [...byPath.values()].sort((left, right) => left.path.localeCompare(right.path))) categories[coverageClass(profile, registeredPaths, candidate.path)].push(candidate);
180
- const registrationCoverage = !profile ? "not-declared" : categories["review-required"].length === 0 ? "closed-for-declared-scope" : "review-required";
181
- return sealArtifact({ schemaVersion: 1, kind: "coverage-audit", project: { id: project.contract.project.id, name: project.contract.project.name }, snapshots: snapshots(project), profile, registrationCoverage, categories, guarantee: "declared-scope-only-never-all-project-truth" }, "auditDigest");
161
+ const registrationCoverage = !profile
162
+ ? "not-declared"
163
+ : categories.unresolved.length > 0
164
+ ? "unresolved"
165
+ : categories["review-required"].length === 0
166
+ ? "closed-for-declared-scope"
167
+ : "review-required";
168
+ return sealArtifact({ schemaVersion: 2, kind: "coverage-audit", project: { id: project.contract.project.id, name: project.contract.project.name }, snapshots: snapshots(project), profile, registrationCoverage, categories, guarantee: "declared-scope-only-never-all-project-truth" }, "auditDigest");
182
169
  }
183
170
 
184
171
  function effectiveByTarget(contract, paths, findings) {
@@ -331,7 +318,7 @@ async function auditRelevantSources(root, project, items, sourceReadContext) {
331
318
  const base = { id: source.id, kind: source.kind, locator: sourceLocator(source), sourceDigest: digestJson(source) };
332
319
  if (!LOCAL_SOURCE_KINDS.has(source.kind)) { evidence.push({ ...base, freshness: "unverifiable-non-local" }); continue; }
333
320
  try {
334
- const actual = await readSourceDigest(root, source, sourceReadContext);
321
+ const actual = await readSourceDigest(root, source, sourceReadContext, { projectionsLock: project.projectionsLock });
335
322
  const expected = lock.get(source.id) ?? null;
336
323
  const freshness = expected !== null && expected === source.digest && actual === expected ? "current" : "drifted";
337
324
  evidence.push({ ...base, freshness, expectedDigest: expected, actualDigest: actual });
@@ -372,8 +359,7 @@ function monotonicBudget(previous, query, findings) {
372
359
  async function targetStates(root, paths) {
373
360
  const result = [];
374
361
  for (const entry of paths) {
375
- try { await access(path.resolve(root, entry)); result.push({ path: entry, state: "existing" }); }
376
- catch (error) { if (error?.code !== "ENOENT") throw error; result.push({ path: entry, state: "prospective-or-deleted" }); }
362
+ result.push({ path: entry, state: await inspectContextTarget(root, entry) });
377
363
  }
378
364
  return result;
379
365
  }
@@ -413,6 +399,7 @@ export async function buildAdaptiveContextBundle(root, project, queryInput, opti
413
399
  if (query.projectId !== project.contract.project.id) fail("context-query-project-mismatch", "context query belongs to a different project");
414
400
  if (query.level === "expanded" && !options.previous) fail("context-query-previous-required", "expanded context requires --previous");
415
401
  if (query.level !== "expanded" && options.previous) fail("context-query-previous-not-allowed", `${query.level} context cannot use --previous`);
402
+ const states = await targetStates(root, uniqueSorted([...query.task.paths, ...query.task.changedPaths]));
416
403
  const previous = options.previous ?? null;
417
404
  const indexResult = await routingIndexForQuery(root, project);
418
405
  const findings = baselineFindings(query, project);
@@ -493,7 +480,7 @@ export async function buildAdaptiveContextBundle(root, project, queryInput, opti
493
480
  },
494
481
  globalHealth,
495
482
  taskHealth: health,
496
- targetStates: await targetStates(root, query.task.paths),
483
+ targetStates: states.filter((entry) => query.task.paths.includes(entry.path)),
497
484
  hydratedItems,
498
485
  retainedItemIds,
499
486
  deferredItems: selection.deferred.filter((item) => !finalIds.includes(item.id)).map(deferredItem),