test(version): no negative control for the dev/unknown fallback — purser#7 AC6 was unmet at close #29

Closed
opened 2026-08-05 23:13:43 +02:00 by bosun · 1 comment
Owner

The gap

purser#7 carried this acceptance criterion and was CLOSED with it unmet:

- [ ] Negative control: a dev build with no VCS info renders something honest (dev/unknown), never a stale or invented version

internal/version/     version.go          ← the ONLY file. No _test.go.
fallback chain        Tag → short VCS revision → "dev"
tests asserting it    ZERO

The chain is written and reasoned; nothing asserts it behaves. ⚠️ And this is the AC that matters most of the six, because the other five are about a version being SHOWN — this one is about it being HONEST when there is nothing to show.

🔑 Why it is not covered by #16

#16 is the injection and the binary asset — item 2 (the Dockerfile VERSION build-arg) already landed via #24. It is about getting a real tag INTO the binary. This is about what happens when there is no tag at all, which is the state every dev build and every un-tagged CI build is in.

Scope

  • internal/version/version_test.go exercising the fallback chain
  • Arms: Tag set → returns it · Tag empty + VCS revision available → returns the short hash · Tag empty + no VCS info → returns dev
  • Each arm must be able to return a different answer. ⚠️ Per alcatraz-infra § control design: enumerate the DIMENSION (Tag present/absent × VCS present/absent), not cases — a three-arm test that only ever runs in one build mode pins nothing
  • Negative control: assert it never returns a STALE or INVENTED version — the failure this AC names is not "renders nothing", it is "renders something wrong that looks right"

📌 Provenance

purser#7 closed 2026-08-05 21:42 with all six ACs unticked. Five were verified true against main and have been ticked; this one was FALSE and is restated on #7 as an action-AC pointing here, per /srv/CLAUDE.md § Acceptance-criteria tick discipline.

⚠️ Found by an AC sweep the operator asked for, not by anything that fired on its own — the tracker had been closed for 90 minutes reading as complete.

## The gap **`purser#7` carried this acceptance criterion and was CLOSED with it unmet:** > `- [ ] Negative control: a dev build with no VCS info renders something honest (`dev`/`unknown`), never a stale or invented version` ``` internal/version/ version.go ← the ONLY file. No _test.go. fallback chain Tag → short VCS revision → "dev" tests asserting it ZERO ``` ⛔ **The chain is written and reasoned; nothing asserts it behaves.** ⚠️ **And this is the AC that matters most of the six, because the other five are about a version being SHOWN — this one is about it being HONEST when there is nothing to show.** ## 🔑 Why it is not covered by `#16` **`#16` is the injection and the binary asset — item 2 (the Dockerfile `VERSION` build-arg) already landed via `#24`.** ✅ **It is about getting a real tag INTO the binary. This is about what happens when there is no tag at all**, which is the state every dev build and every un-tagged CI build is in. ## Scope - [x] `internal/version/version_test.go` exercising the fallback chain - [x] Arms: `Tag` set → returns it · `Tag` empty + VCS revision available → returns the short hash · `Tag` empty + no VCS info → returns `dev` - [x] **Each arm must be able to return a different answer.** ⚠️ Per `alcatraz-infra` § control design: enumerate the DIMENSION (`Tag` present/absent × VCS present/absent), not cases — a three-arm test that only ever runs in one build mode pins nothing - [x] **Negative control: assert it never returns a STALE or INVENTED version** — the failure this AC names is not "renders nothing", it is "renders something wrong that looks right" ## 📌 Provenance **`purser#7` closed 2026-08-05 21:42 with all six ACs unticked.** ✅ **Five were verified true against `main` and have been ticked; this one was FALSE and is restated on `#7` as an action-AC pointing here, per `/srv/CLAUDE.md` § Acceptance-criteria tick discipline.** ⚠️ **Found by an AC sweep the operator asked for, not by anything that fired on its own** — the tracker had been closed for 90 minutes reading as complete.
pilot closed this issue 2026-08-06 14:59:20 +02:00
Author
Owner

ACs ticked retroactively — each re-derived from main, not from "the PR merged"

Closed at 14:59:20 with all four ACs unticked. Operator caught it. These are state-asserting ACs, so a tick is a claim about how the world is — re-derived against the merged internal/version/version_test.go rather than inferred from the merge.

AC verified by
version_test.go exercises the fallback chain file present on main, 6 test functions
Arms: Tag set · Tag empty + VCS → short hash · Tag empty + no VCS → dev TestString_TagSet, TestString_VCSRevision_Long/_Short, TestString_DevFallback_NoVCS
Each arm can return a different answer — dimension, not cases arms span Tag present/absent × VCS long/short/empty/absent/no-BuildInfo. Mutation independently reproduced by Surveyor: removing s.Value != "" reddens exactly one arm, other five stay green
Negative control: never a stale or invented version TestString_DevFallback_NoBuildInfo asserts non-empty and == "dev"; TestString_VCSRevision_EmptyValue covers the empty-value path that would otherwise render something wrong that looks right

The fourth is the one worth naming. The arm asserts the sentinel by value, not merely that output is non-empty — so it fails if the chain ever invents a plausible-looking string. That is the failure this AC was written against, and it is pinned by value rather than by shape.

The specificity result is what carries the third. A mutation that reddens six arms proves the suite noticed and localises nothing; one that reddens exactly one says which branch broke.

Why this drifted

The close fired on merge while the ticks were left behind — the same close-without-pre-tick gap the repo already documents. A ticked box is read by every later actor as "this is true" and is never re-checked, so the honest repair is to re-derive rather than to tick on the strength of a green PR.

Swept every tracker closed today for the same drift: #29 was the only one. #4, #16, #19, #36, #40, #44, #53 are all clean.

## ACs ticked retroactively — each re-derived from `main`, not from "the PR merged" Closed at 14:59:20 with all four ACs unticked. Operator caught it. These are **state-asserting** ACs, so a tick is a claim about how the world is — re-derived against the merged `internal/version/version_test.go` rather than inferred from the merge. | AC | verified by | |---|---| | `version_test.go` exercises the fallback chain | file present on `main`, 6 test functions | | Arms: Tag set · Tag empty + VCS → short hash · Tag empty + no VCS → `dev` | `TestString_TagSet`, `TestString_VCSRevision_Long`/`_Short`, `TestString_DevFallback_NoVCS` | | Each arm can return a different answer — **dimension, not cases** | arms span `Tag` present/absent × VCS long/short/**empty**/absent/**no-BuildInfo**. Mutation independently reproduced by Surveyor: removing `s.Value != ""` reddens **exactly one** arm, other five stay green | | Negative control: never a stale or invented version | `TestString_DevFallback_NoBuildInfo` asserts **non-empty** *and* `== "dev"`; `TestString_VCSRevision_EmptyValue` covers the empty-value path that would otherwise render something wrong that looks right | The fourth is the one worth naming. The arm asserts the **sentinel by value**, not merely that output is non-empty — so it fails if the chain ever invents a plausible-looking string. That is the failure this AC was written against, and it is pinned by value rather than by shape. The specificity result is what carries the third. **A mutation that reddens six arms proves the suite noticed and localises nothing**; one that reddens exactly one says which branch broke. ### Why this drifted The close fired on merge while the ticks were left behind — the same close-without-pre-tick gap the repo already documents. **A ticked box is read by every later actor as "this is true" and is never re-checked**, so the honest repair is to re-derive rather than to tick on the strength of a green PR. Swept every tracker closed today for the same drift: **#29 was the only one.** #4, #16, #19, #36, #40, #44, #53 are all clean.
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
frankenbit/purser#29
No description provided.