@evo-dev/core 0.0.1-alpha.2 → 0.0.1-alpha.20

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 (79) hide show
  1. package/assets/skills/coding/knowledge-distillation/SKILL.md +5 -3
  2. package/assets/team/agents/code-reviewer.md +48 -0
  3. package/assets/team/agents/docs-maintainer.md +51 -0
  4. package/assets/team/agents/implementation-engineer.md +51 -0
  5. package/assets/team/agents/product-scope-analyst.md +58 -0
  6. package/assets/team/agents/release-engineer.md +55 -0
  7. package/assets/team/agents/security-boundary-reviewer.md +50 -0
  8. package/assets/team/agents/solution-architect.md +51 -0
  9. package/assets/team/agents/verification-engineer.md +51 -0
  10. package/assets/team/team.md +102 -0
  11. package/dist/assets/index.js +5 -5
  12. package/dist/config/index.js +793 -241
  13. package/dist/index.js +20840 -12908
  14. package/dist/plugins/index.js +13 -13
  15. package/package.json +1 -1
  16. package/src/agents/index.ts +1 -265
  17. package/src/code-agent-traces/index.ts +11 -12
  18. package/src/config/index.ts +2 -0
  19. package/src/config/settings.ts +116 -7
  20. package/src/config/store.ts +1 -1
  21. package/src/daemon/index.ts +1 -41
  22. package/src/evolution/candidates/index.ts +730 -0
  23. package/src/evolution/control/index.ts +20 -0
  24. package/src/evolution/evidence/analysis.ts +533 -0
  25. package/src/evolution/evidence/index.ts +3 -0
  26. package/src/evolution/evidence/session-memory/analysis.ts +287 -0
  27. package/src/evolution/evidence/session-memory/constants.ts +9 -0
  28. package/src/evolution/evidence/session-memory/index.ts +9 -0
  29. package/src/evolution/evidence/session-memory/paths.ts +29 -0
  30. package/src/evolution/evidence/session-memory/policy.ts +39 -0
  31. package/src/evolution/evidence/session-memory/retention.ts +643 -0
  32. package/src/evolution/evidence/session-memory/segment.ts +216 -0
  33. package/src/evolution/evidence/session-memory/semantic-packet.ts +408 -0
  34. package/src/evolution/evidence/session-memory/sensitivity.ts +335 -0
  35. package/src/evolution/evidence/session-memory/state-machine.ts +249 -0
  36. package/src/evolution/evidence/session-memory/storage.ts +744 -0
  37. package/src/evolution/evidence/session-memory/types.ts +296 -0
  38. package/src/evolution/evidence/session-memory/updater.ts +199 -0
  39. package/src/evolution/formatters.ts +169 -0
  40. package/src/evolution/imports/apply.ts +435 -0
  41. package/src/evolution/imports/diff.ts +472 -0
  42. package/src/evolution/imports/index.ts +7 -0
  43. package/src/evolution/imports/materialize.ts +640 -0
  44. package/src/evolution/imports/paths.ts +129 -0
  45. package/src/evolution/imports/stage.ts +414 -0
  46. package/src/evolution/imports/storage.ts +952 -0
  47. package/src/evolution/imports/types.ts +226 -0
  48. package/src/evolution/index.ts +19 -2827
  49. package/src/evolution/knowledge/change-store.ts +558 -0
  50. package/src/evolution/knowledge/changes.ts +459 -0
  51. package/src/evolution/knowledge/freshness.ts +69 -0
  52. package/src/{knowledge → evolution/knowledge}/index.ts +1532 -206
  53. package/src/evolution/knowledge/review.ts +446 -0
  54. package/src/evolution/knowledge/support.ts +135 -0
  55. package/src/evolution/paths.ts +44 -0
  56. package/src/evolution/processor/distillation.ts +518 -0
  57. package/src/evolution/processor/index.ts +3 -0
  58. package/src/evolution/processor/process.ts +594 -0
  59. package/src/{learning → evolution/review}/index.ts +10 -14
  60. package/src/evolution/schema.ts +639 -0
  61. package/src/evolution/shared.ts +1053 -0
  62. package/src/evolution/triggers/classification.ts +102 -0
  63. package/src/evolution/triggers/index.ts +295 -0
  64. package/src/hooks/index.ts +281 -197
  65. package/src/index.ts +15 -4
  66. package/src/projects/index.ts +934 -0
  67. package/src/runtime-logs/index.ts +100 -13
  68. package/src/team/index.ts +582 -3
  69. package/src/utils/errors.ts +13 -0
  70. package/src/utils/fs.ts +40 -0
  71. package/src/utils/hash.ts +9 -0
  72. package/src/utils/ids.ts +12 -0
  73. package/src/utils/index.ts +7 -0
  74. package/src/utils/parsing.ts +11 -0
  75. package/src/utils/text.ts +18 -0
  76. package/src/utils/time.ts +5 -0
  77. package/src/workflow/index.ts +3 -21
  78. package/src/project/index.ts +0 -507
  79. package/src/task/index.ts +0 -840
@@ -0,0 +1,446 @@
1
+ import { detectSessionMemorySensitivity } from "../evidence/session-memory/sensitivity.ts";
2
+ import {
3
+ type OkfKnowledgeChangeCandidateV1,
4
+ readOkfKnowledgeChangeCandidate,
5
+ replaceOkfKnowledgeChangeCandidate,
6
+ withOkfKnowledgeChangeLock,
7
+ } from "./change-store.ts";
8
+ import {
9
+ type OkfKnowledgeChangeState,
10
+ createOkfKnowledgeRevision,
11
+ createOkfKnowledgeRuntimeProjectionFromConcept,
12
+ } from "./changes.ts";
13
+ import {
14
+ type OkfKnowledgeActivationResult,
15
+ type OkfKnowledgePlan,
16
+ activateOkfKnowledgePlan,
17
+ listOkfKnowledgeConcepts,
18
+ readOkfKnowledgeConcept,
19
+ revokeOkfKnowledgeConcept,
20
+ supersedeOkfKnowledgeConcept,
21
+ } from "./index.ts";
22
+
23
+ export type OkfKnowledgeChangeReviewDecision = "accept" | "reject" | "defer";
24
+
25
+ export interface OkfKnowledgeChangeDecisionPreview {
26
+ changeId: string;
27
+ operation: OkfKnowledgeChangeCandidateV1["operation"];
28
+ currentState: OkfKnowledgeChangeState;
29
+ decision: OkfKnowledgeChangeReviewDecision;
30
+ baseRevision: string | null;
31
+ candidateRevision: string;
32
+ runtimeFields: string[];
33
+ willMutateActiveKnowledge: boolean;
34
+ }
35
+
36
+ export interface OkfKnowledgeChangeDecisionResult extends OkfKnowledgeChangeDecisionPreview {
37
+ state: OkfKnowledgeChangeState;
38
+ activation: OkfKnowledgeActivationResult | null;
39
+ conceptPaths: string[];
40
+ }
41
+
42
+ export async function previewOkfKnowledgeChangeDecision(input: {
43
+ homeDir: string;
44
+ changeId: string;
45
+ decision: OkfKnowledgeChangeReviewDecision;
46
+ expectedState: OkfKnowledgeChangeState;
47
+ expectedBaseRevision: string | null;
48
+ }): Promise<OkfKnowledgeChangeDecisionPreview> {
49
+ const { change } = await readOkfKnowledgeChangeCandidate({
50
+ homeDir: input.homeDir,
51
+ changeId: input.changeId,
52
+ });
53
+ await assertReviewPreconditions({
54
+ homeDir: input.homeDir,
55
+ change,
56
+ expectedState: input.expectedState,
57
+ expectedBaseRevision: input.expectedBaseRevision,
58
+ });
59
+ return toPreview(change, input.decision);
60
+ }
61
+
62
+ export async function decideOkfKnowledgeChange(input: {
63
+ homeDir: string;
64
+ changeId: string;
65
+ decision: OkfKnowledgeChangeReviewDecision;
66
+ expectedState: OkfKnowledgeChangeState;
67
+ expectedBaseRevision: string | null;
68
+ reason?: string | null;
69
+ now?: string | Date;
70
+ }): Promise<OkfKnowledgeChangeDecisionResult> {
71
+ const initial = await readOkfKnowledgeChangeCandidate({
72
+ homeDir: input.homeDir,
73
+ changeId: input.changeId,
74
+ });
75
+ return await withOkfKnowledgeChangeLock({
76
+ homeDir: input.homeDir,
77
+ projectKey: initial.change.projectKey,
78
+ stableKey: initial.change.stableKey,
79
+ conceptId: initial.change.base?.conceptId ?? null,
80
+ run: async () => {
81
+ const { change } = await readOkfKnowledgeChangeCandidate({
82
+ homeDir: input.homeDir,
83
+ changeId: input.changeId,
84
+ projectKey: initial.change.projectKey,
85
+ });
86
+ assertReviewInputPreconditions({
87
+ change,
88
+ expectedState: input.expectedState,
89
+ expectedBaseRevision: input.expectedBaseRevision,
90
+ });
91
+ if (change.state === "accepted" && input.decision !== "accept") {
92
+ throw new Error(
93
+ "Knowledge change state conflict: an accepted change can only resume acceptance.",
94
+ );
95
+ }
96
+ if (change.state === "accepted" && input.decision === "accept") {
97
+ const recovered = await readAlreadyAppliedKnowledgeChange({
98
+ homeDir: input.homeDir,
99
+ change,
100
+ });
101
+ if (recovered !== null) {
102
+ const completed: OkfKnowledgeChangeCandidateV1 = {
103
+ ...change,
104
+ state: "applied",
105
+ updatedAt: resolveNow(input.now),
106
+ };
107
+ await replaceOkfKnowledgeChangeCandidate({
108
+ homeDir: input.homeDir,
109
+ previous: change,
110
+ next: completed,
111
+ reason: input.reason ?? null,
112
+ });
113
+ return {
114
+ ...toPreview(change, input.decision),
115
+ state: "applied",
116
+ activation: null,
117
+ conceptPaths: recovered.conceptPaths,
118
+ };
119
+ }
120
+ }
121
+ try {
122
+ await assertReviewPreconditions({
123
+ homeDir: input.homeDir,
124
+ change,
125
+ expectedState: input.expectedState,
126
+ expectedBaseRevision: input.expectedBaseRevision,
127
+ });
128
+ } catch (error) {
129
+ if (isBaseRevisionConflict(error) && change.state !== "superseded") {
130
+ const superseded = {
131
+ ...change,
132
+ state: "superseded" as const,
133
+ updatedAt: resolveNow(input.now),
134
+ };
135
+ await replaceOkfKnowledgeChangeCandidate({
136
+ homeDir: input.homeDir,
137
+ previous: change,
138
+ next: superseded,
139
+ reason: "Base revision changed before review.",
140
+ });
141
+ }
142
+ throw error;
143
+ }
144
+
145
+ const reason = normalizeDecisionReason(input.reason);
146
+ const decidedAt = resolveNow(input.now);
147
+ const decisionState =
148
+ input.decision === "accept"
149
+ ? ("accepted" as const)
150
+ : input.decision === "reject"
151
+ ? ("rejected" as const)
152
+ : ("deferred" as const);
153
+ const decided =
154
+ change.state === "accepted" && input.decision === "accept"
155
+ ? change
156
+ : {
157
+ ...change,
158
+ state: decisionState,
159
+ decision: {
160
+ state: decisionState,
161
+ reason,
162
+ decidedAt,
163
+ },
164
+ updatedAt: decidedAt,
165
+ };
166
+ if (decided !== change) {
167
+ await replaceOkfKnowledgeChangeCandidate({
168
+ homeDir: input.homeDir,
169
+ previous: change,
170
+ next: decided,
171
+ reason,
172
+ });
173
+ }
174
+ const preview = toPreview(change, input.decision);
175
+ if (input.decision !== "accept") {
176
+ return {
177
+ ...preview,
178
+ state: decided.state,
179
+ activation: null,
180
+ conceptPaths: [],
181
+ };
182
+ }
183
+
184
+ const applied = await applyAcceptedChange({
185
+ homeDir: input.homeDir,
186
+ change: decided,
187
+ now: decidedAt,
188
+ reason,
189
+ });
190
+ const current = (
191
+ await readOkfKnowledgeChangeCandidate({
192
+ homeDir: input.homeDir,
193
+ changeId: input.changeId,
194
+ projectKey: change.projectKey,
195
+ })
196
+ ).change;
197
+ const completed: OkfKnowledgeChangeCandidateV1 = {
198
+ ...current,
199
+ state: "applied",
200
+ updatedAt: resolveNow(input.now),
201
+ };
202
+ await replaceOkfKnowledgeChangeCandidate({
203
+ homeDir: input.homeDir,
204
+ previous: current,
205
+ next: completed,
206
+ reason,
207
+ });
208
+ return {
209
+ ...preview,
210
+ state: "applied",
211
+ activation: applied.activation,
212
+ conceptPaths: applied.conceptPaths,
213
+ };
214
+ },
215
+ });
216
+ }
217
+
218
+ async function assertReviewPreconditions(input: {
219
+ homeDir: string;
220
+ change: OkfKnowledgeChangeCandidateV1;
221
+ expectedState: OkfKnowledgeChangeState;
222
+ expectedBaseRevision: string | null;
223
+ }): Promise<void> {
224
+ assertReviewInputPreconditions(input);
225
+ if (input.change.base === null) {
226
+ const concepts = await listOkfKnowledgeConcepts({ homeDir: input.homeDir });
227
+ if (
228
+ concepts.some(
229
+ (concept) =>
230
+ concept.stableKey === input.change.stableKey ||
231
+ concept.id === conceptIdFromTargetPath(input.change.targetPath),
232
+ )
233
+ ) {
234
+ throw createBaseRevisionConflict(
235
+ "Knowledge change base revision conflict: a concept now uses this stable key or target path.",
236
+ );
237
+ }
238
+ return;
239
+ }
240
+ const current = await readOkfKnowledgeConcept({
241
+ homeDir: input.homeDir,
242
+ conceptId: input.change.base.conceptId,
243
+ });
244
+ const currentRevision = createOkfKnowledgeRevision(
245
+ createOkfKnowledgeRuntimeProjectionFromConcept(current),
246
+ );
247
+ if (currentRevision !== input.change.base.revision) {
248
+ throw createBaseRevisionConflict(
249
+ `Knowledge change base revision conflict: expected ${input.change.base.revision}, found ${currentRevision}.`,
250
+ );
251
+ }
252
+ }
253
+
254
+ function assertReviewInputPreconditions(input: {
255
+ change: OkfKnowledgeChangeCandidateV1;
256
+ expectedState: OkfKnowledgeChangeState;
257
+ expectedBaseRevision: string | null;
258
+ }): void {
259
+ if (input.change.state !== input.expectedState) {
260
+ throw new Error(
261
+ `Knowledge change state conflict: expected ${input.expectedState}, found ${input.change.state}.`,
262
+ );
263
+ }
264
+ const storedBaseRevision = input.change.base?.revision ?? null;
265
+ if (storedBaseRevision !== input.expectedBaseRevision) {
266
+ throw new Error(
267
+ `Knowledge change base revision input does not match candidate: expected ${storedBaseRevision ?? "none"}.`,
268
+ );
269
+ }
270
+ }
271
+
272
+ async function readAlreadyAppliedKnowledgeChange(input: {
273
+ homeDir: string;
274
+ change: OkfKnowledgeChangeCandidateV1;
275
+ }): Promise<{ conceptPaths: string[] } | null> {
276
+ if (input.change.operation === "supersede") {
277
+ if (input.change.base === null) return null;
278
+ const replacement = await readConceptIfPresent(
279
+ input.homeDir,
280
+ conceptIdFromTargetPath(input.change.targetPath),
281
+ );
282
+ const previous = await readConceptIfPresent(input.homeDir, input.change.base.conceptId);
283
+ if (
284
+ replacement === null ||
285
+ previous === null ||
286
+ replacement.stableKey !== input.change.stableKey ||
287
+ previous.lifecycle.status !== "superseded" ||
288
+ previous.lifecycle.supersededBy !== replacement.id ||
289
+ !replacement.lifecycle.supersedes.includes(previous.id)
290
+ ) {
291
+ return null;
292
+ }
293
+ return { conceptPaths: [previous.path, replacement.path] };
294
+ }
295
+
296
+ const conceptId =
297
+ input.change.operation === "create"
298
+ ? conceptIdFromTargetPath(input.change.targetPath)
299
+ : input.change.base?.conceptId;
300
+ if (conceptId === undefined) return null;
301
+ const concept = await readConceptIfPresent(input.homeDir, conceptId);
302
+ if (concept === null || concept.stableKey !== input.change.stableKey) return null;
303
+ const revision = createOkfKnowledgeRevision(
304
+ createOkfKnowledgeRuntimeProjectionFromConcept(concept),
305
+ );
306
+ return revision === input.change.candidate.revision ? { conceptPaths: [concept.path] } : null;
307
+ }
308
+
309
+ async function readConceptIfPresent(
310
+ homeDir: string,
311
+ conceptId: string,
312
+ ): Promise<Awaited<ReturnType<typeof readOkfKnowledgeConcept>> | null> {
313
+ try {
314
+ return await readOkfKnowledgeConcept({ homeDir, conceptId });
315
+ } catch (error) {
316
+ if (error instanceof Error && /not found/iu.test(error.message)) return null;
317
+ throw error;
318
+ }
319
+ }
320
+
321
+ function conceptIdFromTargetPath(targetPath: string): string {
322
+ return targetPath.replace(/^\/+/u, "").replace(/\.md$/u, "");
323
+ }
324
+
325
+ function createBaseRevisionConflict(message: string): Error {
326
+ return Object.assign(new Error(message), {
327
+ code: "KNOWLEDGE_BASE_REVISION_CONFLICT",
328
+ });
329
+ }
330
+
331
+ async function applyAcceptedChange(input: {
332
+ homeDir: string;
333
+ change: OkfKnowledgeChangeCandidateV1;
334
+ now: string;
335
+ reason: string | null;
336
+ }): Promise<{ activation: OkfKnowledgeActivationResult | null; conceptPaths: string[] }> {
337
+ if (input.change.operation === "revoke") {
338
+ if (input.change.base === null) throw new Error("Knowledge revoke requires an active base.");
339
+ const result = await revokeOkfKnowledgeConcept({
340
+ homeDir: input.homeDir,
341
+ conceptId: input.change.base.conceptId,
342
+ reason: input.reason ?? input.change.candidate.planCandidate.decisionReason.slice(0, 500),
343
+ now: input.now,
344
+ });
345
+ return { activation: null, conceptPaths: result.conceptPaths };
346
+ }
347
+
348
+ const planCandidate = {
349
+ ...input.change.candidate.planCandidate,
350
+ decision: input.change.operation === "update" ? ("update" as const) : ("create" as const),
351
+ reviewState: "accepted" as const,
352
+ targetPath:
353
+ input.change.operation === "update" && input.change.base !== null
354
+ ? input.change.base.sourceLink.replace(/^\/+/u, "")
355
+ : input.change.targetPath,
356
+ };
357
+ const plan: OkfKnowledgePlan = {
358
+ schemaVersion: 1,
359
+ kind: "evodev-knowledge-plan",
360
+ projectKey: input.change.projectKey,
361
+ runId: input.change.runId,
362
+ createdAt: input.now,
363
+ evidenceWindowId: input.change.provenance.evidenceWindowId,
364
+ summary: `Apply reviewed knowledge change ${input.change.id}.`,
365
+ evidenceRefs: input.change.provenance.evidenceRefs.map((id) => ({
366
+ id,
367
+ kind: "knowledge-change-evidence",
368
+ source: `knowledge-change:${input.change.id}`,
369
+ rawContentStored: false,
370
+ externalContentCopied: false,
371
+ })),
372
+ evoEvalSets: input.change.candidate.evalSets,
373
+ candidates: [planCandidate],
374
+ droppedSignals: [],
375
+ conflicts: [],
376
+ privacyCheck: planCandidate.privacyCheck,
377
+ };
378
+ const activation = await activateOkfKnowledgePlan({
379
+ homeDir: input.homeDir,
380
+ plan,
381
+ overwrite: input.change.operation === "update",
382
+ reviewedChangeId: input.change.id,
383
+ });
384
+ const conceptPaths = [...activation.conceptPaths];
385
+ if (input.change.operation === "supersede") {
386
+ if (input.change.base === null) throw new Error("Knowledge supersede requires an active base.");
387
+ const replacementId = input.change.targetPath.replace(/^\/+/u, "").replace(/\.md$/u, "");
388
+ const lifecycle = await supersedeOkfKnowledgeConcept({
389
+ homeDir: input.homeDir,
390
+ oldConceptId: input.change.base.conceptId,
391
+ newConceptId: replacementId,
392
+ now: input.now,
393
+ });
394
+ conceptPaths.push(...lifecycle.conceptPaths);
395
+ }
396
+ return { activation, conceptPaths: [...new Set(conceptPaths)] };
397
+ }
398
+
399
+ function toPreview(
400
+ change: OkfKnowledgeChangeCandidateV1,
401
+ decision: OkfKnowledgeChangeReviewDecision,
402
+ ): OkfKnowledgeChangeDecisionPreview {
403
+ return {
404
+ changeId: change.id,
405
+ operation: change.operation,
406
+ currentState: change.state,
407
+ decision,
408
+ baseRevision: change.base?.revision ?? null,
409
+ candidateRevision: change.candidate.revision,
410
+ runtimeFields: change.diff.runtimeFields,
411
+ willMutateActiveKnowledge: decision === "accept",
412
+ };
413
+ }
414
+
415
+ function normalizeDecisionReason(value: string | null | undefined): string | null {
416
+ if (value === undefined || value === null || value.trim() === "") return null;
417
+ const reason = [...value]
418
+ .filter((character) => {
419
+ const code = character.charCodeAt(0);
420
+ return code === 9 || code === 10 || code === 13 || (code >= 32 && code !== 127);
421
+ })
422
+ .join("")
423
+ .trim();
424
+ if (reason.length > 500)
425
+ throw new Error("Knowledge review reason must be 500 characters or less.");
426
+ if (detectSessionMemorySensitivity(reason).classification === "credential") {
427
+ throw new Error("Knowledge review reason contains a credential.");
428
+ }
429
+ return reason;
430
+ }
431
+
432
+ function resolveNow(value: string | Date | undefined): string {
433
+ const date =
434
+ value instanceof Date ? value : typeof value === "string" ? new Date(value) : new Date();
435
+ if (!Number.isFinite(date.getTime())) throw new Error("Invalid knowledge review timestamp.");
436
+ return date.toISOString();
437
+ }
438
+
439
+ function isBaseRevisionConflict(error: unknown): boolean {
440
+ return (
441
+ typeof error === "object" &&
442
+ error !== null &&
443
+ "code" in error &&
444
+ error.code === "KNOWLEDGE_BASE_REVISION_CONFLICT"
445
+ );
446
+ }
@@ -0,0 +1,135 @@
1
+ export type OkfKnowledgeSupportKind =
2
+ | "user-declaration"
3
+ | "repo-policy"
4
+ | "state-observation"
5
+ | "verified-outcome"
6
+ | "semantic-inference";
7
+
8
+ export interface OkfKnowledgeSupportRef {
9
+ id: string;
10
+ kind: OkfKnowledgeSupportKind;
11
+ sourceRefId: string;
12
+ subjectFingerprint: string | null;
13
+ observedAt: string;
14
+ }
15
+
16
+ export type OkfKnowledgeDelivery = "inline" | "reference" | "blocked";
17
+ export type OkfKnowledgeSourceStatus = "current" | "changed" | "unknown";
18
+
19
+ export interface OkfKnowledgeRuntimeEligibility {
20
+ delivery: OkfKnowledgeDelivery;
21
+ freshness: "verified" | "review-due" | "unknown" | "stale";
22
+ reason: string;
23
+ }
24
+
25
+ export function resolveOkfKnowledgeRuntimeEligibility(input: {
26
+ reviewState: string;
27
+ lifecycle: {
28
+ status: string;
29
+ reviewAfter: string;
30
+ staleAfter: string;
31
+ };
32
+ supportRef: OkfKnowledgeSupportRef | null;
33
+ now?: string | Date;
34
+ sourceStatus?: OkfKnowledgeSourceStatus;
35
+ verifiedContradiction?: boolean;
36
+ }): OkfKnowledgeRuntimeEligibility {
37
+ const now = normalizeNow(input.now);
38
+ if (
39
+ input.verifiedContradiction === true ||
40
+ input.lifecycle.status === "stale" ||
41
+ input.lifecycle.status === "deprecated" ||
42
+ input.lifecycle.status === "revoked" ||
43
+ input.lifecycle.status === "superseded" ||
44
+ input.reviewState === "stale" ||
45
+ input.reviewState === "deprecated" ||
46
+ input.reviewState === "revoked" ||
47
+ input.reviewState === "superseded" ||
48
+ isDue(input.lifecycle.staleAfter, now)
49
+ ) {
50
+ return {
51
+ delivery: "blocked",
52
+ freshness: "stale",
53
+ reason:
54
+ input.verifiedContradiction === true
55
+ ? "Verified evidence contradicts the active claim."
56
+ : "Lifecycle state excludes this knowledge from runtime context.",
57
+ };
58
+ }
59
+ if (input.reviewState !== "accepted" && input.reviewState !== "auto-accepted") {
60
+ return {
61
+ delivery: "blocked",
62
+ freshness: "unknown",
63
+ reason: "Knowledge has not passed an active-compatible review state.",
64
+ };
65
+ }
66
+ if (input.sourceStatus === "changed") {
67
+ return {
68
+ delivery: "blocked",
69
+ freshness: "review-due",
70
+ reason: "The supporting source changed and requires revalidation.",
71
+ };
72
+ }
73
+ if (isDue(input.lifecycle.reviewAfter, now)) {
74
+ return {
75
+ delivery: "reference",
76
+ freshness: "review-due",
77
+ reason: `Review was due at ${input.lifecycle.reviewAfter}.`,
78
+ };
79
+ }
80
+ if (input.supportRef === null) {
81
+ return {
82
+ delivery: "reference",
83
+ freshness: "unknown",
84
+ reason: "Legacy knowledge has no structured support reference.",
85
+ };
86
+ }
87
+ if (input.supportRef.kind === "semantic-inference") {
88
+ if (input.reviewState === "accepted") {
89
+ return {
90
+ delivery: "inline",
91
+ freshness: "verified",
92
+ reason: "A human-accepted inference is runtime-eligible as an explicit decision.",
93
+ };
94
+ }
95
+ return {
96
+ delivery: "reference",
97
+ freshness: "unknown",
98
+ reason: "Model-inferred knowledge remains on-demand until supported by a direct source.",
99
+ };
100
+ }
101
+ if (
102
+ (input.supportRef.kind === "repo-policy" || input.supportRef.kind === "state-observation") &&
103
+ input.sourceStatus !== "current"
104
+ ) {
105
+ return {
106
+ delivery: "reference",
107
+ freshness: "unknown",
108
+ reason: "The supporting repository source has not been revalidated for this query.",
109
+ };
110
+ }
111
+ return {
112
+ delivery: "inline",
113
+ freshness: "verified",
114
+ reason: `Runtime-eligible through ${input.supportRef.kind}.`,
115
+ };
116
+ }
117
+
118
+ export function isDirectKnowledgeSupport(
119
+ supportRef: OkfKnowledgeSupportRef | null | undefined,
120
+ ): boolean {
121
+ return (
122
+ supportRef !== undefined && supportRef !== null && supportRef.kind !== "semantic-inference"
123
+ );
124
+ }
125
+
126
+ function normalizeNow(value: string | Date | undefined): Date {
127
+ if (value instanceof Date && Number.isFinite(value.getTime())) return value;
128
+ if (typeof value === "string" && Number.isFinite(Date.parse(value))) return new Date(value);
129
+ return new Date();
130
+ }
131
+
132
+ function isDue(value: string, now: Date): boolean {
133
+ const timestamp = Date.parse(value);
134
+ return Number.isFinite(timestamp) && timestamp <= now.getTime();
135
+ }
@@ -0,0 +1,44 @@
1
+ import { join } from "node:path";
2
+ import { resolveEvoDevPaths } from "../config/paths.ts";
3
+ import type { EvolutionResolvedPaths } from "./schema.ts";
4
+ import { assertPathDescendant, sanitizeStorageId } from "./shared.ts";
5
+
6
+ export function resolveEvolutionPaths(input: {
7
+ homeDir: string;
8
+ projectKey: string;
9
+ runId: string;
10
+ }): EvolutionResolvedPaths {
11
+ const paths = resolveEvoDevPaths(input.homeDir);
12
+ const projectKey = sanitizeStorageId("projectKey", input.projectKey);
13
+ const runId = sanitizeStorageId("runId", input.runId);
14
+ const evolutionStateDir = join(paths.stateDir, "evolution");
15
+ const projectStateDir = join(evolutionStateDir, projectKey);
16
+ const runStateDir = join(evolutionStateDir, projectKey, runId);
17
+ const repoProposalsDir = join(runStateDir, "proposals");
18
+ const reviewCandidatesDir = join(runStateDir, "review-candidates");
19
+ const knowledgeProjectDir = join(paths.knowledgeDir, projectKey);
20
+ const evosCasesProjectDir = join(paths.evosCasesDir, projectKey);
21
+ assertPathDescendant(evolutionStateDir, runStateDir, "runStateDir");
22
+ assertPathDescendant(evolutionStateDir, projectStateDir, "projectStateDir");
23
+ assertPathDescendant(evolutionStateDir, reviewCandidatesDir, "reviewCandidatesDir");
24
+ assertPathDescendant(paths.knowledgeDir, knowledgeProjectDir, "knowledgeProjectDir");
25
+ assertPathDescendant(paths.evosCasesDir, evosCasesProjectDir, "evosCasesProjectDir");
26
+ return {
27
+ projectKey,
28
+ runId,
29
+ evolutionStateDir,
30
+ runStateDir,
31
+ evidenceWindowPath: join(runStateDir, "evidence-window.json"),
32
+ batchPath: join(runStateDir, "distillation-batch.json"),
33
+ triggersDir: join(runStateDir, "triggers"),
34
+ reviewCandidatesDir,
35
+ repoProposalsDir,
36
+ repoProposalsIndexPath: join(runStateDir, "proposals.index.json"),
37
+ knowledgeProjectDir,
38
+ knowledgeRecordsDir: join(knowledgeProjectDir, "records"),
39
+ knowledgeIndexPath: join(knowledgeProjectDir, "index.json"),
40
+ knowledgeReviewHistoryPath: join(projectStateDir, "knowledge-review-history.jsonl"),
41
+ evosCasesProjectDir,
42
+ evosIndexPath: paths.evosIndexPath,
43
+ };
44
+ }