Decision record

AD-0022 Track internal/assert and internal/tabletest

Status: accepted. Date: 2026-08-27.

Context

Two packages sit under internal/. assert holds assertion helpers over type parameters and go-cmp, so a comparison between mismatched types is a compile error at the call site rather than a report at run time, and tabletest is a table-driven test runner built on assert. Each carries its own tests.

The repository’s tests are directed to use internal/assert rather than a library that takes any throughout, because the empty-interface rule governs what this code declares and not what a dependency accepts. Until this decision no allowlist rule named internal/, so git ls-files internal was empty and a tracked test importing github.com/Roarge/sysml-federation/internal/assert would fail on a fresh clone and in CI, which runs go build ./... and the unit tests on every pull request. The implementation phase is test-first (SC-03), so its first tracked test meets that conflict on its first push.

Decision

We track internal/assert and internal/tabletest as ordinary Go packages, and tracked tests import them. The mechanism is three additions: !/internal/ with !/internal/**/ in .gitignore, internal in the Makefile’s ALLOWLIST_ROOTS so that a forgotten file under internal/ is reported, and the tracking policy amended so that internal/ moves from the local-only column to the tracked one. The two allowlist rules open the whole internal/ tree to the allowlisted extensions, and these two packages are all it holds.

Alternatives considered

Keep tracked tests free of the import. Tests would compare with == and go-cmp directly, or each package would carry its own small helpers, and the tracking policy would stand as written. The cost is the same loop and the same comparison repeated in every table-driven test, and two packages left on disk with no consumer, which is the opposite of what the rule directing tests to a typed helper asks for.

Consequences

Tests read as the rule intended, a case whose Want does not match the function’s result is a compile error rather than a run-time report, and a fresh clone and CI run the same tests the maintainer runs. The helpers become public code under Apache 2.0 and are read by the visitor who reads the repository in an afternoon, so they count towards the impression the code makes, though not towards SC-06’s figures, which name no component under internal/.

The tracking policy loses its clean shape. internal/ was one line in the local-only column and becomes a tracked tree with an exception noted beside it, and any future helper that exists only to constrain how the code is written has to live outside the tracked tree instead. The assert package’s own test carries two explicit allowances on a stand-in for testing.TB, whose variadic signature the standard library imposes, and those allowances travel with it into the tracked tree.

No implementation spike depends on this decision.

Requirements affected

SC-03, SC-04

Sources

internal/assert/assert.go and internal/tabletest/tabletest.go as they stand in the repository, and the .gitignore and Makefile rules named above. How the design was run for the allowlist and the empty-interface rule, and From use cases to requirements for the conflict that made the decision necessary.