Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
# check-specs-selftest.sh — proves check-specs.sh can fail.
#
# Both of its rules pass on every file in this repository, and a check that has never been seen to
# Its rules pass on every file in this repository, and a check that has never been seen to
# fail is indistinguishable from one that cannot. Each case below builds a throwaway tree, breaks
# exactly one thing, and asserts the finding names it. Nothing in the real tree is touched.
#
Expand Down Expand Up @@ -336,6 +336,94 @@ build
rm -f "$MINI/tree/specs/2026-01-02-blocked.md"
expect_hit "deleting the target turns the surviving reference red" "no such file is under specs/"

echo
echo "=== unticked tasks: a bare one in a Shipped spec is caught ==="
build
cat >> "$SPEC" <<'EOF'

- [ ] **T9 · The thing that never ran**
Owns: nothing in the tree
Verify: by hand
EOF
expect_hit "an unticked task with no deferral is caught" "unticked in a Shipped spec"

echo
echo "=== unticked tasks: a written deferral is the legal shape ==="
build
cat >> "$SPEC" <<'EOF'

- [ ] **T9 · The thing that never ran**
**Routed to the verification matrix, by decision, and left unticked on purpose.**
Owns: nothing in the tree
EOF
run
if [ "$rc" -eq 0 ]; then
pass "a written deferral passes"
else
fail "a written deferral should pass, got: $out"
fi

build
cat >> "$SPEC" <<'EOF'

- [ ] **T9 · The thing that never ran**
Blocked by: a cluster with RDMA-capable nodes. NOT DONE.
EOF
run
if [ "$rc" -eq 0 ]; then
pass "an external blocker passes"
else
fail "an external blocker should pass, got: $out"
fi

echo
echo "=== unticked tasks: 'Blocked by: None' and a bare dash say nothing ==="
build
cat >> "$SPEC" <<'EOF'

- [ ] **T9 · The thing that never ran**
Blocked by: None
EOF
expect_hit "Blocked by None is not a deferral" "unticked in a Shipped spec"

build
cat >> "$SPEC" <<'EOF'

- [ ] **T9 · The thing that never ran**
Blocked by: —
EOF
expect_hit "a bare dash is not a deferral" "unticked in a Shipped spec"

echo
echo "=== unticked tasks: the plan's own ordering is not a deferral ==="
# The measured corruption shape: every task of a shipped spec unticked, with blocks whose only
# "Blocked by" lines are the plan's own task numbers.
build
cat >> "$SPEC" <<'EOF'

- [ ] **T9 · The thing that never ran**
Blocked by: T2

- [ ] **T10 · The other thing**
Blocked by: T1–T7
EOF
expect_hit "task-ordering blockers are not deferrals" "unticked in a Shipped spec"

echo
echo "=== unticked tasks: a Building spec keeps its open tasks ==="
build
cat >> "$BLOCKED" <<'EOF'

- [ ] **T9 · The thing that never ran**
Owns: nothing in the tree
EOF
run
if [ "$rc" -eq 0 ]; then
pass "an unticked task in a non-Shipped spec is the ordinary shape"
else
fail "a Building spec's open task should pass, got: $out"
fi

echo
if [ "$fails" -gt 0 ]; then
echo "SELFTEST FAILED: $fails case(s)."
Expand Down
63 changes: 63 additions & 0 deletions .agents/skills/gpustack-operator-docs/scripts/check-specs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@
# commands. Measured when a bug fix's specification was moved out: eight markers in two
# shipped specifications went on naming it, and all three gates stayed green.
#
# 5. a task left unticked in a Shipped spec whose block says nothing about why. The box and the
# history then disagree, and completed work reads as outstanding -- measured when two shipped
# specifications reached main with every one of their tasks unticked although every task had
# landed, and a reader was the only thing that caught it. The legal shapes are a written
# deferral in the task's own block ('tracked as', 'routed to', 'left unticked', 'deferred',
# 'moved to', 'parked', 'NOT DONE'), or a 'Blocked by:' naming an external blocker -- a list
# of the plan's own tasks is its ordering, not a deferral, and 'None' or a dash says nothing.
#
# Rule 3 reads the whole markdown corpus, not just specs/: the construct is a command someone
# re-runs, and it goes stale wherever it is written.
#
Expand Down Expand Up @@ -382,6 +390,61 @@ for f in $SPECS; do
done < "$WORK/refs"
done

# --- 5. an unticked task in a Shipped spec says why ---------------------------
echo "==> unticked tasks in shipped specs"

for f in $SPECS; do
if [ ! -f "$f" ]; then
continue
fi
word=$(sed -n '3p' "$f" | sed -e 's/^Status: *//' -e 's/[ ].*$//')
if [ "$word" != "Shipped" ]; then
continue
fi
# A task's block runs from its "- [ ]" line to the next task line or the next heading. The block
# must carry its deferral: one of the written phrases, or a "Blocked by:" that names something
# other than the plan's own task ordering (T-numbers, dashes and "and" are stripped; what
# remains has to be words). "None", a bare dash, or silence all say nothing.
awk '
function flush( v, t) {
if (!open) return
open = 0
if (block ~ /[Tt]racked as/ || block ~ /[Ll]eft unticked/ || block ~ /[Dd]eferred/ || \
block ~ /[Rr]outed to/ || block ~ /[Mm]oved to/ || block ~ /[Pp]arked/ || \
index(block, "NOT DONE") > 0) return
if (match(block, /[Bb]locked by:[^\n]*/)) {
v = substr(block, RSTART + 11, RLENGTH - 11)
gsub(/\*\*/, "", v)
t = v
gsub(/[Tt][0-9]+/, "", t)
gsub(/[Aa]nd/, "", t)
# ASCII punctuation first, dashes as single-char patterns: a bracket holding them next to
# \t reads \t-<dashbyte> as a range that swallows the alphabet, measured on BSD awk.
gsub(/[\t ,.;-]/, "", t)
gsub(/–/, "", t)
gsub(/—/, "", t)
if (t != "" && v !~ /^[ \t]*[Nn]one\.?[ \t]*$/) return
}
print start
}
/^- \[/ {
flush()
open = ($0 ~ /^- \[ \] \*\*/)
start = FNR
block = $0 "\n"
next
}
/^#/ { flush(); next }
{ if (open) block = block $0 "\n" }
END { flush() }
' "$f" > "$WORK/unticked"
while IFS= read -r at; do
if [ -n "$at" ]; then
err "$f:$at: this task is unticked in a Shipped spec and its block does not say why. If the work landed, tick the box; if it did not, the block must carry the deferral in words ('tracked as', 'routed to', 'left unticked', 'deferred', 'moved to', 'parked', 'NOT DONE') or a 'Blocked by:' naming an external blocker — a list of the plan's own tasks is its ordering, not a deferral. Unticked and silent is how completed work reads as outstanding."
fi
done < "$WORK/unticked"
done

echo
if [ "$errors" -gt 0 ]; then
echo "FAIL: $errors problem(s)."
Expand Down
16 changes: 8 additions & 8 deletions specs/2026-09-27-model-image-source.md
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ commit). Stage gates go to the coordinator: plan gate (this document), built gat
T1 is a spike and is run first on purpose: if the kind environment cannot serve image volumes,
the e2e environment decision escalates while the code tasks are still ahead of it, not after.

- [ ] **T1 · e2e environment verification (spike)**
- [x] **T1 · e2e environment verification (spike)**
Blocked by: —
Gate: review
Owns: nothing in the tree (a local kind cluster, a probe Pod, evidence files only).
Expand All @@ -408,15 +408,15 @@ the e2e environment decision escalates while the code tasks are still ahead of i
decision rather than improvising.
Verify: the probe Pod reads the image's marker file; the readings name the node image's
containerd version and the feature state.
- [ ] **T2 · API + codegen**
- [x] **T2 · API + codegen**
Blocked by: —
Owns: `api/worker/v1alpha1/model_artifact.go`, `api/worker/v1alpha1/model_deployment.go`,
generated trees (deepcopy, CRD, openapi, protobuf, applyconfigurations).
Acceptance: the union field and the delivery value carry field docs that state the
identity and the absent resolved fields; `make generate` leaves a clean tree in the gen
tree.
Verify: `go build ./... && go test ./api/worker/...`.
- [ ] **T3 · Admission**
- [x] **T3 · Admission**
Blocked by: T2
Owns: `pkg/kubediscovery/feature.go`,
`pkg/worker/webhooks/worker/model_artifact.go`,
Expand All @@ -425,13 +425,13 @@ the e2e environment decision escalates while the code tasks are still ahead of i
wording above; the capability gate refusing below the floor and admitting at/above it,
both directions unit-tested through the swappable version seam; the prefetch refusal.
Verify: `go test ./pkg/kubediscovery/... ./pkg/worker/webhooks/worker/...`.
- [ ] **T4 · Resolution**
- [x] **T4 · Resolution**
Blocked by: T2
Owns: `pkg/worker/controllers/worker/model_artifact.go` (+ tests).
Acceptance: AC5, AC6 — first-pass resolution with a claim-shaped `resolved`; no nodes
aggregation; no revalidation.
Verify: `go test ./pkg/worker/controllers/worker/...`.
- [ ] **T5 · Renderer + placement**
- [x] **T5 · Renderer + placement**
Blocked by: T3, T4
Owns: `pkg/worker/controllers/worker/model_artifact_placement.go`,
`model_deployment_artifact.go`, `model_deployment.go`, `instance.go`,
Expand All @@ -440,21 +440,21 @@ the e2e environment decision escalates while the code tasks are still ahead of i
raise; `status.model` and `WeightsReady` behavior; the Setting-independence; the Instance
node pre-check and the images-list preference as adjudicated (both in scope).
Verify: `go test ./pkg/worker/controllers/worker/...`.
- [ ] **T6 · Docs**
- [x] **T6 · Docs**
Blocked by: T5
Owns: the three docs paths of AC15/AC16.
Acceptance: page routing, header/Contents/footer and the index entry per the docs skill;
every number carries its version condition.
Verify: `make lint docs`.
- [ ] **T7 · e2e**
- [x] **T7 · e2e**
Blocked by: T1, T5 (T6 not required)
Owns: the case script + SKILL.md row + execution evidence under the task directory's
`spec-7/raw/`.
Acceptance: the cold-mount scenario below on the environment T1 verified (a failed T1
escalates before this task starts); the capability-gate directions stay unit-tested (an
old-version e2e cluster is out of scope).
Verify: the case script against the cluster running the image built from the rebased head.
- [ ] **T8 · Ship prep**
- [x] **T8 · Ship prep**
Blocked by: T1–T7
Owns: rebase, commit folding, chart-matrix local runs (Go change ⇒ REQUIRED: the CI chart
matrix's 7 node images, locally, all rc=0), case number finalization, PR.
Expand Down
18 changes: 9 additions & 9 deletions specs/2026-09-27-model-prefetch.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ Tasks run sequentially in one seat (repo working agreement): every task starts w
they run, and each task lands its own `--signoff` commit (folded per module at ship). No per-task
review gates; the stage gates go to the coordinator (draft confirmed, plan gate, built gate).

- [ ] **T1 · API surface: three CRDs + NodeModelStore fields + codegen**
- [x] **T1 · API surface: three CRDs + NodeModelStore fields + codegen**
Blocked by: None
Owns: `api/worker/v1alpha1/model_store.go`, `api/worker/v1alpha1/model_store_binding.go`,
`api/worker/v1alpha1/model_prefetch.go`, `api/worker/v1alpha1/node_model_store.go`,
Expand All @@ -324,7 +324,7 @@ review gates; the stage gates go to the coordinator (draft confirmed, plan gate,
API-level tests.
Verify: `make generate && go build ./... && go test ./api/worker/...`

- [ ] **T2 · L3 merge + SelectorOverlap (worker)**
- [x] **T2 · L3 merge + SelectorOverlap (worker)**
Blocked by: T1
Owns: `pkg/worker/controllers/worker/node_model_store.go`,
`pkg/worker/controllers/worker/model_store.go` (+ tests)
Expand All @@ -334,14 +334,14 @@ review gates; the stage gates go to the coordinator (draft confirmed, plan gate,
`spec.pinned` field another controller owns.
Verify: `go test ./pkg/worker/controllers/worker/... ./pkg/modelstore/...`

- [ ] **T3 · plugin pinned consumption**
- [x] **T3 · plugin pinned consumption**
Blocked by: T1
Owns: `pkg/modelmanager/gc/gc.go`, `pkg/modelmanager/manager.go` (+ tests)
Acceptance: AC11 — pinned digests are excluded from GC candidates while still counted in
usage and capacity reporting; the saturated-state report is unchanged.
Verify: `go test ./pkg/modelmanager/...`

- [ ] **T4 · prefetch controller (delivery + aggregation)**
- [x] **T4 · prefetch controller (delivery + aggregation)**
Blocked by: T1
Owns: `pkg/worker/controllers/worker/model_prefetch.go` (+ tests),
`pkg/worker/controllers/setup.go`
Expand All @@ -351,7 +351,7 @@ review gates; the stage gates go to the coordinator (draft confirmed, plan gate,
`Progressing` / `Available` / `Degraded` conditions; prefetch deletion removes its Pods.
Verify: `go test ./pkg/worker/controllers/worker/...`

- [ ] **T5 · admission webhooks**
- [x] **T5 · admission webhooks**
Blocked by: T1
Owns: `pkg/worker/webhooks/worker/model_store_binding.go`,
`pkg/worker/webhooks/worker/model_prefetch.go`, `pkg/worker/webhooks/setup.go` (+ tests)
Expand All @@ -362,7 +362,7 @@ review gates; the stage gates go to the coordinator (draft confirmed, plan gate,
`pinned` without permission).
Verify: `go test ./pkg/worker/webhooks/worker/...`

- [ ] **T6 · accounting + per-node pinned writer**
- [x] **T6 · accounting + per-node pinned writer**
Blocked by: T4
Owns: `pkg/worker/controllers/worker/model_store_binding.go`,
`pkg/worker/controllers/worker/model_prefetch.go` (pinned subset) (+ tests),
Expand All @@ -373,23 +373,23 @@ review gates; the stage gates go to the coordinator (draft confirmed, plan gate,
ownership split (prefetch controller writes `pinned`; the L1–L3 reconciler preserves it).
Verify: `go test ./pkg/worker/controllers/worker/...`

- [ ] **T7 · v1 view for ModelPrefetch**
- [x] **T7 · v1 view for ModelPrefetch**
Blocked by: T4
Owns: `pkg/worker/extensionapis/worker/model_prefetch.go` (+ tests), extension-API
registration tables
Acceptance: status-only v1 view with TableConvertor and printer columns per the artifact-view
precedent; the view serves delete (regression guard pattern).
Verify: `go test ./pkg/worker/extensionapis/...`

- [ ] **T8 · docs**
- [x] **T8 · docs**
Blocked by: T6, T7
Owns: `docs/**` (one new page + `docs/README.md` index entry + a `docs/settings.md` entry
for the `model-prefetch-warmup-image` Setting)
Acceptance: page routing, header/Contents/footer per the docs skill; naming avoids KV-cache
vocabulary.
Verify: `make lint docs`

- [ ] **T9 · e2e case on kind**
- [x] **T9 · e2e case on kind**
Blocked by: T2, T3, T5, T6, T7
Owns: `.agents/skills/gpustack-operator-e2e/cases/case-<NNN>.sh` (+ the SKILL.md case-table
row; `<NNN>` = main's max case number + 1, taken at my-ship after the final rebase)
Expand Down
Loading
Loading