@evo-dev/core 0.0.1-alpha.14 → 0.0.1-alpha.16

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.
@@ -3,6 +3,7 @@ import { existsSync, readFileSync, readdirSync } from "node:fs";
3
3
  import { mkdir, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
4
4
  import { dirname, isAbsolute, join, relative, resolve } from "node:path";
5
5
  import { resolveEvoDevPaths } from "../../config/paths.ts";
6
+ import { readRuntimeInjectionSettings } from "../../config/settings.ts";
6
7
  import {
7
8
  canonicalizeProjectKey,
8
9
  listEquivalentProjectKeys,
@@ -21,6 +22,24 @@ import type {
21
22
  EvolutionKnowledgeRecord,
22
23
  } from "../schema.ts";
23
24
  import { REVIEW_STATES } from "../shared.ts";
25
+ import {
26
+ type OkfKnowledgeChangeCandidateV1,
27
+ createOkfKnowledgeChangeCandidate,
28
+ readOkfKnowledgeChangeCandidate,
29
+ writeOkfKnowledgeChangeCandidate,
30
+ } from "./change-store.ts";
31
+ import {
32
+ type OkfKnowledgeChangeClassificationResult,
33
+ classifyOkfKnowledgeCandidate,
34
+ createOkfKnowledgeRevision,
35
+ createOkfKnowledgeRuntimeProjectionFromConcept,
36
+ } from "./changes.ts";
37
+ import {
38
+ type OkfKnowledgeFreshness,
39
+ type OkfKnowledgeVerificationSnapshotV1,
40
+ createOkfKnowledgeVerificationSnapshot,
41
+ deriveOkfKnowledgeFreshness,
42
+ } from "./freshness.ts";
24
43
 
25
44
  export type OkfKnowledgeDecision =
26
45
  | "auto-accept"
@@ -28,7 +47,8 @@ export type OkfKnowledgeDecision =
28
47
  | "create"
29
48
  | "update"
30
49
  | "skip"
31
- | "needs-human";
50
+ | "needs-human"
51
+ | "revoke";
32
52
  export type OkfKnowledgeTargetStore = "okf" | "repo-asset-proposal" | "evo-eval-set" | "none";
33
53
  export type OkfKnowledgeReviewState =
34
54
  | "auto-accepted"
@@ -221,6 +241,7 @@ export interface OkfKnowledgeActivationResult {
221
241
  overlayPaths: string[];
222
242
  skippedCandidates: string[];
223
243
  needsHumanCandidates: string[];
244
+ pendingChangeIds: string[];
224
245
  indexPaths: string[];
225
246
  derivedIndexPaths: string[];
226
247
  logPaths: string[];
@@ -258,6 +279,7 @@ export interface OkfKnowledgeConcept {
258
279
  reviewState: OkfKnowledgeReviewState;
259
280
  lifecycle: OkfKnowledgeLifecycle;
260
281
  lifecyclePersisted: boolean;
282
+ verificationSnapshot: OkfKnowledgeVerificationSnapshotV1 | null;
261
283
  title: string;
262
284
  description: string;
263
285
  tags: string[];
@@ -294,6 +316,8 @@ export interface OkfKnowledgeContextItem {
294
316
  score?: number;
295
317
  title: string;
296
318
  summary: string;
319
+ freshness: OkfKnowledgeFreshness;
320
+ runtimeExcerpt: string | null;
297
321
  matchReasons: string[];
298
322
  }
299
323
 
@@ -337,6 +361,20 @@ export interface ScopedKnowledgeContextPackItem {
337
361
  section: OkfKnowledgeContextSection;
338
362
  rank: number;
339
363
  title: string;
364
+ freshness: OkfKnowledgeFreshness;
365
+ loader:
366
+ | {
367
+ kind: "knowledge-show";
368
+ conceptId: string;
369
+ }
370
+ | {
371
+ kind: "none";
372
+ conceptId: null;
373
+ };
374
+ delivery: {
375
+ mode: "inline" | "reference";
376
+ excerpt: string | null;
377
+ };
340
378
  matchReasons: string[];
341
379
  }
342
380
 
@@ -478,6 +516,7 @@ const OKF_DECISIONS: readonly OkfKnowledgeDecision[] = [
478
516
  "update",
479
517
  "skip",
480
518
  "needs-human",
519
+ "revoke",
481
520
  ];
482
521
  const ACTIVE_OKF_DECISIONS: readonly OkfKnowledgeDecision[] = ["auto-accept", "create", "update"];
483
522
  const BEHAVIOR_CHANGE_KINDS = new Set([
@@ -642,7 +681,7 @@ export function finalizeCuratedKnowledgePlan(input: {
642
681
  };
643
682
  const candidates = input.plan.candidates.map((candidate) => {
644
683
  if (candidate.decision === "no_write" || candidate.decision === "skip") return candidate;
645
- if (candidate.decision === "needs-human") return candidate;
684
+ if (candidate.decision === "needs-human" || candidate.decision === "revoke") return candidate;
646
685
  return decideOkfKnowledgeCandidate(
647
686
  {
648
687
  ...candidate,
@@ -773,7 +812,7 @@ export function parseKnowledgeDistillationOutput(
773
812
 
774
813
  export function validateOkfKnowledgePlanContract(
775
814
  plan: OkfKnowledgePlan,
776
- _options: { homeDir?: string } = {},
815
+ options: { homeDir?: string; allowReviewedHighRisk?: boolean } = {},
777
816
  ): OkfKnowledgePlanValidationResult {
778
817
  const findings: OkfKnowledgePlanValidationFinding[] = [];
779
818
  const add = (
@@ -825,7 +864,9 @@ export function validateOkfKnowledgePlanContract(
825
864
  add("candidates", "plan.candidates", "Plan candidates must be an array.");
826
865
  } else {
827
866
  plan.candidates.forEach((candidate, index) =>
828
- validateCandidateContract(candidate, `candidates[${index}]`, evalSetIds, add),
867
+ validateCandidateContract(candidate, `candidates[${index}]`, evalSetIds, add, {
868
+ allowReviewedHighRisk: options.allowReviewedHighRisk === true,
869
+ }),
829
870
  );
830
871
  }
831
872
 
@@ -877,7 +918,7 @@ export function validateOkfKnowledgePlanContract(
877
918
 
878
919
  export function assertOkfKnowledgePlanContract(
879
920
  plan: OkfKnowledgePlan,
880
- options: { homeDir?: string } = {},
921
+ options: { homeDir?: string; allowReviewedHighRisk?: boolean } = {},
881
922
  ): void {
882
923
  const result = validateOkfKnowledgePlanContract(plan, options);
883
924
  if (!result.ok) {
@@ -948,6 +989,7 @@ function validateCandidateContract(
948
989
  path: string,
949
990
  evalSetIds: Set<string>,
950
991
  add: (path: string, code: string, message: string, severity?: "error" | "warning") => void,
992
+ options: { allowReviewedHighRisk: boolean },
951
993
  ): void {
952
994
  if (!isRecord(candidate)) {
953
995
  add(path, "candidate.object", "Candidate must be an object.");
@@ -1035,7 +1077,7 @@ function validateCandidateContract(
1035
1077
  "Active writes require metadata-only evidence.",
1036
1078
  );
1037
1079
  }
1038
- if (hasHighRiskHumanReviewSignal(candidate)) {
1080
+ if (hasHighRiskHumanReviewSignal(candidate) && !options.allowReviewedHighRisk) {
1039
1081
  add(
1040
1082
  path,
1041
1083
  "candidate.active.highRiskHumanReview",
@@ -1094,6 +1136,47 @@ function validateCandidateContract(
1094
1136
  );
1095
1137
  }
1096
1138
  }
1139
+ if (decision === "revoke") {
1140
+ if (candidate.targetStore !== "okf") {
1141
+ add(
1142
+ `${path}.targetStore`,
1143
+ "candidate.revoke.targetStore",
1144
+ "Knowledge revocation must target OKF.",
1145
+ );
1146
+ }
1147
+ if (candidate.reviewState !== "needs-human") {
1148
+ add(
1149
+ `${path}.reviewState`,
1150
+ "candidate.revoke.reviewState",
1151
+ "Knowledge revocation must enter human review.",
1152
+ );
1153
+ }
1154
+ if (candidate.metadataOnlyEvidence !== true) {
1155
+ add(
1156
+ `${path}.metadataOnlyEvidence`,
1157
+ "candidate.revoke.metadataOnlyEvidence",
1158
+ "Knowledge revocation requires metadata-only evidence.",
1159
+ );
1160
+ }
1161
+ if (!Array.isArray(candidate.evidenceRefs) || candidate.evidenceRefs.length === 0) {
1162
+ add(
1163
+ `${path}.evidenceRefs`,
1164
+ "candidate.revoke.evidenceRefs",
1165
+ "Knowledge revocation requires evidenceRefs.",
1166
+ );
1167
+ }
1168
+ const verification =
1169
+ isRecord(candidate.bodySections) && Array.isArray(candidate.bodySections.verification)
1170
+ ? candidate.bodySections.verification
1171
+ : [];
1172
+ if (verification.length === 0 && !isNonEmptyString(candidate.verificationNotApplicableReason)) {
1173
+ add(
1174
+ `${path}.bodySections.verification`,
1175
+ "candidate.revoke.verification",
1176
+ "Knowledge revocation requires verification or an explicit not-applicable reason.",
1177
+ );
1178
+ }
1179
+ }
1097
1180
  if (noOp && (!isNonEmptyString(candidate.id) || !isNonEmptyString(candidate.decisionReason))) {
1098
1181
  add(path, "candidate.noop.metadata", "No-op candidates require safe skip metadata.");
1099
1182
  }
@@ -1638,9 +1721,8 @@ export function findCandidateConflictOrDuplicate(
1638
1721
  const targetPath = candidate.targetPath.replace(/^\/+/u, "");
1639
1722
  const duplicate = index.concepts.find(
1640
1723
  (concept) =>
1641
- concept.stableKey === candidate.stableKey ||
1642
- concept.targetPath === targetPath ||
1643
- concept.sourceLink === `/${targetPath}`,
1724
+ concept.stableKey !== candidate.stableKey &&
1725
+ (concept.targetPath === targetPath || concept.sourceLink === `/${targetPath}`),
1644
1726
  );
1645
1727
  if (duplicate !== undefined) {
1646
1728
  return {
@@ -1655,6 +1737,7 @@ export function findCandidateConflictOrDuplicate(
1655
1737
  );
1656
1738
  const semanticDuplicate = index.concepts.find(
1657
1739
  (concept) =>
1740
+ concept.stableKey !== candidate.stableKey &&
1658
1741
  candidateFingerprint !== "" &&
1659
1742
  candidateFingerprint === concept.semanticFingerprint &&
1660
1743
  scopesOverlap(candidate.repoTags, concept.repoTags),
@@ -1808,12 +1891,22 @@ export function decideOkfKnowledgeCandidate(
1808
1891
  }
1809
1892
 
1810
1893
  if (highRiskRequiresHuman) {
1894
+ const reviewEligible =
1895
+ !originalUnsafe &&
1896
+ hasReusableSemanticShape(sanitized) &&
1897
+ localActiveTarget &&
1898
+ sanitized.metadataOnlyEvidence &&
1899
+ hasVerification &&
1900
+ hasScopeTags &&
1901
+ scores.privacyRisk <= 2;
1811
1902
  return {
1812
1903
  ...sanitized,
1813
- decision: "no_write",
1904
+ decision: reviewEligible ? "needs-human" : "no_write",
1814
1905
  scores,
1815
- reviewState: "auto-stored/unreviewed",
1816
- decisionReason: "No write: high-risk knowledge is not eligible for automatic activation.",
1906
+ reviewState: reviewEligible ? "needs-human" : "auto-stored/unreviewed",
1907
+ decisionReason: reviewEligible
1908
+ ? "Needs human review: the candidate changes a sensitive engineering domain."
1909
+ : "No write: high-risk knowledge did not pass the privacy, verification, or scope gates.",
1817
1910
  };
1818
1911
  }
1819
1912
 
@@ -1849,19 +1942,103 @@ export function decideOkfKnowledgeCandidate(
1849
1942
  };
1850
1943
  }
1851
1944
 
1945
+ async function readAuthorizedReviewedKnowledgeChange(input: {
1946
+ homeDir: string;
1947
+ plan: OkfKnowledgePlan;
1948
+ changeId: string;
1949
+ }): Promise<OkfKnowledgeChangeCandidateV1> {
1950
+ const { change } = await readOkfKnowledgeChangeCandidate({
1951
+ homeDir: input.homeDir,
1952
+ changeId: input.changeId,
1953
+ });
1954
+ if (change.state !== "accepted" || change.decision?.state !== "accepted") {
1955
+ throw new Error("Reviewed knowledge change must be accepted before activation.");
1956
+ }
1957
+ if (change.operation === "revoke") {
1958
+ throw new Error("Reviewed knowledge revocation must use the lifecycle decision path.");
1959
+ }
1960
+ if (
1961
+ input.plan.projectKey !== change.projectKey ||
1962
+ input.plan.runId !== change.runId ||
1963
+ input.plan.evidenceWindowId !== change.provenance.evidenceWindowId
1964
+ ) {
1965
+ throw new Error("Reviewed knowledge change identity does not match the activation plan.");
1966
+ }
1967
+ if (input.plan.candidates.length !== 1) {
1968
+ throw new Error("Reviewed knowledge activation must contain exactly one candidate.");
1969
+ }
1970
+ const candidate = input.plan.candidates[0] as OkfKnowledgePlanCandidate;
1971
+ const expectedCandidate: OkfKnowledgePlanCandidate = {
1972
+ ...change.candidate.planCandidate,
1973
+ decision: change.operation === "update" ? "update" : "create",
1974
+ reviewState: "accepted",
1975
+ targetPath:
1976
+ change.operation === "update" && change.base !== null
1977
+ ? change.base.sourceLink.replace(/^\/+/u, "")
1978
+ : change.targetPath,
1979
+ };
1980
+ if (stableJsonStringify(candidate) !== stableJsonStringify(expectedCandidate)) {
1981
+ throw new Error("Reviewed knowledge candidate does not match the accepted change.");
1982
+ }
1983
+ if (
1984
+ stableJsonStringify(input.plan.evoEvalSets) !== stableJsonStringify(change.candidate.evalSets)
1985
+ ) {
1986
+ throw new Error("Reviewed knowledge eval sets do not match the accepted change.");
1987
+ }
1988
+ const expectedEvidenceRefs = change.provenance.evidenceRefs.map((id) => ({
1989
+ id,
1990
+ kind: "knowledge-change-evidence",
1991
+ source: `knowledge-change:${change.id}`,
1992
+ rawContentStored: false,
1993
+ externalContentCopied: false,
1994
+ }));
1995
+ if (
1996
+ stableJsonStringify(input.plan.evidenceRefs) !== stableJsonStringify(expectedEvidenceRefs) ||
1997
+ stableJsonStringify(input.plan.privacyCheck) !==
1998
+ stableJsonStringify(expectedCandidate.privacyCheck)
1999
+ ) {
2000
+ throw new Error("Reviewed knowledge provenance does not match the accepted change.");
2001
+ }
2002
+ return change;
2003
+ }
2004
+
2005
+ function assertReviewedKnowledgeClassification(
2006
+ change: OkfKnowledgeChangeCandidateV1,
2007
+ classification: OkfKnowledgeChangeClassificationResult,
2008
+ ): void {
2009
+ if (
2010
+ classification.classification !== change.operation ||
2011
+ classification.baseRevision !== (change.base?.revision ?? null) ||
2012
+ classification.candidateRevision !== change.candidate.revision
2013
+ ) {
2014
+ throw new Error("Reviewed knowledge classification changed before activation.");
2015
+ }
2016
+ }
2017
+
1852
2018
  export async function activateOkfKnowledgePlan(input: {
1853
2019
  homeDir: string;
1854
2020
  plan: OkfKnowledgePlan;
1855
2021
  overwrite?: boolean;
1856
2022
  evidenceWindowPath?: string | null;
2023
+ reviewedChangeId?: string;
1857
2024
  }): Promise<OkfKnowledgeActivationResult> {
1858
- const validation = validateOkfKnowledgePlanContract(input.plan);
1859
2025
  const projectKey =
1860
2026
  isRecord(input.plan) && isNonEmptyString(input.plan.projectKey)
1861
2027
  ? input.plan.projectKey
1862
2028
  : "unknown";
1863
2029
  const runId =
1864
2030
  isRecord(input.plan) && isNonEmptyString(input.plan.runId) ? input.plan.runId : "unknown";
2031
+ const reviewedChange =
2032
+ input.reviewedChangeId === undefined
2033
+ ? null
2034
+ : await readAuthorizedReviewedKnowledgeChange({
2035
+ homeDir: input.homeDir,
2036
+ plan: input.plan,
2037
+ changeId: input.reviewedChangeId,
2038
+ });
2039
+ const validation = validateOkfKnowledgePlanContract(input.plan, {
2040
+ allowReviewedHighRisk: reviewedChange !== null,
2041
+ });
1865
2042
  if (!validation.ok) {
1866
2043
  await writeFailedOkfKnowledgePlanArtifact({
1867
2044
  homeDir: input.homeDir,
@@ -1874,7 +2051,7 @@ export async function activateOkfKnowledgePlan(input: {
1874
2051
  });
1875
2052
  throw new Error("OKF knowledge plan contract validation failed.");
1876
2053
  }
1877
- validateOkfKnowledgePlan(input.plan);
2054
+ validateOkfKnowledgePlan(input.plan, { allowReviewedHighRisk: reviewedChange !== null });
1878
2055
  const paths = resolveOkfKnowledgePaths(input.homeDir);
1879
2056
  const tmpRunDir = join(paths.tmpDir, `${input.plan.projectKey}-${input.plan.runId}`);
1880
2057
  const planPath = join(tmpRunDir, "knowledge-plan.json");
@@ -1891,24 +2068,146 @@ export async function activateOkfKnowledgePlan(input: {
1891
2068
  const overlayPaths: string[] = [];
1892
2069
  const skippedCandidates: string[] = [];
1893
2070
  const needsHumanCandidates: string[] = [];
2071
+ const pendingChangeIds: string[] = [];
1894
2072
  const affectedDirectories = new Set<string>([paths.okfDir]);
1895
2073
 
1896
2074
  try {
1897
- const targetStates = await preflightOkfConceptTargets(paths.okfDir, input.plan.candidates);
2075
+ const settings = await readRuntimeInjectionSettings(input.homeDir);
2076
+ const existingConcepts = await listOkfKnowledgeConcepts({ homeDir: input.homeDir });
2077
+ const existingByStableKey = new Map(
2078
+ existingConcepts
2079
+ .filter(
2080
+ (concept) =>
2081
+ isActiveOkfReviewState(concept.reviewState) && concept.lifecycle.status === "active",
2082
+ )
2083
+ .map((concept) => [concept.stableKey, concept]),
2084
+ );
2085
+ const writeCandidates: OkfKnowledgePlanCandidate[] = [];
2086
+ const existingByCandidateId = new Map<string, OkfKnowledgeConcept>();
1898
2087
  for (const candidate of input.plan.candidates) {
1899
2088
  if (candidate.decision === "no_write" || candidate.decision === "skip") {
1900
2089
  skippedCandidates.push(candidate.id);
1901
2090
  continue;
1902
2091
  }
1903
- if (candidate.decision === "needs-human") {
1904
- needsHumanCandidates.push(candidate.id);
2092
+ if (candidate.targetStore !== "okf") {
2093
+ skippedCandidates.push(candidate.id);
1905
2094
  continue;
1906
2095
  }
1907
- if (candidate.targetStore !== "okf") {
2096
+ const existing = existingByStableKey.get(candidate.stableKey) ?? null;
2097
+ const classification = classifyOkfKnowledgeCandidate({
2098
+ candidate,
2099
+ existingConcept: existing,
2100
+ });
2101
+ if (reviewedChange !== null) {
2102
+ assertReviewedKnowledgeClassification(reviewedChange, classification);
2103
+ }
2104
+ const needsReview =
2105
+ reviewedChange === null &&
2106
+ (candidate.decision === "needs-human" ||
2107
+ classification.classification === "revoke" ||
2108
+ classification.classification === "supersede" ||
2109
+ (classification.classification === "update" && settings.reviewKnowledgeUpdates));
2110
+ if (needsReview) {
2111
+ const operation =
2112
+ classification.classification === "supersede" ||
2113
+ classification.classification === "revoke"
2114
+ ? classification.classification
2115
+ : existing === null
2116
+ ? "create"
2117
+ : "update";
2118
+ const beforeFreshness =
2119
+ existing === null ? null : deriveOkfKnowledgeFreshness({ concept: existing }).freshness;
2120
+ const verificationSnapshot = createOkfKnowledgeVerificationSnapshot({
2121
+ candidate,
2122
+ verifiedAt: input.plan.createdAt,
2123
+ projectKey: input.plan.projectKey,
2124
+ });
2125
+ const change = createOkfKnowledgeChangeCandidate({
2126
+ projectKey: input.plan.projectKey,
2127
+ runId: input.plan.runId,
2128
+ operation,
2129
+ stableKey: candidate.stableKey,
2130
+ targetPath: candidate.targetPath,
2131
+ base:
2132
+ existing === null || classification.baseProjection === null
2133
+ ? null
2134
+ : {
2135
+ conceptId: existing.id,
2136
+ sourceLink: existing.sourceLink,
2137
+ revision:
2138
+ classification.baseRevision ??
2139
+ createOkfKnowledgeRevision(
2140
+ createOkfKnowledgeRuntimeProjectionFromConcept(existing),
2141
+ ),
2142
+ runtimeProjection: classification.baseProjection,
2143
+ },
2144
+ candidateRevision: classification.candidateRevision,
2145
+ planCandidate: candidate,
2146
+ evalSets: input.plan.evoEvalSets.filter((evalSet) =>
2147
+ (candidate.evalSetRefs ?? []).includes(evalSet.id),
2148
+ ),
2149
+ diff: classification.diff,
2150
+ freshness: {
2151
+ before: beforeFreshness,
2152
+ after:
2153
+ classification.classification === "revoke"
2154
+ ? "stale"
2155
+ : verificationSnapshot === null
2156
+ ? "unknown"
2157
+ : "verified",
2158
+ reason:
2159
+ candidate.decision === "needs-human" || candidate.decision === "revoke"
2160
+ ? candidate.decisionReason
2161
+ : classification.reason,
2162
+ },
2163
+ evidenceWindowId: input.plan.evidenceWindowId,
2164
+ evidenceRefs: candidate.evidenceRefs,
2165
+ createdAt: input.plan.createdAt,
2166
+ });
2167
+ await writeOkfKnowledgeChangeCandidate({ homeDir: input.homeDir, change });
2168
+ pendingChangeIds.push(change.id);
2169
+ if (candidate.decision === "needs-human" || candidate.decision === "revoke") {
2170
+ needsHumanCandidates.push(candidate.id);
2171
+ }
2172
+ continue;
2173
+ }
2174
+ if (classification.classification === "no_write") {
1908
2175
  skippedCandidates.push(candidate.id);
1909
2176
  continue;
1910
2177
  }
2178
+ if (classification.classification === "supersede") {
2179
+ writeCandidates.push({
2180
+ ...candidate,
2181
+ decision: "create",
2182
+ reviewState: "accepted",
2183
+ });
2184
+ continue;
2185
+ }
2186
+ if (classification.classification === "revoke") {
2187
+ throw new Error("Knowledge revocation must be applied through its reviewed change.");
2188
+ }
2189
+ if (
2190
+ classification.classification === "update" ||
2191
+ classification.classification === "refresh"
2192
+ ) {
2193
+ if (existing === null) {
2194
+ throw new Error(`Knowledge ${classification.classification} requires an active concept.`);
2195
+ }
2196
+ const effectiveCandidate: OkfKnowledgePlanCandidate = {
2197
+ ...candidate,
2198
+ decision: "update",
2199
+ targetPath: existing.sourceLink.replace(/^\/+/u, ""),
2200
+ reviewState: candidate.reviewState === "auto-accepted" ? "auto-accepted" : "accepted",
2201
+ };
2202
+ writeCandidates.push(effectiveCandidate);
2203
+ existingByCandidateId.set(effectiveCandidate.id, existing);
2204
+ continue;
2205
+ }
2206
+ writeCandidates.push(candidate);
2207
+ }
1911
2208
 
2209
+ const targetStates = await preflightOkfConceptTargets(paths.okfDir, writeCandidates);
2210
+ for (const candidate of writeCandidates) {
1912
2211
  const targetPath = resolveOkfTargetPath(paths.okfDir, candidate.targetPath);
1913
2212
  const targetState = targetStates.get(targetPath);
1914
2213
  if (targetState?.action === "skip-existing") {
@@ -1921,7 +2220,11 @@ export async function activateOkfKnowledgePlan(input: {
1921
2220
  }
1922
2221
 
1923
2222
  await mkdir(dirname(targetPath), { recursive: true });
1924
- await writeFile(targetPath, renderOkfConcept(candidate, input.plan), "utf8");
2223
+ await writeFile(
2224
+ targetPath,
2225
+ renderOkfConcept(candidate, input.plan, existingByCandidateId.get(candidate.id)),
2226
+ "utf8",
2227
+ );
1925
2228
  conceptPaths.push(targetPath);
1926
2229
  affectedDirectories.add(dirname(targetPath));
1927
2230
 
@@ -1962,6 +2265,7 @@ export async function activateOkfKnowledgePlan(input: {
1962
2265
  overlayPaths,
1963
2266
  skippedCandidates,
1964
2267
  needsHumanCandidates,
2268
+ pendingChangeIds,
1965
2269
  indexPaths,
1966
2270
  derivedIndexPaths,
1967
2271
  logPaths,
@@ -2500,17 +2804,26 @@ export async function queryOkfKnowledge(input: {
2500
2804
  return left.concept.id.localeCompare(right.concept.id);
2501
2805
  })
2502
2806
  .slice(0, limit)
2503
- .map<OkfKnowledgeContextItem>((candidate, index) => ({
2504
- id: candidate.concept.id,
2505
- sourceType: "okf",
2506
- sourceLink: candidate.concept.sourceLink,
2507
- section: candidate.section,
2508
- rank: index + 1,
2509
- score: candidate.score,
2510
- title: candidate.concept.title,
2511
- summary: candidate.concept.description || candidate.concept.title,
2512
- matchReasons: candidate.matchReasons.length === 0 ? ["generic"] : candidate.matchReasons,
2513
- }));
2807
+ .map<OkfKnowledgeContextItem>((candidate, index) => {
2808
+ const freshness = deriveOkfKnowledgeFreshness({
2809
+ concept: candidate.concept,
2810
+ now,
2811
+ }).freshness;
2812
+ return {
2813
+ id: candidate.concept.id,
2814
+ sourceType: "okf",
2815
+ sourceLink: candidate.concept.sourceLink,
2816
+ section: candidate.section,
2817
+ rank: index + 1,
2818
+ score: candidate.score,
2819
+ title: candidate.concept.title,
2820
+ summary: candidate.concept.description || candidate.concept.title,
2821
+ freshness,
2822
+ runtimeExcerpt:
2823
+ freshness === "verified" ? createBoundedOkfRuntimeExcerpt(candidate.concept) : null,
2824
+ matchReasons: candidate.matchReasons.length === 0 ? ["generic"] : candidate.matchReasons,
2825
+ };
2826
+ });
2514
2827
 
2515
2828
  return {
2516
2829
  projectKey: scope.projectKey,
@@ -2788,6 +3101,10 @@ export function mergeAcceptedEvosCasesIntoKnowledgeQuery(
2788
3101
  score: structuredScore + lexical.score,
2789
3102
  title: evosCase.title,
2790
3103
  summary: evosCase.expectedFutureBehavior || evosCase.result.summary || evosCase.title,
3104
+ freshness: "verified",
3105
+ runtimeExcerpt: sanitizeOkfText(
3106
+ evosCase.expectedFutureBehavior || evosCase.result.summary || evosCase.title,
3107
+ ).slice(0, 1_200),
2791
3108
  matchReasons:
2792
3109
  lexical.reasons.length === 0
2793
3110
  ? structuredReasons
@@ -2830,6 +3147,15 @@ export async function createScopedKnowledgeContextPack(input: {
2830
3147
  section: item.section,
2831
3148
  rank: item.rank,
2832
3149
  title: sanitizeOkfText(item.title),
3150
+ freshness: item.freshness,
3151
+ loader:
3152
+ item.sourceType === "okf"
3153
+ ? { kind: "knowledge-show", conceptId: item.id }
3154
+ : { kind: "none", conceptId: null },
3155
+ delivery: {
3156
+ mode: item.runtimeExcerpt === null ? "reference" : "inline",
3157
+ excerpt: item.runtimeExcerpt,
3158
+ },
2833
3159
  matchReasons: uniqueStrings(item.matchReasons.map(sanitizeLexicalReason)),
2834
3160
  }));
2835
3161
  const okfIndexRevision = createKnowledgeContextRevision(items);
@@ -2843,7 +3169,9 @@ export async function createScopedKnowledgeContextPack(input: {
2843
3169
  sourceType: item.sourceType,
2844
3170
  sourceLink: item.sourceLink,
2845
3171
  section: item.section,
2846
- rank: item.rank,
3172
+ freshness: item.freshness,
3173
+ loader: item.loader,
3174
+ delivery: item.delivery,
2847
3175
  matchReasons: item.matchReasons,
2848
3176
  })),
2849
3177
  });
@@ -2875,10 +3203,20 @@ export function formatScopedKnowledgePromptBlock(pack: ScopedKnowledgeContextPac
2875
3203
  ...(pack.queryText === undefined ? [] : [`Query: ${sanitizeOkfText(pack.queryText)}`]),
2876
3204
  `Paths: ${pack.scope.paths.length === 0 ? "all" : pack.scope.paths.join(", ")}`,
2877
3205
  "Raw content stored: false",
3206
+ "Freshness is determined by EvoDev lifecycle and verification metadata; do not infer validity from timestamps.",
2878
3207
  "",
2879
3208
  "Applicable items:",
2880
3209
  ...pack.items.flatMap((item) => [
2881
- `- ${item.rank}. ${sanitizeOkfText(item.id)} (${item.section}; ${item.sourceType})`,
3210
+ `- ${item.rank}. ${sanitizeOkfText(item.title)} (${item.section}; ${item.sourceType})`,
3211
+ ` Identity: ${sanitizeOkfText(item.id)}`,
3212
+ ` Freshness: ${item.freshness}`,
3213
+ ` Delivery: ${item.delivery.mode}`,
3214
+ ...(item.delivery.excerpt === null
3215
+ ? [" Guidance: load on demand; do not treat this reference as a standing constraint."]
3216
+ : [` Guidance: ${sanitizeOkfText(item.delivery.excerpt)}`]),
3217
+ ...(item.loader.kind === "knowledge-show"
3218
+ ? [` Load: evodev knowledge show ${sanitizeOkfText(item.loader.conceptId)}`]
3219
+ : []),
2882
3220
  ` Source: ${sanitizeOkfText(item.sourceLink)}`,
2883
3221
  ` Match: ${item.matchReasons.length === 0 ? "generic" : item.matchReasons.join(", ")}`,
2884
3222
  ]),
@@ -3293,7 +3631,7 @@ export function formatOkfKnowledgeQuery(result: OkfKnowledgeQueryResult): string
3293
3631
  ? ["- none"]
3294
3632
  : items.map(
3295
3633
  (item) =>
3296
- `- ${item.summary}\n Source: ${item.sourceLink}\n Match: ${item.matchReasons.join(", ")}\n Rank: ${item.rank}`,
3634
+ `- ${item.summary}\n Freshness: ${item.freshness}\n ${item.sourceType === "okf" ? `Load: evodev knowledge show ${item.id}\n ` : ""}Source: ${item.sourceLink}\n Match: ${item.matchReasons.join(", ")}\n Rank: ${item.rank}`,
3297
3635
  )),
3298
3636
  "",
3299
3637
  ];
@@ -3699,6 +4037,19 @@ function extractSummarySection(body: string): string {
3699
4037
  return body.match(/(?:^|\n)# Summary\s*\n+([\s\S]*?)(?=\n# |$)/u)?.[1]?.trim() ?? "";
3700
4038
  }
3701
4039
 
4040
+ function createBoundedOkfRuntimeExcerpt(concept: OkfKnowledgeConcept): string {
4041
+ const projection = createOkfKnowledgeRuntimeProjectionFromConcept(concept);
4042
+ return sanitizeOkfText(
4043
+ [
4044
+ projection.summary || concept.description,
4045
+ ...projection.guidance.map((item) => `- ${item}`),
4046
+ ...projection.verification.map((item) => `Verification: ${item}`),
4047
+ ]
4048
+ .filter((item) => item.trim() !== "")
4049
+ .join("\n"),
4050
+ ).slice(0, 1_200);
4051
+ }
4052
+
3702
4053
  function scopesOverlap(left: string[], right: string[]): boolean {
3703
4054
  if (left.length === 0 || right.length === 0) return true;
3704
4055
  return left.some((tag) => right.includes(tag));
@@ -3827,8 +4178,41 @@ async function ensureOverlayConcept(input: {
3827
4178
  );
3828
4179
  }
3829
4180
 
3830
- function renderOkfConcept(candidate: OkfKnowledgePlanCandidate, plan: OkfKnowledgePlan): string {
4181
+ function renderOkfConcept(
4182
+ candidate: OkfKnowledgePlanCandidate,
4183
+ plan: OkfKnowledgePlan,
4184
+ existing?: OkfKnowledgeConcept,
4185
+ ): string {
3831
4186
  const scores = resolveCandidateScoresForWrite(candidate);
4187
+ const reviewState = resolveCandidateReviewStateForWrite(candidate);
4188
+ const nextLifecycle = createDefaultOkfLifecycle({
4189
+ type: candidate.okfType,
4190
+ path: candidate.targetPath,
4191
+ tags: [
4192
+ "evodev",
4193
+ candidate.kind,
4194
+ ...candidate.roleTags.map((tag) => `role:${tag}`),
4195
+ ...candidate.repoTags.map((tag) => `repo:${tag}`),
4196
+ ...candidate.workflowTags.map((tag) => `workflow:${tag}`),
4197
+ ],
4198
+ title: candidate.title,
4199
+ reviewState,
4200
+ createdAt: plan.createdAt,
4201
+ });
4202
+ const lifecycle =
4203
+ existing === undefined
4204
+ ? nextLifecycle
4205
+ : {
4206
+ ...nextLifecycle,
4207
+ createdAt: existing.lifecycle.createdAt,
4208
+ supersedes: existing.lifecycle.supersedes,
4209
+ supersededBy: existing.lifecycle.supersededBy,
4210
+ };
4211
+ const verificationSnapshot = createOkfKnowledgeVerificationSnapshot({
4212
+ candidate,
4213
+ verifiedAt: plan.createdAt,
4214
+ projectKey: plan.projectKey,
4215
+ });
3832
4216
  return [
3833
4217
  "---",
3834
4218
  `type: ${yamlString(candidate.okfType)}`,
@@ -3847,24 +4231,9 @@ function renderOkfConcept(candidate: OkfKnowledgePlanCandidate, plan: OkfKnowled
3847
4231
  "evodev:",
3848
4232
  ` schema: ${yamlString("knowledge/v1")}`,
3849
4233
  ` stableKey: ${yamlString(candidate.stableKey)}`,
3850
- ` reviewState: ${yamlString(resolveCandidateReviewStateForWrite(candidate))}`,
3851
- renderLifecycleYaml(
3852
- " ",
3853
- createDefaultOkfLifecycle({
3854
- type: candidate.okfType,
3855
- path: candidate.targetPath,
3856
- tags: [
3857
- "evodev",
3858
- candidate.kind,
3859
- ...candidate.roleTags.map((tag) => `role:${tag}`),
3860
- ...candidate.repoTags.map((tag) => `repo:${tag}`),
3861
- ...candidate.workflowTags.map((tag) => `workflow:${tag}`),
3862
- ],
3863
- title: candidate.title,
3864
- reviewState: resolveCandidateReviewStateForWrite(candidate),
3865
- createdAt: plan.createdAt,
3866
- }),
3867
- ),
4234
+ ` reviewState: ${yamlString(reviewState)}`,
4235
+ renderLifecycleYaml(" ", lifecycle),
4236
+ renderVerificationSnapshotYaml(" ", verificationSnapshot),
3868
4237
  " source:",
3869
4238
  ` kind: ${yamlString("trace-distillation")}`,
3870
4239
  ` projectKey: ${yamlString(plan.projectKey)}`,
@@ -3895,17 +4264,27 @@ function renderOkfConcept(candidate: OkfKnowledgePlanCandidate, plan: OkfKnowled
3895
4264
  "",
3896
4265
  candidate.bodySections.summary,
3897
4266
  "",
4267
+ "# Claim",
4268
+ "",
4269
+ candidate.claim,
4270
+ "",
3898
4271
  "# Applies When",
3899
4272
  "",
3900
4273
  renderMarkdownList(candidate.bodySections.appliesWhen),
3901
4274
  "",
4275
+ "# How to Apply",
4276
+ "",
4277
+ candidate.howToApply,
4278
+ "",
3902
4279
  "# Guidance",
3903
4280
  "",
3904
4281
  renderMarkdownList(candidate.bodySections.guidance),
3905
4282
  "",
3906
4283
  "# Anti-Criteria",
3907
4284
  "",
3908
- renderMarkdownList(candidate.bodySections.antiCriteria),
4285
+ renderMarkdownList(
4286
+ uniqueStrings([...candidate.antiCriteria, ...candidate.bodySections.antiCriteria]),
4287
+ ),
3909
4288
  "",
3910
4289
  "# Verification",
3911
4290
  "",
@@ -4131,6 +4510,7 @@ function parseOkfConceptFile(
4131
4510
  reviewState,
4132
4511
  lifecycle: lifecycleParsed.lifecycle,
4133
4512
  lifecyclePersisted: lifecycleParsed.persisted,
4513
+ verificationSnapshot: parseOkfVerificationSnapshot(frontmatter),
4134
4514
  title,
4135
4515
  description,
4136
4516
  tags,
@@ -4142,6 +4522,22 @@ function parseOkfConceptFile(
4142
4522
  };
4143
4523
  }
4144
4524
 
4525
+ function parseOkfVerificationSnapshot(
4526
+ frontmatter: string,
4527
+ ): OkfKnowledgeVerificationSnapshotV1 | null {
4528
+ const block = extractNestedYamlBlock(frontmatter, "evodev", "verificationSnapshot");
4529
+ if (block === null) return null;
4530
+ const schemaVersion = readIndentedYamlScalar(block, "schemaVersion");
4531
+ const verifiedAt = normalizeIsoDateString(readIndentedYamlScalar(block, "verifiedAt"));
4532
+ if (schemaVersion !== "1" || verifiedAt === null) return null;
4533
+ return {
4534
+ schemaVersion: 1,
4535
+ verifiedAt,
4536
+ evidenceRefs: readYamlList(block, "evidenceRefs"),
4537
+ repository: null,
4538
+ };
4539
+ }
4540
+
4145
4541
  function matchesConceptFilters(
4146
4542
  concept: OkfKnowledgeConcept,
4147
4543
  input: { projectKey?: string; roleId?: string; workflowId?: string; paths?: string[] },
@@ -4241,6 +4637,9 @@ function createKnowledgeContextRevision(items: ScopedKnowledgeContextPackItem[])
4241
4637
  sourceLink: item.sourceLink,
4242
4638
  section: item.section,
4243
4639
  title: item.title,
4640
+ freshness: item.freshness,
4641
+ loader: item.loader,
4642
+ delivery: item.delivery,
4244
4643
  matchReasons: item.matchReasons,
4245
4644
  })),
4246
4645
  ),
@@ -4668,8 +5067,11 @@ function readIndentedYamlBoolean(frontmatter: string, key: string): boolean | nu
4668
5067
  return null;
4669
5068
  }
4670
5069
 
4671
- function validateOkfKnowledgePlan(plan: OkfKnowledgePlan): void {
4672
- assertOkfKnowledgePlanContract(plan);
5070
+ function validateOkfKnowledgePlan(
5071
+ plan: OkfKnowledgePlan,
5072
+ options: { allowReviewedHighRisk?: boolean } = {},
5073
+ ): void {
5074
+ assertOkfKnowledgePlanContract(plan, options);
4673
5075
  }
4674
5076
 
4675
5077
  function validateFailedPlanArtifact(artifact: OkfKnowledgeFailedPlanArtifact): void {
@@ -4887,6 +5289,20 @@ function renderLifecycleYaml(indent: string, lifecycle: OkfKnowledgeLifecycle):
4887
5289
  ].join("\n");
4888
5290
  }
4889
5291
 
5292
+ function renderVerificationSnapshotYaml(
5293
+ indent: string,
5294
+ snapshot: OkfKnowledgeVerificationSnapshotV1 | null,
5295
+ ): string {
5296
+ if (snapshot === null) return `${indent}verificationSnapshot: null`;
5297
+ return [
5298
+ `${indent}verificationSnapshot:`,
5299
+ `${indent} schemaVersion: 1`,
5300
+ `${indent} verifiedAt: ${yamlString(snapshot.verifiedAt)}`,
5301
+ renderYamlList(`${indent} evidenceRefs`, snapshot.evidenceRefs),
5302
+ `${indent} repository: null`,
5303
+ ].join("\n");
5304
+ }
5305
+
4890
5306
  function lifecycleStatusFromReviewState(
4891
5307
  reviewState: OkfKnowledgeReviewState,
4892
5308
  ): OkfKnowledgeLifecycleStatus {