create-agdf 0.14.2 → 0.14.4
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/README.md +39 -3
- package/generated/.opencode/contracts/control-scaffold.md +17 -2
- package/generated/.opencode/contracts/interaction.md +2 -2
- package/generated/.opencode/skills/agdf-delivery-path-search/SKILL.md +16 -4
- package/generated/.opencode/skills/agdf-gate-check/SKILL.md +6 -4
- package/generated/plugins/agdf/.claude-plugin/plugin.json +1 -1
- package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
- package/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
- package/generated/plugins/agdf/meta/contracts/control-scaffold.md +17 -2
- package/generated/plugins/agdf/meta/contracts/interaction.md +2 -2
- package/generated/plugins/agdf/meta/copilot-payload-baseline.json +3 -3
- package/generated/plugins/agdf/meta/distribution-profile-history.json +125 -0
- package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
- package/generated/plugins/agdf/runtime/create-agdf/lib/cli/command-registry.js +8 -1
- package/generated/plugins/agdf/runtime/create-agdf/lib/cli/delivery-path-search-command.js +51 -19
- package/generated/plugins/agdf/runtime/create-agdf/lib/cli/parse-args.js +3 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/doctor.js +13 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/gate-check.js +64 -13
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/run-state.js +6 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-state/run-identity.js +9 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-state/run-state-parser.js +4 -2
- package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/contracts.js +37 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/persistence.js +14 -2
- package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/search-engine.js +126 -36
- package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/state-adapter.js +40 -92
- package/generated/plugins/agdf/runtime/create-agdf/lib/interaction-presentation.js +57 -7
- package/generated/plugins/agdf/runtime/create-agdf/lib/runtime/plugin-provenance.js +46 -3
- package/generated/plugins/agdf/runtime/create-agdf/package.json +1 -1
- package/generated/plugins/agdf/runtime/runtime-manifest.json +2 -2
- package/generated/plugins/agdf/scripts/check-runtime-integrity.mjs +2 -0
- package/generated/plugins/agdf/skills/delivery-path-search/SKILL.md +16 -4
- package/generated/plugins/agdf/skills/gate-check/SKILL.md +6 -4
- package/generated/plugins/agdf/submission/openai/availability.md +1 -1
- package/generated/plugins/agdf/submission/openai/capability-matrix.json +1 -1
- package/generated/plugins/agdf/submission/openai/release-notes.md +7 -1
- package/generated/plugins/agdf/submission/openai/reviewer-cases.json +1 -1
- package/generated/plugins/copilot/agdf/.agdf-payload-inventory.json +71 -51
- package/generated/plugins/copilot/agdf/copilot-skills/agdf-delivery-path-search/SKILL.md +16 -4
- package/generated/plugins/copilot/agdf/copilot-skills/agdf-gate-check/SKILL.md +6 -4
- package/generated/plugins/copilot/agdf/copilot-skills/contracts/control-scaffold.md +17 -2
- package/generated/plugins/copilot/agdf/copilot-skills/contracts/interaction.md +2 -2
- package/generated/plugins/copilot/agdf/meta/agdf-plugin.definition.json +1 -1
- package/generated/plugins/copilot/agdf/meta/distribution-profile-history.json +125 -0
- package/generated/plugins/copilot/agdf/plugin.json +1 -1
- package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/command-registry.js +8 -1
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/delivery-path-search-command.js +51 -19
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/parse-args.js +3 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/doctor.js +13 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/gate-check.js +64 -13
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/run-state.js +6 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/run-identity.js +9 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/run-state-parser.js +4 -2
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/contracts.js +37 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/persistence.js +14 -2
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/search-engine.js +126 -36
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/state-adapter.js +40 -92
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/interaction-presentation.js +57 -7
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/runtime/plugin-provenance.js +46 -3
- package/generated/plugins/copilot/agdf/runtime/create-agdf/package.json +1 -1
- package/generated/plugins/copilot/agdf/runtime/runtime-manifest.json +2 -2
- package/generated/submissions/openai/agdf/.codex-plugin/plugin.json +1 -1
- package/generated/submissions/openai/agdf/meta/agdf-plugin.definition.json +1 -1
- package/generated/submissions/openai/agdf/meta/contracts/control-scaffold.md +17 -2
- package/generated/submissions/openai/agdf/meta/contracts/interaction.md +2 -2
- package/generated/submissions/openai/agdf/meta/copilot-payload-baseline.json +3 -3
- package/generated/submissions/openai/agdf/meta/distribution-profile-history.json +125 -0
- package/generated/submissions/openai/agdf/skills/delivery-path-search/SKILL.md +16 -4
- package/generated/submissions/openai/agdf/skills/gate-check/SKILL.md +6 -4
- package/generated/submissions/openai/agdf/submission/openai/availability.md +1 -1
- package/generated/submissions/openai/agdf/submission/openai/capability-matrix.json +1 -1
- package/generated/submissions/openai/agdf/submission/openai/inventory.json +18 -14
- package/generated/submissions/openai/agdf/submission/openai/listing.json +1 -1
- package/generated/submissions/openai/agdf/submission/openai/readiness.json +2 -2
- package/generated/submissions/openai/agdf/submission/openai/readiness.md +2 -2
- package/generated/submissions/openai/agdf/submission/openai/release-notes.md +7 -1
- package/generated/submissions/openai/agdf/submission/openai/reviewer-cases.json +1 -1
- package/lib/cli/application.js +24 -6
- package/lib/cli/command-registry.js +8 -1
- package/lib/cli/delivery-path-search-command.js +51 -19
- package/lib/cli/parse-args.js +3 -0
- package/lib/control-evaluation/doctor.js +13 -0
- package/lib/control-evaluation/gate-check.js +64 -13
- package/lib/control-evaluation/run-state.js +6 -0
- package/lib/control-state/run-identity.js +9 -0
- package/lib/control-state/run-state-parser.js +4 -2
- package/lib/delivery-path-search/contracts.js +37 -0
- package/lib/delivery-path-search/persistence.js +14 -2
- package/lib/delivery-path-search/search-engine.js +126 -36
- package/lib/delivery-path-search/state-adapter.js +40 -92
- package/lib/installers/claude-cache-recovery.js +76 -0
- package/lib/installers/copilot-settings.js +146 -16
- package/lib/installers/local-marketplace.js +169 -14
- package/lib/installers/plugin-installers.js +96 -9
- package/lib/interaction-presentation.js +57 -7
- package/lib/lifecycle/operations.js +28 -4
- package/lib/lifecycle/result.js +4 -4
- package/lib/release/profile-history.js +222 -0
- package/lib/release/version-bump.js +527 -0
- package/lib/release/version-coherence.js +92 -38
- package/lib/runtime/distribution-profile-history.js +173 -0
- package/lib/runtime/plugin-provenance.js +46 -3
- package/package.json +7 -3
|
@@ -130,6 +130,19 @@ export function evaluateDoctor(targetDir, selection = {}) {
|
|
|
130
130
|
"Add at least one evidence row or explicitly document that no evidence exists yet.",
|
|
131
131
|
);
|
|
132
132
|
}
|
|
133
|
+
|
|
134
|
+
for (const identityFinding of selectedRunState.identity_findings ?? []) {
|
|
135
|
+
addFinding(
|
|
136
|
+
findings,
|
|
137
|
+
"revise",
|
|
138
|
+
identityFinding.code,
|
|
139
|
+
identityFinding.code === "AGDF_RUN_ID_INVALID"
|
|
140
|
+
? "The selected run state has a missing or invalid run_id; interaction cards cannot be rendered for it."
|
|
141
|
+
: "The selected run state has a missing or invalid revision_id; approval interactions cannot be rendered for it.",
|
|
142
|
+
runPath,
|
|
143
|
+
"Run run-migrate to repair the run identity, or migrate the legacy state to a canonical run record.",
|
|
144
|
+
);
|
|
145
|
+
}
|
|
133
146
|
}
|
|
134
147
|
|
|
135
148
|
const backlogPath = join(".agdf", "control", "MASTER_BACKLOG.md");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { attachApprovalOrientationSnapshot, buildArtefactRefs, buildQualityReadiness, gateTitle, localePack, renderApprovalOrientationSnapshot, renderOperationalStatusCard, resolveHumanRunTitle } from '../interaction-presentation.js';
|
|
1
|
+
import { attachApprovalOrientationSnapshot, buildArtefactRefs, buildQualityReadiness, gateTitle, localePack, renderApprovalOrientationSnapshot, renderOperationalStatusCard, resolveHumanRunTitle, validateApprovalOrientationPreconditions, validateApprovalOrientationSnapshot, validateOperationalStatusCardPreconditions } from '../interaction-presentation.js';
|
|
2
2
|
import { interactionLocales, resolveConfiguredChatLanguage } from '../cli/runtime-context.js';
|
|
3
3
|
import { evaluateDoctor } from './doctor.js';
|
|
4
4
|
import { analyzeDeliveryMap, deriveQualityOutlook } from './delivery-map.js';
|
|
@@ -300,8 +300,9 @@ export function evaluateGateCheck(targetDir, selection = {}) {
|
|
|
300
300
|
humanPresentation,
|
|
301
301
|
revisionId,
|
|
302
302
|
});
|
|
303
|
+
const readyForApproval = isReadyUserGateApproval({ status, currentGate, missingApproval });
|
|
303
304
|
const approvalOrientation = attachApprovalOrientationSnapshot(statusCard, {
|
|
304
|
-
ready:
|
|
305
|
+
ready: readyForApproval,
|
|
305
306
|
humanPresentation,
|
|
306
307
|
revisionId,
|
|
307
308
|
registry: interactionLocales,
|
|
@@ -312,6 +313,50 @@ export function evaluateGateCheck(targetDir, selection = {}) {
|
|
|
312
313
|
enumerable: false,
|
|
313
314
|
});
|
|
314
315
|
|
|
316
|
+
const approvalPresentation = renderApprovalOrientationSnapshot(approvalOrientation, {
|
|
317
|
+
registry: interactionLocales,
|
|
318
|
+
expectedIdentity: {
|
|
319
|
+
run_id: statusCard.run_id,
|
|
320
|
+
revision_id: revisionId,
|
|
321
|
+
current_gate: currentGate,
|
|
322
|
+
presentation_language: statusCard.presentation_language,
|
|
323
|
+
},
|
|
324
|
+
});
|
|
325
|
+
const presentationDiagnostics = {};
|
|
326
|
+
if (!statusPresentation) {
|
|
327
|
+
presentationDiagnostics.status_presentation_errors = [
|
|
328
|
+
...validateOperationalStatusCardPreconditions(statusCard, {
|
|
329
|
+
registry: interactionLocales,
|
|
330
|
+
humanPresentation,
|
|
331
|
+
}).errors,
|
|
332
|
+
];
|
|
333
|
+
}
|
|
334
|
+
if (readyForApproval && !approvalPresentation) {
|
|
335
|
+
presentationDiagnostics.approval_presentation_errors = approvalOrientation
|
|
336
|
+
? [
|
|
337
|
+
...validateApprovalOrientationSnapshot(approvalOrientation, {
|
|
338
|
+
registry: interactionLocales,
|
|
339
|
+
expectedIdentity: {
|
|
340
|
+
run_id: statusCard.run_id,
|
|
341
|
+
revision_id: revisionId,
|
|
342
|
+
current_gate: currentGate,
|
|
343
|
+
presentation_language: statusCard.presentation_language,
|
|
344
|
+
},
|
|
345
|
+
}).errors,
|
|
346
|
+
]
|
|
347
|
+
: [
|
|
348
|
+
...validateApprovalOrientationPreconditions({
|
|
349
|
+
statusCard,
|
|
350
|
+
humanPresentation,
|
|
351
|
+
registry: interactionLocales,
|
|
352
|
+
requestedLocale: presentationLocale,
|
|
353
|
+
}).errors,
|
|
354
|
+
];
|
|
355
|
+
if (!presentationDiagnostics.approval_presentation_errors.length) {
|
|
356
|
+
presentationDiagnostics.approval_presentation_errors = ["snapshot_unavailable"];
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
315
360
|
return {
|
|
316
361
|
schema_version: "1",
|
|
317
362
|
status,
|
|
@@ -332,15 +377,8 @@ export function evaluateGateCheck(targetDir, selection = {}) {
|
|
|
332
377
|
candidate_runs: runState.candidate_runs ?? [],
|
|
333
378
|
status_card: statusCard,
|
|
334
379
|
status_presentation: statusPresentation,
|
|
335
|
-
approval_presentation:
|
|
336
|
-
|
|
337
|
-
expectedIdentity: {
|
|
338
|
-
run_id: statusCard.run_id,
|
|
339
|
-
revision_id: revisionId,
|
|
340
|
-
current_gate: currentGate,
|
|
341
|
-
presentation_language: statusCard.presentation_language,
|
|
342
|
-
},
|
|
343
|
-
}),
|
|
380
|
+
approval_presentation: approvalPresentation,
|
|
381
|
+
...(Object.keys(presentationDiagnostics).length ? { presentation_diagnostics: presentationDiagnostics } : {}),
|
|
344
382
|
delivery_map: {
|
|
345
383
|
relationships: deliveryMap.relationships,
|
|
346
384
|
mode_slice_decision: runState.mode_slice_decision,
|
|
@@ -368,6 +406,14 @@ export function printApprovalEnvelope(report, { io = console, reEvaluate } = {})
|
|
|
368
406
|
if (report?.approval_presentation) {
|
|
369
407
|
io.log(report.approval_presentation.blocks.run_status_card.markdown);
|
|
370
408
|
io.log("");
|
|
409
|
+
if (report.status_presentation?.markdown) {
|
|
410
|
+
io.log(report.status_presentation.markdown);
|
|
411
|
+
} else {
|
|
412
|
+
const pack = localePack(interactionLocales, report.approval_presentation.presentation_language || "en");
|
|
413
|
+
const codes = report.presentation_diagnostics?.status_presentation_errors ?? [];
|
|
414
|
+
io.log(codes.length ? `${pack.interaction.statusPresentationFailure} (${codes.join(", ")})` : pack.interaction.statusPresentationFailure);
|
|
415
|
+
}
|
|
416
|
+
io.log("");
|
|
371
417
|
io.log(report.approval_presentation.blocks.gate_transition_card.markdown);
|
|
372
418
|
io.log("");
|
|
373
419
|
io.log(report.approval_presentation.approval_interaction.exact_text_fallback);
|
|
@@ -386,7 +432,10 @@ export function printApprovalEnvelope(report, { io = console, reEvaluate } = {})
|
|
|
386
432
|
const pack = localePack(interactionLocales, refreshed?.status_card?.presentation_language || report?.status_card?.presentation_language || "en");
|
|
387
433
|
const refreshedApproval = canonicalApprovalForReport(refreshed);
|
|
388
434
|
if (initialApproval && refreshedApproval) {
|
|
389
|
-
|
|
435
|
+
const codes = refreshed?.presentation_diagnostics?.approval_presentation_errors
|
|
436
|
+
?? report?.presentation_diagnostics?.approval_presentation_errors
|
|
437
|
+
?? [];
|
|
438
|
+
io.log(codes.length ? `${pack.interaction.presentationFailure} (${codes.join(", ")})` : pack.interaction.presentationFailure);
|
|
390
439
|
io.log(pack.interaction.exactTextRequest.replace("{approval}", refreshedApproval));
|
|
391
440
|
return Object.freeze({ outcome: "exact_text_recovery", requested_decision: true, status: refreshed.status });
|
|
392
441
|
}
|
|
@@ -404,7 +453,9 @@ export function printApprovalEnvelope(report, { io = console, reEvaluate } = {})
|
|
|
404
453
|
function printGateCheckStatusCard(report, io) {
|
|
405
454
|
const card = report.status_card;
|
|
406
455
|
if (!report.status_presentation?.markdown) {
|
|
407
|
-
|
|
456
|
+
const failure = localePack(interactionLocales, card?.presentation_language || "en").interaction.statusPresentationFailure;
|
|
457
|
+
const codes = report.presentation_diagnostics?.status_presentation_errors ?? [];
|
|
458
|
+
io.log(codes.length ? `${failure} (${codes.join(", ")})` : failure);
|
|
408
459
|
return false;
|
|
409
460
|
}
|
|
410
461
|
io.log(report.status_presentation.markdown);
|
|
@@ -2,6 +2,7 @@ import { existsSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
|
2
2
|
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import { parseControlState, resolveRuns } from "../control-state/index.js";
|
|
5
|
+
import { validateRunIdentity } from "../control-state/run-identity.js";
|
|
5
6
|
import { buildRunCandidates } from "../interaction-presentation.js";
|
|
6
7
|
import { cleanStatusCell, filled, isPlaceholderValue, readTargetFile } from "./shared.js";
|
|
7
8
|
import { extractField, isSafeRepoRelativePath, readVerifiedChangeRecord } from "./verified-change.js";
|
|
@@ -87,6 +88,7 @@ export function readRunState(targetDir, selection = {}) {
|
|
|
87
88
|
memory: {},
|
|
88
89
|
candidate_runs: candidateRuns,
|
|
89
90
|
resolution_error: resolutionError,
|
|
91
|
+
identity_findings: [],
|
|
90
92
|
};
|
|
91
93
|
}
|
|
92
94
|
|
|
@@ -101,6 +103,10 @@ export function readRunState(targetDir, selection = {}) {
|
|
|
101
103
|
path: runPath,
|
|
102
104
|
content,
|
|
103
105
|
...parsed,
|
|
106
|
+
identity_findings: validateRunIdentity({
|
|
107
|
+
runId: extractField(content, "run_id").replace(/^`|`$/g, ""),
|
|
108
|
+
revisionId: extractField(content, "revision_id").replace(/^`|`$/g, ""),
|
|
109
|
+
}),
|
|
104
110
|
};
|
|
105
111
|
}
|
|
106
112
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const RUN_ID_PATTERN = /^[a-z0-9][a-z0-9._-]{0,127}$/;
|
|
2
|
+
export const REVISION_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
3
|
+
|
|
4
|
+
export function validateRunIdentity({ runId, revisionId } = {}) {
|
|
5
|
+
const findings = [];
|
|
6
|
+
if (!RUN_ID_PATTERN.test(String(runId ?? ""))) findings.push({ code: "AGDF_RUN_ID_INVALID" });
|
|
7
|
+
if (!REVISION_ID_PATTERN.test(String(revisionId ?? ""))) findings.push({ code: "AGDF_RUN_REVISION_ID_INVALID" });
|
|
8
|
+
return findings;
|
|
9
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { REVISION_ID_PATTERN, RUN_ID_PATTERN } from "./run-identity.js";
|
|
2
|
+
|
|
3
|
+
export { RUN_ID_PATTERN } from "./run-identity.js";
|
|
2
4
|
export const LIFECYCLES = new Set([
|
|
3
5
|
"active",
|
|
4
6
|
"completed",
|
|
@@ -34,7 +36,7 @@ export function parseRunState(content, expected) {
|
|
|
34
36
|
findings.push({ code: "AGDF_RUN_LIFECYCLE_INVALID" });
|
|
35
37
|
if (!/^[1-9]\d*$/.test(values.get("revision") ?? ""))
|
|
36
38
|
findings.push({ code: "AGDF_RUN_REVISION_INVALID" });
|
|
37
|
-
if (
|
|
39
|
+
if (!REVISION_ID_PATTERN.test(values.get("revision_id") ?? ""))
|
|
38
40
|
findings.push({ code: "AGDF_RUN_REVISION_ID_INVALID" });
|
|
39
41
|
return {
|
|
40
42
|
content,
|
|
@@ -68,6 +68,10 @@ export function validateSearchInput(value) {
|
|
|
68
68
|
}
|
|
69
69
|
requireString(input.objective, "objective");
|
|
70
70
|
requireString(input.current_gate, "current_gate");
|
|
71
|
+
optionalBoundedString(input.scope_revision, "scope_revision", 200);
|
|
72
|
+
optionalBoundedString(input.input_failure_code, "input_failure_code", 200);
|
|
73
|
+
optionalBoundedString(input.input_failure_detail, "input_failure_detail", 1000);
|
|
74
|
+
optionalBoundedString(input.input_recovery_action, "input_recovery_action", 1000);
|
|
71
75
|
stringArray(input.allowed_actions, "allowed_actions");
|
|
72
76
|
stringArray(input.forbidden_actions, "forbidden_actions");
|
|
73
77
|
stringArray(input.evidence_refs ?? [], "evidence_refs");
|
|
@@ -81,6 +85,39 @@ export function validateSearchInput(value) {
|
|
|
81
85
|
return structuredClone(input);
|
|
82
86
|
}
|
|
83
87
|
|
|
88
|
+
const RESULT_PHASE_BY_STATUS = new Map([
|
|
89
|
+
["input_unavailable", "input"],
|
|
90
|
+
["no_legal_candidates", "candidate"],
|
|
91
|
+
["evaluator_unavailable", "evaluation"],
|
|
92
|
+
["evaluator_error", "evaluation"],
|
|
93
|
+
["recommendation", "search"],
|
|
94
|
+
["no_safe_recommendation", "search"],
|
|
95
|
+
]);
|
|
96
|
+
|
|
97
|
+
export function validateSearchResult(value) {
|
|
98
|
+
const result = requireObject(value, "search result");
|
|
99
|
+
if (result.contract_version !== CONTRACT_VERSION) throw new Error(`unsupported search result contract_version: ${result.contract_version}`);
|
|
100
|
+
const status = requireString(result.status, "result.status");
|
|
101
|
+
const expectedPhase = RESULT_PHASE_BY_STATUS.get(status);
|
|
102
|
+
if (!expectedPhase) throw new Error(`unsupported search result status: ${status}`);
|
|
103
|
+
if (result.outcome_phase !== expectedPhase) throw new Error(`${status} requires outcome_phase ${expectedPhase}`);
|
|
104
|
+
requireString(result.scope_key, "result.scope_key");
|
|
105
|
+
optionalBoundedString(result.scope_revision, "result.scope_revision", 200);
|
|
106
|
+
const provenance = requireObject(result.provenance, "result.provenance");
|
|
107
|
+
for (const key of ["baseline_candidates", "generated_candidates", "legal_candidates", "rejected_candidates", "evaluation_attempts", "valid_evaluations", "invalid_evaluations"]) {
|
|
108
|
+
if (!Number.isInteger(provenance[key]) || provenance[key] < 0) throw new Error(`result.provenance.${key} must be a non-negative integer`);
|
|
109
|
+
}
|
|
110
|
+
if (provenance.evaluation_attempts !== provenance.valid_evaluations + provenance.invalid_evaluations) throw new Error("result evaluation provenance counts are inconsistent");
|
|
111
|
+
const recommendationFacing = status === "recommendation" || status === "no_safe_recommendation";
|
|
112
|
+
if (recommendationFacing && provenance.valid_evaluations < 1) throw new Error(`${status} requires at least one valid evaluation`);
|
|
113
|
+
if (status === "recommendation" && !result.recommendation) throw new Error("recommendation status requires recommendation content");
|
|
114
|
+
if (status !== "recommendation" && result.recommendation != null) throw new Error(`${status} must not contain recommendation content`);
|
|
115
|
+
if (status === "no_legal_candidates" && (provenance.legal_candidates !== 0 || provenance.evaluation_attempts !== 0)) throw new Error("no_legal_candidates requires zero legal candidates and evaluation attempts");
|
|
116
|
+
if (status === "input_unavailable" && provenance.evaluation_attempts !== 0) throw new Error("input_unavailable requires zero evaluation attempts");
|
|
117
|
+
if ((status === "evaluator_unavailable" || status === "evaluator_error") && provenance.valid_evaluations !== 0) throw new Error(`${status} requires zero valid evaluations`);
|
|
118
|
+
return structuredClone(result);
|
|
119
|
+
}
|
|
120
|
+
|
|
84
121
|
export function validateCandidate(value) {
|
|
85
122
|
const candidate = requireObject(value, "candidate");
|
|
86
123
|
requireString(candidate.id, "candidate.id");
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
|
+
import { validateSearchResult } from "./contracts.js";
|
|
3
4
|
|
|
4
5
|
function safeResult(result) {
|
|
5
6
|
return JSON.parse(JSON.stringify(result, (key, value) => {
|
|
@@ -9,22 +10,33 @@ function safeResult(result) {
|
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
export function persistSearchResult(targetDir, result) {
|
|
12
|
-
const
|
|
13
|
+
const validated = validateSearchResult(result);
|
|
14
|
+
if (!["recommendation", "no_safe_recommendation"].includes(validated.status)) {
|
|
15
|
+
throw new Error(`search result status ${validated.status} is not persistable`);
|
|
16
|
+
}
|
|
17
|
+
if (validated.provenance.valid_evaluations < 1) {
|
|
18
|
+
throw new Error("search result requires at least one valid evaluation before persistence");
|
|
19
|
+
}
|
|
20
|
+
const root = join(targetDir, ".agdf", "control", "artefacts", validated.scope_key);
|
|
13
21
|
const jsonPath = join(root, "DELIVERY_PATH_SEARCH.json");
|
|
14
22
|
const markdownPath = join(root, "DELIVERY_PATH_SEARCH.md");
|
|
15
23
|
mkdirSync(dirname(jsonPath), { recursive: true });
|
|
16
|
-
const safe = safeResult(
|
|
24
|
+
const safe = safeResult(validated);
|
|
17
25
|
writeFileSync(jsonPath, `${JSON.stringify(safe, null, 2)}\n`, "utf8");
|
|
18
26
|
const recommendation = safe.recommendation?.action ?? "No safe recommendation";
|
|
19
27
|
writeFileSync(markdownPath, [
|
|
20
28
|
"# Delivery Path Search Decision",
|
|
21
29
|
"",
|
|
22
30
|
`- status: ${safe.status}`,
|
|
31
|
+
`- outcome_phase: ${safe.outcome_phase}`,
|
|
32
|
+
`- scope_revision: ${safe.scope_revision}`,
|
|
23
33
|
`- current_gate: ${safe.current_gate}`,
|
|
24
34
|
`- enforcement: ${safe.enforcement.level}`,
|
|
25
35
|
`- recommendation: ${recommendation}`,
|
|
26
36
|
`- stopping_reason: ${safe.stopping_reason}`,
|
|
27
37
|
`- evaluations: ${safe.budgets.evaluations}`,
|
|
38
|
+
`- evaluation_attempts: ${safe.provenance.evaluation_attempts}`,
|
|
39
|
+
`- invalid_evaluations: ${safe.provenance.invalid_evaluations}`,
|
|
28
40
|
`- generation_status: ${safe.generation?.status ?? "legacy_deterministic"}`,
|
|
29
41
|
`- generated_candidates: ${safe.generation ? `${safe.generation.accepted}/${safe.generation.returned}` : "0/0"}`,
|
|
30
42
|
`- generation_cost_units: ${safe.generation?.cost_units ?? 0}`,
|
package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/search-engine.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CONTRACT_VERSION, SCORING_POLICY_VERSION, validateEvaluation, validateSearchInput } from "./contracts.js";
|
|
1
|
+
import { CONTRACT_VERSION, SCORING_POLICY_VERSION, validateEvaluation, validateSearchInput, validateSearchResult } from "./contracts.js";
|
|
2
2
|
import { candidateLegality, candidatesFromInput, generatedCandidatesFromResponse } from "./candidate-policy.js";
|
|
3
3
|
import { scoreEvaluation } from "./scoring.js";
|
|
4
4
|
import { generatorRequestFromInput } from "./state-adapter.js";
|
|
@@ -14,6 +14,30 @@ function generatorFailureCode(error) {
|
|
|
14
14
|
return "generator_schema_invalid";
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
function evaluatorFailureCode(error) {
|
|
18
|
+
if (error?.code === "ENOENT") return "evaluator_unavailable";
|
|
19
|
+
if (/auth/i.test(error?.message ?? "")) return "evaluator_authentication_failed";
|
|
20
|
+
if (/unavailable|not found|not configured/i.test(error?.message ?? "")) return "evaluator_unavailable";
|
|
21
|
+
return "evaluator_output_invalid";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function classifyTerminalOutcome({
|
|
25
|
+
inputUnavailable = false,
|
|
26
|
+
legalCandidates = 0,
|
|
27
|
+
evaluationAttempts = 0,
|
|
28
|
+
validEvaluations = 0,
|
|
29
|
+
invalidEvaluations = 0,
|
|
30
|
+
evaluatorUnavailable = false,
|
|
31
|
+
leader = null,
|
|
32
|
+
}) {
|
|
33
|
+
if (inputUnavailable) return { status: "input_unavailable", outcome_phase: "input" };
|
|
34
|
+
if (legalCandidates === 0) return { status: "no_legal_candidates", outcome_phase: "candidate" };
|
|
35
|
+
if (validEvaluations === 0 && evaluatorUnavailable) return { status: "evaluator_unavailable", outcome_phase: "evaluation" };
|
|
36
|
+
if (validEvaluations === 0 && legalCandidates > 0) return { status: "evaluator_error", outcome_phase: "evaluation" };
|
|
37
|
+
if (leader) return { status: "recommendation", outcome_phase: "search" };
|
|
38
|
+
return { status: "no_safe_recommendation", outcome_phase: "search" };
|
|
39
|
+
}
|
|
40
|
+
|
|
17
41
|
export async function runDeliveryPathSearch(inputValue, evaluator, options = {}) {
|
|
18
42
|
const input = validateSearchInput(inputValue);
|
|
19
43
|
const startedAt = Date.now();
|
|
@@ -22,12 +46,18 @@ export async function runDeliveryPathSearch(inputValue, evaluator, options = {})
|
|
|
22
46
|
const rejected = [];
|
|
23
47
|
const evaluated = [];
|
|
24
48
|
let evaluations = 0;
|
|
49
|
+
let evaluationAttempts = 0;
|
|
50
|
+
let invalidEvaluations = 0;
|
|
51
|
+
let legalCandidates = 0;
|
|
52
|
+
let rejectedCandidates = 0;
|
|
53
|
+
let evaluatorUnavailable = false;
|
|
54
|
+
let evaluationFailureCode = null;
|
|
25
55
|
let costUnits = 0;
|
|
26
56
|
let lastLeaderId = null;
|
|
27
57
|
let stableComparisons = 0;
|
|
28
58
|
let stoppingReason = "candidate_queue_exhausted";
|
|
29
59
|
const generation = {
|
|
30
|
-
status: input.generation?.enabled ? "
|
|
60
|
+
status: input.generation?.enabled ? "not_run" : "disabled",
|
|
31
61
|
adapter: options.generator?.metadata ?? null,
|
|
32
62
|
budgets: input.generation ?? null,
|
|
33
63
|
returned: 0,
|
|
@@ -38,7 +68,66 @@ export async function runDeliveryPathSearch(inputValue, evaluator, options = {})
|
|
|
38
68
|
failure_code: null,
|
|
39
69
|
};
|
|
40
70
|
|
|
71
|
+
const finish = ({ leader = null, outcome, failureCode = null, recoveryAction = null }) => {
|
|
72
|
+
evaluated.sort((a, b) => b.score - a.score || a.candidate.id.localeCompare(b.candidate.id));
|
|
73
|
+
const result = {
|
|
74
|
+
contract_version: CONTRACT_VERSION,
|
|
75
|
+
scoring_policy_version: SCORING_POLICY_VERSION,
|
|
76
|
+
scope_key: input.scope_key,
|
|
77
|
+
scope_revision: input.scope_revision ?? "unversioned",
|
|
78
|
+
objective: input.objective,
|
|
79
|
+
current_gate: input.current_gate,
|
|
80
|
+
outcome_phase: outcome.outcome_phase,
|
|
81
|
+
status: outcome.status,
|
|
82
|
+
recommendation: outcome.status === "recommendation" && leader ? {
|
|
83
|
+
candidate_id: leader.candidate.id,
|
|
84
|
+
action: leader.candidate.action,
|
|
85
|
+
score: leader.score,
|
|
86
|
+
rationale: leader.evaluation.rationale,
|
|
87
|
+
expected_evidence: leader.candidate.expected_evidence,
|
|
88
|
+
tests: leader.candidate.tests,
|
|
89
|
+
risks: leader.evaluation.risks,
|
|
90
|
+
assumptions: leader.evaluation.assumptions,
|
|
91
|
+
} : null,
|
|
92
|
+
alternatives: outcome.outcome_phase === "search" ? evaluated.slice(1).map((item) => ({
|
|
93
|
+
candidate_id: item.candidate.id,
|
|
94
|
+
action: item.candidate.action,
|
|
95
|
+
score: item.score,
|
|
96
|
+
rejection_reason: `ranked_below_${leader?.candidate.id ?? "search_policy"}`,
|
|
97
|
+
})) : [],
|
|
98
|
+
rejected,
|
|
99
|
+
provenance: {
|
|
100
|
+
baseline_candidates: baseline.length,
|
|
101
|
+
generated_candidates: generation.accepted,
|
|
102
|
+
legal_candidates: legalCandidates,
|
|
103
|
+
rejected_candidates: rejectedCandidates + generation.rejected.length,
|
|
104
|
+
evaluation_attempts: evaluationAttempts,
|
|
105
|
+
valid_evaluations: evaluations,
|
|
106
|
+
invalid_evaluations: invalidEvaluations,
|
|
107
|
+
},
|
|
108
|
+
enforcement: input.enforcement,
|
|
109
|
+
evaluator: evaluator?.metadata ?? { name: evaluator?.name ?? "unknown" },
|
|
110
|
+
generation,
|
|
111
|
+
budgets: { configured: input.budgets, evaluations, cost_units: costUnits, stable_comparisons: stableComparisons, duration_ms: Date.now() - startedAt },
|
|
112
|
+
stopping_reason: stoppingReason,
|
|
113
|
+
failure_code: failureCode,
|
|
114
|
+
next_action: recoveryAction ?? "Run canonical AGDF gate-check; search does not grant permission.",
|
|
115
|
+
next_gate_action: recoveryAction ?? "Run canonical AGDF gate-check; search does not grant permission.",
|
|
116
|
+
};
|
|
117
|
+
return validateSearchResult(result);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
if (input.input_failure_code || input.allowed_actions.length === 0) {
|
|
121
|
+
stoppingReason = input.input_failure_code || "canonical_actions_unavailable";
|
|
122
|
+
return finish({
|
|
123
|
+
outcome: classifyTerminalOutcome({ inputUnavailable: true }),
|
|
124
|
+
failureCode: input.input_failure_code || "canonical_actions_unavailable",
|
|
125
|
+
recoveryAction: input.input_recovery_action || "Run canonical AGDF gate-check and repair the selected control state.",
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
41
129
|
if (input.generation?.enabled) {
|
|
130
|
+
generation.status = "failed";
|
|
42
131
|
if (!options.generator) {
|
|
43
132
|
generation.failure_code = "generator_unavailable";
|
|
44
133
|
} else {
|
|
@@ -75,17 +164,20 @@ export async function runDeliveryPathSearch(inputValue, evaluator, options = {})
|
|
|
75
164
|
}
|
|
76
165
|
|
|
77
166
|
while (queue.length > 0) {
|
|
78
|
-
if (evaluations >= input.budgets.max_evaluations) { stoppingReason = "evaluation_budget_exhausted"; break; }
|
|
79
|
-
if (costUnits >= input.budgets.max_cost_units) { stoppingReason = "cost_budget_exhausted"; break; }
|
|
80
|
-
if (Date.now() - startedAt >= input.budgets.max_duration_ms) { stoppingReason = "duration_budget_exhausted"; break; }
|
|
81
167
|
const candidate = queue.shift();
|
|
82
168
|
const legality = candidateLegality(candidate, input);
|
|
83
169
|
if (!legality.legal) {
|
|
170
|
+
rejectedCandidates += 1;
|
|
84
171
|
rejected.push({ candidate_id: candidate.id, action: candidate.action, reason: legality.reason });
|
|
85
172
|
continue;
|
|
86
173
|
}
|
|
174
|
+
legalCandidates += 1;
|
|
175
|
+
if (evaluations >= input.budgets.max_evaluations) { stoppingReason = "evaluation_budget_exhausted"; evaluationFailureCode = stoppingReason; break; }
|
|
176
|
+
if (costUnits >= input.budgets.max_cost_units) { stoppingReason = "cost_budget_exhausted"; evaluationFailureCode = stoppingReason; break; }
|
|
177
|
+
if (Date.now() - startedAt >= input.budgets.max_duration_ms) { stoppingReason = "duration_budget_exhausted"; evaluationFailureCode = stoppingReason; break; }
|
|
87
178
|
|
|
88
179
|
try {
|
|
180
|
+
evaluationAttempts += 1;
|
|
89
181
|
const evaluation = validateEvaluation(await evaluator.evaluate(input, candidate), candidate.id);
|
|
90
182
|
evaluations += 1;
|
|
91
183
|
costUnits += evaluation.cost;
|
|
@@ -115,41 +207,39 @@ export async function runDeliveryPathSearch(inputValue, evaluator, options = {})
|
|
|
115
207
|
}
|
|
116
208
|
} catch (error) {
|
|
117
209
|
if (error?.fatalEvaluator === true) throw error;
|
|
210
|
+
invalidEvaluations += 1;
|
|
211
|
+
evaluationFailureCode = evaluatorFailureCode(error);
|
|
212
|
+
evaluatorUnavailable ||= evaluationFailureCode === "evaluator_unavailable" || evaluationFailureCode === "evaluator_authentication_failed";
|
|
118
213
|
rejected.push({ candidate_id: candidate.id, action: candidate.action, reason: `invalid_evaluation: ${error.message}` });
|
|
214
|
+
if (evaluatorUnavailable) break;
|
|
119
215
|
}
|
|
120
216
|
}
|
|
121
217
|
|
|
122
218
|
evaluated.sort((a, b) => b.score - a.score || a.candidate.id.localeCompare(b.candidate.id));
|
|
123
219
|
const leader = evaluated[0];
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
evaluator: evaluator.metadata ?? { name: evaluator.name ?? "unknown" },
|
|
150
|
-
generation,
|
|
151
|
-
budgets: { configured: input.budgets, evaluations, cost_units: costUnits, stable_comparisons: stableComparisons, duration_ms: Date.now() - startedAt },
|
|
152
|
-
stopping_reason: stoppingReason,
|
|
153
|
-
next_gate_action: "Run canonical AGDF gate-check; search does not grant permission.",
|
|
154
|
-
};
|
|
220
|
+
const outcome = classifyTerminalOutcome({
|
|
221
|
+
legalCandidates,
|
|
222
|
+
evaluationAttempts,
|
|
223
|
+
validEvaluations: evaluations,
|
|
224
|
+
invalidEvaluations,
|
|
225
|
+
evaluatorUnavailable,
|
|
226
|
+
leader,
|
|
227
|
+
});
|
|
228
|
+
if (!leader) {
|
|
229
|
+
stoppingReason = outcome.status === "no_legal_candidates"
|
|
230
|
+
? "no_legal_candidates"
|
|
231
|
+
: outcome.status === "evaluator_unavailable"
|
|
232
|
+
? "evaluator_unavailable"
|
|
233
|
+
: outcome.status === "evaluator_error"
|
|
234
|
+
? "evaluator_error"
|
|
235
|
+
: stoppingReason;
|
|
236
|
+
}
|
|
237
|
+
return finish({
|
|
238
|
+
leader,
|
|
239
|
+
outcome,
|
|
240
|
+
failureCode: outcome.outcome_phase === "evaluation" ? evaluationFailureCode : null,
|
|
241
|
+
recoveryAction: outcome.outcome_phase === "evaluation"
|
|
242
|
+
? "Repair the declared evaluator capability and retry from a fresh canonical control snapshot."
|
|
243
|
+
: null,
|
|
244
|
+
});
|
|
155
245
|
}
|