create-agdf 0.11.3 → 0.12.0

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 (80) hide show
  1. package/README.md +5 -4
  2. package/generated/.agdf/control/templates/MASTER_BACKLOG.md +1 -1
  3. package/generated/.agdf/control/templates/artefacts/BROWNFIELD_REVIEW.md +25 -0
  4. package/generated/.github/skills/agdf-brownfield-analysis/SKILL.md +26 -0
  5. package/generated/.github/skills/agdf-gate-check/SKILL.md +40 -18
  6. package/generated/.github/skills/agdf-interaction-locales.json +50 -0
  7. package/generated/.github/skills/agdf-runtime-contract.md +1 -0
  8. package/generated/.github/skills/contracts/control-scaffold.md +2 -0
  9. package/generated/.github/skills/contracts/gate-transition.md +7 -3
  10. package/generated/.github/skills/contracts/interaction.md +22 -0
  11. package/generated/.github/skills/contracts/modes.md +81 -0
  12. package/generated/.github/skills/contracts/task-target-resolution.md +102 -0
  13. package/generated/.opencode/AGDF.md +2 -0
  14. package/generated/.opencode/agdf-interaction-locales.json +50 -0
  15. package/generated/.opencode/agdf-runtime-contract.md +1 -0
  16. package/generated/.opencode/contracts/control-scaffold.md +2 -0
  17. package/generated/.opencode/contracts/gate-transition.md +7 -3
  18. package/generated/.opencode/contracts/interaction.md +22 -0
  19. package/generated/.opencode/contracts/modes.md +81 -0
  20. package/generated/.opencode/contracts/task-target-resolution.md +102 -0
  21. package/generated/.opencode/skills/agdf-brownfield-analysis/SKILL.md +26 -0
  22. package/generated/.opencode/skills/agdf-gate-check/SKILL.md +40 -18
  23. package/generated/AGENTS.md +1 -0
  24. package/generated/plugins/agdf/.claude-plugin/plugin.json +1 -1
  25. package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
  26. package/generated/plugins/agdf/control/templates/MASTER_BACKLOG.md +1 -1
  27. package/generated/plugins/agdf/control/templates/artefacts/BROWNFIELD_REVIEW.md +25 -0
  28. package/generated/plugins/agdf/meta/agdf-agent-router.md +16 -0
  29. package/generated/plugins/agdf/meta/agdf-interaction-locales.json +50 -0
  30. package/generated/plugins/agdf/meta/agdf-plugin.definition.json +2 -1
  31. package/generated/plugins/agdf/meta/agdf-runtime-contract.md +1 -0
  32. package/generated/plugins/agdf/meta/contracts/control-scaffold.md +2 -0
  33. package/generated/plugins/agdf/meta/contracts/gate-transition.md +7 -3
  34. package/generated/plugins/agdf/meta/contracts/interaction.md +22 -0
  35. package/generated/plugins/agdf/meta/contracts/modes.md +81 -0
  36. package/generated/plugins/agdf/meta/contracts/task-target-resolution.md +102 -0
  37. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-interaction-locales.json +50 -0
  38. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +2 -1
  39. package/generated/plugins/agdf/runtime/create-agdf/lib/cli/command-registry.js +2 -2
  40. package/generated/plugins/agdf/runtime/create-agdf/lib/cli/delivery-path-search-command.js +78 -14
  41. package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/gate-policy.js +14 -7
  42. package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/shared.js +1 -0
  43. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/claude.js +2 -11
  44. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/codex.js +2 -11
  45. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/opencode.js +234 -0
  46. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/prompt.js +13 -0
  47. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/search-engine.js +1 -0
  48. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/surfaces/capabilities.js +15 -2
  49. package/generated/plugins/agdf/runtime/create-agdf/lib/interaction-presentation.js +85 -0
  50. package/generated/plugins/agdf/runtime/create-agdf/package.json +6 -2
  51. package/generated/plugins/agdf/runtime/runtime-manifest.json +2 -2
  52. package/generated/plugins/agdf/scripts/check-runtime-integrity.mjs +119 -3
  53. package/generated/plugins/agdf/skills/brownfield-analysis/SKILL.md +26 -0
  54. package/generated/plugins/agdf/skills/gate-check/SKILL.md +40 -18
  55. package/lib/cli/application.js +29 -3
  56. package/lib/cli/command-registry.js +2 -2
  57. package/lib/cli/delivery-path-search-command.js +78 -14
  58. package/lib/control-evaluation/gate-policy.js +14 -7
  59. package/lib/control-evaluation/shared.js +1 -0
  60. package/lib/delivery-path-search/evaluators/claude.js +2 -11
  61. package/lib/delivery-path-search/evaluators/codex.js +2 -11
  62. package/lib/delivery-path-search/evaluators/opencode.js +234 -0
  63. package/lib/delivery-path-search/evaluators/prompt.js +13 -0
  64. package/lib/delivery-path-search/search-engine.js +1 -0
  65. package/lib/delivery-path-search/surfaces/capabilities.js +15 -2
  66. package/lib/installers/opencode.js +341 -10
  67. package/lib/interaction-presentation.js +85 -0
  68. package/lib/lifecycle/operations.js +4 -0
  69. package/lib/live-agent/read-only-structured.js +43 -0
  70. package/lib/proportionality-benchmark/blind-prompt.js +37 -0
  71. package/lib/proportionality-benchmark/contracts.js +120 -0
  72. package/lib/proportionality-benchmark/corpus-loader.js +110 -0
  73. package/lib/proportionality-benchmark/evaluator.js +250 -0
  74. package/lib/proportionality-benchmark/index.js +7 -0
  75. package/lib/proportionality-benchmark/live-recorder.js +100 -0
  76. package/lib/proportionality-benchmark/report.js +51 -0
  77. package/lib/proportionality-benchmark/source-fingerprint.js +30 -0
  78. package/lib/skill-evals/live-recorder.js +3 -26
  79. package/opencode-plugin.js +59 -3
  80. package/package.json +6 -2
@@ -0,0 +1,234 @@
1
+ import { execFileSync, spawnSync } from "node:child_process";
2
+ import process from "node:process";
3
+ import { validateEvaluation } from "../contracts.js";
4
+ import { guardedExecFileSync } from "../transports/read-only-guard.js";
5
+ import { buildEvaluatorPrompt } from "./prompt.js";
6
+
7
+ export const OPENCODE_EVALUATOR_AGENT = "agdf-evaluator";
8
+
9
+ export const OPENCODE_DENY_PERMISSIONS = Object.freeze({
10
+ "*": "deny",
11
+ read: "deny",
12
+ edit: "deny",
13
+ bash: "deny",
14
+ task: "deny",
15
+ webfetch: "deny",
16
+ websearch: "deny",
17
+ skill: "deny",
18
+ lsp: "deny",
19
+ todowrite: "deny",
20
+ question: "deny",
21
+ external_directory: "deny",
22
+ });
23
+
24
+ const executablePermissions = Object.freeze([
25
+ "edit", "bash", "task", "webfetch", "websearch", "skill", "lsp", "todowrite", "question",
26
+ ]);
27
+
28
+ export function openCodePermissionEnvironment(env = process.env) {
29
+ return {
30
+ ...env,
31
+ OPENCODE_PERMISSION: JSON.stringify(OPENCODE_DENY_PERMISSIONS),
32
+ };
33
+ }
34
+
35
+ function preflightFailure(code, detail, evidence = []) {
36
+ return {
37
+ surface: "opencode",
38
+ status: "failed",
39
+ code,
40
+ detail,
41
+ evidence,
42
+ };
43
+ }
44
+
45
+ function extractAgentPermissions(output, agentName) {
46
+ const marker = `${agentName} (`;
47
+ const lines = output.split(/\r?\n/);
48
+ const start = lines.findIndex((line) => line.startsWith(marker));
49
+ if (start < 0) return null;
50
+ const mode = lines[start].slice(marker.length).split(")")[0];
51
+ let jsonStart = -1;
52
+ let depth = 0;
53
+ for (let index = start + 1; index < lines.length; index += 1) {
54
+ const line = lines[index].trim();
55
+ if (jsonStart < 0 && line === "[") {
56
+ jsonStart = index;
57
+ depth = 1;
58
+ continue;
59
+ }
60
+ if (jsonStart >= 0) {
61
+ for (const character of line) {
62
+ if (character === "[") depth += 1;
63
+ if (character === "]") depth -= 1;
64
+ }
65
+ if (depth === 0) return { mode, permissions: JSON.parse(lines.slice(jsonStart, index + 1).join("\n")) };
66
+ }
67
+ }
68
+ return null;
69
+ }
70
+
71
+ function terminalPermissionDecision(permissions, permissionName) {
72
+ let decision = null;
73
+ for (const entry of permissions) {
74
+ if (entry?.pattern === "*" && (entry.permission === "*" || entry.permission === permissionName)) {
75
+ decision = entry.action;
76
+ }
77
+ }
78
+ return decision;
79
+ }
80
+
81
+ export function preflightOpenCodeEvaluator(options = {}) {
82
+ const executable = options.openCodeBin ?? process.env.AGDF_OPENCODE_BIN ?? "opencode";
83
+ const run = options.execFileSync ?? execFileSync;
84
+ const cwd = options.cwd ?? process.cwd();
85
+ const agentName = options.agentName ?? OPENCODE_EVALUATOR_AGENT;
86
+ const env = openCodePermissionEnvironment(options.env ?? process.env);
87
+ let runtime;
88
+ let help;
89
+ let agents;
90
+ try {
91
+ runtime = run(executable, ["--version"], {
92
+ cwd, env, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 5000,
93
+ }).trim();
94
+ } catch (error) {
95
+ return preflightFailure("opencode_host_unavailable", (error.stderr || error.message).toString().trim());
96
+ }
97
+ try {
98
+ if (options.execFileSync) {
99
+ help = run(executable, ["run", "--help"], {
100
+ cwd, env, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 5000,
101
+ });
102
+ } else {
103
+ const helpProbe = (options.spawnSync ?? spawnSync)(executable, ["run", "--help"], {
104
+ cwd, env, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 5000,
105
+ });
106
+ if (helpProbe.error || helpProbe.status !== 0) throw helpProbe.error ?? new Error(helpProbe.stderr || "OpenCode run help failed.");
107
+ help = `${helpProbe.stdout ?? ""}\n${helpProbe.stderr ?? ""}`;
108
+ }
109
+ } catch (error) {
110
+ return preflightFailure("opencode_run_unavailable", (error.stderr || error.message).toString().trim(), [`opencode ${runtime}`]);
111
+ }
112
+ const requiredFlags = ["--pure", "--agent", "--format", "--dir"];
113
+ const missingFlags = requiredFlags.filter((flag) => !help.includes(flag));
114
+ if (missingFlags.length) {
115
+ return preflightFailure("opencode_run_capability_missing", `Missing required flags: ${missingFlags.join(", ")}`, [`opencode ${runtime}`]);
116
+ }
117
+ try {
118
+ agents = run(executable, ["agent", "list", "--pure"], {
119
+ cwd, env, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 10000,
120
+ });
121
+ } catch (error) {
122
+ return preflightFailure("opencode_agent_probe_failed", (error.stderr || error.message).toString().trim(), [`opencode ${runtime}`]);
123
+ }
124
+ let projection;
125
+ try {
126
+ projection = extractAgentPermissions(agents, agentName);
127
+ } catch (error) {
128
+ return preflightFailure("opencode_agent_permissions_unparseable", error.message, [`opencode ${runtime}`]);
129
+ }
130
+ if (!projection) {
131
+ return preflightFailure("opencode_evaluator_agent_missing", `OpenCode agent ${agentName} is not available.`, [`opencode ${runtime}`]);
132
+ }
133
+ if (projection.mode !== "primary") {
134
+ return preflightFailure("opencode_evaluator_agent_incompatible", `OpenCode agent ${agentName} must be primary for opencode run --agent; observed ${projection.mode || "unknown"}.`, [
135
+ `opencode ${runtime}`,
136
+ `agent:${agentName}`,
137
+ ]);
138
+ }
139
+ const unsafe = executablePermissions.filter((permission) => terminalPermissionDecision(projection.permissions, permission) !== "deny");
140
+ if (unsafe.length) {
141
+ return preflightFailure("opencode_evaluator_permissions_unsafe", `Terminal deny is not effective for: ${unsafe.join(", ")}`, [
142
+ `opencode ${runtime}`,
143
+ `agent:${agentName}`,
144
+ ]);
145
+ }
146
+ return {
147
+ surface: "opencode",
148
+ status: "passed",
149
+ code: "opencode_evaluator_ready",
150
+ invocation_id: `${process.pid}-${Date.now()}`,
151
+ runtime,
152
+ agent: agentName,
153
+ evidence: [
154
+ `opencode ${runtime}`,
155
+ "opencode run --pure --agent agdf-evaluator --format json",
156
+ "effective executable permissions terminal-deny",
157
+ ],
158
+ env,
159
+ };
160
+ }
161
+
162
+ export function parseOpenCodeEvaluatorOutput(raw) {
163
+ const text = [];
164
+ for (const line of raw.split(/\r?\n/).filter(Boolean)) {
165
+ const event = JSON.parse(line);
166
+ if (event?.type === "error") {
167
+ const status = event.error?.data?.statusCode;
168
+ const detail = event.error?.data?.message || event.error?.message || "unknown OpenCode evaluator error";
169
+ const error = new Error(`OpenCode evaluator error${status ? ` (${status})` : ""}: ${detail}`);
170
+ error.code = status === 401 || /auth|credential|provider available/i.test(detail)
171
+ ? "OPENCODE_EVALUATOR_AUTHENTICATION_FAILED"
172
+ : "OPENCODE_EVALUATOR_UNAVAILABLE";
173
+ throw error;
174
+ }
175
+ if (event?.part?.type === "text" && typeof event.part.text === "string") text.push(event.part.text);
176
+ else if (event?.type === "text" && typeof event.text === "string") text.push(event.text);
177
+ }
178
+ if (!text.length) throw new Error("OpenCode evaluator returned no text event.");
179
+ if (text.length !== 1) throw new Error(`OpenCode evaluator returned ${text.length} text events; exactly one final payload is required.`);
180
+ return JSON.parse(text[0]);
181
+ }
182
+
183
+ function fatalEvaluatorError(error) {
184
+ const wrapped = new Error(error.message);
185
+ wrapped.code = typeof error.code === "string" && error.code.startsWith("OPENCODE_EVALUATOR_")
186
+ ? error.code
187
+ : error.code === "GENERATOR_MUTATION_DETECTED"
188
+ ? "OPENCODE_EVALUATOR_MUTATION_DETECTED"
189
+ : error.code === "GENERATOR_TIMEOUT"
190
+ ? "OPENCODE_EVALUATOR_TIMEOUT"
191
+ : error.code === "ENOENT"
192
+ ? "OPENCODE_EVALUATOR_UNAVAILABLE"
193
+ : /auth|unauthorized|credential/i.test(`${error.message}\n${error.stderr ?? ""}`)
194
+ ? "OPENCODE_EVALUATOR_AUTHENTICATION_FAILED"
195
+ : "OPENCODE_EVALUATOR_OUTPUT_INVALID";
196
+ wrapped.fatalEvaluator = true;
197
+ return wrapped;
198
+ }
199
+
200
+ export function openCodeEvaluator(options = {}) {
201
+ if (options.preflight?.status !== "passed" || options.preflight.surface !== "opencode") {
202
+ throw new Error("OpenCode evaluator requires a successful current-invocation preflight.");
203
+ }
204
+ const executable = options.openCodeBin ?? process.env.AGDF_OPENCODE_BIN ?? "opencode";
205
+ const agentName = options.preflight.agent ?? OPENCODE_EVALUATOR_AGENT;
206
+ return {
207
+ name: "opencode",
208
+ metadata: {
209
+ name: "opencode",
210
+ runtime: options.preflight.runtime,
211
+ agent: agentName,
212
+ preflight: options.preflight.code,
213
+ },
214
+ async evaluate(input, candidate) {
215
+ const args = [
216
+ "run", "--pure", "--agent", agentName, "--format", "json", "--dir", options.cwd,
217
+ ];
218
+ if (options.model) args.push("--model", options.model);
219
+ args.push(buildEvaluatorPrompt(input, candidate));
220
+ try {
221
+ const raw = guardedExecFileSync(executable, args, {
222
+ cwd: options.cwd,
223
+ env: options.preflight.env,
224
+ encoding: "utf8",
225
+ stdio: ["ignore", "pipe", "pipe"],
226
+ timeout: options.timeoutMs ?? 120000,
227
+ });
228
+ return validateEvaluation(parseOpenCodeEvaluatorOutput(raw), candidate.id);
229
+ } catch (error) {
230
+ throw fatalEvaluatorError(error);
231
+ }
232
+ },
233
+ };
234
+ }
@@ -0,0 +1,13 @@
1
+ export function buildEvaluatorPrompt(input, candidate) {
2
+ return [
3
+ "You are an evaluation-only component. Do not use tools, execute commands, inspect files, delegate work or modify state.",
4
+ `Return only one JSON object matching contract version ${input.contract_version}.`,
5
+ `Objective: ${input.objective}`,
6
+ `Current gate: ${input.current_gate}`,
7
+ `Candidate id: ${candidate.id}`,
8
+ `Candidate action: ${candidate.action}`,
9
+ "Score scope_fit, gate_readiness, risk_reduction, evidence_gain, testability, reversibility and cost from 0 to 5.",
10
+ "Cost 0 is low and 5 is high. uncertainty is 0 low to 5 high.",
11
+ "Keep rationale concise. child_actions must only contain actions permitted by the current gate.",
12
+ ].join("\n");
13
+ }
@@ -114,6 +114,7 @@ export async function runDeliveryPathSearch(inputValue, evaluator, options = {})
114
114
  }
115
115
  }
116
116
  } catch (error) {
117
+ if (error?.fatalEvaluator === true) throw error;
117
118
  rejected.push({ candidate_id: candidate.id, action: candidate.action, reason: `invalid_evaluation: ${error.message}` });
118
119
  }
119
120
  }
@@ -1,6 +1,6 @@
1
1
  import { validateEnforcement } from "../contracts.js";
2
2
 
3
- export function enforcementForSurface(surface, evidence = []) {
3
+ export function enforcementForSurface(surface, evidence = [], context = {}) {
4
4
  const defaults = {
5
5
  codex: { level: "tool_enforced", evidence: ["codex exec --sandbox read-only --ephemeral"] },
6
6
  claude: { level: "tool_enforced", evidence: ["claude -p --disallowedTools Edit,Write,Bash"] },
@@ -9,6 +9,19 @@ export function enforcementForSurface(surface, evidence = []) {
9
9
  generic: { level: "instruction_only", evidence: [] },
10
10
  };
11
11
  const selected = structuredClone(defaults[surface] ?? defaults.generic);
12
- if (evidence.length) selected.evidence = evidence;
12
+ if (surface === "opencode") {
13
+ const preflight = context.preflight;
14
+ if (preflight?.surface === "opencode"
15
+ && preflight.status === "passed"
16
+ && typeof preflight.invocation_id === "string"
17
+ && preflight.invocation_id
18
+ && Array.isArray(preflight.evidence)
19
+ && preflight.evidence === evidence) {
20
+ selected.level = "tool_enforced";
21
+ selected.evidence = [...evidence];
22
+ }
23
+ } else if (evidence.length) {
24
+ selected.evidence = evidence;
25
+ }
13
26
  return validateEnforcement(selected);
14
27
  }
@@ -349,6 +349,91 @@ export function renderOperationalStatusCard(statusCard, {
349
349
  });
350
350
  }
351
351
 
352
+ export function renderTaskTargetOrientation(resolution, {
353
+ registry,
354
+ requestedLocale,
355
+ } = {}) {
356
+ if (!plainObject(resolution)) return null;
357
+
358
+ const resolutionState = String(resolution.resolution_state ?? "").trim();
359
+ const reasonCode = String(resolution.reason_code ?? "").trim();
360
+ const primaryTarget = String(resolution.primary_target ?? "").trim();
361
+ const governanceTarget = String(resolution.governance_target ?? "").trim();
362
+ const workingDirectory = String(resolution.working_directory ?? "").trim();
363
+ const nextAction = String(resolution.next_action ?? "").trim();
364
+ if (typeof resolution.target_changed !== "boolean") return null;
365
+ const targetChanged = resolution.target_changed === true;
366
+ const evidenceSources = Array.isArray(resolution.evidence_sources)
367
+ ? resolution.evidence_sources.map((item) => String(item ?? "").trim()).filter(Boolean)
368
+ : null;
369
+
370
+ const resolvedReasons = new Set(["explicit_target", "continued_target"]);
371
+ const unresolvedReasons = new Set([
372
+ "multiple_plausible_targets",
373
+ "target_content_mismatch",
374
+ "target_unavailable",
375
+ "no_reliable_target",
376
+ ]);
377
+
378
+ if (!["resolved", "unresolved"].includes(resolutionState)) return null;
379
+ if (!workingDirectory || evidenceSources === null) return null;
380
+ if (resolutionState === "resolved") {
381
+ if (!resolvedReasons.has(reasonCode) || !primaryTarget || nextAction) return null;
382
+ } else if (!unresolvedReasons.has(reasonCode) || primaryTarget || governanceTarget || !nextAction || targetChanged) {
383
+ return null;
384
+ }
385
+
386
+ let locale;
387
+ try {
388
+ locale = resolvePresentationLocale(registry, requestedLocale);
389
+ } catch {
390
+ return null;
391
+ }
392
+ if (!locale) return null;
393
+
394
+ const pack = localePack(registry, locale);
395
+ const labels = pack?.taskTargetResolution;
396
+ if (!plainObject(labels) || !plainObject(labels.reasonCodes)) return null;
397
+ const reason = labels.reasonCodes[reasonCode];
398
+ if (!reason) return null;
399
+
400
+ const rows = resolutionState === "resolved"
401
+ ? [
402
+ [labels.state, labels.resolved],
403
+ [labels.primaryTarget, primaryTarget],
404
+ [labels.governanceTarget, governanceTarget || labels.none],
405
+ [labels.evidenceSources, evidenceSources.join("; ") || labels.none],
406
+ [labels.workingDirectory, workingDirectory],
407
+ [labels.reason, reason],
408
+ ...(targetChanged ? [[labels.targetChanged, labels.yes]] : []),
409
+ ]
410
+ : [
411
+ [labels.state, labels.unresolved],
412
+ [labels.reason, reason],
413
+ [labels.evidenceSources, evidenceSources.join("; ") || labels.none],
414
+ [labels.workingDirectory, workingDirectory],
415
+ [labels.nextAction, nextAction],
416
+ ];
417
+
418
+ const markdown = [
419
+ `## ${labels.title}`,
420
+ "",
421
+ `| ${labels.field} | ${labels.value} |`,
422
+ "|---|---|",
423
+ ...rows.map(([label, value]) => `| ${markdownCell(label)} | ${markdownCell(value)} |`),
424
+ ].join("\n");
425
+
426
+ return Object.freeze({
427
+ schema_version: "1",
428
+ semantic_block: "task_target_orientation",
429
+ presentation_language: locale,
430
+ resolution_state: resolutionState,
431
+ reason_code: reasonCode,
432
+ markdown,
433
+ authorizes: false,
434
+ });
435
+ }
436
+
352
437
  export function renderScopeClassificationCard(classification, {
353
438
  registry,
354
439
  requestedLocale,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-agdf",
3
- "version": "0.11.3",
3
+ "version": "0.12.0",
4
4
  "description": "Bootstrap AGDF repository-local plugin files, Copilot/OpenCode instructions, skills, control files and machine-readable checks.",
5
5
  "type": "module",
6
6
  "files": [
@@ -24,6 +24,7 @@
24
24
  "test:delivery-path-search": "node ./scripts/delivery-path-search-test.js",
25
25
  "test:delivery-path-search-unit": "node ./scripts/delivery-path-search-unit-test.js",
26
26
  "test:delivery-path-search-generator": "node ./scripts/delivery-path-search-generator-test.js",
27
+ "test:opencode-hardening": "node ./scripts/opencode-hardening-test.js",
27
28
  "test:cli-modularization": "node ./scripts/cli-modularization-test.js",
28
29
  "test:local-validator": "node ./scripts/local-validator-test.js",
29
30
  "test:local-marketplace": "node ./scripts/local-marketplace-test.js",
@@ -38,9 +39,12 @@
38
39
  "test:skill-evals": "node ./scripts/skill-evals-test.js",
39
40
  "eval:skills": "node ./scripts/run-skill-evals.js",
40
41
  "eval:skills:record": "node ./scripts/record-skill-evals.js",
42
+ "eval:proportionality:record": "node ./scripts/record-proportionality-benchmark.js",
43
+ "eval:proportionality": "node ./scripts/run-proportionality-benchmark.js",
44
+ "test:proportionality": "node ./scripts/proportionality-benchmark-test.js",
41
45
  "test:release-bootstrap": "node ./scripts/release-bootstrap-smoke-test.js",
42
46
  "test:routing": "npm run sync-package-assets && node ./scripts/test-routing.js",
43
- "smoke-test": "npm run sync-package-assets && npm run test:cli-modularization && npm run test:local-validator && npm run test:local-marketplace && npm run test:package-build && npm run test:package-contents && npm run test:lifecycle && npm run test:control-state && npm run test:interaction-presentation && npm run test:verified-change && npm run test:runtime-integrity-layout && npm run test:runtime-integrity-negative && npm run test:skill-evals && npm run eval:skills && npm run test:delivery-path-search && npm run test:delivery-path-search-unit && npm run test:delivery-path-search-generator && node ./scripts/smoke-test.js && node ./scripts/test-routing.js",
47
+ "smoke-test": "npm run sync-package-assets && npm run test:cli-modularization && npm run test:local-validator && npm run test:local-marketplace && npm run test:package-build && npm run test:package-contents && npm run test:lifecycle && npm run test:control-state && npm run test:interaction-presentation && npm run test:verified-change && npm run test:runtime-integrity-layout && npm run test:runtime-integrity-negative && npm run test:skill-evals && npm run eval:skills && npm run test:proportionality && npm run test:delivery-path-search && npm run test:delivery-path-search-unit && npm run test:delivery-path-search-generator && npm run test:opencode-hardening && node ./scripts/smoke-test.js && node ./scripts/test-routing.js",
44
48
  "prepack": "npm run sync-package-assets"
45
49
  },
46
50
  "keywords": [
@@ -2,7 +2,7 @@
2
2
  "schema_version": "1",
3
3
  "generated": true,
4
4
  "source": "create-agdf/cli",
5
- "version": "0.11.3",
5
+ "version": "0.12.0",
6
6
  "entrypoint": "create-agdf/bin/agdf-validator.js",
7
- "digest": "217946af2b7c5782e9a620c18a0aa3ce351993b4bda8874bbfbf7d8406cc48ff"
7
+ "digest": "a869279122dd6a2a7de6a5c719e41eedea5e89b7a2ee32012f7d74ce778ff24e"
8
8
  }
@@ -92,6 +92,7 @@ const agentRouterPath = join(pluginRoot, "meta", "agdf-agent-router.md");
92
92
  const runtimeContractPath = join(pluginRoot, "meta", "agdf-runtime-contract.md");
93
93
  const contractsDir = join(pluginRoot, "meta", "contracts");
94
94
  const contractModules = [
95
+ "task-target-resolution.md",
95
96
  "gate-transition.md",
96
97
  "interaction.md",
97
98
  "modes.md",
@@ -331,12 +332,66 @@ if (!sourceMode && isFile(localRuntimeEntrypointPath)) {
331
332
  }
332
333
  }
333
334
  const runtimeContract = readAllContracts();
335
+ const modesContract = isFile(join(contractsDir, "modes.md")) ? read(join(contractsDir, "modes.md")) : "";
336
+ const gateTransitionContract = isFile(join(contractsDir, "gate-transition.md")) ? read(join(contractsDir, "gate-transition.md")) : "";
337
+ const agentRouterContent = isFile(agentRouterPath) ? read(agentRouterPath) : "";
334
338
  const interactionLocales = isFile(interactionLocalesPath) ? readJson(interactionLocalesPath, "interaction locale registry") : null;
335
339
  const gateCheckSkill = isFile(gateCheckSkillPath) ? read(gateCheckSkillPath) : "";
336
340
  const brownfieldSkill = isFile(brownfieldSkillPath) ? read(brownfieldSkillPath) : "";
337
341
  if (!brownfieldSkill.includes("Persist the completed review and its decision, scope reason, evidence and required next gate in the same internal operation")) {
338
342
  failures.push("brownfield-analysis must own atomic post-UR review and routing persistence");
339
343
  }
344
+ for (const required of [
345
+ "### Structured Depth Decision",
346
+ "depth_unresolved",
347
+ "depth_facts_status",
348
+ "bounded_structured_slice",
349
+ "authority_policy_security_depth",
350
+ "architecture_runtime_depth",
351
+ "persistence_migration_depth",
352
+ "external_contract_depth",
353
+ "release_cross_host_depth",
354
+ "unbounded_consumer_coordination",
355
+ "depth_facts_missing",
356
+ "depth_facts_conflicting",
357
+ "coherent_outcome",
358
+ "authority_boundary",
359
+ "owner_consumer_coordination",
360
+ "full_depth_impacts_absent",
361
+ "migration_propagation_bounded",
362
+ "failure_recovery_local",
363
+ "independently_acceptable",
364
+ ]) {
365
+ if (!modesContract.includes(required)) failures.push(`modes contract Structured Depth Decision missing: ${required}`);
366
+ }
367
+ if (!modesContract.includes("counts are not a decision proxy")
368
+ || !modesContract.includes("threshold for either structured mode")) {
369
+ failures.push("modes contract must reject numeric structured-depth proxies");
370
+ }
371
+ if (!modesContract.includes("Structured Slice and Structured Delivery use the same existing gate and approval sequence")) {
372
+ failures.push("modes contract must preserve structured-depth gate parity");
373
+ }
374
+ if (!gateTransitionContract.includes("sole normative `Structured Depth Decision`")
375
+ || !gateTransitionContract.includes("does not duplicate its")
376
+ || !gateTransitionContract.includes("trigger or bounded-slice matrix")) {
377
+ failures.push("gate-transition contract must reference the Modes-owned Structured Depth Decision without duplicating it");
378
+ }
379
+ if (!gateTransitionContract.includes("`depth_unresolved` product result is persisted through this existing value")
380
+ || !gateTransitionContract.includes("`depth_facts_missing | depth_facts_conflicting`")) {
381
+ failures.push("gate-transition contract must route unresolved structured depth through the existing block value");
382
+ }
383
+ for (const required of [
384
+ "../../meta/contracts/modes.md",
385
+ "depth_policy_version: 1",
386
+ "depth_facts_status: complete",
387
+ "rejected_alternative",
388
+ "missing_or_conflicting_facts",
389
+ "all seven bounded-slice",
390
+ "the existing `block` decision",
391
+ "Brownfield/Mode-Slice re-evaluation",
392
+ ]) {
393
+ if (!brownfieldSkill.includes(required)) failures.push(`brownfield-analysis structured-depth guidance missing: ${required}`);
394
+ }
340
395
  const codexPlugin = isFile(codexPluginPath) ? readJson(codexPluginPath, "Codex plugin manifest") : null;
341
396
  const claudePlugin = isFile(claudePluginPath) ? readJson(claudePluginPath, "Claude plugin manifest") : null;
342
397
  const agdfPackage = isFile(agdfPackagePath) ? readJson(agdfPackagePath, "agdf CLI package manifest") : null;
@@ -354,13 +409,18 @@ if (!runtimeContract.includes("### Interaction Locale Contract") || !runtimeCont
354
409
  }
355
410
 
356
411
  const gateCheckOperationalBoundaries = [
412
+ "Resolve or revalidate the primary task target before selecting repository control state.",
413
+ "Derive repository activation only from the resolved governance target",
357
414
  "Select exactly one run and evaluate its current gate.",
358
415
  "Confirm that the required durable artefact is present and ready.",
359
416
  "Consume the canonical `approval_presentation` verbatim",
360
417
  "obtain deliberate input through the contract-selected native or exact-text path",
361
- "Revalidate the same run, gate and revision immediately after the response and before persistence.",
418
+ "Revalidate the same target, run, gate and revision immediately after the response and before persistence.",
362
419
  "Persist only a currently valid exact approval through the existing control-state workflow.",
363
420
  ];
421
+ if (!gateCheckSkill.includes("../../meta/contracts/task-target-resolution.md")) {
422
+ failures.push("gate-check must load the focused task-target-resolution contract");
423
+ }
364
424
  if (!gateCheckSkill.includes("../../meta/contracts/interaction.md")) {
365
425
  failures.push("gate-check must load the focused interaction contract");
366
426
  }
@@ -403,6 +463,26 @@ if (!runtimeContract.includes("### Repository Activation Diagnosis Boundary")) {
403
463
  if (!runtimeContract.includes("### Scope Classification Card")) {
404
464
  failures.push("interaction contract must own the scope classification card section");
405
465
  }
466
+ if (!runtimeContract.includes("## Purpose And Ordering")
467
+ || !runtimeContract.includes("## Target Authority Precedence")
468
+ || !runtimeContract.includes("## Evidence And Mutation Boundary")
469
+ || !runtimeContract.includes("## Fail-Closed States")) {
470
+ failures.push("task-target-resolution contract must own target precedence, evidence boundaries and fail-closed states");
471
+ }
472
+ if (!runtimeContract.includes("### Task Target Orientation")) {
473
+ failures.push("interaction contract must own the task target orientation section");
474
+ }
475
+ if (!agentRouterContent.includes("## Task Target Resolution")
476
+ || agentRouterContent.indexOf("## Task Target Resolution") > agentRouterContent.indexOf("## Mode Selection")) {
477
+ failures.push("agent router must resolve the task target before Mode Selection");
478
+ }
479
+ if (!gateCheckSkill.includes("task_target_orientation.markdown` verbatim")) {
480
+ failures.push("gate-check must consume the canonical task target orientation verbatim");
481
+ }
482
+ if (gateCheckSkill.includes("## Task Target Orientation Template")
483
+ || gateCheckSkill.includes("| Primary target | Governance target | Evidence sources |")) {
484
+ failures.push("gate-check must not maintain a skill-local task target orientation template");
485
+ }
406
486
  if (!gateCheckSkill.includes("scope_classification.markdown` verbatim")) {
407
487
  failures.push("gate-check must consume the canonical scope classification projection verbatim");
408
488
  }
@@ -415,12 +495,21 @@ if (sourceMode && isFile(interactionPresentationPath)) {
415
495
  if (!interactionPresentation.includes("export function renderScopeClassificationCard")) {
416
496
  failures.push("interaction-presentation.js must export renderScopeClassificationCard");
417
497
  }
498
+ if (!interactionPresentation.includes("export function renderTaskTargetOrientation")) {
499
+ failures.push("interaction-presentation.js must export renderTaskTargetOrientation");
500
+ }
418
501
  }
419
502
  for (const locale of ["en", "de"]) {
420
503
  const pack = interactionLocales?.locales?.[locale];
421
504
  if (!pack?.scopeClassification?.title || !pack?.scopeClassification?.mode?.quick_task || !pack?.scopeClassification?.challenge) {
422
505
  failures.push(`Interaction locale ${locale} missing scopeClassification section keys`);
423
506
  }
507
+ if (!pack?.taskTargetResolution?.title
508
+ || !pack?.taskTargetResolution?.primaryTarget
509
+ || !pack?.taskTargetResolution?.reasonCodes?.target_content_mismatch
510
+ || !pack?.taskTargetResolution?.nextAction) {
511
+ failures.push(`Interaction locale ${locale} missing taskTargetResolution section keys`);
512
+ }
424
513
  }
425
514
  if (!runtimeContract.includes("not an agent-facing diagnosis proof")) {
426
515
  failures.push("control-scaffold contract must classify AGDF_* env vars as non-agent-facing diagnosis");
@@ -902,9 +991,10 @@ for (const skill of expectedSkills) {
902
991
  for (const required of [
903
992
  "## Native Interaction Path",
904
993
  "complete normative owner for interaction kinds, locale",
994
+ "Resolve or revalidate the primary task target before selecting repository control state",
905
995
  "Select exactly one run and evaluate its current gate",
906
996
  "Confirm that the required durable artefact is present and ready",
907
- "Revalidate the same run, gate and revision immediately after the response and before persistence",
997
+ "Revalidate the same target, run, gate and revision immediately after the response and before persistence",
908
998
  "Persist only a currently valid exact approval",
909
999
  ]) {
910
1000
  if (!skillMd.includes(required)) failures.push(`gate-check native interaction guidance missing: ${required}`);
@@ -1066,7 +1156,33 @@ if (isFile(sotRegistryTemplatePath)) {
1066
1156
 
1067
1157
  if (isFile(brownfieldReviewTemplatePath)) {
1068
1158
  const brownfieldReviewTemplate = read(brownfieldReviewTemplatePath);
1069
- for (const required of ["Existing-System View", "Reuse And Parallel-Structure Risk", "Mode / Slice Decision", "transparency_note", "Next Permissible Step", "delivery_context", "ui_ux_impact", "ui_ux_impact_reason", "ux_intent_definition_required", "ux_intent_definition_result"]) {
1159
+ for (const required of [
1160
+ "Existing-System View",
1161
+ "Reuse And Parallel-Structure Risk",
1162
+ "Mode / Slice Decision",
1163
+ "transparency_note",
1164
+ "Next Permissible Step",
1165
+ "delivery_context",
1166
+ "ui_ux_impact",
1167
+ "ui_ux_impact_reason",
1168
+ "ux_intent_definition_required",
1169
+ "ux_intent_definition_result",
1170
+ "Structured Depth Evidence",
1171
+ "depth_policy_version",
1172
+ "depth_facts_status",
1173
+ "primary_reason_code",
1174
+ "decisive_full_depth_triggers",
1175
+ "rejected_alternative",
1176
+ "missing_or_conflicting_facts",
1177
+ "depth_evidence_refs",
1178
+ "coherent_outcome",
1179
+ "authority_boundary",
1180
+ "owner_consumer_coordination",
1181
+ "full_depth_impacts_absent",
1182
+ "migration_propagation_bounded",
1183
+ "failure_recovery_local",
1184
+ "independently_acceptable",
1185
+ ]) {
1070
1186
  if (!brownfieldReviewTemplate.includes(required)) failures.push(`BROWNFIELD_REVIEW.md missing control field: ${required}`);
1071
1187
  }
1072
1188
  }
@@ -22,6 +22,7 @@ The skill answers:
22
22
  Use these focused runtime-contract modules:
23
23
 
24
24
  - `../../meta/contracts/gate-transition.md`
25
+ - `../../meta/contracts/modes.md`
25
26
  - `../../meta/contracts/context-graph.md`
26
27
  - `../../meta/contracts/quality.md`
27
28
 
@@ -54,6 +55,19 @@ not applicable; Brownfield records cite repository evidence. Do not create a sec
54
55
  11. Large UI surfaces and state hooks must be checked for mixed render, view-model, derived state, orchestration, persistence, and recovery ownership.
55
56
  12. Context Graph impact must be curated; it is not a review log or version index.
56
57
  13. Specification archive migrations must follow the archive index if present.
58
+ 14. In `post_ur_review`, evaluate unchanged compact paths first. For a structured candidate, apply
59
+ only the `Structured Depth Decision` in `modes.md`; do not recreate its trigger/check matrix in
60
+ this skill.
61
+ 15. A positive structured decision requires `depth_policy_version: 1`,
62
+ `depth_facts_status: complete`, complete evidence for all seven bounded-slice checks, a primary
63
+ reason code, rejected alternative and evidence references. A decisive full-depth trigger may
64
+ support `structured_delivery`; `structured_slice` requires every bounded-slice check to pass.
65
+ 16. Missing or conflicting decisive facts without an already-evidenced full-depth trigger persist
66
+ the existing `block` decision with `depth_facts_missing | depth_facts_conflicting`, named facts,
67
+ their evidence owner where known, a Brownfield Review link and a precise next action for evidence
68
+ completion and Brownfield/Mode-Slice re-evaluation.
69
+ 17. Owner, file, consumer, task or derived-path counts must never select a structured depth by
70
+ themselves.
57
71
 
58
72
  ## When To Use
59
73
  - after `gate-check` permits `Brownfield Review` or the selected canonical run record names Brownfield Review as the next allowed action
@@ -107,6 +121,11 @@ When used as Brownfield Review after `Approval: UR`, do not recommend PRD, SD, T
107
121
  9. Check UI monolith risk for large surfaces or central hooks.
108
122
  10. Check Context Graph impact according to `../../meta/contracts/context-graph.md`.
109
123
  11. Recommend the minimal clean implementation path.
124
+ 12. In `post_ur_review`, record `depth_policy_version`, `depth_facts_status`,
125
+ `primary_reason_code`, `decisive_full_depth_triggers`, `rejected_alternative`,
126
+ `missing_or_conflicting_facts`, `depth_evidence_refs` and evidence for all seven bounded-slice
127
+ check IDs in the Brownfield Review. Then persist the completed review and the existing
128
+ Mode/Slice Decision atomically.
110
129
 
111
130
  ## Output
112
131
  Use a concise structure:
@@ -133,6 +152,13 @@ For `post_ur_review`, include the four UI/UX routing fields immediately after `s
133
152
  definition is required, its `ready | blocked | not_applicable` result must be visible before PRD
134
153
  readiness. A required `blocked` result is at least `revise` for this routing step.
135
154
 
155
+ For a structured `post_ur_review` candidate, also include the complete `Structured Depth Evidence`
156
+ section from `BROWNFIELD_REVIEW.md`. The decision projection must show the primary reason, decisive
157
+ dimensions, bounded-slice result, rejected alternative, evidence and required next gate. For
158
+ missing/conflicting facts, show product-language `depth_unresolved` while persisting `block`; name
159
+ the gap and exact recovery action. Structural instruction checks can detect missing contract
160
+ elements but do not prove semantic model compliance or live-host behavior.
161
+
136
162
  ## Pass / Revise / Block Guidance
137
163
  - `pass`: existing owners are understood, reuse path is clear, and no blocking drift or parallel structure risk remains.
138
164
  - `revise`: relevant evidence, tests, ownership, or reuse path is incomplete.