@augurworks/augur 0.15.1
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/CANVAS.md +858 -0
- package/INSTALL.md +603 -0
- package/LICENSE +27 -0
- package/README.md +150 -0
- package/TRADEMARKS.md +72 -0
- package/agents/README.md +65 -0
- package/agents/canon-extract.md +117 -0
- package/agents/canon.md +151 -0
- package/agents/canvas.md +88 -0
- package/agents/currency.md +74 -0
- package/agents/drafts.md +92 -0
- package/agents/identity.md +148 -0
- package/agents/prototype-contract.md +69 -0
- package/agents/publishing.md +324 -0
- package/agents/review-feedback.md +76 -0
- package/agents/space-json.md +132 -0
- package/agents/ui-skill.md +101 -0
- package/agents/working-marks.md +86 -0
- package/brand/augur-eye.svg +3 -0
- package/brand/augur-icon.svg +41 -0
- package/brand/augur-mark.svg +5 -0
- package/brand/lentianova-bulky.otf +0 -0
- package/brand/lentianova-median.otf +0 -0
- package/brand/lentianova.woff2 +0 -0
- package/build.js +8409 -0
- package/changelog.md +133 -0
- package/fonts/inter-latin-wght-normal.woff2 +0 -0
- package/package.json +57 -0
- package/pitis/PETS.md +121 -0
- package/pitis/README.md +117 -0
- package/pitis/index.html +266 -0
- package/pitis/piti.build.js +81 -0
- package/pitis/piti.js +929 -0
- package/pitis/roast-agent.md +185 -0
- package/realtime/src/index.js +119 -0
- package/realtime/wrangler.example.toml +44 -0
- package/scripts/adopt.mjs +106 -0
- package/scripts/board-snapshot.mjs +197 -0
- package/scripts/build-seed-pack.mjs +33 -0
- package/scripts/bundle-rekey.mjs +112 -0
- package/scripts/bundle-tenancy-rehearsal.mjs +781 -0
- package/scripts/canon-extract.mjs +578 -0
- package/scripts/canon.mjs +599 -0
- package/scripts/canvas-screen.mjs +333 -0
- package/scripts/check-local.mjs +136 -0
- package/scripts/clawd-art.mjs +75 -0
- package/scripts/clawd-canvas.mjs +741 -0
- package/scripts/cli.mjs +100 -0
- package/scripts/clone.mjs +422 -0
- package/scripts/close.mjs +30 -0
- package/scripts/connect.mjs +103 -0
- package/scripts/deploy.mjs +157 -0
- package/scripts/dev.mjs +54 -0
- package/scripts/doc-lint.mjs +143 -0
- package/scripts/export.mjs +300 -0
- package/scripts/fork.mjs +64 -0
- package/scripts/freeze.mjs +74 -0
- package/scripts/frontdoor-parity.mjs +180 -0
- package/scripts/hook.mjs +67 -0
- package/scripts/identity-rekey.mjs +112 -0
- package/scripts/init.mjs +122 -0
- package/scripts/land.mjs +40 -0
- package/scripts/lib/adapters.mjs +156 -0
- package/scripts/lib/board-snapshot.mjs +511 -0
- package/scripts/lib/draft.mjs +385 -0
- package/scripts/lib/graduate.mjs +276 -0
- package/scripts/lib/instance.mjs +74 -0
- package/scripts/lib/marks.mjs +107 -0
- package/scripts/lib/materialize.mjs +138 -0
- package/scripts/lib/merge3.mjs +124 -0
- package/scripts/lib/offline-posture.mjs +120 -0
- package/scripts/lib/offline-respawn.mjs +18 -0
- package/scripts/lib/offline-wrangler.mjs +78 -0
- package/scripts/lib/publish-compose.mjs +12 -0
- package/scripts/lib/publish-conflict.mjs +78 -0
- package/scripts/lib/publish-evidence.mjs +144 -0
- package/scripts/lib/refine-compare.mjs +140 -0
- package/scripts/lib/refine-ledger.mjs +150 -0
- package/scripts/lib/refine-png.mjs +149 -0
- package/scripts/lib/seed-pack-build.mjs +141 -0
- package/scripts/lib/state-compare.mjs +68 -0
- package/scripts/lib/store.mjs +122 -0
- package/scripts/login.mjs +84 -0
- package/scripts/mark.mjs +112 -0
- package/scripts/migrate-board-keys.mjs +164 -0
- package/scripts/migrate.mjs +265 -0
- package/scripts/no-foreign-vocabulary.mjs +216 -0
- package/scripts/no-product-names.mjs +157 -0
- package/scripts/no-tenant-globals.mjs +1134 -0
- package/scripts/offline.mjs +247 -0
- package/scripts/og.mjs +170 -0
- package/scripts/one-tenant-resolver.mjs +146 -0
- package/scripts/open.mjs +63 -0
- package/scripts/publish.mjs +1041 -0
- package/scripts/read.mjs +31 -0
- package/scripts/refine.mjs +455 -0
- package/scripts/release-drift.mjs +138 -0
- package/scripts/restore.mjs +322 -0
- package/scripts/review.mjs +125 -0
- package/scripts/save.mjs +21 -0
- package/scripts/shell-lint.mjs +189 -0
- package/scripts/ship.mjs +460 -0
- package/scripts/shoot.mjs +264 -0
- package/scripts/smoke-invite.mjs +113 -0
- package/scripts/state-inventory.mjs +223 -0
- package/scripts/status.mjs +177 -0
- package/scripts/sync.mjs +27 -0
- package/scripts/tenant-do-rehearsal.mjs +1314 -0
- package/scripts/ui-copy-lint.mjs +202 -0
- package/scripts/watch.mjs +32 -0
- package/scripts/wrangler-preflight.mjs +246 -0
- package/seed/CANON.md +43 -0
- package/seed/README.md +119 -0
- package/seed/prototype-status.json +9 -0
- package/seed/registry.json +88 -0
- package/seed/skills/starter-ui/SKILL.md +52 -0
- package/seed/skills/starter-ui/skill.json +11 -0
- package/seed/skills/starter-ui/starter-tokens.css +73 -0
- package/seed/skills/starter-ui/starter-ui.css +318 -0
- package/seed/skills/starter-ui/starter-ui.js +50 -0
- package/seed/space.json +5 -0
- package/seed/start-here/prototypes/connect-your-terminal/index.html +428 -0
- package/seed/start-here/prototypes/connect-your-terminal/preview.webp +0 -0
- package/seed/start-here/prototypes/sample-with-comments/index.html +172 -0
- package/seed/start-here/prototypes/sample-with-comments/preview.webp +0 -0
- package/seed/start-here/prototypes/set-up-your-design-system/index.html +353 -0
- package/seed/start-here/prototypes/set-up-your-design-system/preview.webp +0 -0
- package/seed/threads.json +52 -0
- package/seed/worked-examples/prototypes/field-readings/index.html +273 -0
- package/seed/worked-examples/prototypes/field-readings/preview.webp +0 -0
- package/seed/worked-examples/prototypes/slide-deck/index.html +214 -0
- package/seed/worked-examples/prototypes/slide-deck/preview.webp +0 -0
- package/seed/worked-examples/prototypes/specimen-viewer/index.html +203 -0
- package/seed/worked-examples/prototypes/specimen-viewer/preview.webp +0 -0
- package/src/_worker.js +12668 -0
- package/src/board-key.mjs +57 -0
- package/src/board-room.mjs +881 -0
- package/src/bundle-keys.mjs +138 -0
- package/src/canon/collect-in-browser.js +215 -0
- package/src/canon/emit.mjs +232 -0
- package/src/canon/observe.mjs +260 -0
- package/src/canon/schema.mjs +451 -0
- package/src/canvas/DSEG-LICENSE.txt +95 -0
- package/src/canvas/DSEG7Classic-Bold.woff2 +0 -0
- package/src/canvas/canvas.css +885 -0
- package/src/canvas/canvas.js +5780 -0
- package/src/canvas/capture.js +485 -0
- package/src/chrome/appchrome.mjs +568 -0
- package/src/currency.mjs +203 -0
- package/src/delete-confirmation.mjs +254 -0
- package/src/drafts/drafts.js +326 -0
- package/src/entry.js +58 -0
- package/src/galleries.mjs +400 -0
- package/src/health-cron.mjs +173 -0
- package/src/identity.json +1 -0
- package/src/kv-codec.mjs +135 -0
- package/src/kv-identity.mjs +259 -0
- package/src/mail.mjs +599 -0
- package/src/provenance.mjs +74 -0
- package/src/publish-compose.mjs +363 -0
- package/src/publish-fork.mjs +221 -0
- package/src/publish-units.mjs +44 -0
- package/src/purge.mjs +81 -0
- package/src/review/COMMENTING-UX.md +306 -0
- package/src/review/cat.png +0 -0
- package/src/review/comment-cursor.svg +7 -0
- package/src/review/comments.js +1738 -0
- package/src/room-ticket.mjs +96 -0
- package/src/roster-chip.mjs +32 -0
- package/src/seed-pack.mjs +261 -0
- package/src/state-inventory.mjs +249 -0
- package/src/sw-logic.mjs +48 -0
- package/src/tenant-cache.mjs +141 -0
- package/src/tenant-context.mjs +386 -0
- package/src/tenant-do.js +2940 -0
- package/src/tenant-host.mjs +144 -0
- package/src/tenant-quotas.mjs +140 -0
- package/src/unit-core.mjs +103 -0
- package/src/unit-object.mjs +338 -0
- package/templates/README.md +67 -0
- package/templates/shell/deploy.yml +145 -0
- package/templates/shell/engine-bump.yml +142 -0
- package/templates/shell/health.yml +233 -0
- package/templates/shell/kv-backup.yml +201 -0
- package/templates/shell/roster-update.yml +110 -0
- package/templates/shell/space-preflight.yml +35 -0
- package/templates/shell/space-rebake.yml +79 -0
- package/templates/shell/store-backup.yml +152 -0
- package/templates/shell/wrangler.example.toml +266 -0
- package/templates/space/publish.yml +91 -0
package/README.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Augur
|
|
2
|
+
|
|
3
|
+
A prototype and research repository for product teams. Real, clickable
|
|
4
|
+
prototypes and the design system they are built from, on one site with login,
|
|
5
|
+
comments and live boards on top. Underneath it is all git and static HTML.
|
|
6
|
+
|
|
7
|
+
**Building the prototypes is the easy part.** An agent can produce a convincing
|
|
8
|
+
screen in a minute, and most teams already have more of them than they can find.
|
|
9
|
+
What is missing is everything around them: where the last version went, who
|
|
10
|
+
changed it, which of the four things called "the dashboard" is the one being
|
|
11
|
+
reviewed, and where the feedback landed. That is what this is. Prototypes live
|
|
12
|
+
in one place with the design system they are made from, comments stick to the
|
|
13
|
+
pixels they are about, boards let you run the real screens side by side, and
|
|
14
|
+
every version is a commit you can walk back to.
|
|
15
|
+
|
|
16
|
+
It runs on your own box, or on somebody else's. There is no editor to be locked
|
|
17
|
+
out of and no export to negotiate: a prototype is a folder of static HTML in a
|
|
18
|
+
git repository you already own.
|
|
19
|
+
|
|
20
|
+
**See it running: [demo.augur.works](https://demo.augur.works)**, sign in with
|
|
21
|
+
`visita@fulla.demo` / `regadora`. It resets every night, so scribble away.
|
|
22
|
+
|
|
23
|
+

|
|
24
|
+
|
|
25
|
+
The demo runs [Fulla](https://github.com/andratwiro/augur-space-fulla), an
|
|
26
|
+
invented community garden product. That repo doubles as the starter space, so
|
|
27
|
+
everything below works against it.
|
|
28
|
+
|
|
29
|
+
## How a prototype gets made
|
|
30
|
+
|
|
31
|
+
There is no editor. A prototype is briefed, not drawn. You describe the screen
|
|
32
|
+
the way you would brief a designer, and an agent builds it as a page in the
|
|
33
|
+
space, using the design system like anyone else on the team.
|
|
34
|
+
|
|
35
|
+
The brief that made [Seed swap](https://demo.augur.works/garden/seed-swap/):
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
Seed swap — a noticeboard where members offer saved seeds and claim each
|
|
39
|
+
other's. One screen. A board of offer cards: who, what, roughly how many,
|
|
40
|
+
which plot they came from — "Fava beans, saved from plot 12, ~40 seeds".
|
|
41
|
+
Claiming an offer keeps the card on the board but settles it down visually;
|
|
42
|
+
the swap happens at the shed, not in the app. The empty state suggests the
|
|
43
|
+
first action instead of apologizing. Warm and hand-made like the rest of
|
|
44
|
+
Fulla — use the fulla design system, don't invent new vocabulary. Add the
|
|
45
|
+
one-line meta description. No routing, no backend; seed the board with a
|
|
46
|
+
handful of believable offers.
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+

|
|
50
|
+
|
|
51
|
+
That is Claude Code in the capture; any coding agent that can read the
|
|
52
|
+
contracts in [agents/](./agents/) does the same job. Screens in the demo space
|
|
53
|
+
carry the session that made them: [open the repo and read one](https://github.com/andratwiro/augur-space-fulla/tree/main/garden/prompts).
|
|
54
|
+
The follow-ups read like design direction, because that is what they are.
|
|
55
|
+
|
|
56
|
+
## Boards where the prototypes run
|
|
57
|
+
|
|
58
|
+
Drop a live prototype next to the stickies and drive it. Everyone on the board
|
|
59
|
+
sees the same screen state. Agents join as pixel mascots and work next to you.
|
|
60
|
+
Up close: the specimen spins inside its tile, a teammate's cursor works the
|
|
61
|
+
area, and Menta the agent strolls over to watch.
|
|
62
|
+
|
|
63
|
+

|
|
64
|
+
|
|
65
|
+
## Comments on the real pixels
|
|
66
|
+
|
|
67
|
+
Shift+C on any prototype opens review mode. A pin sticks to the element it
|
|
68
|
+
talks about, the thread keeps everyone's face on it, and the design system
|
|
69
|
+
shows through as a layer.
|
|
70
|
+
|
|
71
|
+

|
|
72
|
+
|
|
73
|
+
## Build your design library
|
|
74
|
+
|
|
75
|
+
The same space that holds your prototypes holds your design system. Tokens,
|
|
76
|
+
base, components, patterns and pages each get a tier, every entry is a plain
|
|
77
|
+
HTML page you write, and the site renders it with a live preview and the class
|
|
78
|
+
names it documents. A small registry gives the review overlay the same
|
|
79
|
+
vocabulary, so a pin on a prototype knows which component it landed on.
|
|
80
|
+
|
|
81
|
+

|
|
82
|
+
|
|
83
|
+
Up close, at the tokens depth: paddings shade in place like devtools, and the
|
|
84
|
+
panel lists the tokens a component drinks with their live values. Token usage
|
|
85
|
+
is tracked to the pixel across every prototype that links the system.
|
|
86
|
+
|
|
87
|
+

|
|
88
|
+
|
|
89
|
+
## Try it locally
|
|
90
|
+
|
|
91
|
+
**Under ten seconds from an empty folder to a page in your browser**, and that is
|
|
92
|
+
a measured number rather than a hopeful one: three runs on a laptop with a fast
|
|
93
|
+
connection came in at 7.3s, 9.0s and 9.2s, stopwatch from `git clone` to a `200`
|
|
94
|
+
on `http://localhost:8788/` serving the real gallery. Most of that is the two
|
|
95
|
+
clones, so a slower connection moves the number and nothing else does — the
|
|
96
|
+
engine itself is about two seconds from cold to serving.
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
git clone https://github.com/andratwiro/augur.git
|
|
100
|
+
git clone https://github.com/andratwiro/augur-space-fulla.git
|
|
101
|
+
cd augur-space-fulla
|
|
102
|
+
node ../augur/scripts/dev.mjs
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
There is no install step and no build step in that list, and that is not an
|
|
106
|
+
omission: **the engine has no runtime dependencies**, so plain `node` is enough.
|
|
107
|
+
What you get is the full shell — the gallery, the library, review mode, boards —
|
|
108
|
+
with about a second of hot reload on every save. No login prompt, because a bare
|
|
109
|
+
clone has no user list to gate against; add one and the gate turns on.
|
|
110
|
+
|
|
111
|
+
Prototypes are self-contained static HTML, so `index.html` also opens straight
|
|
112
|
+
from disk. That is fine for your own glance and never a way to show anyone
|
|
113
|
+
anything: no chrome, no comments, no boards, and the link works on exactly one
|
|
114
|
+
machine.
|
|
115
|
+
|
|
116
|
+
## How it is put together
|
|
117
|
+
|
|
118
|
+
- **A space is a git repo.** Your design system and your prototypes, nothing
|
|
119
|
+
else. Only the contents of `prototypes/` folders and the gallery tiers ever
|
|
120
|
+
publish. The research notes sitting next to them stay private by
|
|
121
|
+
construction.
|
|
122
|
+
- **The engine is this repo.** It composes spaces into one static site and
|
|
123
|
+
runs the overlay worker on Cloudflare. It carries no secrets and no content.
|
|
124
|
+
- **A private deploy shell holds your instance.** The engine pin, the user
|
|
125
|
+
list, every secret. Engine fixes reach your instance by pin bump, never by
|
|
126
|
+
forking.
|
|
127
|
+
- **Publishing is `augur land`** on a workspace that serves drafts — open one
|
|
128
|
+
prototype, edit, land; seconds, atomic, with history. Self-hosted instances
|
|
129
|
+
without a unit store publish with `augur publish`. A git push saves and shares
|
|
130
|
+
work without deploying anything.
|
|
131
|
+
|
|
132
|
+
## Run your own
|
|
133
|
+
|
|
134
|
+
[INSTALL.md](./INSTALL.md) is the recipe, written to be executed top to bottom
|
|
135
|
+
by a person or an agent. About an hour, most of it waiting on DNS and CI.
|
|
136
|
+
|
|
137
|
+
## Docs
|
|
138
|
+
|
|
139
|
+
- [CLAUDE.md](./CLAUDE.md), the engine conventions
|
|
140
|
+
- [agents/](./agents/), the contracts for agents working in a space
|
|
141
|
+
- [CANVAS.md](./CANVAS.md), the board engine and how agents co-work on it
|
|
142
|
+
- [CONTRIBUTING.md](./CONTRIBUTING.md), fork to PR, never fork to deploy
|
|
143
|
+
|
|
144
|
+
## License
|
|
145
|
+
|
|
146
|
+
MIT for the code. See [LICENSE](./LICENSE).
|
|
147
|
+
|
|
148
|
+
The name, the marks in `brand/` and the typefaces bundled with them are not part of
|
|
149
|
+
that grant — [TRADEMARKS.md](./TRADEMARKS.md) says what you can do without asking
|
|
150
|
+
(fork it, rename it, say what it is) and what needs a word first.
|
package/TRADEMARKS.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Trademarks, brand assets and bundled fonts
|
|
2
|
+
|
|
3
|
+
[LICENSE](./LICENSE) is MIT and covers the **code**. It does not, and was never meant to,
|
|
4
|
+
hand over the project's name or the files in [`brand/`](./brand). This page says where that
|
|
5
|
+
line is, because a bare MIT file placed next to a logo reads as though it grants the logo
|
|
6
|
+
too.
|
|
7
|
+
|
|
8
|
+
## What the MIT licence does not grant
|
|
9
|
+
|
|
10
|
+
These are **unregistered** marks. That is what is true today and it is what this page
|
|
11
|
+
claims — no more, because claiming a registration you do not hold is a misrepresentation,
|
|
12
|
+
and no less, because an unregistered mark is still a mark.
|
|
13
|
+
|
|
14
|
+
**The name.** "Augur", used as the name of a product, a service, a company or a domain.
|
|
15
|
+
|
|
16
|
+
**The marks.** `brand/augur-mark.svg`, `brand/augur-icon.svg`, `brand/augur-eye.svg`, and
|
|
17
|
+
the copies of them that a build emits (`augur-mark.png`, `augur-icon-192.png`,
|
|
18
|
+
`augur-icon-512.png`, `augur-eye.svg`). Copying a file is not a licence to wear it.
|
|
19
|
+
|
|
20
|
+
**The fonts.** `brand/lentianova.woff2`, `brand/lentianova-bulky.otf` and
|
|
21
|
+
`brand/lentianova-median.otf` are **third-party typefaces, licensed to this project and not
|
|
22
|
+
sublicensable by it**. They sit in the repository because the build needs them; they are
|
|
23
|
+
not covered by the MIT grant, and no MIT grant could cover them, because they are not this
|
|
24
|
+
project's to give. If you are shipping something built from this repository, obtain your
|
|
25
|
+
own licence or substitute your own typeface — every font reference resolves through the
|
|
26
|
+
design-system tokens, so replacing it is a value change, not a code change.
|
|
27
|
+
|
|
28
|
+
> The project's own licence position on these typefaces is not yet settled for commercial
|
|
29
|
+
> use. Until it is, treat them as the most restricted files in the repository.
|
|
30
|
+
|
|
31
|
+
`src/canvas/DSEG7Classic-Bold.woff2` is different: it ships under the SIL Open Font
|
|
32
|
+
License, and its terms are in [`src/canvas/DSEG-LICENSE.txt`](./src/canvas/DSEG-LICENSE.txt)
|
|
33
|
+
alongside it.
|
|
34
|
+
|
|
35
|
+
## What you can do without asking
|
|
36
|
+
|
|
37
|
+
**Run it, fork it, change it, ship it.** That is what MIT is for, and none of the above
|
|
38
|
+
narrows it. A fork that keeps the code and replaces the name and the marks needs no
|
|
39
|
+
permission from anyone.
|
|
40
|
+
|
|
41
|
+
**Say what it is.** "Built on Augur", "compatible with Augur", "an Augur instance" —
|
|
42
|
+
describing a thing accurately by its name is nominative use, and it is fine. So is naming
|
|
43
|
+
the project in a talk, a blog post, an academic paper or a comparison table.
|
|
44
|
+
|
|
45
|
+
**Keep the marks in an unmodified copy.** Redistributing the repository as it is, marks
|
|
46
|
+
included, is fine. What is not fine is lifting the marks out of it.
|
|
47
|
+
|
|
48
|
+
## What needs asking
|
|
49
|
+
|
|
50
|
+
Using the name or the marks in a way that suggests a project is **the** Augur, or is
|
|
51
|
+
endorsed by, affiliated with or maintained by it. Concretely: naming your product or your
|
|
52
|
+
company Augur or something confusable with it; registering a domain of the form
|
|
53
|
+
`augur-<something>` for a service you run; putting the mark on a paid hosted service; or
|
|
54
|
+
using it as the icon of an application that is not this one.
|
|
55
|
+
|
|
56
|
+
Modified copies must not carry the marks. That is the whole point of the carve-out: the
|
|
57
|
+
mark is how someone tells which copy they are looking at, and a mark on a fork makes that
|
|
58
|
+
question unanswerable.
|
|
59
|
+
|
|
60
|
+
## Asking
|
|
61
|
+
|
|
62
|
+
Open an issue on the repository. There is no form and no fee.
|
|
63
|
+
|
|
64
|
+
## In short
|
|
65
|
+
|
|
66
|
+
Take the code. Leave the name.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
*This page describes the position; it is not legal advice, and it has not yet been through
|
|
71
|
+
a lawyer. The trademark itself is not yet cleared — see the project's own launch notes. If
|
|
72
|
+
you are relying on any of this commercially, ask your own counsel.*
|
package/agents/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# agents/ — engine docs for agents working in a workspace
|
|
2
|
+
|
|
3
|
+
These files are the engine's contract for the everyday things a **workspace
|
|
4
|
+
collaborator's agent** does with Augur: shipping work, reading review feedback,
|
|
5
|
+
what the engine expects of a prototype folder, the `space.json` schema, users
|
|
6
|
+
and avatars.
|
|
7
|
+
|
|
8
|
+
The word for the thing is **workspace** — one per instance, served at its root.
|
|
9
|
+
The FILE is still `space.json` and every identifier keeps its old spelling; see
|
|
10
|
+
[space-json.md](./space-json.md) for why that is a decision and not a leftover.
|
|
11
|
+
|
|
12
|
+
They ship WITH the engine so every instance gets the same, current contract —
|
|
13
|
+
when an engine change alters behavior, the same commit updates the doc here.
|
|
14
|
+
Workspace repos should link to these files (the engine clone sits next to every
|
|
15
|
+
workspace clone that publishes), never copy them: a copied contract drifts.
|
|
16
|
+
|
|
17
|
+
## Getting in
|
|
18
|
+
|
|
19
|
+
A workspace is a sign-in gate. Pages need a person's session; creating and publishing
|
|
20
|
+
prototypes needs a **publish token**, and an agent gets one by device pairing, never by
|
|
21
|
+
asking anyone for a password:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
npx augur connect --origin https://<the workspace>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
It prints one line to relay — *ask the owner of this workspace to open `<link>` and
|
|
28
|
+
enter `<code>`* — and waits. The owner opens the link in a browser they are already
|
|
29
|
+
signed in to and types the code; the token lands in `~/.config/augur/tokens.json`, and
|
|
30
|
+
`augur ship` / `augur publish` use it from then on. `publish` runs the pairing itself when
|
|
31
|
+
it finds no token, so inside a workspace tree nothing has to be done first. On a workspace
|
|
32
|
+
that serves drafts (`/.well-known/augur.json` says `drafts.enabled`), the everyday verbs
|
|
33
|
+
are `augur open` and `augur land` instead — see [drafts.md](./drafts.md). To get the
|
|
34
|
+
tree from a hostname, pair first, then `npx augur clone --space <id>` — it reads the
|
|
35
|
+
origin from the pairing.
|
|
36
|
+
|
|
37
|
+
Not on npm yet? The engine clone sits next to every workspace that publishes:
|
|
38
|
+
`node <engine>/scripts/cli.mjs connect --origin <origin>`. The instance says all of this
|
|
39
|
+
itself at `GET /llms.txt` (and as data at `/.well-known/augur.json`); a signed-out
|
|
40
|
+
request for an engine path answers `401` with the same facts.
|
|
41
|
+
|
|
42
|
+
Two things an agent never does: type a password into a terminal (`augur login` exists
|
|
43
|
+
for CI, and says so), and try the `pass` field from a deploy shell's `identity.json` —
|
|
44
|
+
that is a first-sign-in seed for a self-hosted instance, dead on a hosted workspace, and
|
|
45
|
+
the gate throttles failed attempts per address and per email.
|
|
46
|
+
|
|
47
|
+
Read them by trigger, not up front:
|
|
48
|
+
|
|
49
|
+
| When you are… | Read |
|
|
50
|
+
|---|---|
|
|
51
|
+
| asking what is current here, or what changed lately | [currency.md](./currency.md) |
|
|
52
|
+
| starting work on a prototype somebody else might also be in | [working-marks.md](./working-marks.md) — read it BEFORE the first edit |
|
|
53
|
+
| changing a prototype on a workspace that serves drafts | [drafts.md](./drafts.md) — open, edit, land |
|
|
54
|
+
| shipping / going live on a workspace without drafts | [publishing.md](./publishing.md) |
|
|
55
|
+
| acting on review comments | [review-feedback.md](./review-feedback.md) |
|
|
56
|
+
| building a prototype folder | [prototype-contract.md](./prototype-contract.md) |
|
|
57
|
+
| naming a canonical screen, or asked to "pull screens X, Y, Z" | [canon.md](./canon.md) |
|
|
58
|
+
| editing `space.json` | [space-json.md](./space-json.md) |
|
|
59
|
+
| building or forking a design system (`skills/<x>-ui/`) | [ui-skill.md](./ui-skill.md) |
|
|
60
|
+
| asked to copy a design system out of a live product | [canon-extract.md](./canon-extract.md) |
|
|
61
|
+
| adding users / avatars / login questions | [identity.md](./identity.md) |
|
|
62
|
+
| a board-shaped ask (brainstorm, compare, map a flow) or canvas work | [canvas.md](./canvas.md) — includes WHEN to suggest a canvas |
|
|
63
|
+
|
|
64
|
+
Engine *development* (build.js, worker, overlays) is a different audience:
|
|
65
|
+
see [../CLAUDE.md](../CLAUDE.md) and [../CONTRIBUTING.md](../CONTRIBUTING.md).
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Extracting a design system from a live product
|
|
2
|
+
|
|
3
|
+
`augur canon` turns "copy the design system from this URL" into a flow: a working folder,
|
|
4
|
+
a fixed list of token roles, an evidence collector that runs where the login already is, a
|
|
5
|
+
grader that is arithmetic rather than taste, and an emitter that writes the result into the
|
|
6
|
+
workspace's design system.
|
|
7
|
+
|
|
8
|
+
**Augur does none of the deciding.** Which of a product's eleven greys is its hairline, and
|
|
9
|
+
which single colour means "act on this", is judgement. This engine ships the harness; the
|
|
10
|
+
user's own agent, on the user's own account with whatever provider they like, does the
|
|
11
|
+
judging. There is no model call in this repository, no API key, no endpoint, no inference
|
|
12
|
+
dependency, and `augur canon` added none — `../test/canon-no-inference.test.mjs` fails the
|
|
13
|
+
build on the commit that changes that.
|
|
14
|
+
|
|
15
|
+
## Why a URL and not a repository
|
|
16
|
+
|
|
17
|
+
Extracting a design system from a codebase is a solved problem and it is the wrong door for
|
|
18
|
+
the person who needs this one. The people who feel the cost of not having a canon are
|
|
19
|
+
product managers and designers: they hold a **login** to the product, not commit rights to
|
|
20
|
+
it. So the collector runs inside the browser they are already signed in to, sees the real
|
|
21
|
+
screens behind the login, and makes no request of its own.
|
|
22
|
+
|
|
23
|
+
## The flow
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
augur canon start <url> the working folder, the brief, the skeleton answer
|
|
27
|
+
augur canon snippet the collector to paste into your own browser
|
|
28
|
+
augur canon collect <url> or: read a PUBLIC page over HTTP
|
|
29
|
+
augur canon collect --merge fold several screens into one observation
|
|
30
|
+
augur canon grade grade the answer
|
|
31
|
+
augur canon apply write it into the workspace's design system
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`start` writes a `BRIEF.md` into the folder that names the actual URL, prefix and
|
|
35
|
+
workspace, with the role table inline. **That file is written for an agent to follow start
|
|
36
|
+
to finish**; hand it over rather than paraphrasing it.
|
|
37
|
+
|
|
38
|
+
## The three files
|
|
39
|
+
|
|
40
|
+
| File | What it is | Who writes it |
|
|
41
|
+
| --- | --- | --- |
|
|
42
|
+
| `observation.json` | evidence: ranked colours, type sizes, gaps, radii, shadows, the product's own custom properties, candidate class families | the collector |
|
|
43
|
+
| `canon.json` | the answer: a value for each token role, plus named components | **the user's agent** |
|
|
44
|
+
| the skill folder | `<prefix>-tokens.css`, `<prefix>-canon.css`, `skill.json`, `registry.json`, `CANON.md` | `apply` |
|
|
45
|
+
|
|
46
|
+
The observation is ranked by how much of the screen a value actually covers, not by how
|
|
47
|
+
many times it appears in a stylesheet — which is why the browser door is the better
|
|
48
|
+
evidence as well as the only one that gets past a login. Text colour is weighted by the ink
|
|
49
|
+
it puts on the page rather than by the box, so a full-height container inheriting a colour
|
|
50
|
+
it never paints does not outrank the body.
|
|
51
|
+
|
|
52
|
+
## One format, not two
|
|
53
|
+
|
|
54
|
+
The roles in `../src/canon/schema.mjs` are exactly the tokens a workspace is born with (see
|
|
55
|
+
`../seed/README.md`). That is enforced: `../test/canon-schema.test.mjs` fails if the seed
|
|
56
|
+
grows a token the schema does not name, or reads one the extractor would not emit.
|
|
57
|
+
|
|
58
|
+
It buys three things, and they are the reason the list is fixed rather than discovered per
|
|
59
|
+
product:
|
|
60
|
+
|
|
61
|
+
- **The screens a workspace already has re-skin instead of breaking.** Every prototype
|
|
62
|
+
links `<prefix>-tokens.css`; replacing the values moves all of them at once.
|
|
63
|
+
- **The page that teaches the design system keeps teaching it.** `set-up-your-design-system`
|
|
64
|
+
reads the token file live, so after an extraction it shows the team their own product.
|
|
65
|
+
- **A prototype written against one design system works against another.** Pulling a screen
|
|
66
|
+
from an example workspace into a real one is a copy, not a port.
|
|
67
|
+
|
|
68
|
+
Roles marked *computed* may be left out; they are derived from the ones that were observed,
|
|
69
|
+
and derived to be legible — a `mark-ink` computed from `mark` is guaranteed to clear 4.5:1
|
|
70
|
+
as text on the sheet, which a colour picked by eye frequently does not. A value the roles
|
|
71
|
+
have no slot for goes under an `x-` name and emits as a real token.
|
|
72
|
+
|
|
73
|
+
## What `apply` will and will not do
|
|
74
|
+
|
|
75
|
+
- **Regenerates** `<prefix>-tokens.css`. Safe because the roles are fixed.
|
|
76
|
+
- **Creates** `<prefix>-canon.css` with the extracted components. Its own file.
|
|
77
|
+
- **Never touches** the workspace's own component stylesheet (`<prefix>-ui.css`). That is
|
|
78
|
+
the workspace's writing, not the extractor's.
|
|
79
|
+
- **Refuses** a class name the workspace already defines. Two rules for one selector in two
|
|
80
|
+
files is a bug that shows up on one screen and nowhere else.
|
|
81
|
+
- **Merges** `registry.json` by name, keeping the workspace's own labels and descriptions.
|
|
82
|
+
- Emits under the **workspace's** prefix even when the canon calls itself something else —
|
|
83
|
+
renaming a workspace's tokens orphans every prototype that reads them.
|
|
84
|
+
|
|
85
|
+
## What the grader enforces
|
|
86
|
+
|
|
87
|
+
Arithmetic only, and each rule exists because the failure it catches is invisible in a diff:
|
|
88
|
+
|
|
89
|
+
- every observed role answered, every value the right kind;
|
|
90
|
+
- the type scale and the space ramp climb;
|
|
91
|
+
- body text clears 4.5:1 on the sheet, and so does the mark used as text;
|
|
92
|
+
- **no component hard-codes a colour** — a component that does stops moving when a token
|
|
93
|
+
changes, and then the design system is decoration;
|
|
94
|
+
- every `var()` a component reads is a token the canon defines;
|
|
95
|
+
- every class carries the skill's class prefix, and the CSS defines every class it lists.
|
|
96
|
+
|
|
97
|
+
`augur canon grade --space <workspace>` grades the design system a workspace is actually
|
|
98
|
+
carrying, read back off disk. A workspace that has never been near the extractor should
|
|
99
|
+
pass it.
|
|
100
|
+
|
|
101
|
+
## Where it stops
|
|
102
|
+
|
|
103
|
+
- The collector sees **rendered** values, so a colour that only exists in a state nobody
|
|
104
|
+
triggered — an error toast, a disabled row — is not in the evidence. Collect the screens
|
|
105
|
+
that matter, including the ugly ones.
|
|
106
|
+
- It reads no images. A logo, an illustration style and a photographic treatment are design
|
|
107
|
+
system and none of them are in here.
|
|
108
|
+
- **`font-display` and `font-body` are stacks, not files.** Nothing downloads a face, so a
|
|
109
|
+
stack naming a face the workspace does not already have falls back silently — and a near
|
|
110
|
+
fallback looks intentional, which makes it harder to notice, not easier. Self-host the
|
|
111
|
+
face and add the `@font-face` yourself, or name one that is already there.
|
|
112
|
+
- A role typed `length` holds one length, so a `border-radius` SHORTHAND (four values, or a
|
|
113
|
+
slash) has no role. Put a representative length in the role so the scale still works and
|
|
114
|
+
carry the real shorthand as an `x-` token.
|
|
115
|
+
- Dark mode is a second canon, not a second column: collect it separately.
|
|
116
|
+
- A cross-origin stylesheet cannot be read for custom properties. Computed styles still
|
|
117
|
+
count, and the observation says how many sheets were unreadable.
|
package/agents/canon.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# The canon — how canonical screens and parts are named
|
|
2
|
+
|
|
3
|
+
The canon is the part of a workspace that is meant to be **pulled by name**: "build it
|
|
4
|
+
the way `invoice-detail` is built", "pull `invoice-list`, `invoice-detail` and
|
|
5
|
+
`invoice-empty` and wire them together". That instruction is the whole return on having a
|
|
6
|
+
design system, and it presumes `invoice-detail` names something an agent can find COLD —
|
|
7
|
+
no human pointing, in a session that has never seen this workspace before.
|
|
8
|
+
|
|
9
|
+
Names decide whether that works, and names drift the moment they are only a habit. One
|
|
10
|
+
screen lands as `checkout-v2`, the next as `new_Checkout`, a third as a scratch folder
|
|
11
|
+
wearing the same name as a canonical page, and the instruction stops resolving. Renaming
|
|
12
|
+
everything afterwards works and costs a day. This file is the scheme that makes the day
|
|
13
|
+
unnecessary, and `augur canon` is that scheme as a command.
|
|
14
|
+
|
|
15
|
+
**Resolving a name is one line, and it needs nothing else read first:**
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
augur canon find invoice-detail # → the tier, the folder, the entry page, what it links
|
|
19
|
+
augur canon list # → every canonical name with its one-line description
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Both read the tree at the moment they are asked. There is no index file, so there is
|
|
23
|
+
nothing to regenerate and nothing that can be stale — the directory names ARE the index.
|
|
24
|
+
That holds only while they obey the scheme below, which is what `augur canon check` is
|
|
25
|
+
for.
|
|
26
|
+
|
|
27
|
+
**If the workspace has no canon yet, this file is the second thing to read.** A team that
|
|
28
|
+
would benefit from a canon usually already has one — their live product — and
|
|
29
|
+
`augur canon <start|collect|snippet|grade|apply>` copies it out of there into this
|
|
30
|
+
workspace's design system: see [canon-extract.md](canon-extract.md). Same command, same
|
|
31
|
+
`canon` word, because it is the same job at a different stage; the scheme below is what
|
|
32
|
+
the extracted result then gets named by. Note that the extractor's grading verb is
|
|
33
|
+
`grade` rather than `check` — `check` is this file's verb, and it answers a different
|
|
34
|
+
question (do the NAMES resolve, not do the TOKENS pass).
|
|
35
|
+
|
|
36
|
+
## Where the canon lives
|
|
37
|
+
|
|
38
|
+
Four directories at the workspace root. Each is published and gets its own tab on the
|
|
39
|
+
site, and the build reads them directly — this is the engine's existing contract, not a
|
|
40
|
+
new layout.
|
|
41
|
+
|
|
42
|
+
| Where | What it holds | Pull it when |
|
|
43
|
+
| --- | --- | --- |
|
|
44
|
+
| `base/<name>/` | one atom, every state on one page | you need the button, the input, the badge |
|
|
45
|
+
| `components/<name>/` | one composed component | you need the thing itself |
|
|
46
|
+
| `patterns/<name>/` | an arrangement several screens repeat | you need the layout, not the parts |
|
|
47
|
+
| `pages/<name>/` | a whole screen | somebody said "pull screens X, Y, Z" |
|
|
48
|
+
|
|
49
|
+
Everything else in a workspace is **not** the canon, and a bare name never means one of
|
|
50
|
+
them. A folder under `<project>/prototypes/` is a working prototype — an exploration,
|
|
51
|
+
kept as the record of one. `playground/` is scratch. Both can be promoted; see
|
|
52
|
+
[Growing the canon](#growing-the-canon).
|
|
53
|
+
|
|
54
|
+
## The name
|
|
55
|
+
|
|
56
|
+
**A canonical name is a directory name, and there is no second name.** The live site can
|
|
57
|
+
rename what a card DISPLAYS (that override lives in KV, not in the repo) and it never
|
|
58
|
+
changes what a name resolves to. If the two disagree, the folder is right.
|
|
59
|
+
|
|
60
|
+
**Form.** Lowercase ASCII letters, digits and single hyphens — `^[a-z0-9]+(-[a-z0-9]+)*$`.
|
|
61
|
+
No underscores, no spaces, no capitals, forty characters and four words at the outside.
|
|
62
|
+
|
|
63
|
+
**Subject first, qualifier last.** `invoice-list`, `invoice-detail`, `invoice-empty` —
|
|
64
|
+
never `list-of-invoices`, never `empty-invoice`. This is the rule that does the work:
|
|
65
|
+
A→Z sorts a subject's screens together, so a directory listing reads as a table of
|
|
66
|
+
contents and "pull the invoice screens" is one glance instead of a search. It is also
|
|
67
|
+
what lets somebody who knows one name in a family guess the rest.
|
|
68
|
+
|
|
69
|
+
**The qualifier comes from a closed set**, so the same idea is spelled the same way in
|
|
70
|
+
every workspace and an agent told "the empty state of the invoice list" can write down
|
|
71
|
+
`invoice-empty` without asking:
|
|
72
|
+
|
|
73
|
+
`-list` · `-detail` · `-new` · `-edit` · `-empty` · `-error` · `-loading` · `-confirm` · `-success`
|
|
74
|
+
|
|
75
|
+
Anything else at the end of a name is a second subject word, not a state. `-index`,
|
|
76
|
+
`-view`, `-show`, `-create`, `-add`, `-blank`, `-none`, `-fail`, `-done` and `-ok` are
|
|
77
|
+
the near misses — every one means a word already on the list, and `augur canon check`
|
|
78
|
+
names them rather than renaming anything, because which of two words a team says is
|
|
79
|
+
theirs to settle.
|
|
80
|
+
|
|
81
|
+
**A name says WHAT, never WHEN.** `-v2`, `-final`, `-copy`, `-old`, `-wip`, `-draft`,
|
|
82
|
+
`-tmp`, `-test`, a date, a ticket id — all refused. Every one encodes a moment in
|
|
83
|
+
somebody's afternoon, and a name carrying one cannot be guessed by the person who has to
|
|
84
|
+
pull it tomorrow. The version of a canon entry is its git history. (`-new` is on the
|
|
85
|
+
allowed list because it is a state — the create screen; `-new2` is not.)
|
|
86
|
+
|
|
87
|
+
**One name, one thing.** A name resolves in exactly one tier. Two tiers holding the same
|
|
88
|
+
name fails `check`; a prototype or playground folder holding a canonical name is reported
|
|
89
|
+
as a shadow, because the canon wins and that copy is one nobody will ever be sent to.
|
|
90
|
+
|
|
91
|
+
**Every entry says what it shows**, in one sentence in `<meta name="description">` — the
|
|
92
|
+
same tag the gallery card, the link preview and the canvas insert-picker already read
|
|
93
|
+
(see [prototype-contract.md](./prototype-contract.md)). `augur canon list` prints it, and
|
|
94
|
+
it is the only way a name is found by somebody who does not know it yet. An entry without
|
|
95
|
+
one fails `check`, and `augur canon save` refuses to create one.
|
|
96
|
+
|
|
97
|
+
## Growing the canon
|
|
98
|
+
|
|
99
|
+
A canon that only grows when somebody schedules an afternoon for it does not grow. The
|
|
100
|
+
promotion is therefore one command, run at the moment the screen is finished and still
|
|
101
|
+
in front of you:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
augur canon save orders/prototypes/order-detail # → pages/order-detail/
|
|
105
|
+
augur canon save playground/filter-bar --tier components
|
|
106
|
+
augur canon save . --as invoice-detail --dry-run
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
What it does, and what each part of it exists to prevent:
|
|
110
|
+
|
|
111
|
+
- **Copies, never moves.** The prototype stays where it is: comment pins are keyed to the
|
|
112
|
+
URL a screen was reviewed at, and the prototype is the record of the exploration. The
|
|
113
|
+
canon entry is the thing to copy from. When the two later disagree, `check` reports the
|
|
114
|
+
shadow rather than guessing which is current.
|
|
115
|
+
- **Applies the naming rules, and says so.** A source folder called `order-detail-v2`
|
|
116
|
+
promotes as `order-detail`, with the change printed. `--as <name>` overrides.
|
|
117
|
+
- **Repoints the design-system references for the new depth.** A prototype three levels
|
|
118
|
+
down references `../../../skills/<prefix>-ui/…`; a canon entry two levels down
|
|
119
|
+
references `../../skills/<prefix>-ui/…`. Getting that wrong produces a page that looks
|
|
120
|
+
correct on the site (the build rewrites the reference anyway) and opens unstyled from
|
|
121
|
+
disk — which is the failure that made promotion feel like a chore, because it is
|
|
122
|
+
invisible until somebody opens the file. This is the hand-edit the command exists to
|
|
123
|
+
remove.
|
|
124
|
+
- **Refuses rather than half-doing it.** No description and no `--desc` → refused. A name
|
|
125
|
+
already used in another tier → refused. An entry already there → refused unless
|
|
126
|
+
`--replace`. `--dry-run` prints the identical plan and writes nothing.
|
|
127
|
+
- **Writes `CANON.md` at the workspace root** if the workspace has none, so an agent
|
|
128
|
+
arriving cold finds the scheme by looking at the workspace rather than by being told.
|
|
129
|
+
Most workspaces already have it at birth — `augur init` scaffolds one and the seed ships
|
|
130
|
+
one — because the workspace whose names have already drifted is exactly the one that has
|
|
131
|
+
been worked in for a month without anybody promoting anything.
|
|
132
|
+
|
|
133
|
+
Promotion is also how a planned page becomes a real one: a slug listed in `space.json`
|
|
134
|
+
`pendingPages` shows on the site as a roadmap entry and drops off by itself the moment
|
|
135
|
+
`pages/<slug>/` exists.
|
|
136
|
+
|
|
137
|
+
## Checking it
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
augur canon check
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Fails on the things that make a name un-findable: a name that is not lowercase-hyphen, a
|
|
144
|
+
name carrying a version or a date, two tiers holding one name, an entry with no
|
|
145
|
+
`index.html`, an entry with no description, and a skill reference that does not resolve
|
|
146
|
+
from where the entry sits. Reports as advice: a near-miss qualifier, a name over four
|
|
147
|
+
words, a prototype shadowing a canonical name, a missing `CANON.md`.
|
|
148
|
+
|
|
149
|
+
A green run says the names are findable. It cannot say they are the right names for the
|
|
150
|
+
things — whether `invoice-detail` is the screen a person would ask for by that name is a
|
|
151
|
+
review question, and no lint has ever been able to answer it.
|
package/agents/canvas.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Canvas — live multiplayer whiteboards, and when to suggest one
|
|
2
|
+
|
|
3
|
+
Augur has an infinite-canvas layer: sticky notes, shapes, text, images, tables,
|
|
4
|
+
drawings, arrows, sections, stamps, and **live prototype tiles** — all
|
|
5
|
+
multiplayer (shared cursors, presence, co-typing) with an agent able to join the
|
|
6
|
+
room as a visible participant. A canvas IS a prototype (a small loader page in
|
|
7
|
+
the workspace repo), so boards publish, share, and comment like any other prototype.
|
|
8
|
+
|
|
9
|
+
The deep reference — node schemas, the co-work protocol, engine internals — is
|
|
10
|
+
**`../CANVAS.md`**. This file is the agent-facing contract only.
|
|
11
|
+
|
|
12
|
+
## Suggest it — the ask-shape rule
|
|
13
|
+
|
|
14
|
+
Canvas is a tool in your belt. When a request is **board-shaped, offer it** (one
|
|
15
|
+
line: "this feels like a board — want me to set up a canvas?"); don't silently
|
|
16
|
+
build one, don't wait to be asked:
|
|
17
|
+
|
|
18
|
+
- "brainstorm / explore options / how might we…" → stickies + sections
|
|
19
|
+
- comparing variants side by side → tiles of the alternatives + annotation
|
|
20
|
+
- mapping a flow / journey / timeline / system → shapes + arrows + sections
|
|
21
|
+
- workshop-style input from several people → a shared board beats a doc
|
|
22
|
+
- reviewing many screens at once → prototype tiles arranged spatially
|
|
23
|
+
|
|
24
|
+
## Making a board
|
|
25
|
+
|
|
26
|
+
Copy any existing board folder — a short loader (`window.GV_CANVAS = {name}` +
|
|
27
|
+
the two engine tags) — into `<opportunity>/prototypes/<board>/`, or use the
|
|
28
|
+
in-app "+ New canvas" button (signed-in users). Publish like any prototype.
|
|
29
|
+
Boards are public by the same obscure-share-link model as prototypes.
|
|
30
|
+
|
|
31
|
+
## Reading and writing a board
|
|
32
|
+
|
|
33
|
+
- **Read:** `GET /__board?path=/<opportunity>/<board>/` → `{doc}`, where `doc` is
|
|
34
|
+
`{name, nameV, nodes, tombs, clock}` (or `{doc: null}` if the board was never
|
|
35
|
+
saved). `nodes` and `tombs` live INSIDE `doc`. Node types and their exact
|
|
36
|
+
fields: **`../CANVAS.md` § "Node schemas"** — the single write contract
|
|
37
|
+
(positions in world px, `rich` HTML whitelist, image `desc` rules).
|
|
38
|
+
- **Write (full-state):** `POST /__board?path=…` with the body wrapped in a doc
|
|
39
|
+
envelope — `{"doc": {…, "nodes": [...]}}`. A bare node array (or any body
|
|
40
|
+
without `doc.nodes` as an array) 400s.
|
|
41
|
+
- **Versions:** every node carries `v` + `vn` under last-writer-wins. Writing
|
|
42
|
+
through the proper doors (the ClawdCanvas client, `GVCanvas.addNode`, the
|
|
43
|
+
in-page editor) bumps automatically. Hand-rolling a full-state write over an
|
|
44
|
+
existing board: bump each modified node's `v` above what you read, or the room
|
|
45
|
+
keeps its copy.
|
|
46
|
+
- **Images** upload via `POST /__asset` (content-hashed, immutable) — content
|
|
47
|
+
type must be `image/jpeg|png|webp|gif` (else **415**), 4MB cap (else **413**);
|
|
48
|
+
they read back at `GET /__asset/<40-hex-hash>`. Or point `src` at an image
|
|
49
|
+
committed in the workspace repo. Never write new data-URLs.
|
|
50
|
+
|
|
51
|
+
## Co-working live (the default when changing a board)
|
|
52
|
+
|
|
53
|
+
Join the board's room as a visible agent and stream per-node ops — never a
|
|
54
|
+
full-state `POST /__board` while people are on it. The client:
|
|
55
|
+
`../scripts/clawd-canvas.mjs` (`ClawdCanvas`; needs **Node 22+** for the global
|
|
56
|
+
WebSocket) — connect, then `moveCursorTo`, `pose('thinking'|'sparkles'|…)`,
|
|
57
|
+
`say()/unsay()`, `focus(id)`, `upsert/del/rename`, `dragNode`, `typeNode`,
|
|
58
|
+
`status()`. Long sessions run the daemon (`node clawd-canvas.mjs daemon
|
|
59
|
+
<boardPath> <cmdFile>` — launch detached; identity is derived, don't pass
|
|
60
|
+
`--name` unless it's a `--sibling` agent — the daemon rejects a bare `--name`).
|
|
61
|
+
|
|
62
|
+
**Show activity first:** the human watches the canvas, not your terminal. On any
|
|
63
|
+
ask, your first move is visual — walk to the spot, pose, say what you're doing,
|
|
64
|
+
stub the placeholder — *then* do the real work, and delete the stub when the
|
|
65
|
+
artifact lands. Full protocol + daemon command set: **`../CANVAS.md` § "Working
|
|
66
|
+
on the canvas"**.
|
|
67
|
+
|
|
68
|
+
⏳ **A room is named by the PATH THE CLIENT SENDS, and that is not where this is
|
|
69
|
+
going.** Today the path you pass is the room's identity, so two boards at the same
|
|
70
|
+
path are the same room and the server takes the client's word for which one it is.
|
|
71
|
+
Deriving the name server-side from the workspace plus the path is planned and not
|
|
72
|
+
built. Nothing in this doc depends on which it is — you pass the board's path
|
|
73
|
+
either way — but do not build anything that relies on choosing a room name, and do
|
|
74
|
+
not read today's behaviour as a guarantee.
|
|
75
|
+
|
|
76
|
+
**Origins:** with a deploy shell next door the client resolves them itself. From
|
|
77
|
+
a bare workspace clone it works too: `space.json`'s `siteOrigin` supplies the site
|
|
78
|
+
and the client connects through the site's `/__rt` proxy. Env overrides:
|
|
79
|
+
`CANVAS_SITE_ORIGIN`, `CANVAS_RT_ORIGIN`.
|
|
80
|
+
|
|
81
|
+
## Testing near canvases
|
|
82
|
+
|
|
83
|
+
A test that opens a canvas page (or connects a client) **joins the real room and
|
|
84
|
+
writes real data**. Isolate the room path onto a throwaway path — and it **must
|
|
85
|
+
be under `/__test/`**: only `/__test/` rooms are RAM-only, every other path gets
|
|
86
|
+
real DO SQLite + KV writes (there is no self-healing for a non-`/__test/` path).
|
|
87
|
+
Use the `Object.defineProperty` clobber in **`../CANVAS.md` § "Multiplayer"** and
|
|
88
|
+
block `POST **/__board` in Playwright routes.
|