feat(gate-104): reports are cards on one page, in the footer - #656
Merged
Conversation
added 3 commits
August 31, 2026 22:16
ADR-112. Reports arrive one at a time, each one looks like a menu item, and the
menu is where the last one went, so a Reports branch grows an entry per report
and never loses one. shillinq reached 96 report types plus seven separate
report menu entries; dossiq declares Reports as a menu parent at order 55, in
the middle of the operational entries a case handler uses all day.
Three structural checks against the effective manifest: at most one
type:"reports" page, its menu entry in section:"footer", and no other menu
entry pointing at a page that page already carries as a card.
WHAT IT REFUSES TO CHECK. It does not require an app to HAVE a reports page:
ADR-112 Decision 4 is "one page if you have reports", not "a Reports page in
all twenty-one apps", because an empty Reports page is a promise the app does
not keep. Such an app is NOT APPLICABLE and the gate says so out loud, because
a gate reporting "passed" for an app it never inspected is how a fleet sweep
learns nothing. It also does not check that a card's route resolves — gate-53's
reachability walk already answers that question, and a second disagreeing
implementation is worse than none.
The fixtures earned their place immediately. All four first reported NOT
APPLICABLE, including three that plainly declare a reports page:
assembleFromDir returns { manifest, inputs, expansion } and I read .pages off
the wrapper, so the gate saw zero pages everywhere and called it a pass. Run
only against the real apps — which genuinely have no reports page yet — that
bug was invisible, and the gate would have shipped passing everything forever.
The entry-point suite caught a real omission: --require-full-coverage exited 98 on the all-not-applicable fixture, where 0 is the requirement. That flag exists so a gate which emitted nothing is treated as a GAP rather than a pass, and gate-104 was emitting nothing on a repo with no src/manifest.json because its whole block is guarded on that file. Gates 15, 22, 53 and 68 are already declared not-applicable by that exact prerequisite, in the central table that mirrors each guard. 104 belongs in the same line and is now in it. Worth naming why the central table is safe rather than duplicated intent: _declare_na refuses to touch a gate that already reported, so a declaration can only ever explain a silence, never un-run a gate or mask a FAIL. And the condition fires only when the prerequisite is absent, so a genuinely dead gate on a repo that HAS a manifest still lands in DID NOT RUN. 70 entry-point tests pass.
…ed scope Two acceptance suites caught this gate, and both were right. test_gate_acceptance_matrix: "gate-104 is DECLARED by the runner but has neither a planted/clean fixture nor a reasoned row in UNCOVERED.md. A gate can be added to the runner and never tested; this is that moment." It is exactly that moment: NO fleet app declares a type:"reports" page — the page type shipped the same day — so every corpus run finds the gate NOT APPLICABLE and reports "passed" having inspected nothing. Not hypothetical. The checker's first version read `.pages` off the wrapper assembleFromDir returns, saw zero pages everywhere, and called all four of its own unit fixtures NOT APPLICABLE. Against real apps that bug was invisible. The bundle arms the gate deliberately, with three rows because the rule has three directions, and a clean arm that applies the remedy to all three. Two anti-widening controls: a page no card names, which must stay clean, and a clean arm that still HAS a reports page, so a gate falling back to NOT APPLICABLE would pass it for the wrong reason. test_gate_empty_scope_never_passes: gate-104 printed PASS over a scope that excluded every planted defect (.github#374). It reads the whole effective manifest, so a narrowed run that never touches the manifest inspected nothing — and PASS is COUNTED toward the COVERAGE line, unlike a not-applicable. It now declares `na` with a reason via _skip_empty_scope, the same guard gate-22 uses for the same file. Both suites pass: acceptance matrix 0 failures, empty-scope 0 failures.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Enforces ADR-112 (hydra#pending). Depends on the
reportspage type in nextcloud-vue#893.Reports arrive one at a time, each looks like a menu item, and the menu is where the last one went. So a Reports branch grows an entry per report and never loses one. shillinq reached 96 report types plus seven separate report menu entries; dossiq declares Reports as a menu parent at order 55, among the operational entries a case handler uses all day.
Three structural checks
type:"reports"page.section: "footer".What it refuses to check
It does not require an app to have a reports page. ADR-112 Decision 4 is "one page if you have reports", not "a Reports page in all twenty-one apps" — an empty Reports page is a promise the app does not keep. Such an app is NOT APPLICABLE, and the gate says so out loud, because a gate reporting "passed" for an app it never inspected is how a fleet sweep learns nothing.
It does not check that a card's route resolves. gate-53's reachability walk already answers that, and a second disagreeing implementation of the same question is worse than none.
The fixtures earned their place immediately
All four fixtures first reported NOT APPLICABLE — including three that plainly declare a reports page.
assembleFromDirreturns{ manifest, inputs, expansion }and I read.pagesoff the wrapper, so the gate saw zero pages everywhere and called it a pass.Run only against the real apps, that bug was invisible: procest and shillinq genuinely have no
type:"reports"page yet, so NOT APPLICABLE looked correct. The gate would have shipped passing everything, forever.Fixture results after the fix:
goodduplicate-entrywrong-sectiontwo-pagesWired fail-closed, mirroring gate-68: a missing vendored helper FAILs, and a checker that exits without its
findings=marker is a wiring SKIP, never a silent pass.