From 9b43ffadd66d73fc749ca61b5ee0b66a9f623ed9 Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Wed, 7 Oct 2026 09:34:23 +0200 Subject: [PATCH] feat(hydra-gates): Reports sits in the settings foldout, labelled Advanced (gates 104, 107) Ruben decided on 2026-10-07 that the settings foldout is renamed Advanced and that Reports goes under it in every app (ADR-112 D3, ADR-114 D1 and D3, amended in hydra). gate-104 now requires the Reports entry in section "settings" and names the footer or the main menu when it is not. gate-107 moves Reports from the footer to settings and drops it from the footer sequence. Fixtures: the reports-one-page and app-chrome bundles follow the new rule (app-chrome's footer-order defect is now planted on Store). Two new bundles, reports-one-page-footer and app-chrome-reports-footer, differ in one field: Reports in the footer fails, Reports in settings passes. --- hydra-gates/scripts/lib/check_app_chrome.js | 30 ++- .../scripts/lib/check_reports_one_page.js | 25 ++- hydra-gates/scripts/run-hydra-gates.sh | 12 +- .../clean/appinfo/info.xml | 13 ++ .../clean/package-lock.json | 11 ++ .../clean/src/manifest.json | 176 +++++++++++++++++ .../app-chrome-reports-footer/expect.conf | 19 ++ .../planted/appinfo/info.xml | 13 ++ .../planted/package-lock.json | 11 ++ .../planted/src/manifest.json | 177 ++++++++++++++++++ .../app-chrome/clean/src/manifest.json | 2 +- .../gate-acceptance/app-chrome/expect.conf | 13 +- .../app-chrome/planted/src/manifest.json | 12 +- .../clean/appinfo/info.xml | 13 ++ .../clean/package-lock.json | 11 ++ .../clean/src/manifest.json | 99 ++++++++++ .../reports-one-page-footer/expect.conf | 18 ++ .../planted/appinfo/info.xml | 13 ++ .../planted/package-lock.json | 11 ++ .../planted/src/manifest.json | 100 ++++++++++ .../reports-one-page/clean/src/manifest.json | 4 +- .../reports-one-page/expect.conf | 11 +- .../planted/src/manifest.json | 2 +- .../duplicate-entry/src/manifest.json | 2 +- .../reports-one-page/good/src/manifest.json | 2 +- .../two-pages/src/manifest.json | 2 +- 26 files changed, 758 insertions(+), 44 deletions(-) create mode 100644 hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/clean/appinfo/info.xml create mode 100644 hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/clean/package-lock.json create mode 100644 hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/clean/src/manifest.json create mode 100644 hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/expect.conf create mode 100644 hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/planted/appinfo/info.xml create mode 100644 hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/planted/package-lock.json create mode 100644 hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/planted/src/manifest.json create mode 100644 hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/clean/appinfo/info.xml create mode 100644 hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/clean/package-lock.json create mode 100644 hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/clean/src/manifest.json create mode 100644 hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/expect.conf create mode 100644 hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/planted/appinfo/info.xml create mode 100644 hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/planted/package-lock.json create mode 100644 hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/planted/src/manifest.json diff --git a/hydra-gates/scripts/lib/check_app_chrome.js b/hydra-gates/scripts/lib/check_app_chrome.js index 892aa022e..e334dd69e 100644 --- a/hydra-gates/scripts/lib/check_app_chrome.js +++ b/hydra-gates/scripts/lib/check_app_chrome.js @@ -12,10 +12,15 @@ // // footer Documentation order 90 href to the app's docs site // footer Store order 92 type: "store" -// footer Reports order 95 type: "reports" // footer Features & roadmap order 100 type: "roadmap" +// settings Reports type: "reports" // settings Flows type: "flows" // +// The settings foldout is labelled Advanced. Reports moved there from the +// footer on 2026-10-07, on Ruben's decision (ADR-114 Decisions 1 and 3 and +// ADR-112 Decision 3, amended that day). A Reports entry in the footer or the +// main menu is now a placement finding. +// // Measured 2026-09-03 across the 21 core apps: ONE carries all seven, and it // is dossiq. Reports is in 3 of 19, Store in 4 of 19. // @@ -362,10 +367,11 @@ const CHROME = [ }, { item: 'Reports', - section: 'footer', + section: 'settings', blocking: REPORTS_IS_BLOCKING, find: () => entries.find(({ node }) => isSurface(pageOf(node), 'reports', '/reports')), - absent: 'no Reports entry. ADR-114 Decision 3 amends ADR-112 Decision 4: the title of that ADR wins, and every app in scope declares one type:"reports" page in section:"footer" at order 95. An empty Reports page is still forbidden, so an app with nothing to report fills the page rather than skipping it.', + absent: 'no Reports entry. ADR-114 Decision 3 amends ADR-112 Decision 4: every app in scope declares one type:"reports" page and one entry for it in section:"settings", the foldout labelled Advanced (amended 2026-10-07). An empty Reports page is still forbidden, so an app with nothing to report fills the page rather than skipping it.', + misplaced: 'ADR-114 Decisions 1 and 3, amended 2026-10-07: Reports is one entry in the settings foldout, labelled Advanced, not in the footer or the main menu.', }, { item: 'Features & roadmap', @@ -400,7 +406,8 @@ for (const spec of CHROME) { continue } - report(severity, spec.item, `the ${spec.item} entry is section:${JSON.stringify(hit.node.section ?? null)}, not ${JSON.stringify(spec.section)}. ADR-114 Decision 1 fixes the sections so the bottom-left of the navigation reads the same in every app.`) + const why = spec.misplaced || 'ADR-114 Decision 1 fixes the sections so the bottom-left of the navigation reads the same in every app.' + report(severity, spec.item, `the ${spec.item} entry is section:${JSON.stringify(hit.node.section ?? null)}, not ${JSON.stringify(spec.section)}. ${why}`) } // --- Decision 1: the footer group reads in the same ORDER everywhere -------- @@ -413,14 +420,19 @@ for (const spec of CHROME) { // would be a fleet-wide diff that changes not one pixel. // // What a user can actually see is the sequence, so the sequence is the rule: -// Documentation, then Store, then Reports, then Features & roadmap. Measured -// 2026-09-03: all 19 manifest-driven apps already satisfy it, so this blocks -// nothing today and catches the next entry dropped in at the wrong number. +// Documentation, then Store, then Features & roadmap. Measured 2026-09-03: +// all 19 manifest-driven apps already satisfy it, so this blocks nothing today +// and catches the next entry dropped in at the wrong number. +// +// Reports left this sequence on 2026-10-07, when it moved to the settings +// foldout (ADR-114 Decision 1, amended). A Reports entry still in the footer is +// reported once, as a placement finding above, and is not ordered here. { - const wanted = ['Documentation', 'Store', 'Reports', 'Features & roadmap'] + const wanted = ['Documentation', 'Store', 'Features & roadmap'] const placed = [] for (const spec of CHROME) { if (spec.section !== 'footer') { continue } + if (!wanted.includes(spec.item)) { continue } const hit = spec.find() if (hit === undefined || sectionOf(hit.node) !== 'footer') { continue } const order = typeof hit.node.order === 'number' ? hit.node.order : null @@ -433,7 +445,7 @@ for (const spec of CHROME) { const expected = wanted.filter((w) => actual.includes(w)) if (actual.join(' > ') !== expected.join(' > ')) { - report('error', 'footer order', `the footer group reads ${JSON.stringify(actual.join(' > '))}, and ADR-114 Decision 1 orders it ${JSON.stringify(expected.join(' > '))}. The RELATIVE order is the rule, not the absolute numbers: a new entry takes Documentation 90, Store 92, Reports 95, Features & roadmap 100, but an app already running its footer at other numbers only has to keep the sequence.`) + report('error', 'footer order', `the footer group reads ${JSON.stringify(actual.join(' > '))}, and ADR-114 Decision 1 orders it ${JSON.stringify(expected.join(' > '))}. The RELATIVE order is the rule, not the absolute numbers: a new entry takes Documentation 90, Store 92, Features & roadmap 100, but an app already running its footer at other numbers only has to keep the sequence.`) } } diff --git a/hydra-gates/scripts/lib/check_reports_one_page.js b/hydra-gates/scripts/lib/check_reports_one_page.js index ae86c6ff7..7149a94d3 100644 --- a/hydra-gates/scripts/lib/check_reports_one_page.js +++ b/hydra-gates/scripts/lib/check_reports_one_page.js @@ -3,8 +3,8 @@ // // check_reports_one_page.js — gate (reports-one-page) checker. // -// Implements ADR-112: "Reports are one page of cards, in the footer, in every -// app." Reports arrive one at a time, each one is obviously a menu item, and +// Implements ADR-112: "Reports are one page of cards, in every app." Reports +// arrive one at a time, each one is obviously 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. // @@ -14,7 +14,10 @@ // // 1. AT MOST ONE `type: "reports"` page. Two reports pages is the submenu // problem again, one level down. -// 2. The menu entry pointing at it is `section: "footer"` (ADR-112 D3). +// 2. The menu entry pointing at it is `section: "settings"`, the foldout +// the shell labels Advanced (ADR-112 D3, amended 2026-10-07 on Ruben's +// decision). It used to be `section: "footer"`; a Reports entry in the +// footer or in the main menu is now a finding. // 3. No OTHER menu entry points at a page that the reports page already // lists as a card. That is the duplication the ADR exists to remove: the // report is reachable twice, and the menu copy is the one that never @@ -155,21 +158,29 @@ if (reportsPages.length > 1) { const reportsPage = reportsPages[0] -// 2. Its menu entry sits in the footer group. +// 2. Its menu entry sits in the settings foldout, labelled Advanced. +// +// ADR-112 Decision 3 as amended 2026-10-07 (Ruben's decision): the shell's +// settings foldout is renamed Advanced, and Reports is one entry inside it. +// Before that date the rule was `section: "footer"`, and the footer is now +// exactly as wrong as the main menu, so the message names both. +const REPORTS_SECTION = 'settings' const reportsEntry = entries.find(({ node }) => node.route === reportsPage.id) if (reportsEntry === undefined) { findings.push({ severity: 'error', path: rel, pageIds: [reportsPage.id], - message: `the type:"reports" page "${reportsPage.id}" has no menu entry, so nothing reaches it. ADR-112 Decision 3 puts one in the footer group.`, + message: `the type:"reports" page "${reportsPage.id}" has no menu entry, so nothing reaches it. ADR-112 Decision 3 (amended 2026-10-07) puts one entry in the settings foldout, labelled Advanced.`, }) -} else if (reportsEntry.node.section !== 'footer') { +} else if (reportsEntry.node.section !== REPORTS_SECTION) { + const sec = reportsEntry.node.section + const where = sec === 'footer' ? 'the footer' : ((sec === undefined || sec === null || sec === '' || sec === 'main') ? 'the main menu' : `the "${sec}" section`) findings.push({ severity: 'error', path: rel, pageIds: [reportsPage.id], - message: `the Reports menu entry is section:${JSON.stringify(reportsEntry.node.section ?? null)}, not "footer". ADR-112 Decision 3: Reports is a place you go deliberately, not a place you work, so it belongs with Documentation and Features & roadmap rather than among the operational entries.`, + message: `the Reports menu entry is section:${JSON.stringify(reportsEntry.node.section ?? null)}, not "settings", so it renders in ${where}. ADR-112 Decision 3, amended 2026-10-07: Reports is one entry in the settings foldout, labelled Advanced, not in the footer or the main menu. Reports is a place you go deliberately, not a place you work.`, }) } diff --git a/hydra-gates/scripts/run-hydra-gates.sh b/hydra-gates/scripts/run-hydra-gates.sh index 29590e789..f1bda9b4d 100755 --- a/hydra-gates/scripts/run-hydra-gates.sh +++ b/hydra-gates/scripts/run-hydra-gates.sh @@ -11246,14 +11246,15 @@ if [ -f src/manifest.json ]; then fi # --------------------------------------------------------------------------- -# Gate 104: reports-one-page — reports are cards on one page, in the footer. +# Gate 104: reports-one-page — reports are cards on one page, in the settings +# foldout labelled Advanced (ADR-112 D3, amended 2026-10-07; was the footer). # # ADR-112. 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). # # Checks three STRUCTURAL things against the effective manifest: at most one -# type:"reports" page; its menu entry is section:"footer"; and no other menu +# type:"reports" page; its menu entry is section:"settings"; and no other menu # entry points at a page that page already carries as a card. # # NOT APPLICABLE when the app declares no type:"reports" page — ADR-112 @@ -11328,7 +11329,7 @@ if [ -f src/manifest.json ]; then if [ "${_rop_rc}" -eq 0 ]; then _pass 104 "reports-one-page" else - _fail 104 "reports-one-page" "${_rop_reported} finding(s) against ADR-112 (one reports page, in the footer, no report reachable both as a card and as a menu entry) — see ${_rop_log}" + _fail 104 "reports-one-page" "${_rop_reported} finding(s) against ADR-112 (one reports page, its entry in the settings foldout labelled Advanced, no report reachable both as a card and as a menu entry) — see ${_rop_log}" fi ;; esac @@ -11341,8 +11342,9 @@ fi # # ADR-114. CnAppNav draws Personal settings and Admin settings itself and no # app declares them (ADR-079 / ADR-110 D2). The other five are the app's own: -# Documentation, Store, Reports and Features & roadmap in `footer`, Flows in -# the settings foldout. Measured 2026-09-03: ONE of 21 apps carries all seven. +# Documentation, Store and Features & roadmap in `footer`, Reports and Flows in +# the settings foldout labelled Advanced (Reports moved there from the footer +# on 2026-10-07, ADR-114 D1 and D3 as amended). Measured 2026-09-03: ONE of 21 apps carries all seven. # # WHY THIS IS NOT COVERED BY THE FOUR GATES THAT ALREADY READ THIS MENU: # every one of them is presence-blind. gate-60 validates the icon on an entry diff --git a/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/clean/appinfo/info.xml b/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/clean/appinfo/info.xml new file mode 100644 index 000000000..551ef108c --- /dev/null +++ b/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/clean/appinfo/info.xml @@ -0,0 +1,13 @@ + + + acfx + App Chrome Fixture + Fixture app for gate-107 acceptance. Not a real app. + Exists so the gate suite can drive an app whose bottom-left chrome is and is not the seven items ADR-114 requires, through the real effective-manifest builder. + 1.0.0 + agpl + Conduction + AcFx + tools + + diff --git a/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/clean/package-lock.json b/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/clean/package-lock.json new file mode 100644 index 000000000..71cf7c6d9 --- /dev/null +++ b/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/clean/package-lock.json @@ -0,0 +1,11 @@ +{ + "name": "gate107-fixture", + "lockfileVersion": 3, + "requires": true, + "packages": { + "node_modules/@conduction/nextcloud-vue": { + "version": "2.33.0", + "comment": "First generation carrying CnReportsPage (2026-08-31) AND the store page type, so a type:\"reports\" / type:\"store\" page in this fixture is one that would actually render." + } + } +} diff --git a/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/clean/src/manifest.json b/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/clean/src/manifest.json new file mode 100644 index 000000000..dfb54d444 --- /dev/null +++ b/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/clean/src/manifest.json @@ -0,0 +1,176 @@ +{ + "$schema": "https://raw.githubusercontent.com/ConductionNL/nextcloud-vue/main/src/schemas/app-manifest-v2.schema.json", + "version": "2.0.0", + "nav": { + "includePersonalSettings": false, + "_note": "ANTI-WIDENING 4: suppressing the auto-prepended entry is LEGITIMATE when the app declares its own action:\"user-settings\" entry, which opens the same dialog. keepiq does exactly this and injects its own sections into CnAppRoot's #user-settings slot; re-enabling the shell copy would give it two entries onto one dialog. A gate that flagged the flag alone would red keepiq." + }, + "_note": "CLEAN arm: the same app with ReportsMenu in section:\"settings\", the foldout labelled Advanced (amended 2026-10-07). Everything else matches the planted arm.", + "menu": [ + { + "id": "DashboardMenu", + "label": "Dashboard", + "icon": "ViewDashboardOutline", + "route": "Dashboard", + "order": 10 + }, + { + "id": "FlowRunsMenu", + "label": "Flow runs", + "icon": "PlayCircleOutline", + "route": "FlowRuns", + "order": 20, + "_note": "ANTI-WIDENING 1: an operational list whose LABEL contains 'flow' but which is an ordinary index, in main, and is not the flow authoring surface. A gate that matched Flows on the label rather than on the page type would accept this as the Flows entry in the planted arm and would flag its placement here. Detection is by page type for exactly this reason." + }, + { + "id": "DocsMenu", + "label": "Documentation", + "icon": "BookOpenVariantOutline", + "href": "https://example.org", + "order": 90, + "section": "footer" + }, + { + "id": "StoreMenu", + "label": "Store", + "icon": "StoreOutline", + "route": "Store", + "order": 92, + "section": "footer" + }, + { + "id": "ReportsMenu", + "label": "Reports", + "icon": "ChartBoxOutline", + "route": "Reports", + "order": 95, + "section": "settings" + }, + { + "id": "RoadmapMenu", + "label": "Features & roadmap", + "icon": "MapMarkerPath", + "route": "FeaturesRoadmap", + "order": 100, + "section": "footer" + }, + { + "id": "UserSettings", + "label": "Personal settings", + "icon": "CogOutline", + "action": "user-settings", + "section": "settings", + "order": 1 + }, + { + "id": "ConfigGroup", + "label": "Configuration", + "icon": "CogOutline", + "section": "settings", + "order": 50, + "children": [ + { + "id": "FlowsMenu", + "label": "Flows", + "icon": "Sitemap", + "route": "Flows", + "order": 10, + "section": "settings", + "_note": "ANTI-WIDENING 2: nested under a group rather than declared top-level. ADR-110's own fleet audit walked only top-level menu[] entries, read integriq's nested Flows as absent, and had to withdraw the claim inside the ADR. A gate that does not descend into children[] turns this clean arm red." + } + ] + } + ], + "pages": [ + { + "id": "Dashboard", + "route": "/", + "type": "dashboard", + "title": "Dashboard", + "config": { + "widgets": [] + } + }, + { + "id": "FlowRuns", + "route": "/flow-runs", + "type": "index", + "title": "Flow runs", + "config": { + "register": "acfx", + "schema": "case", + "columns": [ + "name" + ] + } + }, + { + "id": "Flows", + "route": "/flows", + "type": "index", + "title": "Flows", + "config": { + "entitySource": "flows", + "app": "acfx", + "rowRoute": "FlowDetail" + }, + "_note": "The CANONICAL flow index: type:\"index\" over the named `flows` entity source. nextcloud-vue marks the dedicated `flows` / `flow-detail` page types DEPRECATED in CnPageRenderer/pageTypes.js, kept only so the fleet can migrate without a flag day. The gate must recognise this shape, because it is the one new apps are supposed to write." + }, + { + "id": "FlowDetail", + "route": "/flows/:id", + "type": "flow", + "title": "Flow", + "config": { + "app": "acfx" + } + }, + { + "id": "Store", + "route": "/store", + "type": "custom", + "component": "StoreGallery", + "title": "Store", + "config": { + "storeRegistry": true + }, + "_note": "The shape dossiq ships today. `store` is NOT yet a page type in the manifest schema, so a fixture declaring one would red gate-22 while proving nothing about gate-107. When nextcloud-vue absorbs dossiq's StoreGallery as CnStorePage and the enum gains `store`, this becomes type:\"store\" and the checker already accepts it." + }, + { + "id": "Reports", + "route": "/reports", + "type": "reports", + "title": "Reports", + "config": { + "cards": [ + { + "id": "t", + "label": "Throughput", + "route": "Throughput" + } + ] + } + }, + { + "id": "Throughput", + "route": "/reports/throughput", + "type": "index", + "title": "Throughput", + "config": { + "register": "acfx", + "schema": "case", + "columns": [ + "name" + ] + } + }, + { + "id": "FeaturesRoadmap", + "route": "/features-roadmap", + "type": "custom", + "component": "FeaturesRoadmapIndex", + "title": "Features & roadmap", + "_note": "ANTI-WIDENING 3: the roadmap surface is type:\"custom\" at the canonical path, which is what openregister actually ships (\"the built-in roadmap page is not adopted in this shell-swap change\"), and what shillinq ships for reports. A gate that insisted on the page TYPE would call this MISSING and red an app that is right. Reverting isSurface() to a type-only test turns this clean arm red, which is the point: the regression was found on the live fleet before the gate merged, and this is what stops it coming back." + } + ] +} diff --git a/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/expect.conf b/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/expect.conf new file mode 100644 index 000000000..b4708d66c --- /dev/null +++ b/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/expect.conf @@ -0,0 +1,19 @@ +# expect.conf — read by scripts/lib/test_gate_acceptance_matrix.sh +# +# Columns: +# gate +# +# Bundle: app-chrome-reports-footer — gate-107 (ADR-114 Decisions 1 and 3, +# amended 2026-10-07). +# +# Ruben decided on 2026-10-07 that the settings foldout is labelled Advanced +# and that Reports lives in it, in every app. Until then ADR-114 put Reports in +# the footer at order 95, so every app that followed the old rule now has +# exactly this planted shape. +# +# Both arms are copies of the app-chrome CLEAN arm, so every other chrome item +# is present and correctly placed, and the anti-widening controls of that +# bundle ride along. The arms differ in ONE field: ReportsMenu's section. The +# planted arm has it in `footer` and must fail naming the new rule; the clean +# arm has it in `settings` and must pass. +gate 107 hydra-gate-app-chrome.log FAIL PASS Reports is one entry in the settings foldout, labelled Advanced diff --git a/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/planted/appinfo/info.xml b/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/planted/appinfo/info.xml new file mode 100644 index 000000000..551ef108c --- /dev/null +++ b/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/planted/appinfo/info.xml @@ -0,0 +1,13 @@ + + + acfx + App Chrome Fixture + Fixture app for gate-107 acceptance. Not a real app. + Exists so the gate suite can drive an app whose bottom-left chrome is and is not the seven items ADR-114 requires, through the real effective-manifest builder. + 1.0.0 + agpl + Conduction + AcFx + tools + + diff --git a/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/planted/package-lock.json b/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/planted/package-lock.json new file mode 100644 index 000000000..71cf7c6d9 --- /dev/null +++ b/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/planted/package-lock.json @@ -0,0 +1,11 @@ +{ + "name": "gate107-fixture", + "lockfileVersion": 3, + "requires": true, + "packages": { + "node_modules/@conduction/nextcloud-vue": { + "version": "2.33.0", + "comment": "First generation carrying CnReportsPage (2026-08-31) AND the store page type, so a type:\"reports\" / type:\"store\" page in this fixture is one that would actually render." + } + } +} diff --git a/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/planted/src/manifest.json b/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/planted/src/manifest.json new file mode 100644 index 000000000..73ebc6702 --- /dev/null +++ b/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome-reports-footer/planted/src/manifest.json @@ -0,0 +1,177 @@ +{ + "$schema": "https://raw.githubusercontent.com/ConductionNL/nextcloud-vue/main/src/schemas/app-manifest-v2.schema.json", + "version": "2.0.0", + "nav": { + "includePersonalSettings": false, + "_note": "ANTI-WIDENING 4: suppressing the auto-prepended entry is LEGITIMATE when the app declares its own action:\"user-settings\" entry, which opens the same dialog. keepiq does exactly this and injects its own sections into CnAppRoot's #user-settings slot; re-enabling the shell copy would give it two entries onto one dialog. A gate that flagged the flag alone would red keepiq." + }, + "_note": "PLANTED arm: identical to the clean arm except that ReportsMenu sits in the footer, which was the rule until 2026-10-07. It must fail on that one placement and nothing else.", + "menu": [ + { + "id": "DashboardMenu", + "label": "Dashboard", + "icon": "ViewDashboardOutline", + "route": "Dashboard", + "order": 10 + }, + { + "id": "FlowRunsMenu", + "label": "Flow runs", + "icon": "PlayCircleOutline", + "route": "FlowRuns", + "order": 20, + "_note": "ANTI-WIDENING 1: an operational list whose LABEL contains 'flow' but which is an ordinary index, in main, and is not the flow authoring surface. A gate that matched Flows on the label rather than on the page type would accept this as the Flows entry in the planted arm and would flag its placement here. Detection is by page type for exactly this reason." + }, + { + "id": "DocsMenu", + "label": "Documentation", + "icon": "BookOpenVariantOutline", + "href": "https://example.org", + "order": 90, + "section": "footer" + }, + { + "id": "StoreMenu", + "label": "Store", + "icon": "StoreOutline", + "route": "Store", + "order": 92, + "section": "footer" + }, + { + "id": "ReportsMenu", + "label": "Reports", + "icon": "ChartBoxOutline", + "route": "Reports", + "order": 95, + "section": "footer", + "_note": "DEFECT: Reports in the footer. ADR-114 Decisions 1 and 3, amended 2026-10-07: Reports is one entry in the settings foldout, labelled Advanced." + }, + { + "id": "RoadmapMenu", + "label": "Features & roadmap", + "icon": "MapMarkerPath", + "route": "FeaturesRoadmap", + "order": 100, + "section": "footer" + }, + { + "id": "UserSettings", + "label": "Personal settings", + "icon": "CogOutline", + "action": "user-settings", + "section": "settings", + "order": 1 + }, + { + "id": "ConfigGroup", + "label": "Configuration", + "icon": "CogOutline", + "section": "settings", + "order": 50, + "children": [ + { + "id": "FlowsMenu", + "label": "Flows", + "icon": "Sitemap", + "route": "Flows", + "order": 10, + "section": "settings", + "_note": "ANTI-WIDENING 2: nested under a group rather than declared top-level. ADR-110's own fleet audit walked only top-level menu[] entries, read integriq's nested Flows as absent, and had to withdraw the claim inside the ADR. A gate that does not descend into children[] turns this clean arm red." + } + ] + } + ], + "pages": [ + { + "id": "Dashboard", + "route": "/", + "type": "dashboard", + "title": "Dashboard", + "config": { + "widgets": [] + } + }, + { + "id": "FlowRuns", + "route": "/flow-runs", + "type": "index", + "title": "Flow runs", + "config": { + "register": "acfx", + "schema": "case", + "columns": [ + "name" + ] + } + }, + { + "id": "Flows", + "route": "/flows", + "type": "index", + "title": "Flows", + "config": { + "entitySource": "flows", + "app": "acfx", + "rowRoute": "FlowDetail" + }, + "_note": "The CANONICAL flow index: type:\"index\" over the named `flows` entity source. nextcloud-vue marks the dedicated `flows` / `flow-detail` page types DEPRECATED in CnPageRenderer/pageTypes.js, kept only so the fleet can migrate without a flag day. The gate must recognise this shape, because it is the one new apps are supposed to write." + }, + { + "id": "FlowDetail", + "route": "/flows/:id", + "type": "flow", + "title": "Flow", + "config": { + "app": "acfx" + } + }, + { + "id": "Store", + "route": "/store", + "type": "custom", + "component": "StoreGallery", + "title": "Store", + "config": { + "storeRegistry": true + }, + "_note": "The shape dossiq ships today. `store` is NOT yet a page type in the manifest schema, so a fixture declaring one would red gate-22 while proving nothing about gate-107. When nextcloud-vue absorbs dossiq's StoreGallery as CnStorePage and the enum gains `store`, this becomes type:\"store\" and the checker already accepts it." + }, + { + "id": "Reports", + "route": "/reports", + "type": "reports", + "title": "Reports", + "config": { + "cards": [ + { + "id": "t", + "label": "Throughput", + "route": "Throughput" + } + ] + } + }, + { + "id": "Throughput", + "route": "/reports/throughput", + "type": "index", + "title": "Throughput", + "config": { + "register": "acfx", + "schema": "case", + "columns": [ + "name" + ] + } + }, + { + "id": "FeaturesRoadmap", + "route": "/features-roadmap", + "type": "custom", + "component": "FeaturesRoadmapIndex", + "title": "Features & roadmap", + "_note": "ANTI-WIDENING 3: the roadmap surface is type:\"custom\" at the canonical path, which is what openregister actually ships (\"the built-in roadmap page is not adopted in this shell-swap change\"), and what shillinq ships for reports. A gate that insisted on the page TYPE would call this MISSING and red an app that is right. Reverting isSurface() to a type-only test turns this clean arm red, which is the point: the regression was found on the live fleet before the gate merged, and this is what stops it coming back." + } + ] +} diff --git a/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome/clean/src/manifest.json b/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome/clean/src/manifest.json index 581f92073..010efc70f 100644 --- a/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome/clean/src/manifest.json +++ b/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome/clean/src/manifest.json @@ -44,7 +44,7 @@ "icon": "ChartBoxOutline", "route": "Reports", "order": 95, - "section": "footer" + "section": "settings" }, { "id": "RoadmapMenu", diff --git a/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome/expect.conf b/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome/expect.conf index 59efbf2a9..0b2012c2e 100644 --- a/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome/expect.conf +++ b/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome/expect.conf @@ -20,16 +20,19 @@ # Documentation outside `footer` -> Decision 1 (placement) # no Flows surface at all -> Decision 6 / ADR-110 D4 (presence) # a hand-rolled Admin settings link-> Decision 2 (the shell already draws it) -# Reports below Features & roadmap -> Decision 1 (footer sequence) +# Store below Features & roadmap -> Decision 1 (footer sequence) # Personal settings suppressed -> Decision 1 item 6 (reachability) # # The CLEAN arm applies the remedy the gate prescribes to all five: move # Documentation to `footer`, add the Flows page and its settings-foldout entry, -# delete the hand-rolled link, put Reports back above Features & roadmap, and +# delete the hand-rolled link, put Store back above Features & roadmap, and # give Personal settings a way to be reached. Fixing only one leaves the others # red, which is the point. # -# ⚠️ STORE AND REPORTS ARE PRESENT AND CORRECT IN **BOTH** ARMS, DELIBERATELY. +# ⚠️ STORE AND REPORTS ARE PRESENT IN **BOTH** ARMS, DELIBERATELY. Reports +# sits in the settings foldout in both (ADR-114 D1 and D3, amended 2026-10-07); +# the footer-to-settings direction is graded by its own bundle, +# app-chrome-reports-footer. # They sit on the ADR-114 Decision 8 ratchet and are WARN today. If the planted # arm were missing them, this bundle would keep passing for the wrong reason on # the day the ratchet promotes them to blocking: the arm would fail on Store @@ -53,8 +56,8 @@ # the live fleet before this gate merged; reverting isSurface() to a # type-only test turns this arm red. # * The footer-order rule is RELATIVE, not absolute. The planted arm's -# Reports is at 200 and fails; pipelinq ships 160 / 200 / 230 on the live -# fleet and passes. A gate hardcoding 90 / 92 / 95 / 100 would red four +# Store is at 200 and fails; pipelinq ships 160 / 200 / 230 on the live +# fleet and passes. A gate hardcoding 90 / 92 / 100 would red four # apps whose footers already read correctly. # * The clean arm keeps `includePersonalSettings: false` AND declares its own # `action: "user-settings"` entry, which is keepiq's shape: that flag is diff --git a/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome/planted/src/manifest.json b/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome/planted/src/manifest.json index a01d956e0..b93ae2700 100644 --- a/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome/planted/src/manifest.json +++ b/hydra-gates/scripts/test-fixtures/gate-acceptance/app-chrome/planted/src/manifest.json @@ -5,7 +5,7 @@ "includePersonalSettings": false, "_note": "DEFECT 5: Personal settings is suppressed and nothing replaces it, so the user's notification preferences and the ADR-110 Integrations section are reachable nowhere. CnAppRoot's #user-settings slot is NOT empty by default." }, - "_note": "PLANTED arm for gate-107 (ADR-114). Three blocking defects, one per direction of the rule: Documentation sits in main instead of footer (Decision 1), there is no Flows surface at all (Decision 6 / ADR-110 D4), and AdminSettingsLink hand-rolls the link CnAppNav already prepends (Decision 2). Store and Reports are PRESENT and correctly placed in both arms on purpose: they are on the D8 ratchet, so if they were absent here the arm would still fail for the three real reasons and the fixture would stop proving anything the day the ratchet promotes.", + "_note": "PLANTED arm for gate-107 (ADR-114). Three blocking defects, one per direction of the rule: Documentation sits in main instead of footer (Decision 1), there is no Flows surface at all (Decision 6 / ADR-110 D4), and AdminSettingsLink hand-rolls the link CnAppNav already prepends (Decision 2). Store and Reports are PRESENT in their right sections in both arms on purpose (Reports in the settings foldout since 2026-10-07; Store carries the footer-order defect): they are on the D8 ratchet, so if they were absent here the arm would still fail for the three real reasons and the fixture would stop proving anything the day the ratchet promotes.", "menu": [ { "id": "DashboardMenu", @@ -27,17 +27,17 @@ "label": "Store", "icon": "StoreOutline", "route": "Store", - "order": 92, - "section": "footer" + "order": 200, + "section": "footer", + "_note": "DEFECT 4: Store sits BELOW Features & roadmap, so the footer group reads in a different order from every other app. ADR-114 Decision 1 fixes the sequence: Documentation, Store, Features & roadmap. The RELATIVE order is the rule, which is why 200 fails here while pipelinq's 160/200/230 passes on the live fleet. Until 2026-10-07 this defect was planted on Reports; Reports has since left the footer for the settings foldout." }, { "id": "ReportsMenu", "label": "Reports", "icon": "ChartBoxOutline", "route": "Reports", - "order": 200, - "section": "footer", - "_note": "DEFECT 4: Reports sits BELOW Features & roadmap, so the footer group reads in a different order from every other app. ADR-114 Decision 1 fixes the sequence: Documentation, Store, Reports, Features & roadmap. The RELATIVE order is the rule, which is why 200 fails here while pipelinq's 160/200/230 passes on the live fleet." + "order": 95, + "section": "settings" }, { "id": "RoadmapMenu", diff --git a/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/clean/appinfo/info.xml b/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/clean/appinfo/info.xml new file mode 100644 index 000000000..2b3f74cb4 --- /dev/null +++ b/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/clean/appinfo/info.xml @@ -0,0 +1,13 @@ + + + ropfx + Reports One Page Fixture + Fixture app for gate-70 acceptance. Not a real app. + Exists so the gate suite can drive a tour that does and does not point at a flows page through the real wrapper. + 1.0.0 + agpl + Conduction + WfsFx + tools + + diff --git a/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/clean/package-lock.json b/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/clean/package-lock.json new file mode 100644 index 000000000..12a8cb9c1 --- /dev/null +++ b/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/clean/package-lock.json @@ -0,0 +1,11 @@ +{ + "name": "gate70-clean", + "lockfileVersion": 3, + "requires": true, + "packages": { + "node_modules/@conduction/nextcloud-vue": { + "version": "2.21.0", + "comment": "First release whose CnAppNav emits data-cn-route on the SETTINGS loop (#811), so a stop targeting a settings entry can actually anchor." + } + } +} diff --git a/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/clean/src/manifest.json b/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/clean/src/manifest.json new file mode 100644 index 000000000..106a499cf --- /dev/null +++ b/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/clean/src/manifest.json @@ -0,0 +1,99 @@ +{ + "$schema": "https://raw.githubusercontent.com/ConductionNL/nextcloud-vue/main/src/schemas/app-manifest-v2.schema.json", + "version": "2.0.0", + "_note": "CLEAN arm: the same app with ReportsMenu in section:\"settings\", the foldout labelled Advanced (amended 2026-10-07). Everything else matches the planted arm.", + "menu": [ + { + "id": "DashboardMenu", + "label": "Dashboard", + "icon": "ViewDashboardOutline", + "route": "Dashboard", + "order": 10 + }, + { + "id": "ReportsMenu", + "label": "Reports", + "icon": "ChartBoxOutline", + "route": "Reports", + "order": 95, + "section": "settings" + }, + { + "id": "DocsMenu", + "label": "Documentation", + "icon": "BookOpenOutline", + "href": "https://example.org", + "order": 90, + "section": "footer" + }, + { + "id": "RoadmapMenu", + "label": "Features & roadmap", + "icon": "MapOutline", + "route": "FeaturesRoadmap", + "order": 100, + "section": "footer" + } + ], + "pages": [ + { + "id": "Dashboard", + "route": "/", + "type": "dashboard", + "title": "Dashboard", + "config": { + "widgets": [] + } + }, + { + "id": "Throughput", + "route": "/reports/throughput", + "type": "index", + "title": "Processing time", + "config": { + "register": "ropfx", + "schema": "case", + "columns": [ + "name" + ] + } + }, + { + "id": "ThingDetail", + "route": "/things/:id", + "type": "detail", + "title": "Thing", + "_note": "ANTI-WIDENING: not a report and named by no card. A gate widened to 'every page needs a card' turns the clean arm red on this.", + "config": { + "register": "ropfx", + "schema": "case", + "fields": [] + } + }, + { + "id": "FeaturesRoadmap", + "route": "/features-roadmap", + "type": "roadmap", + "title": "Features & roadmap" + }, + { + "id": "Reports", + "route": "/reports", + "type": "reports", + "title": "Reports", + "config": { + "categories": { + "throughput": "Throughput" + }, + "cards": [ + { + "id": "t", + "label": "Processing time", + "category": "throughput", + "route": "Throughput" + } + ] + } + } + ] +} diff --git a/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/expect.conf b/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/expect.conf new file mode 100644 index 000000000..bded0ced4 --- /dev/null +++ b/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/expect.conf @@ -0,0 +1,18 @@ +# expect.conf — read by scripts/lib/test_gate_acceptance_matrix.sh +# +# Columns: +# gate +# +# Bundle: reports-one-page-footer — gate-104 (ADR-112 Decision 3, amended +# 2026-10-07). +# +# Ruben decided on 2026-10-07 that the settings foldout is labelled Advanced +# and that Reports lives in it, in every app. Until then ADR-112 Decision 3 +# put Reports in the footer, so every app that followed the old rule now has +# exactly this planted shape. +# +# The two arms differ in ONE field: ReportsMenu's section. The planted arm has +# it in `footer` and must fail naming the footer; the clean arm has it in +# `settings` and must pass. The reports-one-page bundle covers the main-menu +# direction of the same rule. +gate 104 hydra-gate-reports-one-page.log FAIL PASS not "settings", so it renders in the footer diff --git a/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/planted/appinfo/info.xml b/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/planted/appinfo/info.xml new file mode 100644 index 000000000..2b3f74cb4 --- /dev/null +++ b/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/planted/appinfo/info.xml @@ -0,0 +1,13 @@ + + + ropfx + Reports One Page Fixture + Fixture app for gate-70 acceptance. Not a real app. + Exists so the gate suite can drive a tour that does and does not point at a flows page through the real wrapper. + 1.0.0 + agpl + Conduction + WfsFx + tools + + diff --git a/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/planted/package-lock.json b/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/planted/package-lock.json new file mode 100644 index 000000000..12a8cb9c1 --- /dev/null +++ b/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/planted/package-lock.json @@ -0,0 +1,11 @@ +{ + "name": "gate70-clean", + "lockfileVersion": 3, + "requires": true, + "packages": { + "node_modules/@conduction/nextcloud-vue": { + "version": "2.21.0", + "comment": "First release whose CnAppNav emits data-cn-route on the SETTINGS loop (#811), so a stop targeting a settings entry can actually anchor." + } + } +} diff --git a/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/planted/src/manifest.json b/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/planted/src/manifest.json new file mode 100644 index 000000000..18e0af630 --- /dev/null +++ b/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page-footer/planted/src/manifest.json @@ -0,0 +1,100 @@ +{ + "$schema": "https://raw.githubusercontent.com/ConductionNL/nextcloud-vue/main/src/schemas/app-manifest-v2.schema.json", + "version": "2.0.0", + "_note": "PLANTED arm: identical to the clean arm except that ReportsMenu sits in the footer, which was the rule until 2026-10-07. It must fail on that one placement and nothing else.", + "menu": [ + { + "id": "DashboardMenu", + "label": "Dashboard", + "icon": "ViewDashboardOutline", + "route": "Dashboard", + "order": 10 + }, + { + "id": "ReportsMenu", + "label": "Reports", + "icon": "ChartBoxOutline", + "route": "Reports", + "order": 95, + "section": "footer", + "_note": "DEFECT: Reports in the footer. ADR-112 Decision 3, amended 2026-10-07: Reports is one entry in the settings foldout, labelled Advanced." + }, + { + "id": "DocsMenu", + "label": "Documentation", + "icon": "BookOpenOutline", + "href": "https://example.org", + "order": 90, + "section": "footer" + }, + { + "id": "RoadmapMenu", + "label": "Features & roadmap", + "icon": "MapOutline", + "route": "FeaturesRoadmap", + "order": 100, + "section": "footer" + } + ], + "pages": [ + { + "id": "Dashboard", + "route": "/", + "type": "dashboard", + "title": "Dashboard", + "config": { + "widgets": [] + } + }, + { + "id": "Throughput", + "route": "/reports/throughput", + "type": "index", + "title": "Processing time", + "config": { + "register": "ropfx", + "schema": "case", + "columns": [ + "name" + ] + } + }, + { + "id": "ThingDetail", + "route": "/things/:id", + "type": "detail", + "title": "Thing", + "_note": "ANTI-WIDENING: not a report and named by no card. A gate widened to 'every page needs a card' turns the clean arm red on this.", + "config": { + "register": "ropfx", + "schema": "case", + "fields": [] + } + }, + { + "id": "FeaturesRoadmap", + "route": "/features-roadmap", + "type": "roadmap", + "title": "Features & roadmap" + }, + { + "id": "Reports", + "route": "/reports", + "type": "reports", + "title": "Reports", + "config": { + "categories": { + "throughput": "Throughput" + }, + "cards": [ + { + "id": "t", + "label": "Processing time", + "category": "throughput", + "route": "Throughput" + } + ] + } + } + ] +} diff --git a/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page/clean/src/manifest.json b/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page/clean/src/manifest.json index eb1acbecc..bb5998a93 100644 --- a/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page/clean/src/manifest.json +++ b/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page/clean/src/manifest.json @@ -1,7 +1,7 @@ { "$schema": "https://raw.githubusercontent.com/ConductionNL/nextcloud-vue/main/src/schemas/app-manifest-v2.schema.json", "version": "2.0.0", - "_note": "CLEAN arm for gate-104 (ADR-112). The remedy the gate prescribes, applied to all three findings: one type:\"reports\" page, its entry in the footer between Documentation and Features & roadmap, and the carded report's own menu entry retired. ThingDetail stays as the anti-widening control: it is not a report and no card names it, and it must not fire.", + "_note": "CLEAN arm for gate-104 (ADR-112). The remedy the gate prescribes, applied to all three findings: one type:\"reports\" page, its entry in the settings foldout labelled Advanced (ADR-112 Decision 3, amended 2026-10-07), and the carded report's own menu entry retired. ThingDetail stays as the anti-widening control: it is not a report and no card names it, and it must not fire.", "menu": [ { "id": "DashboardMenu", @@ -16,7 +16,7 @@ "icon": "ChartBoxOutline", "route": "Reports", "order": 95, - "section": "footer" + "section": "settings" }, { "id": "DocsMenu", diff --git a/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page/expect.conf b/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page/expect.conf index 59fdd6bd0..2b3fb548f 100644 --- a/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page/expect.conf +++ b/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page/expect.conf @@ -23,13 +23,14 @@ # regression that leaves the other two findings standing. # # two type:"reports" pages -> ADR-112 Decision 1 -# the Reports entry outside footer -> ADR-112 Decision 3 +# the Reports entry outside settings -> ADR-112 Decision 3 (amended 2026-10-07) # a report carded AND in the menu -> ADR-112 Decision 2 # # The CLEAN arm applies the remedy the gate prescribes to all three: drop the -# second reports page, move the entry to `section: "footer"` at order 95 (so it -# renders between Documentation at 90 and Features & roadmap at 100), and -# retire the carded report's own menu entry. Fixing only one leaves the others +# second reports page, move the entry to `section: "settings"` (the foldout +# labelled Advanced; it was `footer` until 2026-10-07), and retire the carded +# report's own menu entry. The footer-to-settings direction has its own +# bundle, reports-one-page-footer. Fixing only one leaves the others # red, which is the point. # # ANTI-WIDENING, both arms: @@ -40,5 +41,5 @@ # APPLICABLE over this bundle is a FAILURE, never an excuse: both arms are # built to arm the gate. gate 104 hydra-gate-reports-one-page.log FAIL PASS ADR-112 Decision 1 allows one -gate 104 hydra-gate-reports-one-page.log FAIL PASS not "footer" +gate 104 hydra-gate-reports-one-page.log FAIL PASS not "settings", so it renders in the main menu gate 104 hydra-gate-reports-one-page.log FAIL PASS already lists as a card diff --git a/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page/planted/src/manifest.json b/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page/planted/src/manifest.json index c26f62dde..d973bcf4f 100644 --- a/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page/planted/src/manifest.json +++ b/hydra-gates/scripts/test-fixtures/gate-acceptance/reports-one-page/planted/src/manifest.json @@ -1,7 +1,7 @@ { "$schema": "https://raw.githubusercontent.com/ConductionNL/nextcloud-vue/main/src/schemas/app-manifest-v2.schema.json", "version": "2.0.0", - "_note": "PLANTED arm for gate-104 (ADR-112). Breaks all three rules at once: two type:\"reports\" pages, the Reports entry outside the footer, and a report that is reachable BOTH as a card and as its own menu entry.", + "_note": "PLANTED arm for gate-104 (ADR-112). Breaks all three rules at once: two type:\"reports\" pages, the Reports entry in the main menu instead of the settings foldout, and a report that is reachable BOTH as a card and as its own menu entry.", "menu": [ { "id": "DashboardMenu", "label": "Dashboard", "icon": "ViewDashboardOutline", "route": "Dashboard", "order": 10 }, { "id": "ThroughputMenu", "label": "Processing time", "icon": "ChartLine", "route": "Throughput", "order": 50 }, diff --git a/hydra-gates/scripts/test-fixtures/reports-one-page/duplicate-entry/src/manifest.json b/hydra-gates/scripts/test-fixtures/reports-one-page/duplicate-entry/src/manifest.json index cfb26d21e..db1727c71 100644 --- a/hydra-gates/scripts/test-fixtures/reports-one-page/duplicate-entry/src/manifest.json +++ b/hydra-gates/scripts/test-fixtures/reports-one-page/duplicate-entry/src/manifest.json @@ -20,7 +20,7 @@ "label": "Reports", "route": "Reports", "order": 95, - "section": "footer" + "section": "settings" }, { "id": "RoadmapMenu", diff --git a/hydra-gates/scripts/test-fixtures/reports-one-page/good/src/manifest.json b/hydra-gates/scripts/test-fixtures/reports-one-page/good/src/manifest.json index 26410adcd..44a957283 100644 --- a/hydra-gates/scripts/test-fixtures/reports-one-page/good/src/manifest.json +++ b/hydra-gates/scripts/test-fixtures/reports-one-page/good/src/manifest.json @@ -4,7 +4,7 @@ "menu": [ { "id": "DashboardMenu", "label": "Dashboard", "route": "Dashboard", "order": 10 }, { "id": "DocsMenu", "label": "Documentation", "href": "https://example.org", "order": 90, "section": "footer" }, - { "id": "ReportsMenu", "label": "Reports", "route": "Reports", "order": 95, "section": "footer" }, + { "id": "ReportsMenu", "label": "Reports", "route": "Reports", "order": 95, "section": "settings" }, { "id": "RoadmapMenu", "label": "Features & roadmap", "route": "FeaturesRoadmap", "order": 100, "section": "footer" } ], "pages": [ diff --git a/hydra-gates/scripts/test-fixtures/reports-one-page/two-pages/src/manifest.json b/hydra-gates/scripts/test-fixtures/reports-one-page/two-pages/src/manifest.json index e307c2381..0d45b2bc3 100644 --- a/hydra-gates/scripts/test-fixtures/reports-one-page/two-pages/src/manifest.json +++ b/hydra-gates/scripts/test-fixtures/reports-one-page/two-pages/src/manifest.json @@ -20,7 +20,7 @@ "label": "Reports", "route": "Reports", "order": 95, - "section": "footer" + "section": "settings" }, { "id": "RoadmapMenu",