@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.
- package/.agents/skills/kit-release/SKILL.md +12 -0
- package/.agents/skills/kit-update/SKILL.md +6 -3
- package/.agents/skills/setup-workflow/SKILL.md +13 -5
- package/.agents/skills/setup-workflow/assets/agent-workflow-kit-update.yml +1 -3
- package/.claude/skills/kit-release/SKILL.md +12 -0
- package/.claude/skills/kit-update/SKILL.md +6 -3
- package/.claude/skills/setup-workflow/SKILL.md +13 -5
- package/.claude/skills/setup-workflow/assets/agent-workflow-kit-update.yml +1 -3
- package/README.md +17 -0
- package/agent-workflow-kit.package.json +12 -12
- package/docs/adr/0001-consumer-divergence-policy.md +4 -0
- package/docs/adr/0003-kit-core-and-project-extension-lifecycle.md +63 -0
- package/docs/agents/board-sync.md +1 -1
- package/docs/agents/workflow-capabilities.json +17 -0
- package/docs/research/benchlm-routing-source.md +198 -0
- package/docs/research/consumer-owned-protocol-files.md +238 -0
- package/docs/research/frontend-agent-benchmarks.md +282 -0
- package/docs/research/model-effort-routing-benchmarks.md +261 -0
- package/docs/research/provider-neutral-agent-routing.md +207 -0
- package/package.json +1 -1
- package/scripts/kit-update-pr.mjs +1 -1
- package/scripts/kit-update-pr.test.mjs +2 -0
- package/scripts/release-delta-guard.mjs +28 -1
- package/scripts/release-delta-guard.test.mjs +45 -0
- package/scripts/release-state.mjs +7 -3
- package/scripts/release-state.test.mjs +22 -0
- package/scripts/test_skill_readiness_contract.py +6 -1
- package/scripts/test_skill_setup_workflow_seeds.py +18 -17
- package/src/commands/update.mjs +52 -20
- package/src/lib/bundle.mjs +1 -1
- package/src/lib/updateCandidate.mjs +278 -50
- package/src/lib/verifyUpdateCandidate.mjs +220 -0
- package/src/lib/verifyUpdateCandidateArtifacts.mjs +78 -0
- package/src/lib/verifyUpdateCandidateProtocol.mjs +221 -0
- 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
|
+
"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
|
|
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
|
|
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 });
|