From 1bb341d69346bf2a2fc004bfe891e09d66c2707e Mon Sep 17 00:00:00 2001 From: Martin Castro Laminrs Date: Thu, 28 May 2026 15:05:58 +0200 Subject: [PATCH] =?UTF-8?q?chore:=20prune=20docs/=20bloat=20=E2=80=94=20ke?= =?UTF-8?q?ep=20only=20screenshots;=20fix=20cross-link=20rot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Owner directive: minimal docs, focused on shipping. After #550 collapsed this repo to the React-super-layer, most of `docs/` either described the local API (now gone, in `django-admin-rest-api`) or agent governance (sole-agent mode now). ## Deleted (47 files) - `docs/agents/` — agent governance, autonomy policy, decisions log, PR workflow, per-role subdirs. None of it applies in sole-agent mode. - `docs/api-contract.md` — wire contract for `django-admin-rest-api`; belongs in that repo, not duplicated here. - `docs/extensions.md` — extension API doc that was already marked "not yet implemented" + the wiring lives in the API repo. - `docs/threat-model.md` — STRIDE threat model. SECURITY.md is the durable security document; per-endpoint threat surface is now an API-repo concern. - `docs/data-layer.md` — frontend data-layer internals; SPA-only and the code is its own documentation. - `docs/ux/` — UX principles / nav / responsive / a11y / PWA. Was the PM agent's internal doc — sole-agent mode has no PM agent. - `docs/consumer/` — pilot requirements; internal-only. ## Kept - `docs/screenshots/` (6 PNGs + README) — referenced from `README.md` for the PyPI landing page. - `docs/README.md` — trimmed to a 1-paragraph pointer at the screenshots + the root docs. ## Cross-link rot fixed - `README.md`: dropped the "see `docs/extensions.md`" ref (API-repo concern) and "Full wire contract: `docs/api-contract.md`" (the API repo owns it). - `ARCHITECTURE.md`: dropped 3 references to deleted docs/ files (decisions log, M2M ADR pointer, `installation.md`). - `SECURITY.md`: surgically removed every ref to `docs/agents/`, `docs/threat-model.md`, `tests/test_security.py` (deleted with the API in #550), and the now-obsolete CI-revisit prose. The §10 cross-references now point at README + ARCHITECTURE + the API repo's SECURITY. ## Verification - 42 SPA tests pass (unchanged from #550). - No remaining dead `docs/*` references in any of the 4 surviving root `.md` files. Co-Authored-By: Claude Opus 4.7 (1M context) --- ARCHITECTURE.md | 8 +- README.md | 10 +- SECURITY.md | 68 +- docs/README.md | 60 +- docs/agents/README.md | 95 -- docs/agents/autonomy-policy.md | 228 ---- docs/agents/consumer/AGENT.md | 162 --- docs/agents/consumer/DECISIONS.md | 67 - docs/agents/consumer/OPEN_QUESTIONS.md | 33 - docs/agents/consumer/README.md | 15 - docs/agents/consumer/SKILLS.md | 134 -- docs/agents/decisions.md | 267 ---- docs/agents/open-questions.md | 177 --- docs/agents/pr-workflow.md | 307 ----- docs/agents/product-manager/AGENT.md | 247 ---- docs/agents/product-manager/DECISIONS.md | 75 - docs/agents/product-manager/NEXT_STEPS.md | 56 - docs/agents/product-manager/OPEN_QUESTIONS.md | 94 -- docs/agents/product-manager/README.md | 17 - .../product-manager/REVIEW_CHECKLIST.md | 153 --- docs/agents/product-manager/SKILLS.md | 132 -- docs/agents/product-manager/STATUS.md | 87 -- docs/agents/security-expert/AGENT.md | 287 ---- docs/agents/security-expert/DECISIONS.md | 52 - docs/agents/security-expert/OPEN_QUESTIONS.md | 100 -- docs/agents/security-expert/README.md | 17 - .../security-expert/REVIEW_CHECKLIST.md | 213 --- docs/agents/security-expert/SKILLS.md | 122 -- docs/agents/software-architect/AGENT.md | 205 --- docs/agents/software-architect/DECISIONS.md | 71 - docs/agents/software-architect/NEXT_STEPS.md | 109 -- .../software-architect/OPEN_QUESTIONS.md | 73 - docs/agents/software-architect/README.md | 21 - docs/agents/software-architect/SKILLS.md | 118 -- docs/agents/software-architect/STATUS.md | 89 -- docs/api-contract.md | 1201 ----------------- docs/consumer/README.md | 47 - .../consumer/requirements-pilot-2026-05-26.md | 199 --- docs/data-layer.md | 237 ---- docs/extensions.md | 236 ---- docs/threat-model.md | 358 ----- docs/ux/README.md | 37 - docs/ux/accessibility.md | 106 -- docs/ux/extensibility.md | 703 ---------- docs/ux/navigation.md | 154 --- docs/ux/primary-flows.md | 250 ---- docs/ux/principles.md | 163 --- docs/ux/pwa.md | 205 --- docs/ux/responsive.md | 238 ---- docs/ux/states.md | 267 ---- docs/ux/theming.md | 150 -- 51 files changed, 46 insertions(+), 8474 deletions(-) delete mode 100644 docs/agents/README.md delete mode 100644 docs/agents/autonomy-policy.md delete mode 100644 docs/agents/consumer/AGENT.md delete mode 100644 docs/agents/consumer/DECISIONS.md delete mode 100644 docs/agents/consumer/OPEN_QUESTIONS.md delete mode 100644 docs/agents/consumer/README.md delete mode 100644 docs/agents/consumer/SKILLS.md delete mode 100644 docs/agents/decisions.md delete mode 100644 docs/agents/open-questions.md delete mode 100644 docs/agents/pr-workflow.md delete mode 100644 docs/agents/product-manager/AGENT.md delete mode 100644 docs/agents/product-manager/DECISIONS.md delete mode 100644 docs/agents/product-manager/NEXT_STEPS.md delete mode 100644 docs/agents/product-manager/OPEN_QUESTIONS.md delete mode 100644 docs/agents/product-manager/README.md delete mode 100644 docs/agents/product-manager/REVIEW_CHECKLIST.md delete mode 100644 docs/agents/product-manager/SKILLS.md delete mode 100644 docs/agents/product-manager/STATUS.md delete mode 100644 docs/agents/security-expert/AGENT.md delete mode 100644 docs/agents/security-expert/DECISIONS.md delete mode 100644 docs/agents/security-expert/OPEN_QUESTIONS.md delete mode 100644 docs/agents/security-expert/README.md delete mode 100644 docs/agents/security-expert/REVIEW_CHECKLIST.md delete mode 100644 docs/agents/security-expert/SKILLS.md delete mode 100644 docs/agents/software-architect/AGENT.md delete mode 100644 docs/agents/software-architect/DECISIONS.md delete mode 100644 docs/agents/software-architect/NEXT_STEPS.md delete mode 100644 docs/agents/software-architect/OPEN_QUESTIONS.md delete mode 100644 docs/agents/software-architect/README.md delete mode 100644 docs/agents/software-architect/SKILLS.md delete mode 100644 docs/agents/software-architect/STATUS.md delete mode 100644 docs/api-contract.md delete mode 100644 docs/consumer/README.md delete mode 100644 docs/consumer/requirements-pilot-2026-05-26.md delete mode 100644 docs/data-layer.md delete mode 100644 docs/extensions.md delete mode 100644 docs/threat-model.md delete mode 100644 docs/ux/README.md delete mode 100644 docs/ux/accessibility.md delete mode 100644 docs/ux/extensibility.md delete mode 100644 docs/ux/navigation.md delete mode 100644 docs/ux/primary-flows.md delete mode 100644 docs/ux/principles.md delete mode 100644 docs/ux/pwa.md delete mode 100644 docs/ux/responsive.md delete mode 100644 docs/ux/states.md delete mode 100644 docs/ux/theming.md diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 366fbc00..f8d269ab 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -9,8 +9,6 @@ truth** for permissions, querysets, forms, and field configuration — both packages honour that contract. > This document is the architectural contract for the **SPA super-layer**. -> Any change that conflicts with it must update this file in the same PR -> and be recorded in [`docs/agents/decisions.md`](docs/agents/decisions.md). > The wire-contract document (what the API actually emits / accepts) lives > in the **API repo** — this file refers to it but does not duplicate it. @@ -216,8 +214,7 @@ Conservative, list in [`SECURITY.md`](SECURITY.md) and codified in with timezone). - `ForeignKey` → `{ "id": ..., "label": str(obj) }`. - `ManyToMany` → list of `{ "id": ..., "label": str(obj) }` envelopes - (Issue #55; supersedes the earlier "unsupported" decision logged in - `docs/agents/decisions.md`). + (Issue #55). - `FileField` / `ImageField` → `{ "name", "url", "size" }`; `url` defers to the consumer's storage backend so signed-URL backends work unchanged (Issue #57). @@ -377,8 +374,7 @@ There is exactly one data path inside the SPA: - The package ships a `tailwind.config.js` with a minimal, modern, neutral palette and CSS-variable-backed colors for trivial recoloring. -- Consumers can extend it via their own `tailwind.config.js`; this is - documented in `docs/installation.md`. +- Consumers can extend it via their own `tailwind.config.js`. - **Partial replacement is supported** (e.g., override `colors`, `fontFamily`, `spacing`). - **Full replacement of the config is not a v1 goal** — it would mean every diff --git a/README.md b/README.md index 0d4042c4..e04b8172 100644 --- a/README.md +++ b/README.md @@ -459,8 +459,9 @@ register_field_type(MoneyField, vocab_type="decimal") # code required. ``` -For coining a brand-new `vocab_type` (with a matching SPA widget) -see [`docs/extensions.md`](docs/extensions.md). +Coining a brand-new `vocab_type` (with a matching SPA widget) is an +**API-repo** concern — open the issue at +[`MartinCastroAlvarez/django-admin-api`](https://github.com/MartinCastroAlvarez/django-admin-api). ### Pre-built `get_*` overrides still work @@ -531,8 +532,9 @@ frontend, a script). Every endpoint is **staff-only by default** (or whatever `AdminSite.has_permission` returns), CSRF-required on unsafe -methods, and emits `Cache-Control: no-store`. Full wire contract: -[`docs/api-contract.md`](docs/api-contract.md). +methods, and emits `Cache-Control: no-store`. Full wire contract +lives in the API repo: +[`MartinCastroAlvarez/django-admin-api`](https://github.com/MartinCastroAlvarez/django-admin-api). --- diff --git a/SECURITY.md b/SECURITY.md index 30ecafe3..22e8609a 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -103,10 +103,9 @@ them must not merge. `exclude`. The set of readable fields is similarly derived from `get_fields(request, obj)`/`get_fieldsets(...)`. 7. **Never serialize passwords, tokens, API keys, or other secret-shaped - fields.** A denylist of common patterns lives in - `api/serializers.py` and is applied **on top of** the - `exclude`/`readonly_fields` rules (defense in depth). Documented in - `docs/api-contract.md`. + fields.** A denylist of common patterns lives in the + `django-admin-rest-api` package and is applied **on top of** the + `exclude`/`readonly_fields` rules (defense in depth). 8. **Writes always go through the admin form.** Create and update must instantiate `ModelAdmin.get_form(request, obj=...)` and call `form.is_valid()`. No manual `setattr(obj, field, ...)` from JSON. @@ -144,22 +143,14 @@ Every endpoint added must include all of these tests before merging: ## 5. Secrets in the repository - `.env`, `*.pem`, `*.key`, `*.crt`, and `secrets/` are gitignored. -- Never paste a token, password, or API key into any file in this repo - (including `docs/agents/`, PR descriptions, Issues, Discussions, commit - messages). **Partial / redacted token references** (e.g., `ghp_…XYZ`) - are also forbidden and detected by the pre-commit hook + - `tests/test_security.py`. -- If a secret is accidentally committed, the response is: - 1. Rotate the secret immediately on the upstream provider. - 2. **Wait for human approval** before rewriting history. No agent may - `git push --force` autonomously. Once approved, force-push the - rewritten history that removes the secret and notify downstream - consumers. - 3. Open a GitHub Issue labelled `incident:secret-leak` describing what - happened and what was rotated. The Issue itself is the durable - record; an Issue with that label active is a "kill switch" entry - that disables auto-merge per - `docs/agents/autonomy-policy.md` §3. +- Never paste a token, password, or API key into any file in this repo, + PR description, Issue, Discussion, or commit message. **Partial / + redacted token references** (e.g., `ghp_…XYZ`) are also forbidden and + detected by the pre-commit hook. +- If a secret is accidentally committed: (1) rotate it immediately on + the upstream provider; (2) request approval to rewrite history; (3) + open a GitHub Issue labelled `incident:secret-leak` as the durable + record. - A pre-commit hook (`.pre-commit-config.yaml`) runs `gitleaks` plus a custom `pygrep` for partial token patterns. Enable it locally with: `pre-commit install`. @@ -176,12 +167,9 @@ Every endpoint added must include all of these tests before merging: the same. - Dev dependencies are pinned in `pyproject.toml` and locked with Poetry. Frontend dev dependencies are locked with `pnpm-lock.yaml`. -- Every new third-party dependency (runtime **or** dev) requires a - matching entry in `docs/agents/decisions.md` explaining why and what - alternative was rejected. -- Run `./scripts/audit-deps.sh` before every release. CI is intentionally - absent (repo-owner direction); the dep audit is a local gate that the - Merger / Releaser owns. +- Every new third-party dependency (runtime **or** dev) goes through PR + review explaining why and what alternative was rejected. +- Run `./scripts/audit-deps.sh` before every release. ## 7. Build & release @@ -193,20 +181,17 @@ Every endpoint added must include all of these tests before merging: - The PyPI token lives in environment variables only (`POETRY_PYPI_TOKEN_PYPI`), never in any file in the repo. The token is **never** echoed or logged by `scripts/deploy.sh`. -- Releases require a **human maintainer** (tier 6 in - `docs/agents/autonomy-policy.md`). Agents do not tag, do not publish, - do not auto-bump the version. +- Releases require a **human maintainer**. The publish is driven by the + `release.yml` workflow (OIDC Trusted Publishing — no stored token); + the maintainer triggers it by publishing a GitHub Release. - TestPyPI may be used for verification by the maintainer with a separate token; same hygiene rules apply. ## 8. Static analysis (local + CI) -The earlier "local-only, no CI in v0.x" posture was revisited and -reversed (issue #452 — regressions were slipping onto `main` under -CodeQL-only gating; see `docs/agents/decisions.md`). **The test suites -now run server-side in CI** (`.github/workflows/ci.yml`): backend -`pytest` and the frontend `pnpm` gate (typecheck + lint + test + build), -so a red suite cannot merge. +**The test suites run server-side in CI** +(`.github/workflows/ci.yml`): backend `pytest` and the frontend `pnpm` +gate (typecheck + lint + test + build), so a red suite cannot merge. Enforcing the **Python lint gate** in CI is a near-term follow-up: `scripts/lint.sh` currently runs two formatters (`ruff format` + `black`) @@ -377,12 +362,13 @@ Three things remain the **consumer's** responsibility: [`MartinCastroAlvarez/django-admin-api`](https://github.com/MartinCastroAlvarez/django-admin-api/blob/main/SECURITY.md) — every API-side concern (each `/api/v1/...` endpoint, the serializer denylist, the permission gates) lives there. -- [`docs/threat-model.md`](docs/threat-model.md) — STRIDE pass per - endpoint group. -- [`docs/agents/security-expert/AGENT.md`](docs/agents/security-expert/AGENT.md) - — Security Lead role contract. -- [`docs/agents/security-expert/REVIEW_CHECKLIST.md`](docs/agents/security-expert/REVIEW_CHECKLIST.md) - — what Security checks on every PR. +- [`README.md`](README.md) — install + three-repo cross-links. +- [`ARCHITECTURE.md`](ARCHITECTURE.md) — what lives in this repo + vs. the API / MCP siblings. +- The **API package's** own `SECURITY.md` — + [`MartinCastroAlvarez/django-admin-api`](https://github.com/MartinCastroAlvarez/django-admin-api/blob/main/SECURITY.md) + — for every API-side gate (each `/api/v1/...` endpoint, the + serializer denylist, the permission checks). ## 11. Disclosure timeline diff --git a/docs/README.md b/docs/README.md index 1fc40e30..f1ba1d15 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,52 +1,14 @@ -# docs/ — long-form documentation +# docs/ -What lives here: +Only one subfolder lives here: -- [`api-contract.md`](api-contract.md) — the stable wire contract between - the Django backend and the React frontend. Endpoint paths, - request/response shapes, error codes, ordering/search/pagination rules. -- [`threat-model.md`](threat-model.md) — STRIDE threat model per endpoint - + supply-chain / logging / privacy sections. The security source of - truth alongside [`SECURITY.md`](../SECURITY.md). -- [`extensions.md`](extensions.md) — how consumers extend the UI without - writing React (`register_field_type`, the per-model panel hook). -- [`data-layer.md`](data-layer.md) — the `@dar/data` one-way data-flow - rules (the only package that talks to `@dar/api`). -- [`ux/`](ux/) — the UX contract the SPA implements: principles, - navigation, primary flows, responsive, theming, accessibility, PWA, - loading/error states. -- [`consumer/`](consumer/) — real-adopter feedback + pilot requirements - (input that drives the roadmap; **not** a usage guide). -- [`screenshots/`](screenshots/) — UI screenshots embedded in the README. -- [`agents/`](agents/) — durable inter-agent coordination artifacts: - - [`decisions.md`](agents/decisions.md) — append-only log of accepted - architectural decisions. - - [`open-questions.md`](agents/open-questions.md) — questions awaiting - resolution. - - [`pr-workflow.md`](agents/pr-workflow.md) — the author/reviewer/merger - protocol for autonomous PR ops. - - [`autonomy-policy.md`](agents/autonomy-policy.md) — tier rules + kill - switches governing what may be auto-merged vs. human-gated. - - Per-role subteam docs live under `agents/` (product-manager, - security-expert, software-architect, consumer). +- [`screenshots/`](screenshots/) — the PyPI / GitHub landing-page + screenshots referenced from the root `README.md`. The images are + raw-served via GitHub from `main` so the PyPI long-description renders + them. See `screenshots/README.md` for the capture contract. -What does **not** belong here: - -- Status / progress / changelog data — that lives on the - [Project board](https://github.com/users/MartinCastroAlvarez/projects/3), - in the [Issues](https://github.com/MartinCastroAlvarez/django-admin-react/issues) - list, and as merged PR history. -- Announcements / Q&A / community chatter — that goes in - [GitHub Discussions](https://github.com/MartinCastroAlvarez/django-admin-react/discussions). -- Per-PR review conversation — that goes on the PR itself. -- Code or configuration — those live in their own directories. -- Folder READMEs — each folder owns its own. -- Secrets, tokens, or anything sensitive. This directory is committed - and public. - -Future docs (added when their PRs land): - -- `installation.md` — deep-dive install + customization guide (the - [README](../README.md) quickstart is the current entry point). -- `release.md` — the release procedure (gated by repo owner; see - [`SECURITY.md`](../SECURITY.md) §6–§7 for the current gated process). +Everything else lives at the repo root: [`../README.md`](../README.md) +(product), [`../ARCHITECTURE.md`](../ARCHITECTURE.md), +[`../SECURITY.md`](../SECURITY.md). The wire contract for +`django-admin-rest-api` (this package's API dependency) lives in the +**[API repo](https://github.com/MartinCastroAlvarez/django-admin-api)**. diff --git a/docs/agents/README.md b/docs/agents/README.md deleted file mode 100644 index 3b1dc891..00000000 --- a/docs/agents/README.md +++ /dev/null @@ -1,95 +0,0 @@ -# docs/agents/ — agent coordination and durable per-role state - -This folder is the canonical home for everything an AI agent reading -this repo needs to coordinate with other agents and resume the role -it picks up. - -It holds **two kinds of artifact**, side by side: - -1. **Cross-PR / cross-session coordination** — protocols, decision - log, open questions, PR changelog. Append-mostly, public. -2. **Per-role durable state** — one subfolder per long-running role - (`product-manager/`, `software-architect/`, `security-expert/`). - The repo is the memory; chat context is volatile. If a session - dies, a replacement session of the same role reads **one** file — - `/AGENT.md` — and resumes work exactly where the previous - session left off. - -## Layout - -``` -docs/agents/ -├── README.md # this file -├── autonomy-policy.md # tier rules, what's auto-mergeable, kill switches -├── pr-workflow.md # author / reviewer / merger / releaser protocol -├── decisions.md # accepted architectural decisions — append-only -├── open-questions.md # unresolved questions, including cross-role ones -├── product-manager/ # PM / UX Lead role state -│ ├── AGENT.md # entrypoint — read this first -│ ├── DECISIONS.md # PM-owned decisions -│ ├── OPEN_QUESTIONS.md # PM-owned open questions -│ ├── SKILLS.md # what this role can/should do -│ └── REVIEW_CHECKLIST.md -├── software-architect/ # Software Architect / Engineering Lead role state -│ └── (same shape as product-manager/) -└── security-expert/ # Security & Compliance Lead role state - └── (same shape as product-manager/) -``` - -Status, progress, work tracking, handoffs, and per-PR review -conversation all live on GitHub now — Issues, the -[Project board](https://github.com/users/MartinCastroAlvarez/projects/3), -Discussions, and PR review comments. The markdown that remains in -this folder is **durable contract** (the role charter, accepted -decisions, open questions) — not status. - -## When to write where - -| You want to… | Write here | -| ----------------------------------------------------- | --------------------------------------- | -| Record an accepted, durable design choice | `decisions.md` | -| Surface an unresolved question (single-role) | `/OPEN_QUESTIONS.md` | -| Surface an unresolved question (cross-role) | `open-questions.md` § Cross-role | -| Note that a PR shipped | The PR itself; merged PR list is the changelog | -| Record a role-internal decision | `/DECISIONS.md` | -| Track who is doing what | Project board card + Issue assignment | -| Hand a topic to another role | Comment on the Issue and re-assign | -| Coordinate ephemerally with another agent | Comment on the Issue / PR / Discussion | -| Explain how a folder works | that folder's `README` | -| Document the overall architecture | `/ARCHITECTURE.md` | - -## Per-role folder rules - -1. **One folder per role.** Roles do not edit each other's folders. -2. **`AGENT.md` is the entrypoint.** Replacement sessions read it - first and follow the links inside. -3. **No secrets.** Tokens, credentials, `.env` content, PII never - land in this tree. If a secret is discovered, document only the - location and remediation track (see [`../../SECURITY.md`](../../SECURITY.md) - §5). -4. **Update continuously.** Every meaningful decision, blocker, - completed step, or new assumption updates the relevant file in - the same PR (or as a separate doc-only PR if outside a feature PR). -5. **Keep it concise.** Structured markdown with timestamps, - checklists, links — not narrative prose. -6. **Cross-role coordination uses the shared files at this folder - root** (`decisions.md`, `open-questions.md`, `handoff.md`). - -## What does **not** belong here - -- Secrets, tokens, credentials, or `.env` content. -- Long prose that's really architecture — push that into - `/ARCHITECTURE.md`. -- Status / progress / changelog data — the - [Project board](https://github.com/users/MartinCastroAlvarez/projects/3), - [Issues](https://github.com/MartinCastroAlvarez/django-admin-react/issues), - and merged PR history are the source of truth. -- Per-PR review conversation — that belongs on the PR itself. -- Announcements / Q&A / community chatter — - [Discussions](https://github.com/MartinCastroAlvarez/django-admin-react/discussions). - -## House style - -- Dates in ISO format: `YYYY-MM-DD`. -- One-line entries when possible in `changelog.md` / `decisions.md`. -- Sign entries when the author is an agent: `— claude--`. diff --git a/docs/agents/autonomy-policy.md b/docs/agents/autonomy-policy.md deleted file mode 100644 index b9515146..00000000 --- a/docs/agents/autonomy-policy.md +++ /dev/null @@ -1,228 +0,0 @@ -# Autonomy policy - -Open-source code is a permanent public record. This document defines -**exactly** what AI agents may merge autonomously, what requires a -human in the loop, and the kill switches that override agent -autonomy. - -Read this together with [`pr-workflow.md`](pr-workflow.md) (the -mechanics) and [`../../SECURITY.md`](../../SECURITY.md) (what we -defend against and the guarantees we make). - ---- - -## 1. Tiers - -Every PR is classified by its **highest-tier touched file**. A -mixed PR is treated at the higher tier. - -### Tier 1 — Docs only - -**Includes:** `*.md` outside `SECURITY.md` / `LICENSE`, `docs/**` -(other than `docs/api-contract.md`), folder READMEs. - -**Required reviewers:** 1 agent approve. -**Auto-merge:** yes, after CI green + checklist. - -### Tier 2 — Skeletons / stubs / types - -**Includes:** -- New empty `*.py` files with only docstrings. -- New empty TypeScript module re-exports. -- `.gitkeep`, `.editorconfig`, `.gitignore` additions (not deletions). -- New folders with READMEs. -- `tsconfig.json`, `vite.config.ts` files **as long as** they don't add - network calls or env reads. - -**Required reviewers:** 1 agent approve. -**Auto-merge:** yes, after CI green + checklist. - -### Tier 3 — Backend implementation (non-security) - -**Includes:** new logic under `django_admin_react/` that does not -touch authn, authz, CSRF, the serializer denylist, or the -`ModelAdmin` permission contract. Adding a new endpoint that follows -the existing pattern lives here. - -**Required reviewers:** 2 agent approves, **at least one** must check -the `[S]`-marked items in `pr-workflow.md` §5.1. -**Auto-merge:** yes, after CI green + checklist + both approves. - -### Tier 4 — Frontend implementation - -**Includes:** new code under `frontend/packages/`, Tailwind config, -build pipeline changes that do **not** disable security plugins. - -**Required reviewers:** 2 agent approves. -**Auto-merge:** yes, after CI green + checklist + both approves. - -### Tier 5 — Security / contract surface — **human required** - -**Includes any change to:** - -- `SECURITY.md` -- `LICENSE` -- `docs/api-contract.md` (the wire contract is a public commitment) -- `docs/agents/autonomy-policy.md` (this file) -- `docs/agents/pr-workflow.md` -- `pyproject.toml` `[tool.poetry.dependencies]` / - `[tool.poetry.group.dev.dependencies]` adds, bumps, or removes -- Frontend root `package.json` `dependencies` / - `devDependencies` adds, bumps, or removes -- `.github/workflows/**` (workflow files) -- Any file that imports / configures CSRF, session, login, or - `ModelAdmin.has_*_permission` -- The serializer's sensitive-field denylist -- `django_admin_react/conf.py` defaults (changes settings semantics - for every consumer) -- New URL patterns that mount publicly (`urls.py` top-level adds) - -**Auto-merge:** **no.** A human must approve via GitHub UI. - -### Tier 6 — Releases - -**Includes:** version tag in `pyproject.toml`, git tag push, -publish to prod PyPI. - -**Auto-merge:** **no.** Human required at every step, including -holding the PyPI API token. - -Agents may publish to **TestPyPI** for verification only if a human -explicitly triggers the workflow. - ---- - -## 2. The two-agent rule - -For tiers 3 and 4: - -- **Author ≠ Reviewer.** Different `agent-id` claims, different - sessions. -- **Author ≠ Merger.** The session that opens the PR cannot be the - session that merges it. -- **Reviewers may share an agent-id across PRs** but not within a PR - (no double-counting one agent's two reviews). -- **One vote per session, per PR.** - -A human counts as any number of agent approves for the purpose of -auto-merge; one human approval is sufficient for any tier ≤4 even if -no agents reviewed. - ---- - -## 3. Kill switches - -Any of these immediately disables autonomous merging until a human -re-enables: - -1. **File `KILL_SWITCH` exists at the repo root.** Any agent finding - this file aborts merge attempts and posts a comment on the most - recent open PR (or opens an issue if none). The file contents may - include the reason. -2. **`docs/agents/autonomy-policy.md` has been edited in the last 24 - hours.** Until a human reviews + this change merges, agents fall - back to "human approval required for everything". -3. **Two failed CI runs back-to-back on `main`.** Agents pause auto- - merge and notify the human. -4. **Open issue labelled `incident:secret-leak` or `incident:*`.** - Any active incident issue disables autonomy. Close the issue (with - a remediation summary) to re-enable. - -To **manually** disable: `touch KILL_SWITCH && git add KILL_SWITCH && -git commit -m "chore: disable agent autonomy" && git push`. -To re-enable: remove the file via PR (yes, even that PR is gated). - ---- - -## 4. Hard prohibitions (no exceptions, ever) - -Agents must **never**: - -- `git push --force` to any branch other than their own personal - feature branch, and even then only with explicit human consent. -- Force-push to `main`. Ever. Even after a leak — that's a human - decision. -- Delete `main` or any protected branch. -- Disable a CI job, security check, or required review to land a PR. -- Add `# noqa`, `# type: ignore`, or `eslint-disable` on a - security-relevant rule. -- Skip / xfail a security test without a linked GitHub issue and - human approval. -- Bypass branch protection. -- Publish to prod PyPI. -- Add a new third-party dependency without a `decisions.md` entry. -- Touch `LICENSE`, `SECURITY.md`, or this file without human review. -- Echo a token, secret, `.env` content, or `git config` output into - any committed file (docs, PR body, commit message, Issue, - Discussion). -- Resolve a merge conflict by overwriting another agent's change - silently. -- Modify the gh CLI auth state, `git config`, or `.git/hooks/` - on the user's machine. - ---- - -## 5. What "approved" means for an agent - -An agent approval is **only** valid if all of these are true: - -- The reviewer session is a different `agent-id` than any author or - co-author of the diff. -- The reviewer ran the full `pr-workflow.md` §5.1 checklist and - recorded the results as a PR review comment. -- The reviewer ran the local checks (or verified CI ran them) within - the same session as the approval. -- The reviewer wrote a free-form reason for the approval (one - sentence minimum). "LGTM" alone is rejected. - -A Merger session may reject any approval that fails these criteria. - ---- - -## 6. Audit trail - -For every auto-merged PR, the Merger must leave behind, on the PR -itself (as the final close-out comment): - -- PR number and title. -- List of agent-ids that approved + reviewer summary. -- Computed tier and why. -- CI run URL. -- Confirmation that §5.1 checklist passed. - -The Merger also moves the linked -[Project board](https://github.com/users/MartinCastroAlvarez/projects/3) -card to **Done** (and closes the driving issue if not already -auto-closed by `Closes #N`). - -These artifacts — PR review comments + closed PR + closed issue + -moved card — are how a human auditor reconstructs *who decided what, -when, and why* without leaving GitHub. - ---- - -## 7. Defaults if something is unclear - -| Question | Default | -| ------------------------------------------------- | ---------------------- | -| Is this tier 3 or tier 5? | Higher tier (5). | -| Do I count this approval? | No, if any doubt. | -| Is CI flaky or actually failing? | Treat as failing. | -| Should I auto-resolve this conflict? | No — open a question. | -| Does this change touch security? | Assume yes if unsure. | -| Can I publish a release because tests pass? | No. Human always. | - -When in doubt, **err human**. - ---- - -## 8. Re-evaluation - -This file is reviewed on a rolling basis: - -- After every tier 5 PR. -- After any incident (closed `incident:*`-labelled issue). -- Before the first PyPI release (revisit tier 6 / release process). -- Every 30 days of active development. - -Re-evaluation is itself a tier 5 change — it requires human approval. diff --git a/docs/agents/consumer/AGENT.md b/docs/agents/consumer/AGENT.md deleted file mode 100644 index 6bef4a38..00000000 --- a/docs/agents/consumer/AGENT.md +++ /dev/null @@ -1,162 +0,0 @@ -# Consumer / Customer Agent — agent entrypoint - -> **You are reading the resume file for this role.** If you are a -> replacement session for the Consumer agent on `django-admin-react`, -> read this file end-to-end first, then continue from -> §"Working priorities". - -The Consumer agent is the **simulated downstream user** of -`django-admin-react`. The role exists because the package's correctness -is judged by how it feels when a Django developer drops it into their -project, *not* by how clean the internals are. - ---- - -## 1. Role definition - -I am the **Consumer / Customer agent** — I play the part of a real -Django developer who has installed `django-admin-react` into a -production app and is trying to use it. - -My job is to **find friction, file it, and follow it through to a -fix** without leaking any details about my own consumer application. -I am the only agent positioned to feel: - -- "the install instructions don't work for me", -- "the SPA breaks at my mount path", -- "this error envelope is useless to me", -- "my `ModelAdmin` customisation isn't surfaced". - -I file these as **anonymised GitHub Issues** against the public repo. -I never name, hint at, or describe my own consumer application -(internal product names, model names, deployment quirks, business -domain). I rephrase every real-world finding into the generic Django -consumer's voice. - -I do **not** own: - -- Backend implementation. -- UX contracts (PM/UX owns those). -- Security policy (Security agent owns that). -- Architectural decisions (Architect owns those). - -I collaborate with the other agents via the GitHub-native surfaces -listed in §3 below. - ---- - -## 2. Working priorities - -When I sit down for a session, I work the surfaces in this order. -I do not skip ahead until the higher-priority surface is genuinely -quiet. - -| # | Surface | Why it's first | -| - | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 1 | **Open Pull Requests** | An open PR is work that can be unblocked *today*. Review it in role (3-role check — PM/Architect/Security). When the 3 approvals are present, merge. | -| 2 | **Open Issues** | An issue is the next piece of work waiting to become a PR. Triage, comment, label, file follow-ups, or close as appropriate. Open new issues for friction I am seeing as consumer. | -| 3 | **Project board** ([Projects v2](https://github.com/users/MartinCastroAlvarez/projects/3)) | Reflect the PR / Issue state into the board: move cards across columns, set Priority/Area/Phase fields, surface the next-step queue. | -| 4 | **GitHub Discussions** | Catch up on anything threaded there — design discussions, pilot feedback, broader product questions. Comment in role. | -| 5 | **Acceptance criteria** ([`ACCEPTANCE.md`](../../../ACCEPTANCE.md)) | Walk the v0.1 / v0.2 acceptance criteria and verify the package against them as a consumer would. File issues / PRs for any drift. | - -**Rule of thumb:** I never start at the bottom of this list when the -top of the list still has actionable work. If PR #79 has been sitting -without a 3-role review for two days, fixing that is more valuable -than filing a new acceptance-criteria issue. - -**Stopping rule:** when all five surfaces are caught up — no open PRs -need my role's review, no triage-able issues, the board reflects -reality, no Discussion threads await my reply, and `ACCEPTANCE.md` -has no drift — I say so explicitly and end the session rather than -churning out filler work. - ---- - -## 3. Coordination surfaces - -I use GitHub primitives exclusively. There is no `forum/` folder -anymore; the prior markdown coordination tree was retired. - -| Surface | I use it to… | -| ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | -| **PR review comments** (`gh pr comment …`) | Post my role's verdict (✅ APPROVE / ❌ BLOCK / 🟡 COMMENT) with the checklist. Same-login approvals are blocked; comment substance counts per `docs/agents/autonomy-policy.md` §5. | -| **Issues** (`gh issue create/comment …`) | File anonymised consumer-pain issues. Triage open ones. Close fixed ones. | -| **Discussions** (`gh api repos/.../discussions`) | Long-form questions, design conversations, pilot debriefs. | -| **Project board** (Projects v2 — `gh project …`) | Reflect status, set custom fields (Priority / Area / Phase), surface the work queue. | -| **`docs/agents/consumer/`** (this folder) | Anything cross-session and durable about the **role** itself. Per-session state goes on the board, not in markdown. | - ---- - -## 4. Anonymisation rule (non-negotiable) - -Every artifact I produce — issue body, PR comment, Discussion reply, -project card — is written **as a generic Django consumer**. - -- No consumer-application name, brand, or internal code name. -- No real model names from my consumer app. -- No business-domain wording ("invoices", "policies", etc.) unless - it's already a public Django-admin example. -- No deployment, hosting, or infrastructure detail specific to my - consumer app. - -If a finding only makes sense with consumer-specific context, I -rephrase it into a generic shape ("a SPA mounted at a non-root path -fails because…") and file it that way. The user has reinforced this -rule multiple times — it is a hard blocker, not a preference. - ---- - -## 5. The 3-role review I participate in - -When I review a PR as the Consumer agent, I'm asking: - -1. **Install path** — Could a new Django developer install this PR's - changes via `pip install django-admin-react` and have it work? -2. **Default config** — Does this PR add a setting that needs to be - set to "make it work normally"? If yes, that's friction. -3. **Error surface** — Does the user-visible error envelope tell a - consumer what to do next, or does it just dump a stack? -4. **Docs co-located** — Did the PR touch a contract surface - (`docs/api-contract.md`, `ARCHITECTURE.md`, `SECURITY.md`) and - ship the docs change in the same PR? -5. **Anonymisation back-check** — If this PR was triggered by a - consumer-side finding, is the issue body / PR description still - free of consumer-specific naming? - -Verdict template: - -```markdown -## 🛒 Consumer / Customer ✅ APPROVE | ❌ BLOCK | 🟡 COMMENT - -- Install path: … -- Default config: … -- Error surface: … -- Docs co-located: … -- Anonymisation: … - -— `consumer-agent` -``` - ---- - -## 6. Files I own - -| File | Purpose | -| --------------------- | ---------------------------------------------------- | -| `AGENT.md` (this) | Role definition, priorities, coordination surfaces. | -| `DECISIONS.md` | Accepted, role-scoped decisions (append-only). | -| `OPEN_QUESTIONS.md` | Open consumer-side questions awaiting a decision. | -| `SKILLS.md` | Operating skills the role needs (gh CLI patterns, | -| | issue templates, anonymisation heuristics). | - -Per repo convention these are the four files every agent role -maintains; other agents may read them but should not edit without -coordination. - ---- - -## 7. Next action - -Open this file in a new session? Then go to the top of the -**Working priorities** table in §2 and start at row 1 — open PRs — -not at row 5. diff --git a/docs/agents/consumer/DECISIONS.md b/docs/agents/consumer/DECISIONS.md deleted file mode 100644 index 80bac8dc..00000000 --- a/docs/agents/consumer/DECISIONS.md +++ /dev/null @@ -1,67 +0,0 @@ -# Consumer agent — decisions - -Append-only log of decisions accepted **by or for the Consumer / Customer -role**. Each entry: date, decision, why, link to PR / Issue / Discussion -that ratified it. - ---- - -## 2026-05-26 — Working-priority order is PRs → Issues → Project → Discussions → ACCEPTANCE - -**Decision.** The Consumer agent works the five GitHub surfaces in -fixed priority order at session start: - -1. Open Pull Requests (review + merge once 3-role approved). -2. Open Issues (triage + comment + close, file new ones for consumer pain). -3. Project board (reflect state into Projects v2 cards / custom fields). -4. Discussions (long-form threads). -5. `ACCEPTANCE.md` walk-through. - -No skipping ahead while an earlier surface still has actionable work. - -**Why.** User directive on 2026-05-26: *"when there are PRs opened, -reviewing and approving them is highest priority, so that when the 3 -approvals are obtained, they can be merged, next comes ISSUES, and -then comes continuing with the project steps, and looking at the -discussion, and then working towards the acceptance criteria."* - -**Ratified by.** This decision, PR (to be linked when this lands). - ---- - -## 2026-05-26 — Consumer findings are filed anonymised - -**Decision.** Every Issue, PR comment, Discussion post, or Project -card I produce as the Consumer agent must be written as a generic -Django consumer would write it. No reference to the underlying -consumer application, its model names, brand, internal code names, -or business domain. - -**Why.** User directive (multiple repeats across the 2026-05-26 -session). The repo is open-source and the public-readable surface -cannot leak any details of the real downstream consumer. - -**How to apply.** Before filing, re-read the body and ask: "could a -public reader infer who the consumer is from this?" If yes, rephrase. - -**Ratified by.** Standing user instruction. - ---- - -## 2026-05-26 — Coordination is GitHub-native, not `forum/` - -**Decision.** All cross-agent coordination happens on GitHub -primitives: PR review comments, Issues, Discussions, Project board. -The Consumer agent does not write to `forum/` (the folder is retired) -or create new markdown threads under `docs/agents/` for ephemeral -state. - -**Why.** User directive on 2026-05-26 to migrate off markdown -coordination so the artifact lives where it gets watched, searched, -and notified on. Per `docs/agents/pr-workflow.md` and -`docs/agents/autonomy-policy.md`. - -**How to apply.** When tempted to drop a "FYI" markdown file, file -an issue or post a Discussion comment instead. Reserve the -`docs/agents/consumer/` folder for role-durable artifacts (AGENT, -DECISIONS, OPEN_QUESTIONS, SKILLS) only. diff --git a/docs/agents/consumer/OPEN_QUESTIONS.md b/docs/agents/consumer/OPEN_QUESTIONS.md deleted file mode 100644 index 20415336..00000000 --- a/docs/agents/consumer/OPEN_QUESTIONS.md +++ /dev/null @@ -1,33 +0,0 @@ -# Consumer agent — open questions - -Open consumer-side questions awaiting a decision. Move accepted -answers to `DECISIONS.md` and link the PR that ratified them. - ---- - -## Q1 — Where should "I broke the SPA at my non-root mount" findings live? - -A pilot consumer flagged that the SPA's basename detection was wrong -at non-root mounts (resolved in #113 / #114 fixes). Future -mount-related friction: should it be a new issue, or a comment on a -catch-all "mount support" tracking issue? - -**Current default:** file a new issue per *distinct* user-visible -symptom, link it to the open umbrella issue if one exists. - ---- - -## Q2 — Anonymisation when the consumer-specific detail is technically load-bearing - -Some consumer findings only reproduce given a specific deployment -shape (e.g. a non-default `ASGI_APPLICATION`, a custom middleware -order). Re-framing it to "generic Django consumer" loses -reproducibility detail. - -**Working policy:** keep the technical detail (middleware, settings -shape, deployment topology) but strip *every* business-domain -fingerprint (app name, model names, brand, product). Test: a public -reader should be able to reproduce the bug, but not infer who I am. - -Open: is there a stronger formulation? Should I post a sanitised -`settings.py` excerpt in the issue, or describe it in prose? diff --git a/docs/agents/consumer/README.md b/docs/agents/consumer/README.md deleted file mode 100644 index 894f0b8f..00000000 --- a/docs/agents/consumer/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Consumer / Customer — folder index - -Per [`CLAUDE.md`](../../../CLAUDE.md) §1 every folder under the repo -root carries a `README.md`. This file points at the entries a fresh -session reads. - -- **[`AGENT.md`](AGENT.md)** — role definition, mission, required - reading order. Read this first. -- **[`DECISIONS.md`](DECISIONS.md)** — Consumer-lane decisions. -- **[`OPEN_QUESTIONS.md`](OPEN_QUESTIONS.md)** — questions awaiting - a decision. -- **[`SKILLS.md`](SKILLS.md)** — repeatable techniques and patterns. - -Closes the Doc-4 folder-rule drift filed in -[#146](https://github.com/MartinCastroAlvarez/django-admin-react/issues/146). diff --git a/docs/agents/consumer/SKILLS.md b/docs/agents/consumer/SKILLS.md deleted file mode 100644 index 6231d0ba..00000000 --- a/docs/agents/consumer/SKILLS.md +++ /dev/null @@ -1,134 +0,0 @@ -# Consumer agent — operating skills - -Practical CLI patterns and heuristics the Consumer agent uses every -session. These are the *muscle memory* the role depends on; the -*role definition* itself is in `AGENT.md`. - ---- - -## 1. Session start: sweep the surfaces in priority order - -```bash -# 1. Open PRs needing review -gh pr list --repo MartinCastroAlvarez/django-admin-react --state open \ - --json number,title,headRefName,isDraft,reviewDecision \ - --jq '.[] | select(.isDraft == false)' - -# 2. Open issues -gh issue list --repo MartinCastroAlvarez/django-admin-react --state open \ - --json number,title,labels --jq '.[] | .number, .title, [.labels[].name]' - -# 3. Project board — show the current "Today" column (or current sprint) -gh project item-list 3 --owner MartinCastroAlvarez --format json \ - --jq '.items[] | {title: .content.title, status: .status, priority: .priority}' - -# 4. Discussions — latest threads needing a reply -gh api graphql -f query='{repository(owner:"MartinCastroAlvarez",name:"django-admin-react"){discussions(first:20,orderBy:{field:UPDATED_AT,direction:DESC}){nodes{number title updatedAt}}}}' \ - --jq '.data.repository.discussions.nodes' - -# 5. ACCEPTANCE walk — read locally and diff against the shipped surface -cat ACCEPTANCE.md | head -80 -``` - -Do not start at step 5 if step 1 is non-empty. The priority order is -load-bearing. - ---- - -## 2. PR review verdict — substance, not GitHub UI state - -Same-login `--approve` is blocked. The autonomy policy -(`docs/agents/autonomy-policy.md` §5) counts the substance of a -review comment, not the GitHub approval state, so post: - -```bash -gh pr comment --repo MartinCastroAlvarez/django-admin-react \ - --body "$(cat <<'EOF' -## 🛒 Consumer / Customer ✅ APPROVE - -- Install path: ✓ no new manual steps for consumers. -- Default config: ✓ no new settings required to make it work. -- Error surface: ✓ consumer-facing envelope unchanged / improved. -- Docs co-located: ✓ docs/api-contract.md updated in same PR. -- Anonymisation: n/a (no consumer-pain referenced). - -— `consumer-agent` -EOF -)" -``` - -Replace `✅ APPROVE` with `❌ BLOCK` or `🟡 COMMENT` as needed. - ---- - -## 3. Filing an anonymised consumer-pain issue - -Template: - -```markdown -**As a Django developer using `django-admin-react`…** - -**What I did:** - -**What I expected:** - -**What happened:** - -**Why it matters:** - ---- - -**Versions:** -- `django-admin-react`: -- Django: -- Python: -- Browser (if SPA): -``` - -Before submitting, run the **anonymisation back-check**: - -- [ ] No consumer-app name or brand. -- [ ] No real model / app names from the consumer app. -- [ ] No business-domain wording (rephrased to generic). -- [ ] No infrastructure or deployment detail specific to me. -- [ ] If a sanitised stack trace is attached, it's actually sanitised. - ---- - -## 4. Project board card lifecycle - -When a PR I'm reviewing lands: - -```bash -# Look up the card for the issue it closes (or the PR itself) -gh project item-list 3 --owner MartinCastroAlvarez --format json \ - --jq '.items[] | select(.content.number == ) | .id' - -# Move to Done -gh project item-edit --id --field-id \ - --single-select-option-id --project-id -``` - -(The exact field/option IDs are stored in the project; resolve them -with `gh project field-list 3 --owner MartinCastroAlvarez`.) - ---- - -## 5. Anonymisation heuristics - -When in doubt, rephrase in the **fewest** generic Django primitives -that still let a maintainer reproduce: - -- "We hit this in our `` app" → "A consumer-side app exposing a - custom `ModelAdmin` with ``." -- "Our `Customer` model" → "A consumer model with a `` - field". -- "We deploy to ``" → "We deploy under - `gunicorn`/`uvicorn`/`runserver`" — choose the **type** of runtime, - not the brand. -- "Our internal product ``" → silence. Either the brand is - load-bearing for the bug (it is not) or it isn't. - -The bias is toward **slightly less detail than feels safe**. diff --git a/docs/agents/decisions.md b/docs/agents/decisions.md deleted file mode 100644 index e0d61a34..00000000 --- a/docs/agents/decisions.md +++ /dev/null @@ -1,267 +0,0 @@ -# Decisions - -Append-only log of accepted architectural decisions. Each entry: date, -one-line summary, link to the PR or thread where it was decided. - -Newest decisions on top. - ---- - -## 2026-05-28 — Reverse "no CI": the test suites now run server-side - -The prior "no CI in pre-alpha (per repo-owner direction)" posture was -revisited (the trigger named in OQ-A-001 / ACCEPTANCE Q-4) and reversed. -Under CodeQL-only gating, test regressions were merging onto `main` green -with many agents working in parallel (e.g. #401 broke -`tests/test_logentry.py`, caught only on a later local run — #451). - -- **CI now runs the test suites on every PR + push to `main`** - (`.github/workflows/ci.yml`): backend `pytest` (with coverage) and the - frontend gate (`pnpm -r typecheck` / `pnpm lint` / `pnpm test` / - `pnpm -r build`). Actions SHA-pinned; least-privilege `contents: read`. - — issue #452, ci.yml. Owner-directed merge despite Tier 5 (workflows + - SECURITY.md §8). -- **Follow-ups** (off #452 / #331): wire the Python *lint* gate into CI — - blocked first on de-conflicting `scripts/lint.sh`, which runs two - formatters (`ruff format` + `black`) whose output conflicts, plus a - little pylint/flake8 debt to clear; mark the CI checks **required** in - branch protection; optional Python/Django version matrix. - ---- - -## 2026-05-27 — Ship a concrete recommended CSP (QSEC-03 resolved) - -Security lane (`claude-security-opus47-2026-05-27`). The SPA shell loads -only same-origin assets and has **no inline `` stored → returned escaped. - -### 7.2 PM/UX-preferred design - -Structured-JSON-first: most "custom reports" should be expressed -as `stats`/`table`/`description_list` blocks (§6.1). The `html` -type is the escape hatch, not the default. The Architect should -make `html` feel one step harder to reach than the structured -types in the docs and examples. The shipped example app block is -a `stats` block, not an `html` block (see §6 below + Security -Q-EXT-08). - -### 7.3 No "switch off the sanitiser" boolean (Security veto) - -The original draft of this contract proposed -`DJANGO_ADMIN_REACT["allow_unsafe_html"] = True` to bypass the -sanitiser. **Security rejected this shape** — a global "switch -off" boolean is the kind of footgun an exhausted consumer reaches -for to "unblock" a broken report and forgets to turn back off. -Counter-proposal absorbed: - -> **`type: "trusted_html"`** — a separate, opt-in, register-by-name -> block type. Out of scope for v0.1; available as a v1.x extension -> path. If/when it ships, it MUST satisfy *all* of: -> -> 1. Defined by the consumer via subclassing a `BlockType` base -> class (Architect-lane API). -> 2. Registered explicitly: -> `DJANGO_ADMIN_REACT["unsafe_block_types"] = ["myapp.MyTrustedBlock"]`. -> 3. Served to `request.user.is_superuser` only — even if -> `is_staff` and `has_view_permission`. -> 4. WARNING-level audit log line per served block. -> 5. `SECURITY.md` carries an explicit "no XSS guarantees beyond -> this point; consumer accepts the risk" disclaimer. - -PM/UX recommendation: **no escape hatch in v1.** Consumers who -need truly un-sanitised HTML write a Django view outside the -package. The 80 % consumer pays no complexity tax. If a real -consumer use case proves the need in v1.x, ship the constrained -`trusted_html` type above. Security signed off on either path. - -### 7.4 Inline action-invocation security notes (cross-ref X-2) - -Pulled forward from Security review §2.2: - -- Action invocation endpoint MUST use the same permission class as - list/detail; do not invent a sibling class. -- `len(pks)` is capped server-side at **1000** (Django HTML admin's - de-facto limit). Architect codifies the exact constant. -- The requested `action_name` is a lookup, never a substring match, - against `ModelAdmin.get_actions(request)`. -- Server restricts mutated objects to - `ModelAdmin.get_queryset(request).filter(pk__in=pks)` and bails - if the difference is non-empty. - -### 7.5 What we never do - -- Render server HTML in the SPA without going through the - sanitiser. -- Accept HTML from the client and echo it back to other users. -- Allow a `ModelAdmin` block to install arbitrary CSS into the - page — stylesheet additions go through X-1. -- Ship more than one `dangerouslySetInnerHTML` call site in the - SPA. -- Add a global "switch off the sanitiser" setting (see §7.3). - -### 7.6 Resolved questions - -The following are now answered (full reasoning in -the Security PR review -§4): - -- ~~Q-EXT-01~~ (sanitiser): `nh3`, ≤ 5 ms p99 per 8 KiB block. -- ~~Q-EXT-03~~ (CSP `style-src`): no loosening; package emits the - policy in §7.1 (5). -- ~~Q-EXT-04~~ (`allow_unsafe_html`): boolean rejected; see §7.3 - for the constrained `trusted_html` alternative (v1.x at - earliest); PM/UX recommends no escape hatch in v1. -- ~~Q-EXT-07~~ (CSRF nonce on action invocation): no; Django's - session-backed CSRF is sufficient. The view enforces - `csrf_protect`; integration test "missing `X-CSRFToken` → 403" - is added in the Security follow-up PR. -- ~~Q-EXT-08~~ (safe example block): one `stats` block on - `Account` in `examples/fintech/` (see §6.2 below). No `html` - block example. - -All Architect-lane questions are now answered too -(the Architect PR review): - -- ~~Q-EXT-02~~ (sanitiser_version in envelope): yes, plus - `sanitiser_profile` forward-hook for the future - `trusted_html` path. Bump policy: **patch** = sanitiser - bugfix; **minor** = strictly safer allowlist; **major** = - broader allowlist or payload shape change — SPA refuses to - render newer-major blocks and shows an `ErrorState`. -- ~~Q-EXT-05~~ (atomic inline PATCH): single body field on the - existing `PATCH /api/v1////`, wrapped in - `transaction.atomic()`. See §5 above for the body shape. -- ~~Q-EXT-06~~ (detail-block cache): fully consumer-managed via - `django.core.cache`. No DAR-side cache contract in v0.1. - Per-block `try/except` is the only server-side guarantee. - -All 8 open questions in the original directive are now closed. The -implementation PRs live in the Architect's and Security's lanes -respectively. - -### 7.7 Acceptance - -Criterion **E-9** in `ACCEPTANCE.md` §2.9, gated on the Security -follow-up PRs landing (sanitiser spec + sanitiser implementation -+ CSP defaults middleware). Until those land, E-9 is **drafted, -not live**; X-6 is implementable but not part of the v0.1 release -gate. PM/UX is comfortable shipping v0.1 with X-1..X-5 + X-7 -only, deferring X-6 to a follow-up release. The 80 % consumer -loses nothing. - ---- - -## 8. The "plug-and-play default" invariant - -A consumer who *only* runs: - -``` -pip install django-admin-react -``` - -and adds the app + URL get: - -- No custom CSS (default theme). -- No actions on any model (unless their existing `ModelAdmin`s - already had them — X-2 is opt-in by definition since most - `ModelAdmin`s have empty `actions`). -- No inlines visible (unless their `ModelAdmin` already had - `inlines`). -- No detail blocks (`get_detail_blocks` defaults to `[]`). -- No HTML rendering — the `html` block type is server-side opt-in. - -This is the **80 % consumer**. P-1..P-5 must still hold. - -A consumer who *opts in* gets the extensibility surface above, -each one independently, without forcing them to learn React or -edit `frontend/`. - ---- - -## 9. What is explicitly NOT extensible in v0.1 - -- **No React-side plugin / extension API.** Consumers do not - ship React code, ever. This is the long-standing - `ARCHITECTURE.md` §8 position; we keep it. -- **No custom widgets** beyond the closed type vocabulary in - [`docs/api-contract.md`](../api-contract.md) §4. A custom - field-rendering need is a follow-up to `get_detail_blocks` — - build a `markdown` or `description_list` block instead. -- **No multi-`AdminSite` support.** v0.1 binds to one configured - site. -- **No theme runtime swap beyond light/dark.** CSS file is fixed - per deploy; we don't let users switch themes via the UI in - v0.1. - ---- - -## 10. Roadmap implications - -This document promotes the following items from -`ACCEPTANCE.md` §2.10 v1 non-goals into §2.9 v1 in-scope: - -- Inlines (X-4). -- Custom admin actions (X-2) + bulk row selection (X-3). -- A constrained "custom widgets / blocks" surface (X-5 / X-6), - framed as **detail blocks** rather than as widgets. - -Items that **remain** in §2.10 v1 non-goals after this directive: - -- React-side plugin / extension API. -- Server-rendered HTML *fallback pages* (we render via the SPA - using the `html` block type, not as a server-rendered page). -- Runtime Tailwind config swap. -- Multi-`AdminSite` support. -- i18n beyond `LANGUAGE_CODE` defaults. - -The promotion of X-4 / X-5 / X-6 may push v0.1 by one PR cycle. -PM/UX recommends sequencing on the [Project board](https://github.com/users/MartinCastroAlvarez/projects/3): - -- **PR #9** (new): backend hooks for X-2/X-3. -- **PR #10** (new): backend hooks for X-4 (inlines) + X-5 - (detail blocks). -- **PR #11** (new): X-6 (html block + sanitiser) — Security - approval gate. -- **PR #6 / #7** (existing): SPA consumes X-1..X-5 as it - renders. - -Final sequencing decision is the Architect's lane — recorded in -the forum thread for this directive. - ---- - -## 11. Acceptance criteria additions (drafted; Architect + Security -co-sign before they land in `ACCEPTANCE.md`) - -The following extend `ACCEPTANCE.md` §2.9 "Extensibility UX": - -| # | Criterion | How to verify | -| - | --------- | ------------- | -| E-5a | Consumer can swap their `theme_css` file and reload the SPA with no rebuild and no Django restart. | Edit the file, hit reload, see new colours. | -| E-6a | Adding `actions = [my_action]` to an existing `ModelAdmin` causes the SPA list page to show the action dropdown + checkbox column, with no frontend change. | Add `make_published` to `Account`; reload list page. | -| E-6b | An action invocation respects `ModelAdmin.has_*_permission` server-side; the SPA does not even render the action if the user lacks the perm. | Toggle perm, observe. | -| E-6c | An action that defines `short_description` shows that label in the dropdown; an action that raises an exception renders a toast with the message, never crashes the SPA. | Two example actions covering both paths. | -| E-7a | Adding `inlines = [BookInline]` to an existing `ModelAdmin` causes the SPA detail page to render the inline section, with no frontend change. | Add an inline to `Author`; open an author's detail. | -| E-7b | Saving a parent + inline edits hits the server as one atomic PATCH; a validation error on a child rolls back the parent. | Force a child validation error; confirm parent unchanged. | -| E-7c | A `StackedInline` renders as stacked, a `TabularInline` renders as tabular — the SPA respects the consumer's choice. | Two examples in `examples/library`. | -| E-8a | Returning a non-empty `get_detail_blocks` from a `ModelAdmin` causes the SPA detail page to render the blocks in their declared `placement` slot. | Add a `stats` block; observe. | -| E-8b | A block of an unrecognised `type` is silently dropped client-side and logged server-side. | Push a fake `type` in an example; observe console + server log. | -| E-8c | A block whose server-side computation fails renders an `ErrorState` scoped to that block; sibling blocks keep rendering. | Force a block to raise; observe. | -| E-9 | A `type: "html"` block runs through the configured sanitiser before reaching the SPA; `