Review: #20 docs/security.md (decomp:1800)
Verdict: READY
Board item 1803 (board-iss), Forgejo issue
ldraney/intelligentstaffingsystems#20, 2 points, decomposition child of #19 (parent item 1800 is tracking-only per review-1800-2026-07-05). Single-file docs ticket; every AC is backed by verified source material on main or in the read-only landscaping reference. Both board-wide traceability gaps flagged in the parent review are resolved or accounted for.Template Completeness (Feature template)
- [x] Type — Feature (routes to template-issue-feature)
- [x] Lineage — child of #19, spec pointer to docs/adoption-plan.md security.md content spec
- [x] Repo — ldraney/intelligentstaffingsystems
- [x] User Story — security doc before code; doubles as sales collateral
- [x] Context — self-contained: landscaping heading structure, 3-role adaptation, SOPS, Hetzner+Tailscale network rewrite, Lead-claim seed with mitigation
- [x] File Targets — one create target plus do-not-touch (siblings own other docs)
- [x] Feature Flag — "none"; correct (docs-only; repo has no docs/feature-flags.md — that is a Tier 2 item)
- [x] Acceptance Criteria — 5 checkboxes
- [x] Test Expectations — manual mermaid + link check, run command N/A (appropriate for docs-only)
- [x] Constraints — landscaping as reference not copy-paste; Keycloak roles authoritative, Lead.role display-only
- [x] Checklist
- [x] Related
All 12 sections present. No template gaps.
Traceability
- [x] story:project-setup label — present on board item 1803
- [x] story note verified — project page
project-issuser-stories section now exists (created after review-1800 flagged it); rowproject-setupbacked by README + docs/architecture.md + docs/adoption-plan.md. The parent review's [SCOPE] gap is resolved. - [x] arch:docs label — present on board item 1803
- [x] arch backing verified via taxonomy —
arch:docsis a defined Docs/Process category label ("Documentation work") inconvention-architecture-ids. No dedicatedarch-docsnote exists (search_notes empty), but this is a process-category label, not a system component; the convention note is its backing. The board-wide component arch-note gap (arch-rails-app) remains tracked on review-1800-2026-07-05 and is not re-raised per child. - [x] Forgejo issue — https://forgejo.tail5b443a.ts.net/ldraney/intelligentstaffingsystems/issues/20, open, label sprint:0
File Targets
- [x] docs/security.md (create) — does not exist on main; matches adoption-plan.md Tier 1 row ("now (docs phase)") and the plan's security.md content spec nearly verbatim (heading chain, Controller Role Map, SOPS not kubectl, Hetzner Caddy edge rewrite, Lead-claiming seed with "require verified email before claim")
- [x] ~/landscaping-assistant/docs/security.md (read-only reference) — exists; its H2 structure is exactly the 8 sections AC1 names: Authentication Model / Authorization Model / Attack Surface / Input Validation / CSRF Protection / Secrets Management / Network Security / Session Security; Controller Role Map at line 92; Host Authorization "Rails config.hosts" rule at line 233
- [x] docs/architecture.md cross-reference — "### Lead ↔ Keycloak Account Linking" exists on main at line 184, including the email-claim path (line 200) that AC4's attack-surface seed describes; no pending sibling change is required for the reference to resolve
- [x] Do-not-touch list — accurate: siblings #21 (pipeline.md), #22 (testing-strategy + local-dev-setup), #23 (user-stories + architecture amendments), #24 (README closer) own all other docs
Repo Placement
OK. Single-repo docs work; issue filed on the repo it modifies. No other repo affected.
Dependencies
Blocked by: nothing. The docs it reads are merged to main (item 1780/#1 sits in validation but its commits are on main — verified architecture.md content directly). Nothing on board-iss is in_progress. Parent 1800 (#19) is labeled
decomposed, tracking-only.Blocks: #24 (item 1807, README + plan-status closer) carries
blocker:siblings and waits on this ticket and #21–#23 — relationship documented on both sides. Downstream design coupling: security.md's Controller Role Map and the Lead email-claim mitigation become requirements for #6 (Keycloak OIDC auth) and the controller tickets.Parallel-safe vs siblings: each decomposition child owns disjoint files; #20 creates a single new file and edits nothing, so no merge conflicts with #21–#23 regardless of ordering. #23 modifies architecture.md, which #20 only reads — and the referenced section already exists on main.
Acceptance Criteria
All 5 criteria are objectively verifiable by an agent:
- AC1 heading structure — diff against the 8 named H2s (verified identical to the landscaping reference's actual structure)
- AC2 Controller Role Map coverage — enumerable input set verified by reading issues #6–#16: ApplicationController + SessionsController (#6), PagesController (#7), LeadsController (#8), DashboardController (#9), CatalogController + Admin::CatalogController (#11), MessagesController (#12), ProfileController (#13), Admin::UsersController (#14), AppointmentsController (#15); #10 and #16 name no controllers (harmless) — ~10 controllers total
- AC3 secrets table — the four named secrets match architecture.md line 310's SOPS-encrypted secrets.enc.yaml inventory (Keycloak client secret, Postmark API key, DB creds) plus Rails-standard SECRET_KEY_BASE
- AC4 Known Gaps Lead-claim seed — the attack path and mitigation exist verbatim in the adoption-plan spec and the architecture.md linking section
- AC5 Network Security — Hetzner edge (architecture.md:13), funnel hosts (architecture.md:275, ios.md:70), and the config.hosts rule (landscaping reference line 233, plan says "keep Allowed Hosts table rule") all have concrete source material
Test Expectations are honest: manual mermaid/link check, no fake test commands.
Blast Radius
Docs-only; no code or config. New file, zero edits to existing files — no conflict surface. The README Documentation table row for security.md is correctly deferred to closer #24. One cross-ticket effect worth carrying forward: the "require verified email before claim" mitigation seeded here is a design requirement #6's implementer must read (Known Gaps section) — the docs-before-code intent working as designed. Non-blocking note for the implementer: adoption-plan open decision 3 says "keep MinIO in the secrets list" (ActiveStorage → MinIO for catalog images, #11); AC3's list is a minimum, so adding a MinIO row now is consistent with the plan and saves a later amendment.
Decomposition Assessment
No decomposition needed. 1 file target in 1 repo (rule: >3 files across >2 repos — not tripped); 5 ACs (rule: >5 — at the boundary, not tripped); estimated single-pass time within the 5-minute rule — read one reference doc + one architecture section + enumerate controllers from issues already listed, then author one doc. 2 points. This ticket is itself the product of skill-decompose-ticket sizing, and it landed inside the limits.
Recommendation
No action needed.