@afokapu/atdd-bun 0.10.27 → 0.10.28
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/conventions/delivery/delivery.config-schema.convention.yaml +1 -1
- package/conventions/delivery/delivery.model-allowed.convention.yaml +5 -17
- package/conventions/delivery/delivery.operating-model.convention.yaml +2 -2
- package/integrity.json +6 -6
- package/package.json +1 -1
- package/planner-schemas/delivery-config.schema.json +2 -2
- package/src/delivery.ts +8 -17
|
@@ -13,7 +13,7 @@ content:
|
|
|
13
13
|
summary: >-
|
|
14
14
|
The skill and the validators read the same policy. A misspelled key would be ignored by one and not the other, so a malformed block is a finding, and the rest of the run judges evidence against the package defaults.
|
|
15
15
|
normative_text: |
|
|
16
|
-
The policy decides which models may write and review each stage, in
|
|
16
|
+
The policy decides which models may write and review each stage (an allowed set, in no order; "*" allows any), how independent a reviewer must be, and when a model counts as unavailable. It is read by the delivery skill when it dispatches work and by this profile when it judges the record, so both must see the same, valid document.
|
|
17
17
|
fix_hint: |
|
|
18
18
|
Correct the key the finding names. The defaults are:
|
|
19
19
|
|
|
@@ -4,31 +4,19 @@ kind: rule
|
|
|
4
4
|
status: active
|
|
5
5
|
name: Authors and reviewers are allowed models, and every fallback says why
|
|
6
6
|
statement: >-
|
|
7
|
-
Each work entry's writer
|
|
7
|
+
Each work entry's writer is a model its stage allows for writers, and each review's reviewer a model it allows for reviewers. A stage's list is the set of allowed models, in no order; "*" allows any model. Which allowed model does the work is the operator's choice at any moment, so no fallback has to explain it; the record names the model that actually ran (REQUIRED).
|
|
8
8
|
terms:
|
|
9
9
|
- term_id: fallback
|
|
10
10
|
text: >-
|
|
11
11
|
a recorded switch from a preferred model to a later one in the stage's list, with role (writer or reviewer) and a reason: an outage, a rate limit, or no auditable report, counted by failures; or the operator's order, referenced by its ruling. A REQUEST CHANGES verdict is never a reason.
|
|
12
12
|
content:
|
|
13
13
|
summary: >-
|
|
14
|
-
The lists are the operator's choice of
|
|
14
|
+
The lists are the operator's choice of which models may do the work. Switching between allowed models needs no policy edit and no paperwork, so a rate limit or a preference change is handled on the spot; a model outside the list is still reported.
|
|
15
15
|
normative_text: |
|
|
16
|
-
The
|
|
17
|
-
|
|
16
|
+
The record must name the model that ran. Reviewer independence (a reviewer is never the model that wrote the work it reviews, under different-model) is judged by delivery.reviewer-independent, and a disputed finding upheld on a fresh review blocks the tranche, so switching reviewers cannot buy an approval.
|
|
17
|
+
A fallback entry (kind outage, rate_limit, no_report, timeout or operator) may still record why a model was used; it is optional and, when present, must be well formed.
|
|
18
18
|
fix_hint: |
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
fallback:
|
|
22
|
-
- { role: reviewer, from: codex, kind: rate_limit, failures: 3, window: { from: "2026-09-25T09:00:00Z", to: "2026-09-25T09:08:00Z" }, reason: "429 on 3 attempts in 10 minutes" }
|
|
23
|
-
|
|
24
|
-
or, for a substitution the operator ordered:
|
|
25
|
-
|
|
26
|
-
- { role: writer, from: claude-opus, kind: operator, decided_by: "the owner", decided_at: "2026-09-27T23:00:00Z", ruling: "atdd-frg-workstation #abc123", reason: "Standing substitution while Claude is rate-limited: kimi for Opus-tier work" }
|
|
27
|
-
|
|
28
|
-
or use a model the stage lists.
|
|
29
|
-
exceptions:
|
|
30
|
-
- >-
|
|
31
|
-
When a stage lists one model, no fallback is possible: an unavailable model blocks the tranche.
|
|
19
|
+
Use a model the stage lists, or have the operator add it (or "*") to the stage's list in atdd-bun.yaml; the integrity check reports an added model once for the operator to approve.
|
|
32
20
|
metadata:
|
|
33
21
|
aliases:
|
|
34
22
|
- DELIVERY-MODEL-ALLOWED-001
|
|
@@ -11,13 +11,13 @@ terms:
|
|
|
11
11
|
Owns throughput, never the work. It splits the program into tranches with explicit dependencies and writes why, the scope and the split in docs/delivery/index.adoc; activates a tranche once its own dependencies have merged (one still waiting may run plan and its review, nothing after); starts one driver per tranche in its own worktree; keeps provider health for the program, telling every driver to skip a model that is down; intervenes when a tranche is idle without being blocked, or has deliverables and no commit after two hours; and hands BLOCKED disputed-finding to a human with both sides. It never writes, reviews or merges.
|
|
12
12
|
- term_id: driver
|
|
13
13
|
text: >-
|
|
14
|
-
Owns one tranche end to end. For each stage of delivery.stages, in order, it starts the stage's writer (
|
|
14
|
+
Owns one tranche end to end. For each stage of delivery.stages, in order, it starts the stage's writer (any model in its writer list, as the operator chooses) in the tranche worktree, commits red on its own before green starts, passes the stage's gate, and appends a work entry; for each stage with a reviewer it starts a review. It never reviews.
|
|
15
15
|
- term_id: review_run
|
|
16
16
|
text: >-
|
|
17
17
|
Every review is a fresh process in its own detached worktree at the exact SHA (git worktree add --detach), launched with a prompt that gives it the delivery.review convention, the stage, the base SHA, the SHA and the RED commit. Afterwards git status --porcelain must be empty and HEAD still the SHA, or the review is a REVIEWER_FAILURE and does not count. The raw output is kept in the tranche folder and named in the review's report.
|
|
18
18
|
- term_id: fallback
|
|
19
19
|
text: >-
|
|
20
|
-
|
|
20
|
+
Any model a stage lists may be used, so a switch needs no fallback: when a model is down or rate-limited, the driver moves to another listed model and records the one that ran. A fallback entry may still say why (outage, rate limit, no auditable report, timeout, or the operator's order); it is optional. A request-changes verdict is never a reason to switch reviewers: a disputed finding upheld on a fresh review blocks the tranche. When no listed model is available, when_exhausted block emits BLOCKED provider-unavailable.
|
|
21
21
|
- term_id: repair
|
|
22
22
|
text: >-
|
|
23
23
|
For each finding of a request-changes review the writer either fixes it or writes one rebuttal with evidence; a fresh review of the same stage follows. If that reviewer upholds a disputed finding, the driver emits BLOCKED disputed-finding and never disputes it again. Any commit, regenerated file, conflict fix or rebase after an approval cancels it and the change goes back to that stage's review.
|
package/integrity.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.10.
|
|
2
|
+
"version": "0.10.28",
|
|
3
3
|
"files": {
|
|
4
4
|
"HOOK_AUDIT.md": "5329d840db37671b1918f688ead26865473b87db73dbc75f7c8b2a8bbe8d6d43",
|
|
5
5
|
"PLANNER_PORT.md": "fb5935bac8b7ac18994de21e43ace3a5ef8cd55f85b0e3349fca261280054f11",
|
|
@@ -94,12 +94,12 @@
|
|
|
94
94
|
"conventions/coder.htmx/coder.htmx.verb-mutation-signals-progress.convention.yaml": "ae85c5ea01a09a3c41b939988088a8e5da91848d2710a1f0a1e0c86308638ac9",
|
|
95
95
|
"conventions/delivery/delivery.approved-sha-resolves.convention.yaml": "68f2ecc10870e780abb05bb31d211b1923d47b56672df4b3950f437aa9cf8ca8",
|
|
96
96
|
"conventions/delivery/delivery.board.convention.yaml": "4c544f51de80b09961a8df41028867023ab802aa4b31dcb0a1ce29e890552e6d",
|
|
97
|
-
"conventions/delivery/delivery.config-schema.convention.yaml": "
|
|
97
|
+
"conventions/delivery/delivery.config-schema.convention.yaml": "b59a69b7789ad0c3d0b1ac9ea2b955fbbb5dfa07a9ee169a037b406a198813f0",
|
|
98
98
|
"conventions/delivery/delivery.evidence-schema.convention.yaml": "9c6a22495dfb3e2fcbca700392866c3b5660084b1f5594230eb320b499d5e5cb",
|
|
99
99
|
"conventions/delivery/delivery.findings-resolved.convention.yaml": "f6574e93a9a2d4bdd6d6f5645720b1a647fc0048d95f7675fe783d2ddb62c05b",
|
|
100
100
|
"conventions/delivery/delivery.merge-gate.convention.yaml": "48745ec23625b68be9b8fe2c6dcdcba88b2b38924f6593abf6987391ff0ee243",
|
|
101
|
-
"conventions/delivery/delivery.model-allowed.convention.yaml": "
|
|
102
|
-
"conventions/delivery/delivery.operating-model.convention.yaml": "
|
|
101
|
+
"conventions/delivery/delivery.model-allowed.convention.yaml": "e9690e95f3604483f182b3032ae60eac20109eb458509f14651945ccf07692f2",
|
|
102
|
+
"conventions/delivery/delivery.operating-model.convention.yaml": "1a86a1fe84695968a2fb719a899629af9a8baadcd90bdb8776f1f0208cebbbb4",
|
|
103
103
|
"conventions/delivery/delivery.review.convention.yaml": "b033579c2d90fcb098d325dcc07c7b084e048cca43fbe8e28159ec08a412018a",
|
|
104
104
|
"conventions/delivery/delivery.reviewer-independent.convention.yaml": "8c436d66ef5b1f6d6bc5dd55a235caca2490020e88665eecadb2ca41cd4149c5",
|
|
105
105
|
"conventions/delivery/delivery.stages-complete.convention.yaml": "0e5dd8057ad51eff7906e3424a9686a81be1814447afb9a30acab43d39e49b4c",
|
|
@@ -1179,7 +1179,7 @@
|
|
|
1179
1179
|
"planner-schemas/binding-lock.schema.json": "f2455606702f45fafa194549126d4a66e6c286ba1c4ba9e6bdcf932286a6d34d",
|
|
1180
1180
|
"planner-schemas/component.schema.json": "d20d3502c155627a120cb10d5e9bdec4ba2c73af9c3e9a9d10e3c351f167c72a",
|
|
1181
1181
|
"planner-schemas/contract-registry.schema.json": "b9dbaf6c87d4f4d676e3b4251344b9eada523b1c5e739c0423cabe267012c12e",
|
|
1182
|
-
"planner-schemas/delivery-config.schema.json": "
|
|
1182
|
+
"planner-schemas/delivery-config.schema.json": "7cd9c15ea4345ad83195b9a1c7d09a0b66275e9a3fdf8b5d6905d96bbec579d3",
|
|
1183
1183
|
"planner-schemas/delivery-evidence.schema.json": "72ba1450abe5482c43f2069e9ee7d121398008a0ddef36c6b269d38a16872e0e",
|
|
1184
1184
|
"planner-schemas/feature.schema.json": "eb4aa40d89067ae5c86a079bb3d8e4f45c8727f9e15db2b86c08dacf8e4ae56a",
|
|
1185
1185
|
"planner-schemas/journey.schema.json": "1ff705bcf89a2823cf60c9e1597e254f97844e7a74dc58299d53192ad2c9f8c3",
|
|
@@ -1199,7 +1199,7 @@
|
|
|
1199
1199
|
"src/board.ts": "bc70e479874f2f05a44293226e53eb9e763b661bdcebade4f8948b0dd2ee2ead",
|
|
1200
1200
|
"src/ci.ts": "67e54d2cbfc44a9e42837d5e750af526d6255c539d3c452a4d5fca3226c0d9ac",
|
|
1201
1201
|
"src/cli.ts": "e5ab4d2435fdcbebf462b4c2b67b495bd975d5deb668b088d8cba04a9d8b78a5",
|
|
1202
|
-
"src/delivery.ts": "
|
|
1202
|
+
"src/delivery.ts": "2eaaf4fd87aea26dce70b4c88a4e0a2f25b9de165bc2e38330fd7eb449587901",
|
|
1203
1203
|
"src/docs-capability.ts": "8a7a4360976773fe462b1860eaf26182b016bcb1fbcd6a6d5423bd81379dbe67",
|
|
1204
1204
|
"src/enforce.ts": "41a6f8c058a2a034996c817f224191403cc66ccc4ed4e04bbefc430014c783d8",
|
|
1205
1205
|
"src/hooks.ts": "2feeec2674c1462c94256de02ce59ccc6e3ff671ec4dca1bf9903c32888b600b",
|
package/package.json
CHANGED
|
@@ -76,8 +76,8 @@
|
|
|
76
76
|
"type": "array",
|
|
77
77
|
"minItems": 1,
|
|
78
78
|
"uniqueItems": true,
|
|
79
|
-
"items": { "type": "string", "pattern": "^[a-z][a-z0-9-]
|
|
80
|
-
"description": "
|
|
79
|
+
"items": { "type": "string", "pattern": "^(\\*|[a-z][a-z0-9-]*)$" },
|
|
80
|
+
"description": "The models allowed for the role, in no order: any listed model may do the work, and \"*\" allows any model."
|
|
81
81
|
},
|
|
82
82
|
"stage": {
|
|
83
83
|
"type": "object",
|
package/src/delivery.ts
CHANGED
|
@@ -39,7 +39,7 @@ export type DeliveryPolicy = {
|
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
/** The default operating model: two reviews, the plan and the whole change; the stages between them are written and
|
|
42
|
-
* held by their deterministic gates.
|
|
42
|
+
* held by their deterministic gates. Each list is the set of models allowed for the role, in no order; "*" allows any. */
|
|
43
43
|
const DEFAULT_STAGES: Partial<Record<Stage, { writer?: string[]; reviewer?: string[] }>> = {
|
|
44
44
|
plan: { writer: ["codex", "claude-opus", "kimi"], reviewer: ["glm", "claude-opus", "codex", "kimi"] },
|
|
45
45
|
red: { writer: ["glm", "claude-sonnet", "deepseek-flash", "claude-opus", "codex", "kimi"] },
|
|
@@ -122,10 +122,7 @@ export function loosenedDelivery(base: unknown, current: unknown): string[] {
|
|
|
122
122
|
for (const role of ["reviewer", "writer"] as const) {
|
|
123
123
|
const added = c[role].filter(model => !b[role].includes(model));
|
|
124
124
|
if (added.length && b[role].length) out.push(`delivery.stages.${stage}.${role} adds ${added.join(", ")}`);
|
|
125
|
-
// The lists are
|
|
126
|
-
// fallback model usable without the fallback.
|
|
127
|
-
const promoted = c[role].filter(model => b[role].includes(model) && c[role].indexOf(model) < b[role].indexOf(model));
|
|
128
|
-
if (promoted.length) out.push(`delivery.stages.${stage}.${role} [${b[role].join(", ")}] → [${c[role].join(", ")}] promotes ${promoted.join(", ")}`);
|
|
125
|
+
// The lists are allowed sets: reordering one changes nothing; only an added model (or "*") is a loosening.
|
|
129
126
|
}
|
|
130
127
|
// A stage that had no writer list now accepting writers is a loosening too: before, nothing could be written there.
|
|
131
128
|
if (!b.writer.length && c.writer.length) out.push(`delivery.stages.${stage}.writer adds ${c.writer.join(", ")}`);
|
|
@@ -235,18 +232,12 @@ export async function loadEvidence(root: string, policy: DeliveryPolicy): Promis
|
|
|
235
232
|
return out;
|
|
236
233
|
}
|
|
237
234
|
|
|
238
|
-
/** A model must
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
function checkModel(file: string, at: string, stage: Stage, role: "writer" | "reviewer", actor: Actor, list: string[],
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
if (index < 0) return [finding("delivery.model-allowed", file, `${at}: ${role} model '${actor.model}' is not in ${stage}.${role} [${list.join(", ")}]`)];
|
|
245
|
-
// `author` is the 0.9 spelling of `writer`.
|
|
246
|
-
const recorded = fallbacks.filter(entry => ((entry.role === "author" ? "writer" : entry.role) ?? defaultRole) === role).map(entry => entry.from);
|
|
247
|
-
for (const skipped of list.slice(0, index)) if (!recorded.includes(skipped) && !ineligible.includes(skipped)) out.push(finding("delivery.model-allowed", file, `${at}: ${role} '${actor.model}' is a fallback, but no fallback from '${skipped}' records why it was unavailable`));
|
|
248
|
-
for (const from of recorded) if (!list.slice(0, index).includes(from)) out.push(finding("delivery.model-allowed", file, `${at}: ${role} fallback from '${from}' does not precede '${actor.model}' in [${list.join(", ")}]`));
|
|
249
|
-
return out;
|
|
235
|
+
/** A model must be one the stage allows: its list is the set of allowed models, in no order, and "*" allows any. Which
|
|
236
|
+
* listed model does the work is the operator's choice at any moment, so no fallback has to explain it; the record still
|
|
237
|
+
* names the model that ran, and reviewer independence is judged separately (delivery.reviewer-independent). */
|
|
238
|
+
function checkModel(file: string, at: string, stage: Stage, role: "writer" | "reviewer", actor: Actor, list: string[], _fallbacks: Fallback[], _defaultRole: "writer" | "reviewer", _fallback: DeliveryPolicy["fallback"], _ineligible: string[] = []): PlanFinding[] {
|
|
239
|
+
if (list.includes("*") || list.includes(actor.model)) return [];
|
|
240
|
+
return [finding("delivery.model-allowed", file, `${at}: ${role} model '${actor.model}' is not allowed for ${stage}.${role} [${list.join(", ")}]`)];
|
|
250
241
|
}
|
|
251
242
|
|
|
252
243
|
/** The count and window of every recorded fallback are the driver's claims, but explicit ones: outside the policy is out of policy. */
|