diff --git a/.claude/project/design/authoring-skills.md b/.claude/project/design/authoring-skills.md new file mode 100644 index 0000000..2e08810 --- /dev/null +++ b/.claude/project/design/authoring-skills.md @@ -0,0 +1,67 @@ +# Authoring Skills (deploy-time AI) + +> Cross-cutting pattern behind epic-002. Read when planning or changing a +> deploy-time authoring skill (`design-adoption`, `legal-extraction`, or a future +> sibling). Established across slice-018 + slice-019. + +## What they are + +Authoring skills are **deploy-time Claude-Code skills** that help a non-programmer +operator (or their AI-literate agency) reach Revoco's best use without writing +code — adopting a shop's visual identity, importing existing legal pages, etc. +They run in a Claude-Code session **at setup time**. Nothing ships in the running +container; there is no request-time AI, no live scraping when a consumer submits. + +Skill homes live under `.claude/skills//SKILL.md` (repo-shipped). + +## The core pattern: deterministic command core + AI wrapper + +Each authoring skill is split into two parts with a hard seam between them: + +1. **A deterministic artisan command** — the correctness-critical core. It takes + explicit, validated input and produces the real state change (a generated + overlay file, a settings write). This is the **CRAFT-tested seam**: a Pest + feature test drives it end-to-end. +2. **An AI skill wrapper** — the non-deterministic part (scan a live site, extract + a token set or legal content, map it to the command's input). It is **verified + hands-on in Phase 5**, never in the automated suite, because a live-site scrape + has no stable fixture. + +*Why the split:* a pure-prose skill has no automated test and cannot pass the +Phase-3 test gate. Extracting a deterministic seam makes the correctness-critical +part stable and testable, while the inherently fuzzy AI step is owned by the skill +and proven against a real site by a human. + +Instances: +- **design-adoption** (slice-018): skill scans a shop URL → `revoco:make-theme` + generates the validated `--wf-*` overlay + placement report. +- **legal-extraction** (slice-019): skill scrapes Impressum/privacy URLs → + `revoco:import-legal` validates, sanitizes and persists into `LegalSettings`. + +## Invariants every authoring skill upholds + +- **Operator-reviewed, never auto-published.** AI output is a draft. The operator + confirms theme placement / reviews the legal settings in Filament before it goes + live. The operator remains the data controller; Revoco makes no legal-correctness + guarantee. +- **Emit existing shapes, invent no new path.** Design emits the established + `--wf-*` / `APP_THEME` overlay mechanism; legal fills the existing + `spatie/laravel-settings` store. No new stores, no runtime coupling, no bespoke + formats. +- **Never write brand/operator specifics into this public repo.** Generated + overlays and assets target the operator's private infra overlay / deployment + mount (or stdout). Enforced by the rules.md Tabu. +- **Untrusted input is validated and sanitized.** Scraped third-party HTML is + sanitized with the same tool the render path uses (`Str::sanitizeHtml`); + unknown/invalid payload keys are rejected and nothing is written. Existing + operator-reviewed content is never silently clobbered (explicit `--overwrite`). +- **CLI conventions (slice-008).** Commands are parameter-driven, prompt only on a + TTY, and fail cleanly under `--no-interaction`. Env→param bridging, if any, lives + in a `task` wrapper, not in the command. + +## Test strategy + +Test the command, hands-on the AI. The Pest feature test asserts the deterministic +contract (valid output shape, known-tokens-only / known-keys-only, a11y invariants +preserved, sanitization, overwrite guard, clean `--no-interaction` failure). The AI +extraction step is a Phase-5 hands-on run against a real URL. diff --git a/.claude/project/design/deployment.md b/.claude/project/design/deployment.md index 9b35594..ca50119 100644 --- a/.claude/project/design/deployment.md +++ b/.claude/project/design/deployment.md @@ -57,7 +57,11 @@ Locally: `task smoke` builds the prod image, boots the generic stack with a thro ## Configuration (per operator, via `.env`) - App: `APP_KEY` (required, fail-fast), `APP_URL`, `APP_TIMEZONE`, locale. -- Branding: `APP_THEME` (neutral default) + logo/copy/links. +- Branding: `APP_THEME` (neutral default) + logo/copy/links. **`APP_THEME` is baked + into the container as an OS env var at `docker compose up` (`docker-compose.yml`: + `APP_THEME: "${APP_THEME:-neutral}"`), which shadows the `.env` file — switching a + deployment's theme therefore requires recreating the `app` service + (`docker compose up -d --force-recreate app`), not only editing `.env`.** (slice-018) - Mail: direct SMTP credentials (operator's mail server); sender address. - Push: `NTFY_ENABLED`, `NTFY_SERVER`, `NTFY_TOPIC`, optional token. - Queue: `QUEUE_CONNECTION=database` (default; uses SQLite on `app_storage` volume). diff --git a/.claude/project/roadmap.md b/.claude/project/roadmap.md index 66faaeb..1577e3d 100644 --- a/.claude/project/roadmap.md +++ b/.claude/project/roadmap.md @@ -81,13 +81,19 @@ a per-deployment design document; (b) privacy-policy scrape + translate → an operator-reviewed draft. AI output is always operator-reviewed, never auto-published. -Status: backlog — separate epic. Its precondition (the Phase 10 app slices) has landed, -so this is now ready to plan via `/craft:epic`. +Status: done — epic-002 (authoring-skills) shipped: design-adoption (slice-018, +`revoco:make-theme` overlay generator) + legal-extraction (slice-019, +`revoco:import-legal` Impressum/Datenschutz importer). Deploy-time Claude-Code skills +paired with Pest-tested artisan commands; all output operator-reviewed via Filament, +never auto-published. The cross-cutting "deterministic command core + AI wrapper" +pattern is captured in `design/authoring-skills.md`. ## Releases (optional) | Version | Date | Highlights | |---|---|---| +| 0.7.0 | 2026-07-06 | PHP 8.5 baseline (Docker-first); dependency refresh (Pest 4.7, Pint 1.29, Filament settings 5.6.8). | +| 0.6.0 | 2026-07-06 | Staging-findings round: operator-managed notification recipient + test mail + prominent spam subject (021), admin panel Revoco branding (022), paste-HTML editor button (023), sticky editor toolbar (024). | | 0.5.0 | 2026-06-30 | First public release (AGPL-3.0): full feature set — form, submit, async mails/push, operator backend, containerization/CI, DE/EN i18n + DST-aware mail timestamps. | > The earlier 0.1.0 / 0.2.0 entries (legal minimum; containerization + backend) were diff --git a/.claude/project/slices/slice-018-design-adoption.md b/.claude/project/slices/slice-018-design-adoption.md new file mode 100644 index 0000000..3e96460 --- /dev/null +++ b/.claude/project/slices/slice-018-design-adoption.md @@ -0,0 +1,62 @@ +# Slice 018 — Design Adoption (skill + theme-overlay generator) + +> Completed: 2026-07-08 (landed on main via PR #46; slice PR #25 into the epic branch 2026-07-02) +> Commits: 0aa120a, 5d72561 (PR #25) +> Epic: epic-002 (authoring-skills), first slice. + +## What + +A deploy-time Claude-Code skill (`.claude/skills/design-adoption/SKILL.md`) that +turns a shop's website URL into a ready-to-place Revoco brand theme, backed by a +tested artisan command `revoco:make-theme` (`MakeThemeCommand` + +`ThemeOverlayGenerator`). The skill scans the site and extracts its corporate +identity (colours, typography, logo); the command deterministically emits a +`.wf-card[data-theme=""]` overlay against the existing `--wf-*` token +contract plus a placement report (`APP_THEME=`, logo wiring via +`REVOCO_LOGO_URL` / `--wf-logo-*`). Output goes to an operator-chosen `--output` +path or stdout — never into this repo's committed theme. No app-runtime change. + +## Why + +Revoco is self-hosted and often set up by a non-programmer or an agency. Without +this, mirroring the shop's look means hand-writing CSS against the `--wf-*` +contract. The skill closes that gap so the form matches the shop (logo included) +without code. + +## Decisions + +- **Tested seam = artisan command; skill = AI wrapper.** The deterministic + generator/validator is the CRAFT-testable core; the non-deterministic AI + extraction (scan site → token set) lives in the skill and is verified hands-on + in Phase 5, not in the Pest suite. Promoted to + [`design/authoring-skills.md`](../design/authoring-skills.md) at epic close. +- **Output is operator-placed, never committed here.** The overlay + logo target + the operator's private infra overlay / deployment mount; the command writes to + an external path or stdout and the report says so. *Grounds:* rules.md Tabu (no + brand overlays/assets in the public OSS repo) + `design/theming.md` + public/private split. +- **Overlay honours the `--wf-*` contract + a11y invariants.** The generator + emits only known contract token names and never overrides the focus-ring / + reduced-motion base styles — both a correctness rule and a test assertion. +- **Skill home = `.claude/skills/`.** First repo-shipped authoring skill; + establishes where epic-002's deploy-time skills live. +- **CLI conventions (slice-008).** Parameter-driven; prompts only on a TTY; fails + cleanly under `--no-interaction` with a required option missing. + +## Commits + +- `0aa120a` — feat(skills): add design-adoption skill + revoco:make-theme generator +- `5d72561` — docs(readme): document the design-adoption authoring skill + +Files: `.claude/skills/design-adoption/SKILL.md`, +`app/Console/Commands/MakeThemeCommand.php`, +`app/Services/ThemeOverlayGenerator.php`, `app/Services/ThemeOverlay.php`, +`tests/Feature/MakeThemeCommandTest.php`, `README.md`, +`.claude/project/design/theming.md` (1 line). + +## Follow-ups + +- **Operator note surfaced during Phase-5 hands-on:** switching a deployment's + `APP_THEME` requires a container `--force-recreate`, not just an `.env` edit — + `APP_THEME` is baked as an OS env var at `docker compose up` and shadows `.env`. + Recorded in [`design/deployment.md`](../design/deployment.md) at epic close. diff --git a/.claude/project/slices/slice-019-legal-extraction.md b/.claude/project/slices/slice-019-legal-extraction.md new file mode 100644 index 0000000..2c81710 --- /dev/null +++ b/.claude/project/slices/slice-019-legal-extraction.md @@ -0,0 +1,69 @@ +# Slice 019 — Legal Extraction (skill + Impressum/Datenschutz importer) + +> Completed: 2026-07-08 (landed on main via PR #46; slice PR #26 into the epic branch 2026-07-02) +> Commits: 1dfb746, 2190492, 02e9b97 (PR #26) +> Epic: epic-002 (authoring-skills), second slice. Depends-On: slice-018. + +## What + +A deploy-time Claude-Code skill (`.claude/skills/legal-extraction/SKILL.md`) that +scrapes the operator's existing Impressum and privacy-policy pages from URLs and +loads their content into the Phase-10 `LegalSettings`, backed by a tested artisan +command `revoco:import-legal` (`ImportLegalCommand` + `LegalContentImporter`). The +command reads a structured JSON payload (`--input`), validates it against the +`LegalSettings` schema (known keys only), **sanitizes** incoming privacy/addendum +HTML, and persists the § 5 DDG imprint fields (global) plus per-locale +`privacy_content` / `imprint_address` / `imprint_addendum` under `--locale`. The +operator then reviews the result in Filament `ManageLegal` before it goes live. No +app-runtime change. + +## Why + +Operators typically already have legal pages elsewhere (their shop, a CMS). +Hand-copying that content into the panel is tedious and error-prone; the skill +populates the settings automatically, and the Filament panel remains the review +gate so the operator stays the data controller. + +## Decisions + +- **Same seam pattern as slice-018** — deterministic command core + AI wrapper; + the command is Pest-tested, the scrape/extraction is Phase-5 hands-on. The + second instance of the pattern; promoted to + [`design/authoring-skills.md`](../design/authoring-skills.md) at epic close. +- **Payload = validated JSON (`--input`), not ~18 flags.** The § 5 DDG field count + plus the nested per-locale structure make JSON the natural CLI interface; the + command writes nothing on a validation error. Still slice-008-compliant. +- **Fill-empty by default; `--overwrite` to replace.** Refuses to clobber + already-populated legal fields unless `--overwrite` is passed (clean fail listing + the conflicts). *Grounds:* legal text is operator-reviewed and liability-bearing + — an AI scrape must never silently destroy it. +- **Scraped HTML is sanitized before storing** (`Str::sanitizeHtml`, allow-list) — + `privacy_content` / `imprint_addendum` render on the public legal pages, so + imported HTML from arbitrary sites is untrusted (stored-XSS). Reuses the exact + tool the render path uses. +- **Scope = internal content fields, per `--locale`.** Imports content into the + internal fields, not the external `*_link` overrides; `fallback_order` is config + and left untouched. Natural-language fields (address, prose) are per-locale; + structured imprint fields are global. +- **Filament `ManageLegal` is the review gate.** The command prints a mandatory + controller-review reminder; it never auto-publishes. + +## Commits + +- `1dfb746` — feat(skills): add legal-extraction skill + revoco:import-legal importer +- `2190492` — docs(readme): document the legal-extraction authoring skill +- `02e9b97` — docs: genericize brand-specific examples + +Files: `.claude/skills/legal-extraction/SKILL.md`, +`app/Console/Commands/ImportLegalCommand.php`, +`app/Services/LegalContentImporter.php`, `app/Services/LegalImportResult.php`, +`tests/Feature/ImportLegalCommandTest.php`, `README.md`. + +## Follow-ups + +- **Phase-5 hands-on (Shopware)** confirmed the design: the overwrite guard fired + live (dev had legal content → refused, then `--overwrite` wrote 14 fields), + `Str::sanitizeHtml` kept structure, the Impressum rendered fully structured. + Extraction quality on flat CMS markup is best-effort by design — which is why + the Filament review gate exists and the skill (not the tested command) owns + extraction quality.