@evo-dev/core 0.0.1-alpha.19 → 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.
- package/dist/config/index.js +59 -8
- package/dist/index.js +803 -230
- package/package.json +1 -1
- package/src/evolution/evidence/session-memory/retention.ts +359 -78
- package/src/evolution/evidence/session-memory/segment.ts +15 -1
- package/src/evolution/evidence/session-memory/storage.ts +37 -6
- package/src/evolution/evidence/session-memory/types.ts +5 -2
- package/src/evolution/evidence/session-memory/updater.ts +23 -15
- package/src/evolution/knowledge/changes.ts +6 -0
- package/src/evolution/knowledge/freshness.ts +17 -57
- package/src/evolution/knowledge/index.ts +204 -12
- package/src/evolution/knowledge/support.ts +135 -0
- package/src/hooks/index.ts +124 -20
|
@@ -40,6 +40,16 @@ import {
|
|
|
40
40
|
createOkfKnowledgeVerificationSnapshot,
|
|
41
41
|
deriveOkfKnowledgeFreshness,
|
|
42
42
|
} from "./freshness.ts";
|
|
43
|
+
import { type OkfKnowledgeSupportRef, isDirectKnowledgeSupport } from "./support.ts";
|
|
44
|
+
|
|
45
|
+
export type {
|
|
46
|
+
OkfKnowledgeDelivery,
|
|
47
|
+
OkfKnowledgeRuntimeEligibility,
|
|
48
|
+
OkfKnowledgeSourceStatus,
|
|
49
|
+
OkfKnowledgeSupportKind,
|
|
50
|
+
OkfKnowledgeSupportRef,
|
|
51
|
+
} from "./support.ts";
|
|
52
|
+
export { resolveOkfKnowledgeRuntimeEligibility } from "./support.ts";
|
|
43
53
|
|
|
44
54
|
export type OkfKnowledgeDecision =
|
|
45
55
|
| "auto-accept"
|
|
@@ -160,6 +170,7 @@ export interface OkfKnowledgePlanCandidate {
|
|
|
160
170
|
scores: OkfKnowledgeCandidateScores;
|
|
161
171
|
decisionReason: string;
|
|
162
172
|
evidenceRefs: string[];
|
|
173
|
+
supportRef?: OkfKnowledgeSupportRef;
|
|
163
174
|
reviewState: OkfKnowledgeReviewState;
|
|
164
175
|
verificationNotApplicableReason?: string;
|
|
165
176
|
evalSetRefs?: string[];
|
|
@@ -280,6 +291,7 @@ export interface OkfKnowledgeConcept {
|
|
|
280
291
|
lifecycle: OkfKnowledgeLifecycle;
|
|
281
292
|
lifecyclePersisted: boolean;
|
|
282
293
|
verificationSnapshot: OkfKnowledgeVerificationSnapshotV1 | null;
|
|
294
|
+
supportRef: OkfKnowledgeSupportRef | null;
|
|
283
295
|
title: string;
|
|
284
296
|
description: string;
|
|
285
297
|
tags: string[];
|
|
@@ -391,13 +403,13 @@ export interface ScopedKnowledgeContextPack {
|
|
|
391
403
|
id: string;
|
|
392
404
|
okfIndexRevision: string;
|
|
393
405
|
scope: ScopedKnowledgeContextPackScope;
|
|
394
|
-
|
|
406
|
+
queryHash?: string;
|
|
395
407
|
items: ScopedKnowledgeContextPackItem[];
|
|
396
408
|
warnings: string[];
|
|
397
409
|
rawContentStored: false;
|
|
398
410
|
}
|
|
399
411
|
|
|
400
|
-
export type ContextInjectionTrigger = "team-startup" | "hook-safe-point";
|
|
412
|
+
export type ContextInjectionTrigger = "team-startup" | "hook-safe-point" | "ordinary-session";
|
|
401
413
|
|
|
402
414
|
export interface ContextInjectionReceipt {
|
|
403
415
|
version: 1;
|
|
@@ -405,9 +417,17 @@ export interface ContextInjectionReceipt {
|
|
|
405
417
|
okfIndexRevision: string;
|
|
406
418
|
scope: ScopedKnowledgeContextPackScope;
|
|
407
419
|
itemIds: string[];
|
|
420
|
+
queryHash: string | null;
|
|
408
421
|
injectedAt: string;
|
|
409
422
|
hookEventId: string | null;
|
|
410
423
|
trigger: ContextInjectionTrigger;
|
|
424
|
+
outcome: null | {
|
|
425
|
+
status: "verified";
|
|
426
|
+
eventId: string;
|
|
427
|
+
observedAt: string;
|
|
428
|
+
summaryHash: string;
|
|
429
|
+
rawContentStored: false;
|
|
430
|
+
};
|
|
411
431
|
rawContentStored: false;
|
|
412
432
|
}
|
|
413
433
|
|
|
@@ -963,6 +983,10 @@ function normalizePlanCandidate(value: unknown, path: string): OkfKnowledgePlanC
|
|
|
963
983
|
readRequiredString(input.decisionReason, `${path}.decisionReason`),
|
|
964
984
|
),
|
|
965
985
|
evidenceRefs: readStringArray(input.evidenceRefs, `${path}.evidenceRefs`).map(sanitizeOkfText),
|
|
986
|
+
supportRef:
|
|
987
|
+
input.supportRef === undefined
|
|
988
|
+
? undefined
|
|
989
|
+
: normalizeKnowledgeSupportRef(input.supportRef, `${path}.supportRef`),
|
|
966
990
|
reviewState: parseOkfReviewState(readRequiredString(input.reviewState, `${path}.reviewState`)),
|
|
967
991
|
verificationNotApplicableReason:
|
|
968
992
|
typeof input.verificationNotApplicableReason === "string"
|
|
@@ -1047,6 +1071,7 @@ function validateCandidateContract(
|
|
|
1047
1071
|
validateStringArrayValue(candidate.pathScopes, `${path}.pathScopes`, add);
|
|
1048
1072
|
validateStringArrayValue(candidate.relatedConceptLinks, `${path}.relatedConceptLinks`, add);
|
|
1049
1073
|
validateStringArrayValue(candidate.evidenceRefs, `${path}.evidenceRefs`, add);
|
|
1074
|
+
validateKnowledgeSupportRefValue(candidate.supportRef, `${path}.supportRef`, add);
|
|
1050
1075
|
validateOverlayUpdatesValue(candidate.overlayUpdates, `${path}.overlayUpdates`, add);
|
|
1051
1076
|
validateScoresValue(candidate.scores, `${path}.scores`, add);
|
|
1052
1077
|
validatePrivacyCheckValue(candidate.privacyCheck, `${path}.privacyCheck`, add);
|
|
@@ -1381,6 +1406,33 @@ function normalizeBasis(value: string): "direct" | "inferred" {
|
|
|
1381
1406
|
return value;
|
|
1382
1407
|
}
|
|
1383
1408
|
|
|
1409
|
+
function normalizeKnowledgeSupportRef(value: unknown, path: string): OkfKnowledgeSupportRef {
|
|
1410
|
+
const input = assertRecordValue(value, path) as Record<string, unknown>;
|
|
1411
|
+
const kind = readRequiredString(input.kind, `${path}.kind`);
|
|
1412
|
+
if (
|
|
1413
|
+
kind !== "user-declaration" &&
|
|
1414
|
+
kind !== "repo-policy" &&
|
|
1415
|
+
kind !== "state-observation" &&
|
|
1416
|
+
kind !== "verified-outcome" &&
|
|
1417
|
+
kind !== "semantic-inference"
|
|
1418
|
+
) {
|
|
1419
|
+
throw new Error(`${path}.kind is invalid.`);
|
|
1420
|
+
}
|
|
1421
|
+
const observedAt = readRequiredString(input.observedAt, `${path}.observedAt`);
|
|
1422
|
+
if (!Number.isFinite(Date.parse(observedAt))) throw new Error(`${path}.observedAt is invalid.`);
|
|
1423
|
+
const subjectFingerprint =
|
|
1424
|
+
input.subjectFingerprint === null
|
|
1425
|
+
? null
|
|
1426
|
+
: sanitizeOkfText(readRequiredString(input.subjectFingerprint, `${path}.subjectFingerprint`));
|
|
1427
|
+
return {
|
|
1428
|
+
id: sanitizeOkfText(readRequiredString(input.id, `${path}.id`)),
|
|
1429
|
+
kind,
|
|
1430
|
+
sourceRefId: sanitizeOkfText(readRequiredString(input.sourceRefId, `${path}.sourceRefId`)),
|
|
1431
|
+
subjectFingerprint,
|
|
1432
|
+
observedAt: new Date(observedAt).toISOString(),
|
|
1433
|
+
};
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1384
1436
|
function normalizeEvalSetDecision(value: string): OkfKnowledgeEvalSet["decision"] {
|
|
1385
1437
|
const normalized = value === "no-write" ? "no_write" : value;
|
|
1386
1438
|
if (
|
|
@@ -1550,6 +1602,45 @@ function validateStringArrayValue(
|
|
|
1550
1602
|
});
|
|
1551
1603
|
}
|
|
1552
1604
|
|
|
1605
|
+
function validateKnowledgeSupportRefValue(
|
|
1606
|
+
value: unknown,
|
|
1607
|
+
path: string,
|
|
1608
|
+
add: (path: string, code: string, message: string, severity?: "error" | "warning") => void,
|
|
1609
|
+
): void {
|
|
1610
|
+
if (value === undefined) return;
|
|
1611
|
+
if (!isRecord(value)) {
|
|
1612
|
+
add(path, "supportRef.object", "Candidate supportRef must be an object.");
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
if (!isNonEmptyString(value.id)) add(`${path}.id`, "supportRef.id", "supportRef id is required.");
|
|
1616
|
+
if (
|
|
1617
|
+
value.kind !== "user-declaration" &&
|
|
1618
|
+
value.kind !== "repo-policy" &&
|
|
1619
|
+
value.kind !== "state-observation" &&
|
|
1620
|
+
value.kind !== "verified-outcome" &&
|
|
1621
|
+
value.kind !== "semantic-inference"
|
|
1622
|
+
) {
|
|
1623
|
+
add(`${path}.kind`, "supportRef.kind", "supportRef kind is invalid.");
|
|
1624
|
+
}
|
|
1625
|
+
if (!isNonEmptyString(value.sourceRefId)) {
|
|
1626
|
+
add(`${path}.sourceRefId`, "supportRef.sourceRefId", "supportRef sourceRefId is required.");
|
|
1627
|
+
}
|
|
1628
|
+
if (value.subjectFingerprint !== null && !isNonEmptyString(value.subjectFingerprint)) {
|
|
1629
|
+
add(
|
|
1630
|
+
`${path}.subjectFingerprint`,
|
|
1631
|
+
"supportRef.subjectFingerprint",
|
|
1632
|
+
"supportRef subjectFingerprint must be null or a non-empty string.",
|
|
1633
|
+
);
|
|
1634
|
+
}
|
|
1635
|
+
if (!isNonEmptyString(value.observedAt) || !Number.isFinite(Date.parse(value.observedAt))) {
|
|
1636
|
+
add(
|
|
1637
|
+
`${path}.observedAt`,
|
|
1638
|
+
"supportRef.observedAt",
|
|
1639
|
+
"supportRef observedAt must be an ISO timestamp.",
|
|
1640
|
+
);
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1553
1644
|
function validateOverlayUpdatesValue(
|
|
1554
1645
|
value: unknown,
|
|
1555
1646
|
path: string,
|
|
@@ -1858,6 +1949,7 @@ export function decideOkfKnowledgeCandidate(
|
|
|
1858
1949
|
localActiveTarget &&
|
|
1859
1950
|
sanitized.targetStore === "okf" &&
|
|
1860
1951
|
sanitized.basis === "direct" &&
|
|
1952
|
+
isDirectKnowledgeSupport(sanitized.supportRef) &&
|
|
1861
1953
|
sanitized.metadataOnlyEvidence &&
|
|
1862
1954
|
hasVerification &&
|
|
1863
1955
|
hasScopeTags &&
|
|
@@ -3101,10 +3193,8 @@ export function mergeAcceptedEvosCasesIntoKnowledgeQuery(
|
|
|
3101
3193
|
score: structuredScore + lexical.score,
|
|
3102
3194
|
title: evosCase.title,
|
|
3103
3195
|
summary: evosCase.expectedFutureBehavior || evosCase.result.summary || evosCase.title,
|
|
3104
|
-
freshness: "
|
|
3105
|
-
runtimeExcerpt:
|
|
3106
|
-
evosCase.expectedFutureBehavior || evosCase.result.summary || evosCase.title,
|
|
3107
|
-
).slice(0, 1_200),
|
|
3196
|
+
freshness: "unknown",
|
|
3197
|
+
runtimeExcerpt: null,
|
|
3108
3198
|
matchReasons:
|
|
3109
3199
|
lexical.reasons.length === 0
|
|
3110
3200
|
? structuredReasons
|
|
@@ -3131,16 +3221,24 @@ export async function createScopedKnowledgeContextPack(input: {
|
|
|
3131
3221
|
paths?: string[];
|
|
3132
3222
|
queryText?: string;
|
|
3133
3223
|
limit?: number;
|
|
3224
|
+
inlineOnly?: boolean;
|
|
3134
3225
|
}): Promise<ScopedKnowledgeContextPack | null> {
|
|
3135
|
-
const result = await queryScopedOkfKnowledgeContext(
|
|
3136
|
-
|
|
3226
|
+
const result = await queryScopedOkfKnowledgeContext({
|
|
3227
|
+
...input,
|
|
3228
|
+
limit: input.inlineOnly === true ? Math.max(input.limit ?? 3, 50) : input.limit,
|
|
3229
|
+
});
|
|
3230
|
+
const selectedItems =
|
|
3231
|
+
input.inlineOnly === true
|
|
3232
|
+
? result.items.filter((item) => item.runtimeExcerpt !== null).slice(0, input.limit ?? 3)
|
|
3233
|
+
: result.items;
|
|
3234
|
+
if (selectedItems.length === 0) return null;
|
|
3137
3235
|
const scope: ScopedKnowledgeContextPackScope = {
|
|
3138
3236
|
...(result.projectKey === undefined ? {} : { projectKey: result.projectKey }),
|
|
3139
3237
|
...(result.roleId === undefined ? {} : { roleId: result.roleId }),
|
|
3140
3238
|
...(result.workflowId === undefined ? {} : { workflowId: result.workflowId }),
|
|
3141
3239
|
paths: result.paths,
|
|
3142
3240
|
};
|
|
3143
|
-
const items =
|
|
3241
|
+
const items = selectedItems.map<ScopedKnowledgeContextPackItem>((item) => ({
|
|
3144
3242
|
id: item.id,
|
|
3145
3243
|
sourceType: item.sourceType,
|
|
3146
3244
|
sourceLink: item.sourceLink,
|
|
@@ -3160,10 +3258,11 @@ export async function createScopedKnowledgeContextPack(input: {
|
|
|
3160
3258
|
}));
|
|
3161
3259
|
const okfIndexRevision = createKnowledgeContextRevision(items);
|
|
3162
3260
|
const queryText = normalizeKnowledgeQueryText(result.queryText);
|
|
3261
|
+
const queryHash = queryText === undefined ? undefined : sha256Short(queryText);
|
|
3163
3262
|
const packSeed = stableJsonStringify({
|
|
3164
3263
|
okfIndexRevision,
|
|
3165
3264
|
scope,
|
|
3166
|
-
|
|
3265
|
+
queryHash: queryHash ?? null,
|
|
3167
3266
|
items: items.map((item) => ({
|
|
3168
3267
|
id: item.id,
|
|
3169
3268
|
sourceType: item.sourceType,
|
|
@@ -3181,7 +3280,7 @@ export async function createScopedKnowledgeContextPack(input: {
|
|
|
3181
3280
|
id: `ctxpack-${sha256Short(packSeed)}`,
|
|
3182
3281
|
okfIndexRevision,
|
|
3183
3282
|
scope,
|
|
3184
|
-
...(
|
|
3283
|
+
...(queryHash === undefined ? {} : { queryHash }),
|
|
3185
3284
|
items,
|
|
3186
3285
|
warnings:
|
|
3187
3286
|
result.warnings.length === 0
|
|
@@ -3200,7 +3299,6 @@ export function formatScopedKnowledgePromptBlock(pack: ScopedKnowledgeContextPac
|
|
|
3200
3299
|
`Project: ${pack.scope.projectKey ?? "all"}`,
|
|
3201
3300
|
`Role: ${pack.scope.roleId ?? "any"}`,
|
|
3202
3301
|
`Workflow: ${pack.scope.workflowId ?? "any"}`,
|
|
3203
|
-
...(pack.queryText === undefined ? [] : [`Query: ${sanitizeOkfText(pack.queryText)}`]),
|
|
3204
3302
|
`Paths: ${pack.scope.paths.length === 0 ? "all" : pack.scope.paths.join(", ")}`,
|
|
3205
3303
|
"Raw content stored: false",
|
|
3206
3304
|
"Freshness is determined by EvoDev lifecycle and verification metadata; do not infer validity from timestamps.",
|
|
@@ -3266,9 +3364,11 @@ export async function writeContextInjectionReceipt(input: {
|
|
|
3266
3364
|
okfIndexRevision: input.pack.okfIndexRevision,
|
|
3267
3365
|
scope: input.pack.scope,
|
|
3268
3366
|
itemIds: input.pack.items.map((item) => item.id),
|
|
3367
|
+
queryHash: input.pack.queryHash ?? null,
|
|
3269
3368
|
injectedAt: input.injectedAt ?? new Date().toISOString(),
|
|
3270
3369
|
hookEventId: input.hookEventId ?? null,
|
|
3271
3370
|
trigger: input.trigger,
|
|
3371
|
+
outcome: null,
|
|
3272
3372
|
rawContentStored: false,
|
|
3273
3373
|
};
|
|
3274
3374
|
const path = resolveContextInjectionReceiptPath({
|
|
@@ -3280,6 +3380,57 @@ export async function writeContextInjectionReceipt(input: {
|
|
|
3280
3380
|
return { path, receipt };
|
|
3281
3381
|
}
|
|
3282
3382
|
|
|
3383
|
+
export async function recordContextInjectionOutcome(input: {
|
|
3384
|
+
homeDir: string;
|
|
3385
|
+
sessionKey: string;
|
|
3386
|
+
eventId: string;
|
|
3387
|
+
observedAt: string;
|
|
3388
|
+
summary: string;
|
|
3389
|
+
}): Promise<string[]> {
|
|
3390
|
+
const stateDir = resolveEvoDevPaths(input.homeDir).stateDir;
|
|
3391
|
+
const directory = join(
|
|
3392
|
+
stateDir,
|
|
3393
|
+
"context-injections",
|
|
3394
|
+
sanitizeReceiptPathSegment(input.sessionKey),
|
|
3395
|
+
);
|
|
3396
|
+
let names: string[];
|
|
3397
|
+
try {
|
|
3398
|
+
names = await readdir(directory);
|
|
3399
|
+
} catch {
|
|
3400
|
+
return [];
|
|
3401
|
+
}
|
|
3402
|
+
const paths: string[] = [];
|
|
3403
|
+
for (const name of names.filter((item) => item.endsWith(".json")).sort()) {
|
|
3404
|
+
const path = join(directory, name);
|
|
3405
|
+
let receipt: ContextInjectionReceipt;
|
|
3406
|
+
try {
|
|
3407
|
+
receipt = JSON.parse(await readFile(path, "utf8")) as ContextInjectionReceipt;
|
|
3408
|
+
} catch {
|
|
3409
|
+
continue;
|
|
3410
|
+
}
|
|
3411
|
+
if (
|
|
3412
|
+
receipt.version !== 1 ||
|
|
3413
|
+
receipt.rawContentStored !== false ||
|
|
3414
|
+
(receipt.outcome !== null && receipt.outcome !== undefined)
|
|
3415
|
+
) {
|
|
3416
|
+
continue;
|
|
3417
|
+
}
|
|
3418
|
+
const updated: ContextInjectionReceipt = {
|
|
3419
|
+
...receipt,
|
|
3420
|
+
outcome: {
|
|
3421
|
+
status: "verified",
|
|
3422
|
+
eventId: sanitizeReceiptPathSegment(input.eventId),
|
|
3423
|
+
observedAt: normalizeTimestamp(input.observedAt),
|
|
3424
|
+
summaryHash: sha256Short(input.summary),
|
|
3425
|
+
rawContentStored: false,
|
|
3426
|
+
},
|
|
3427
|
+
};
|
|
3428
|
+
await writeJson(path, updated, { overwrite: true });
|
|
3429
|
+
paths.push(path);
|
|
3430
|
+
}
|
|
3431
|
+
return paths;
|
|
3432
|
+
}
|
|
3433
|
+
|
|
3283
3434
|
export async function markOkfKnowledgeConceptStale(input: {
|
|
3284
3435
|
homeDir: string;
|
|
3285
3436
|
conceptId: string;
|
|
@@ -3947,6 +4098,9 @@ function explainAutomaticNoWriteDecision(
|
|
|
3947
4098
|
const reasons: string[] = [];
|
|
3948
4099
|
if (candidate.targetStore !== "okf") reasons.push("target is not user-local OKF");
|
|
3949
4100
|
if (candidate.basis !== "direct") reasons.push("basis is inferred");
|
|
4101
|
+
if (!isDirectKnowledgeSupport(candidate.supportRef)) {
|
|
4102
|
+
reasons.push("structured direct support is missing");
|
|
4103
|
+
}
|
|
3950
4104
|
if (!candidate.metadataOnlyEvidence) reasons.push("evidence is not metadata-only");
|
|
3951
4105
|
if (!hasVerification) reasons.push("verification evidence is missing");
|
|
3952
4106
|
if (!hasScopeTags) reasons.push("repo or role tags are missing");
|
|
@@ -4233,6 +4387,7 @@ function renderOkfConcept(
|
|
|
4233
4387
|
` stableKey: ${yamlString(candidate.stableKey)}`,
|
|
4234
4388
|
` reviewState: ${yamlString(reviewState)}`,
|
|
4235
4389
|
renderLifecycleYaml(" ", lifecycle),
|
|
4390
|
+
renderKnowledgeSupportRefYaml(" ", candidate.supportRef ?? null),
|
|
4236
4391
|
renderVerificationSnapshotYaml(" ", verificationSnapshot),
|
|
4237
4392
|
" source:",
|
|
4238
4393
|
` kind: ${yamlString("trace-distillation")}`,
|
|
@@ -4511,6 +4666,7 @@ function parseOkfConceptFile(
|
|
|
4511
4666
|
lifecycle: lifecycleParsed.lifecycle,
|
|
4512
4667
|
lifecyclePersisted: lifecycleParsed.persisted,
|
|
4513
4668
|
verificationSnapshot: parseOkfVerificationSnapshot(frontmatter),
|
|
4669
|
+
supportRef: parseOkfKnowledgeSupportRef(frontmatter),
|
|
4514
4670
|
title,
|
|
4515
4671
|
description,
|
|
4516
4672
|
tags,
|
|
@@ -4522,6 +4678,27 @@ function parseOkfConceptFile(
|
|
|
4522
4678
|
};
|
|
4523
4679
|
}
|
|
4524
4680
|
|
|
4681
|
+
function parseOkfKnowledgeSupportRef(frontmatter: string): OkfKnowledgeSupportRef | null {
|
|
4682
|
+
const block = extractNestedYamlBlock(frontmatter, "evodev", "supportRef");
|
|
4683
|
+
if (block === null) return null;
|
|
4684
|
+
try {
|
|
4685
|
+
return normalizeKnowledgeSupportRef(
|
|
4686
|
+
{
|
|
4687
|
+
id: readIndentedYamlScalar(block, "id"),
|
|
4688
|
+
kind: readIndentedYamlScalar(block, "kind"),
|
|
4689
|
+
sourceRefId: readIndentedYamlScalar(block, "sourceRefId"),
|
|
4690
|
+
subjectFingerprint: readNullableLifecycleString(
|
|
4691
|
+
readIndentedYamlScalar(block, "subjectFingerprint"),
|
|
4692
|
+
),
|
|
4693
|
+
observedAt: readIndentedYamlScalar(block, "observedAt"),
|
|
4694
|
+
},
|
|
4695
|
+
"evodev.supportRef",
|
|
4696
|
+
);
|
|
4697
|
+
} catch {
|
|
4698
|
+
return null;
|
|
4699
|
+
}
|
|
4700
|
+
}
|
|
4701
|
+
|
|
4525
4702
|
function parseOkfVerificationSnapshot(
|
|
4526
4703
|
frontmatter: string,
|
|
4527
4704
|
): OkfKnowledgeVerificationSnapshotV1 | null {
|
|
@@ -5303,6 +5480,21 @@ function renderVerificationSnapshotYaml(
|
|
|
5303
5480
|
].join("\n");
|
|
5304
5481
|
}
|
|
5305
5482
|
|
|
5483
|
+
function renderKnowledgeSupportRefYaml(
|
|
5484
|
+
indent: string,
|
|
5485
|
+
supportRef: OkfKnowledgeSupportRef | null,
|
|
5486
|
+
): string {
|
|
5487
|
+
if (supportRef === null) return `${indent}supportRef: null`;
|
|
5488
|
+
return [
|
|
5489
|
+
`${indent}supportRef:`,
|
|
5490
|
+
`${indent} id: ${yamlString(supportRef.id)}`,
|
|
5491
|
+
`${indent} kind: ${yamlString(supportRef.kind)}`,
|
|
5492
|
+
`${indent} sourceRefId: ${yamlString(supportRef.sourceRefId)}`,
|
|
5493
|
+
`${indent} subjectFingerprint: ${yamlNullableString(supportRef.subjectFingerprint)}`,
|
|
5494
|
+
`${indent} observedAt: ${yamlString(supportRef.observedAt)}`,
|
|
5495
|
+
].join("\n");
|
|
5496
|
+
}
|
|
5497
|
+
|
|
5306
5498
|
function lifecycleStatusFromReviewState(
|
|
5307
5499
|
reviewState: OkfKnowledgeReviewState,
|
|
5308
5500
|
): OkfKnowledgeLifecycleStatus {
|
|
@@ -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
|
+
}
|