triad-plus 1.9.0 → 1.10.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.10.0 — 2026-09-16
4
+
5
+ - Improve TUI and doctor output with terminal-native summaries, sober color
6
+ handling, `NO_COLOR`, and non-TTY support.
7
+ - Add the agent-first model-configuration skill with `.triad-plus/team.json`
8
+ as the role/model source of truth and explicit host-native bindings,
9
+ including OpenCode `variant` materialization.
10
+ - Add shared-skill diagnostics and repair during managed upgrades, plus
11
+ deterministic repository/runtime context verification and nested-repository
12
+ hardening.
13
+ - Fail closed on incorrect working directories, mixed Git roots, symlink
14
+ escapes, missing or mutated skills, and implicit fallback to unrelated skill
15
+ copies.
16
+
3
17
  ## 1.9.0 — 2026-09-16
4
18
 
5
19
  - Add native BMAD `epics.md` intake while preserving the boundary that BMAD
package/README.md CHANGED
@@ -184,6 +184,13 @@ workspace with that adapter. Triad+ does not dispatch different roles across
184
184
  different hosts in one run. Read [configuration](docs/configuration.md) and
185
185
  [runtime details](docs/runtimes.md).
186
186
 
187
+ The installed `triad-model-configuration` Agent Skill lets the host agent
188
+ inspect or change requested role models while preserving `team.json` as the
189
+ source of truth and reporting host-specific binding limits.
190
+
191
+ On OpenCode, `team.json`'s `reasoning_effort` is materialized as the native
192
+ per-agent `variant`; the OpenCode session default is a separate host setting.
193
+
187
194
  `triad-verify` validates assignment/candidate binding, runs declared
188
195
  control-plane gates, detects mutation, and writes evidence. It supports the
189
196
  loop; it never replaces the Orchestrator. See [verification](docs/verification.md).
@@ -1,7 +1,7 @@
1
1
  # Triad+ for Antigravity
2
2
 
3
3
  The Antigravity adapter uses native workspace paths: `.agents/skills/` for the
4
- five shared Triad+ skills and `/triad` workflow, plus `.agents/agents/` for the
4
+ six shared Triad+ skills and `/triad` workflow, plus `.agents/agents/` for the
5
5
  four technical agents. Start Antigravity from the project-control workspace and
6
6
  invoke:
7
7
 
@@ -30,7 +30,7 @@ fi
30
30
  adapter_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
31
31
  repository_root="$(cd -- "$adapter_root/../.." && pwd)"
32
32
  agent_names=(triad-orchestrator triad-developer triad-evaluator triad-reviewer)
33
- skill_names=(triad-loop-bootstrap triad-loop-orchestrator triad-loop-developer triad-loop-evaluator triad-loop-reviewer)
33
+ skill_names=(triad-loop-bootstrap triad-loop-orchestrator triad-loop-developer triad-loop-evaluator triad-loop-reviewer triad-model-configuration)
34
34
  collisions=()
35
35
 
36
36
  for agent_name in "${agent_names[@]}"; do
@@ -1,6 +1,6 @@
1
1
  # Triad+ Engineering Loop for Claude Code
2
2
 
3
- This adapter installs the shared five Triad skills, three isolated Claude Code
3
+ This adapter installs the shared six Triad skills, three isolated Claude Code
4
4
  subagents, the `/triad` command, and—on a project installation—the Node control
5
5
  plane. It does not modify global Claude settings or automatically activate a
6
6
  hook.
@@ -30,7 +30,7 @@ fi
30
30
  adapter_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
31
31
  repository_root="$(cd -- "$adapter_root/../.." && pwd)"
32
32
  agent_names=(triad-developer triad-evaluator triad-reviewer)
33
- skill_names=(triad-loop-bootstrap triad-loop-orchestrator triad-loop-developer triad-loop-evaluator triad-loop-reviewer)
33
+ skill_names=(triad-loop-bootstrap triad-loop-orchestrator triad-loop-developer triad-loop-evaluator triad-loop-reviewer triad-model-configuration)
34
34
  collisions=()
35
35
 
36
36
  for agent_name in "${agent_names[@]}"; do
@@ -18,7 +18,7 @@ Then install the global entry point and the skills once for the Codex user:
18
18
  ```
19
19
 
20
20
  The installer refuses any overwrite. It installs the project runtime under
21
- `.triad-runtime` and the five shared skills under `.agents/skills`; the global
21
+ `.triad-runtime` and the six shared skills under `.agents/skills`; the global
22
22
  installation also makes the skills available to Codex and writes
23
23
  `$CODEX_HOME/prompts/triad.md` (`$HOME/.codex` by default).
24
24
 
@@ -8,10 +8,10 @@ Usage:
8
8
  ./adapters/codex/install.sh --global
9
9
 
10
10
  Install the Triad Codex adapter without overwriting existing skills, prompt, or
11
- project runtime. --project installs the five skills under
11
+ project runtime. --project installs the six skills under
12
12
  <control-repository-path>/.agents/skills and installs .triad-runtime. It does
13
13
  not install the user-level Codex prompt. --global installs the /prompts:triad
14
- entry point and the five skills under $CODEX_HOME (default: $HOME/.codex).
14
+ entry point and the six skills under $CODEX_HOME (default: $HOME/.codex).
15
15
 
16
16
  For normal use run both commands once: --project for each control repository,
17
17
  then --global for the Codex entry point. Existing paths are never overwritten.
@@ -19,7 +19,7 @@ EOF
19
19
  }
20
20
 
21
21
  codex_home="${CODEX_HOME:-$HOME/.codex}"
22
- skill_names=(triad-loop-bootstrap triad-loop-orchestrator triad-loop-developer triad-loop-evaluator triad-loop-reviewer)
22
+ skill_names=(triad-loop-bootstrap triad-loop-orchestrator triad-loop-developer triad-loop-evaluator triad-loop-reviewer triad-model-configuration)
23
23
 
24
24
  if [[ $# -eq 2 && "$1" == "--project" ]]; then
25
25
  project_root="$2"
@@ -18,7 +18,7 @@ fi
18
18
 
19
19
  adapter_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
20
20
  repository_root="$(cd -- "$adapter_root/../.." && pwd)"
21
- skill_names=(triad-loop-bootstrap triad-loop-orchestrator triad-loop-developer triad-loop-evaluator triad-loop-reviewer)
21
+ skill_names=(triad-loop-bootstrap triad-loop-orchestrator triad-loop-developer triad-loop-evaluator triad-loop-reviewer triad-model-configuration)
22
22
 
23
23
  if [[ "$global_install" == true ]]; then
24
24
  hermes_root="${HERMES_HOME:-$HOME/.hermes}"
@@ -28,6 +28,20 @@ At activation, read `.triad-plus/team.json`. Your first report to the
28
28
  Orchestrator identifies the configured `roles.developer.displayName` as the
29
29
  Triad+ Developer and names the assigned card.
30
30
 
31
+ Before editing, run the exact runtime-context proof from this activation's
32
+ actual cwd:
33
+
34
+ ```bash
35
+ node /absolute/path/to/control-workspace/.triad-runtime/triad-runtime-context.mjs \
36
+ --project /absolute/path/to/control-workspace \
37
+ --assignment /absolute/path/to/control-workspace/.loop/runtime/assignments/<assignment-file>.json
38
+ ```
39
+
40
+ Record packet cwd, process cwd, shell `pwd`, Git top-level, and every resolved
41
+ repository-skill path/hash. A non-zero result or any mismatch is invalid
42
+ runtime context: stop and report it, and never substitute a control-workspace
43
+ or global skill copy.
44
+
31
45
  Verify the worktree and branch. Do not expand scope, silently change project
32
46
  policy, add dependencies without authorization, or make delivery decisions. Run
33
47
  the required gates, measure the declared metrics, and report exact commands and
@@ -17,6 +17,7 @@ permission:
17
17
  triad-reviewer: allow
18
18
  skill:
19
19
  "triad-loop-*": allow
20
+ "triad-model-configuration": allow
20
21
  ---
21
22
 
22
23
  You are the Triad+ Engineering Loop orchestrator. Govern the workflow and the
@@ -30,6 +30,21 @@ At activation, read `.triad-plus/team.json`. Your first report to the
30
30
  Orchestrator identifies the configured `roles.reviewer.displayName` as the
31
31
  Triad+ Reviewer and names the feature and attempt under review.
32
32
 
33
+ Before inspecting the candidate, run this exact runtime-context proof from the
34
+ actual Reviewer cwd:
35
+
36
+ ```bash
37
+ node /absolute/path/to/control-workspace/.triad-runtime/triad-runtime-context.mjs \
38
+ --project /absolute/path/to/control-workspace \
39
+ --assignment /absolute/path/to/control-workspace/.loop/runtime/assignments/<assignment-file>.json
40
+ ```
41
+
42
+ Record packet cwd, process cwd, shell `pwd`, Git top-level, and each resolved
43
+ repository-skill path/hash. On non-zero status, `NotFound`, hash mismatch,
44
+ mixed repository roots, or cwd inconsistency, return `blocked` / invalid
45
+ runtime context. Do not use a same-named skill from the control workspace or
46
+ global skills as a fallback.
47
+
33
48
  Return one evidence-based recommendation: `approved`, `rework`, or `blocked`.
34
49
  List severity-ranked findings before gate and metric evidence, residual risks,
35
50
  and the recommendation. A blocked recommendation must identify the exact owner
@@ -1,6 +1,6 @@
1
1
  # Triad+ Engineering Loop for OpenCode
2
2
 
3
- This adapter makes the repository's existing five standard Agent Skills
3
+ This adapter makes the repository's existing six standard Agent Skills
4
4
  operational in OpenCode. It installs four specialized agents and the `/triad`
5
5
  command; it does not duplicate the method or alter `opencode.json`.
6
6
 
@@ -26,6 +26,7 @@ skills/triad-loop-orchestrator/
26
26
  skills/triad-loop-developer/
27
27
  skills/triad-loop-evaluator/
28
28
  skills/triad-loop-reviewer/
29
+ skills/triad-model-configuration/
29
30
  .triad-runtime/
30
31
  triad-verify.mjs
31
32
  triad-runtime-capabilities.mjs
@@ -55,12 +56,12 @@ remain in a declared project workspace.
55
56
 
56
57
  `.triad-plus/team.json` is the source of truth for role-level model
57
58
  configuration. When a team file supplies values, Triad+ materializes the
58
- OpenCode-native `model` and `reasoningEffort` frontmatter fields in each
59
- project agent definition during `init` and `upgrade --apply`:
59
+ OpenCode-native `model` and `variant` frontmatter fields in each project agent
60
+ definition during `init` and `upgrade --apply`:
60
61
 
61
62
  ```text
62
63
  team.json model → OpenCode model
63
- team.json reasoning_effort → OpenCode reasoningEffort
64
+ team.json reasoning_effort → OpenCode variant
64
65
  ```
65
66
 
66
67
  Do not treat generated `.opencode/agents/*.md` files as an independent
@@ -70,8 +71,9 @@ absent so OpenCode uses its host/session default.
70
71
 
71
72
  The adapter only writes fields supported by this OpenCode materialization. Host
72
73
  and provider capabilities may still limit which model or reasoning levels are
73
- available. Inspect identifiers with `opencode models` and keep role-specific
74
- routing explicit in the team file:
74
+ available. Inspect identifiers and variants with `opencode models` and keep
75
+ role-specific routing explicit in the team file. OpenCode's role `variant` is
76
+ not the same thing as the default model/session selected by the TUI:
75
77
 
76
78
  | Agent | Recommended profile |
77
79
  | --- | --- |
@@ -84,6 +86,13 @@ Use `opencode models` to inspect the provider/model identifiers available in
84
86
  the local installation. Keep the developer, evaluator, and reviewer separately
85
87
  configured when independent model routing is required.
86
88
 
89
+ The adapter targets the installed OpenCode agent contract: `variant` is the
90
+ host-native per-agent option, while `team.json` remains the canonical desired
91
+ configuration. A null or blank model/effort removes the managed field and lets
92
+ OpenCode use its host or session default. `upgrade --apply` refreshes managed
93
+ agents and then reapplies this same binding; it does not rewrite `team.json`.
94
+ OpenCode 1.18.x exposes this contract through the `AgentConfig.variant` field.
95
+
87
96
  ## Start the loop
88
97
 
89
98
  Launch OpenCode from the project-control repository, then enter:
@@ -31,7 +31,7 @@ adapter_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
31
31
  repository_root="$(cd -- "$adapter_root/../.." && pwd)"
32
32
 
33
33
  agent_names=(triad-orchestrator triad-developer triad-evaluator triad-reviewer)
34
- skill_names=(triad-loop-bootstrap triad-loop-orchestrator triad-loop-developer triad-loop-evaluator triad-loop-reviewer)
34
+ skill_names=(triad-loop-bootstrap triad-loop-orchestrator triad-loop-developer triad-loop-evaluator triad-loop-reviewer triad-model-configuration)
35
35
  command_name="triad"
36
36
  collisions=()
37
37
 
@@ -7,7 +7,8 @@ export const sharedSkillNames = [
7
7
  'triad-loop-orchestrator',
8
8
  'triad-loop-developer',
9
9
  'triad-loop-evaluator',
10
- 'triad-loop-reviewer'
10
+ 'triad-loop-reviewer',
11
+ 'triad-model-configuration'
11
12
  ];
12
13
 
13
14
  export const roleDefinitions = [
@@ -87,7 +88,13 @@ const definitions = [
87
88
  entry: '/triad',
88
89
  lifecycle: null,
89
90
  modelBinding: 'project-frontmatter',
90
- modelFields: ['model', 'reasoningEffort'],
91
+ // OpenCode's agent frontmatter has a first-class `variant` field. Keep
92
+ // Triad's canonical `reasoning_effort` name in team.json and map it to
93
+ // that host-native field rather than emitting provider options such as
94
+ // `reasoningEffort` into the agent profile.
95
+ modelFields: ['model', 'variant'],
96
+ modelFieldSources: { model: 'model', variant: 'reasoning_effort' },
97
+ modelCleanupFields: ['reasoningEffort'],
91
98
  modelRoles: roleDefinitions.map((role) => role.id),
92
99
  projectAssets: [
93
100
  { source: 'adapters/opencode/.opencode/agents', destination: '.opencode/agents' },
@@ -114,6 +121,10 @@ const definitions = [
114
121
  },
115
122
  roleModelPaths(controlRoot) {
116
123
  return roleDefinitions.map((role) => join(controlRoot, '.opencode', 'agents', `triad-${role.id}.md`));
124
+ },
125
+ globalRoleModelPaths() {
126
+ const root = hostHome('.config', 'opencode');
127
+ return rolePaths(root, roleDefinitions.map((role) => `triad-${role.id}`));
117
128
  }
118
129
  },
119
130
  {
@@ -157,6 +168,10 @@ const definitions = [
157
168
  },
158
169
  roleModelPaths(controlRoot) {
159
170
  return ['developer', 'reviewer', 'evaluator'].map((role) => join(controlRoot, '.claude', 'agents', `triad-${role}.md`));
171
+ },
172
+ globalRoleModelPaths() {
173
+ const root = hostHome('.claude');
174
+ return ['developer', 'reviewer', 'evaluator'].map((role) => join(root, 'agents', `triad-${role}.md`));
160
175
  }
161
176
  },
162
177
  {
@@ -222,6 +237,7 @@ const definitions = [
222
237
  lifecycle: null,
223
238
  modelBinding: 'project-frontmatter',
224
239
  modelFields: ['model', 'reasoningEffort'],
240
+ modelFieldSources: { model: 'model', reasoningEffort: 'reasoning_effort' },
225
241
  modelRoles: roleDefinitions.map((role) => role.id),
226
242
  projectAssets: [
227
243
  { source: 'adapters/copilot/.github/agents', destination: '.github/agents' },
@@ -253,6 +269,10 @@ const definitions = [
253
269
  },
254
270
  roleModelPaths(controlRoot) {
255
271
  return roleDefinitions.map((role) => join(controlRoot, '.github', 'agents', `triad-${role.id}.agent.md`));
272
+ },
273
+ globalRoleModelPaths() {
274
+ const root = hostHome('.copilot');
275
+ return roleDefinitions.map((role) => join(root, 'agents', `triad-${role.id}.agent.md`));
256
276
  }
257
277
  }
258
278
  ];