fix(module): coverage over nothing is not coverage - #61
Conversation
`gds module coverage` reported success having compared no module against any gate. An estate whose repositories are not declared as git-submodule-consumer relationships gets `"modules": []` and `result: succeeded`, which is indistinguishable from every module being covered. The estates most likely to hit it are exactly the ones asking whether their gates are watched. setup-systems ran it against eight repositories expecting a verdict on fourteen required contexts each, and got green over an empty set. That is the same class of defect this command exists to find, arriving inside the tool meant to find it. Empty scope now emits GDS_MODULE_COVERAGE_SCOPE_NOT_PROVEN, carrying the declared relationship count so the reason is legible. Not-proven rather than validation: an estate with no modules is not invalid, but nothing about its gates was proven either. There were no tests for CoverModules at all, which is part of why this shipped. The new test runs the real command against this repository, which declares no relationships, and asserts the specific finding code rather than a non-zero exit -- with `XDG_CONFIG_HOME` redirected the command fails earlier at GDS_POLICY_ESTATE_NOT_PROVEN and never reaches this path, so a test asserting only the exit code would have passed without covering anything. Verified by mutation: replacing the guard condition with `false` makes it fail.
`core/app` is one of the source paths the bundle lock digests, so the coverage fix changed the source tree identity and left both projections stale against it. Regenerated through `gds generate repository` plan/apply; `--check` now reports no findings.
…hing # Conflicts: # .gds/bundle.lock.yaml # .github/workflows/gds-ci.yml
The merge with main brought in the schema-digest change's own regenerated projections; both are generated from the same source paths, so the merged tree needs one regeneration rather than either side's. `--check` reports no findings.
The first version of this test lived in core/cli and was green on my machine for the wrong reason: a registered estate happened to be configured there, so the command reached the guard. CI has none, so `gds module coverage` failed earlier at GDS_POLICY_ESTATE_NOT_PROVEN and the test never touched the path it claimed to cover. That is the defect this pull request fixes, reproduced inside its own test. It was only visible because the assertion named the finding code; an assertion on a non-zero exit would have stayed green over the wrong refusal, locally and in CI alike. Moved to core/app against `appTestRepositoryRoot`, which builds an external estate in a temporary directory and exports GDS_ESTATE_ROOT, so the test carries its own preconditions. Verified twice: mutating the guard to `false` makes it fail, and it passes with HOME, XDG_CONFIG_HOME, XDG_STATE_HOME and GDS_ESTATE_ROOT all cleared, which is the CI shape.
|
The test I shipped first was green on my machine and red in CI, for exactly the reason this PR exists.
So the defect reproduced itself inside its own test, one layer up. Moved to Verified in both directions again on the new location: mutating the guard to |
`core/app` is a digested source path in whole, so a test-only change to it moves the source tree identity as much as a change to the command does.
gds module coveragereported success having compared no module against any gate.The defect
An estate whose repositories are not declared as
git-submodule-consumerrelationships gets:{"result": "succeeded", "exit_class": "success", "data": {"modules": []}}Success over an empty set. That is indistinguishable from every module being covered, and the estates most likely to hit it are exactly the ones asking whether their gates are watched at all.
setup-systemsfound it: they ran it against eight repositories expecting a verdict on fourteen required contexts each, and got green over nothing. This command exists to catch a gate drifting away from its declaration — the same class of defect arriving inside the tool meant to find it.There was already a guard for the narrower case:
--module <name>that matches nothing returnsGDS_MODULE_COVERAGE_SELECTION_UNKNOWN. The unselected empty case had none.The change
Empty scope now emits
GDS_MODULE_COVERAGE_SCOPE_NOT_PROVEN, carryingdeclared_relationshipsso the reason is legible rather than inferred:{"result": "not-proven", "exit_code": 3, "findings": [{"code": "GDS_MODULE_COVERAGE_SCOPE_NOT_PROVEN", "evidence": {"declared_relationships": 0, "repository_id": "repo_…"}}]}Not-proven rather than validation. An estate with no declared modules is not invalid — a standalone repository legitimately has none. But nothing about its gates was proven either, and that is what exit 3 says. The classifier keys
NOT_PROVENin the code name toExitNotProven, so the name carries the classification.Testing
There were no tests for
CoverModulesat all, which is part of why this shipped.The new test runs the real command against this repository, which declares no relationships, and asserts the specific finding code rather than a non-zero exit. That distinction turned out to matter: with
XDG_CONFIG_HOMEredirected to a temp dir — the pattern the neighbouring tests use — the command fails earlier atGDS_POLICY_ESTATE_NOT_PROVENand never reaches this path. A test asserting onlyexitCode != 0would have passed while covering nothing, which is the defect this PR fixes, in the test for it.Verified by mutation: replacing the guard condition with
falsemakes the test fail; restoring it passes.Projections
core/appis one of the source paths the bundle lock digests, so the fix changed the source tree identity. Regenerated throughgds generate repositoryplan/apply;--checkreports no findings and the full suite is clean.