@oneie/claude 0.1.0
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.
- package/.claude-plugin/plugin.json +16 -0
- package/.mcp.json +12 -0
- package/README.md +204 -0
- package/agents/w1-recon.md +102 -0
- package/agents/w2-decide.md +164 -0
- package/agents/w3-edit.md +91 -0
- package/agents/w4-verify.md +416 -0
- package/commands/browser.md +55 -0
- package/commands/cc-connect.md +67 -0
- package/commands/claw.md +135 -0
- package/commands/close.md +143 -0
- package/commands/create.md +78 -0
- package/commands/deploy.md +415 -0
- package/commands/do-autonomous.md +80 -0
- package/commands/do-improve.md +51 -0
- package/commands/do-show.md +89 -0
- package/commands/do.md +226 -0
- package/commands/improve.md +99 -0
- package/commands/kill.md +45 -0
- package/commands/release.md +144 -0
- package/commands/see.md +161 -0
- package/commands/setup.md +75 -0
- package/commands/sync.md +185 -0
- package/hooks/hooks.json +90 -0
- package/hooks/lib/signal.sh +28 -0
- package/hooks/scripts/design-check.sh +83 -0
- package/hooks/scripts/post-edit-check.sh +32 -0
- package/hooks/scripts/session-end-verify.sh +51 -0
- package/hooks/scripts/session-start.sh +88 -0
- package/hooks/scripts/stop-reflect.sh +95 -0
- package/hooks/scripts/sync-todo-docs.sh +46 -0
- package/hooks/scripts/task-complete-verify.sh +52 -0
- package/hooks/scripts/tool-signal.sh +48 -0
- package/package.json +33 -0
- package/rules/api.md +50 -0
- package/rules/astro.md +206 -0
- package/rules/design.md +221 -0
- package/rules/documentation.md +218 -0
- package/rules/engine.md +297 -0
- package/rules/react.md +137 -0
- package/rules/ui.md +82 -0
- package/scripts/cc-connect.sh +345 -0
- package/scripts/do-analyze.sh +42 -0
- package/scripts/do-folder.sh +63 -0
- package/scripts/do-prove.sh +51 -0
- package/scripts/do-reconcile.sh +28 -0
- package/scripts/do-smoke.sh +60 -0
- package/scripts/do-survey.sh +30 -0
- package/scripts/do-tier.sh +43 -0
- package/skills/build/SKILL.md +52 -0
- package/skills/cloudflare/SKILL.md +503 -0
- package/skills/dev/SKILL.md +58 -0
- package/skills/do/SKILL.md +24 -0
- package/skills/oneie/SKILL.md +51 -0
- package/skills/perf/SKILL.md +45 -0
- package/skills/signal/SKILL.md +108 -0
- package/skills/sui/SKILL.md +441 -0
- package/skills/tutorial/SKILL.md +96 -0
- package/skills/typecheck/SKILL.md +66 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# /close
|
|
2
|
+
|
|
3
|
+
**Skills:** `/signal` (4-outcome grammar: result / timeout / dissolved / failure) · `/typedb` (mark-dims, rubric scoring, write completion)
|
|
4
|
+
|
|
5
|
+
Mark a result — close the signal loop.
|
|
6
|
+
|
|
7
|
+
## Flags
|
|
8
|
+
|
|
9
|
+
| Flag | Signal | Outcome | Loop |
|
|
10
|
+
|------|--------|---------|------|
|
|
11
|
+
| `<task-id>` | mark() | result — chain strengthens | L2 |
|
|
12
|
+
| `<task-id> --fail` | warn(1) | failure — full warn, chain breaks | L2 |
|
|
13
|
+
| `<task-id> --dissolved` | warn(0.5) | dissolved — mild warn | L2 |
|
|
14
|
+
| `<task-id> --timeout` | neutral | timeout — slow, not bad | L2 |
|
|
15
|
+
| *(no arg)* | mark() + tick | session report — record all outcomes | L2, L6 |
|
|
16
|
+
| `--todo <slug> --wave N` | emit `do:close` | wave close (soft gate) — append learnings.md; next wave proceeds if skipped | L1, L6 |
|
|
17
|
+
| `--todo <slug> --cycle N` | emit `do:close` | cycle close (**hard gate**) — verify learnings grew; block next cycle if missing | L1, L2, L6 |
|
|
18
|
+
| `--todo <slug> --cycle N --wave N` | emit `do:close` | specific wave of a cycle — used for re-running a failed close | L1 |
|
|
19
|
+
|
|
20
|
+
## Routing
|
|
21
|
+
|
|
22
|
+
`/close` maps to `mark()` or `warn()` — the human emitting the Four Outcomes
|
|
23
|
+
signal back into the substrate. Rule 1 (Closed Loop): every signal must close.
|
|
24
|
+
Without `/close`, paths cannot learn.
|
|
25
|
+
|
|
26
|
+
## Four Outcomes Reference (smoke)
|
|
27
|
+
|
|
28
|
+
The four flags map 1:1 to the Four Outcomes from routing.md:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
result → /close <id> mark() strength++ chain strengthens
|
|
32
|
+
timeout → /close <id> --timeout neutral no change chain continues
|
|
33
|
+
dissolved → /close <id> --dissolved warn(0.5) resist+=0.5 mild — path missing
|
|
34
|
+
failure → /close <id> --fail warn(1) resist+=1 full — agent failed
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Every possible outcome has a corresponding `/close` flag. No outcome goes unmarked.
|
|
38
|
+
This is Rule 1 enforced at the human boundary.
|
|
39
|
+
|
|
40
|
+
## Steps
|
|
41
|
+
|
|
42
|
+
### `<task-id>` — success
|
|
43
|
+
|
|
44
|
+
1. Run W4 gate — score code rubric (each dimension 0–1, composite ≥ 0.65 to close):
|
|
45
|
+
- **security**: no injections, no secrets, all API routes validated
|
|
46
|
+
- **stability**: tests pass, zero type errors, every handler closes its loop
|
|
47
|
+
- **simplicity**: files focused, functions ≤ 20 lines, no ceremony beyond scope
|
|
48
|
+
- **speed**: Lighthouse held, bundle ≤ W0, tokens lean, cache hit ≥ 80%
|
|
49
|
+
- composite: `0.35·security + 0.30·stability + 0.25·simplicity + 0.10·speed`
|
|
50
|
+
2. POST `http://localhost:4321/api/tasks/{id}/complete`
|
|
51
|
+
3. POST `http://localhost:4321/api/loop/mark-dims` with `{ security, stability, simplicity, speed }`:
|
|
52
|
+
- Dim ≥ 0.5 → mark() on that dimension path (strength++)
|
|
53
|
+
- Dim < 0.5 → warn() on that dimension path (resistance++)
|
|
54
|
+
4. Self-checkoff: update task checkbox in the TODO file `[ ]` → `[x]`
|
|
55
|
+
5. **Emit feedback signal** — POST `http://localhost:4321/api/signal`:
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"receiver": "loop:feedback",
|
|
59
|
+
"data": {
|
|
60
|
+
"tags": ["<task-tags>"],
|
|
61
|
+
"strength": "<composite 0–1>",
|
|
62
|
+
"content": {
|
|
63
|
+
"task_id": "<id>",
|
|
64
|
+
"rubric": { "security": X, "stability": Y, "simplicity": Z, "speed": W },
|
|
65
|
+
"composite": N,
|
|
66
|
+
"velocity": "±N",
|
|
67
|
+
"outcome": "result"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
This is the return-path pheromone. Future agents with matching tags follow this trail.
|
|
73
|
+
`composite >= 0.65` → mark each tag path. `composite < 0.65` → warn(0.5) each tag path.
|
|
74
|
+
6. Report unlocked tasks (tasks where this was in their `blocks` list)
|
|
75
|
+
7. **Propagate to docs** — auto-edit when triggers from the cycle's `.w2-doc-plan.json` match. Zero agent spawns; all bash + Edit tool.
|
|
76
|
+
|
|
77
|
+
| Trigger from W3 diff | Target | Edit |
|
|
78
|
+
|---|---|---|
|
|
79
|
+
| new MCP / CLI / SDK / API export | root `README.md` § public surface | append row |
|
|
80
|
+
| new component family or directory | nearest `one.ie/web/src/components/*/CLAUDE.md` | append section |
|
|
81
|
+
| 6-dim, L1-L8, or locked-rule change | root `CLAUDE.md` | edit relevant section |
|
|
82
|
+
| feature doc named in plan `source_of_truth` | feature doc | sync verb/component/endpoint counts |
|
|
83
|
+
| rename across W3 | every `.md` containing old name (W4 stale-name list) | already done by W3 grep |
|
|
84
|
+
|
|
85
|
+
For each trigger, the bash dispatcher reads the W3 diff, locates the anchor in the target doc, and applies the Edit tool inline. Anchor mismatch → log to `docs/improvements.md` for manual W2 next cycle (no halt).
|
|
86
|
+
|
|
87
|
+
8. Report:
|
|
88
|
+
```
|
|
89
|
+
mark(+5) on <from>→<to>
|
|
90
|
+
Rubric: security=X stability=Y simplicity=Z speed=W composite=N velocity=±N
|
|
91
|
+
Feedback: signal emitted → loop:feedback tags=[<tags>] strength=N
|
|
92
|
+
Propagate: docs=N edited triggers=[<codes>] manual=N
|
|
93
|
+
Unlocked: N tasks
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### `--fail`
|
|
97
|
+
|
|
98
|
+
1. POST `http://localhost:4321/api/tasks/{id}/complete` with `{ failed: true }`
|
|
99
|
+
2. warn(1) — resistance += 1 on the path. Full failure, chain breaks.
|
|
100
|
+
3. Report:
|
|
101
|
+
```
|
|
102
|
+
warn(1) on <from>→<to>
|
|
103
|
+
Full failure — resistance++. Chain breaks.
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### `--dissolved`
|
|
107
|
+
|
|
108
|
+
1. POST `http://localhost:4321/api/tasks/{id}/complete` with `{ dissolved: true }`
|
|
109
|
+
2. warn(0.5) — resistance += 0.5. Mild warn — path doesn't exist yet.
|
|
110
|
+
3. Report:
|
|
111
|
+
```
|
|
112
|
+
warn(0.5) on <from>→<to>
|
|
113
|
+
Dissolved — missing unit/capability. Mild warn. Chain breaks.
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### `--timeout`
|
|
117
|
+
|
|
118
|
+
1. POST `http://localhost:4321/api/tasks/{id}/complete` with `{ timeout: true }`
|
|
119
|
+
2. No mark, no warn — neutral. Slow, not bad.
|
|
120
|
+
3. Report:
|
|
121
|
+
```
|
|
122
|
+
neutral on <from>→<to>
|
|
123
|
+
Timeout — slow, not bad. Chain continues.
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### *(no arg)* — session report
|
|
127
|
+
|
|
128
|
+
1. Read git diff: `git diff --stat HEAD` — what changed this session
|
|
129
|
+
2. For each completed task this session: POST `/api/tasks/{id}/complete`
|
|
130
|
+
3. POST `http://localhost:4321/api/tick` — process accumulated pheromone
|
|
131
|
+
4. Report session outcomes (numbers first):
|
|
132
|
+
```
|
|
133
|
+
Session: N tasks completed M tests K commits
|
|
134
|
+
Pheromone: N marks M warns this session
|
|
135
|
+
Rubric: security=X stability=Y simplicity=Z speed=W composite=N velocity=±N (session averages)
|
|
136
|
+
Hardened: N highways promoted to permanent (L6)
|
|
137
|
+
Frontiers: N new unexplored clusters (L7)
|
|
138
|
+
Next: 1. <task> priority=N 2. <task> priority=M 3. <task> priority=K
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
*`/close` is `mark()` made human-readable. Every outcome must close its loop.*
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# /create
|
|
2
|
+
|
|
3
|
+
**Skills:** `/typedb` (write new entity) · `/signal` (emit creation, `ui:*` or `cli:create:*`)
|
|
4
|
+
|
|
5
|
+
Emit a new entity into the substrate.
|
|
6
|
+
|
|
7
|
+
## Nouns
|
|
8
|
+
|
|
9
|
+
| Noun | What | Loop |
|
|
10
|
+
|------|------|------|
|
|
11
|
+
| `task <name> [--tags T] [--weight W]` | Atomic task into TypeDB via API | L1 |
|
|
12
|
+
| `todo <source-doc?>` | TODO from template (Haiku extract + wave structure) | L1 |
|
|
13
|
+
| `agent <markdown-file>` | Parse agent.md frontmatter → TypeDB unit + skills | L1 |
|
|
14
|
+
| `signal <receiver> <data>` | Ad-hoc signal emission for testing | L1 |
|
|
15
|
+
|
|
16
|
+
## Routing
|
|
17
|
+
|
|
18
|
+
`/create` maps to `send()` — emit new entity or signal into the substrate.
|
|
19
|
+
Every noun writes to TypeDB and/or the in-memory queue.
|
|
20
|
+
|
|
21
|
+
| Noun | Primitive | Destination |
|
|
22
|
+
|------|-----------|-------------|
|
|
23
|
+
| task | `send()` | POST `/api/tasks` |
|
|
24
|
+
| todo | `send()` | Write `docs/{name}-todo.md` + POST `/api/tasks/sync` |
|
|
25
|
+
| agent | `send()` | `agent-md.ts` → TypeDB unit + skills + capabilities |
|
|
26
|
+
| signal | `send()` | POST `/api/signal` |
|
|
27
|
+
|
|
28
|
+
## Steps
|
|
29
|
+
|
|
30
|
+
### task
|
|
31
|
+
|
|
32
|
+
1. Parse `$ARGUMENTS` into task fields:
|
|
33
|
+
- id (kebab-case from name), name
|
|
34
|
+
- value: critical / high / medium
|
|
35
|
+
- phase: C1-C7
|
|
36
|
+
- persona: ceo / dev / investor / gamer / kid / freelancer / agent
|
|
37
|
+
- tags (space-separated), blocks (other task IDs), exit condition
|
|
38
|
+
2. Compute priority: value + phase + persona + blocking weight (max 115, min 35)
|
|
39
|
+
3. POST to `http://localhost:4321/api/tasks` with JSON body
|
|
40
|
+
4. Confirm created task: id, name, priority score + formula, tags
|
|
41
|
+
5. Suggest `/see tasks` to view ranked list
|
|
42
|
+
|
|
43
|
+
### todo
|
|
44
|
+
|
|
45
|
+
1. Resolve source doc from `$ARGUMENTS`: try full path → `docs/$ARGUMENTS` → `docs/$ARGUMENTS.md`
|
|
46
|
+
2. Run Haiku one-shot to extract raw tasks (~$0.004):
|
|
47
|
+
- Read doc, extract actionable items as checkbox tasks with metadata
|
|
48
|
+
3. Load base context: `one/dictionary.md`, `one/rubrics.md`, `one/template-todo.md`
|
|
49
|
+
4. Promote raw tasks into full wave template:
|
|
50
|
+
- Group by cycles (Wire → Prove → Grow) and waves (W1=Haiku, W2=Opus, W3=Sonnet, W4=Sonnet)
|
|
51
|
+
- Assign full metadata per task: id, value, effort, phase, persona, blocks, exit, tags
|
|
52
|
+
- Include: routing diagram, schema reference, wave structure, rubric scoring in W4, self-checkoff
|
|
53
|
+
5. Write `docs/{docname}-todo.md`
|
|
54
|
+
6. Verify: all tasks have 7 metadata fields, blocks references are valid, exit conditions are verifiable
|
|
55
|
+
7. Report: cycle count, tasks per cycle, critical path, cost estimate
|
|
56
|
+
|
|
57
|
+
### agent
|
|
58
|
+
|
|
59
|
+
1. Read `<markdown-file>` (frontmatter: name, model, channels, group, skills, sensitivity)
|
|
60
|
+
2. Parse via `src/engine/agent-md.ts` → `AgentSpec`
|
|
61
|
+
3. Sync to TypeDB:
|
|
62
|
+
- Unit: uid, name, model, system-prompt, tags
|
|
63
|
+
- Skills: skill-id, name, price, tags per skill
|
|
64
|
+
- Capabilities: (provider: unit, offered: skill) relation
|
|
65
|
+
- Group membership: (group: $g, member: unit) relation
|
|
66
|
+
4. Report: uid, model, skills count, group, TypeDB insert count
|
|
67
|
+
|
|
68
|
+
### signal
|
|
69
|
+
|
|
70
|
+
1. Parse `<receiver>` and `<data>` from `$ARGUMENTS`
|
|
71
|
+
- receiver: string (e.g. `bob:schema`, `analyst`)
|
|
72
|
+
- data: JSON or plain string
|
|
73
|
+
2. POST to `http://localhost:4321/api/signal` with `{ receiver, data }`
|
|
74
|
+
3. Report: signal sent, response outcome (result / timeout / dissolved / failure), any path marks triggered
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
*Every `/create` is a `send()` call — a new signal enters the world.*
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
# /deploy
|
|
2
|
+
|
|
3
|
+
**Skills:** `/cloudflare` (Workers auth) · `/signal` (deploy:success / deploy:degraded)
|
|
4
|
+
|
|
5
|
+
Ship all four services to Cloudflare. Deterministic sandwich — W0 baseline, build, smoke, approval, parallel deploy, health.
|
|
6
|
+
|
|
7
|
+
> **Production cutover (2026-05-23):** Astro site runs on **CF Workers with Static Assets** (not Pages). Production target is **`https://one.ie`** via the `one-prod` worker. Deploy command is `wrangler deploy --env production`.
|
|
8
|
+
>
|
|
9
|
+
> **Environment model:**
|
|
10
|
+
> - **Production (live):** `https://one.ie` — `one-prod` worker, deployed from `one.ie/web/` via `wrangler deploy --env production`
|
|
11
|
+
> - **Dev (live):** `https://dev.one.ie` — `one-substrate` worker, deployed on every `main` push
|
|
12
|
+
> - **Gateway (live, stable):** `https://api.one.ie` — `one-gateway` worker
|
|
13
|
+
> - **Legacy idle (rollback only — do not deploy):**
|
|
14
|
+
> - `https://oneie.pages.dev` — old Pages project for `one.ie` ("Ecommerce Playbook" content). Re-attach `one.ie` to this project to roll back.
|
|
15
|
+
> - `app.one.ie`, `demo.one.ie`, `onestudio.dev` — still bound to the prior `one-demo` worker. `one-demo` stays in the account untouched for rollback; redeploying `one-demo` would push stale code, so don't.
|
|
16
|
+
> - `https://one-substrate.pages.dev` — paused Pages project, rollback only.
|
|
17
|
+
>
|
|
18
|
+
> See `docs/cf-workers-migration-todo.md` for migration history. The custom-domain detach step (Pages → Worker) is done manually via the CF API (no in-repo script today).
|
|
19
|
+
|
|
20
|
+
## Modes
|
|
21
|
+
|
|
22
|
+
| Invocation | What |
|
|
23
|
+
|-----------|------|
|
|
24
|
+
| `/deploy` | Full pipeline — W0 + build + 4 services + health |
|
|
25
|
+
| `/deploy --skip-tests` | Skip W0 baseline (risky — use only when already verified) |
|
|
26
|
+
| `/deploy --dry-run` | Build + smoke only, no deploy |
|
|
27
|
+
| `/deploy --preview-only` | Build + preview Worker deploy (no CI-gated services) |
|
|
28
|
+
| `/deploy astro` | Astro Worker only (re-bundle after UI changes) |
|
|
29
|
+
| `/deploy workers` | Gateway + Sync + Agents only (no Astro rebuild) |
|
|
30
|
+
| `/deploy gateway` | Gateway worker only |
|
|
31
|
+
| `/deploy sync` | Sync worker only |
|
|
32
|
+
| `/deploy agents` | Agents edge agents only |
|
|
33
|
+
|
|
34
|
+
## The 8-Step Pipeline
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
bun run deploy # full pipeline
|
|
38
|
+
bun run deploy -- --dry-run
|
|
39
|
+
bun run deploy -- --skip-tests
|
|
40
|
+
DEPLOY_CONFIRM=yes bun run deploy # CI / non-interactive
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Step 1 — W0 Baseline**
|
|
44
|
+
```bash
|
|
45
|
+
bun run verify # biome + tsc --noEmit + vitest run + audit:design
|
|
46
|
+
```
|
|
47
|
+
Record tests passed/total. Fix before proceeding — never deploy on red.
|
|
48
|
+
|
|
49
|
+
**Step 2 — Changes**
|
|
50
|
+
`git diff` summary. Flag large changesets.
|
|
51
|
+
|
|
52
|
+
**Step 3 — Build**
|
|
53
|
+
```bash
|
|
54
|
+
NODE_ENV=production astro build
|
|
55
|
+
```
|
|
56
|
+
Target: ~23s. Emits `dist/_worker.js/index.js` + static assets via `@astrojs/cloudflare@13`. Watch for bundle size warnings.
|
|
57
|
+
|
|
58
|
+
**Step 4 — Credentials**
|
|
59
|
+
`CLOUDFLARE_API_TOKEN` must be unset. Deploy uses `CLOUDFLARE_GLOBAL_API_KEY` only.
|
|
60
|
+
Scoped tokens lack permissions for workers + custom domains.
|
|
61
|
+
|
|
62
|
+
**Step 5 — Smoke**
|
|
63
|
+
Verify `dist/server/` exists, all 4 wrangler configs present: `api/wrangler.toml`, `sync/wrangler.toml`, `agents/wrangler.toml`, root `wrangler.toml`.
|
|
64
|
+
|
|
65
|
+
**Step 6 — Approval**
|
|
66
|
+
`main` branch: prompts "yes". Other branches: auto-approved.
|
|
67
|
+
CI: `DEPLOY_CONFIRM=yes bun run deploy` (already set in `.github/workflows/deploy.yml`).
|
|
68
|
+
|
|
69
|
+
**Step 6.5 — D1 Migrations**
|
|
70
|
+
Run before any worker deploy so schema is current when new code lands:
|
|
71
|
+
```bash
|
|
72
|
+
cd one.ie/web && bunx wrangler d1 migrations apply DB --remote --env production
|
|
73
|
+
```
|
|
74
|
+
"✅ No migrations to apply!" is a pass. Any applied migration is logged and counted.
|
|
75
|
+
Migration failures block deploy — never ship worker code ahead of its schema.
|
|
76
|
+
|
|
77
|
+
**Step 7 — Deploy (parallel workers + astro)**
|
|
78
|
+
Gateway + Sync + Agents deploy in parallel (~16s). Astro Worker deploys after (~16s).
|
|
79
|
+
|
|
80
|
+
**Step 8 — Health**
|
|
81
|
+
```bash
|
|
82
|
+
curl https://api.one.ie/health # Gateway
|
|
83
|
+
curl https://one.ie/api/health # Astro Worker (production)
|
|
84
|
+
curl https://one-sync.oneie.workers.dev/ # Sync
|
|
85
|
+
curl https://agents.oneie.workers.dev/health # Agents
|
|
86
|
+
```
|
|
87
|
+
3 retries with backoff. All 4 must return 200. Astro `/api/health` must report `units: 140` (or current count) — empty `units: 0` means the build didn't bake `PUBLIC_GATEWAY_URL` correctly.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Bundle Size Rules (CF Workers Free Tier — 3 MiB gzipped upload)
|
|
92
|
+
|
|
93
|
+
The Astro Worker upload must stay under **3 MiB gzipped** on the free tier (10 MiB
|
|
94
|
+
on paid). Wrangler reports both `Total Upload` (uncompressed) and `gzip` — only
|
|
95
|
+
gzip counts toward the ceiling. All chunks in `dist/server/chunks/` are uploaded
|
|
96
|
+
together; dynamic `await import()` does NOT exclude code from the upload.
|
|
97
|
+
|
|
98
|
+
These rules are **LOCKED** — do not revert them. Apply identically to any
|
|
99
|
+
developer template we ship (`oneie init` Workers scaffold mirrors this shape).
|
|
100
|
+
|
|
101
|
+
### Rule 1 — `syntaxHighlight: false` in `astro.config.mjs`
|
|
102
|
+
|
|
103
|
+
```js
|
|
104
|
+
markdown: { syntaxHighlight: false }
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Disables Shiki from Astro's markdown pipeline. Without this, Shiki pulls ~5.8 MiB of
|
|
108
|
+
language grammar files into the SSR worker on every build. **Do not re-enable.**
|
|
109
|
+
|
|
110
|
+
### Rule 2 — `ssr.external` for heavy packages
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
ssr: {
|
|
114
|
+
external: ["node:async_hooks", "@mysten/sui", "@mysten/bcs", "shiki", "@shikijs/core", "@shikijs/types"]
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The CF adapter bundles everything by default. `ssr.external` creates a bare
|
|
119
|
+
`import { x } from 'pkg'` reference without inlining the package.
|
|
120
|
+
|
|
121
|
+
**Critical nuance:** `ssr.external` only works safely when the externalized package is
|
|
122
|
+
never executed on the server path. For `shiki`: `codeToHtml` is imported by `code-block.tsx`,
|
|
123
|
+
but all components that use `code-block.tsx` are `client:only` — so `codeToHtml` is never
|
|
124
|
+
called in the worker. The import statement exists in the bundle but is dead code.
|
|
125
|
+
|
|
126
|
+
If you add a new heavy dependency used only client-side, add it here.
|
|
127
|
+
|
|
128
|
+
### Rule 3 — Pure-shell pages use `client:only` + `prerender = true`
|
|
129
|
+
|
|
130
|
+
```astro
|
|
131
|
+
---
|
|
132
|
+
export const prerender = true
|
|
133
|
+
import { MyComponent } from "@/components/MyComponent"
|
|
134
|
+
---
|
|
135
|
+
<Layout title="...">
|
|
136
|
+
<MyComponent client:only="react" />
|
|
137
|
+
</Layout>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
`client:only="react"` — Astro renders an empty div on the server; the component never
|
|
141
|
+
runs in the worker. The React component tree (+ all its imports) stays out of the SSR bundle.
|
|
142
|
+
|
|
143
|
+
`export const prerender = true` — the page becomes a static asset generated once
|
|
144
|
+
at build time. The page's SSR handler collapses to a small stub. Zero worker cost
|
|
145
|
+
at runtime.
|
|
146
|
+
|
|
147
|
+
**Use this pattern for:** any page that has no server-side data dependencies
|
|
148
|
+
(no `Astro.locals`, no `Astro.request`, no DB queries in frontmatter).
|
|
149
|
+
|
|
150
|
+
Currently prerendered (verify with `grep -l "export const prerender = true" src/pages/*.astro`):
|
|
151
|
+
`404.astro`, `board.astro`, `build.astro`, `ceo.astro`, `chat.astro`, `chat-agents.astro`,
|
|
152
|
+
`chat-fast.astro`, `chat-routing.astro`, `in.astro`, `speed.astro`.
|
|
153
|
+
|
|
154
|
+
Pages that CANNOT be prerendered (need runtime session/auth):
|
|
155
|
+
`world.astro` (reads `Astro.locals.session`), `market.astro` (fetches capabilities).
|
|
156
|
+
|
|
157
|
+
### Rule 4 — `inlineStylesheets: 'auto'` in `astro.config.mjs`
|
|
158
|
+
|
|
159
|
+
```js
|
|
160
|
+
build: { inlineStylesheets: 'auto' }, // ← NEVER 'always'
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
With `'always'`, Astro inlines the full Tailwind stylesheet into **every route's
|
|
164
|
+
serialized manifest entry**. With ~100 routes the entry chunk balloons by 8+ MiB
|
|
165
|
+
of duplicated CSS as a single string literal — diagnosable in worker-entry at
|
|
166
|
+
the line `const _manifest = deserializeManifest({...})`.
|
|
167
|
+
|
|
168
|
+
`'auto'` ships the bundle as one external `<link rel="stylesheet">` referenced
|
|
169
|
+
once across all routes. Browsers cache it across navigations — a page-speed
|
|
170
|
+
win, not just a worker-size win.
|
|
171
|
+
|
|
172
|
+
**Verified 2026-05-22:** flipping `always` → `auto` dropped worker-entry from
|
|
173
|
+
9.5 MiB → 672 KiB and total gzip from 3302 KiB → 2079 KiB.
|
|
174
|
+
|
|
175
|
+
### Rule 5 — `react-dom/server.edge` alias (production only)
|
|
176
|
+
|
|
177
|
+
```js
|
|
178
|
+
resolve: {
|
|
179
|
+
alias: {
|
|
180
|
+
...(isDev ? {} : { "react-dom/server": "react-dom/server.edge" })
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Already in `astro.config.mjs`. Required for CF Edge runtime compatibility.
|
|
186
|
+
Do not remove for production builds.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Verified Bundle Numbers
|
|
191
|
+
|
|
192
|
+
| Snapshot | Total upload | gzip | Worker-entry | What changed |
|
|
193
|
+
|---|---|---|---|---|
|
|
194
|
+
| 2026-04-18 (post Pages→Workers migration) | — | — | 9.5 MiB | Rules 1-3 + 5 |
|
|
195
|
+
| 2026-05-22 before `inlineStylesheets` fix | 18.5 MiB | 3.3 MiB | 9.5 MiB | Over 3 MiB ceiling — deploy FAILED |
|
|
196
|
+
| 2026-05-22 after Rule 4 (`'always'` → `'auto'`) | 10.1 MiB | **2.1 MiB** | **672 KiB** | Under ceiling — deploy ✓ |
|
|
197
|
+
|
|
198
|
+
The 2026-05-22 regression was caused by `build: { inlineStylesheets: 'always' }`
|
|
199
|
+
inlining the full Tailwind stylesheet into every route's manifest entry. One
|
|
200
|
+
char change (`always` → `auto`) saved 8.8 MiB.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Service Map (post-migration)
|
|
205
|
+
|
|
206
|
+
| Service | URL | Config | Deploy command |
|
|
207
|
+
|---------|-----|--------|---------------|
|
|
208
|
+
| Astro Worker (prod) | `one.ie` → `one-prod` | `one.ie/web/wrangler.toml` | `cd one.ie/web && wrangler deploy --env production` |
|
|
209
|
+
| Astro Worker (dev) | `dev.one.ie` → `one-substrate` | `wrangler.toml` (root) | `wrangler deploy` |
|
|
210
|
+
| Gateway | `api.one.ie` → `one-gateway` | `api/wrangler.toml` | `cd api && wrangler deploy` |
|
|
211
|
+
| Sync | `one-sync.oneie.workers.dev` | `sync/wrangler.toml` | `cd sync && wrangler deploy` |
|
|
212
|
+
| Agents | `agents.oneie.workers.dev` | `agents/wrangler.toml` | `cd agents && wrangler deploy` |
|
|
213
|
+
| Pages (legacy idle, rollback) | `oneie.pages.dev` | — | **do not deploy** — rollback target for `one.ie` |
|
|
214
|
+
| Worker (legacy idle, rollback) | `one-demo` (still serves `app.one.ie`, `demo.one.ie`, `onestudio.dev`) | — | **do not deploy** — rollback window for the prod cutover |
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Auth (CRITICAL — never change)
|
|
219
|
+
|
|
220
|
+
Always: `CLOUDFLARE_GLOBAL_API_KEY` + `CLOUDFLARE_EMAIL`.
|
|
221
|
+
Never: `CLOUDFLARE_API_TOKEN` (scoped token lacks workers + custom domain permissions).
|
|
222
|
+
|
|
223
|
+
The deploy script auto-unsets `CLOUDFLARE_API_TOKEN` from the spawned env to prevent
|
|
224
|
+
accidental use of a scoped token that was exported in the shell.
|
|
225
|
+
|
|
226
|
+
CI secrets required in `.github/workflows/deploy.yml` env block:
|
|
227
|
+
- `CLOUDFLARE_API_KEY` (mapped from `secrets.CLOUDFLARE_GLOBAL_API_KEY`)
|
|
228
|
+
- `CLOUDFLARE_EMAIL`
|
|
229
|
+
- `CLOUDFLARE_ACCOUNT_ID`
|
|
230
|
+
- `CLOUDFLARE_API_TOKEN: ''` (explicit blank)
|
|
231
|
+
- `DEPLOY_CONFIRM: 'yes'`
|
|
232
|
+
- `PUBLIC_GATEWAY_URL: https://api.one.ie` (build-time-inlined by Astro — **required**; without this the Worker bundle falls back to `one-gateway.oneie.workers.dev` and `/api/health` returns `units: 0`)
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Steps
|
|
237
|
+
|
|
238
|
+
### `/deploy` (full pipeline)
|
|
239
|
+
|
|
240
|
+
1. `bun run verify` — W0 gate. Fail here means don't deploy.
|
|
241
|
+
2. `git diff` summary — surface scope to user.
|
|
242
|
+
3. `NODE_ENV=production bun run build` — Astro production build.
|
|
243
|
+
4. Verify credentials: assert `CLOUDFLARE_GLOBAL_API_KEY` present, unset `CLOUDFLARE_API_TOKEN`.
|
|
244
|
+
5. Smoke check: assert `dist/_worker.js/` exists, all 4 wrangler configs present.
|
|
245
|
+
6. Approval gate: prompt on `main`, auto on other branches.
|
|
246
|
+
7. Parallel deploy: Gateway + Sync + Agents concurrently, then Astro Worker.
|
|
247
|
+
8. Health checks: all 4 endpoints × 3 retries with backoff.
|
|
248
|
+
9. Report:
|
|
249
|
+
```
|
|
250
|
+
Branch: main
|
|
251
|
+
Tests: 320/320 pass
|
|
252
|
+
Build: 23.2s
|
|
253
|
+
Migrations: 2 applied (0 already up-to-date)
|
|
254
|
+
Workers: parallel 16.7s (vs ~42s sequential)
|
|
255
|
+
Astro: 16.1s
|
|
256
|
+
Health: 4/4 (Gateway 308ms, Astro 666ms, Sync 287ms, Agents 287ms)
|
|
257
|
+
Preview: https://<hash>.one-substrate.<account>.workers.dev
|
|
258
|
+
Total: ~65s
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### `/deploy astro`
|
|
262
|
+
|
|
263
|
+
Deploys the production worker (`one-prod`) to `one.ie` from the `one.ie/web/` directory.
|
|
264
|
+
|
|
265
|
+
1. `cd one.ie/web && NODE_ENV=production bun run build`
|
|
266
|
+
2. Check bundle size: `du -sh one.ie/web/dist/server/`
|
|
267
|
+
3. If > 12 MiB: check which chunk grew (`ls -lhS one.ie/web/dist/server/chunks/ | head -15`)
|
|
268
|
+
4. Run D1 migrations: `cd one.ie/web && bunx wrangler d1 migrations apply DB --remote --env production`
|
|
269
|
+
5. `cd one.ie/web && bunx wrangler deploy --env production`
|
|
270
|
+
6. Health: `curl -sL https://one.ie/api/health` (expect 200, `units > 0`)
|
|
271
|
+
|
|
272
|
+
**Pre-flight (one-time, on cutover only):** ensure no other CF entity owns the `one.ie` custom domain. If wrangler errors with a hostname conflict, detach the prior owner first:
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
# If a Pages project owns it (was `oneie` project pre-cutover):
|
|
276
|
+
curl -s -X DELETE \
|
|
277
|
+
"https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/pages/projects/oneie/domains/one.ie" \
|
|
278
|
+
-H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
|
|
279
|
+
-H "X-Auth-Key: $CLOUDFLARE_GLOBAL_API_KEY"
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### `/deploy workers`
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
cd api && bun wrangler deploy && cd ../workers/sync && bun wrangler deploy && cd ../../agents && bun wrangler deploy && cd ..
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Report: version hash per worker, health latency per service.
|
|
289
|
+
|
|
290
|
+
### Cutover tool (`scripts/cf-cutover.ts`)
|
|
291
|
+
|
|
292
|
+
For Pages→Workers custom-domain flips on other services (script is parameterized):
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
bun run cf-cutover # dry-run, safe
|
|
296
|
+
bun run cf-cutover --execute # real cutover: Workers route + Pages detach + health verify + substrate signal
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Defaults: domain=`dev.one.ie`, worker=`one-substrate`, pages=`one-substrate`. Override via `CF_CUTOVER_DOMAIN`, `CF_CUTOVER_WORKER`, `CF_CUTOVER_PAGES_PROJECT` env vars.
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## Bundle Size Diagnosis
|
|
304
|
+
|
|
305
|
+
If build fails with "exceeds size limit":
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
# Check total worker size
|
|
309
|
+
du -sh dist/server/
|
|
310
|
+
|
|
311
|
+
# Find top offenders
|
|
312
|
+
ls -lhS dist/server/chunks/ | head -20
|
|
313
|
+
|
|
314
|
+
# Check if a new import pulled in Shiki
|
|
315
|
+
grep -r "from 'shiki'" dist/server/chunks/ | wc -l
|
|
316
|
+
# If > 0: a component that imports shiki was SSR'd
|
|
317
|
+
# Fix: make its page client:only="react" + prerender=true
|
|
318
|
+
|
|
319
|
+
# Check if React crept back into worker via client:load
|
|
320
|
+
grep -l "react-vendor" dist/server/chunks/
|
|
321
|
+
# If multiple chunks: some page SSR-renders React via client:load
|
|
322
|
+
# Fix: audit src/pages/*.astro for client:load on pure-shell pages
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Rollback
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
# Workers — rollback to previous version
|
|
331
|
+
bun wrangler rollback --name one-prod # production
|
|
332
|
+
bun wrangler rollback --name one-substrate # dev
|
|
333
|
+
|
|
334
|
+
# Legacy Pages fallback (still live at one-substrate.pages.dev for rollback window):
|
|
335
|
+
bun wrangler pages deployment list --project-name=one-substrate
|
|
336
|
+
bun wrangler pages deployments rollback --project-name=one-substrate
|
|
337
|
+
|
|
338
|
+
# Revert a Worker via git
|
|
339
|
+
cd api && git stash && bun wrangler deploy
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
## Known-Flaky Test Allowlist
|
|
345
|
+
|
|
346
|
+
Located in `scripts/deploy.ts`:
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
const KNOWN_FLAKY = [
|
|
350
|
+
'Act 15: Speed Benchmarks', // hardware-dependent
|
|
351
|
+
'STAN distribution', // stochastic
|
|
352
|
+
'explorer mode', // stochastic
|
|
353
|
+
]
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
These failures don't block deploy. Real failures (type errors, broken logic)
|
|
357
|
+
always block. Use `--strict` to require full green.
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## First-Time Setup
|
|
362
|
+
|
|
363
|
+
Only needed once — see `docs/deploy.md` for full walkthrough:
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
# Create CF resources
|
|
367
|
+
bun wrangler d1 create one
|
|
368
|
+
bun wrangler kv namespace create KV
|
|
369
|
+
# → Paste IDs into wrangler.toml + sync/wrangler.toml
|
|
370
|
+
|
|
371
|
+
# Run D1 migration
|
|
372
|
+
bun wrangler d1 execute one --remote --file=migrations/0001_init.sql
|
|
373
|
+
|
|
374
|
+
# Gateway secrets (TypeDB credentials)
|
|
375
|
+
cd api
|
|
376
|
+
printf 'admin' | bun wrangler secret put TYPEDB_USERNAME
|
|
377
|
+
printf 'YOUR-PASSWORD' | bun wrangler secret put TYPEDB_PASSWORD
|
|
378
|
+
cd ..
|
|
379
|
+
|
|
380
|
+
# First deploy — Worker auto-provisions on first `wrangler deploy`
|
|
381
|
+
bun run deploy
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
## Logs
|
|
387
|
+
|
|
388
|
+
- `.deploy.log` — all deployment output, each worker appends here
|
|
389
|
+
- `.deploy-build.log` — W0 baseline diagnostics (biome + tsc + vitest separate)
|
|
390
|
+
|
|
391
|
+
Live logs:
|
|
392
|
+
|
|
393
|
+
```bash
|
|
394
|
+
bun wrangler tail --name one-prod # Astro Worker (production)
|
|
395
|
+
bun wrangler tail --name one-substrate # Astro Worker (dev)
|
|
396
|
+
cd api && bun wrangler tail && cd .. # Gateway
|
|
397
|
+
bun wrangler deployments list --name one-prod | head -10
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
## Gotchas
|
|
403
|
+
|
|
404
|
+
- TypeDB Cloud port is **1729** (not 80 or 443)
|
|
405
|
+
- TypeDB HTTP API prefix is `/v1/` (signin, query, databases)
|
|
406
|
+
- Always `CLOUDFLARE_GLOBAL_API_KEY` — scoped tokens lack permissions for workers + custom domains
|
|
407
|
+
- `import.meta.env` is build-time — `PUBLIC_GATEWAY_URL` is baked into the worker bundle at build. Missing → `/api/health` returns `units: 0`
|
|
408
|
+
- Custom domains: `[[routes]]` double bracket, no wildcards, add `workers_dev = true`
|
|
409
|
+
- Worker upload limit: **3 MiB gzipped** on free tier (10 MiB on paid). Wrangler reports `gzip:` — only that number counts. Follow the 5 Bundle Size Rules above
|
|
410
|
+
- **D1 schema-drift fails at runtime, not compile-time.** Migrations that DROP+CREATE a table (e.g. `0059_domains.sql` renamed `slug`→`gid`, `verified`→`verified_at`) silently break any code that queries the old columns — typecheck passes, deploy succeeds, the route 500s in production. After any DROP+CREATE migration, grep the codebase for the old column names and fix call sites BEFORE deploying
|
|
411
|
+
- **Error responses get the same `cache-control` as success responses.** Astro's Layout sets `public, max-age=300, s-maxage=86400, stale-while-revalidate=604800` on every render including 5xx pages. A bad deploy will be cached at the CF edge for 24h. When diagnosing, always bust the cache: `curl "https://host/path?_t=$(date +%s)"`. Consider a middleware rule that strips `cache-control` on `>= 500` status
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
*Deploy is the closed loop. W0 baseline in, health check out. If health fails, mark() is blocked. Determinism: every step reports numbers, every number gets marked.*
|