@aotter/mantle 0.0.11-alpha.27 → 0.0.11-alpha.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,24 +1,29 @@
1
- # ADR-0016: Site semantic layer — `AGENTS.md` + `mantle/site.md`
1
+ # ADR-0016: Site semantic layer — `AGENTS.md` + launch state
2
2
 
3
3
  ## Status
4
4
 
5
- Accepted (slimmed 2026-05-12 per Epic #116; letter surface suspended 2026-06-19).
5
+ Accepted (slimmed 2026-05-12 per Epic #116; `mantle/site.md` letter
6
+ surface suspended 2026-06-19 and removed from first-run scaffolds).
6
7
 
7
8
  ## Decision
8
9
 
9
- Every agent-authored mantle project carries two files at fixed paths. They serve different audiences, change at different rates, and are filled by `create-mantle` from `_common/*.template` files.
10
+ Every agent-authored mantle project carries a small cross-tool entry file
11
+ and deterministic launch state at fixed paths:
10
12
 
11
13
  | File | Audience | Size budget | Format |
12
14
  |---|---|---|---|
13
15
  | `AGENTS.md` | Any cross-tool agent harness (Codex / Cursor / Aider / Amp / Factory / Claude Code) | ~30 lines | Plain markdown |
14
- | `mantle/site.md` | Mantle install / customize / deploy context | ~300 lines | Frontmatter + section bodies |
16
+ | `.mantle/launch-state.json` | Install/provision context captured by landing or direct CLI flags | Small JSON record | JSON |
15
17
 
16
- `AGENTS.md` answers "what is this and how do I run it." `mantle/site.md` carries the site's semantic layer:
18
+ `AGENTS.md` answers "what is this and how do I run it." Launch state
19
+ carries install-critical facts such as archetype, brand, description,
20
+ locales, selected features, GitHub owner/admin login, starter ref, and
21
+ repo target.
17
22
 
18
- - Frontmatter: machine-readable (`archetype`, `brand`, `locales`, `site_url`, `revisions[]`, `futures[]`, `dont_touch[]`).
19
- - Body sections (`## site`, `## voice`, `## editor`, `## history`) each open with a `> purpose:` header so agents can route reads without parsing prose. Mantle reads the whole file on return, edits sections, writes the whole file back. **Atomic replace, not append.**
20
-
21
- The earlier `## welcome` 5-card letter surface is suspended. Provisioning must not block on prose completion in `mantle/site.md`; a first deploy should be possible from deterministic scaffold state.
23
+ The earlier `mantle/site.md` semantic/letter surface and `## welcome`
24
+ 5-card letter surface are suspended for the first-run path. Provisioning
25
+ must not block on prose completion; a first deploy should be possible
26
+ from deterministic scaffold state.
22
27
 
23
28
  ## Placeholder macros
24
29
 
@@ -40,18 +45,21 @@ New macros must be added here, to `_common/*.template`, and to the substitution
40
45
 
41
46
  ## Update rules
42
47
 
43
- - **Mantle on return**: read whole `mantle/site.md`, edit relevant sections, write whole atomically, append one paragraph to `## history`.
44
- - **provision on deploy**: rewrite frontmatter `site_url:` placeholder → real Workers URL; append a `revisions:` entry. Same `Public site:` rewrite in `AGENTS.md`. Single commit at end of provision.
45
- - **No mid-section staged-and-running mutation.** A section is prose-replaced atomically, or a frontmatter scalar/list is replaced — never partial writes.
48
+ - **Mantle on return**: read `AGENTS.md`, `.mantle/launch-state.json`,
49
+ and repo-local skills before changing code.
50
+ - **provision on deploy**: rewrite `AGENTS.md` `Public site:`
51
+ placeholder → real Workers URL. Single commit at end of provision.
52
+ - **No hidden letter gate.** Prose may be added later, but first deploy
53
+ only depends on deterministic scaffold state and provider configuration.
46
54
 
47
55
  ## Cross-tool compatibility
48
56
 
49
- `AGENTS.md` lives at repo root because that is where the AGENTS.md ecosystem (`agents.md`) looks. `mantle/` is a mantle-owned subdirectory; the naming is deliberately specific so a generic AGENTS.md reader does not interpret it as its own state.
57
+ `AGENTS.md` lives at repo root because that is where the AGENTS.md ecosystem (`agents.md`) looks. `.mantle/` is for Mantle-owned non-secret state.
50
58
 
51
59
  ## Implementation
52
60
 
53
- - Templates: `mantle-starters/_common/AGENTS.md.template` and `mantle-starters/_common/mantle/site.md.template`.
61
+ - Templates: `mantle-starters/_common/AGENTS.md.template`.
54
62
  - Substitution: `packages/create-mantle/src/placeholder.ts`.
55
63
  - Install handoff: `skills/install/SKILL.md` describes deterministic scaffold validation and the short post-substitution notes fill.
56
- - Provision update: `skills/provision/SKILL.md` describes the `site_url:` + `revisions:` write after deploy.
64
+ - Provision update: `skills/provision/SKILL.md` describes the `AGENTS.md` public-site rewrite after deploy.
57
65
  - Theme overlay merge (Epic #116): `themes/<theme-key>/` overlay applies after the archetype starter and may touch `src/theme/` — never these two files.
@@ -16,7 +16,7 @@ Records of *why* mantle ended up shaped this way. The numbering preserves POC AD
16
16
  | [0012](0012-views-as-public-rest.md) | Views auto-expose `GET /api/views/<name>` as the public REST read surface. Schemas never get a public REST endpoint. Filter comparison values accept `{ $param: <name> }`; `?page=&show=` reserved for pagination. | Accepted (new) |
17
17
  | [0013](0013-agent-provisioned-consumer-projects.md) | Agent-provisioned consumer projects: website prompt → Skill → npm packages → starter setup → first-run provision/seed → owner/MCP handoff. | Accepted (new) |
18
18
  | [0014](0014-auth-better-auth-and-multi-tenant-mcp.md) | Better Auth for staff sign-in (D1 session); the MCP OAuth surface carves out to `@cloudflare/workers-oauth-provider` (KV grant store) at top level. The two meet at `/oauth/authorize` where the consent handler reads the Better Auth session. MCP splits into `/mcp/staff` (write, admin-role) and `/mcp` (read, any signed-in). Scope advertised as `["mcp"]` (single non-colon) because claude.ai rejects colon-shaped scopes. Auth port disappears; runtime takes Better Auth instance directly. See § "Amendment 2026-05-15". | Accepted + amended |
19
- | [0016](0016-site-semantic-layer.md) | Site semantic layer: `AGENTS.md` (cross-tool entry, ~30 lines) + `mantle/site.md` (Mantle's frontmatter + section bodies, ~300 lines). Both ship as `{{PLACEHOLDER}}` templates filled by `create-mantle`; Mantle reads whole, edits sections, writes whole atomically. Slimmed 2026-05-12 per Epic #116. | Accepted (slimmed) |
19
+ | [0016](0016-site-semantic-layer.md) | Site semantic layer: `AGENTS.md` (cross-tool entry) + `.mantle/launch-state.json` (deterministic install context). The older `mantle/site.md` letter surface is suspended from first-run scaffolds. | Accepted (slimmed) |
20
20
 
21
21
  ## Reading order
22
22
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aotter/mantle",
3
- "version": "0.0.11-alpha.27",
3
+ "version": "0.0.11-alpha.29",
4
4
  "description": "Umbrella entry for @aotter/mantle. Adopters install this one package and import from subpaths: /spec, /runtime, /cloudflare, /admin-ui. Sub-packages remain individually installable on npm for tooling / alt-adapter authors. The Netlify adapter ships as a private workspace stub in v0.1 — its subpath will be added when the impl lands in v0.2.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://mantle.tools/",
@@ -47,10 +47,10 @@
47
47
  "README.md"
48
48
  ],
49
49
  "dependencies": {
50
- "@aotter/mantle-admin-ui": "0.0.11-alpha.27",
51
- "@aotter/mantle-cloudflare": "0.0.11-alpha.27",
52
- "@aotter/mantle-runtime": "0.0.11-alpha.27",
53
- "@aotter/mantle-spec": "0.0.11-alpha.27"
50
+ "@aotter/mantle-cloudflare": "0.0.11-alpha.29",
51
+ "@aotter/mantle-spec": "0.0.11-alpha.29",
52
+ "@aotter/mantle-admin-ui": "0.0.11-alpha.29",
53
+ "@aotter/mantle-runtime": "0.0.11-alpha.29"
54
54
  },
55
55
  "peerDependencies": {
56
56
  "@cloudflare/workers-oauth-provider": "^0.7.0",
@@ -1,76 +1,86 @@
1
1
  ---
2
2
  name: mantle install
3
- description: Install a mantle consumer project. Preferred path consumes a landing-created launch session and scaffolds immediately via create-mantle. Manual fallback collects the exact create-mantle flags, scaffolds deterministically, then continues to provision. Use when the user pasted a launch command/session URL or when starting from an empty repo.
3
+ description: Install a Mantle consumer project from a landing-created launch command/session, then continue into deterministic provision. Use when the user pasted a Mantle launch command or session URL.
4
4
  when_to_invoke: |
5
- Empty repo + landing-page launch command/session URL, or empty repo + user wants to create a Mantle site without landing.
5
+ The user has a Mantle landing launch command/session URL and wants a new Mantle site scaffolded.
6
6
  ---
7
7
 
8
8
  # mantle install
9
9
 
10
- You're installing a mantle site for the user. If the user arrived from
11
- the landing launch flow, a short-lived launch session already carries
12
- the launch-critical values. Treat that session as the source of truth
13
- and scaffold first.
10
+ You are installing a Mantle site for the user. The normal path is:
14
11
 
15
- ## Ground truth
12
+ 1. Mantle landing asks the launch questions.
13
+ 2. Landing renders a short-lived launch session and `create-mantle`
14
+ command.
15
+ 3. You run that command from the parent directory where the project
16
+ should be created.
17
+ 4. You validate the generated repo, push it to the user's GitHub, then
18
+ continue with repo-local `mantle:provision`.
16
19
 
17
- `@aotter/mantle-*` exposes **exactly four declarative atoms** scoped to `cms.mantle.aotter.net/v1`, mapping 1-to-1 to Postgres primitives:
20
+ Do not rebuild the old interview harness in chat. If the user has no
21
+ landing-created launch command/session, send them back to Mantle landing
22
+ to create one. Manual flag-by-flag scaffolding is for maintainers
23
+ debugging the scaffolder, not the user-facing install UX.
24
+
25
+ ## Ground Truth
26
+
27
+ `@aotter/mantle-*` exposes exactly four declarative atoms scoped to
28
+ `cms.mantle.aotter.net/v1`, mapping 1-to-1 to Postgres primitives:
18
29
 
19
30
  | Atom | Postgres analog | External surface |
20
31
  |---|---|---|
21
- | **Schema** | `CREATE TABLE` | none (manipulated via View / Procedure) |
22
- | **View** | `CREATE VIEW` | auto-mounted at `GET /api/views/<name>` |
23
- | **Procedure** | `CREATE FUNCTION` | none directly; needs a Trigger to bind it |
24
- | **Trigger** | `CREATE TRIGGER` + cron + REST route + LISTEN/NOTIFY | binding atom — turns Procedures into HTTP / lifecycle / MCP surfaces |
32
+ | Schema | `CREATE TABLE` | none directly |
33
+ | View | `CREATE VIEW` | auto-mounted at `GET /api/views/<name>` |
34
+ | Procedure | `CREATE FUNCTION` | none directly |
35
+ | Trigger | `CREATE TRIGGER` + cron + REST route + LISTEN/NOTIFY | binding atom |
25
36
 
26
- Anything domain-shaped (Form, Membership, Workflow) is **composed in the consumer project** from these four plus user TypeScript. Full grammar reference: <https://raw.githubusercontent.com/aotter/mantle/develop/docs/design-atoms.md>.
37
+ Anything domain-shaped (Form, Membership, Workflow) is composed in the
38
+ consumer project from these four atoms plus user TypeScript. Full grammar
39
+ reference:
40
+ <https://raw.githubusercontent.com/aotter/mantle/develop/docs/design-atoms.md>.
27
41
 
28
42
  After `create-mantle` runs, the scaffold's ground truth lives in:
29
43
 
30
44
  | Path | Contents |
31
45
  |---|---|
32
- | `manifests/*.yaml` | Schemas / Views / Procedures / Triggers this archetype ships |
33
- | `src/mantleConfig.ts` | Site defaults, handler-ref registration, runtime bindings |
34
- | `src/handlers/` | Handler implementations (referenced from Procedures with `handler.kind: ref`) |
35
- | `src/.mantle/generated.*.ts` | Scaffolder-owned feature glue, regenerated from selected feature overlays |
36
- | `.mantle/features.json` | Scaffolder-owned receipt of selected source overlays |
37
- | `mantle/site.md` | Site semantic layer brand / voice / locales / futures / revisions |
38
- | `AGENTS.md` | Cross-tool agent entry; updates on every Mantle pass |
39
-
40
- Live introspection (run from project root):
41
-
42
- ```bash
43
- pnpm introspect # current manifest dump (atoms inventory)
44
- pnpm emit-openapi # generated HTTP surface
45
- pnpm emit-types # generated TS types
46
- pnpm validate # grammar + cross-ref check (preview phase by default).
47
- # For production-only checks, use
48
- # `pnpm validate --phase deploy` or `pnpm validate:deploy`.
49
- ```
50
-
51
- Diagnostics are structured JSON with `code` + `suggestion` fields — surface both verbatim, don't paraphrase.
46
+ | `manifests/*.yaml` | Schemas / Views / Procedures / Triggers |
47
+ | `src/mantleConfig.ts` | Site defaults, handler registration, runtime bindings |
48
+ | `src/handlers/` | Handler implementations |
49
+ | `src/.mantle/generated.*.ts` | Scaffolder-owned feature glue |
50
+ | `.mantle/features.json` | Selected feature overlays |
51
+ | `.mantle/launch-state.json` | Non-secret launch choices from landing |
52
+ | `AGENTS.md` | Cross-tool agent entry |
52
53
 
53
- ## Launch-session fast path
54
+ ## Accepted Inputs
54
55
 
55
- Use this path first when the prompt contains either:
56
+ Use the launch-session fast path when the prompt contains either:
56
57
 
57
- - a command shaped like `npx <tarball> launch --session <url-or-file>`, or
58
- - a launch session URL/file plus instructions to run `create-mantle launch`.
58
+ - a command shaped like `npx <tarball> launch --session <url-or-file>`;
59
+ - a command shaped like `npx <tarball> <archetype> ...` generated by
60
+ Mantle landing;
61
+ - a launch session URL/file plus instructions to run `create-mantle
62
+ launch`.
59
63
 
60
- The landing page already collected the launch-critical values and, when the GitHub App flow is present, supplied the GitHub admin/default-owner login. Treat those values as user-authorized for scaffolding. Do **not** run the pre-scaffold interview first.
64
+ Landing already collected the launch-critical values. Treat those values
65
+ as user-authorized for scaffolding. Do not ask content, voice, repo, or
66
+ locale questions again before scaffold.
61
67
 
62
- ### Session authority
68
+ The launch session/command authorizes only initial scaffold values:
63
69
 
64
- The launch session is an authorization artifact for initial scaffold values only:
70
+ - project name;
71
+ - brand / site name;
72
+ - archetype;
73
+ - theme and selected feature overlays;
74
+ - locales and canonical locale;
75
+ - GitHub owner / admin login;
76
+ - install description and summary.
65
77
 
66
- - It may authorize `project_name`, `brand`/site name, `archetype`, `theme`, `features`, `locales`, `github_owner` / `admin_github_login`, and repo intent.
67
- - It must be short-lived. If `create-mantle launch` reports an expired/invalid session, stop and send the user back to the landing page to create a new session.
68
- - It must not be copied into committed files, shell history snippets, issue comments, or handoff text. The scaffolder writes `.mantle/launch-state.json` with non-secret resumable metadata and intentionally redacts the raw session URL.
69
- - It does **not** authorize Cloudflare resources, provider billing, custom domains, or production secrets. Provision still asks the user to complete the Cloudflare Dashboard first deploy and GitHub OAuth App setup in their own accounts.
78
+ It does not authorize Cloudflare resources, provider billing, OAuth
79
+ secrets, custom domains, or production deploy.
70
80
 
71
- ### Fast-path preflight
81
+ ## Preflight
72
82
 
73
- Still verify the environment and target directory before running the command:
83
+ Run these before creating files:
74
84
 
75
85
  ```bash
76
86
  node --version # need >= 22
@@ -78,271 +88,141 @@ pnpm --version # need >= 9
78
88
  git --version
79
89
  ```
80
90
 
81
- Confirm that the current working directory is the parent directory where the new project should appear. If a child directory already exists with the session's project slug, stop; do not merge into an existing directory.
91
+ Confirm the current working directory is the parent directory where the
92
+ new project should appear. If the child project directory already exists,
93
+ stop and ask the user whether to choose another empty directory. Do not
94
+ merge a fresh scaffold into an existing project.
95
+
96
+ ## Scaffold
82
97
 
83
- Then run the launch command exactly as supplied, except for replacing an obvious local file path if the user gave you one out of band:
98
+ Run the launch command exactly as supplied. Do not rewrite values. Only
99
+ replace an obvious local file path if the user gave the file out of band.
100
+
101
+ Examples:
84
102
 
85
103
  ```bash
86
104
  npx <create-mantle-tarball> launch --session <session-url-or-file>
87
105
  ```
88
106
 
89
- `create-mantle launch` validates the session before filesystem writes, downloads the requested starter ref, scaffolds, runs `git init` and `pnpm install`, and prints RUN_NOTES JSON with a `launch` block.
107
+ ```bash
108
+ npx <create-mantle-tarball> <archetype> \
109
+ --project-name <project-name> \
110
+ --brand "<brand>" \
111
+ --description "<description>" \
112
+ --locales "<locale[,locale]>" \
113
+ --github-owner <github-owner> \
114
+ --admin-github-login <admin-login> \
115
+ --summary "<summary>"
116
+ ```
117
+
118
+ `create-mantle` validates the session before filesystem writes,
119
+ downloads the requested starter ref, merges `_common/` + `<archetype>/`
120
+ + feature overlays + theme overlays, fills placeholders, runs
121
+ `git init`, runs `pnpm install`, writes `.mantle/launch-state.json`, and
122
+ prints RUN_NOTES JSON.
90
123
 
91
124
  After it returns:
92
125
 
93
126
  1. Read RUN_NOTES.
94
127
  2. Read `.mantle/launch-state.json`.
95
- 3. Walk the ground-truth files listed above.
96
- 4. Continue from local validation and preview setup below.
97
-
98
- Do not ask content/voice questions before scaffold. The natural point for user conversation is after the site exists locally and, preferably, after provision has produced a working URL. If the user explicitly wants to revise content before provision, keep it to the same small adjustment window described later in this Skill.
128
+ 3. Walk the ground-truth files above.
129
+ 4. Continue with local validation.
99
130
 
100
- ## Preflight — before manual fallback
131
+ ## Local Validation
101
132
 
102
- Verify the environment can run the flow. Don't waste the user's time interviewing for a site we can't build:
133
+ From the generated project root:
103
134
 
104
135
  ```bash
105
- node --version # need ≥ 22 (starter `engines.node`)
106
- pnpm --version # need ≥ 9
107
- git --version # any recent
136
+ pnpm validate
137
+ pnpm typecheck
138
+ if node -e "process.exit(require('./package.json').scripts?.test ? 0 : 1)"; then
139
+ pnpm test
140
+ else
141
+ echo "No pnpm test script; skipping."
142
+ fi
108
143
  ```
109
144
 
110
- If any is missing or below the minimum, surface install hints once and stop until the user confirms tools are ready:
111
- - node ≥ 22: nvm (`nvm install 22 && nvm use 22`), Homebrew, or the official installer at nodejs.org
112
- - pnpm ≥ 9: `corepack enable && corepack prepare pnpm@latest --activate`, or `npm install -g pnpm@9`
113
- - git: system package manager (Homebrew on macOS, apt on Debian/Ubuntu, winget on Windows)
114
-
115
- Also confirm the current working directory is an appropriate parent directory for the new project, and that no child directory already exists with the authorized `<<PROJECT_NAME>>`. `create-mantle` writes into `./<<PROJECT_NAME>>`; collisions with pre-existing files are surprising and rarely what the user wanted.
116
-
117
- Do not proceed to manual fallback until preflight passes.
118
-
119
- ## Manual fallback
120
-
121
- Use this only when there is no valid launch session. The manual path is
122
- for development, recovery, or a user who did not start from landing. It
123
- is not the primary UX.
124
-
125
- Ask only for the values that the `create-mantle` command truly needs.
126
- Keep it one question at a time and confirm the exact values before
127
- running the command.
128
-
129
- ### Goal — what you must land before dispatch
130
-
131
- Listed in discovery order — purpose comes first, brand near the end. **Do not read this table as a top-down checklist to ask in order.** The order below mirrors how the interview should flow:
132
-
133
- | Value | For |
134
- |---|---|
135
- | **purpose / audience** | one-line description and locale choice |
136
- | **audience scope + locales** | `--locales` (count + first is canonical) |
137
- | **description** | `--description` — one-line site identity, agent-drafted in user's language |
138
- | **summary** | `--summary` — one-line install-moment marker, agent-drafted in user's language |
139
- | **brand** | `--brand` — proposed by you after purpose + audience texture is in; user picks or supplies their own |
140
- | **github identity** | `--github-owner` and optional `--admin-github-login` |
141
-
142
- Every value above must be set with the user's explicit confirmation
143
- before you dispatch. Do not guess from email, folder name, or the
144
- language of the conversation.
145
-
146
- ### Multi-round purpose discovery — start here, not with brand
147
-
148
- Open with **what's this site for** — not the brand name. Don't ask cold
149
- for a full brief. Ask one simple question, summarize back, then move to
150
- the next required value.
151
-
152
- User answers -> react -> ask the next missing value. One question per
153
- turn. Stop as soon as the command values are known.
154
-
155
- ### Stances (the few non-archetype rules)
156
-
157
- **Audience + locales — ask, don't infer.**
158
-
159
- Audience scope drives the locale choice and feeds Mantle. Ask the user explicitly who this site is for — is it a domestic audience (and if so, which country / region), or an international audience? Don't infer audience from the user's own writing language alone; a user writing to you in one language may be building for readers in another.
160
-
161
- - Domestic audience → propose monolingual in the audience's primary language. Confirm.
162
- - International audience → propose bilingual, canonical = the user's working language, secondary = the audience's language. Confirm.
163
- - Ambiguous (mixed signals, user not sure) → ask once: monolingual `<primary>` or bilingual `<primary>+<secondary>`?
164
- - Use Mantle's v0.1 locale subset: BCP 47 language + optional 2-letter region. The runtime canonicalizer rejects script subtags even though they are valid BCP 47 — map Traditional Chinese / `zh-Hant` to `zh-TW`, Simplified Chinese / `zh-Hans` to `zh-CN`, and otherwise use bare-language or `<lang>-<2-letter-region>`.
165
-
166
- **Description + summary — different roles, both agent-synthesized in the user's language.**
167
-
168
- These are CLI flags, not separate interview questions. They land in different places and serve different purposes:
169
-
170
- | Field | Lands in | Role |
171
- |---|---|---|
172
- | `description` | `mantle/site.md` frontmatter → `siteDefaults.description` → SEO `<meta description>` on every page | **Site brochure** — what the site *is* (perpetual). |
173
- | `summary` | `mantle/site.md` `revisions[0].summary` | **Changelog entry** — what *this install moment* did. Provision / extend / customize-design append their own later. |
174
-
175
- Don't write the same one-liner twice. `description` is a one-sentence site identity. `summary` is a one-line install-moment marker — terse, factual, often as short as "Initial scaffold." or "Site created from publication archetype." The site's actual identity already lives in `description`; `summary` is the timestamp's caption, not a second pitch.
176
-
177
- Show both drafts when you synthesize; user confirms or corrects.
178
-
179
- **Brand — propose last, never first.** Only after purpose + audience + voice texture has surfaced through the archetype probes. Then offer two paths: "Tell me a name, or I can propose 2-3 based on what you've described." If user picks the second, propose 2-3 with a one-line rationale each tied to what they actually said. Don't make the user invent a name cold; and don't propose a name before you have material to anchor the proposal in.
180
-
181
- **GitHub identity — factual, last.** Ask once near the end. Pure config; no elaboration needed.
182
-
183
- **Other observations — capture without pushing.** Emotional weight, dates that matter, things-not-to-touch, futures — let them surface naturally during the archetype probes. Don't checklist them. Mantle uses whatever you noticed; she doesn't need everything.
184
-
185
- ### Synthesize and confirm
145
+ Run deploy validation if the starter exposes it:
186
146
 
187
- Before running `create-mantle`, rehearse the install back to the user in their language. Translate technical tokens to something a non-engineer reads naturally — BCP 47 codes become the language's natural name in the user's language, config keys like `github_owner` become their everyday phrasing, archetype codenames become the site type's everyday meaning rather than the codeword.
188
-
189
- Surface `description` and `summary` as separate one-line drafts for the user to nod or tweak, since they land in different places (SEO meta vs. revisions log).
190
-
191
- ## If the archetype is roadmap
192
-
193
- If the archetype hint says `status: roadmap`, follow its **Refuse path** — the hint specifies the framing (honest "not yet" → two holding paths → write intent into `mantle/site.md` `futures:`). Move to the holding path the user picks. Skip the rest of the interview steps below.
194
-
195
- ## When to act
196
-
197
- ### Why running `create-mantle` is a destructive action under Auto Mode
198
-
199
- Invoking the `create-mantle` release tarball is **not low-risk work**. The command writes the user's site identity — brand, audience, locale, description — into `mantle/site.md` and `src/mantleConfig.ts` `siteDefaults`, then runs `git init` and `pnpm install`. Those values drive, perpetually:
200
-
201
- - every page's SEO `<meta description>`
202
- - locale routing for the entire site (canonical + redirects)
203
- - the persistent site notes in `mantle/site.md`
204
- - 22 starter files' `{{PLACEHOLDER}}` substitution
205
- - `revisions[0]` — the permanent install-moment entry in the changelog
206
-
207
- Wrong values ship into the user's first-load impression and cannot be cleanly walked back without wiping the scaffold and re-scaffolding from empty.
208
-
209
- **Auto Mode's contract has four clauses. Clauses 1–3 say "execute immediately / minimize interruptions / prefer action". Clause 4 is the carve-out: do not take overly destructive actions without authorization.** This Skill classifies the scaffolder invocation under clause 4. Each create-time value must be either carried by a valid landing session or explicitly confirmed by the user. Auto-derivation — from the user's email, the current working directory's name, the archetype query, the theme query, or "the locale of the message the user wrote to me" — is **not** authorization. That kind of inference is what Auto Mode's clauses 1–3 want for low-risk work. This Skill specifically does not accept it for scaffolding values.
210
-
211
- If you have not had a turn where the user looked at the exact value and replied affirmatively (or supplied a replacement), the value is unauthorized.
212
-
213
- Launch-session exception: when the user hands you a landing-created
214
- `create-mantle launch --session ...` command, the landing session is the
215
- explicit authorization for those scaffold values. Your job is to
216
- validate/run it, not to repeat manual fallback before scaffold. If the
217
- session is invalid or expired, stop and ask the user to regenerate it
218
- from the landing page; do not infer replacement values.
219
-
220
- ### Prerequisites — each parameter must be user-authorized before invocation
221
-
222
- Same discovery order as the Goal table above — purpose first, brand later. The order matters because it reflects the interview shape, not arbitrary alphabetization.
223
-
224
- | Value | Authorized when |
225
- |---|---|
226
- | **purpose / audience** | enough texture to summarize the site intent, not inferred |
227
- | **audience scope** | user explicitly stated domestic/international audience and languages |
228
- | **locales** | derived from audience scope; user nodded on the resulting BCP 47 list |
229
- | **description** | agent-drafted in user's language; user nodded on the exact one-liner |
230
- | **summary** | agent-drafted in user's language; user nodded on the exact one-liner |
231
- | **brand** | user picked one, supplied one, or accepted your proposal |
232
- | **project-name** | lowercase-hyphenated slug shown to user and confirmed |
233
- | **github owner** | user explicitly stated their GitHub login/org, not derived from email |
234
-
235
- If any value is unauthorized — including auto-derivation that "looks reasonable" — the work is still in the interview. Return there. Step 1 below IS the rehearsal back to the user in their language; it is not the moment you collect authorization for unfilled values.
236
-
237
- 1. **Confirm the synthesized draft.** User accepts or corrects.
238
-
239
- 2. **Run `create-mantle`.** Use the release tarball URL supplied by the
240
- landing page, release notes, or Mantle starter docs. Fill flags only
241
- from confirmed values:
242
-
243
- ```bash
244
- npx <create-mantle-tarball> <archetype> \
245
- --project-name <project-name> \
246
- --brand "<brand>" \
247
- --description "<description>" \
248
- --locales "<locale[,locale]>" \
249
- --github-owner <github-owner> \
250
- --admin-github-login <admin-login> \
251
- --summary "<summary>" \
252
- [--theme <theme>] \
253
- [--feature <feature>]
254
- ```
255
-
256
- Do not invent `--theme` or `--feature` flags. They must come from
257
- landing, a starter doc, or an explicit user request.
258
-
259
- The CLI fetches `sources.json` at runtime from the requested starter ref (`--ref` / `--starter-ref`; release commands should pin a tag or explicit ref), downloads the starters tarball, merges `_common/` + `<archetype>/` + selected feature overlays + (optional) `themes/<theme>/`, fills `{{PLACEHOLDER}}` macros, renames `.template` files, runs `git init` and `pnpm install`. RUN_NOTES JSON arrives on stdout, including `features` when overlays were selected.
260
-
261
- 3. **Read the RUN_NOTES.** The `files_written` list is your scaffold inventory. If `features` is non-empty, read `.mantle/features.json` and the generated `src/.mantle/generated.*.ts` glue before deciding anything else. Walk the ground-truth files — at minimum `manifests/`, `src/mantleConfig.ts`, `mantle/site.md` — before deciding anything else.
262
-
263
- 4. **Adjustment window** (optional, see § below). Only if the interview surfaced a concrete deletion or single-field gap. Always `pnpm validate` after edit.
264
-
265
- 5. **Validate locally:**
266
-
267
- ```bash
268
- pnpm validate
269
- pnpm typecheck
270
- ```
271
-
272
- `pnpm validate` runs in the **preview phase** by default — grammar + cross-Schema checks only. Anything non-zero → surface `code` + `suggestion` verbatim.
273
-
274
- The deploy-phase variant (`pnpm validate --phase deploy`, or `pnpm validate:deploy` if the starter ships that script) is safe to run on a fresh scaffold. It must not block on unfinished prose in `mantle/site.md`.
275
-
276
- **Then set up `.dev.vars` so `pnpm dev` works.** Starters that ship `.dev.vars.example` (publication / transaction / intake / presence) require a real `BETTER_AUTH_SECRET` before `pnpm dev` — the worker returns `auth_not_configured` on every request until it's filled. Copy the file, generate a value, and write it in:
277
-
278
- ```bash
279
- [ -f .dev.vars.example ] && cp .dev.vars.example .dev.vars
280
- # Generate, then paste the output into the BETTER_AUTH_SECRET= line of .dev.vars
281
- openssl rand -hex 32
282
- ```
283
-
284
- This secret is **local only** — `.dev.vars` is gitignored and never reaches Cloudflare. Production's secret is minted separately by `provision:up` (see the provision Skill). Tell the user this distinction explicitly so they don't try to reuse the local value or expect it to follow them to prod.
147
+ ```bash
148
+ if node -e "process.exit(require('./package.json').scripts?.['validate:deploy'] ? 0 : 1)"; then
149
+ pnpm validate:deploy
150
+ else
151
+ pnpm validate --phase deploy
152
+ fi
153
+ ```
285
154
 
286
- **Start local preview and explain empty-content states.** Run `pnpm dev` after validation and `.dev.vars` setup, then probe the public home route, `/admin`, and any obvious list/API route the archetype exposes. A fresh scaffold may legitimately return 404 on `/` or locale home routes (`/en`, `/zh-TW`, etc.) because D1/KV has no `home` page yet. Treat that as an empty-site state, not a broken install.
155
+ Diagnostics are structured JSON with `code` and `suggestion` fields.
156
+ Surface both verbatim when validation fails.
287
157
 
288
- When the public home route is empty/404, tell the user plainly: "the worker is running; the public homepage has no content yet." Then ask whether they want a **local preview seed** so they can see the homepage and, for post-shaped archetypes, a couple of sample posts in the browser. Do not run generic test fixtures or seed content without explicit consent. If they say yes, keep the seed preview-only and local: use user-approved copy from the interview or step 6 drafting, make it clear it is not production content, and do not commit local DB/KV artifacts or `.dev.vars`. If they say no, continue with the install flow; production content can be created later through `/admin` or MCP.
158
+ ## Local Preview
289
159
 
290
- 6. **Keep `mantle/site.md` deterministic.**
160
+ If the user wants to see the scaffold locally:
291
161
 
292
- The scaffold already records the launch description and open
293
- provision items. Do not block first deploy on polishing voice, welcome
294
- copy, or editor prompts. If the user gave concrete notes, add a short
295
- `## history` paragraph and validate. Otherwise leave content work for
296
- the post-deploy coding/content agent.
162
+ ```bash
163
+ [ -f .dev.vars.example ] && cp .dev.vars.example .dev.vars
164
+ openssl rand -hex 32
165
+ pnpm dev
166
+ ```
297
167
 
298
- 7. **Run deploy validation.**
168
+ Paste the generated random value into `.dev.vars` as
169
+ `BETTER_AUTH_SECRET`. This secret is local only and must not be reused
170
+ for production. Use `localhost`, not `127.0.0.1`, in local examples.
299
171
 
300
- ```bash
301
- pnpm validate:deploy
302
- ```
172
+ A fresh scaffold may legitimately return 404 for the public home route
173
+ because production content is not seeded yet. Treat that as an empty-site
174
+ state, not a failed install, when validation and admin routes are alive.
303
175
 
304
- If the project only exposes the CLI command, run `pnpm validate --phase deploy`. This is the readiness gate before handing off to provision.
176
+ ## Provision Handoff
305
177
 
306
- 8. **Commit.** If step 4 produced an adjustment, that's its own commit. Then the main commit: `mantle: deterministic scaffold`.
178
+ Commit the deterministic scaffold:
307
179
 
308
- 9. **Continue to provision — don't push a URL onto the user.** Provision
309
- is the next phase in the same conversation. Read
310
- `.mantle/launch-state.json` for GitHub owner/admin metadata when it
311
- exists, then use the repo-local `mantle:provision` skill generated
312
- into the scaffold. If it is missing, fetch
313
- `https://raw.githubusercontent.com/aotter/mantle/develop/skills/provision/SKILL.md`.
180
+ ```bash
181
+ git status --short
182
+ git add .
183
+ git commit -m "mantle: deterministic scaffold"
184
+ ```
314
185
 
315
- The current provision shape is deterministic first: create/push the private GitHub repo, guide the user through Cloudflare's GitHub-backed first deploy, then use Wrangler to take over follow-up bindings/secrets/migrations. If GitHub CLI auth is invalid, the user's next involvement is re-auth first; after they reply that it is fixed, re-run `gh auth status` and then continue provision. Don't promise production-readiness until provision completes and a second agent connects through MCP.
186
+ Then continue in the same conversation with the repo-local provision
187
+ skill:
316
188
 
317
- ## Adjustment window — between scaffold and provision
189
+ ```text
190
+ .agent/skills/mantle-provision/SKILL.md
191
+ ```
318
192
 
319
- A permitted modification turn after `create-mantle` returns and before provision starts. Small concrete edits to match what the user said.
193
+ The current provision shape is provider-first:
320
194
 
321
- ### In scope
195
+ 1. The agent creates/pushes the user's private GitHub repo.
196
+ 2. The user creates the first Cloudflare Worker deploy from that GitHub
197
+ repo in Cloudflare Dashboard.
198
+ 3. The user reports the Worker URL.
199
+ 4. The user creates the per-site GitHub OAuth App.
200
+ 5. The agent runs `pnpm run provision:up` to write non-secret config,
201
+ set Worker secrets with Wrangler, commit, push, redeploy, and smoke
202
+ test.
322
203
 
323
- | Action | Why |
324
- |---|---|
325
- | Delete a manifest the user explicitly said they don't need | Honesty over inertia |
326
- | Add a single field to an existing Schema, from a concrete interview signal | Small, validated, recoverable |
327
- | Edit `src/mantleConfig.ts` site defaults beyond what `create-mantle` set | Site-shape, fits Mantle's surface |
328
- | Tweak `src/theme/` tokens if the user gave a strong visual register | Prefer deferring to the customize-design Skill unless explicit |
204
+ Do not stop at "here is a URL". Walk the provider steps with the user
205
+ until the deployed Worker, GitHub owner sign-in, and Staff MCP endpoint
206
+ are verified.
329
207
 
330
- ### Out of scope
208
+ ## Small Adjustments
331
209
 
332
- | Action | Route to |
333
- |---|---|
334
- | Add a new Schema (beyond single-field tweak), View, Procedure, or Trigger | The extend skill |
335
- | Substantial theme work (template fork, layout reshape) | The customize-design skill, after deploy |
336
- | Anything touching DRAFT grammar keys | Never at install — grammar locked at v0.1 |
210
+ Before provision, only make small deterministic edits that come directly
211
+ from the landing answers or the user's explicit request:
337
212
 
338
- ### Discipline
213
+ - delete an unused manifest;
214
+ - add one clearly requested field to an existing Schema;
215
+ - correct site defaults in `src/mantleConfig.ts`;
216
+ - fix a typo in `AGENTS.md`.
339
217
 
340
- - `pnpm validate` after every edit. Non-clean tree never advances to provision.
341
- - Show the diff before applying. A deleted manifest deserves a one-line confirm.
342
- - Don't speculate. "I think you might also want X" is generation, not interview.
218
+ Run `pnpm validate` after every edit. Route larger feature work to
219
+ `mantle:development` after production provision.
343
220
 
344
221
  ## Don't
345
222
 
346
- - Don't write into `src/theme.default/` or any "system-looking" path during install — design changes happen after deploy via the customize-design skill.
347
- - Don't keep speaking after the handoff to provision the handoff IS the end of this Skill.
348
- - Don't echo the same specific user detail across every generated note — let each section have its own job.
223
+ - Don't resurrect the manual interview / prompt-composition harness.
224
+ - Don't block first deploy on polishing prose or writing a site letter.
225
+ - Don't ask for a Cloudflare API token in the base first-run path.
226
+ - Don't commit provider secrets.
227
+ - Don't use `127.0.0.1` in OAuth callback examples; use `localhost`
228
+ locally and the real Worker URL in production.
@@ -26,6 +26,10 @@ The base flow is deterministic first, provider-browser second:
26
26
  Mantle landing is not the executor. It provides launch context and a
27
27
  handoff; provider authority stays with the user and their coding agent.
28
28
 
29
+ Do deterministic work before interrupting the user. When provider UI is
30
+ required, give one exact browser task at a time: link, button path,
31
+ expected result, and what value the user should report back.
32
+
29
33
  ## End State
30
34
 
31
35
  - The scaffold is committed and pushed to the user's private GitHub repo.
@@ -34,8 +38,7 @@ handoff; provider authority stays with the user and their coding agent.
34
38
  `ADMIN_GITHUB_LOGIN`, and the correct Worker name.
35
39
  - Worker secrets are set: `GITHUB_CLIENT_SECRET`,
36
40
  `BETTER_AUTH_SECRET`, and optional feature/provider secrets.
37
- - `mantle/site.md` frontmatter `site_url:` and `AGENTS.md` `Public
38
- site:` point at the deployed Worker URL.
41
+ - `AGENTS.md` `Public site:` points at the deployed Worker URL.
39
42
  - Staff MCP and browser admin sign-in are ready to test.
40
43
  - Operator setup URL is ready to hand to the owner:
41
44
  `https://mantle.tools/connect?site=<url-encoded-worker-url>`.
@@ -84,7 +87,13 @@ ask the user to switch/login before creating the repo.
84
87
  2. Create a private GitHub repo in the selected owner, add the remote,
85
88
  commit the scaffold, and push. Use the user's GitHub auth context.
86
89
 
87
- 3. Hand the user directly to Cloudflare's Git import path:
90
+ 3. Print the deterministic browser plan:
91
+
92
+ ```bash
93
+ pnpm run provision:plan
94
+ ```
95
+
96
+ 4. Hand the user directly to Cloudflare's Git import path:
88
97
 
89
98
  ```text
90
99
  https://dash.cloudflare.com/?to=%2F%3Aaccount%2Fworkers-and-pages
@@ -92,18 +101,19 @@ https://dash.cloudflare.com/?to=%2F%3Aaccount%2Fworkers-and-pages
92
101
 
93
102
  Ask them to create a Worker from the pushed GitHub repo, keep the Worker
94
103
  name equal to `wrangler.toml` `name`, wait for deploy, and send back the
95
- live `*.workers.dev` URL.
104
+ live `*.workers.dev` URL. Ask for the Worker name only if Cloudflare
105
+ forced a name different from the repo/project name.
96
106
 
97
- 4. Print the deterministic plan:
107
+ 5. After the Worker URL is known, print the worker-specific plan:
98
108
 
99
109
  ```bash
100
- pnpm run provision:plan
110
+ pnpm run provision:plan -- --worker-url <worker-url>
101
111
  ```
102
112
 
103
113
  Read only the values needed for the current project. Do not dump
104
114
  internal notes or placeholder syntax onto a non-coder.
105
115
 
106
- 5. Ask the user to create the per-site GitHub OAuth App after the Worker
116
+ 6. Ask the user to create the per-site GitHub OAuth App after the Worker
107
117
  URL is known:
108
118
 
109
119
  - Homepage URL: `<worker-url>`
@@ -113,7 +123,7 @@ internal notes or placeholder syntax onto a non-coder.
113
123
  Ask for the Client ID in chat. Keep the Client Secret out of chat and
114
124
  pass it through the hidden shell prompt below.
115
125
 
116
- 6. Authorize Wrangler and apply provision:
126
+ 7. Authorize Wrangler and apply provision:
117
127
 
118
128
  ```bash
119
129
  pnpm exec wrangler login
@@ -122,13 +132,20 @@ pnpm run provision:up -- --worker-url <worker-url> --github-username <gh-login>
122
132
  unset GITHUB_CLIENT_SECRET
123
133
  ```
124
134
 
125
- 7. Commit and push generated non-secret outputs:
135
+ 8. Commit and push generated non-secret outputs:
126
136
 
127
137
  ```bash
128
- git status --short -- wrangler.toml src/mantleConfig.ts mantle/site.md AGENTS.md
138
+ git status --short -- wrangler.toml src/mantleConfig.ts AGENTS.md
139
+ git add wrangler.toml src/mantleConfig.ts AGENTS.md
140
+ git commit -m "mantle: wire production provision"
141
+ git push
129
142
  ```
130
143
 
131
- 8. Smoke test:
144
+ Wait for Cloudflare Workers Builds to redeploy from the pushed commit. If
145
+ the dashboard build is unavailable, run `pnpm deploy` from this repo as a
146
+ fallback and explain that fallback to the user.
147
+
148
+ 9. Smoke test:
132
149
 
133
150
  - public home route;
134
151
  - `/admin/sign-in`;
@@ -136,6 +153,10 @@ git status --short -- wrangler.toml src/mantleConfig.ts mantle/site.md AGENTS.md
136
153
  - `/mcp/staff` with an agent client when available;
137
154
  - a starter-specific core workflow.
138
155
 
156
+ A fresh site may have no public home content yet. A 404 on the locale
157
+ homepage is acceptable only after the Worker boots, `/admin/sign-in`
158
+ loads, and auth/MCP boundaries behave correctly.
159
+
139
160
  ## Feature Overlays
140
161
 
141
162
  If `.mantle/features.json` lists features, run the repo-local feature
@@ -161,8 +182,8 @@ language:
161
182
  - What changed locally and what was committed.
162
183
  - Any intentionally deferred feature setup.
163
184
 
164
- Point future agents at `mantle/site.md`, `AGENTS.md`, and the repo-local
165
- `.agent/skills/` directory.
185
+ Point future agents at `AGENTS.md`, `.mantle/launch-state.json`, and the
186
+ repo-local `.agent/skills/` directory.
166
187
 
167
188
  ## Diagnostics
168
189