diff --git a/agent-system/README.md b/agent-system/README.md
new file mode 100644
index 0000000..7ad0985
--- /dev/null
+++ b/agent-system/README.md
@@ -0,0 +1,14 @@
+# Agent System
+
+This folder contains the technical assets for the multi-role workflow.
+
+Structure:
+- `contracts/`: JSON schemas for role outputs
+- `templates/`: starter JSON payloads
+- `prompts/`: role-specific prompt baselines
+- `checks/`: guard catalog, quality gates, and review checklists
+- `runs/`: optional runtime artifacts (can stay untracked)
+
+References:
+- Planner standard: `tools/codex-skills/starterkit-planner/SKILL.md`
+- Guardrails: `tools/codex-skills/core-guardrails/SKILL.md`
diff --git a/agent-system/checks/acceptance-checklist.md b/agent-system/checks/acceptance-checklist.md
new file mode 100644
index 0000000..3dc64a7
--- /dev/null
+++ b/agent-system/checks/acceptance-checklist.md
@@ -0,0 +1,25 @@
+# Acceptance Checklist
+
+Use this checklist for the acceptance reviewer.
+
+## Scope
+- all in-scope items from `plan.json` are implemented
+- out-of-scope items are not silently introduced
+
+## Behavior
+- each `SC-*` success criterion from `plan.json` is verifiably met
+- negative and edge paths behave as defined
+
+## Regressions
+- existing flows still work after change
+- no critical UX break on key user paths
+
+## Tests
+- every test listed in `plan.json tests[]` is implemented and passes (QG-001 green)
+- new Service/Gateway logic has at least one test covering the happy path and one failure/edge case (GR-TEST-001)
+- no new untestable code patterns introduced (GR-TEST-002)
+
+## Evidence
+- each criterion has explicit pass/fail evidence
+- each criterion check references the exact `SC-*` ID
+- fail findings include exact missing behavior
diff --git a/agent-system/checks/guard-catalog.json b/agent-system/checks/guard-catalog.json
new file mode 100644
index 0000000..24d99d3
--- /dev/null
+++ b/agent-system/checks/guard-catalog.json
@@ -0,0 +1,272 @@
+{
+ "version": "1.0.0",
+ "source_skill": "tools/codex-skills/core-guardrails/SKILL.md",
+ "guards": [
+ {
+ "id": "GR-CORE-001",
+ "area": "core",
+ "title": "Layering boundaries",
+ "requirement": "MUST keep pages orchestration-only and business logic in services.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
+ },
+ {
+ "id": "GR-CORE-002",
+ "area": "core",
+ "title": "No direct service instantiation in pages",
+ "requirement": "MUST NOT instantiate Service, Gateway or Repository directly in pages.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
+ },
+ {
+ "id": "GR-CORE-003",
+ "area": "core",
+ "title": "Request input contract",
+ "requirement": "MUST read input via requestInput in pages and avoid superglobals for request data.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
+ },
+ {
+ "id": "GR-CORE-004",
+ "area": "core",
+ "title": "API json body contract",
+ "requirement": "MUST NOT use ApiResponse::readJsonBody in pages/api/v1.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
+ },
+ {
+ "id": "GR-CORE-005",
+ "area": "security",
+ "title": "Server-side authz",
+ "requirement": "MUST enforce authorization server-side.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
+ },
+ {
+ "id": "GR-CORE-006",
+ "area": "security",
+ "title": "Server-side tenant scope",
+ "requirement": "MUST enforce tenant scope server-side.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
+ },
+ {
+ "id": "GR-CORE-007",
+ "area": "api",
+ "title": "OpenAPI sync",
+ "requirement": "MUST update OpenAPI in same merge when API changes.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
+ },
+ {
+ "id": "GR-UI-001",
+ "area": "ui",
+ "title": "List initialization standard",
+ "requirement": "MUST initialize lists via initStandardListPage.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
+ },
+ {
+ "id": "GR-UI-002",
+ "area": "ui",
+ "title": "No legacy filter toggle markup",
+ "requirement": "MUST NOT introduce legacy filter toggle attributes.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
+ },
+ {
+ "id": "GR-UI-003",
+ "area": "ui",
+ "title": "Row interaction standard",
+ "requirement": "MUST preserve row action column plus enter and dblclick fallback behavior.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
+ },
+ {
+ "id": "GR-UI-004",
+ "area": "ui",
+ "title": "Page size standard",
+ "requirement": "MUST preserve page size options and URL/localStorage/default precedence.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
+ },
+ {
+ "id": "GR-UI-005",
+ "area": "ui",
+ "title": "Detail page standard",
+ "requirement": "MUST use standard detail page contracts for dirty-state and actions.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
+ },
+ {
+ "id": "GR-UI-006",
+ "area": "ui",
+ "title": "No inline confirm",
+ "requirement": "MUST NOT use inline confirm handlers on standardized detail flows.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
+ },
+ {
+ "id": "GR-UI-007",
+ "area": "ui",
+ "title": "Shared partial usage",
+ "requirement": "MUST use shared list and detail partials for standardized UI blocks.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
+ },
+ {
+ "id": "GR-UI-016",
+ "area": "ui",
+ "title": "Template structure discipline for new features",
+ "requirement": "New pages MUST use the established layout system (templates/default.phtml or a named layout variant). UI blocks that appear in two or more pages MUST be extracted as a partial under templates/partials/ rather than duplicated inline. New partials MUST follow the naming convention app-{context}-{purpose}.phtml. Page-specific rendering belongs in pages/*.phtml, not in templates/. MUST NOT create ad-hoc layout HTML inside pages that bypasses the layout system.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
+ },
+ {
+ "id": "GR-UI-008",
+ "area": "ui",
+ "title": "Template RBAC contract",
+ "requirement": "MUST use viewAuth in templates and avoid can calls.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
+ },
+ {
+ "id": "GR-UI-009",
+ "area": "ui",
+ "title": "No a11y regression",
+ "requirement": "MUST NOT introduce regressions in accessibility basics (labels, roles, keyboard focus) on touched flows.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
+ },
+ {
+ "id": "GR-CORE-008",
+ "area": "core",
+ "title": "No direct superglobal access in pages",
+ "requirement": "MUST NOT use $_SESSION, $_SERVER, $_COOKIE, $_ENV directly in pages; use framework abstractions (app(SessionStoreInterface::class), RequestContext, requestInput()) instead.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
+ },
+ {
+ "id": "GR-SEC-001",
+ "area": "security",
+ "title": "CSRF verification on POST endpoints",
+ "requirement": "MUST verify CSRF token on every POST endpoint in pages before processing the request body.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
+ },
+ {
+ "id": "GR-SEC-002",
+ "area": "security",
+ "title": "No unredacted PII or secrets in logs and audit metadata",
+ "requirement": "MUST NOT write PII (email, names, UUIDs) or secrets (tokens, passwords) to logs or audit metadata without prior redaction.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
+ },
+ {
+ "id": "GR-SEC-003",
+ "area": "security",
+ "title": "SQL via Repository with prepared statements only",
+ "requirement": "MUST NOT build SQL strings with user-controlled input; all queries MUST go through Repository classes using prepared statements.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
+ },
+ {
+ "id": "GR-LANG-001",
+ "area": "lang",
+ "title": "PSR-4 namespace alignment",
+ "requirement": "MUST align PHP namespace with directory path (MintyPHP\\ → lib/, MintyPHP\\Tests\\ → tests/); violations are caught by QG-003.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
+ },
+ {
+ "id": "GR-LANG-002",
+ "area": "lang",
+ "title": "PHP style compliance",
+ "requirement": "MUST pass composer cs:check without diff (php-cs-fixer dry-run); fix with composer cs:fix before merge.",
+ "source": "tools/codex-skills/starterkit-php-style-ci/SKILL.md"
+ },
+ {
+ "id": "GR-TEST-001",
+ "area": "test",
+ "title": "Tests ship with new logic",
+ "requirement": "New or changed business logic in lib/Service/ MUST be accompanied by PHPUnit tests in the same merge covering the happy path and at least one failure or edge case. No new public method in a Service or Gateway without a test.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
+ },
+ {
+ "id": "GR-TEST-002",
+ "area": "test",
+ "title": "Code must be unit-testable",
+ "requirement": "New code MUST use constructor injection for all dependencies. MUST NOT introduce static side-effects in constructors, hidden global state, or direct DB/HTTP calls outside Repository/Gateway abstractions — patterns that make unit testing without mocks impossible.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
+ },
+ {
+ "id": "GR-CORE-010",
+ "area": "core",
+ "title": "DB schema changes for existing installs must be idempotent SQL scripts in db/updates/",
+ "requirement": "Any schema or data change for existing installations MUST be shipped as an idempotent SQL script in db/updates/ (e.g. using IF NOT EXISTS, ON DUPLICATE KEY, or guarded ALTER TABLE). MUST NOT modify db/init/init.sql only — existing containers will not re-run init.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
+ },
+ {
+ "id": "GR-CORE-011",
+ "area": "core",
+ "title": "New settings belong in the DB settings table, not in config files",
+ "requirement": "New application settings MUST be stored in the settings DB table as the single source of truth. config/settings.php is a partial hot-path cache only and MUST NOT be used as primary storage for new settings. Security, integration, and feature-flag settings MUST go through the settings gateway layer.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
+ },
+ {
+ "id": "GR-CORE-012",
+ "area": "core",
+ "title": "POST-Redirect-GET after successful mutating actions",
+ "requirement": "Actions that successfully mutate state (create, update, delete) MUST respond with a redirect (PRG pattern) rather than rendering a response directly. This prevents double-submit on browser reload. Flash messages MUST be set before the redirect via Flash::set().",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
+ },
+ {
+ "id": "GR-CORE-009",
+ "area": "core",
+ "title": "Repository list queries must use RepoQuery::sanitizeLimitOffset",
+ "requirement": "New Repository methods that return lists MUST use RepoQuery::sanitizeLimitOffset() to enforce LIMIT and OFFSET. Unbounded SELECT queries without LIMIT on potentially large tables are forbidden. The default maxLimit of 100 MUST NOT be raised without explicit justification reviewed and approved.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
+ },
+ {
+ "id": "GR-SEC-006",
+ "area": "security",
+ "title": "User-uploaded files stored in storage/, never in web/",
+ "requirement": "Files uploaded by users MUST be stored under storage/ (e.g. storage/users/{uuid}/, storage/tenants/{uuid}/, storage/imports/) and MUST NOT be written to web/ or any other publicly served directory. Storage paths MUST use non-guessable identifiers (UUID v4 or equivalent) to prevent enumeration. Files MUST be served through a PHP controller that enforces authorization, not directly by the web server. MUST validate file type (MIME + extension allowlist) before storing.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
+ },
+ {
+ "id": "GR-SEC-005",
+ "area": "security",
+ "title": "Encryption only via lib/Support/Crypto.php",
+ "requirement": "Any encryption or decryption of sensitive data MUST use the Crypto class (lib/Support/Crypto.php) which provides AES-256-GCM via APP_CRYPTO_KEY. MUST NOT use raw openssl_encrypt/openssl_decrypt, base64 encoding as a substitute for encryption, or any other hand-rolled crypto primitives.",
+ "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
+ },
+ {
+ "id": "GR-SEC-004",
+ "area": "security",
+ "title": "No external CDN or remote asset loading",
+ "requirement": "MUST NOT load JS, CSS, fonts, or any other assets from external CDNs (Cloudflare, jsDelivr, unpkg, Google Fonts, etc.) or remote URLs in templates, pages, or layout files. If a third-party library is required it MUST be vendored locally in the repository (e.g. web/js/vendor/ or web/css/vendor/) and served via assetVersion(). No
@@ -53,6 +75,7 @@ if ($bufferTitle !== '') {
+