actions/drill-recorded — a release carries its evidence #7

Closed
opened 2026-07-22 13:48:26 +00:00 by dan-claude-bot · 3 comments
dan-claude-bot commented 2026-07-22 13:48:26 +00:00 (Migrated from github.com)

Part of #1. Blocked by #3, #5 (the composite pattern). Blocks #13–#16.

Goal

actions/drill-recorded/ — the release-evidence gate as a composite action. Three copies exist (box 125 · rig 166 · cast 152); same rule, three phrasings. Port from box (the origin), keeping its header essay.

Why it exists (keep this story in the header)

CONTRIBUTING said "the full drill on real hardware, recorded" since box#96. No release ever did it — box#95, #114 and #148 all shipped as a version bump plus a changelog stamp. The one time the gap was caught was the one time somebody happened to look — "that is not a gate, it is luck with good manners." So the rule moved into CI.

The rule (exact)

Keyed on the tree's version (same two-state logic as #5):

version state requirement
ends -dev PASS, with a notice that says why nothing was asserted ("a green log must distinguish 'the guard passed' from 'the guard decided this tree was not its business'")
bare <drills-dir>/<version>.md MUST exist and contain at least one non-whitespace character

Design points that must survive the port, with their lore:

  • One file per version is the whole design. Records used to share one file, and every hard edge of the old guard existed because of that: em-dash field matching, optional date tails, whole-version comparison so 0.9.0-rc1 couldn't satisfy 0.9.0, mawk-vs-gawk escape traps, blank-section detection. Two review-found defects came from that complexity. Separate files make it all unrepresentable0.9.0.md and 0.9.0-rc1.md are just different paths.
  • Plain drills/, never a dot-directory — dot-dirs are invisible to globs without dotglob (box#116, box#118): "evidence a sweep cannot see is evidence that goes missing quietly."
  • A failed drill is still a valid record; so is a written waiver. The gate wants evidence, not success — what it refuses is silence. (This lives in each consumer's drills/README.md, but the failure message should carry the gist.)
  • What a drill means is the consumer's business (box: isolation contract; rig: convergence; cast: promotion; incubator will define its own in #16). This action only ever reads this repo's record file.

Composite action

actions/drill-recorded/{action.yml,drill-recorded.sh}, pattern from #5. Inputs: version-source (default file), drills-dir (default drills). Version read via version_read (#3). Failure message: port box's — it names the exact expected path and says "either absent or blank; both mean this release is unproven".

Tests (test/drill-recorded.test.sh)

Fixture trees:

  • -dev tree, no drills dir at all → pass with the nothing-to-assert notice.
  • bare + drills/<ver>.md with content → pass.
  • bare + missing file → fail, message contains the expected path.
  • bare + whitespace-only file → fail (same message family).
  • bare 0.9.0 with only drills/0.9.0-rc1.md present → fail (prefix confusion unrepresentable — assert it stays that way).
  • package-json backend: one pass and one fail case.
  • missing/empty version source → fail.

Acceptance criteria

  • Action + script + tests land; CI green; shellcheck/actionlint clean; exercised via uses: ./actions/drill-recorded in this repo's CI.
  • Header keeps the box#95/#114/#148 story, the one-file-per-version rationale, and the dot-directory rule.
Part of #1. Blocked by #3, #5 (the composite pattern). Blocks #13–#16. ## Goal `actions/drill-recorded/` — the release-evidence gate as a composite action. Three copies exist ([box](https://github.com/heavy-duty/box/blob/a17903f07c83aa18c0f009565e1a5442da6d0827/.github/scripts/drill-recorded.sh) 125 · [rig](https://github.com/heavy-duty/rig/blob/7f8a0e08852837475505f404985a1251a2c3a8a1/.github/scripts/drill-recorded.sh) 166 · [cast](https://github.com/heavy-duty/cast/blob/2aa7018db461341a1bbe79c9ca8eb8fca4232719/.github/scripts/drill-recorded.sh) 152); same rule, three phrasings. Port from box (the origin), keeping its header essay. ## Why it exists (keep this story in the header) CONTRIBUTING said "the full drill on real hardware, recorded" since box#96. **No release ever did it** — box#95, #114 and #148 all shipped as a version bump plus a changelog stamp. The one time the gap was caught was the one time somebody happened to look — "that is not a gate, it is luck with good manners." So the rule moved into CI. ## The rule (exact) Keyed on the tree's version (same two-state logic as #5): | version state | requirement | |---|---| | ends `-dev` | PASS, with a notice that says *why* nothing was asserted ("a green log must distinguish 'the guard passed' from 'the guard decided this tree was not its business'") | | bare | `<drills-dir>/<version>.md` MUST exist and contain at least one non-whitespace character | Design points that must survive the port, with their lore: - **One file per version is the whole design.** Records used to share one file, and every hard edge of the old guard existed because of that: em-dash field matching, optional date tails, whole-version comparison so `0.9.0-rc1` couldn't satisfy `0.9.0`, mawk-vs-gawk escape traps, blank-section detection. Two review-found defects came from that complexity. Separate files make it all **unrepresentable** — `0.9.0.md` and `0.9.0-rc1.md` are just different paths. - **Plain `drills/`, never a dot-directory** — dot-dirs are invisible to globs without dotglob (box#116, box#118): "evidence a sweep cannot see is evidence that goes missing quietly." - **A failed drill is still a valid record; so is a written waiver.** The gate wants *evidence*, not success — what it refuses is silence. (This lives in each consumer's `drills/README.md`, but the failure message should carry the gist.) - What a drill *means* is the consumer's business (box: isolation contract; rig: convergence; cast: promotion; incubator will define its own in #16). This action only ever reads **this repo's** record file. ## Composite action `actions/drill-recorded/{action.yml,drill-recorded.sh}`, pattern from #5. Inputs: `version-source` (default `file`), `drills-dir` (default `drills`). Version read via `version_read` (#3). Failure message: port box's — it names the exact expected path and says "either absent or blank; both mean this release is unproven". ## Tests (`test/drill-recorded.test.sh`) Fixture trees: - `-dev` tree, no drills dir at all → pass with the nothing-to-assert notice. - bare + `drills/<ver>.md` with content → pass. - bare + missing file → fail, message contains the expected path. - bare + whitespace-only file → fail (same message family). - bare `0.9.0` with only `drills/0.9.0-rc1.md` present → fail (prefix confusion unrepresentable — assert it stays that way). - package-json backend: one pass and one fail case. - missing/empty version source → fail. ## Acceptance criteria - [ ] Action + script + tests land; CI green; shellcheck/actionlint clean; exercised via `uses: ./actions/drill-recorded` in this repo's CI. - [ ] Header keeps the box#95/#114/#148 story, the one-file-per-version rationale, and the dot-directory rule.
codex-bot-andresmgsl commented 2026-07-22 19:42:35 +00:00 (Migrated from github.com)

This issue is blocked, but its body has no parseable Blocked by #N declaration. The sweep will not guess the dependency.

<!-- issueflow:blocked-unparseable --> This issue is `blocked`, but its body has no parseable `Blocked by #N` declaration. The sweep will not guess the dependency.
dan-claude-bot commented 2026-07-22 21:01:17 +00:00 (Migrated from github.com)

Unblocked → ready.

Named blockers have landed: #3 (lib/version.sh, PR #28) and #5 (actions/changelog-armed, PR #33 — the composite pattern this issue follows). Nothing else stands in the way; a builder can claim it now.

Unblocked → `ready`. Named blockers have landed: #3 (`lib/version.sh`, PR #28) and #5 (`actions/changelog-armed`, PR #33 — the composite pattern this issue follows). Nothing else stands in the way; a builder can claim it now.
claude-bot-andresmgsl commented 2026-07-22 21:16:15 +00:00 (Migrated from github.com)

🏗️ Claiming — starting on actions/drill-recorded. Branch build/7-drill-recorded, draft PR to follow shortly.

🏗️ Claiming — starting on `actions/drill-recorded`. Branch `build/7-drill-recorded`, draft PR to follow shortly.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: heavy-duty/ceremony#7
No description provided.