feat(gate-107): the same seven items at the bottom-left of every app - #668
Merged
Conversation
ADR-114. Measured 2026-09-03 across the 21 core apps: ONE carries the whole chrome, and it is dossiq. Reports is in 3 of 19, Store in 4 of 19. WHY A NEW GATE, WHEN FOUR ALREADY READ THIS MENU. Every one of them is presence-blind. gate-60 validates the icon on an entry that exists. gates 62 and 63 judge the names of entries that exist. gate-104 says in its own header that it does NOT require an app to HAVE a reports page and answers NOT APPLICABLE when there is none. gate-53 checks a declared entry resolves, which cannot ask why an entry was never declared. Fifteen apps ship no Store at all and every one of those gates is green. Detection is by PAGE TYPE, not by label: launchpad and humaniq ship i18n keys as labels, dossiq's Reports entry has the id AnalyticsGroup, and a label may be translated. Only Documentation has no page behind it and is matched on id/label, which the checker documents as the exception. The walk descends into children[]. ADR-110's own fleet audit walked only top-level entries, read integriq's nested Flows as absent, and had to withdraw the claim inside the ADR. The clean fixture arm nests Flows under a group for exactly that regression. RATCHET, per ADR-114 Decision 8. Documentation, Features & roadmap, Flows and a hand-rolled Admin settings link block from day one. Store and Reports WARN until their rollouts land; two constants at the top of the checker are the promotion, and they move BOTH the presence and the placement rule so no app is hard-failed for misplacing a Store while fifteen are merely warned for having none. NOT APPLICABLE, never PASS, for an app with no pages (ADR-040 Tier-0). The test is the measurement, not a list: ADR-110 named planninq Tier-0 while it shipped pages:[], it now ships nine, and the exemption had quietly lapsed. Fixtures: three rows, one per direction of the rule, because the driver grades a verdict plus ONE named subject. Store and Reports are present and correct in BOTH arms on purpose, so promoting the ratchet later does not silently change what these rows prove. Acceptance matrix: 232 passed, 0 failed, 80 of 87 gates fixtured.
This was referenced Sep 3, 2026
added 2 commits
September 3, 2026 11:58
The runner's coverage accounting treats a gate that simply never spoke as DID
NOT RUN, by design: silence counts AGAINST coverage, and a gate stops counting
only by declaring itself not-applicable out loud. gate-107 was guarded by a
bare `if [ -f src/manifest.json ]` with no else arm, so on a repo with no
manifest it was the only silent gate in the package.
Two CI jobs caught it, both correctly:
- the entry-point invariant suite exited 98 under --require-full-coverage on
a fixture where every gap is legitimately not-applicable (69 passed, 1
failed)
- the published-location install job: "73 not-applicable + 1 unrun gates are
counted, but only 73 said so on their own line"
Adds the else arm gate-60 already has. Reproduced locally first (rc=98, gate-107
the only name in the DID NOT RUN list), then confirmed rc=0 after.
Entry-point tests: 70 passed, 0 failed.
…orce the ORDER not the numbers Both found by pointing the gate at all 19 manifest-driven apps before merging it. Either would have shipped a gate that reds an app for being right. 1. PAGE TYPE WAS NOT THE ONLY CORRECT IMPLEMENTATION. The first version tested `page.type` alone and called openregister's Features & roadmap MISSING. It is type:"custom" at /features-roadmap on purpose, and its manifest says so: "the built-in roadmap page is not adopted in this shell-swap change". shillinq's reports catalogue is custom for a reason gate-104's own header records. isSurface() now accepts the canonical route PATH as well as the type: the path is the half of the contract deep links and e2e specs already address, and gate-53 holds the menu-to-page join independently. 2. THE FOOTER ORDER IS RELATIVE. ADR-114's table names 90/92/95/100, but openregister runs 1 and 2, pipelinq 160/200/230 and keepiq 80 and 85, and every one of them is already in the right SEQUENCE. Hardcoding the numbers would red four apps whose footers read correctly and change no pixel. The gate checks the order the user can see; the numbers are what a NEW entry takes. Fixtures carry both as controls. The clean arm's roadmap is type:"custom" at the canonical path, so reverting isSurface() to a type-only test reds it (verified: findings 0 -> 1 and back). The planted arm's Reports sits at 200, below Features & roadmap, so the order row grades a real defect while pipelinq's 160/200/230 still passes on the live fleet. Fleet after the eight app PRs merged: 19 of 19 at findings=0, thematiq NOT APPLICABLE. Acceptance matrix: 235 passed, 0 failed, all four app-chrome rows graded on both arms.
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.
Implements ADR-114 (ConductionNL/hydra#643).
Why
Seven things belong at the bottom-left of every Conduction app.
CnAppNavdraws two itself (Personal settings, Admin settings, ADR-079). The other five are the app's own: Documentation, Store, Reports and Features & roadmap infooter, Flows in the settings foldout.Measured 2026-09-03 across the 21 core apps, from the effective manifest: one app carries all seven, and it is dossiq. Reports is in 3 of 19, Store in 4 of 19.
Why a new gate, when four already read this menu
Every one of them is presence-blind, which is how fifteen apps ship no Store while every gate stays green.
navigation-budget(ADR-097 D8) was specified and never implemented.How it decides
By page type, not by label. launchpad and humaniq ship i18n keys as labels (
launchpad.menu.dashboards), dossiq's Reports entry has the idAnalyticsGroup, and a label may be translated. Four of the five items are resolved by followingentry.routeto a page and reading itstype. Documentation has no page behind it and is matched on id/label as a documented exception.The walk descends into
children[]. ADR-110's own fleet audit walked only top-level entries, read integriq's nestedFlowsas absent, and had to withdraw the claim inside the ADR. The clean fixture arm nests Flows under a group for exactly that regression.The ratchet (ADR-114 D8)
/settings/admin/<appId>link.pages: [], it now ships nine, and the exemption had quietly lapsed.na, not a pass.Fixtures
Three rows, one per direction of the rule, because the driver grades a verdict plus one named subject. Store and Reports are present and correct in both arms deliberately, so promoting the ratchet later does not silently change what these rows prove. Anti-widening in the clean arm: a
Flow runsindex inmainwhose label contains "flow", and the real Flows entry nested inside a group.Verification
test_gate_acceptance_matrix.sh: 232 passed, 0 failed, 80 of 87 gates fixtured, gate-107 among them. Run against the live fleet the gate reproduces the audit exactly, including integriq/hermiq/openregister's documented Flows-in-main exceptions and shillinq's hand-rolled admin link.🤖 Generated with Claude Code
Update after running it against the live fleet
Two defects the corpus found before this merged, both of which would have shipped a gate that reds an app for being right:
page.typealone and called openregister's Features & roadmap MISSING. It istype: "custom"at/features-roadmapdeliberately ("the built-in roadmap page is not adopted in this shell-swap change"), and shillinq's reports catalogue is custom for a reason gate-104's own header records.isSurface()now accepts the canonical route path as well as the type.Both are fixture controls now: the clean arm's roadmap is a custom page at the canonical path (reverting
isSurfacereds it, verified 0 → 1 → 0), and the planted arm's Reports sits below Features & roadmap so the order row grades a real defect.Also fixed: gate-107 was the only silent gate in the package on a repo with no manifest, which failed the entry-point invariant suite under
--require-full-coverage(69/1) and the published-location install job. It now declaresnaout loud, the way gate-60 does. 70 passed, 0 failed.Fleet state: all 19 manifest-driven apps are at
findings=0, thematiq NOT APPLICABLE. The eight app PRs this gate depends on are merged. Acceptance matrix: 235 passed, 0 failed.