Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .claude/project/design/authoring-skills.md
Original file line number Diff line number Diff line change
@@ -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/<name>/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.
6 changes: 5 additions & 1 deletion .claude/project/design/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
10 changes: 8 additions & 2 deletions .claude/project/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
62 changes: 62 additions & 0 deletions .claude/project/slices/slice-018-design-adoption.md
Original file line number Diff line number Diff line change
@@ -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="<slug>"]` overlay against the existing `--wf-*` token
contract plus a placement report (`APP_THEME=<slug>`, 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.
69 changes: 69 additions & 0 deletions .claude/project/slices/slice-019-legal-extraction.md
Original file line number Diff line number Diff line change
@@ -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.