@ikon85/agent-workflow-kit 0.34.3 → 0.34.5

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 (35) hide show
  1. package/.agents/skills/kit-release/SKILL.md +12 -0
  2. package/.agents/skills/kit-update/SKILL.md +6 -3
  3. package/.agents/skills/setup-workflow/SKILL.md +13 -5
  4. package/.agents/skills/setup-workflow/assets/agent-workflow-kit-update.yml +1 -3
  5. package/.claude/skills/kit-release/SKILL.md +12 -0
  6. package/.claude/skills/kit-update/SKILL.md +6 -3
  7. package/.claude/skills/setup-workflow/SKILL.md +13 -5
  8. package/.claude/skills/setup-workflow/assets/agent-workflow-kit-update.yml +1 -3
  9. package/README.md +17 -0
  10. package/agent-workflow-kit.package.json +12 -12
  11. package/docs/adr/0001-consumer-divergence-policy.md +4 -0
  12. package/docs/adr/0003-kit-core-and-project-extension-lifecycle.md +63 -0
  13. package/docs/agents/board-sync.md +1 -1
  14. package/docs/agents/workflow-capabilities.json +17 -0
  15. package/docs/research/benchlm-routing-source.md +198 -0
  16. package/docs/research/consumer-owned-protocol-files.md +238 -0
  17. package/docs/research/frontend-agent-benchmarks.md +282 -0
  18. package/docs/research/model-effort-routing-benchmarks.md +261 -0
  19. package/docs/research/provider-neutral-agent-routing.md +207 -0
  20. package/package.json +1 -1
  21. package/scripts/kit-update-pr.mjs +1 -1
  22. package/scripts/kit-update-pr.test.mjs +2 -0
  23. package/scripts/release-delta-guard.mjs +28 -1
  24. package/scripts/release-delta-guard.test.mjs +45 -0
  25. package/scripts/release-state.mjs +7 -3
  26. package/scripts/release-state.test.mjs +22 -0
  27. package/scripts/test_skill_readiness_contract.py +6 -1
  28. package/scripts/test_skill_setup_workflow_seeds.py +18 -17
  29. package/src/commands/update.mjs +52 -20
  30. package/src/lib/bundle.mjs +1 -1
  31. package/src/lib/updateCandidate.mjs +278 -50
  32. package/src/lib/verifyUpdateCandidate.mjs +220 -0
  33. package/src/lib/verifyUpdateCandidateArtifacts.mjs +78 -0
  34. package/src/lib/verifyUpdateCandidateProtocol.mjs +221 -0
  35. package/src/lib/verifyUpdateCandidateTransaction.mjs +152 -0
@@ -0,0 +1,261 @@
1
+ # Benchmark evidence for model and effort routing
2
+
3
+ **Researched:** 2026-07-22
4
+ **Question:** How should current benchmark evidence inform a provider-neutral,
5
+ user-owned routing policy without turning today's model and effort winners into
6
+ permanent Kit defaults?
7
+
8
+ ## Conclusion
9
+
10
+ The evidence supports a **mutable, workload-specific routing policy**, not a
11
+ universal mapping such as `mechanical -> Luna/high`, `development ->
12
+ Terra/max`, or `judgment -> Fable/high`.
13
+
14
+ Three findings matter for the current design:
15
+
16
+ 1. **Effort scaling is model-specific.** On DeepSWE, moving from `xhigh` to
17
+ `max` adds about 9.4 pass-rate points for GPT-5.6 Terra and 10.3 points for
18
+ Luna. For Claude Fable 5, `high`, `xhigh`, and `max` overlap within the
19
+ reported uncertainty while average cost rises from $9.18 to $21.63 per
20
+ trial.
21
+ 2. **The best route depends on the workload and objective.** Sol leads the
22
+ current Artificial Analysis coding-agent aggregate and DataCurve DeepSWE;
23
+ Fable leads Sol on Artificial Analysis' repository-Q&A component and has
24
+ separate strengths in knowledge work. Luna is exceptionally cost-efficient.
25
+ Artificial Analysis' intelligence results place Luna and Sol, not Terra, on
26
+ the cross-family cost/quality frontier.
27
+ 3. **Benchmark rows are configurations, not timeless model facts.** Harness,
28
+ effort, fallback behavior, benchmark version, provider pricing, retries, and
29
+ access failures all affect the result. A model mapping inferred today can be
30
+ stale when a new family, alias, effort level, or benchmark revision lands.
31
+
32
+ The Kit should therefore own a routing schema, evidence format, resolver, and
33
+ reconciliation workflow. The user or organization should own the current
34
+ model/effort choices. Benchmarks can generate a dated recommendation, but may
35
+ not silently rewrite that policy.
36
+
37
+ ## Current evidence
38
+
39
+ ### DataCurve DeepSWE v1.1
40
+
41
+ DeepSWE contains 113 original, long-horizon software-engineering tasks across
42
+ 91 repositories and five languages. The current leaderboard was updated on
43
+ 2026-07-21. Every row below uses the same `mini-swe-agent` harness and four
44
+ whole-benchmark runs; pass-rate uncertainty is DataCurve's reported 95%
45
+ run-to-run interval. Costs are average API cost per completed trial
46
+ ([leaderboard](https://deepswe.datacurve.ai/),
47
+ [machine-readable leaderboard artifact](https://deepswe.datacurve.ai/artifacts/v1.1/leaderboard-live.json)).
48
+
49
+ | Configuration | Pass@1 | 95% half-width | Avg cost/task | Attempts |
50
+ |---|---:|---:|---:|---:|
51
+ | GPT-5.6 Luna medium | 11.3% | ±0.8 | $0.22 | 452 |
52
+ | GPT-5.6 Luna high | 44.2% | ±2.9 | $0.78 | 452 |
53
+ | GPT-5.6 Luna xhigh | 56.9% | ±2.2 | $1.54 | 452 |
54
+ | **GPT-5.6 Luna max** | **67.2%** | **±4.0** | **$3.03** | 448 |
55
+ | GPT-5.6 Terra medium | 35.1% | ±3.4 | $0.58 | 450 |
56
+ | GPT-5.6 Terra high | 53.8% | ±4.3 | $1.13 | 452 |
57
+ | GPT-5.6 Terra xhigh | 60.2% | ±2.1 | $2.13 | 452 |
58
+ | **GPT-5.6 Terra max** | **69.6%** | **±2.6** | **$4.95** | 451 |
59
+ | GPT-5.6 Sol medium | 61.1% | ±1.6 | $1.86 | 452 |
60
+ | **GPT-5.6 Sol high** | **69.4%** | **±1.4** | **$3.47** | 451 |
61
+ | GPT-5.6 Sol xhigh | 70.7% | ±0.8 | $4.70 | 451 |
62
+ | GPT-5.6 Sol max | 72.7% | ±2.8 | $8.39 | 450 |
63
+ | Claude Fable 5 medium | 65.4% | ±4.4 | $6.09 | 436 |
64
+ | **Claude Fable 5 high** | **68.6%** | **±1.1** | **$9.18** | 430 |
65
+ | Claude Fable 5 xhigh | 69.9% | ±3.2 | $13.41 | 452 |
66
+ | Claude Fable 5 max | 69.7% | ±4.0 | $21.63 | 436 |
67
+
68
+ What this does and does not establish:
69
+
70
+ - **Luna max is strongly efficient for this workload.** It reaches 67.2% for
71
+ $3.03, statistically overlaps Terra max and Fable's higher-effort rows, and
72
+ is cheaper than all of them.
73
+ - **Terra max is a real within-family improvement.** It gains 9.4 points over
74
+ Terra xhigh and 15.9 over high. On point estimates across families, however,
75
+ Sol xhigh is both slightly cheaper and slightly higher-scoring, so Terra max
76
+ is not a universal Pareto winner.
77
+ - **Sol high is a defensible efficiency point.** Sol max adds 3.3 points to
78
+ high for about 2.4 times the cost, and their confidence intervals overlap.
79
+ Sol xhigh adds 1.3 points for about 1.36 times the cost. This supports the
80
+ user's observation that `high` can be sufficient, but it does not prove the
81
+ higher efforts never matter.
82
+ - **Fable high is the clearest “high is sufficient” result here.** High,
83
+ xhigh, and max are statistically overlapping; max has a slightly lower point
84
+ estimate than xhigh while costing 2.36 times high. The result argues against
85
+ defaulting Fable to max for implementation work.
86
+
87
+ DeepSWE itself warns that adjacent frontier configurations often overlap in
88
+ confidence intervals. Its v1.1 execution grades only committed patches in a
89
+ fresh verifier container and removed dependency drift and flaky tests. It also
90
+ reports that 73 of Fable's 2,260 trials did not complete after access was
91
+ suspended during the sweep; Fable rates are calculated over completed trials
92
+ ([v1.1 report](https://deepswe.datacurve.ai/blog/deepswe-v1-1)).
93
+
94
+ The standardized harness is both a strength and a limitation. It isolates the
95
+ model comparison, but gives every model one Bash tool and a shared prompt
96
+ instead of Codex's or Claude Code's native editing tools and prompts. DataCurve
97
+ explicitly says the leaderboard does not directly represent those native
98
+ products, and the corpus under-represents bug localization and refactoring
99
+ ([DeepSWE methodology and limitations](https://deepswe.datacurve.ai/blog/deepswe#limitations)).
100
+
101
+ ### Artificial Analysis
102
+
103
+ #### Current coding-agent comparison
104
+
105
+ Artificial Analysis' current Coding Agent Index v1.3 combines DeepSWE,
106
+ Terminal-Bench, and SWE-Atlas-QnA. The currently exposed Codex/Claude Code rows
107
+ use `max` effort. They are **agent + model + settings** measurements, not the
108
+ same mini-swe-agent experiment as DataCurve
109
+ ([current comparison](https://artificialanalysis.ai/agents/coding-agents/comparisons/claude-code-vs-codex),
110
+ [coding-agent methodology](https://artificialanalysis.ai/methodology/coding-agents-benchmarking)).
111
+
112
+ | Agent configuration | Coding Agent Index | DeepSWE | Terminal-Bench | SWE-Atlas-QnA | Avg cost/task |
113
+ |---|---:|---:|---:|---:|---:|
114
+ | Codex + GPT-5.6 Sol max | 66.57 | 68.73% | 87.70% | 43.28% | $7.08 |
115
+ | Codex + GPT-5.6 Terra max | 62.28 | 66.96% | 84.13% | 35.75% | $2.76 |
116
+ | Codex + GPT-5.6 Luna max | 58.66 | 63.42% | 79.76% | 32.80% | $1.57 |
117
+ | Claude Code + Fable 5 max | 65.85 | 66.08% | 82.54% | **48.92%** | $11.71 |
118
+
119
+ Sol narrowly leads the aggregate and the implementation/terminal components;
120
+ Fable leads repository Q&A. Luna gives up about 7.9 index points to Sol while
121
+ costing about 78% less per task. That supports different routes for repository
122
+ analysis, demanding implementation, and cost-sensitive execution rather than
123
+ one global “best model.”
124
+
125
+ Do not mix these figures with Artificial Analysis' July 9 launch chart without
126
+ a version label. The launch chart used the prior scoring contract and reported
127
+ 80/77/77/75 for Sol/Terra/Fable/Luna. Coding Agent Index v1.2 changed
128
+ SWE-Atlas-QnA from rubric reward to binary all-criteria success and v1.3 refined
129
+ the alignment, so the current values are not a performance regression measured
130
+ on an unchanged scale
131
+ ([launch analysis](https://artificialanalysis.ai/articles/gpt-5-6-has-landed),
132
+ [versioned methodology](https://artificialanalysis.ai/methodology/coding-agents-benchmarking)).
133
+
134
+ #### Intelligence-versus-cost by effort
135
+
136
+ Artificial Analysis Intelligence Index v4.1 is a broader, English text-only
137
+ composite: agents 34%, coding 24%, scientific reasoning 24%, and general
138
+ capability 18%. Artificial Analysis estimates the composite's 95% confidence
139
+ interval below ±1%, while warning that individual evaluations can be wider
140
+ ([Intelligence Index methodology](https://artificialanalysis.ai/methodology/intelligence-benchmarking)).
141
+
142
+ Current model pages expose the following scores and weighted API cost per
143
+ Intelligence Index task:
144
+
145
+ | Model | Medium | High | Xhigh | Max |
146
+ |---|---:|---:|---:|---:|
147
+ | GPT-5.6 Sol | 54 / $0.314 | 56 / $0.453 | 58 / $0.682 | 59 / $1.037 |
148
+ | GPT-5.6 Terra | 46 / $0.175 | 49 / $0.336 | 52 / $0.477 | 55 / $0.825 |
149
+ | GPT-5.6 Luna | 38 / $0.050 | 46 / $0.095 | 49 / $0.139 | 51 / $0.209 |
150
+ | Claude Fable 5 | — | — | — | 60 / $2.750 |
151
+
152
+ Sources: Artificial Analysis model pages for
153
+ [Sol max](https://artificialanalysis.ai/models/gpt-5-6-sol),
154
+ [Sol high](https://artificialanalysis.ai/models/gpt-5-6-sol-high),
155
+ [Terra max](https://artificialanalysis.ai/models/gpt-5-6-terra),
156
+ [Terra high](https://artificialanalysis.ai/models/gpt-5-6-terra-high),
157
+ [Luna max](https://artificialanalysis.ai/models/gpt-5-6-luna),
158
+ [Luna high](https://artificialanalysis.ai/models/gpt-5-6-luna-high), and
159
+ [Fable max](https://artificialanalysis.ai/models/claude-fable-5).
160
+
161
+ Artificial Analysis' own cross-effort analysis concludes that Luna and Sol are
162
+ ahead of Terra at every point on this composite's intelligence-versus-cost
163
+ chart: for any Terra effort, a Luna or Sol configuration is at least as capable
164
+ for no more cost, or equally capable for less. Luna is the standout
165
+ cost-efficient family
166
+ ([GPT-5.6 intelligence-versus-cost analysis](https://artificialanalysis.ai/articles/gpt-5-6-intelligence-vs-cost-across-sol-terra-luna)).
167
+
168
+ This result does **not** contradict Terra max's strong DeepSWE result. It shows
169
+ why a single benchmark or a single global Pareto frontier cannot determine the
170
+ route for every workload.
171
+
172
+ ## Provider guidance and availability
173
+
174
+ OpenAI describes Sol, Terra, and Luna as durable capability tiers that can
175
+ advance independently. Codex users with access can select each tier and an
176
+ effort level; `max` is available across the family. OpenAI prices Sol at
177
+ $5/$30, Terra at $2.50/$15, and Luna at $1/$6 per million input/output tokens
178
+ ([GPT-5.6 launch and availability](https://openai.com/index/gpt-5-6/)).
179
+
180
+ Anthropic exposes `low`, `medium`, `high`, `xhigh`, and `max` where supported.
181
+ It defines effort as a behavioral signal rather than a strict token budget and
182
+ says the effect varies by workload. For Fable 5, Anthropic recommends starting
183
+ at `high`, using `xhigh` only for the most capability-sensitive workloads, and
184
+ lowering effort for routine work. Its general guidance is to use `max` only
185
+ when the absolute highest capability justifies unconstrained token spending
186
+ ([Anthropic effort guidance](https://platform.claude.com/docs/en/build-with-claude/effort#recommended-effort-levels-for-claude-fable-5)).
187
+
188
+ The Artificial Analysis Fable results also require a provenance warning:
189
+ Fable's evaluated configuration uses adaptive reasoning at max effort with an
190
+ Opus 4.8 fallback. It is a routing-system result, not a pure Fable-only result
191
+ ([Artificial Analysis Fable analysis](https://artificialanalysis.ai/articles/claude-fable-5-mythos)).
192
+
193
+ ## Measurement caveats
194
+
195
+ - **Costs are estimates for the measured API traffic.** They include reported
196
+ input/output and supported cache prices, but exclude subscription economics,
197
+ developer supervision, CI, failed deployments, and the cost of rerunning an
198
+ incorrect patch. Provider or gateway pricing can differ.
199
+ - **Retries differ by benchmark.** Artificial Analysis retries API failures up
200
+ to 30 times and withholds persistently broken results; this is reliability
201
+ handling, not multiple solution attempts. DeepSWE reports pass@1 over four
202
+ repeated whole-benchmark runs and exposes incomplete attempts separately.
203
+ - **Pass rate is not expected project value.** A cheaper row can be worse if a
204
+ failure causes an expensive human recovery. Conversely, paying for max on a
205
+ highly constrained mechanical task can waste tokens without reducing risk.
206
+ - **Statistical overlap matters.** Point estimates such as Sol high versus max
207
+ or Fable high versus max should not be treated as proven capability gaps when
208
+ their intervals overlap.
209
+ - **Harness and task mix matter.** DataCurve's model-neutral Bash harness and
210
+ Artificial Analysis' native Codex/Claude Code rows answer different
211
+ questions. Neither directly measures this Kit's planning, grilling,
212
+ orchestration, review, or release workflows.
213
+ - **Evidence expires.** Both leaderboards were updated during July 2026 and
214
+ their benchmark contracts have already changed. A recommendation without a
215
+ source date, benchmark version, and configuration identity is unsafe input
216
+ to automation.
217
+
218
+ ## Implication for the Kit routing policy
219
+
220
+ The durable issue contract should continue to contain provider-neutral work
221
+ intent. The mutable user or organization policy should be richer than a fixed
222
+ `tier -> model + effort` lookup:
223
+
224
+ ```yaml
225
+ routing-intent: implementation
226
+ objective:
227
+ quality-floor: normal
228
+ optimize: expected-cost
229
+ risk:
230
+ recovery-cost: medium
231
+ ```
232
+
233
+ At reconciliation time, a surface adapter can evaluate the user's allowed
234
+ models against a dated evidence catalog and propose a concrete route. The
235
+ catalog entry must identify at least:
236
+
237
+ - provider, model or alias, and effort;
238
+ - benchmark name and version;
239
+ - workload tags and harness;
240
+ - score, uncertainty, average cost, and observation date;
241
+ - fallback behavior and important missing data.
242
+
243
+ The policy lifecycle should be:
244
+
245
+ 1. `setup-workflow` installs the schema and defaults to `inherit`; it may offer
246
+ an explicit user-local policy setup.
247
+ 2. Kit releases update schemas, adapters, and an optional evidence snapshot,
248
+ but never overwrite personal mappings.
249
+ 3. A separate `routing-policy reconcile` compares current choices with
250
+ available models and dated evidence, shows the relevant Pareto candidates,
251
+ and asks before changing the user-global policy.
252
+ 4. The resolver records the effective model and effort plus the policy/evidence
253
+ revision in run evidence.
254
+ 5. Local outcome telemetry and explicit user judgment may override public
255
+ benchmark recommendations. Public data supplies a prior; the user's real
256
+ workflow is the calibration set.
257
+
258
+ This permits a current personal preference such as Fable high for planning,
259
+ Sol or Terra for implementation, and Luna for cheap mechanical work without
260
+ claiming that those mappings are correct for every consumer—or even for the
261
+ same user after the next model or benchmark release.
@@ -0,0 +1,207 @@
1
+ # Provider-neutral agent routing for shared work items
2
+
3
+ **Researched:** 2026-07-22
4
+ **Question:** How should shared agent-workflow issues express model-routing intent across Claude Code and OpenAI Codex without embedding short-lived provider model names?
5
+
6
+ ## Recommendation
7
+
8
+ Persist **task intent, not a model selection**, in shared issues. Use two small,
9
+ provider-neutral fields:
10
+
11
+ ```yaml
12
+ routing-intent: judgment | development | mechanical
13
+ reasoning-intent: deep | balanced | light
14
+ ```
15
+
16
+ The exact vocabulary is a Kit domain decision; the important constraint is that
17
+ the values describe the work and desired trade-off, not Anthropic or OpenAI
18
+ products. A surface-local resolver maps those values at dispatch time:
19
+
20
+ ```text
21
+ shared issue intent
22
+ |
23
+ +-- Claude resolver -> current Claude alias/model + session effort
24
+ |
25
+ `-- Codex resolver -> current Codex model + model_reasoning_effort
26
+ ```
27
+
28
+ The default resolution must be `inherit` when no local policy exists or when a
29
+ configured target cannot be proven usable. The orchestrator should report the
30
+ effective resolution at session start, but it should not write that volatile
31
+ result back into the issue.
32
+
33
+ This separates three ownership layers:
34
+
35
+ 1. **Kit-owned schema and validation:** allowed intent values, inheritance,
36
+ diagnostics, and dispatch contract.
37
+ 2. **User/organization-owned surface policy:** how intent maps to models and
38
+ effort for that account, budget, provider, and current catalog.
39
+ 3. **Project-owned constraints, only when genuinely shared:** for example, a
40
+ prohibition on a model or a minimum capability required by every
41
+ collaborator. A project should not carry one maintainer's preferred model
42
+ mapping.
43
+
44
+ ## Verified platform facts
45
+
46
+ ### Claude Code
47
+
48
+ Claude Code has Managed, User, Project, and Local scopes. For ordinary scalar
49
+ settings, precedence is Managed, command-line, Local, Project, then User. A
50
+ committed `.claude/settings.json` therefore affects all collaborators and
51
+ overrides their user settings, while `.claude/settings.local.json` is personal
52
+ and repository-specific ([Claude Code settings: scopes and precedence](https://code.claude.com/docs/en/settings#configuration-scopes)).
53
+
54
+ Claude accepts provider aliases as well as full model IDs. Aliases such as
55
+ `fable` and `sonnet` resolve to current models, while `default` returns to the
56
+ runtime default for the account or organization. Aliases reduce version churn,
57
+ but remain Claude-specific and do not encode a cross-provider task class
58
+ ([Claude Code model aliases](https://code.claude.com/docs/en/model-config#model-aliases)).
59
+
60
+ The main-session model can be changed for a session with `/model` or at launch
61
+ with `--model`; `ANTHROPIC_MODEL` and the persistent `model` setting are also
62
+ supported. Organization restrictions and managed settings can replace or
63
+ reject a requested selection, so a repository cannot assume that a named model
64
+ is usable for every collaborator ([Claude Code model selection](https://code.claude.com/docs/en/model-config#setting-your-model),
65
+ [Claude Code model restrictions](https://code.claude.com/docs/en/model-config#restrict-model-selection)).
66
+
67
+ Claude subagent definitions support an alias, a full ID, or `inherit`; omission
68
+ means `inherit`. Resolution currently considers
69
+ `CLAUDE_CODE_SUBAGENT_MODEL`, a per-invocation value, subagent frontmatter, and
70
+ finally the main conversation model. Excluded selections fall back to the
71
+ inherited model. Extended-thinking configuration is inherited from the main
72
+ conversation and has no per-subagent setting
73
+ ([Claude Code subagent model resolution](https://code.claude.com/docs/en/sub-agents#choose-a-model)).
74
+
75
+ Claude effort levels are model-dependent. Unsupported levels are reduced to a
76
+ supported level, organization policy can cap them, and some effort choices are
77
+ session-only. Skill and subagent frontmatter can override the inherited session
78
+ effort, subject to higher-precedence environment and organization constraints.
79
+ Therefore a provider-neutral reasoning intent can be resolved on Claude, but a
80
+ shared issue must not claim that one literal effort value has identical
81
+ enforcement everywhere
82
+ ([Claude Code effort levels](https://code.claude.com/docs/en/model-config#adjust-effort-level),
83
+ [Claude Code subagent effort](https://code.claude.com/docs/en/sub-agents#supported-frontmatter-fields)).
84
+
85
+ Anthropic exposes `GET /v1/models`, whose result describes models available to
86
+ that API credential ([Anthropic Models API](https://platform.claude.com/docs/en/api/models/list)).
87
+ That is not a universal Claude Code discovery contract: Claude Code may run
88
+ through a Claude subscription, Bedrock, Vertex, Foundry, or a gateway. Gateway
89
+ discovery is optional, limited to compatible `/v1/models` gateways, cached,
90
+ and falls back to built-in entries if discovery fails
91
+ ([Claude Code gateway model discovery](https://code.claude.com/docs/en/llm-gateway#model-selection)).
92
+
93
+ ### OpenAI Codex
94
+
95
+ Codex reads personal defaults from `~/.codex/config.toml` and project overrides
96
+ from trusted `.codex/config.toml` files. Its documented precedence is CLI,
97
+ project config, selected user profile, user config, system config, then built-in
98
+ defaults. Consequently, a committed project routing table can override a
99
+ collaborator's personal defaults; untrusted projects skip project-local layers
100
+ entirely ([Codex configuration precedence](https://learn.chatgpt.com/docs/config-file/config-basic#configuration-precedence)).
101
+
102
+ Codex exposes `model` and `model_reasoning_effort` as separate configuration
103
+ values ([Codex common configuration options](https://learn.chatgpt.com/docs/config-file/config-basic#common-configuration-options)).
104
+ For custom subagents, a custom-agent file may override both. Otherwise each is
105
+ resolved independently from an explicit spawn value, the `[agents]` default,
106
+ and the parent session. Omitting a setting inherits it; changing the model
107
+ without an explicit effort uses the selected model's default effort
108
+ ([Codex custom-agent resolution](https://learn.chatgpt.com/docs/agent-configuration/subagents#custom-agents)).
109
+
110
+ Official Codex guidance explicitly allows leaving both values unpinned so the
111
+ runtime can balance capability, speed, and price. It also documents that
112
+ available reasoning levels depend on the selected model
113
+ ([Codex choosing models and reasoning](https://learn.chatgpt.com/docs/agent-configuration/subagents#choosing-models-and-reasoning)).
114
+
115
+ OpenAI exposes `GET /v1/models` for models available to an API key
116
+ ([OpenAI Models API](https://platform.openai.com/docs/api-reference/models/list)).
117
+ The Codex documentation does not provide a stable, public model-discovery API
118
+ for the effective catalog of every Codex/ChatGPT account and local client.
119
+ An API-key catalog therefore cannot safely populate routing for users whose
120
+ Codex authentication, plan, organization policy, or client surface differs.
121
+
122
+ ## Options compared
123
+
124
+ | Option | Survives catalog changes | Works across Claude and Codex | Multi-user behavior | Offline/update behavior | Verdict |
125
+ |---|---:|---:|---|---|---|
126
+ | Concrete model names in issues | No. Every rename, replacement, or access change makes existing issues stale. | No. Names and effort controls are provider-specific. | Assumes every collaborator has the same entitlement and budget. | Old issues remain wrong until rewritten. | Reject. Suitable only for an explicit reproducibility pin in a provider-specific experiment. |
127
+ | Stable task intent resolved by user/org surface policy | Yes. Existing issues survive mapping changes. | Yes. Each surface resolves independently or inherits. | Preserves personal and organization choices. | Cached/local policy continues to work; unknown targets can inherit and warn. | **Adopt.** |
128
+ | Project-local concrete routing table | Partly. One table can be updated, but every checkout still receives volatile provider data. | Only by maintaining multiple provider sections. | Project config outranks user config on both surfaces, so the maintainer's preferences can override collaborators. | Fresh clones can start stale; offline discovery cannot repair it. | Reject as the default. Permit only explicit team policy/constraints. |
129
+
130
+ ## Setup and update implications
131
+
132
+ ### `setup-workflow`
133
+
134
+ `setup-workflow` should install or reconcile only the Kit-owned intent schema,
135
+ templates, validation, and a documented inheritance fallback. It may **offer**
136
+ to create a user-local routing policy for the detected surface, but only with
137
+ explicit consent and never as a committed project default.
138
+
139
+ Setup must not attempt to infer a complete routing table from `/v1/models`:
140
+
141
+ - API discovery requires credentials and network access.
142
+ - API availability is not equivalent to Claude Code or Codex subscription
143
+ availability across all supported providers and account types.
144
+ - A discovered model ID says nothing about the user's desired cost/capability
145
+ trade-off.
146
+
147
+ An interactive setup may validate explicitly selected targets using the active
148
+ surface and show a preview. Failure or absence of discovery should leave the
149
+ policy at `inherit`, not install guessed names.
150
+
151
+ ### `kit-update`
152
+
153
+ `kit-update` should update the Kit-owned schema, resolver implementation, and
154
+ provider adapter capabilities transactionally. It must preserve user-owned
155
+ mappings and project-owned constraints. If the schema changes, migration must
156
+ be semantic (for example, rename an intent key), previewed, and reversible;
157
+ ordinary updates must not rewrite historical issues or replace personal model
158
+ choices.
159
+
160
+ At dispatch, the resolver should:
161
+
162
+ 1. read the issue's stable intent;
163
+ 2. apply organization/user policy and explicit session overrides according to
164
+ the active surface;
165
+ 3. resolve or inherit without network-dependent mutation;
166
+ 4. reject a malformed intent, but fall back to `inherit` with a visible warning
167
+ when a volatile target is unavailable;
168
+ 5. record the effective model and effort in run evidence, not in the durable
169
+ issue contract.
170
+
171
+ This lets a later Kit release improve mappings for users who accept Kit
172
+ defaults while allowing a maintainer's user-local policy to evolve independently.
173
+
174
+ ## Implication for #213
175
+
176
+ #213 should not hardcode `sol`, `terra`, `luna`, Claude aliases, or full model
177
+ IDs into the issue template or validator. It should instead establish the
178
+ provider-neutral intent schema and make `to-issues` emit that schema. The
179
+ execute-ready check should reject concrete provider model recommendations in
180
+ shared handoffs, while orchestration resolves the intent on the active surface.
181
+
182
+ The existing free-text `Recommended model: <Model [Effort]>` field conflates a
183
+ durable work classification with a volatile local decision. Replace it with
184
+ machine-readable intent and render the effective local recommendation only
185
+ when a session is actually dispatched.
186
+
187
+ ## Open risks and decisions
188
+
189
+ 1. **Vocabulary quality:** `judgment/development/mechanical` and
190
+ `deep/balanced/light` need task-based definitions and contract tests so they
191
+ do not become disguised provider tiers.
192
+ 2. **Enforcement asymmetry:** Both surfaces can override model and effort for
193
+ subagents, but their precedence, supported values, organization controls,
194
+ and fallback behavior differ. The Kit must promise intent resolution, not
195
+ identical low-level enforcement.
196
+ 3. **Unavailable targets:** silent substitution hides policy drift. Inheritance
197
+ should be visible in run evidence, and strict team policies may choose to
198
+ fail instead.
199
+ 4. **Organization policy:** managed restrictions outrank personal/project
200
+ wishes. The resolver must treat the surface's effective selection as
201
+ authoritative.
202
+ 5. **Reproducibility exception:** a benchmark or regression reproduction may
203
+ legitimately pin a provider/model snapshot. That is a distinct,
204
+ provider-specific execution constraint, not the normal issue-routing field.
205
+ 6. **No universal discovery source:** model catalogs are useful validation
206
+ inputs where available, but cannot be the SSOT for setup or updates across
207
+ all supported account types and providers.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ikon85/agent-workflow-kit",
3
- "version": "0.34.3",
3
+ "version": "0.34.5",
4
4
  "description": "Portable AI-agent workflow skills (plan → execute → land → learn) for Claude Code & Codex — grilling, TDD, diagnosis, two-axis code review, cross-model Codex review, design & domain-modeling, plus a skill router (ask-matt). npx init/update/diff/uninstall.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -11,7 +11,7 @@ export const UPDATE_TITLE = 'chore: update agent workflow kit';
11
11
  const UPDATE_BODY = [
12
12
  'Automated, parity-verified update of `@ikon85/agent-workflow-kit`.',
13
13
  '',
14
- 'The transactional update candidate passed the consumer test suite before this branch was published.',
14
+ 'The transactional update candidate passed the built-in Kit invariants before this branch was published.',
15
15
  '',
16
16
  'This pull request is never merged automatically.',
17
17
  ].join('\n');
@@ -46,6 +46,8 @@ test('automated update pull requests carry the behavior availability summary', a
46
46
  assert.match(created.body, /## Availability/);
47
47
  assert.match(created.body, /newly degraded: orchestrate-wave\.projectRecipe/);
48
48
  assert.match(created.body, /still unresolved: orchestrateWaveRecipe:invalid/);
49
+ assert.match(created.body, /built-in Kit invariants/);
50
+ assert.doesNotMatch(created.body, /consumer test/i);
49
51
  assert.match(created.body, /never merged automatically/);
50
52
  });
51
53
 
@@ -57,6 +57,18 @@ export function assessRelease(input) {
57
57
  if (hasDelta(payloadDrift)) {
58
58
  errors.push(`npm package payload does not match built manifest (${describe(payloadDrift)})`);
59
59
  }
60
+ // Merging integrates a prepared version; only its annotated tag publishes it.
61
+ // Nothing else notices when that tag never arrives, so the next release PR
62
+ // would silently stack on top and the skipped version would never exist as
63
+ // its own artifact. Block that here, where a human is present anyway. A
64
+ // repository without any matching tag is bootstrapping, not stacking.
65
+ const { baseTag } = input;
66
+ if (baseTag?.repoHasTags && !baseTag.exists && input.currentVersion !== input.baseVersion) {
67
+ errors.push(
68
+ `previous release ${input.baseVersion} is still awaiting-tag (no ${baseTag.name} tag); `
69
+ + `tag and publish it before preparing ${input.currentVersion}`,
70
+ );
71
+ }
60
72
  const recommendedBump = recommendBump(delta);
61
73
  const actual = bumpKind(input.baseVersion, input.currentVersion);
62
74
  const rank = { patch: 1, minor: 2, major: 3 };
@@ -73,6 +85,19 @@ function gitShowJson(repoRoot, ref, path) {
73
85
  return JSON.parse(execFileSync('git', ['show', `${ref}:${path}`], { cwd: repoRoot, encoding: 'utf8' }));
74
86
  }
75
87
 
88
+ function git(repoRoot, args) {
89
+ return execFileSync('git', args, { cwd: repoRoot, encoding: 'utf8' }).trim();
90
+ }
91
+
92
+ export function resolveBaseTag({ repoRoot, baseVersion, tagPrefix = 'v' }) {
93
+ const name = `${tagPrefix}${baseVersion}`;
94
+ let exists = true;
95
+ try {
96
+ git(repoRoot, ['rev-parse', '-q', '--verify', `refs/tags/${name}`]);
97
+ } catch { exists = false; }
98
+ return { name, exists, repoHasTags: git(repoRoot, ['tag', '-l', `${tagPrefix}*`]) !== '' };
99
+ }
100
+
76
101
  export async function packedPayloadManifest({ repoRoot, manifest }) {
77
102
  const tempRoot = await mkdtemp(join(tmpdir(), 'awkit-package-payload-'));
78
103
  try {
@@ -103,8 +128,10 @@ export async function checkReleaseDelta({ repoRoot, baseRef = 'origin/main' } =
103
128
  await buildKit({ repoRoot, distDir });
104
129
  const currentPackage = JSON.parse(await readFile(join(repoRoot, 'package.json'), 'utf8'));
105
130
  const builtManifest = JSON.parse(await readFile(join(distDir, 'agent-workflow-kit.package.json'), 'utf8'));
131
+ const baseVersion = gitShowJson(repoRoot, baseRef, 'package.json').version;
106
132
  return assessRelease({
107
- baseVersion: gitShowJson(repoRoot, baseRef, 'package.json').version,
133
+ baseVersion,
134
+ baseTag: resolveBaseTag({ repoRoot, baseVersion }),
108
135
  currentVersion: currentPackage.version,
109
136
  baseManifest: gitShowJson(repoRoot, baseRef, 'agent-workflow-kit.package.json'),
110
137
  checkedManifest: JSON.parse(await readFile(join(repoRoot, 'agent-workflow-kit.package.json'), 'utf8')),
@@ -75,6 +75,51 @@ test('shipped content rejects downgrade and malformed version transitions', () =
75
75
  }
76
76
  });
77
77
 
78
+ test('a bump on top of an untagged previous release is blocked', () => {
79
+ const current = { kitVersion: '1.3.0', files: [file('new.md', 'one')] };
80
+ const result = assessRelease({
81
+ baseVersion: '1.2.3', currentVersion: '1.3.0',
82
+ baseManifest: { kitVersion: '1.2.3', files: [] }, builtManifest: current,
83
+ checkedManifest: current, payloadManifest: current,
84
+ baseTag: { name: 'v1.2.3', exists: false, repoHasTags: true },
85
+ });
86
+ assert.equal(result.ok, false);
87
+ assert.match(result.errors.join('\n'), /1\.2\.3 is still awaiting-tag/);
88
+ assert.match(result.errors.join('\n'), /v1\.2\.3/);
89
+ });
90
+
91
+ test('a bump passes once the previous release carries its tag', () => {
92
+ const current = { kitVersion: '1.3.0', files: [file('new.md', 'one')] };
93
+ const result = assessRelease({
94
+ baseVersion: '1.2.3', currentVersion: '1.3.0',
95
+ baseManifest: { kitVersion: '1.2.3', files: [] }, builtManifest: current,
96
+ checkedManifest: current, payloadManifest: current,
97
+ baseTag: { name: 'v1.2.3', exists: true, repoHasTags: true },
98
+ });
99
+ assert.equal(result.ok, true);
100
+ });
101
+
102
+ test('a never-tagged repository is not blocked from its first release', () => {
103
+ const current = { kitVersion: '0.1.0', files: [file('new.md', 'one')] };
104
+ const result = assessRelease({
105
+ baseVersion: '0.0.0', currentVersion: '0.1.0',
106
+ baseManifest: { kitVersion: '0.0.0', files: [] }, builtManifest: current,
107
+ checkedManifest: current, payloadManifest: current,
108
+ baseTag: { name: 'v0.0.0', exists: false, repoHasTags: false },
109
+ });
110
+ assert.equal(result.ok, true);
111
+ });
112
+
113
+ test('an untagged base blocks nothing while the version stays put', () => {
114
+ const same = { kitVersion: '1.2.3', files: [file('skill.md', 'one')] };
115
+ const result = assessRelease({
116
+ baseVersion: '1.2.3', currentVersion: '1.2.3',
117
+ baseManifest: same, builtManifest: same, checkedManifest: same, payloadManifest: same,
118
+ baseTag: { name: 'v1.2.3', exists: false, repoHasTags: true },
119
+ });
120
+ assert.equal(result.ok, true);
121
+ });
122
+
78
123
  test('actual npm payload drift is blocked even when checked and built manifests match', () => {
79
124
  const scrubbed = { kitVersion: '1.2.3', files: [file('skill.md', 'scrubbed')] };
80
125
  const result = assessRelease({
@@ -99,9 +99,13 @@ export function npmTarballFilename(name, version) {
99
99
  return `${name.replace(/^@/, '').replaceAll('/', '-')}-${version}.tgz`;
100
100
  }
101
101
 
102
- async function packedTarball(run, spec, directory, repoRoot) {
102
+ async function packedTarball(run, spec, directory, repoRoot, { preferOnline = false } = {}) {
103
+ // A stale local packument answers ETARGET for a version that IS published.
104
+ // Read through it for registry specs: the status would otherwise report
105
+ // `awaiting-npm` for a released package and invite a second publish.
106
+ const cachePolicy = preferOnline ? ['--prefer-online'] : [];
103
107
  const { stdout } = await run(
104
- 'npm', ['pack', spec, '--json', '--pack-destination', directory], { cwd: repoRoot },
108
+ 'npm', ['pack', spec, '--json', '--pack-destination', directory, ...cachePolicy], { cwd: repoRoot },
105
109
  );
106
110
  const result = JSON.parse(stdout);
107
111
  if (!Array.isArray(result) || result.length !== 1 || !result[0].filename) {
@@ -120,7 +124,7 @@ function releaseReaders(context) {
120
124
  async function npm(identity) {
121
125
  try {
122
126
  state.npmTarball = await packedTarball(
123
- run, `${identity.name}@${identity.version}`, scratch, repoRoot,
127
+ run, `${identity.name}@${identity.version}`, scratch, repoRoot, { preferOnline: true },
124
128
  );
125
129
  return await releaseIdentityFromTarball(state.npmTarball);
126
130
  } catch (error) {
@@ -170,6 +170,28 @@ test('post-merge status inspection is read-only and reports the reconstructable
170
170
  assert.deepEqual(fixture.events, ['read npm', 'read github']);
171
171
  });
172
172
 
173
+ test('the registry read bypasses the local packument cache, the local pack does not', async () => {
174
+ const calls = [];
175
+ const run = async (command, args) => {
176
+ calls.push({ command, args });
177
+ throw Object.assign(new Error('stubbed pack'), { stderr: 'stubbed pack' });
178
+ };
179
+ const probe = await createCommandAdapter({ repoRoot: REPO, run });
180
+ try {
181
+ await assert.rejects(() => probe.local());
182
+ await assert.rejects(() => probe.npm(identity));
183
+ } finally { await probe.dispose(); }
184
+
185
+ const [localPack, registryPack] = calls;
186
+ // A stale packument makes `npm pack <name>@<version>` answer ETARGET for a
187
+ // version that IS published — the status would then claim awaiting-npm and
188
+ // invite a second publish. The local pack must NOT reach for the registry.
189
+ assert.ok(registryPack.args.includes('--prefer-online'), 'registry read must bypass the cache');
190
+ assert.ok(registryPack.args.includes(`${identity.name}@${identity.version}`));
191
+ assert.ok(!localPack.args.includes('--prefer-online'), 'local pack must stay offline');
192
+ assert.ok(localPack.args.includes('.'));
193
+ });
194
+
173
195
  test('local Claude overrides cannot change the packed release identity or enter the tarball', async () => {
174
196
  const fixture = await mkdtemp(join(tmpdir(), 'awkit-local-overrides-'));
175
197
  await mkdir(join(fixture, '.claude'), { recursive: true });