Skip to content
Draft
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
12 changes: 7 additions & 5 deletions .agents/skills/constructive-builder/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ The happy path: a warm hub → a verified basic CRUD app + one auth flow, zero b
- **S4 — Pre-patch the template, then ONE install + codegen.** Pin ONE `graphql` (workspace `pnpm.overrides`), keep the **root** `pnpm-workspace.yaml` (the single workspace — `wire-app.mjs` already stripped the nested boilerplate one under `packages/app`, S3), gate app TS with a scoped `tsconfig.appcheck.json` (`next build` type-checks the whole monorepo). `wire-app.mjs` (S3) already DECLARED the extra app deps in `<app>/package.json` (`@constructive-io/graphql-codegen@latest` dev; for a blocks app `@constructive-io/ui` + `@simplewebauthn/browser`), so run a **single** `pnpm install` (NOT separate `pnpm add` rounds — each re-resolves the heavy tree, the warm-time sink) then `pnpm codegen`. → `references/phase-3-frontend-sdk.md`.
- **S5 — Blocks on-ramp** (only if the brief needs auth/account/org UI; else skip to S7). Pick the flow id from `references/flow-catalog.md` / `references/flows.json`; install its blocks from the **GitHub-Pages registry** (fix #3 below); the provider wiring is already done by `wire-app.mjs` — including the **per-request `app`-token seam (GAP-A / SDK-008)**: wire-app injects a custom `fetch` into the `app` SDK config that re-reads the live token on every request, so the FIRST create in a fresh session (right after sign-up/sign-in, before any reload) is authed instead of silently failing as anonymous (HTTP 200 + permission-denied + 0 rows). No longer a manual step. → `references/phase-4-blocks.md`, `references/blocks-onramp.md`.
- **S6 — Confirm the SDK preflight** (`node scripts/check-sdk.mjs --project <app>` exits 0) when S5 ran. → `references/phase-4-blocks.md`.
- **S7 — Build the domain CRUD body** (always). `node scripts/scaffold-frontend.mjs <brief> <app>` stamps the runtime-generic CRUD Stack + `_meta` meta-forms and emits a page per `ui.routes[].kind:crud`. → `references/phase-4-blocks.md`, plus the `constructive-frontend` skill.
- **S7 — Scaffold the domain SKELETON** (always). `node scripts/scaffold-frontend.mjs <brief> <app>` stamps the runtime-generic CRUD Stack + `_meta` meta-forms and emits a page per `ui.routes[].kind:crud` — the **working functional skeleton** (the data wiring, the `<entity>-*` testids, the four states, row-scoping, RLS scoping, Blocks mounts), with a neutral, DENSITY-keyed default look. This is the SKELETON to AUTHOR FROM, **not** the final UI. → `references/phase-4-blocks.md`, plus the `constructive-frontend` skill.
- **S7.5 — AUTHOR the presentation from the `design.md`** (always — this is where the app gets its design). The `design.md` is the full design spec; **you, the agent, author the whole frontend from it** — there is no compile/theme step, you write the CSS yourself. Customize/replace the stock shadcn components, set the type (fonts/scale/weights), compose the layout (shell + each entity page's `return(...)`: list → data-table / gallery / split-pane / editorial / board), establish real hierarchy + spacing rhythm + intentional ornament, add subtle (reduced-motion) motion. Blocks compose as **ingredients** (restyle them via the token names). The ONLY hard rails are **Rail 1** (the functional contract — every `<entity>-*` testid, row-scoping, the hooks/selection/refetch/Stack-pushes, the RLS scoping const(s), the route/block/flow surfaces) and **Rail 2** (the shadcn token names + Tailwind-v4 wiring in the built `globals.css`, so Blocks render — checked by `node scripts/check-design.mjs --app <app>`). Selectors are testid/role-only and the token contract is name-only, so the gates can't see your restyle. Record the chosen shell/composition/density in the `design.md` so re-runs stay consistent. → **[`references/design-guide.md`](./references/design-guide.md)** (the authoring guide + both preserve-contracts + the taste self-check + the example palettes).
- **S8 — Build + the two TS gates.** `pnpm exec tsc -p tsconfig.appcheck.json --noEmit` (real app-TS gate), then `pnpm build`, then `pnpm dev --port <port>` and curl 200. → `references/phase-3-frontend-sdk.md`.
- **S9 — Verify end-to-end through the UI.** Done = a real round-trip (signup → login → create a row → reload → row persists, mutation 2xx), not a green build. Then the automated gates `./scripts/verify-phase.sh 2.1 2.3 2.6 3`, and the independent evaluator. → "Verification" below + `references/speedrun.md`.
- **S9 — Verify end-to-end through the UI.** Done = a real round-trip (signup → login → create a row → reload → row persists, mutation 2xx), not a green build. Then the automated gates `./scripts/verify-phase.sh 2.1 2.3 2.6 3` (which include the functional design gate `node scripts/check-design.mjs --app <app>` — the Rail 2 Blocks-render token contract), and the independent evaluator. → "Verification" below + `references/speedrun.md`.

> **Checkpoint discipline.** After every green `verify-phase.sh` gate: update the per-app run-state (start from `fixtures/run-state.template.json`; the verifier **reads** it, never writes) and `git commit`/tag the green state so you can roll back to the last passing phase. With `APP_ID=<app-id>` exported (app-id = the brief's `naming.db_name`) the gates read `build/<app-id>/run-state.json` + `build/<app-id>/app-brief.yaml`; unset = the legacy singleton `build/run-state.json`. Roll back with `git reset --hard green-phase-<N>`, then re-run that phase. See `references/troubleshooting.md`.

Expand All @@ -59,7 +60,7 @@ The happy path: a warm hub → a verified basic CRUD app + one auth flow, zero b
| **1 — Backend Up** | Docker (`postgres-plus:18`) + `pgpm deploy` + GraphQL server. **Warm hub → skip.** | S0 | `verify-phase.sh 1` | [`phase-1-backend.md`](./references/phase-1-backend.md) |
| **2 — Data Model Provisioned** | Workspace + provision package + blueprint (tables, RLS, grants). | S1–S2 | `verify-phase.sh 2.1`, `2.3` | [`phase-2-data-model.md`](./references/phase-2-data-model.md) |
| **3 — Frontend + SDK** | Scaffold Next.js app + `pnpm codegen` (admin + auth + app SDK). | S3–S4, S8 | `verify-phase.sh 2.6` | [`phase-3-frontend-sdk.md`](./references/phase-3-frontend-sdk.md) |
| **4 — UI / Blocks** | Blocks on-ramp (conditional) + domain CRUD body. | S5–S7 | `verify-phase.sh 3` | [`phase-4-blocks.md`](./references/phase-4-blocks.md) |
| **4 — UI / Blocks** | Blocks on-ramp (conditional) + domain skeleton, then AUTHOR the presentation from the `design.md`. | S5–S7.5 | `verify-phase.sh 3` | [`phase-4-blocks.md`](./references/phase-4-blocks.md) |

Side-quests — search, email, standalone SDK, seed data — are **not** mainline; add one only when the brief calls for it (each phase doc points to the matching extension). Authoring depth for the data/security surfaces is in the cross-referenced skills below.

Expand Down Expand Up @@ -99,8 +100,9 @@ Each table declares a **policy intent**, which maps to one of three tiers:
| [phase-1-backend.md](./references/phase-1-backend.md) | Cold-infra: Docker (`postgres-plus:18`), `pgpm deploy`, GraphQL server, gate `1` | Standing up a backend from cold (skip on a warm hub) |
| [phase-2-data-model.md](./references/phase-2-data-model.md) | Workspace + provision pkg + blueprint; object-form grants, policy intents, module list, grant outcomes | Provisioning the data model / RLS, or a 2.1/2.3 gate fails |
| [phase-3-frontend-sdk.md](./references/phase-3-frontend-sdk.md) | Scaffold the Next.js app, env + `api-<sub>` endpoints, `graphql` override, codegen, the TS gates | Scaffolding the frontend / running codegen, or a 2.6 gate fails |
| [phase-4-blocks.md](./references/phase-4-blocks.md) | Blocks on-ramp branch + the domain CRUD body; gate `3` | Wiring auth/account/org UI or building entity CRUD |
| [brief-grammar.md](./references/brief-grammar.md) | The brief schema: entities, policy intents, flows, acceptance, escape hatches | Authoring or editing a brief |
| [phase-4-blocks.md](./references/phase-4-blocks.md) | Blocks on-ramp branch + the domain CRUD skeleton; gate `3` | Wiring auth/account/org UI or scaffolding entity CRUD |
| [brief-grammar.md](./references/brief-grammar.md) | The brief schema: entities, policy intents, flows, acceptance, escape hatches, the optional `design:` block | Authoring or editing a brief |
| [design-guide.md](./references/design-guide.md) | The design guide: author the frontend from a `design.md` — intent + signature, craft foundations, the two rails (functional + shadcn-token), light dials, structure archetypes, the taste self-check, and the example palettes | Shaping/authoring an app's look-and-feel (S7.5) |
| [infra-setup.md](./references/infra-setup.md) | Hub coordinates, `constructive.config.json`, `CONSTRUCTIVE_*` env, smoke/restart | Pointing a build at a different backend/ports, or the hub is down |
| [blocks-onramp.md](./references/blocks-onramp.md) | The six-step Blocks bridge (binding, deps, env, install, providers, preflight) for this template | Deep Blocks install/wiring, or a BLOCKS-NNN issue |
| [flow-catalog.md](./references/flow-catalog.md) | Human-readable GA auth-flow catalog (preset, modules, exposed ops, blocks) | Choosing which auth flow(s) to install |
Expand Down
56 changes: 56 additions & 0 deletions .agents/skills/constructive-builder/references/brief-grammar.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ The brief is **intent-level**: you pick WHAT (a `modules.preset`, a set of `flow
> scalars (bare / "double" / 'single' / int / float / true|false|null), full-line and trailing comments,
> and bare tokens with internal `+` / `/` / `:` (so `public-read+owner-write` parses). If a brief needs a
> construct beyond this, add to the grammar in that one module — don't reach for a dep.
>
> **Not supported: folded/literal block scalars** (`>-`, `>`, `|`, `|-`). Write every multi-word string
> value (e.g. `app.description`, `design.brief`) as a **single-line quoted string** — never spread it
> across following indented lines with a `>`/`|` indicator (it would be mis-parsed as a nested block).

---

Expand All @@ -41,6 +45,7 @@ The brief is **intent-level**: you pick WHAT (a `modules.preset`, a set of `flow
| `data_model` | `data_model.tables` non-empty | your domain tables + relations — see [Data model](#data-model-tables) |
| `ui` | optional | one route per surface (`ui.routes[]`); `kind` selects how the page is emitted — see [UI routes](#ui-routes) |
| `acceptance` | optional | `required_flows[]` the live-QA gate verifies end-to-end (the SAME flow ids as `flows`) |
| `design` | optional | look-and-feel intent. **ABSENT ⇒ auto-propose a RICH `design.md`** + AUTHOR the frontend from it; `{ preset: <alias> }` ⇒ start from that named exemplar (`references/examples/<alias>.md`) and adapt. The design.md is the AUTHORED spec — there is no compile step; the only machine check is the functional Rail-2 gate (`node scripts/check-design.mjs --app <app>`). See [design (optional)](#design-optional) + [design-guide.md](./design-guide.md) |
| `assumptions` | optional | free-text notes |

> **App-id / per-app state.** The build-state id (`APP_ID`) is derived from `naming.db_name`, sanitized to
Expand Down Expand Up @@ -553,3 +558,54 @@ non-b2b preset **fails validation**. Full file: `fixtures/test-memberowner-brief
For access models beyond the seven intents (peer ownership, composite, related-member-list, …), drop to
`nodes_raw` / `policies_raw` and the **`constructive-security`** skill. See also `fixtures/test-childfk-brief.yaml`
for a multi-table FK shape.

---

## design (optional)

`design:` is an **additive, optional** top-level block that constrains the generated app's **look and
feel**. It is purely additive: a brief **without** a `design:` block is fully valid. There is **no
compile step** — the `design.md` is the AUTHORED design spec and **you (the agent) hand-author the whole
frontend from it**, guided by **[design-guide.md](./design-guide.md)** + the worked exemplars in
**[references/examples/](./examples/)**. Blocks (auth / account / org) compose into that frontend. The
only machine check is the FUNCTIONAL Rail-2 gate — `node scripts/check-design.mjs --app <app>` — which
asserts the built `globals.css` still defines the shadcn token NAMES + Tailwind-v4 wiring so Blocks
render. It never judges taste.

> **Default = auto-propose a RICH design.md, then author from it.** When `design:` is **absent**, the
> build **proposes a full, opinionated `design.md`** (a generated app should not ship a stock theme nor a
> token-swapped generic template) and then **authors the whole frontend from it** — the shell, every
> page's composition, the type, spacing, surfaces, ornament, copy, and `globals.css`. **The quality of
> the `design.md` is the quality ceiling of the app.**

> **`{ preset: <alias> }` = start from a named exemplar and adapt.** Each alias names a worked exemplar
> under `references/examples/<alias>.md`; the build opens it as the starting point and adapts it for this
> app. Use this block only to *constrain* the auto-proposal at intent level — you may also pin a few
> palette **roles** (semantic role names, NOT shadcn var names) or a one-line `brief:` sentence; the
> agent authors everything else richly. None of it is required to get a theme.

### Shape (every field optional)

```yaml
design:
brief: "warm, calm, high-end print feel" # one-line style sentence that steers the auto-proposal
preset: <alias> # start from references/examples/<alias>.md and adapt
density: compact # comfortable | cozy | compact — seeds the scaffold skeleton's spacing
colors: # a FEW role-level pins (semantic ROLES, never shadcn var names)
primary: "oklch(0.55 0.11 162)" # the one brand/action color
accent: "oklch(0.70 0.12 250)" # at most one accent
neutral: "oklch(0.55 0.01 250)" # one gray temperature
font: { sans: <family>, mono: <family> } # type-family hints the authored frontend honors
radius: "0.5rem" # radius scalar hint (px / em / rem)
```

> **`density` seeds the skeleton.** The `density` dial is the one design key the scaffold skeleton reads —
> it sets the generated entity pages' starting spacing tier (`comfortable | cozy | compact`, default
> `cozy`). Everything else here is intent the AUTHORED `design.md` + frontend honor; pin only what you
> want to constrain and author the rest. The full authoring playbook — intent + signature, palette,
> type, spacing, surfaces, ornament, copy, and the two hard rails — is in
> **[design-guide.md](./design-guide.md)**.

> **All design strings must be single-line quoted strings.** The zero-dep brief YAML reader does **not**
> support folded/literal block scalars (`>-`, `>`, `|`, `|-`). Write `brief:` (and every other string
> value) on one line — never spread across following indented lines with a `>`/`|` indicator.
Loading