@timqi/pier 0.0.29 → 0.1.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.
Files changed (165) hide show
  1. package/README.md +58 -125
  2. package/dist/agent/config.js +24 -11
  3. package/dist/agent/credentials.js +11 -23
  4. package/dist/agent/events.js +43 -62
  5. package/dist/agent/listing.js +113 -68
  6. package/dist/agent/pi.js +204 -211
  7. package/dist/boards/boards.js +19 -29
  8. package/dist/channels/attach.js +14 -42
  9. package/dist/channels/chains.js +33 -37
  10. package/dist/channels/chunk.js +8 -28
  11. package/dist/channels/commands.js +3 -14
  12. package/dist/channels/config.js +33 -52
  13. package/dist/channels/control.js +4 -13
  14. package/dist/channels/conversations.js +8 -25
  15. package/dist/channels/dedup.js +8 -17
  16. package/dist/channels/gatekeeper.js +13 -23
  17. package/dist/channels/lark-api.js +23 -63
  18. package/dist/channels/lark-outbound.js +12 -44
  19. package/dist/channels/lark-panel.js +8 -24
  20. package/dist/channels/lark-render.js +18 -62
  21. package/dist/channels/lark.js +52 -141
  22. package/dist/channels/lines.js +13 -15
  23. package/dist/channels/panel.js +16 -36
  24. package/dist/channels/receipts.js +29 -52
  25. package/dist/channels/routes.js +3 -9
  26. package/dist/channels/runtime.js +12 -23
  27. package/dist/channels/slack-api.js +34 -86
  28. package/dist/channels/slack-directory.js +7 -23
  29. package/dist/channels/slack-outbound.js +12 -56
  30. package/dist/channels/slack-panel.js +4 -13
  31. package/dist/channels/slack-render.js +23 -91
  32. package/dist/channels/slack-tool.js +48 -171
  33. package/dist/channels/slack.js +73 -239
  34. package/dist/channels/telegram-api.js +8 -20
  35. package/dist/channels/telegram-panel.js +5 -21
  36. package/dist/channels/telegram-render.js +13 -40
  37. package/dist/channels/telegram.js +54 -146
  38. package/dist/channels/types.js +5 -16
  39. package/dist/cli.js +17 -41
  40. package/dist/config-sync.js +87 -4
  41. package/dist/core/hub.js +7 -20
  42. package/dist/core/identity.js +20 -59
  43. package/dist/core/inbound-file.js +15 -49
  44. package/dist/core/inbox.js +13 -35
  45. package/dist/core/queue.js +3 -5
  46. package/dist/core/reply.js +41 -142
  47. package/dist/core/router.js +209 -260
  48. package/dist/core/types.js +17 -1
  49. package/dist/db.js +98 -252
  50. package/dist/drain.js +58 -51
  51. package/dist/extensions/index.js +3 -11
  52. package/dist/extensions/web/anthropic.js +3 -9
  53. package/dist/extensions/web/artifacts.js +2 -5
  54. package/dist/extensions/web/content.js +6 -14
  55. package/dist/extensions/web/http.js +2 -6
  56. package/dist/extensions/web/language.js +8 -18
  57. package/dist/extensions/web/openai.js +1 -1
  58. package/dist/extensions/web/provider.js +5 -18
  59. package/dist/extensions/web/tools.js +19 -63
  60. package/dist/lock.js +98 -0
  61. package/dist/log.js +9 -26
  62. package/dist/main.js +87 -179
  63. package/dist/paths.js +10 -26
  64. package/dist/secrets.js +19 -46
  65. package/dist/service.js +33 -75
  66. package/dist/settings.js +42 -65
  67. package/dist/tasks/agent.js +129 -114
  68. package/dist/tasks/callbacks.js +9 -19
  69. package/dist/tasks/command.js +29 -14
  70. package/dist/tasks/definitions.js +39 -62
  71. package/dist/tasks/execution.js +46 -42
  72. package/dist/tasks/groups.js +41 -35
  73. package/dist/tasks/messages.js +121 -182
  74. package/dist/tasks/outbox.js +61 -55
  75. package/dist/tasks/routes.js +5 -11
  76. package/dist/tasks/runs.js +14 -13
  77. package/dist/tasks/service.js +53 -54
  78. package/dist/tasks/store.js +53 -30
  79. package/dist/tasks/tool.js +132 -61
  80. package/dist/tools-task.js +20 -60
  81. package/dist/tools.js +100 -327
  82. package/dist/update.js +21 -44
  83. package/dist/web/auth.js +118 -179
  84. package/dist/web/config-sync.js +2 -2
  85. package/dist/web/config.js +3 -7
  86. package/dist/web/explorer.js +10 -21
  87. package/dist/web/fs.js +20 -42
  88. package/dist/web/instance.js +45 -85
  89. package/dist/web/providers.js +14 -13
  90. package/dist/web/public/assets/{activity-D3m4L2IL.js → activity-B89_hH7q.js} +2 -2
  91. package/dist/web/public/assets/activity-B89_hH7q.js.br +0 -0
  92. package/dist/web/public/assets/activity-B89_hH7q.js.gz +0 -0
  93. package/dist/web/public/assets/boards-BeKW0ZXK.js +1 -0
  94. package/dist/web/public/assets/boards-BeKW0ZXK.js.br +0 -0
  95. package/dist/web/public/assets/boards-BeKW0ZXK.js.gz +0 -0
  96. package/dist/web/public/assets/explorer-DIuMlaV3.js +4 -0
  97. package/dist/web/public/assets/explorer-DIuMlaV3.js.br +0 -0
  98. package/dist/web/public/assets/explorer-DIuMlaV3.js.gz +0 -0
  99. package/dist/web/public/assets/index-DzXDXra_.js +85 -0
  100. package/dist/web/public/assets/index-DzXDXra_.js.br +0 -0
  101. package/dist/web/public/assets/index-DzXDXra_.js.gz +0 -0
  102. package/dist/web/public/assets/index-eqQLVS8Q.css +2 -0
  103. package/dist/web/public/assets/index-eqQLVS8Q.css.br +0 -0
  104. package/dist/web/public/assets/index-eqQLVS8Q.css.gz +0 -0
  105. package/dist/web/public/assets/runs-Cwy0mN8i.js +1 -0
  106. package/dist/web/public/assets/runs-Cwy0mN8i.js.br +0 -0
  107. package/dist/web/public/assets/runs-Cwy0mN8i.js.gz +0 -0
  108. package/dist/web/public/assets/settings-DzZLmujq.js +5 -0
  109. package/dist/web/public/assets/settings-DzZLmujq.js.br +0 -0
  110. package/dist/web/public/assets/settings-DzZLmujq.js.gz +0 -0
  111. package/dist/web/public/assets/task-runs-BCakxFk8.js +3 -0
  112. package/dist/web/public/assets/task-runs-BCakxFk8.js.br +0 -0
  113. package/dist/web/public/assets/task-runs-BCakxFk8.js.gz +0 -0
  114. package/dist/web/public/assets/tasks-BlzEbk11.js +4 -0
  115. package/dist/web/public/assets/tasks-BlzEbk11.js.br +0 -0
  116. package/dist/web/public/assets/tasks-BlzEbk11.js.gz +0 -0
  117. package/dist/web/public/index.html +100 -130
  118. package/dist/web/public/index.html.br +0 -0
  119. package/dist/web/public/index.html.gz +0 -0
  120. package/dist/web/public/manifest.webmanifest +2 -2
  121. package/dist/web/public/manifest.webmanifest.br +0 -0
  122. package/dist/web/public/manifest.webmanifest.gz +0 -0
  123. package/dist/web/public/sw.js +14 -2
  124. package/dist/web/public/sw.js.br +0 -0
  125. package/dist/web/public/sw.js.gz +0 -0
  126. package/dist/web/push.js +55 -77
  127. package/dist/web/route.js +3 -7
  128. package/dist/web/server.js +131 -180
  129. package/dist/web/session-state.js +14 -54
  130. package/dist/web/types.js +2 -4
  131. package/dist/web/webpush.js +10 -25
  132. package/docs/deploy.md +115 -330
  133. package/package.json +2 -1
  134. package/skills/pier-boards/SKILL.md +81 -160
  135. package/skills/pier-help/SKILL.md +23 -20
  136. package/skills/pier-slack/SKILL.md +2 -2
  137. package/skills/pier-tasks/SKILL.md +153 -160
  138. package/dist/config-sync-fetch.js +0 -84
  139. package/dist/limits.js +0 -14
  140. package/dist/web/public/assets/activity-D3m4L2IL.js.br +0 -0
  141. package/dist/web/public/assets/activity-D3m4L2IL.js.gz +0 -0
  142. package/dist/web/public/assets/boards-BIObcQeX.js +0 -1
  143. package/dist/web/public/assets/boards-BIObcQeX.js.br +0 -0
  144. package/dist/web/public/assets/boards-BIObcQeX.js.gz +0 -0
  145. package/dist/web/public/assets/explorer-C_rSWPNB.js +0 -4
  146. package/dist/web/public/assets/explorer-C_rSWPNB.js.br +0 -0
  147. package/dist/web/public/assets/explorer-C_rSWPNB.js.gz +0 -0
  148. package/dist/web/public/assets/index-CX3fYZY5.css +0 -2
  149. package/dist/web/public/assets/index-CX3fYZY5.css.br +0 -0
  150. package/dist/web/public/assets/index-CX3fYZY5.css.gz +0 -0
  151. package/dist/web/public/assets/index-uFsZkKOQ.js +0 -85
  152. package/dist/web/public/assets/index-uFsZkKOQ.js.br +0 -0
  153. package/dist/web/public/assets/index-uFsZkKOQ.js.gz +0 -0
  154. package/dist/web/public/assets/runs-Ch6DZq6O.js +0 -1
  155. package/dist/web/public/assets/runs-Ch6DZq6O.js.br +0 -0
  156. package/dist/web/public/assets/runs-Ch6DZq6O.js.gz +0 -0
  157. package/dist/web/public/assets/settings-BWcEIEcv.js +0 -5
  158. package/dist/web/public/assets/settings-BWcEIEcv.js.br +0 -0
  159. package/dist/web/public/assets/settings-BWcEIEcv.js.gz +0 -0
  160. package/dist/web/public/assets/task-runs-DPkwv2UE.js +0 -3
  161. package/dist/web/public/assets/task-runs-DPkwv2UE.js.br +0 -0
  162. package/dist/web/public/assets/task-runs-DPkwv2UE.js.gz +0 -0
  163. package/dist/web/public/assets/tasks-DTiCi2mH.js +0 -4
  164. package/dist/web/public/assets/tasks-DTiCi2mH.js.br +0 -0
  165. package/dist/web/public/assets/tasks-DTiCi2mH.js.gz +0 -0
@@ -5,10 +5,9 @@ description: Publish a Board — a folder of static HTML Pier serves at a stable
5
5
 
6
6
  # Building a Pier board
7
7
 
8
- A **board** is a directory in the boards folder `<pier>/AGENTS.md` names under
9
- "This Pier instance" — use that path verbatim; `~/.pier` is only the default.
10
- Pier serves `<board>/site/` and nothing else. Boards outlive sessions: any
11
- session may read or rewrite any board, and closing this one changes nothing.
8
+ A **board** lives in the exact boards folder named under "This Pier instance"
9
+ in `<pier>/AGENTS.md`; do not assume `~/.pier`. Only `<board>/site/` is served.
10
+ Boards survive sessions; any session may read or update them.
12
11
 
13
12
  ## Create one
14
13
 
@@ -27,13 +26,9 @@ session may read or rewrite any board, and closing this one changes nothing.
27
26
  }
28
27
  ```
29
28
 
30
- Those four fields are the whole manifest (publishing adds a fifth — below).
31
-
32
- - `slug`: `[a-z0-9][a-z0-9-]{0,63}`, and it is the URL — short and stable. Do
33
- not add random characters of your own; publishing adds them (see below).
34
- - `description` is the Console list entry: write it for someone who has
35
- forgotten this conversation.
36
- - `sessions`: append your own id, never replace — other ids are provenance too.
29
+ - `slug`: `[a-z0-9][a-z0-9-]{0,63}`; keep it short and stable, without random suffixes.
30
+ - `description`: Console list text, understandable without this conversation.
31
+ - `sessions`: append your id; preserve others as provenance.
37
32
 
38
33
  ## Publish, then hand over the link
39
34
 
@@ -42,40 +37,25 @@ asked for a public or shareable board *in this request*; otherwise leave it
42
37
  `false` and say the board is private. Never publish personal data or anything
43
38
  the user has not seen.
44
39
 
45
- The published address is `/p/<slug>-<token>/`, not `/p/<slug>/`, so a public
46
- board's URL cannot be guessed from its name. `token` is a fifth manifest field
47
- you write next to `"public": true` — eight hex characters from
48
- `openssl rand -hex 4`, never invented in your head, never reused between
49
- boards. Leave it out and Pier mints one on the first request, but then the link
50
- is only visible in the Console, so write it yourself and you can hand it over
51
- in the same message.
52
-
53
- Asked to make an existing board public? Set `"public": true` and a fresh
54
- `token` in `board.json`, then reply with the `/p/<slug>-<token>/` link — that is
55
- the whole answer. Already has a token? Keep it: the link may be out there. No
56
- verification step, no narrating the edit, no restating what the page holds.
57
-
58
- The message announcing the board carries **one bare URL** — paste the address
59
- itself, never `[title](url)`: link labels get mangled or truncated on some chat
60
- surfaces, and the title is already on the page. No filesystem paths either —
61
- `…/boards/<slug>/board.json` means nothing to the reader.
62
- `<pier>/AGENTS.md` gives you the address, so there is nothing to look up:
63
-
64
- | The user asked for | Send |
40
+ For publishing, preserve the manifest's `token` or add eight hex characters
41
+ from `openssl rand -hex 4`; never invent or reuse another board's token.
42
+
43
+ Use the instance address from `<pier>/AGENTS.md` plus:
44
+
45
+ | Visibility | Path |
65
46
  | --- | --- |
66
- | a board, nothing about sharing | `https://pier.example.com/boards/weekly-digest/` — behind the Pier password; Console → Boards makes it public |
67
- | a **public** board | `https://pier.example.com/p/weekly-digest-3f9ac128/` — no password; the suffix is the manifest's `token`, copied verbatim |
47
+ | Private (default) | `/boards/<slug>/` — password required; Console → Boards can publish |
48
+ | Public | `/p/<slug>-<token>/` — copy the token verbatim; 404 unless `public: true` |
68
49
 
69
- Never both: the pair invites pasting the password-free URL of a board that was
70
- never meant to leave the workspace, and `/p/<slug>-<token>/` 404s unless the manifest
71
- says `"public": true`. No address configured? Give the path, say Console →
72
- Settings turns it into a link, and never guess a host.
50
+ Return **one bare URL**, never both, Markdown link labels or filesystem paths.
51
+ If no address is configured, give the path and point to Console → Settings;
52
+ never guess a host. For a publish-only request, set `public: true` and a token
53
+ if missing, then return only the public URL; no page verification or narration.
73
54
 
74
55
  ## Writing the page
75
56
 
76
- A board is a **presentation**, not a text file: someone opens it to get an
77
- answer fast. Link the shipped stylesheet and write plain semantic HTML — no
78
- build, no npm, no framework:
57
+ Let the question and evidence determine layout; no sections are required.
58
+ Use semantic HTML without a framework. Adapt this shell's language and content:
79
59
 
80
60
  ```html
81
61
  <!doctype html>
@@ -87,143 +67,84 @@ build, no npm, no framework:
87
67
  <link rel="stylesheet" href="/p/_assets/pier.css">
88
68
  </head>
89
69
  <body>
90
- <div class="hero">
91
- <h1>Weekly digest — infra</h1>
92
- <p class="lede">Throughput is up, but two migrations need a decision before Friday.</p>
93
- <p class="muted">18 Feb 2026 · covers 12 repos</p>
94
- </div>
95
-
96
- <div class="grid">
97
- <div class="card"><span class="kpi good">12</span> PRs merged <span class="muted">+3 vs last week</span></div>
98
- <div class="card"><span class="kpi warn">2</span> awaiting decision</div>
99
- <div class="card"><span class="kpi">98.9%</span> uptime <span class="muted">30 d</span></div>
100
- </div>
101
-
102
- <div class="callout warn">
103
- <strong>Needs you:</strong> the payments migration blocks two teams — approve or defer by Friday.
104
- </div>
105
-
106
- <h2>Payments is the only degraded service</h2>
107
- <table>
108
- <thead><tr><th>Service</th><th>Status</th><th class="num">p95</th><th>Error budget</th></tr></thead>
109
- <tbody>
110
- <tr><td><strong>payments</strong></td><td><span class="tag bad">degraded</span></td><td class="num">910 ms</td>
111
- <td><span class="bar bad" style="--v:18%"></span></td></tr>
112
- <tr><td>api</td><td><span class="tag good">healthy</span></td><td class="num">142 ms</td>
113
- <td><span class="bar good" style="--v:82%"></span></td></tr>
114
- </tbody>
115
- </table>
116
-
117
- <details><summary>All 12 merged PRs</summary>
118
- <table><thead><tr><th>PR</th><th>Author</th><th>Merged</th></tr></thead>
119
- <tbody><tr><td>#412</td><td>ana</td><td>Mon</td></tr></tbody></table>
120
- </details>
121
-
122
- <footer>Written by Pier · data as of 18 Feb 09:00 · ask for an update to refresh</footer>
70
+ <h1>Weekly digest — infra</h1>
71
+ <p class="lede">The main finding, grounded in the available evidence.</p>
72
+ <footer>Data as of … · sources … · how to refresh …</footer>
123
73
  </body>
124
74
  </html>
125
75
  ```
126
76
 
127
- What makes it read as designed rather than generated:
128
-
129
- - **The lede is the verdict, not the topic.** "Two migrations need a decision by
130
- Friday" answers; "This digest covers infra activity" restates the prompt. A
131
- reader who stops after the hero must still leave with the point.
132
- - **Headings are findings.** "Payments is the only degraded service", not
133
- "Services". A heading that could top any report — Overview, Summary, Details —
134
- says nothing about this one.
135
- - **Every number carries its unit and its baseline**: "142 ms p95, was 120",
136
- "12 of 40". A bare number is decoration; so is fake precision (98.8724% →
137
- 98.9%).
138
- - **End at the last useful block.** No closing summary, no filler section. The
139
- footer holds provenance: data as-of, source, how to refresh.
77
+ Lead with the verdict; headings state findings ("Payments alone is degraded").
78
+ Numbers carry units and baselines ("142 ms p95, was 120", "12 of 40"), without
79
+ false precision. End without filler or a closing summary; put the data
80
+ timestamp, sources and refresh instructions in the footer.
140
81
 
141
82
  ## What `pier.css` gives you
142
83
 
143
- Headings, paragraphs, lists, tables, `pre`/`code`, `blockquote`, `details` and
144
- `footer` are styled with **no classes at all**, dark mode included. The page
145
- sizes itself: a wide desktop canvas (62rem, 76rem on a very large screen) that
146
- reflows to one column on a phone, prose held to a readable measure while tables,
147
- `.grid`, `.split` and `.hero` use the full width — don't add a `max-width` of
148
- your own. Zebra striping, tables that scroll inside themselves, and `<details>`
149
- that print open are free. On top of that:
84
+ Classless defaults include readable prose, responsive widths, dark mode,
85
+ scrolling zebra tables and print styles. Override widths only as needed.
150
86
 
151
87
  | Class | Use it for |
152
88
  | --- | --- |
153
- | `.lede` | the one-sentence answer under the title |
154
- | `.hero` | the opening block: title + lede + date/scope, on a tinted panel |
155
- | `.grid` + `.card` | a KPI row (auto-fits to one column on a phone) |
156
- | `.kpi` | the headline number inside a card |
157
- | `.card` + `.good/.warn/.bad/.info` | a status card: accent edge and tinted fill |
158
- | `.callout` (`.good` `.warn` `.bad`) | the takeaway or the ask |
159
- | `.tag` (`.good` `.warn` `.bad` `.info`) | status pills in tables and lists |
160
- | `.good` `.warn` `.bad` | colour on a number or a word |
161
- | `.num` | right-aligned, tabular numeric table cells |
162
- | `.bar` (`.good` `.warn` `.bad`) | share-of-total inside a table: `style="--v:62%"` |
163
- | `.split` | two columns that stack on a phone: before/after, text + aside |
164
- | `.muted` | secondary text: dates, deltas, units, scope |
165
-
166
- Need something it lacks? A `<style>` block or your own CSS file inside `site/`
167
- is normal, and so is a custom colour or a hand-written layout when the content
168
- calls for one.
89
+ | `.lede` / `.hero` | opening answer / tinted opening panel |
90
+ | `.grid` + `.card` + `.kpi` | responsive cards with headline numbers |
91
+ | `.callout` / `.tag` | takeaway or ask / status pill |
92
+ | `.num` | right-aligned tabular numbers |
93
+ | `.bar` | proportion: `style="--v:62%"` |
94
+ | `.split` | two columns, stacking on phones |
95
+ | `.muted` | dates, deltas, units, scope |
96
+
97
+ Status modifiers: `.good` healthy/done, `.warn` attention/pending, `.bad`
98
+ broken/blocked; express status in words too. These colour text, cards,
99
+ callouts, tags and bars; cards and tags also accept `.info` for neutral emphasis.
100
+ All helpers are optional. Custom `<style>` or CSS under `site/` may change
101
+ layout and palette while preserving contrast and phone reflow.
169
102
 
170
103
  ## Pick the form from the content
171
104
 
172
- | What you have | How to present it |
105
+ | Content | Possible form |
173
106
  | --- | --- |
174
- | The single most important fact | `.hero` lede, or one `.kpi` on its own |
175
- | 2–4 headline metrics | `.grid` of `.card`s, each with `.kpi` + a `.muted` delta |
176
- | Something the reader must act on | `.callout warn` near the top, naming the deadline |
177
- | Items with a state | table with `.tag` pills, worst rows first |
178
- | Ranked or compared numbers | table, `.num` columns, `.bar` for share of total |
179
- | Progress toward a goal | `.bar` per row, or `.kpi` + "of 40 done" in `.muted` |
180
- | Two alternatives | `.split` with a `.card` each, verdict in the lede above |
181
- | A sequence of events | ordered list, date in `.muted` at the start of each item |
182
- | A trend over time | first/last + delta in words; inline SVG only if the shape *is* the news |
183
- | 200 rows | aggregate, show the 5–10 that matter, rest in `<details>` with the count in its summary |
184
- | 1–2 data points | a sentence or a `.callout` — a one-row table is a table costume |
185
- | Long raw output, logs, code | `<details>` at the bottom, or `<pre><code>` trimmed to the lines that matter |
186
- | Nothing to report | say so in the lede ("all 14 checks green") and stop — short is finished, not thin |
187
-
188
- Vary the forms: never two blocks of the same kind in a row, and prefer number →
189
- ask → proof → fold over six paragraphs. A status or decision board earns one
190
- screenful before the first `<details>`; only a handover or postmortem earns a
191
- long scroll. Colour is part of the message — green healthy/done, amber
192
- attention/pending, red broken/blocked, `.info` neutral emphasis — on the numbers
193
- and status cells that carry the point, never on ordinary prose; that contrast is
194
- what makes it read as signal.
107
+ | One finding or 1–2 values | sentence; if nothing changed, say so and stop |
108
+ | Headline metrics | KPI cards with deltas; progress includes the total |
109
+ | Required action | prominent ask with deadline |
110
+ | States, rankings or shared comparison criteria | table; worst states first |
111
+ | Alternatives needing separate explanations | parallel sections; recommendation first |
112
+ | Events / trends | dated list / endpoints and delta; chart when shape explains the finding |
113
+ | Large datasets or raw output | aggregate, show relevant rows, fold the rest with a count; trim code/logs |
114
+
115
+ Repeat forms for comparable items; vary them when information changes. Put
116
+ findings and actions first, then evidence; fold only supplementary detail.
195
117
 
196
118
  ## Rules
197
119
 
198
- - **No secrets, ever.** Tokens, API keys, credentials, internal hostnames and
199
- private paths never go into a board — not in the page, not in a `<details>`
200
- fold, not in a code sample. A private board is one Console toggle from
201
- public, so write every page as if it already were.
202
- - **Static and self-contained.** Everything the page needs lives under `site/`
203
- with relative paths. No CDN, no external fonts, no analytics, no `fetch()` —
204
- a published board is served under a CSP that blocks all of it, so an external
205
- reference is a broken page, not a slow one.
206
- - **Content, not an app.** Interaction is `<details>` and anchors; the page
207
- stays readable with JavaScript off.
208
- - **Show, don't narrate.** A sentence describing numbers should have been a KPI
209
- row or a table. Prose is for judgement — what it means, what to do.
210
- - **Structure before graphics.** A chart is inline SVG (no library) and only
211
- when the *shape* of the data is the message. A three-row table beats any
212
- picture of three numbers; no chart beats a decorative one.
213
- - **Only real data.** Every figure traces to something you saw this session, and
214
- a gap is shown as a gap ("no data since Tue", "~3 weeks", "n=3") — never
215
- smoothed into a clean number.
216
- - **Rewrite in place.** Updating a board means editing its files, not creating
217
- `weekly-digest-v2`. The URL is the point.
218
- - Top-down — what this is → the answer → detail → raw data in `<details>` —
219
- prose in the user's language, short headings, no emoji chrome.
120
+ - **No secrets:** no tokens, API keys, credentials, internal hostnames or
121
+ private paths in page content, including folds and code samples; private
122
+ boards can become public with one Console toggle.
123
+ - **Self-contained:** assets live under `site/` with relative paths, except
124
+ the shipped stylesheet; no CDN, external fonts, analytics or `fetch()`
125
+ (blocked by the public CSP).
126
+ - **Interaction:** prefer `<details>` and anchors; local JS may sort, filter or
127
+ inspect embedded data, without network calls or server runtime. With JS off,
128
+ keep the answer and evidence readable; hide or disable unavailable controls.
129
+ - **Charts:** use when data shape explains the finding; prefer inline SVG,
130
+ a small bundled library only for needed interaction, with text/table fallback.
131
+ - **Real data:** every figure traces to evidence seen this session; expose
132
+ gaps and uncertainty ("no data since Tue", "~3 weeks", "n=3").
133
+ - **Update in place:** edit the existing board; preserve its URL.
134
+ - Write in the user's language, with short headings and no emoji chrome.
135
+
136
+ ## Check the page
137
+
138
+ For new pages or layout/interaction changes, run the checks below; for content
139
+ edits, check the affected content and links. Report actual coverage or gaps.
140
+
141
+ - Phone/desktop, light/dark: readable contrast, no page overflow, tables scroll internally.
142
+ - Keyboard/touch: working links and controls, visible focus, usable targets, text status labels.
143
+ - Assets load without console errors; answer and evidence remain readable with JS off.
220
144
 
221
145
  ## If a board needs a build
222
146
 
223
- Pier ships no toolchain and the default is no build. If one is genuinely needed
224
- (a bundled charting library, a component layout), you own it: keep sources
225
- outside `site/` (e.g. `<board>/src/`), emit into `site/`, and write a
226
- `<board>/README.md` a future session can follow cold — install command, build
227
- command, output path, where the data came from. Never leave `site/`
228
- inconsistent with its sources; on an existing board look for that README first
229
- and rebuild, because hand-patching `site/` is lost on the next build.
147
+ Pier ships no toolchain. If a build is needed, keep sources outside `site/`,
148
+ emit into `site/`, and document install/build commands, output path and data
149
+ sources in `<board>/README.md`. Before editing an existing board, check for
150
+ that README; update sources and rebuild so `site/` stays consistent.
@@ -6,15 +6,15 @@ description: How Pier itself works — durable sessions, what survives a restart
6
6
  # How Pier works
7
7
 
8
8
  Pier is the workspace this session runs in: agent sessions behind chat
9
- surfaces — a web workbench and IM channels (Slack and Telegram today) — plus
10
- scheduled tasks, subagents and boards. Answer questions about it from the
11
- facts below. If the answer is not here, say you do not know how this instance
12
- is configured rather than guessing: the Console (Pier's admin web UI) is the
13
- operator's source of truth.
9
+ surfaces — a web workbench and IM channels (Slack, Telegram, Lark) — plus
10
+ scheduled tasks, subagents and boards. Answer from the facts below. If the
11
+ answer is not here, say you do not know how this instance is configured rather
12
+ than guessing: the Console (Pier's admin web UI) is the operator's source of
13
+ truth.
14
14
 
15
15
  ## Sessions and persistence
16
16
 
17
- - One durable session per conversation: a web chat, a Slack thread, a
17
+ - One durable session per conversation: a web chat, a Slack or Lark thread, a
18
18
  Telegram chat or topic. The mapping survives restarts — the next message
19
19
  lands in the same transcript with its context intact.
20
20
  - Idle sessions leave memory but keep their transcript; they resume
@@ -48,7 +48,8 @@ operator's source of truth.
48
48
  into the running turn as a steer.
49
49
  - From an IM chat, every mid-turn message steers the running turn directly —
50
50
  no `!` needed, and a leading `!` is just content.
51
- - `/stop` aborts the current turn outright.
51
+ - `/stop` from an IM chat aborts the current turn outright; the web has a Stop
52
+ button.
52
53
 
53
54
  ## In-chat commands and the settings panel
54
55
 
@@ -61,12 +62,13 @@ operator's source of truth.
61
62
 
62
63
  ## What a turn looks like from outside
63
64
 
64
- - Telegram and Slack put a 👀 on the message that started a turn and take it
65
- off when the turn settles; a restart and a periodic sweep clear stragglers.
65
+ - IM channels put a 👀 (Lark: "OnIt") on the message that started a turn and
66
+ take it off when the turn settles; a restart and a periodic sweep clear stragglers.
66
67
  A 👀 that never clears means the turn died, not that you are still thinking.
67
68
  - Every finished reply carries its cost: elapsed time and the context size at
68
69
  completion (`1m14s · 32K tok`) — a running total, not this turn's spend. IM
69
- shows it as a footer line, the web on hover.
70
+ shows it as a footer line; the web shows the duration in the reply's activity
71
+ headline and the context size in the session header.
70
72
  - A reply past the platform's message cap is split across several messages
71
73
  (Telegram ~3.8k chars); the footer and the next-step buttons ride the last
72
74
  one.
@@ -96,16 +98,17 @@ operator's source of truth.
96
98
 
97
99
  ## Service restart, reload and update
98
100
 
99
- - `pier restart` is the graceful systemd path: it refuses new work, waits up to
100
- five minutes for active turns and Task runs, then restarts. If the deadline
101
- aborts an IM turn, the next process tells that conversation.
102
- - `pier reload` stays in-process: channel adapters re-read configuration and
103
- idle, unwatched sessions reopen with current agent files on their next
104
- message. Streaming or watched sessions are not interrupted.
105
- - `pier update` is deliberately different: the separate updater hard-stops the
106
- service, backs up the database, replaces the package, and starts it again.
107
- It can interrupt active work. All three are operator shell commands for an
108
- installed Linux systemd service, not tools available to the agent.
101
+ - `pier restart`: refuses new work, waits up to five minutes for active turns
102
+ and Task runs, then restarts. If the deadline aborts an IM turn, the next
103
+ process tells that conversation.
104
+ - `pier reload`: channel adapters re-read configuration and idle, unwatched
105
+ sessions reopen with current agent files on their next message. Streaming or
106
+ watched sessions are not interrupted.
107
+ - `pier update`: a separate updater backs up the database and installs the new
108
+ package while Pier is still up, then hard-stops and starts the service. From
109
+ the shell it does not drain, so it can interrupt active work; the Console's
110
+ Update and auto-update drain first. All three are operator shell commands for an installed Linux systemd
111
+ service, not tools available to the agent.
109
112
 
110
113
  ## Only the Console can change
111
114
 
@@ -30,8 +30,8 @@ Four things markdown cannot express:
30
30
  thread. Asking a human to paste their own user ID is never acceptable.
31
31
  - Escape `&` `<` `>` when they are text, not markup: `&amp;` `&lt;` `&gt;`.
32
32
  - Emoji as `:white_check_mark:`, not the raw glyph.
33
- - ~11,000 chars per message: split longer content across replies in one thread
34
- rather than truncating.
33
+ - 11,000 chars per message; the tool refuses longer `text`. Split longer
34
+ content across replies in one thread rather than truncating.
35
35
 
36
36
  ## Targeting
37
37