create-agdf 0.14.1 → 0.14.3

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 (128) hide show
  1. package/README.md +13 -3
  2. package/generated/.opencode/contracts/control-scaffold.md +17 -2
  3. package/generated/.opencode/skills/agdf-delivery-path-search/SKILL.md +16 -4
  4. package/generated/plugins/agdf/.claude-plugin/plugin.json +1 -1
  5. package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
  6. package/generated/plugins/agdf/meta/agdf-plugin.definition.json +6 -1
  7. package/generated/plugins/agdf/meta/contracts/control-scaffold.md +17 -2
  8. package/generated/plugins/agdf/meta/copilot-payload-baseline.json +7 -0
  9. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +6 -1
  10. package/generated/plugins/agdf/runtime/create-agdf/lib/cli/delivery-path-search-command.js +51 -19
  11. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/contracts.js +37 -0
  12. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/persistence.js +14 -2
  13. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/search-engine.js +126 -36
  14. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/state-adapter.js +40 -92
  15. package/generated/plugins/agdf/runtime/create-agdf/lib/runtime/local-validator.js +29 -17
  16. package/generated/plugins/agdf/runtime/create-agdf/lib/runtime/plugin-provenance.js +50 -1
  17. package/generated/plugins/agdf/runtime/create-agdf/package.json +1 -1
  18. package/generated/plugins/agdf/runtime/runtime-manifest.json +2 -2
  19. package/generated/plugins/agdf/scripts/check-runtime-integrity.mjs +2 -1
  20. package/generated/plugins/agdf/skills/delivery-path-search/SKILL.md +16 -4
  21. package/generated/plugins/agdf/submission/openai/availability.md +1 -1
  22. package/generated/plugins/agdf/submission/openai/capability-matrix.json +1 -1
  23. package/generated/plugins/agdf/submission/openai/release-notes.md +7 -1
  24. package/generated/plugins/agdf/submission/openai/reviewer-cases.json +1 -1
  25. package/generated/plugins/copilot/agdf/.agdf-payload-inventory.json +771 -0
  26. package/generated/plugins/copilot/agdf/LICENSE +201 -0
  27. package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-delivery-path-search/SKILL.md +16 -4
  28. package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/contracts/control-scaffold.md +17 -2
  29. package/generated/plugins/copilot/agdf/meta/agdf-agent-router.md +129 -0
  30. package/generated/plugins/copilot/agdf/meta/agdf-constitution.md +329 -0
  31. package/generated/plugins/copilot/agdf/meta/agdf-plugin.definition.json +331 -0
  32. package/generated/plugins/copilot/agdf/meta/agdf-runtime-contract.md +16 -0
  33. package/generated/plugins/{agdf → copilot/agdf}/plugin.json +1 -1
  34. package/generated/plugins/copilot/agdf/runtime/agdf-local.js +18 -0
  35. package/generated/plugins/copilot/agdf/runtime/agdf-session-check.js +97 -0
  36. package/generated/plugins/copilot/agdf/runtime/create-agdf/NOTICE +10 -0
  37. package/generated/plugins/copilot/agdf/runtime/create-agdf/bin/agdf-validator.js +11 -0
  38. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-interaction-locales.json +512 -0
  39. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +331 -0
  40. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/command-registry.js +144 -0
  41. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/delivery-path-search-command.js +171 -0
  42. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/parse-args.js +173 -0
  43. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/runtime-context.js +83 -0
  44. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/validation-handlers.js +46 -0
  45. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/delivery-map.js +301 -0
  46. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/doctor.js +272 -0
  47. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/gate-check.js +455 -0
  48. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/gate-policy.js +305 -0
  49. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/parent-reconciliation.js +284 -0
  50. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/required-files.js +9 -0
  51. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/run-state.js +217 -0
  52. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/shared.js +345 -0
  53. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/verified-change.js +156 -0
  54. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/aggregate.js +18 -0
  55. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/gate-approval-validator.js +49 -0
  56. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/index.js +7 -0
  57. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/legacy-migration.js +95 -0
  58. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/run-state-parser.js +265 -0
  59. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/run-state-repository.js +68 -0
  60. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/run-state-resolver.js +45 -0
  61. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/run-state-writer.js +97 -0
  62. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/candidate-policy.js +99 -0
  63. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/contracts.js +274 -0
  64. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/claude.js +35 -0
  65. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/codex.js +44 -0
  66. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/opencode.js +234 -0
  67. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/prompt.js +13 -0
  68. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/protocol.js +13 -0
  69. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/generators/claude.js +40 -0
  70. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/generators/codex.js +38 -0
  71. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/generators/protocol.js +12 -0
  72. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/persistence.js +51 -0
  73. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/scoring.js +21 -0
  74. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/search-engine.js +245 -0
  75. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/state-adapter.js +109 -0
  76. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/surfaces/capabilities.js +27 -0
  77. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/transports/read-only-guard.js +43 -0
  78. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/interaction-presentation.js +842 -0
  79. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/runtime/local-validator.js +239 -0
  80. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/runtime/plugin-provenance.js +255 -0
  81. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/runtime/validator-application.js +40 -0
  82. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/runtime-check-consent/contract.js +73 -0
  83. package/generated/plugins/copilot/agdf/runtime/create-agdf/package.json +6 -0
  84. package/generated/plugins/copilot/agdf/runtime/runtime-manifest.json +8 -0
  85. package/generated/submissions/openai/agdf/.codex-plugin/plugin.json +1 -1
  86. package/generated/submissions/openai/agdf/meta/agdf-plugin.definition.json +6 -1
  87. package/generated/submissions/openai/agdf/meta/contracts/control-scaffold.md +17 -2
  88. package/generated/submissions/openai/agdf/meta/copilot-payload-baseline.json +7 -0
  89. package/generated/submissions/openai/agdf/skills/delivery-path-search/SKILL.md +16 -4
  90. package/generated/submissions/openai/agdf/submission/openai/availability.md +1 -1
  91. package/generated/submissions/openai/agdf/submission/openai/capability-matrix.json +1 -1
  92. package/generated/submissions/openai/agdf/submission/openai/inventory.json +15 -11
  93. package/generated/submissions/openai/agdf/submission/openai/listing.json +1 -1
  94. package/generated/submissions/openai/agdf/submission/openai/readiness.json +2 -2
  95. package/generated/submissions/openai/agdf/submission/openai/readiness.md +2 -2
  96. package/generated/submissions/openai/agdf/submission/openai/release-notes.md +7 -1
  97. package/generated/submissions/openai/agdf/submission/openai/reviewer-cases.json +1 -1
  98. package/lib/cli/delivery-path-search-command.js +51 -19
  99. package/lib/delivery-path-search/contracts.js +37 -0
  100. package/lib/delivery-path-search/persistence.js +14 -2
  101. package/lib/delivery-path-search/search-engine.js +126 -36
  102. package/lib/delivery-path-search/state-adapter.js +40 -92
  103. package/lib/installers/local-marketplace.js +112 -30
  104. package/lib/installers/plugin-installers.js +45 -9
  105. package/lib/public-plugin/copilot-profile.js +126 -0
  106. package/lib/release/version-coherence.js +4 -1
  107. package/lib/runtime/local-validator.js +29 -17
  108. package/lib/runtime/plugin-provenance.js +50 -1
  109. package/package.json +3 -2
  110. /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-brownfield-analysis/SKILL.md +0 -0
  111. /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-clean-implementation-review/SKILL.md +0 -0
  112. /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-code-review/SKILL.md +0 -0
  113. /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-delivery-closeout/SKILL.md +0 -0
  114. /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-gate-check/SKILL.md +0 -0
  115. /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-interaction-locales.json +0 -0
  116. /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-qa-gate/SKILL.md +0 -0
  117. /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-release-or/SKILL.md +0 -0
  118. /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-runtime-contract.md +0 -0
  119. /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-task-plan-review/SKILL.md +0 -0
  120. /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-ux-intent-definition/SKILL.md +0 -0
  121. /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/contracts/closeout.md +0 -0
  122. /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/contracts/context-graph.md +0 -0
  123. /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/contracts/gate-transition.md +0 -0
  124. /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/contracts/interaction.md +0 -0
  125. /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/contracts/modes.md +0 -0
  126. /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/contracts/quality.md +0 -0
  127. /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/contracts/task-target-resolution.md +0 -0
  128. /package/generated/plugins/{agdf → copilot/agdf}/hooks/copilot-hooks.json +0 -0
@@ -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,
@@ -5,6 +5,7 @@ import process from "node:process";
5
5
  import {
6
6
  INSTALLATION_PROVENANCE_FILE,
7
7
  digestDirectory,
8
+ inspectCopilotPayloadInventory,
8
9
  inspectGeneratedRepositoryMarketplace,
9
10
  inspectInstallationProvenance,
10
11
  validateDistributionProfiles,
@@ -83,41 +84,52 @@ export function resolveLocalValidator(options) {
83
84
  }
84
85
  const codexManifest = readJson(join(pluginRoot, ".codex-plugin", "plugin.json"));
85
86
  const claudeManifest = readJson(join(pluginRoot, ".claude-plugin", "plugin.json"));
86
- const pluginVersion = codexManifest?.version ?? null;
87
- if (!codexManifest || !claudeManifest || claudeManifest.version !== definition.version) {
88
- return { envelope: envelope("version_mismatch", options, { source: "plugin_bundle", pluginRoot, pluginVersion, canonicalVersion: definition.version, distributionProfile: "runtime-plugin", provenanceStatus: "invalid", reason: "manifest_invalid" }) };
87
+ const copilotManifest = readJson(join(pluginRoot, "plugin.json"));
88
+ const copilotProfile = Boolean(copilotManifest && !codexManifest && !claudeManifest);
89
+ const distributionProfile = copilotProfile ? "copilot-runtime-plugin" : "runtime-plugin";
90
+ const pluginVersion = copilotProfile ? copilotManifest?.version ?? null : codexManifest?.version ?? null;
91
+ if (copilotProfile
92
+ ? copilotManifest.version !== definition.version
93
+ : (!codexManifest || !claudeManifest || claudeManifest.version !== definition.version)) {
94
+ return { envelope: envelope("version_mismatch", options, { source: "plugin_bundle", pluginRoot, pluginVersion, canonicalVersion: definition.version, distributionProfile, provenanceStatus: "invalid", reason: "manifest_invalid" }) };
95
+ }
96
+ const copilotInventory = copilotProfile ? inspectCopilotPayloadInventory(pluginRoot, definition.version) : null;
97
+ if (copilotProfile && copilotInventory.status !== "matched") {
98
+ return { envelope: envelope("version_mismatch", options, { source: "plugin_bundle", pluginRoot, pluginVersion, canonicalVersion: definition.version, distributionProfile, provenanceStatus: "invalid", reason: copilotInventory.reason }) };
89
99
  }
90
100
  const packageRoot = join(options.runtimeRoot, "create-agdf");
91
101
  if (!existsSync(packageRoot)) {
92
- return { envelope: envelope("unavailable", options, { source: "plugin_bundle", pluginRoot, pluginVersion, distributionProfile: "runtime-plugin", reason: "runtime_missing" }) };
102
+ return { envelope: envelope("unavailable", options, { source: "plugin_bundle", pluginRoot, pluginVersion, distributionProfile, reason: "runtime_missing" }) };
93
103
  }
94
104
  const packageManifest = readJson(join(packageRoot, "package.json"));
95
105
  const observedVersion = packageManifest?.version ?? manifest.version ?? null;
96
106
  if (observedVersion !== options.expectedVersion || manifest.version !== options.expectedVersion) {
97
- return { envelope: envelope("version_mismatch", options, { observedVersion, source: "plugin_bundle", pluginRoot, pluginVersion, canonicalVersion: definition.version, distributionProfile: "runtime-plugin" }) };
107
+ return { envelope: envelope("version_mismatch", options, { observedVersion, source: "plugin_bundle", pluginRoot, pluginVersion, canonicalVersion: definition.version, distributionProfile }) };
98
108
  }
99
109
  let observedDigest = null;
100
110
  try { observedDigest = digestDirectory(packageRoot); } catch {}
101
111
  if (!observedDigest || manifest.digest !== observedDigest) {
102
- return { envelope: envelope("version_mismatch", options, { observedVersion, source: "plugin_bundle", pluginRoot, pluginVersion, canonicalVersion: definition.version, runtimeDigest: observedDigest, distributionProfile: "runtime-plugin", reason: "runtime_digest_mismatch" }) };
112
+ return { envelope: envelope("version_mismatch", options, { observedVersion, source: "plugin_bundle", pluginRoot, pluginVersion, canonicalVersion: definition.version, runtimeDigest: observedDigest, distributionProfile, reason: "runtime_digest_mismatch" }) };
103
113
  }
104
114
  const entrypoint = resolve(options.runtimeRoot, manifest.entrypoint ?? "");
105
115
  const entrypointRelative = relative(options.runtimeRoot, entrypoint);
106
116
  if (!manifest.entrypoint || entrypointRelative === ".." || entrypointRelative.startsWith(`..${sep}`) || isAbsolute(entrypointRelative)) {
107
- return { envelope: envelope("version_mismatch", options, { observedVersion, source: "plugin_bundle", pluginRoot, pluginVersion, canonicalVersion: definition.version, runtimeDigest: observedDigest, distributionProfile: "runtime-plugin", reason: "invalid_entrypoint" }) };
117
+ return { envelope: envelope("version_mismatch", options, { observedVersion, source: "plugin_bundle", pluginRoot, pluginVersion, canonicalVersion: definition.version, runtimeDigest: observedDigest, distributionProfile, reason: "invalid_entrypoint" }) };
108
118
  }
109
119
  if (!existsSync(entrypoint)) {
110
- return { envelope: envelope("unavailable", options, { observedVersion, source: "plugin_bundle", pluginRoot, pluginVersion, canonicalVersion: definition.version, runtimeDigest: observedDigest, distributionProfile: "runtime-plugin", reason: "entrypoint_missing" }) };
120
+ return { envelope: envelope("unavailable", options, { observedVersion, source: "plugin_bundle", pluginRoot, pluginVersion, canonicalVersion: definition.version, runtimeDigest: observedDigest, distributionProfile, reason: "entrypoint_missing" }) };
111
121
  }
112
122
  const installed = existsSync(join(pluginRoot, INSTALLATION_PROVENANCE_FILE));
113
123
  let provenanceStatus = "not_applicable";
114
124
  let sourceDigest = null;
115
- let evidencePlane = "loaded_session";
125
+ let evidencePlane = options.expectedPluginRoot ? "loaded_session" : "installed_plugin_root";
116
126
  if (installed) {
117
127
  const provenance = inspectInstallationProvenance(pluginRoot, {
118
128
  definition,
119
129
  runtimeManifest: manifest,
120
130
  pluginVersion,
131
+ profileId: distributionProfile,
132
+ inventoryDigest: copilotInventory?.inventoryDigest ?? null,
121
133
  });
122
134
  provenanceStatus = provenance.status;
123
135
  sourceDigest = provenance.marker?.source_digest ?? null;
@@ -130,8 +142,8 @@ export function resolveLocalValidator(options) {
130
142
  canonicalVersion: definition.version,
131
143
  runtimeDigest: observedDigest,
132
144
  sourceDigest,
133
- distributionProfile: "runtime-plugin",
134
- evidencePlane: "loaded_session",
145
+ distributionProfile,
146
+ evidencePlane,
135
147
  provenanceStatus,
136
148
  reason: provenance.reason,
137
149
  }) };
@@ -144,16 +156,16 @@ export function resolveLocalValidator(options) {
144
156
  pluginVersion,
145
157
  canonicalVersion: definition.version,
146
158
  runtimeDigest: observedDigest,
147
- distributionProfile: "runtime-plugin",
159
+ distributionProfile,
148
160
  evidencePlane: "loaded_session",
149
161
  provenanceStatus: "missing",
150
162
  reason: "installation_provenance_missing",
151
163
  }) };
152
164
  } else {
153
- const generatedRepository = inspectGeneratedRepositoryMarketplace(resolve(pluginRoot, "..", ".."));
165
+ const generatedRepository = copilotProfile ? null : inspectGeneratedRepositoryMarketplace(resolve(pluginRoot, "..", ".."));
154
166
  if (pluginVersion !== definition.version
155
- || generatedRepository.status !== "matched"
156
- || canonicalPath(generatedRepository.pluginRoot) !== pluginRoot) {
167
+ || (!copilotProfile && (generatedRepository.status !== "matched"
168
+ || canonicalPath(generatedRepository.pluginRoot) !== pluginRoot))) {
157
169
  return { envelope: envelope("unavailable", options, {
158
170
  observedVersion,
159
171
  source: "plugin_bundle",
@@ -161,7 +173,7 @@ export function resolveLocalValidator(options) {
161
173
  pluginVersion,
162
174
  canonicalVersion: definition.version,
163
175
  runtimeDigest: observedDigest,
164
- distributionProfile: "runtime-plugin",
176
+ distributionProfile,
165
177
  evidencePlane: "installed_plugin_root",
166
178
  provenanceStatus: "missing",
167
179
  reason: "installation_provenance_missing",
@@ -178,7 +190,7 @@ export function resolveLocalValidator(options) {
178
190
  canonicalVersion: definition.version,
179
191
  runtimeDigest: observedDigest,
180
192
  sourceDigest,
181
- distributionProfile: "runtime-plugin",
193
+ distributionProfile,
182
194
  evidencePlane,
183
195
  provenanceStatus,
184
196
  }),
@@ -4,10 +4,12 @@ import { isAbsolute, join, relative, resolve, sep } from "node:path";
4
4
 
5
5
  export const INSTALLATION_PROVENANCE_FILE = ".agdf-installation.json";
6
6
  export const LEGACY_LOCAL_INSTALL_FILE = ".agdf-local-install.json";
7
+ export const COPILOT_PAYLOAD_INVENTORY_FILE = ".agdf-payload-inventory.json";
7
8
 
8
9
  const EXPECTED_PROFILES = Object.freeze({
9
10
  "source-development": Object.freeze({ runtime: "absent", installable: false, machineValidation: "unavailable" }),
10
11
  "runtime-plugin": Object.freeze({ runtime: "required", installable: true, machineValidation: "local_exact_version_digest" }),
12
+ "copilot-runtime-plugin": Object.freeze({ runtime: "required", installable: true, machineValidation: "local_exact_version_digest_inventory" }),
11
13
  "opencode-config-local": Object.freeze({ runtime: "config_local_package", installable: true, machineValidation: "local_exact_version" }),
12
14
  "portable-skills": Object.freeze({ runtime: "absent", installable: true, machineValidation: "unavailable_or_external_required" }),
13
15
  });
@@ -41,6 +43,50 @@ export function digestDirectory(root) {
41
43
  return hash.digest("hex");
42
44
  }
43
45
 
46
+ export function digestFile(path) {
47
+ return createHash("sha256").update(readFileSync(path)).digest("hex");
48
+ }
49
+
50
+ export function inspectCopilotPayloadInventory(pluginRoot, expectedVersion) {
51
+ const inventoryPath = join(pluginRoot, COPILOT_PAYLOAD_INVENTORY_FILE);
52
+ const inventory = readJson(inventoryPath);
53
+ if (!inventory || inventory.schema_version !== 1 || inventory.profile_id !== "copilot-runtime-plugin"
54
+ || inventory.version !== expectedVersion || !Array.isArray(inventory.entries)) {
55
+ return { status: "invalid", reason: "copilot_payload_inventory_invalid" };
56
+ }
57
+ const destinations = inventory.entries.map((entry) => entry.destination);
58
+ if (new Set(destinations).size !== destinations.length) {
59
+ return { status: "invalid", reason: "copilot_payload_inventory_duplicate" };
60
+ }
61
+ const actual = [];
62
+ function visit(directory) {
63
+ for (const name of readdirSync(directory).sort()) {
64
+ const path = join(directory, name);
65
+ const stats = statSync(path);
66
+ if (stats.isDirectory()) visit(path);
67
+ else if (stats.isFile()) {
68
+ const normalized = relative(pluginRoot, path).replaceAll("\\", "/");
69
+ if (![COPILOT_PAYLOAD_INVENTORY_FILE, INSTALLATION_PROVENANCE_FILE, LEGACY_LOCAL_INSTALL_FILE].includes(normalized)) actual.push(normalized);
70
+ }
71
+ }
72
+ }
73
+ try { visit(pluginRoot); } catch { return { status: "invalid", reason: "copilot_payload_incomplete" }; }
74
+ if (JSON.stringify(actual.sort()) !== JSON.stringify([...destinations].sort())) {
75
+ return { status: "invalid", reason: "copilot_payload_inventory_mismatch" };
76
+ }
77
+ for (const entry of inventory.entries) {
78
+ const path = join(pluginRoot, entry.destination);
79
+ if (!existsSync(path) || digestFile(path) !== entry.digest || statSync(path).size !== entry.bytes) {
80
+ return { status: "invalid", reason: "copilot_payload_digest_mismatch", entry: entry.destination };
81
+ }
82
+ }
83
+ const excluded = actual.some((path) => path.startsWith("skills/") || path.startsWith(".codex-plugin/")
84
+ || path.startsWith(".claude-plugin/") || path.startsWith("submission/")
85
+ || path === "hooks/hooks.json" || path === "hooks/session-start.sh");
86
+ if (excluded) return { status: "invalid", reason: "copilot_payload_excluded_surface" };
87
+ return { status: "matched", inventory, inventoryDigest: digestFile(inventoryPath) };
88
+ }
89
+
44
90
  export function digestNormalizedPluginSource(root, canonicalVersion) {
45
91
  const files = [];
46
92
  function visit(directory) {
@@ -148,6 +194,8 @@ export function inspectInstallationProvenance(pluginRoot, {
148
194
  runtimeManifest,
149
195
  pluginVersion,
150
196
  allowLegacy = false,
197
+ profileId = "runtime-plugin",
198
+ inventoryDigest = null,
151
199
  } = {}) {
152
200
  const markerPath = join(pluginRoot, INSTALLATION_PROVENANCE_FILE);
153
201
  const legacyPath = join(pluginRoot, LEGACY_LOCAL_INSTALL_FILE);
@@ -181,11 +229,12 @@ export function inspectInstallationProvenance(pluginRoot, {
181
229
  if (!marker
182
230
  || marker.schema_version !== 1
183
231
  || marker.owner !== "create-agdf"
184
- || marker.profile_id !== "runtime-plugin"
232
+ || marker.profile_id !== profileId
185
233
  || marker.marketplace_id !== profile.contract.marketplaceIdentities.durable
186
234
  || marker.canonical_version !== definition.version
187
235
  || marker.codex_install_version !== pluginVersion
188
236
  || marker.runtime_digest !== runtimeManifest?.digest
237
+ || (profileId === "copilot-runtime-plugin" && marker.inventory_digest !== inventoryDigest)
189
238
  || !/^[a-f0-9]{64}$/.test(marker.source_digest ?? "")) {
190
239
  return { status: "invalid", reason: "installation_provenance_invalid", marker };
191
240
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agdf/local-validator-runtime",
3
- "version": "0.14.1",
3
+ "version": "0.14.3",
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.1",
5
+ "version": "0.14.3",
6
6
  "entrypoint": "create-agdf/bin/agdf-validator.js",
7
- "digest": "724174653ad423b30654aa64a34e1699a18cc67037172c10aa249d1d61087116"
7
+ "digest": "8f6dbd5e647aff7dfc1405f4e54a5b135a8d820858b5f8f85873964f6eb2f7cf"
8
8
  }
@@ -681,6 +681,7 @@ if (pluginDefinition) {
681
681
  const expectedProfiles = {
682
682
  "source-development": { runtime: "absent", installable: false, machineValidation: "unavailable" },
683
683
  "runtime-plugin": { runtime: "required", installable: true, machineValidation: "local_exact_version_digest" },
684
+ "copilot-runtime-plugin": { runtime: "required", installable: true, machineValidation: "local_exact_version_digest_inventory" },
684
685
  "opencode-config-local": { runtime: "config_local_package", installable: true, machineValidation: "local_exact_version" },
685
686
  "portable-skills": { runtime: "absent", installable: true, machineValidation: "unavailable_or_external_required" },
686
687
  };
@@ -888,7 +889,7 @@ if (pluginDefinition) {
888
889
  }
889
890
  }
890
891
 
891
- if (!sourceMode) {
892
+ if (!sourceMode && installationProvenance?.profile_id === "copilot-runtime-plugin") {
892
893
  const copilotPlugin = isFile(copilotPluginPath) ? readJson(copilotPluginPath, "Copilot plugin manifest") : null;
893
894
  const copilotHooks = isFile(copilotHooksPath) ? readJson(copilotHooksPath, "Copilot plugin hooks") : null;
894
895
  if (!copilotPlugin) failures.push("installed runtime plugin must include root plugin.json for GitHub Copilot");