smol-symphony 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/AGENTS.md +73 -0
- package/DESIGN.md +453 -0
- package/LICENSE +21 -0
- package/PRODUCT.md +106 -0
- package/README.md +232 -0
- package/SPEC.md +2169 -0
- package/WORKFLOW.md +269 -0
- package/WORKFLOW.template.md +307 -0
- package/dist/agent/acp.js +304 -0
- package/dist/agent/acp.js.map +1 -0
- package/dist/agent/adapters.js +275 -0
- package/dist/agent/adapters.js.map +1 -0
- package/dist/agent/codex.js +439 -0
- package/dist/agent/codex.js.map +1 -0
- package/dist/agent/runner.js +394 -0
- package/dist/agent/runner.js.map +1 -0
- package/dist/agent/smolvm.js +174 -0
- package/dist/agent/smolvm.js.map +1 -0
- package/dist/bin/symphony.js +205 -0
- package/dist/bin/symphony.js.map +1 -0
- package/dist/http.js +1189 -0
- package/dist/http.js.map +1 -0
- package/dist/logging.js +45 -0
- package/dist/logging.js.map +1 -0
- package/dist/mcp.js +478 -0
- package/dist/mcp.js.map +1 -0
- package/dist/orchestrator.js +683 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/prompt.js +65 -0
- package/dist/prompt.js.map +1 -0
- package/dist/trackers/local.js +344 -0
- package/dist/trackers/local.js.map +1 -0
- package/dist/trackers/types.js +10 -0
- package/dist/trackers/types.js.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/workflow.js +385 -0
- package/dist/workflow.js.map +1 -0
- package/dist/workspace.js +196 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +68 -0
- package/scripts/build-vm.sh +67 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Standing instructions for any AI agent (Claude Code, Codex, OpenCode, etc.)
|
|
4
|
+
working on this repo. Short list; keep it that way.
|
|
5
|
+
|
|
6
|
+
## Workflow template stays in sync
|
|
7
|
+
|
|
8
|
+
The repo ships two workflow files:
|
|
9
|
+
|
|
10
|
+
- `WORKFLOW.md` — the canonical workflow used to dispatch agents against this
|
|
11
|
+
project.
|
|
12
|
+
- `WORKFLOW.template.md` — the annotated reference covering every supported
|
|
13
|
+
option, its type, default, and example.
|
|
14
|
+
|
|
15
|
+
**When you change anything that affects workflow file syntax or semantics,
|
|
16
|
+
update `WORKFLOW.template.md` in the same commit.** Concretely:
|
|
17
|
+
|
|
18
|
+
- Adding a new YAML key under `tracker:`, `polling:`, `workspace:`, `hooks:`,
|
|
19
|
+
`agent:`, `acp:`, `smolvm:`, `server:`, or `mcp:` → document it in the
|
|
20
|
+
matching section of the template.
|
|
21
|
+
- Renaming or removing a key → rename or remove it in the template too.
|
|
22
|
+
- Changing a default value in `src/workflow.ts` (or whichever parser becomes
|
|
23
|
+
authoritative) → update the `Default:` annotation in the template.
|
|
24
|
+
- Introducing a new top-level section → add a new section block to the
|
|
25
|
+
template.
|
|
26
|
+
- Adding a new hook env var or Liquid context field → list it in the template
|
|
27
|
+
alongside the existing ones (under `hooks:` or under the prompt-body
|
|
28
|
+
comment).
|
|
29
|
+
|
|
30
|
+
If you find the template already drifted from the parser at the start of your
|
|
31
|
+
task, fix it as part of your change. The template is the contract for what
|
|
32
|
+
operators can write; an out-of-date template is a bug, not paperwork.
|
|
33
|
+
|
|
34
|
+
## Build, test, and check before declaring done
|
|
35
|
+
|
|
36
|
+
- `npm run typecheck` — must pass.
|
|
37
|
+
- `npm test` — must pass.
|
|
38
|
+
- `npm run build` — must pass.
|
|
39
|
+
|
|
40
|
+
Run all three before calling `symphony.mark_done`.
|
|
41
|
+
|
|
42
|
+
## Handoff: patch bundle vs. pull request
|
|
43
|
+
|
|
44
|
+
`after_run` in `WORKFLOW.md` ships in two modes:
|
|
45
|
+
|
|
46
|
+
- **Patch bundle (default).** Writes `git format-patch` to
|
|
47
|
+
`.symphony/patches/<branch>.patch` for human review. No remote needed.
|
|
48
|
+
This is what fires when no GitHub remote is wired up.
|
|
49
|
+
- **Pull request.** Triggered when `SYMPHONY_REPO=<owner>/<repo>` is exported
|
|
50
|
+
before launch AND the working repo has an `origin` remote. The hook then
|
|
51
|
+
pushes the per-issue branch and runs `gh pr create`. `gh auth status` must
|
|
52
|
+
be clean on the host; the token never enters the VM.
|
|
53
|
+
|
|
54
|
+
To switch this project to PR mode:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
git remote add origin git@github.com:<owner>/smol-symphony.git
|
|
58
|
+
git push -u origin main
|
|
59
|
+
SYMPHONY_REPO=<owner>/smol-symphony npx symphony WORKFLOW.md
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`SYMPHONY_BASE_BRANCH` (default `main`) overrides the base the agent branches
|
|
63
|
+
from and the PR opens against.
|
|
64
|
+
|
|
65
|
+
## Don't write to generated state
|
|
66
|
+
|
|
67
|
+
Skip these when staging commits unless the user asks:
|
|
68
|
+
|
|
69
|
+
- `.agents/`, `.claude/`, `.impeccable/` — local tooling state.
|
|
70
|
+
- `issues*/Done/`, `issues*/Cancelled/`, `issues*/In Progress/` — runtime
|
|
71
|
+
tracker artifacts from prior symphony runs.
|
|
72
|
+
- `.symphony/` — workspaces, patch bundles, runtime caches.
|
|
73
|
+
- `skills-lock.json` — auto-generated.
|
package/DESIGN.md
ADDED
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: smol-symphony
|
|
3
|
+
description: The orchestrator's console. A small dark workshop for dispatching and watching coding agents.
|
|
4
|
+
colors:
|
|
5
|
+
surface-inset: "#0c0f15"
|
|
6
|
+
surface-base: "#0f1115"
|
|
7
|
+
surface-raised: "#161a22"
|
|
8
|
+
surface-chip: "#20242c"
|
|
9
|
+
border-soft: "#1c2029"
|
|
10
|
+
border-firm: "#2a2e36"
|
|
11
|
+
text-dim: "#6b7280"
|
|
12
|
+
text-muted: "#9aa4b2"
|
|
13
|
+
text-base: "#dfe2e7"
|
|
14
|
+
text-strong: "#e6ebf2"
|
|
15
|
+
text-on-accent: "#ffffff"
|
|
16
|
+
dispatch-blue: "#2a6df4"
|
|
17
|
+
running-bg: "#1f3a26"
|
|
18
|
+
running-fg: "#58d68d"
|
|
19
|
+
retrying-bg: "#3a2f1f"
|
|
20
|
+
retrying-fg: "#f0c060"
|
|
21
|
+
idle-bg: "#20242c"
|
|
22
|
+
idle-fg: "#9aa4b2"
|
|
23
|
+
error-fg: "#ff7676"
|
|
24
|
+
success-fg: "#58d68d"
|
|
25
|
+
typography:
|
|
26
|
+
headline:
|
|
27
|
+
fontFamily: "ui-sans-serif, system-ui, sans-serif"
|
|
28
|
+
fontSize: "1.3rem"
|
|
29
|
+
fontWeight: 400
|
|
30
|
+
lineHeight: 1.2
|
|
31
|
+
title:
|
|
32
|
+
fontFamily: "ui-sans-serif, system-ui, sans-serif"
|
|
33
|
+
fontSize: "1rem"
|
|
34
|
+
fontWeight: 400
|
|
35
|
+
lineHeight: 1.3
|
|
36
|
+
body:
|
|
37
|
+
fontFamily: "ui-sans-serif, system-ui, sans-serif"
|
|
38
|
+
fontSize: "14px"
|
|
39
|
+
fontWeight: 400
|
|
40
|
+
lineHeight: 1.45
|
|
41
|
+
label:
|
|
42
|
+
fontFamily: "ui-sans-serif, system-ui, sans-serif"
|
|
43
|
+
fontSize: "14px"
|
|
44
|
+
fontWeight: 400
|
|
45
|
+
lineHeight: 1.4
|
|
46
|
+
pill:
|
|
47
|
+
fontFamily: "ui-sans-serif, system-ui, sans-serif"
|
|
48
|
+
fontSize: "0.85em"
|
|
49
|
+
fontWeight: 400
|
|
50
|
+
lineHeight: 1.4
|
|
51
|
+
data:
|
|
52
|
+
fontFamily: "ui-sans-serif, system-ui, sans-serif"
|
|
53
|
+
fontSize: "14px"
|
|
54
|
+
fontWeight: 400
|
|
55
|
+
fontFeature: "tabular-nums"
|
|
56
|
+
lineHeight: 1.45
|
|
57
|
+
th:
|
|
58
|
+
fontFamily: "ui-sans-serif, system-ui, sans-serif"
|
|
59
|
+
fontSize: "14px"
|
|
60
|
+
fontWeight: 500
|
|
61
|
+
lineHeight: 1.4
|
|
62
|
+
rounded:
|
|
63
|
+
sm: "4px"
|
|
64
|
+
md: "8px"
|
|
65
|
+
pill: "999px"
|
|
66
|
+
spacing:
|
|
67
|
+
"2xs": "0.1rem"
|
|
68
|
+
xs: "0.3rem"
|
|
69
|
+
sm: "0.5rem"
|
|
70
|
+
md: "1rem"
|
|
71
|
+
lg: "1.5rem"
|
|
72
|
+
xl: "2rem"
|
|
73
|
+
components:
|
|
74
|
+
button-primary:
|
|
75
|
+
backgroundColor: "{colors.dispatch-blue}"
|
|
76
|
+
textColor: "{colors.text-on-accent}"
|
|
77
|
+
rounded: "{rounded.sm}"
|
|
78
|
+
padding: "0.5rem 1rem"
|
|
79
|
+
typography: "{typography.body}"
|
|
80
|
+
button-ghost:
|
|
81
|
+
backgroundColor: "{colors.surface-chip}"
|
|
82
|
+
textColor: "{colors.text-base}"
|
|
83
|
+
rounded: "{rounded.sm}"
|
|
84
|
+
padding: "0.3rem 0.7rem"
|
|
85
|
+
typography: "{typography.body}"
|
|
86
|
+
input:
|
|
87
|
+
backgroundColor: "{colors.surface-inset}"
|
|
88
|
+
textColor: "{colors.text-strong}"
|
|
89
|
+
rounded: "{rounded.sm}"
|
|
90
|
+
padding: "0.4rem 0.6rem"
|
|
91
|
+
typography: "{typography.body}"
|
|
92
|
+
card-form:
|
|
93
|
+
backgroundColor: "{colors.surface-raised}"
|
|
94
|
+
textColor: "{colors.text-base}"
|
|
95
|
+
rounded: "{rounded.md}"
|
|
96
|
+
padding: "1rem"
|
|
97
|
+
pill-running:
|
|
98
|
+
backgroundColor: "{colors.running-bg}"
|
|
99
|
+
textColor: "{colors.running-fg}"
|
|
100
|
+
rounded: "{rounded.pill}"
|
|
101
|
+
padding: "0.1rem 0.5rem"
|
|
102
|
+
typography: "{typography.pill}"
|
|
103
|
+
pill-retrying:
|
|
104
|
+
backgroundColor: "{colors.retrying-bg}"
|
|
105
|
+
textColor: "{colors.retrying-fg}"
|
|
106
|
+
rounded: "{rounded.pill}"
|
|
107
|
+
padding: "0.1rem 0.5rem"
|
|
108
|
+
typography: "{typography.pill}"
|
|
109
|
+
pill-idle:
|
|
110
|
+
backgroundColor: "{colors.idle-bg}"
|
|
111
|
+
textColor: "{colors.idle-fg}"
|
|
112
|
+
rounded: "{rounded.pill}"
|
|
113
|
+
padding: "0.1rem 0.5rem"
|
|
114
|
+
typography: "{typography.pill}"
|
|
115
|
+
th:
|
|
116
|
+
backgroundColor: "{colors.surface-base}"
|
|
117
|
+
textColor: "{colors.text-muted}"
|
|
118
|
+
typography: "{typography.th}"
|
|
119
|
+
padding: "0.4rem 0.6rem"
|
|
120
|
+
td:
|
|
121
|
+
backgroundColor: "{colors.surface-base}"
|
|
122
|
+
textColor: "{colors.text-base}"
|
|
123
|
+
typography: "{typography.data}"
|
|
124
|
+
padding: "0.4rem 0.6rem"
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
# Design System: smol-symphony
|
|
128
|
+
|
|
129
|
+
## 1. Overview
|
|
130
|
+
|
|
131
|
+
**Creative North Star: "The Quiet Workshop"**
|
|
132
|
+
|
|
133
|
+
A tidy workbench, not a control room. The orchestrator does the real work; this
|
|
134
|
+
surface only shows what is on the bench right now, and gives one place to put
|
|
135
|
+
new work on. Everything you can see is there because the orchestrator actually
|
|
136
|
+
knows it, and nothing is there for atmosphere.
|
|
137
|
+
|
|
138
|
+
The system is dark because the user opens it next to a terminal, against an
|
|
139
|
+
already-dark editor, in the middle of doing other things. It is flat because
|
|
140
|
+
the workshop has no spotlights: depth is conveyed by stacking slightly lighter
|
|
141
|
+
panels on a slightly darker bench, never by glow or lift. Numbers are tabular,
|
|
142
|
+
labels are real field names, and pills mean exactly the three states the
|
|
143
|
+
orchestrator tracks: running, retrying, idle.
|
|
144
|
+
|
|
145
|
+
What this system rejects, drawn from PRODUCT.md: generic SaaS dashboard chrome
|
|
146
|
+
(sidebar nav, gradient metric tiles, identical card grids), Jira-style ITSM
|
|
147
|
+
heaviness (modal-on-modal flows, status pills whose colors don't encode
|
|
148
|
+
distinct state), and AI-product chrome (violet gradients, glassmorphism,
|
|
149
|
+
sparkle iconography). smol-symphony runs AI agents but is not an AI product;
|
|
150
|
+
the agents are infrastructure, not the narrative.
|
|
151
|
+
|
|
152
|
+
**Key Characteristics:**
|
|
153
|
+
- Dark, cool-blue-leaning neutrals stacked into four tonal layers.
|
|
154
|
+
- One saturated accent (Dispatch Blue) reserved for the single primary verb.
|
|
155
|
+
- Three semantic pill states, each a paired bg + fg color. No fourth state, ever.
|
|
156
|
+
- No shadows. Depth is tonal stacking only.
|
|
157
|
+
- Single sans family. Tabular numerics for any cell that holds a number.
|
|
158
|
+
- Density over whitespace luxury: tables are dense, the form is grid-packed.
|
|
159
|
+
|
|
160
|
+
## 2. Colors: The Workshop Bench Palette
|
|
161
|
+
|
|
162
|
+
A cool-blue-leaning dark stack with one saturated accent and three semantic
|
|
163
|
+
status pairs. Each neutral is tinted toward the accent hue at near-zero
|
|
164
|
+
chroma so no surface reads as pure grey.
|
|
165
|
+
|
|
166
|
+
### Primary
|
|
167
|
+
- **Dispatch Blue** (`#2a6df4`, ≈`oklch(55% 0.22 261)`): the verb. Used only on
|
|
168
|
+
the Create-issue submit button. Nowhere else on the page is this saturated.
|
|
169
|
+
The button is the one place the user makes the orchestrator do something
|
|
170
|
+
new; the color marks that action and only that action.
|
|
171
|
+
|
|
172
|
+
### Neutral (the bench, four tonal layers)
|
|
173
|
+
- **Inset** (`#0c0f15`, ≈`oklch(16% 0.008 260)`): input field interiors.
|
|
174
|
+
Deeper than the body so fields read as sunken into the form, not floating.
|
|
175
|
+
- **Bench** (`#0f1115`, ≈`oklch(18% 0.005 260)`): the page body. The default
|
|
176
|
+
surface; everything else stacks on or beneath this.
|
|
177
|
+
- **Raised Panel** (`#161a22`, ≈`oklch(21% 0.012 260)`): the create-issue form
|
|
178
|
+
card. One step lighter than the bench, signalling "this is where you put new
|
|
179
|
+
things."
|
|
180
|
+
- **Chip** (`#20242c`, ≈`oklch(27% 0.012 260)`): the refresh button and the
|
|
181
|
+
idle pill. The most-lifted neutral; reserved for small interactive surfaces
|
|
182
|
+
and quiet status.
|
|
183
|
+
|
|
184
|
+
### Border / Rule
|
|
185
|
+
- **Soft Rule** (`#1c2029`, ≈`oklch(25% 0.010 260)`): horizontal table row
|
|
186
|
+
dividers. Just barely visible; the eye should track columns, not rows.
|
|
187
|
+
- **Firm Rule** (`#2a2e36`, ≈`oklch(31% 0.008 260)`): input borders and the
|
|
188
|
+
underline beneath section headings. The structural lines.
|
|
189
|
+
|
|
190
|
+
### Text (the four-step ramp)
|
|
191
|
+
- **Dim** (`#6b7280`): timestamps, `small.dim` annotations. The lowest reading
|
|
192
|
+
layer; reachable but not pulling attention.
|
|
193
|
+
- **Muted** (`#9aa4b2`): form labels, table headers, secondary copy.
|
|
194
|
+
- **Base** (`#dfe2e7`): primary body text and table cell contents.
|
|
195
|
+
- **Strong** (`#e6ebf2`): input field text and other content the user is
|
|
196
|
+
actively typing or editing. One step brighter than base so what you're
|
|
197
|
+
writing stands out from what you're reading.
|
|
198
|
+
|
|
199
|
+
### Status (three paired roles)
|
|
200
|
+
Each pill carries both a background and a foreground; they are not
|
|
201
|
+
interchangeable across states. The dark-on-color is what reads at a glance.
|
|
202
|
+
|
|
203
|
+
- **Running** (`bg #1f3a26`, `fg #58d68d`): active sessions. The orchestrator
|
|
204
|
+
is currently spending tokens on this issue.
|
|
205
|
+
- **Retrying** (`bg #3a2f1f`, `fg #f0c060`): retry queue. The orchestrator
|
|
206
|
+
hit a recoverable failure and will dispatch again on backoff.
|
|
207
|
+
- **Idle** (`bg #20242c`, `fg #9aa4b2`): on disk, not running, not retrying.
|
|
208
|
+
Deliberately the same hex as Chip + Muted, because idle is *the absence of
|
|
209
|
+
status color*.
|
|
210
|
+
|
|
211
|
+
### Inline message
|
|
212
|
+
- **Error** (`#ff7676`): the create-form error line. Inline, not a toast, not
|
|
213
|
+
a modal.
|
|
214
|
+
- **Success** (`#58d68d`): the create-form OK line. Shares the running-fg
|
|
215
|
+
green by design; success means "the orchestrator now knows about this."
|
|
216
|
+
|
|
217
|
+
### Named Rules
|
|
218
|
+
|
|
219
|
+
**The One Verb Rule.** Dispatch Blue appears on exactly one element per page:
|
|
220
|
+
the primary action. If a second saturated accent is needed, the design is
|
|
221
|
+
wrong; reach for a status color (running/retrying/idle) or text weight first.
|
|
222
|
+
|
|
223
|
+
**The Three-Pill Rule.** Status pills encode running, retrying, idle.
|
|
224
|
+
Nothing else. Never invent a fourth pill color for taste; if a fourth state
|
|
225
|
+
emerges from the orchestrator, name it semantically and pair a new bg + fg
|
|
226
|
+
before adding it here.
|
|
227
|
+
|
|
228
|
+
**The Tinted Neutral Rule.** No pure greys. Every neutral leans on the cool
|
|
229
|
+
blue brand axis (hue ≈260) at near-zero chroma (0.005–0.012). Pure `#000` and
|
|
230
|
+
pure `#fff` are prohibited.
|
|
231
|
+
|
|
232
|
+
## 3. Typography
|
|
233
|
+
|
|
234
|
+
**Body Font:** `ui-sans-serif, system-ui, sans-serif` (browser native).
|
|
235
|
+
**Display Font:** none. The same family carries every role.
|
|
236
|
+
**Mono / Data Font:** still the same family, with `font-variant-numeric:
|
|
237
|
+
tabular-nums` on every cell that holds a number.
|
|
238
|
+
|
|
239
|
+
**Character:** the system font of whatever OS the user is on. No web fonts,
|
|
240
|
+
no FOUT, no loading flash. The console reads like the operating system, not
|
|
241
|
+
like a product. Numbers line up; text doesn't try to be elegant.
|
|
242
|
+
|
|
243
|
+
### Hierarchy
|
|
244
|
+
|
|
245
|
+
The scale is intentionally tight, with one density trick: section titles (h2)
|
|
246
|
+
match body in size and depend on a divider rule plus generous top margin for
|
|
247
|
+
hierarchy, not on type contrast. This keeps the page feeling dense and
|
|
248
|
+
console-like rather than article-like.
|
|
249
|
+
|
|
250
|
+
- **Headline** (h1, 400, `1.3rem` ≈ 20.8px, lh 1.2): the page title only.
|
|
251
|
+
Appears once per page (`Symphony — tracker.root = …`).
|
|
252
|
+
- **Title** (h2, 400, `1rem` = 16px, lh 1.3, with a `1px` bottom rule in Firm
|
|
253
|
+
Rule): section labels (`Create issue`, `Active sessions`, etc.). Same size
|
|
254
|
+
as a paragraph; the rule beneath does the hierarchy work.
|
|
255
|
+
- **Body** (400, `14px`, lh 1.45): default text, table cells.
|
|
256
|
+
- **Label** (400, `14px`, color Muted): form labels and table headers.
|
|
257
|
+
- **Data** (400, `14px`, `tabular-nums`): every number that appears in a
|
|
258
|
+
cell. Token counts, attempt numbers, runtime seconds, time-of-day.
|
|
259
|
+
- **TH** (500, `14px`, color Muted): table column headers. Heavier than body
|
|
260
|
+
by one step (400 → 500), so columns name themselves.
|
|
261
|
+
- **Pill** (400, `0.85em` ≈ 11.9px against 14px body): the three status pills.
|
|
262
|
+
Smaller than body so pills read as inline metadata, not content.
|
|
263
|
+
|
|
264
|
+
### Named Rules
|
|
265
|
+
|
|
266
|
+
**The System-Font Rule.** No web fonts are loaded, ever. The console must
|
|
267
|
+
look like the user's OS. If a custom face is added later, it has to justify
|
|
268
|
+
the latency cost in writing.
|
|
269
|
+
|
|
270
|
+
**The Tabular-Numbers Rule.** Any cell that contains a number applies
|
|
271
|
+
`font-variant-numeric: tabular-nums`. Token counts, attempt counts,
|
|
272
|
+
timestamps, IDs with digits: all of them. Columns of numbers must line up.
|
|
273
|
+
|
|
274
|
+
**The Same-Size Section-Header Rule.** Section titles (h2) are the same size
|
|
275
|
+
as body. Hierarchy comes from the divider rule beneath and the top margin
|
|
276
|
+
above, not from type scale. This keeps page density high.
|
|
277
|
+
|
|
278
|
+
## 4. Elevation
|
|
279
|
+
|
|
280
|
+
**The system is flat.** No `box-shadow` rule appears anywhere in the
|
|
281
|
+
stylesheet. Depth is conveyed entirely by tonal stacking of the neutrals:
|
|
282
|
+
`surface-inset` (deepest) → `surface-base` (default) → `surface-raised` (form
|
|
283
|
+
card) → `surface-chip` (refresh button, idle pill).
|
|
284
|
+
|
|
285
|
+
There is no hover lift, no focus glow, no drop shadow on the form card. A
|
|
286
|
+
panel reads as "raised" only because it is one step lighter than the bench
|
|
287
|
+
beneath it.
|
|
288
|
+
|
|
289
|
+
### Named Rules
|
|
290
|
+
|
|
291
|
+
**The Flat-By-Default Rule.** Surfaces are flat. Shadows are forbidden.
|
|
292
|
+
Depth cues come from the four-step neutral stack and from the divider rules
|
|
293
|
+
under section titles. If a future state needs visible elevation, introduce a
|
|
294
|
+
fifth neutral step before reaching for `box-shadow`.
|
|
295
|
+
|
|
296
|
+
**The No-Glow Rule.** Focus, hover, and active states change color or
|
|
297
|
+
border, never glow. Glassmorphism, blur backdrops, and shimmer animations
|
|
298
|
+
are prohibited (carries PRODUCT.md's AI-product-chrome anti-reference).
|
|
299
|
+
|
|
300
|
+
## 5. Components
|
|
301
|
+
|
|
302
|
+
The doctrine is **crisp and exact**: minimal radius vocabulary (4px for
|
|
303
|
+
controls, 8px for the form card, 999px for pills), 1px borders only where
|
|
304
|
+
structurally necessary, hairline-tight alignment, no decorative motion.
|
|
305
|
+
|
|
306
|
+
### Buttons
|
|
307
|
+
|
|
308
|
+
Two variants. Both share radius `4px` and the body type role.
|
|
309
|
+
|
|
310
|
+
- **Primary** (Create issue): background Dispatch Blue (`#2a6df4`), text
|
|
311
|
+
on-accent (`#ffffff`, see Don'ts), padding `0.5rem 1rem`, no border. Placed
|
|
312
|
+
inside the form grid as `grid-column: 2; justify-self: start`, so it
|
|
313
|
+
aligns under the input column rather than spanning the form.
|
|
314
|
+
- **Ghost / Refresh** (next to "Active sessions"): background Chip
|
|
315
|
+
(`#20242c`), text Base (`#dfe2e7`), 1px Firm Rule border, padding
|
|
316
|
+
`0.3rem 0.7rem`. Smaller than primary on every axis; used for
|
|
317
|
+
console-style "refresh now" actions only.
|
|
318
|
+
|
|
319
|
+
Hover and focus states are presently unstyled. The "crisp and exact"
|
|
320
|
+
philosophy permits adding a `1px` Dispatch Blue focus-visible ring on both
|
|
321
|
+
variants in a future pass; do not add hover lift or color shifts that imply
|
|
322
|
+
elevation.
|
|
323
|
+
|
|
324
|
+
### Inputs
|
|
325
|
+
|
|
326
|
+
`<input>`, `<select>`, `<textarea>` share one style.
|
|
327
|
+
|
|
328
|
+
- **Background** Inset (`#0c0f15`), one step *deeper* than the form card.
|
|
329
|
+
Inputs read as sunken slots, not floating tiles.
|
|
330
|
+
- **Border** `1px solid` Firm Rule (`#2a2e36`).
|
|
331
|
+
- **Text** Strong (`#e6ebf2`) so what is being typed sits one step above
|
|
332
|
+
surrounding body text.
|
|
333
|
+
- **Radius** `4px`.
|
|
334
|
+
- **Padding** `0.4rem 0.6rem`.
|
|
335
|
+
- **Width** `100%` of the form grid's value column.
|
|
336
|
+
- **Textareas** add `min-height: 80px; resize: vertical`.
|
|
337
|
+
|
|
338
|
+
Focus state is currently the browser default. Adding a `1px` Dispatch Blue
|
|
339
|
+
focus-visible outline is the obvious next refinement, consistent with the
|
|
340
|
+
philosophy.
|
|
341
|
+
|
|
342
|
+
### Card (the create-issue form)
|
|
343
|
+
|
|
344
|
+
The only card-shaped surface in the system. There is exactly one.
|
|
345
|
+
|
|
346
|
+
- **Background** Raised Panel (`#161a22`).
|
|
347
|
+
- **Radius** `8px` (the only place 8px appears).
|
|
348
|
+
- **Padding** `1rem`.
|
|
349
|
+
- **No border, no shadow.**
|
|
350
|
+
- **Layout** `display: grid; grid-template-columns: max-content 1fr; gap:
|
|
351
|
+
0.5rem 1rem; align-items: center;` so labels (max-content) align with
|
|
352
|
+
their inputs (1fr) on the same row.
|
|
353
|
+
|
|
354
|
+
This is the workshop's input slot. The 8px corner is intentionally slightly
|
|
355
|
+
softer than the rest of the system; on the next polish pass we may sharpen
|
|
356
|
+
it to 4px or to a `1px` Firm Rule border with no radius. Either move would
|
|
357
|
+
push the system further toward CLI-in-HTML.
|
|
358
|
+
|
|
359
|
+
### Pills (status chips)
|
|
360
|
+
|
|
361
|
+
`display: inline-block`, `padding: 0.1rem 0.5rem`, `border-radius: 999px`,
|
|
362
|
+
`font-size: 0.85em`. Three semantic instances, each a paired bg + fg:
|
|
363
|
+
|
|
364
|
+
- **Running** — bg `#1f3a26`, fg `#58d68d`. Live session.
|
|
365
|
+
- **Retrying** — bg `#3a2f1f`, fg `#f0c060`. Backoff queue.
|
|
366
|
+
- **Idle** — bg `#20242c`, fg `#9aa4b2`. On disk, not active.
|
|
367
|
+
|
|
368
|
+
No fourth pill exists. If a new orchestrator state appears, the new pill
|
|
369
|
+
gets a named bg + fg pair entered into the colors block and a row added in
|
|
370
|
+
this section. Do not reuse an existing pair for a new meaning.
|
|
371
|
+
|
|
372
|
+
### Tables
|
|
373
|
+
|
|
374
|
+
The dominant content shape. Three tables on the page: Active sessions, Retry
|
|
375
|
+
queue, All known issues.
|
|
376
|
+
|
|
377
|
+
- `width: 100%`, `border-collapse: collapse`.
|
|
378
|
+
- `th, td` padding `0.4rem 0.6rem`, `border-bottom: 1px solid` Soft Rule
|
|
379
|
+
(`#1c2029`).
|
|
380
|
+
- `th` text-align left, color Muted, weight 500.
|
|
381
|
+
- Every cell inherits `font-variant-numeric: tabular-nums`.
|
|
382
|
+
- No striping. The Soft Rule divider is enough.
|
|
383
|
+
|
|
384
|
+
### Inline status messages
|
|
385
|
+
|
|
386
|
+
The form's `.msg` line is `grid-column: 1 / span 2; min-height: 1.2em`. It
|
|
387
|
+
holds three states: neutral (color Muted), error (`#ff7676`), ok (`#58d68d`).
|
|
388
|
+
Always inline, never a toast, never a modal.
|
|
389
|
+
|
|
390
|
+
### Named Rules
|
|
391
|
+
|
|
392
|
+
**The One-Card Rule.** The create-issue form is the only card-shaped
|
|
393
|
+
surface. Other sections are unbordered blocks separated by section titles
|
|
394
|
+
and divider rules. Resist adding cards to wrap tables or stats.
|
|
395
|
+
|
|
396
|
+
**The Inline-Feedback Rule.** Success and error feedback land in the same
|
|
397
|
+
inline `.msg` slot where the form sits. No toast notifications. No modal
|
|
398
|
+
confirmations. The user already saw the issue land in the table below.
|
|
399
|
+
|
|
400
|
+
**The Sunken-Input Rule.** Inputs are visually deeper than the surface they
|
|
401
|
+
sit on (Inset against Raised Panel). They must not appear to float; if a
|
|
402
|
+
new input style is introduced, it follows this depth rule.
|
|
403
|
+
|
|
404
|
+
## 6. Do's and Don'ts
|
|
405
|
+
|
|
406
|
+
### Do:
|
|
407
|
+
- **Do** stack neutrals in four steps (Inset → Bench → Raised Panel → Chip)
|
|
408
|
+
to convey depth. No shadows.
|
|
409
|
+
- **Do** keep Dispatch Blue (`#2a6df4`) on exactly one element per page: the
|
|
410
|
+
primary verb. Everywhere else, status colors and text weight do the work.
|
|
411
|
+
- **Do** apply `font-variant-numeric: tabular-nums` to every cell holding a
|
|
412
|
+
number. Token counts and attempt numbers must align across rows.
|
|
413
|
+
- **Do** name new colors semantically (`surface-chip`, `retrying-fg`), not
|
|
414
|
+
by hue (`grey-3`, `green-400`). The token name is the contract.
|
|
415
|
+
- **Do** put feedback inline in the `.msg` slot. Errors and successes never
|
|
416
|
+
leave their grid cell.
|
|
417
|
+
- **Do** keep the type scale tight: h1 1.3rem, h2 1rem (same as body),
|
|
418
|
+
body 14px, pill 0.85em. Hierarchy through rules and weight, not scale.
|
|
419
|
+
- **Do** keep section titles (h2) the same size as body, separated by a
|
|
420
|
+
Firm Rule underline and 2rem of top margin.
|
|
421
|
+
|
|
422
|
+
### Don't:
|
|
423
|
+
- **Don't** add a fourth saturated accent. There is one verb color. Status
|
|
424
|
+
pills carry the only other saturation in the system, and they encode three
|
|
425
|
+
named states. A fourth pill is a smell.
|
|
426
|
+
- **Don't** ship a sidebar nav, breadcrumb trail, or a "Welcome back" hero.
|
|
427
|
+
PRODUCT.md flags this as generic-SaaS-dashboard chrome; the workshop has
|
|
428
|
+
one room. Add a tab strip only when a second page genuinely exists.
|
|
429
|
+
- **Don't** use big-number-with-tiny-label metric tiles. The hero-metric
|
|
430
|
+
template is explicitly prohibited.
|
|
431
|
+
- **Don't** add violet gradients, glassmorphic blur, sparkle / star icons,
|
|
432
|
+
or any "AI inside" iconography. smol-symphony runs AI agents but is not an
|
|
433
|
+
AI product. (PRODUCT.md anti-reference, verbatim.)
|
|
434
|
+
- **Don't** add Jira-style status pills whose colors do not map to distinct
|
|
435
|
+
orchestrator state. Three pills, three states, three bg + fg pairs. That
|
|
436
|
+
is the entire vocabulary.
|
|
437
|
+
- **Don't** introduce `box-shadow`. Depth is tonal. If you reach for a
|
|
438
|
+
shadow, add a fifth neutral step instead.
|
|
439
|
+
- **Don't** use `border-left` greater than `1px` as a colored stripe on
|
|
440
|
+
rows, cards, or alerts (carries impeccable's absolute ban).
|
|
441
|
+
- **Don't** use `#000` or `#fff` as text or background. The single existing
|
|
442
|
+
`color: white` on the primary button is a known soft spot to tint toward
|
|
443
|
+
the brand hue (target ≈`oklch(98% 0.005 260)`) on the next polish pass.
|
|
444
|
+
- **Don't** introduce a web font. The console reads like the OS;
|
|
445
|
+
`ui-sans-serif, system-ui, sans-serif` is non-negotiable unless an
|
|
446
|
+
intentional editorial change is being made.
|
|
447
|
+
- **Don't** add modals. Inline-first, always. PRODUCT.md flags
|
|
448
|
+
modal-on-modal flows as Jira-ITSM heaviness.
|
|
449
|
+
- **Don't** animate layout properties or add motion that doesn't encode
|
|
450
|
+
state. The 2-second polling refresh is the only "motion" the system has,
|
|
451
|
+
and that is informational, not decorative.
|
|
452
|
+
- **Don't** wrap content in a generic container. The form is the one card.
|
|
453
|
+
Tables and stat lines stand on the bench directly.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Didrik A. Rognstad
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/PRODUCT.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Product
|
|
2
|
+
|
|
3
|
+
## Register
|
|
4
|
+
|
|
5
|
+
product
|
|
6
|
+
|
|
7
|
+
## Users
|
|
8
|
+
|
|
9
|
+
Open-source developers self-hosting smol-symphony on their own machine (or a
|
|
10
|
+
trusted home server reached over tailscale). They run the orchestrator from a
|
|
11
|
+
terminal, live mostly in their editor, and open the HTTP dashboard to triage
|
|
12
|
+
work and watch agents run.
|
|
13
|
+
|
|
14
|
+
The dashboard is a glance surface, not a destination. A typical session: pop it
|
|
15
|
+
open, add an issue or two, scan what's running, close it. They are not babysitting
|
|
16
|
+
a feed.
|
|
17
|
+
|
|
18
|
+
Context when using it: localhost or LAN, modern desktop browser, usually a second
|
|
19
|
+
monitor or background tab next to the editor and a terminal where the symphony
|
|
20
|
+
process is logging.
|
|
21
|
+
|
|
22
|
+
## Product Purpose
|
|
23
|
+
|
|
24
|
+
smol-symphony is a small TypeScript orchestrator that takes Markdown issues off a
|
|
25
|
+
local tracker, prepares per-issue workspaces, and runs coding agents
|
|
26
|
+
(Claude Code, Codex, OpenCode) inside isolated smolvm microVMs over ACP. The
|
|
27
|
+
HTTP dashboard exists to do two things well:
|
|
28
|
+
|
|
29
|
+
1. **Dispatch** — create issues into the tracker without dropping back to the
|
|
30
|
+
filesystem.
|
|
31
|
+
2. **Triage** — see at a glance which sessions are running, which are retrying,
|
|
32
|
+
and which issues are sitting idle on disk.
|
|
33
|
+
|
|
34
|
+
Success is when a self-hoster can keep agents fed and notice problems quickly,
|
|
35
|
+
without ever feeling they are using a SaaS product.
|
|
36
|
+
|
|
37
|
+
## Brand Personality
|
|
38
|
+
|
|
39
|
+
Quiet. Precise. Infrastructural.
|
|
40
|
+
|
|
41
|
+
Reads like a well-built CLI rendered in HTML: terse labels, technical accuracy,
|
|
42
|
+
no flourish. The orchestrator itself is small (the "smol" is load-bearing) and
|
|
43
|
+
the dashboard should feel like an honest extension of it, not an aspirational
|
|
44
|
+
face glued on top.
|
|
45
|
+
|
|
46
|
+
Voice: direct, lowercase-comfortable, names things by what they are
|
|
47
|
+
(`tracker.root`, `retry queue`, `usage_update`). Never marketing copy. Never a
|
|
48
|
+
welcoming hero. The user already chose to run this.
|
|
49
|
+
|
|
50
|
+
## Anti-references
|
|
51
|
+
|
|
52
|
+
Three patterns this must never resemble:
|
|
53
|
+
|
|
54
|
+
- **Generic SaaS dashboard.** Sidebar nav, gradient hero tiles, big-number /
|
|
55
|
+
tiny-label metric cards, identical-card grids, "Welcome back" greetings.
|
|
56
|
+
Indistinguishable-Linear-clone aesthetic. The big-number-tiny-label
|
|
57
|
+
hero-metric template is explicitly banned.
|
|
58
|
+
- **Jira / enterprise ITSM heaviness.** Modal-on-modal workflows, status pills
|
|
59
|
+
whose colors do not actually map to meaningful state, dense forms with
|
|
60
|
+
required fields nobody fills in, the texture of bureaucracy. Status pills
|
|
61
|
+
are allowed only when they encode real, distinct states.
|
|
62
|
+
- **AI-product chrome.** Violet gradients, glassmorphic cards, sparkle / star
|
|
63
|
+
iconography signalling "AI inside". smol-symphony runs AI agents but is not
|
|
64
|
+
an AI product; the agents are infrastructure, not the narrative. No
|
|
65
|
+
purple-gradient-on-dark, no shimmer, no animated thinking dots.
|
|
66
|
+
|
|
67
|
+
## Design Principles
|
|
68
|
+
|
|
69
|
+
1. **Match the orchestrator's restraint.** The codebase is small and
|
|
70
|
+
plumbing-first; the dashboard should feel like an extension of that, not a
|
|
71
|
+
product face bolted on. If a feature would not exist in a pure CLI version
|
|
72
|
+
of the same tool, justify why it earns space in the UI.
|
|
73
|
+
|
|
74
|
+
2. **Show real state; refuse status theater.** Every pill, color, and number
|
|
75
|
+
must correspond to a distinct, true thing the orchestrator knows. No
|
|
76
|
+
progress affordances that don't reflect progress. No counts that don't
|
|
77
|
+
count. Tokens, attempts, due-at timestamps — exact values, no rounding for
|
|
78
|
+
aesthetics.
|
|
79
|
+
|
|
80
|
+
3. **Optimize for the glance, not the session.** Users are not living in this
|
|
81
|
+
tab. The first second of looking at the page should answer "is anything
|
|
82
|
+
stuck?" and "is anything running?" Density beats whitespace luxury for
|
|
83
|
+
the running / retry / issue tables.
|
|
84
|
+
|
|
85
|
+
4. **Agents are not the brand.** This tool dispatches Claude, Codex, OpenCode —
|
|
86
|
+
it does not perform AI-ness. The product narrative is "small orchestrator
|
|
87
|
+
with isolated VM execution", not "AI-powered work runner". Visual language
|
|
88
|
+
should reflect that.
|
|
89
|
+
|
|
90
|
+
5. **Read like a CLI in HTML form.** Monospace-friendly information, tabular
|
|
91
|
+
numbers, exact identifiers, plain verbs. If a label sounds like marketing,
|
|
92
|
+
replace it with the field name.
|
|
93
|
+
|
|
94
|
+
## Accessibility & Inclusion
|
|
95
|
+
|
|
96
|
+
Bare minimum responsible baseline for a self-hosted dev tool:
|
|
97
|
+
|
|
98
|
+
- Keyboard navigation works for the create-issue form and any actionable
|
|
99
|
+
controls.
|
|
100
|
+
- Contrast on text and pills is legible against the chosen background (no
|
|
101
|
+
decorative-only low-contrast text).
|
|
102
|
+
- Form labels are real `<label for=...>` associations, not floating placeholders.
|
|
103
|
+
|
|
104
|
+
Out of scope unless requested: WCAG 2.2 AA certification, screen-reader landmark
|
|
105
|
+
choreography, full reduced-motion design system, light-mode parity. Revisit if
|
|
106
|
+
real users report needs.
|