Skip to content

feat(gate-104): reports are cards on one page, in the footer - #656

Merged
rubenvdlinde merged 3 commits into
mainfrom
feat/gate-reports-one-page
Sep 1, 2026
Merged

feat(gate-104): reports are cards on one page, in the footer#656
rubenvdlinde merged 3 commits into
mainfrom
feat/gate-reports-one-page

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

Enforces ADR-112 (hydra#pending). Depends on the reports page 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

  1. At most one type:"reports" page.
  2. Its menu entry is section: "footer".
  3. No other menu entry points 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" — 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. 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, 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:

fixture result
good 0 findings
duplicate-entry catches a report reachable as both card and menu entry
wrong-section catches Reports outside the footer
two-pages catches a second reports page

Wired 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.

Conduction Release Bot 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.
@rubenvdlinde
rubenvdlinde merged commit 45db47d into main Sep 1, 2026
41 of 42 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant