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.
Files changed (103) hide show
  1. package/README.md +39 -3
  2. package/generated/.opencode/contracts/control-scaffold.md +17 -2
  3. package/generated/.opencode/contracts/interaction.md +2 -2
  4. package/generated/.opencode/skills/agdf-delivery-path-search/SKILL.md +16 -4
  5. package/generated/.opencode/skills/agdf-gate-check/SKILL.md +6 -4
  6. package/generated/plugins/agdf/.claude-plugin/plugin.json +1 -1
  7. package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
  8. package/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
  9. package/generated/plugins/agdf/meta/contracts/control-scaffold.md +17 -2
  10. package/generated/plugins/agdf/meta/contracts/interaction.md +2 -2
  11. package/generated/plugins/agdf/meta/copilot-payload-baseline.json +3 -3
  12. package/generated/plugins/agdf/meta/distribution-profile-history.json +125 -0
  13. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
  14. package/generated/plugins/agdf/runtime/create-agdf/lib/cli/command-registry.js +8 -1
  15. package/generated/plugins/agdf/runtime/create-agdf/lib/cli/delivery-path-search-command.js +51 -19
  16. package/generated/plugins/agdf/runtime/create-agdf/lib/cli/parse-args.js +3 -0
  17. package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/doctor.js +13 -0
  18. package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/gate-check.js +64 -13
  19. package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/run-state.js +6 -0
  20. package/generated/plugins/agdf/runtime/create-agdf/lib/control-state/run-identity.js +9 -0
  21. package/generated/plugins/agdf/runtime/create-agdf/lib/control-state/run-state-parser.js +4 -2
  22. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/contracts.js +37 -0
  23. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/persistence.js +14 -2
  24. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/search-engine.js +126 -36
  25. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/state-adapter.js +40 -92
  26. package/generated/plugins/agdf/runtime/create-agdf/lib/interaction-presentation.js +57 -7
  27. package/generated/plugins/agdf/runtime/create-agdf/lib/runtime/plugin-provenance.js +46 -3
  28. package/generated/plugins/agdf/runtime/create-agdf/package.json +1 -1
  29. package/generated/plugins/agdf/runtime/runtime-manifest.json +2 -2
  30. package/generated/plugins/agdf/scripts/check-runtime-integrity.mjs +2 -0
  31. package/generated/plugins/agdf/skills/delivery-path-search/SKILL.md +16 -4
  32. package/generated/plugins/agdf/skills/gate-check/SKILL.md +6 -4
  33. package/generated/plugins/agdf/submission/openai/availability.md +1 -1
  34. package/generated/plugins/agdf/submission/openai/capability-matrix.json +1 -1
  35. package/generated/plugins/agdf/submission/openai/release-notes.md +7 -1
  36. package/generated/plugins/agdf/submission/openai/reviewer-cases.json +1 -1
  37. package/generated/plugins/copilot/agdf/.agdf-payload-inventory.json +71 -51
  38. package/generated/plugins/copilot/agdf/copilot-skills/agdf-delivery-path-search/SKILL.md +16 -4
  39. package/generated/plugins/copilot/agdf/copilot-skills/agdf-gate-check/SKILL.md +6 -4
  40. package/generated/plugins/copilot/agdf/copilot-skills/contracts/control-scaffold.md +17 -2
  41. package/generated/plugins/copilot/agdf/copilot-skills/contracts/interaction.md +2 -2
  42. package/generated/plugins/copilot/agdf/meta/agdf-plugin.definition.json +1 -1
  43. package/generated/plugins/copilot/agdf/meta/distribution-profile-history.json +125 -0
  44. package/generated/plugins/copilot/agdf/plugin.json +1 -1
  45. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
  46. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/command-registry.js +8 -1
  47. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/delivery-path-search-command.js +51 -19
  48. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/parse-args.js +3 -0
  49. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/doctor.js +13 -0
  50. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/gate-check.js +64 -13
  51. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/run-state.js +6 -0
  52. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/run-identity.js +9 -0
  53. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/run-state-parser.js +4 -2
  54. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/contracts.js +37 -0
  55. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/persistence.js +14 -2
  56. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/search-engine.js +126 -36
  57. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/state-adapter.js +40 -92
  58. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/interaction-presentation.js +57 -7
  59. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/runtime/plugin-provenance.js +46 -3
  60. package/generated/plugins/copilot/agdf/runtime/create-agdf/package.json +1 -1
  61. package/generated/plugins/copilot/agdf/runtime/runtime-manifest.json +2 -2
  62. package/generated/submissions/openai/agdf/.codex-plugin/plugin.json +1 -1
  63. package/generated/submissions/openai/agdf/meta/agdf-plugin.definition.json +1 -1
  64. package/generated/submissions/openai/agdf/meta/contracts/control-scaffold.md +17 -2
  65. package/generated/submissions/openai/agdf/meta/contracts/interaction.md +2 -2
  66. package/generated/submissions/openai/agdf/meta/copilot-payload-baseline.json +3 -3
  67. package/generated/submissions/openai/agdf/meta/distribution-profile-history.json +125 -0
  68. package/generated/submissions/openai/agdf/skills/delivery-path-search/SKILL.md +16 -4
  69. package/generated/submissions/openai/agdf/skills/gate-check/SKILL.md +6 -4
  70. package/generated/submissions/openai/agdf/submission/openai/availability.md +1 -1
  71. package/generated/submissions/openai/agdf/submission/openai/capability-matrix.json +1 -1
  72. package/generated/submissions/openai/agdf/submission/openai/inventory.json +18 -14
  73. package/generated/submissions/openai/agdf/submission/openai/listing.json +1 -1
  74. package/generated/submissions/openai/agdf/submission/openai/readiness.json +2 -2
  75. package/generated/submissions/openai/agdf/submission/openai/readiness.md +2 -2
  76. package/generated/submissions/openai/agdf/submission/openai/release-notes.md +7 -1
  77. package/generated/submissions/openai/agdf/submission/openai/reviewer-cases.json +1 -1
  78. package/lib/cli/application.js +24 -6
  79. package/lib/cli/command-registry.js +8 -1
  80. package/lib/cli/delivery-path-search-command.js +51 -19
  81. package/lib/cli/parse-args.js +3 -0
  82. package/lib/control-evaluation/doctor.js +13 -0
  83. package/lib/control-evaluation/gate-check.js +64 -13
  84. package/lib/control-evaluation/run-state.js +6 -0
  85. package/lib/control-state/run-identity.js +9 -0
  86. package/lib/control-state/run-state-parser.js +4 -2
  87. package/lib/delivery-path-search/contracts.js +37 -0
  88. package/lib/delivery-path-search/persistence.js +14 -2
  89. package/lib/delivery-path-search/search-engine.js +126 -36
  90. package/lib/delivery-path-search/state-adapter.js +40 -92
  91. package/lib/installers/claude-cache-recovery.js +76 -0
  92. package/lib/installers/copilot-settings.js +146 -16
  93. package/lib/installers/local-marketplace.js +169 -14
  94. package/lib/installers/plugin-installers.js +96 -9
  95. package/lib/interaction-presentation.js +57 -7
  96. package/lib/lifecycle/operations.js +28 -4
  97. package/lib/lifecycle/result.js +4 -4
  98. package/lib/release/profile-history.js +222 -0
  99. package/lib/release/version-bump.js +527 -0
  100. package/lib/release/version-coherence.js +92 -38
  101. package/lib/runtime/distribution-profile-history.js +173 -0
  102. package/lib/runtime/plugin-provenance.js +46 -3
  103. package/package.json +7 -3
@@ -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 ? "failed" : "disabled",
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
- if (!leader) stoppingReason = rejected.length ? "no_safe_candidate" : stoppingReason;
125
- return {
126
- contract_version: CONTRACT_VERSION,
127
- scoring_policy_version: SCORING_POLICY_VERSION,
128
- scope_key: input.scope_key,
129
- current_gate: input.current_gate,
130
- status: leader ? "recommendation" : "no_safe_recommendation",
131
- recommendation: leader ? {
132
- candidate_id: leader.candidate.id,
133
- action: leader.candidate.action,
134
- score: leader.score,
135
- rationale: leader.evaluation.rationale,
136
- expected_evidence: leader.candidate.expected_evidence,
137
- tests: leader.candidate.tests,
138
- risks: leader.evaluation.risks,
139
- assumptions: leader.evaluation.assumptions,
140
- } : null,
141
- alternatives: evaluated.slice(1).map((item) => ({
142
- candidate_id: item.candidate.id,
143
- action: item.candidate.action,
144
- score: item.score,
145
- rejection_reason: `ranked_below_${leader?.candidate.id ?? "safety_threshold"}`,
146
- })),
147
- rejected,
148
- enforcement: input.enforcement,
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
  }
@@ -1,11 +1,10 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import { join } from "node:path";
3
1
  import {
4
2
  CONTRACT_VERSION,
5
3
  GENERATOR_CONTRACT_VERSION,
6
4
  validateGeneratorRequest,
7
5
  } from "./contracts.js";
8
- import { resolveRuns, verifyLegacyProjection } from "../control-state/index.js";
6
+ import { evaluateGateCheck } from "../control-evaluation/gate-check.js";
7
+ import { readRunState } from "../control-evaluation/run-state.js";
9
8
 
10
9
  function section(content, heading) {
11
10
  return (
@@ -15,100 +14,44 @@ function section(content, heading) {
15
14
  );
16
15
  }
17
16
 
18
- function tableAnswers(content) {
19
- const answers = {};
20
- for (const line of section(content, "Current Control State").split(/\r?\n/)) {
21
- const cells = line
22
- .split("|")
23
- .slice(1, -1)
24
- .map((cell) => cell.trim());
25
- if (
26
- cells.length >= 2 &&
27
- cells[0] &&
28
- !cells[0].startsWith("---") &&
29
- cells[0] !== "Question"
30
- )
31
- answers[cells[0]] = cells[1];
32
- }
33
- return answers;
34
- }
35
-
36
- function statusCard(content) {
37
- const card = {};
38
- for (const line of section(content, "Run Status Card").split(/\r?\n/)) {
39
- const cells = line
40
- .split("|")
41
- .slice(1, -1)
42
- .map((cell) => cell.trim().replace(/^`|`$/g, ""));
43
- if (
44
- cells.length >= 2 &&
45
- cells[0] &&
46
- !cells[0].startsWith("---") &&
47
- cells[0] !== "Run status"
48
- )
49
- card[cells[0]] = cells[1];
50
- }
51
- return card;
17
+ function metaField(content, name) {
18
+ return content.match(new RegExp(`^- ${name}:\\s*(.+)$`, "m"))?.[1]
19
+ ?.replace(/^`|`$/g, "")
20
+ .trim() ?? "";
52
21
  }
53
22
 
54
- function listCell(value) {
55
- return String(value ?? "")
56
- .split(/\s*;\s*/)
57
- .map((item) => item.trim())
58
- .filter(Boolean);
59
- }
60
-
61
- export function searchInputFromControl(targetDir, options = {}) {
62
- const canonicalRoot = join(targetDir, ".agdf", "control", "runs");
63
- if (existsSync(canonicalRoot)) {
64
- const projection = verifyLegacyProjection(targetDir);
65
- if (!["absent", "valid"].includes(projection.status)) {
66
- throw new Error(`AGDF_LEGACY_PROJECTION_DRIFT:${projection.status}`);
67
- }
23
+ export function searchInputFromControl(targetDir, options = {}, dependencies = {}) {
24
+ const evaluate = dependencies.evaluateGateCheck ?? evaluateGateCheck;
25
+ const readRun = dependencies.readRunState ?? readRunState;
26
+ const selection = options.scopeKey ? { runId: options.scopeKey } : {};
27
+ const gate = evaluate(targetDir, selection);
28
+ const run = readRun(targetDir, selection);
29
+ const content = run.content ?? "";
30
+ const evaluatedRunId = gate.status_card?.run_id ?? "";
31
+ const evaluatedRevision = gate.status_presentation?.revision_id ?? "";
32
+ const runId = metaField(content, "run_id");
33
+ const runRevision = metaField(content, "revision_id");
34
+ const runGate = metaField(content, "current_gate");
35
+ let inputFailureCode = "";
36
+ if (!content || run.resolution_error) inputFailureCode = "canonical_control_unavailable";
37
+ else if (!evaluatedRunId || !evaluatedRevision) inputFailureCode = "canonical_snapshot_identity_missing";
38
+ else if (evaluatedRunId !== runId || evaluatedRevision !== runRevision || gate.current_gate !== runGate) {
39
+ inputFailureCode = "stale_control_snapshot";
40
+ } else if (!Array.isArray(gate.allowed) || gate.allowed.length === 0) {
41
+ inputFailureCode = "canonical_actions_unavailable";
68
42
  }
69
- const resolved = existsSync(canonicalRoot)
70
- ? resolveRuns(targetDir, {
71
- runIdArg: options.scopeKey,
72
- runIdEnv: process.env.AGDF_RUN_ID,
73
- })
74
- : null;
75
- const runPath =
76
- resolved?.run.path ?? join(targetDir, ".agdf", "control", "AGDF_RUN.md");
77
- if (!existsSync(runPath)) throw new Error("missing AGDF run state");
78
- const content = readFileSync(runPath, "utf8");
79
- const meta = Object.fromEntries(
80
- [...content.matchAll(/^- ([a-z_]+):\s*(.+)$/gm)].map((match) => [
81
- match[1],
82
- match[2].replace(/^`|`$/g, "").trim(),
83
- ]),
84
- );
85
- const answers = tableAnswers(content);
86
- const card = statusCard(content);
87
43
  const result = {
88
44
  contract_version: CONTRACT_VERSION,
89
- scope_key: options.scopeKey ?? meta.run_id ?? "unknown-scope",
90
- objective: section(content, "Objective").trim(),
91
- current_gate: card["Current gate"] ?? meta.current_gate ?? "unknown",
92
- allowed_actions: options.allowedActions ?? listCell(card["Allowed now"]),
93
- forbidden_actions:
94
- options.forbiddenActions ??
95
- listCell(answers["What is explicitly forbidden right now?"]),
96
- evidence_refs: [
97
- ...content.matchAll(
98
- /\|\s*([^|]+)\s*\|\s*([^|]+)\s*\|\s*([^|]+)\s*\|\s*(direct|indirect)\s*\|/g,
99
- ),
100
- ]
101
- .slice(1)
102
- .map((match) => `${match[1].trim()}: ${match[2].trim()}`),
103
- risks: section(content, "Risks")
104
- .split(/\r?\n/)
105
- .filter(
106
- (line) =>
107
- line.startsWith("|") &&
108
- !line.includes("---") &&
109
- !line.includes("| Risk |"),
110
- )
111
- .map((line) => line.split("|")[1].trim()),
45
+ scope_key: evaluatedRunId || runId || options.scopeKey || "unknown-scope",
46
+ scope_revision: evaluatedRevision || runRevision || "unversioned",
47
+ objective: section(content, "Objective").trim() || "Canonical Delivery Path Search input unavailable",
48
+ current_gate: gate.current_gate || runGate || "unknown",
49
+ allowed_actions: inputFailureCode ? [] : [...gate.allowed],
50
+ forbidden_actions: Array.isArray(gate.forbidden) ? [...gate.forbidden] : [],
51
+ evidence_refs: Array.isArray(gate.evidence_refs)
52
+ ? gate.evidence_refs.map((item) => `${item.evidence}: ${item.source}`)
53
+ : [],
54
+ risks: Array.isArray(run.risks) ? run.risks.map((item) => item.risk).filter(Boolean) : [],
112
55
  enforcement: options.enforcement ?? {
113
56
  level: "instruction_only",
114
57
  evidence: ["surface instruction prohibits implementation during search"],
@@ -122,6 +65,11 @@ export function searchInputFromControl(targetDir, options = {}) {
122
65
  stability_window: options.stabilityWindow ?? 3,
123
66
  },
124
67
  };
68
+ if (inputFailureCode) {
69
+ result.input_failure_code = inputFailureCode;
70
+ result.input_failure_detail = run.resolution_error || (gate.blocking_reason !== "none" ? gate.blocking_reason : "") || "canonical input is not ready";
71
+ result.input_recovery_action = gate.next_allowed_action || "Run canonical AGDF gate-check and repair the selected control state.";
72
+ }
125
73
  if (options.generation?.enabled)
126
74
  result.generation = {
127
75
  enabled: true,
@@ -1,3 +1,5 @@
1
+ import { RUN_ID_PATTERN } from "./control-state/run-identity.js";
2
+
1
3
  const REQUIRED_GATES = ["UR", "PRD", "SD", "TP", "QA", "UAT"];
2
4
  const ARTEFACT_ORDER = ["UR", "PRD", "SD", "TP"];
3
5
  const OUTCOMES = new Set(["approve", "revise", "decline", "cancel", "no_response", "timeout", "empty", "invalid", "stale"]);
@@ -313,17 +315,37 @@ function operationalBreadcrumb(value, labels) {
313
315
  .join(labels.breadcrumbSeparator);
314
316
  }
315
317
 
318
+ export function validateOperationalStatusCardPreconditions(statusCard, {
319
+ registry,
320
+ humanPresentation,
321
+ } = {}) {
322
+ const errors = [];
323
+ if (!plainObject(statusCard)) errors.push("status_card_missing");
324
+ if (humanPresentation !== undefined && !plainObject(humanPresentation)) errors.push("human_presentation_missing");
325
+ if (plainObject(statusCard)) {
326
+ if (!String(statusCard.run_id ?? "").trim()) errors.push("run_id_missing");
327
+ if (!String(statusCard.current_gate ?? "").trim()) errors.push("current_gate_missing");
328
+ let locale = "";
329
+ try {
330
+ locale = resolvePresentationLocale(registry, statusCard.presentation_language);
331
+ } catch {
332
+ locale = "";
333
+ }
334
+ if (!locale) errors.push("locale_unresolved");
335
+ }
336
+ return Object.freeze({ valid: errors.length === 0, errors: Object.freeze(errors) });
337
+ }
338
+
316
339
  export function renderOperationalStatusCard(statusCard, {
317
340
  registry,
318
341
  humanPresentation,
319
342
  revisionId = "",
320
343
  } = {}) {
321
- if (!plainObject(statusCard) || !plainObject(humanPresentation)) return null;
344
+ if (!validateOperationalStatusCardPreconditions(statusCard, { registry, humanPresentation: humanPresentation ?? null }).valid) return null;
322
345
  const runId = String(statusCard.run_id ?? "").trim();
323
346
  const currentGate = String(statusCard.current_gate ?? "").trim();
324
347
  const revision = String(revisionId ?? "").trim() || "unversioned";
325
348
  const locale = resolvePresentationLocale(registry, statusCard.presentation_language);
326
- if (!runId || !currentGate || !locale) return null;
327
349
 
328
350
  const pack = localePack(registry, locale);
329
351
  const labels = pack.statusCard;
@@ -537,6 +559,37 @@ export function normalizeInteractionOutcome(value, { expectedApproval, stale = f
537
559
  return "invalid";
538
560
  }
539
561
 
562
+ export function validateApprovalOrientationPreconditions({
563
+ statusCard,
564
+ humanPresentation,
565
+ registry,
566
+ requestedLocale,
567
+ } = {}) {
568
+ const errors = [];
569
+ if (!plainObject(statusCard)) return Object.freeze({ valid: false, errors: Object.freeze(["status_card_missing"]) });
570
+ if (humanPresentation !== undefined && !plainObject(humanPresentation)) errors.push("human_presentation_missing");
571
+ const gate = String(statusCard.current_gate ?? "").trim();
572
+ const runId = String(statusCard.run_id ?? "").trim();
573
+ if (!REQUIRED_GATES.includes(gate)) errors.push("gate_not_user_gate");
574
+ if (!RUN_ID_PATTERN.test(runId)) errors.push("run_id_invalid");
575
+ if (statusCard.status !== "open") errors.push("status_not_open");
576
+ if (statusCard.missing_approval !== `Approval: ${gate}`) errors.push("missing_approval_mismatch");
577
+ const nextGate = String(statusCard.next_gate_after_approval ?? "none");
578
+ const nextUserGate = String(statusCard.next_user_gate ?? "").trim();
579
+ const userActionRequired = String(statusCard.user_action_required ?? "").trim();
580
+ const userActionSemanticsValid = (userActionRequired === "no" && nextUserGate === "none")
581
+ || (userActionRequired === "yes" && REQUIRED_GATES.includes(nextUserGate) && nextGate === nextUserGate);
582
+ if (!userActionSemanticsValid) errors.push("user_action_semantics_invalid");
583
+ let locale = "";
584
+ try {
585
+ locale = resolvePresentationLocale(registry, requestedLocale);
586
+ } catch {
587
+ locale = "";
588
+ }
589
+ if (!locale) errors.push("locale_unresolved");
590
+ return Object.freeze({ valid: errors.length === 0, errors: Object.freeze(errors) });
591
+ }
592
+
540
593
  export function buildApprovalOrientationSnapshot({
541
594
  ready = false,
542
595
  statusCard,
@@ -545,11 +598,11 @@ export function buildApprovalOrientationSnapshot({
545
598
  registry,
546
599
  requestedLocale,
547
600
  } = {}) {
548
- if (!ready || !plainObject(statusCard) || !plainObject(humanPresentation)) return null;
601
+ if (!ready) return null;
602
+ if (!validateApprovalOrientationPreconditions({ statusCard, humanPresentation: humanPresentation ?? null, registry, requestedLocale }).valid) return null;
549
603
  const gate = String(statusCard.current_gate ?? "").trim();
550
604
  const runId = String(statusCard.run_id ?? "").trim();
551
605
  const expectedApproval = `Approval: ${gate}`;
552
- if (!REQUIRED_GATES.includes(gate) || !/^[A-Za-z0-9._-]+$/.test(runId) || statusCard.status !== "open" || statusCard.missing_approval !== expectedApproval) return null;
553
606
 
554
607
  const locale = resolvePresentationLocale(registry, requestedLocale);
555
608
  const pack = localePack(registry, locale);
@@ -560,9 +613,6 @@ export function buildApprovalOrientationSnapshot({
560
613
  const nextGate = String(statusCard.next_gate_after_approval ?? "none");
561
614
  const nextUserGate = String(statusCard.next_user_gate ?? "").trim();
562
615
  const userActionRequired = String(statusCard.user_action_required ?? "").trim();
563
- const userActionSemanticsValid = (userActionRequired === "no" && nextUserGate === "none")
564
- || (userActionRequired === "yes" && REQUIRED_GATES.includes(nextUserGate) && nextGate === nextUserGate);
565
- if (!userActionSemanticsValid) return null;
566
616
  const narration = pack.primary.narration.gates[gate] ?? {};
567
617
  const nextDecision = userActionRequired === "no"
568
618
  ? pack.primary.narration.noAction
@@ -137,6 +137,22 @@ export function validateDistributionProfiles(definition) {
137
137
  return { status: "matched", contract };
138
138
  }
139
139
 
140
+ function classifyInstalledHistoricalProfile(catalogue, definition, classifyHistoricalProfile) {
141
+ if (typeof classifyHistoricalProfile !== "function") {
142
+ return { status: "invalid", reason: "profile_history_invalid" };
143
+ }
144
+ const result = classifyHistoricalProfile({
145
+ catalogue,
146
+ version: definition?.version,
147
+ distributionProfiles: definition?.distributionProfiles,
148
+ });
149
+ if (result.status !== "matched") return result;
150
+ return {
151
+ ...result,
152
+ contract: catalogue.contracts[result.contract_id].distribution_profiles,
153
+ };
154
+ }
155
+
140
156
  export function inspectGeneratedRepositoryMarketplace(targetDir) {
141
157
  const marketplacePath = join(targetDir, ".agents", "plugins", "marketplace.json");
142
158
  if (!existsSync(marketplacePath)) return { status: "absent", marketplacePath };
@@ -196,11 +212,23 @@ export function inspectInstallationProvenance(pluginRoot, {
196
212
  allowLegacy = false,
197
213
  profileId = "runtime-plugin",
198
214
  inventoryDigest = null,
215
+ allowHistoricalProfilesForMigration = false,
216
+ distributionProfileHistory = null,
217
+ classifyHistoricalProfile = null,
199
218
  } = {}) {
200
219
  const markerPath = join(pluginRoot, INSTALLATION_PROVENANCE_FILE);
201
220
  const legacyPath = join(pluginRoot, LEGACY_LOCAL_INSTALL_FILE);
202
221
  if (!existsSync(markerPath)) {
203
222
  if (allowLegacy && existsSync(legacyPath)) {
223
+ const currentProfile = validateDistributionProfiles(definition);
224
+ if (Object.hasOwn(definition ?? {}, "distributionProfiles")
225
+ && currentProfile.status !== "matched") {
226
+ return allowHistoricalProfilesForMigration
227
+ ? classifyInstalledHistoricalProfile(distributionProfileHistory, definition, classifyHistoricalProfile).status === "matched"
228
+ ? { status: "invalid", reason: "installation_provenance_invalid" }
229
+ : classifyInstalledHistoricalProfile(distributionProfileHistory, definition, classifyHistoricalProfile)
230
+ : currentProfile;
231
+ }
204
232
  const marker = readJson(legacyPath);
205
233
  if (!marker
206
234
  || marker.schema_version !== 1
@@ -223,8 +251,14 @@ export function inspectInstallationProvenance(pluginRoot, {
223
251
  if (profile.status !== "matched") return profile;
224
252
  return { status: "missing", reason: "installation_provenance_missing" };
225
253
  }
226
- const profile = validateDistributionProfiles(definition);
227
- if (profile.status !== "matched") return profile;
254
+ const currentProfile = validateDistributionProfiles(definition);
255
+ const historicalProfile = currentProfile.status !== "matched" && allowHistoricalProfilesForMigration
256
+ ? classifyInstalledHistoricalProfile(distributionProfileHistory, definition, classifyHistoricalProfile)
257
+ : null;
258
+ if (currentProfile.status !== "matched" && historicalProfile?.status !== "matched") {
259
+ return historicalProfile ?? currentProfile;
260
+ }
261
+ const profile = currentProfile.status === "matched" ? currentProfile : historicalProfile;
228
262
  const marker = readJson(markerPath);
229
263
  if (!marker
230
264
  || marker.schema_version !== 1
@@ -247,7 +281,16 @@ export function inspectInstallationProvenance(pluginRoot, {
247
281
  if (observedSourceDigest !== marker.source_digest) {
248
282
  return { status: "invalid", reason: "source_digest_mismatch", marker, observedSourceDigest };
249
283
  }
250
- return { status: "matched", marker, observedSourceDigest };
284
+ return {
285
+ status: "matched",
286
+ marker,
287
+ observedSourceDigest,
288
+ profileClassification: historicalProfile ? "supported_historical" : "current",
289
+ historicalReleaseVersion: historicalProfile?.release_version ?? null,
290
+ historicalContractId: historicalProfile?.contract_id ?? null,
291
+ historicalContractDigest: historicalProfile?.contract_digest ?? null,
292
+ historicalEntryDigest: historicalProfile?.entry_digest ?? null,
293
+ };
251
294
  }
252
295
 
253
296
  export const distributionProfileContract = Object.freeze({
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agdf/local-validator-runtime",
3
- "version": "0.14.2",
3
+ "version": "0.14.4",
4
4
  "private": true,
5
5
  "type": "module"
6
6
  }
@@ -2,7 +2,7 @@
2
2
  "schema_version": "1",
3
3
  "generated": true,
4
4
  "source": "create-agdf/cli",
5
- "version": "0.14.2",
5
+ "version": "0.14.4",
6
6
  "entrypoint": "create-agdf/bin/agdf-validator.js",
7
- "digest": "51035c8735d47aa727980d82709326071b372464ae077a1d269aadc0d30f2624"
7
+ "digest": "68b83a6721a799c34067cdbcc0ed6664f7fbf89bce186a3d8de284b0959deac7"
8
8
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agdf",
3
- "version": "0.14.2",
3
+ "version": "0.14.4",
4
4
  "description": "Control layer for governed AI-assisted delivery.",
5
5
  "author": {
6
6
  "name": "Arndt Gold",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "id": "agdf",
4
- "version": "0.14.2",
4
+ "version": "0.14.4",
5
5
  "displayName": "AI Governance & Delivery Framework",
6
6
  "developerName": "Arndt Gold",
7
7
  "description": "Control layer for governed AI-assisted delivery.",
@@ -4,11 +4,24 @@
4
4
 
5
5
  Delivery Path Search is an optional read-only planning step for high-impact decisions with several materially different next actions.
6
6
 
7
- - It consumes current AGDF state and must reject gate-illegal candidates before model evaluation.
8
- - It returns one advisory recommendation or `no_safe_recommendation`.
7
+ - It consumes the canonical selected-run `gate-check` evaluation for run, revision, current gate and
8
+ legal actions. Persisted presentation Markdown such as the Run Status Card is never policy input.
9
+ - It binds the selected `scope_key`, `scope_revision` and objective to one control snapshot. A stale
10
+ or incomplete snapshot fails before candidate generation or evaluation.
11
+ - It reports the phase that actually ran: `input | candidate | evaluation | search`. A phase that
12
+ did not run cannot produce a later-phase conclusion.
13
+ - Terminal status is `input_unavailable | no_legal_candidates | evaluator_unavailable |
14
+ evaluator_error | recommendation | no_safe_recommendation`. Recommendation-facing statuses
15
+ require at least one contract-valid evaluation. `candidate_queue_exhausted` is a stopping reason,
16
+ not recommendation evidence.
17
+ - It reports baseline/generated/legal/rejected candidate counts and attempted/valid/invalid
18
+ evaluation counts. Zero valid evaluations must never be presented or persisted as an evaluated
19
+ recommendation conclusion.
9
20
  - It must report budgets, stopping reason and `full | tool_enforced | instruction_only` enforcement.
10
21
  - Model scores are judgements, not measurements.
11
22
  - Search output is evidence only. Canonical `gate-check` independently decides what may proceed.
23
+ - Search applies only to the selected run objective. An unrelated decision needs the correct
24
+ governed scope; Delivery Path Search is not a second task-target resolver.
12
25
  - The bounded first-release algorithm must not be labelled MCTS.
13
26
  - Surface adapters may translate transport and presentation, but must not fork scoring, search or gate semantics.
14
27
  - Optional AI-native candidate generation supplements the deterministic candidate baseline; it never replaces it.
@@ -19,6 +32,8 @@ Delivery Path Search is an optional read-only planning step for high-impact deci
19
32
  - Codex and Claude Code may provide tool-enforced generator transports. Copilot, OpenCode and generic surfaces remain instruction-only without conforming evidence.
20
33
  - OpenCode may provide a tool-enforced evaluator only for an invocation whose capability preflight proves `opencode run --pure --agent`, the owned evaluator agent and effective executable-tool denies. Preflight or transport failure must return `evaluator_unavailable` with `instruction_only` enforcement and point to the existing instruction-only workflow; it must not continue through a weaker subprocess.
21
34
  - OpenCode candidate generation remains unavailable.
35
+ - Persistence accepts only contract-valid `recommendation` or `no_safe_recommendation` results with
36
+ at least one valid evaluation. Input, candidate and evaluator failure results remain transient.
22
37
 
23
38
  The field names above are the stable machine-readable contract used by JSON
24
39
  reports and automation. Human-facing Markdown must present the same projection
@@ -154,8 +154,8 @@ required decision and one neutral instruction to choose approve, request revisio
154
154
  or decline. It does not contain the exact approval value or quality outlook. It uses localized human-readable labels
155
155
  and must not contain evidence, diagnostic codes, raw control-state keys,
156
156
  allowed/forbidden inventories or machine values. The complete Run Status Card remains the operational,
157
- CLI and audit projection and remains available for complete detail outside the
158
- approval-time compact view.
157
+ CLI and audit projection. Every ready-gate presentation renders the complete operational Run Status Card exactly once, between the compact approval-time Run Status Card and the Gate Transition Card, consumed verbatim from the code-owned status presentation; when it is undeliverable, the localized status-presentation failure line with its concrete diagnostic codes takes its place and the decision is still requested.
158
+ The exact-approval once-only requirement applies to the compact approval-time Run Status Card and the Gate Transition Card only; the complete Run Status Card may carry the missing approval as audit data.
159
159
 
160
160
  The first scan across both cards reveals the requested decision, readiness,
161
161
  required approval, approval effect, remaining boundary and next transition.