docs(agents): encode list-export convention as GR-UI-EXPORT guard
Make the core-primitive-end-to-end rule for Grid.js list exports binding instead of advisory — so future consumers (and reviewers) cannot quietly drift back into inline fputcsv / hand-rolled headers / bespoke click listeners / lurl() for query-carrying URLs. - CLAUDE.md: new UI Patterns bullet + two "Never Do This" entries spelling out the server/view/client contract in one place. - .agents/checks/guard-catalog.json: new GR-UI-EXPORT entry describing the end-to-end requirement (exportRequireGetRequest + exportCapLimit + exportSendCsv + ExportColumn + shared filter-schema + dropdown partial + endpointUrl() + initListExport). - .agents/checks/guard-enforcement-map.json: wire the new guard to tests/Architecture/ListExportContractTest.php as the automated evidence source. - .agents/checks/guard-checklist.md & .agents/prompts/reviewer-code.md: add GR-UI-EXPORT so the Code Reviewer prompt and the checklist flag violations alongside GR-UI-LIST. - tests/Architecture/ListExportContractFiles: registry of every list export (currently helpdesk-domains, admin/users, audit/system-audit). Adding a new list = one entry, contract test covers the rest. - tests/Architecture/ListExportContractTest: six checks per registered entry — endpoint uses the primitive, no hand-rolled output, data and export share the same filter-schema, template includes the dropdown partial and uses endpointUrl(), page module imports and invokes initListExport, and the shared dropdown partial stays zero-config. - pages/admin/users/export().php: align with the new contract by switching from ad-hoc requestInput()->queryAll() reads to gridParseFiltersFromSchemaFile(__DIR__ . '/filter-schema.php'), same as the data endpoint — eliminates the last place Grid and export could disagree on what "the current filter" means. Gates: PHPUnit 1900 OK (+6 contract checks), PHPStan 0 errors. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -92,6 +92,13 @@
|
||||
"title": "Reuse-first (JS + CSS)",
|
||||
"requirement": "MUST check web/js/core/ and web/js/components/ for existing exports before adding new functions. MUST check existing CSS layers and app-* classes before writing new rules. No duplication of logic from core modules."
|
||||
},
|
||||
{
|
||||
"id": "GR-UI-EXPORT",
|
||||
"area": "ui",
|
||||
"reviewer": "code",
|
||||
"title": "List export via core primitive",
|
||||
"requirement": "Grid.js list pages that offer CSV/Excel downloads MUST use the core export primitive end-to-end. Server endpoints MUST call exportRequireGetRequest(), cap rows via exportCapLimit(), stream with exportSendCsv() from core/Support/helpers/export.php, and declare columns as MintyPHP\\Service\\Export\\ExportColumn. The SAME filter-schema.php as the list's data endpoint MUST be parsed via gridParseFiltersFromSchemaFile() so exported rows match the grid under identical filters. Views MUST include templates/partials/app-list-export-dropdown.phtml in $listTitleActionsHtml and pass 'exportUrl' => endpointUrl('<route-path>') in the page config (never plain lurl() — query-string survival on module routes where path != target depends on endpointUrl). Page JS MUST call initListExport({ gridConfig, exportUrl }) from /js/core/app-list-export.js. No inline fputcsv, no hand-rolled headers, no bespoke click listener."
|
||||
},
|
||||
{
|
||||
"id": "GR-SEC-001",
|
||||
"area": "security",
|
||||
|
||||
@@ -17,6 +17,7 @@ Guard IDs live in `.agents/checks/guard-catalog.json` (guards where `reviewer` =
|
||||
## UI (skip if backend-only)
|
||||
- `GR-UI-014` new views/components handle loading, empty, error, success states
|
||||
- `GR-UI-LIST` list/grid standards preserved (row actions, page size, initStandardListPage)
|
||||
- `GR-UI-EXPORT` list exports go through the core primitive: server uses exportRequireGetRequest + exportCapLimit + exportSendCsv + ExportColumn, shares filter-schema.php with the data endpoint; view uses `app-list-export-dropdown.phtml` partial and `endpointUrl()` (never `lurl()`) for the exportUrl; JS calls `initListExport` from `/js/core/app-list-export.js`. Add the new list to `tests/Architecture/ListExportContractFiles.php`.
|
||||
- `GR-UI-DETAIL` detail page contracts, shared partials, no inline confirm, layout system
|
||||
- `GR-UI-A11Y` no a11y regression; new components use semantic HTML, keyboard-operable
|
||||
- `GR-UI-I18N` all visible text uses t(); keys present in all language files
|
||||
|
||||
@@ -127,6 +127,13 @@
|
||||
".agents/runs/<TASK>/review-code.json"
|
||||
]
|
||||
},
|
||||
{
|
||||
"guard_id": "GR-UI-EXPORT",
|
||||
"enforcement_mode": "automated",
|
||||
"evidence_source": [
|
||||
"tests/Architecture/ListExportContractTest.php"
|
||||
]
|
||||
},
|
||||
{
|
||||
"guard_id": "GR-SEC-001",
|
||||
"enforcement_mode": "automated",
|
||||
|
||||
@@ -25,6 +25,7 @@ Scope — verify these guards:
|
||||
- GR-TEST-002: code is testable
|
||||
- GR-UI-014: UI state completeness (if UI touched)
|
||||
- GR-UI-LIST: list/grid standards (if lists touched)
|
||||
- GR-UI-EXPORT: list export via core primitive (if list exports touched)
|
||||
- GR-UI-DETAIL: detail page standards (if detail pages touched)
|
||||
- GR-UI-A11Y: accessibility (if UI touched)
|
||||
- GR-UI-I18N: i18n completeness (if visible text touched)
|
||||
|
||||
Reference in New Issue
Block a user