create-agdf 0.11.4 → 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 (56) hide show
  1. package/README.md +1 -1
  2. package/generated/.agdf/control/templates/artefacts/BROWNFIELD_REVIEW.md +25 -0
  3. package/generated/.github/skills/agdf-brownfield-analysis/SKILL.md +26 -0
  4. package/generated/.github/skills/agdf-gate-check/SKILL.md +40 -18
  5. package/generated/.github/skills/agdf-interaction-locales.json +50 -0
  6. package/generated/.github/skills/agdf-runtime-contract.md +1 -0
  7. package/generated/.github/skills/contracts/gate-transition.md +7 -3
  8. package/generated/.github/skills/contracts/interaction.md +22 -0
  9. package/generated/.github/skills/contracts/modes.md +81 -0
  10. package/generated/.github/skills/contracts/task-target-resolution.md +102 -0
  11. package/generated/.opencode/AGDF.md +2 -0
  12. package/generated/.opencode/agdf-interaction-locales.json +50 -0
  13. package/generated/.opencode/agdf-runtime-contract.md +1 -0
  14. package/generated/.opencode/contracts/gate-transition.md +7 -3
  15. package/generated/.opencode/contracts/interaction.md +22 -0
  16. package/generated/.opencode/contracts/modes.md +81 -0
  17. package/generated/.opencode/contracts/task-target-resolution.md +102 -0
  18. package/generated/.opencode/skills/agdf-brownfield-analysis/SKILL.md +26 -0
  19. package/generated/.opencode/skills/agdf-gate-check/SKILL.md +40 -18
  20. package/generated/AGENTS.md +1 -0
  21. package/generated/plugins/agdf/.claude-plugin/plugin.json +1 -1
  22. package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
  23. package/generated/plugins/agdf/control/templates/artefacts/BROWNFIELD_REVIEW.md +25 -0
  24. package/generated/plugins/agdf/meta/agdf-agent-router.md +16 -0
  25. package/generated/plugins/agdf/meta/agdf-interaction-locales.json +50 -0
  26. package/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
  27. package/generated/plugins/agdf/meta/agdf-runtime-contract.md +1 -0
  28. package/generated/plugins/agdf/meta/contracts/gate-transition.md +7 -3
  29. package/generated/plugins/agdf/meta/contracts/interaction.md +22 -0
  30. package/generated/plugins/agdf/meta/contracts/modes.md +81 -0
  31. package/generated/plugins/agdf/meta/contracts/task-target-resolution.md +102 -0
  32. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-interaction-locales.json +50 -0
  33. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
  34. package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/gate-policy.js +14 -7
  35. package/generated/plugins/agdf/runtime/create-agdf/lib/interaction-presentation.js +85 -0
  36. package/generated/plugins/agdf/runtime/create-agdf/package.json +5 -2
  37. package/generated/plugins/agdf/runtime/runtime-manifest.json +2 -2
  38. package/generated/plugins/agdf/scripts/check-runtime-integrity.mjs +119 -3
  39. package/generated/plugins/agdf/skills/brownfield-analysis/SKILL.md +26 -0
  40. package/generated/plugins/agdf/skills/gate-check/SKILL.md +40 -18
  41. package/lib/cli/application.js +13 -2
  42. package/lib/control-evaluation/gate-policy.js +14 -7
  43. package/lib/installers/opencode.js +128 -1
  44. package/lib/interaction-presentation.js +85 -0
  45. package/lib/live-agent/read-only-structured.js +43 -0
  46. package/lib/proportionality-benchmark/blind-prompt.js +37 -0
  47. package/lib/proportionality-benchmark/contracts.js +120 -0
  48. package/lib/proportionality-benchmark/corpus-loader.js +110 -0
  49. package/lib/proportionality-benchmark/evaluator.js +250 -0
  50. package/lib/proportionality-benchmark/index.js +7 -0
  51. package/lib/proportionality-benchmark/live-recorder.js +100 -0
  52. package/lib/proportionality-benchmark/report.js +51 -0
  53. package/lib/proportionality-benchmark/source-fingerprint.js +30 -0
  54. package/lib/skill-evals/live-recorder.js +3 -26
  55. package/opencode-plugin.js +40 -1
  56. package/package.json +5 -2
@@ -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.
@@ -20,6 +20,7 @@ This skill must not create later artefacts such as PRD, SD, TP, CD, CR, QA, or U
20
20
  ## Runtime Contract
21
21
  Use these focused runtime-contract modules:
22
22
 
23
+ - `../../meta/contracts/task-target-resolution.md`
23
24
  - `../../meta/contracts/gate-transition.md`
24
25
  - `../../meta/contracts/interaction.md`
25
26
  - `../../meta/contracts/control-scaffold.md`
@@ -75,6 +76,12 @@ The CLI reports are validators and JSON evidence, not the primary user experienc
75
76
 
76
77
  Plugin `AGDF_*` environment variables set by the OpenCode `shell.env` hook are convenience only and must not be used as the only proof of activation; host shell-env propagation to spawned tool shells is not guaranteed and is OpenCode-owned, not AGDF-owned. A relative glob or `grep` for `.agdf/control/config.json` must not be used as proof of presence or absence of `.agdf/control/config.json`; an absolute `read` or the canonical CLI probe is required.
77
78
 
79
+ ## OpenCode Subagent Enforcement Boundary
80
+
81
+ OpenCode plugin hooks (`tool.execute.before`) do not intercept tool calls from subagents spawned via the `task` tool (anomalyco/opencode issue #5894, PR #36238 open). AGDF enforcement through plugin hooks applies to primary-agent tool calls only; in the subagent path, AGDF governance is auditing-only, not enforcement.
82
+
83
+ This is a host limitation, not an AGDF-owned defect. Do not claim that AGDF gates are technically enforced in the OpenCode subagent path. Do not route work to a subagent to bypass a primary-agent gate. When subagent work touches gate-relevant files, verify the result through the canonical validator (`doctor --json`, `gate-check --json`) after the subagent returns, not during.
84
+
78
85
  ## Native Interaction Path
79
86
 
80
87
  `../../meta/contracts/interaction.md` is the complete normative owner for interaction kinds, locale,
@@ -84,12 +91,15 @@ do not restate or infer a surface matrix in this skill.
84
91
 
85
92
  This skill owns only these operational responsibilities:
86
93
 
87
- 1. Select exactly one run and evaluate its current gate.
88
- 2. Confirm that the required durable artefact is present and ready.
89
- 3. Consume the canonical `approval_presentation` verbatim for the selected run, gate and revision.
90
- 4. Present that projection and obtain deliberate input through the contract-selected native or exact-text path.
91
- 5. Revalidate the same run, gate and revision immediately after the response and before persistence.
92
- 6. Persist only a currently valid exact approval through the existing control-state workflow.
94
+ 1. Resolve or revalidate the primary task target before selecting repository control state.
95
+ 2. Derive repository activation only from the resolved governance target; an evidence source or
96
+ working directory is not sufficient.
97
+ 3. Select exactly one run and evaluate its current gate.
98
+ 4. Confirm that the required durable artefact is present and ready.
99
+ 5. Consume the canonical `approval_presentation` verbatim for the selected run, gate and revision.
100
+ 6. Present that projection and obtain deliberate input through the contract-selected native or exact-text path.
101
+ 7. Revalidate the same target, run, gate and revision immediately after the response and before persistence.
102
+ 8. Persist only a currently valid exact approval through the existing control-state workflow.
93
103
 
94
104
  For status, blocked, read-only or rationale interactions, consume the canonical
95
105
  `status_presentation.markdown` verbatim and apply the locale contract without asking for approval.
@@ -156,18 +166,30 @@ Use what is available:
156
166
  If a status is not explicit, do not assume it is satisfied.
157
167
 
158
168
  ## Workflow
159
- 1. Check exact approvals.
160
- 2. Check artefact status.
161
- 3. Determine the earliest blocking user gate or internal mandatory step.
162
- 4. Derive allowed and forbidden outputs.
163
- 5. Name the exact missing approval, if any.
164
- 6. If consent was only implicit, say it is not yet approval and provide the exact formula.
165
- 7. Ensure the next step follows the gate transition table. In particular, never jump from `Approval: UR` to implementation.
166
- 8. Treat a generic "start", "continue" or "leg los" request as a request to perform only the current next allowed action.
167
- 9. After Brownfield Review, choose the smallest safe process path before creating later artefacts.
168
- 10. If the selected path is not visibly recorded with scope reason and evidence, keep the run at `Mode/Slice Decision`.
169
- 11. When `.agdf/control/` is missing or incomplete, make the next allowed artefact action explicit. For a fresh request, that means draft the minimal UR in the response, then request `Approval: UR`. Do not write a full control scaffold unless durable control state was explicitly requested or is already the repository's live AGDF working state.
170
- 12. If branch, workspace and durable artefacts disagree, do not choose a scope silently; report the ambiguity and ask for the smallest clarifying gate action.
169
+ 1. Resolve or revalidate the primary target through `task-target-resolution.md`.
170
+ 2. If resolution is unresolved, consume the canonical target orientation, request only its
171
+ clarification/recovery action and stop before repository activation, gate evaluation or mutation.
172
+ 3. Derive the governance target from the primary target; never from `cwd` or an evidence source alone.
173
+ 4. Check exact approvals.
174
+ 5. Check artefact status.
175
+ 6. Determine the earliest blocking user gate or internal mandatory step.
176
+ 7. Derive allowed and forbidden outputs.
177
+ 8. Name the exact missing approval, if any.
178
+ 9. If consent was only implicit, say it is not yet approval and provide the exact formula.
179
+ 10. Ensure the next step follows the gate transition table. In particular, never jump from `Approval: UR` to implementation.
180
+ 11. Treat a generic "start", "continue" or "leg los" request as a request to perform only the current next allowed action.
181
+ 12. After Brownfield Review, choose the smallest safe process path before creating later artefacts.
182
+ 13. If the selected path is not visibly recorded with scope reason and evidence, keep the run at `Mode/Slice Decision`.
183
+ 14. When `.agdf/control/` is missing or incomplete, make the next allowed artefact action explicit. For a fresh request, that means draft the minimal UR in the response, then request `Approval: UR`. Do not write a full control scaffold unless durable control state was explicitly requested or is already the repository's live AGDF working state.
184
+ 15. If branch, workspace and durable artefacts disagree, do not choose a scope silently; report the ambiguity and ask for the smallest clarifying gate action.
185
+
186
+ ### Task Target Orientation
187
+
188
+ When target/context separation is material, the target changed, or resolution is unresolved, consume
189
+ `task_target_orientation.markdown` verbatim from `renderTaskTargetOrientation` in
190
+ `../../meta/contracts/interaction.md`. Do not reconstruct its fields or maintain a skill-local target
191
+ template. If the renderer returns `null`, fail closed to target clarification and do not proceed to
192
+ repository activation, Scope Classification or gate evaluation.
171
193
 
172
194
  ### Scope Classification Output
173
195
  When this skill classifies a fresh scope as ungated (Quick Task or Trivial Change Boundary), consume
@@ -115,10 +115,19 @@ function createHandlers({ io, env, exec, prepare }) {
115
115
  const result = runLifecyclePhase("plugin_operation", () => installOpenCodeGlobalPlugin(configDir));
116
116
  runLifecyclePhase("global_surface", () => installOpenCodeGlobalSurface(configDir));
117
117
  const report = runLifecyclePhase("verification", () => evaluateOpenCodeStatus(options.dir, configDir, result.transition));
118
+ const alignmentHealthy = ["already_matching", "aligned"].includes(result.sdk_alignment.status);
118
119
  const verificationHealthy = report.status === "configured"
119
120
  && report.package.version_status === "current"
120
121
  && report.global_native_surface.complete
121
- && report.experimental_hooks.aggregate === "declared_supported";
122
+ && report.experimental_hooks.aggregate === "declared_supported"
123
+ && report.host_sdk_version.status === "matching"
124
+ && alignmentHealthy;
125
+ const nextAction = verificationHealthy
126
+ ? globalInstallRestartAction(options.target)
127
+ : {
128
+ kind: "recovery",
129
+ text: `Retry the OpenCode installation to align @opencode-ai/plugin to ${result.sdk_alignment.target_version || "the exact host version"}; observed SDK: ${result.sdk_alignment.installed_version || "unknown"}.`,
130
+ };
122
131
  printLifecycleResult(createLifecycleResult({
123
132
  operation: result.transition.status === "updated" ? "update" : "install",
124
133
  result: verificationHealthy ? "success" : "partial",
@@ -140,13 +149,15 @@ function createHandlers({ io, env, exec, prepare }) {
140
149
  `plugin_sdk=${report.plugin_sdk.installed_version || "unknown"}`,
141
150
  `experimental_hooks=${report.experimental_hooks.aggregate}`,
142
151
  `host_sdk_version=${report.host_sdk_version.status};policy=${report.host_sdk_version.policy}`,
152
+ `sdk_alignment=${result.sdk_alignment.status};target=${result.sdk_alignment.target_version || "unknown"};installed=${result.sdk_alignment.installed_version || "unknown"}`,
143
153
  ],
144
154
  },
145
155
  restart: { required: true, reason: "host_reload" },
146
- next_action: globalInstallRestartAction(options.target),
156
+ next_action: nextAction,
147
157
  }), { json: options.json, io });
148
158
  if (!options.json) {
149
159
  io.log(`OpenCode host / plugin SDK: ${report.host.installed_version || "unknown"} / ${report.plugin_sdk.installed_version || "unknown"} (${report.host_sdk_version.status}; ${report.host_sdk_version.policy})`);
160
+ io.log(`Plugin SDK alignment: ${result.sdk_alignment.status} (target ${result.sdk_alignment.target_version || "unknown"}; installed ${result.sdk_alignment.installed_version || "unknown"})`);
150
161
  io.log(`Experimental hook declarations: ${report.experimental_hooks.aggregate} (SDK declaration evidence; live invocation not observed)`);
151
162
  }
152
163
  return verificationHealthy ? 0 : 1;
@@ -29,8 +29,9 @@ export function isGateSatisfied(runState, gate) {
29
29
  return isDurableGateArtefactSatisfied(runState, gate);
30
30
  }
31
31
 
32
- function qaRevisionRequired(runState) {
33
- return gateArtefactStatus(runState, "QA").status === "revise";
32
+ function qaNonPassDecision(runState) {
33
+ const status = gateArtefactStatus(runState, "QA").status;
34
+ return status === "revise" || status === "block" ? status : null;
34
35
  }
35
36
 
36
37
  export function transitionDecisionForRunState(runState, verifiedChange = null) {
@@ -233,15 +234,21 @@ export function transitionDecisionForRunState(runState, verifiedChange = null) {
233
234
  };
234
235
  }
235
236
 
236
- if (qaRevisionRequired(runState)) {
237
+ const qaNonPass = qaNonPassDecision(runState);
238
+ if (qaNonPass) {
239
+ const blocked = qaNonPass === "block";
237
240
  return {
238
- status: "open",
241
+ status: blocked ? "blocked" : "open",
239
242
  current_gate: "QA",
240
- blocking_reason: "qa_revise_required",
243
+ blocking_reason: blocked ? "qa_blocked" : "qa_revise_required",
241
244
  missing_approval: "none",
242
- allowed: ["revise the implementation against the QA findings", "refresh CD+Tests and mandatory reviews", "rerun QA with refreshed evidence"],
245
+ allowed: blocked
246
+ ? ["route the blocking QA findings to their authoritative owner", "record the next permitted remediation or upstream decision", "rerun QA only after the blocker is resolved"]
247
+ : ["revise the implementation against the QA findings", "refresh CD+Tests and mandatory reviews", "rerun QA with refreshed evidence"],
243
248
  forbidden: ["request QA approval", "request UAT approval", "release", "claim delivery readiness"],
244
- next_allowed_action: "Resolve the QA revise findings, refresh CD+Tests and reviews, then rerun QA. Do not request Approval: QA from a revise report.",
249
+ next_allowed_action: blocked
250
+ ? "Resolve or route the blocking QA findings through their authoritative owner, then rerun the required delivery steps. Do not request Approval: QA from a block report."
251
+ : "Resolve the QA revise findings, refresh CD+Tests and reviews, then rerun QA. Do not request Approval: QA from a revise report.",
245
252
  };
246
253
  }
247
254
 
@@ -116,11 +116,13 @@ export function installOpenCodeGlobalPlugin(configDir) {
116
116
  throw openCodeLifecycleError("configuration", `Failed to write OpenCode config ${configPath}: ${error.message}`, { configPath });
117
117
  }
118
118
  const installedPackage = resolveOpenCodePackage(configDir);
119
+ const sdkAlignment = alignOpenCodePluginSdk(configDir);
119
120
 
120
121
  return {
121
122
  configPath,
122
123
  added: !alreadyInstalled,
123
124
  transition: openCodePackageTransition(previousPackage, installedPackage),
125
+ sdk_alignment: sdkAlignment,
124
126
  };
125
127
  }
126
128
 
@@ -519,6 +521,131 @@ export function evaluateOpenCodeHostSdk(configDir, dependencies = {}) {
519
521
  };
520
522
  }
521
523
 
524
+ const exactOpenCodeVersionPattern = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
525
+
526
+ function openCodeSdkAlignmentResult(status, initial, overrides = {}) {
527
+ return {
528
+ status,
529
+ attempted: false,
530
+ host_version: initial.host.installed_version || null,
531
+ previous_version: initial.plugin_sdk.installed_version || null,
532
+ target_version: null,
533
+ installed_version: initial.plugin_sdk.installed_version || null,
534
+ error: "",
535
+ ...overrides,
536
+ };
537
+ }
538
+
539
+ function openCodeRegistryVersion(raw) {
540
+ const value = String(raw ?? "").trim();
541
+ if (!value) return "";
542
+ try {
543
+ const parsed = JSON.parse(value);
544
+ return typeof parsed === "string" ? parsed.trim() : "";
545
+ } catch {
546
+ return value;
547
+ }
548
+ }
549
+
550
+ function openCodeSdkUnavailable(error) {
551
+ return /(?:ETARGET|E404|404 Not Found|No matching version)/i.test(
552
+ (error?.stderr || error?.message || "").toString(),
553
+ );
554
+ }
555
+
556
+ export function alignOpenCodePluginSdk(configDir, dependencies = {}) {
557
+ const initial = evaluateOpenCodeHostSdk(configDir, dependencies);
558
+ const hostVersion = initial.host.installed_version || "";
559
+ const sdkVersion = initial.plugin_sdk.installed_version || "";
560
+ if (!initial.host.inspectable || !initial.plugin_sdk.loadable || !exactOpenCodeVersionPattern.test(hostVersion)) {
561
+ return openCodeSdkAlignmentResult("not_attempted", initial, {
562
+ target_version: exactOpenCodeVersionPattern.test(hostVersion) ? hostVersion : null,
563
+ error: !initial.host.inspectable
564
+ ? initial.host.error || "OpenCode host version is not inspectable."
565
+ : !initial.plugin_sdk.loadable
566
+ ? initial.plugin_sdk.error || "OpenCode plugin SDK is not inspectable."
567
+ : `OpenCode host version is not an exact semantic version: ${hostVersion || "unknown"}`,
568
+ });
569
+ }
570
+ if (sdkVersion === hostVersion) {
571
+ return openCodeSdkAlignmentResult("already_matching", initial, {
572
+ target_version: hostVersion,
573
+ });
574
+ }
575
+
576
+ const run = dependencies.execFileSync ?? execFileSync;
577
+ const packageSpecifier = `@opencode-ai/plugin@${hostVersion}`;
578
+ const registryInvocation = openCodeNpmInvocation(["view", packageSpecifier, "version", "--json"]);
579
+ try {
580
+ const availableVersion = openCodeRegistryVersion(run(registryInvocation.executable, registryInvocation.args, {
581
+ cwd: configDir,
582
+ encoding: "utf8",
583
+ stdio: ["ignore", "pipe", "pipe"],
584
+ timeout: 15000,
585
+ }));
586
+ if (availableVersion !== hostVersion) {
587
+ return openCodeSdkAlignmentResult("unavailable", initial, {
588
+ target_version: hostVersion,
589
+ error: `Exact plugin SDK version is unavailable: ${packageSpecifier}`,
590
+ });
591
+ }
592
+ } catch (error) {
593
+ return openCodeSdkAlignmentResult(openCodeSdkUnavailable(error) ? "unavailable" : "failed", initial, {
594
+ target_version: hostVersion,
595
+ error: (error.stderr || error.message || "plugin SDK registry lookup failed").toString().trim(),
596
+ });
597
+ }
598
+
599
+ const installInvocation = openCodeNpmInvocation([
600
+ "install",
601
+ "--silent",
602
+ "--ignore-scripts",
603
+ "--no-audit",
604
+ "--no-fund",
605
+ "--save-prod",
606
+ "--save-exact",
607
+ packageSpecifier,
608
+ ]);
609
+ let installError = null;
610
+ try {
611
+ run(installInvocation.executable, installInvocation.args, {
612
+ cwd: configDir,
613
+ encoding: "utf8",
614
+ stdio: ["ignore", "pipe", "pipe"],
615
+ timeout: 60000,
616
+ });
617
+ } catch (error) {
618
+ installError = error;
619
+ }
620
+
621
+ const observed = evaluateOpenCodeHostSdk(configDir, dependencies);
622
+ const verified = observed.plugin_sdk.installed_version === hostVersion
623
+ && observed.experimental_hooks.aggregate === "declared_supported";
624
+ if (installError) {
625
+ return openCodeSdkAlignmentResult("failed", initial, {
626
+ attempted: true,
627
+ target_version: hostVersion,
628
+ installed_version: observed.plugin_sdk.installed_version || null,
629
+ error: (installError.stderr || installError.message || "plugin SDK installation failed").toString().trim(),
630
+ });
631
+ }
632
+ if (!verified) {
633
+ return openCodeSdkAlignmentResult("verification_failed", initial, {
634
+ attempted: true,
635
+ target_version: hostVersion,
636
+ installed_version: observed.plugin_sdk.installed_version || null,
637
+ error: observed.plugin_sdk.installed_version !== hostVersion
638
+ ? `Observed plugin SDK ${observed.plugin_sdk.installed_version || "unknown"} after targeting ${hostVersion}.`
639
+ : `Plugin SDK ${hostVersion} does not declare all required experimental hooks.`,
640
+ });
641
+ }
642
+ return openCodeSdkAlignmentResult("aligned", initial, {
643
+ attempted: true,
644
+ target_version: hostVersion,
645
+ installed_version: observed.plugin_sdk.installed_version,
646
+ });
647
+ }
648
+
522
649
  function openCodePackageVersionStatus(packageState) {
523
650
  if (!packageState.loadable) return "unloadable";
524
651
  if (!packageState.installed_version) return "unknown";
@@ -680,7 +807,7 @@ export function evaluateOpenCodeStatus(targetDir, configDir = defaultOpenCodeCon
680
807
  : hostSdk.experimental_hooks.aggregate === "degraded"
681
808
  ? "Use static AGDF instructions and review OpenCode compatibility before relying on dynamic hook injection."
682
809
  : hostSdk.host_sdk_version.status === "divergent"
683
- ? "Review OpenCode host/plugin-SDK compatibility; AGDF reports this divergence but does not align versions automatically."
810
+ ? "Run npx --yes @agdf/cli@latest opencode to align the plugin SDK to the exact OpenCode host version; status itself remains read-only."
684
811
  : repositoryActivation.state === "invalid_control"
685
812
  ? "Repair .agdf/control/config.json so it contains valid artifact_language, chat_language and runtime_language values."
686
813
  : repositoryActivation.active
@@ -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,
@@ -0,0 +1,43 @@
1
+ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { guardedExecFileSync } from "../delivery-path-search/transports/read-only-guard.js";
5
+
6
+ export function executeStructuredAgent({ surface, cwd, prompt, model, outputSchema, timeoutMs = 120000 }) {
7
+ if (surface === "codex") {
8
+ const temp = mkdtempSync(join(tmpdir(), "agdf-live-codex-"));
9
+ try {
10
+ const schemaPath = join(temp, "schema.json");
11
+ const outputPath = join(temp, "output.json");
12
+ writeFileSync(schemaPath, `${JSON.stringify(outputSchema, null, 2)}\n`, "utf8");
13
+ const args = [
14
+ "exec", "--sandbox", "read-only", "--ephemeral", "--ignore-user-config",
15
+ "--skip-git-repo-check", "--output-schema", schemaPath,
16
+ "--output-last-message", outputPath, "--color", "never",
17
+ ];
18
+ if (model) args.push("--model", model);
19
+ args.push(prompt);
20
+ guardedExecFileSync("codex", args, {
21
+ cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: timeoutMs,
22
+ });
23
+ return readFileSync(outputPath, "utf8");
24
+ } finally {
25
+ rmSync(temp, { recursive: true, force: true });
26
+ }
27
+ }
28
+ if (surface === "claude") {
29
+ const args = [
30
+ "-p", "--disallowedTools", "Edit,Write,Bash", "--output-format", "json",
31
+ "--json-schema", JSON.stringify(outputSchema),
32
+ ];
33
+ if (model) args.push("--model", model);
34
+ args.push(prompt);
35
+ const raw = guardedExecFileSync("claude", args, {
36
+ cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: timeoutMs,
37
+ });
38
+ const envelope = JSON.parse(raw);
39
+ if (envelope.is_error) throw new Error(`claude live adapter failed: ${envelope.result ?? "unknown error"}`);
40
+ return envelope.result;
41
+ }
42
+ throw new Error(`unsupported live surface: ${surface}`);
43
+ }
@@ -0,0 +1,37 @@
1
+ import { DELIVERY_PATHS } from "./contracts.js";
2
+
3
+ export function buildBlindPrompt(testCase, behaviorSources) {
4
+ if (testCase.profile_id === "staged-v2") {
5
+ return [
6
+ "Perform a read-only, non-authorizing AGDF benchmark classification. Do not modify files, invoke tools, persist control state, or claim a real approval.",
7
+ "Synthetic fixture state is measurement input only. Return only JSON matching the supplied schema.",
8
+ `Allowed stages: ${testCase.allowed_stages.join(", ")}.`,
9
+ `Allowed paths: ${DELIVERY_PATHS.join(", ")}.`,
10
+ `Requested axes: ${testCase.requested_axes.join(", ")}. Every requested axis must use evaluated with one non-null allowed value. Non-requested axes must be null with not_evaluable_yet.`,
11
+ "eventual_delivery_path is a benchmark candidate, never an operative Mode/Slice Decision.",
12
+ "compact_delivery requires visible approved UR, completed Brownfield analysis, and stored quick_task context in decision_grounds.",
13
+ `Scenario ID: ${testCase.scenario_id}`,
14
+ `Case ID: ${testCase.case_id}`,
15
+ `Lifecycle stage: ${testCase.lifecycle_stage}`,
16
+ `Task: ${testCase.task_summary}`,
17
+ `Repository context: ${testCase.repository_context}`,
18
+ `Control-state context: ${testCase.control_state_context}`,
19
+ `Evidence pack: ${JSON.stringify(testCase.evidence_pack ?? {})}`,
20
+ "Current canonical routing sources follow:",
21
+ behaviorSources,
22
+ ].join("\n\n");
23
+ }
24
+ return [
25
+ "Classify the delivery path AGDF would select for this task. Do not modify files or invoke tools.",
26
+ "Return only JSON matching the supplied schema. Do not reveal hidden reasoning.",
27
+ `Allowed paths: ${DELIVERY_PATHS.join(", ")}.`,
28
+ "Use null plus ambiguous=true when the evidence does not support one path.",
29
+ "compact_delivery requires decision_grounds that visibly reference approved UR, completed Brownfield analysis, and stored quick_task context.",
30
+ `Case ID: ${testCase.case_id}`,
31
+ `Task: ${testCase.task_summary}`,
32
+ `Repository context: ${testCase.repository_context}`,
33
+ `Control-state context: ${testCase.control_state_context}`,
34
+ "Current canonical routing sources follow:",
35
+ behaviorSources,
36
+ ].join("\n\n");
37
+ }