@turmind/halo 0.1.9 → 0.2.1-bc7cf2b

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 (66) hide show
  1. package/README.md +1 -5
  2. package/admin-out/404.html +1 -1
  3. package/admin-out/_next/static/chunks/212-170855bed19f67f3.js +1 -0
  4. package/admin-out/_next/static/chunks/{503-7c6d9935df6e0cea.js → 307-d7a99f9a4a436ec3.js} +1 -1
  5. package/admin-out/_next/static/chunks/525.2c10a4fd329d0d24.js +1 -0
  6. package/admin-out/_next/static/chunks/app/layout-c2fa8035998e48d5.js +1 -0
  7. package/admin-out/_next/static/chunks/app/page-31317ef1fb0ba31f.js +1 -0
  8. package/admin-out/_next/static/chunks/{webpack-8bd7ef88a6527feb.js → webpack-47520cdc55be63e7.js} +1 -1
  9. package/admin-out/_next/static/css/66df341f99220f5c.css +3 -0
  10. package/admin-out/index.html +1 -1
  11. package/admin-out/index.txt +21 -20
  12. package/bundled-docs/design/agent.md +1 -1
  13. package/bundled-docs/design/evolution.md +11 -2
  14. package/bundled-docs/design/halo-city.md +31 -0
  15. package/bundled-docs/design/prompt-system.md +3 -3
  16. package/bundled-docs/design/session.md +26 -1
  17. package/bundled-docs/design/storage.md +13 -0
  18. package/bundled-docs/dev/acp-adapter.md +31 -4
  19. package/bundled-docs/dev/api.md +6 -4
  20. package/bundled-docs/dev/desktop-packaging.md +98 -8
  21. package/bundled-docs/dev/env.md +4 -2
  22. package/bundled-docs/dev/previews.md +3 -0
  23. package/bundled-docs/dev/tools.md +3 -1
  24. package/bundled-docs/guide/cli.md +40 -16
  25. package/bundled-docs/guide/getting-started.md +3 -2
  26. package/bundled-docs/requirements/editor.md +1 -1
  27. package/bundled-docs/requirements/explorer.md +11 -0
  28. package/dist/index.js +14102 -13249
  29. package/package.json +1 -2
  30. package/templates/INSTRUCTIONS.md +0 -4
  31. package/templates/agents/__apply_agent__/AGENT.md +41 -19
  32. package/templates/agents/__evo_agent__/AGENT.md +73 -45
  33. package/templates/agents/__evo_agent__/agent.yaml +9 -6
  34. package/templates/agents/__score__/AGENT.md +65 -33
  35. package/templates/agents/deep-executor/AGENT.md +23 -6
  36. package/templates/agents/deep-executor/agent.yaml +4 -1
  37. package/templates/agents/default/AGENT.md +32 -8
  38. package/templates/agents/executor/AGENT.md +19 -1
  39. package/templates/agents/executor/agent.yaml +6 -3
  40. package/templates/models/aws-bedrock-claude-invoke.yaml +53 -0
  41. package/templates/prompts/all/WORKSPACE_CONVENTIONS.md +5 -0
  42. package/templates/prompts/bootstrap/BOOTSTRAP.md +4 -0
  43. package/templates/prompts/root/WORKSPACE_MEMORY.md +2 -0
  44. package/templates/skills/acp/SKILL.md +8 -1
  45. package/templates/skills/acp/templates/SKILL.md.tmpl +2 -1
  46. package/templates/skills/acp/templates/ask.py +2 -1
  47. package/templates/skills/acp/templates/config.yaml.tmpl +5 -0
  48. package/admin-out/_next/static/chunks/298-d6b224b569da4de0.js +0 -1
  49. package/admin-out/_next/static/chunks/661.a4fe93f3f2a5e871.js +0 -1
  50. package/admin-out/_next/static/chunks/app/layout-58f773336e6fce61.js +0 -1
  51. package/admin-out/_next/static/chunks/app/page-cc8043335282d745.js +0 -1
  52. package/admin-out/_next/static/css/8fce4e1a54dac12a.css +0 -3
  53. package/bundled-docs/plans/cron-tasks.md +0 -362
  54. package/bundled-docs/plans/design-suggestions.md +0 -203
  55. package/bundled-docs/plans/issue-session-deep-nesting.md +0 -208
  56. package/bundled-docs/plans/launch-plan.md +0 -111
  57. package/bundled-docs/plans/md-and-agent-refactor.md +0 -197
  58. package/bundled-docs/plans/remote-mac-worker.md +0 -196
  59. package/bundled-docs/plans/roadmap.md +0 -40
  60. package/bundled-docs/plans/self-evolution.md +0 -1379
  61. package/bundled-docs/plans/todo.md +0 -32
  62. package/bundled-docs/plans/weixin-channel.md +0 -255
  63. package/bundled-docs/test/docs-seeding.md +0 -101
  64. package/bundled-docs/test/session.md +0 -374
  65. /package/admin-out/_next/static/{IBP_mwBDf-cPwZSZZGpi8 → iSrc6429pyTB0lK-EE5oH}/_buildManifest.js +0 -0
  66. /package/admin-out/_next/static/{IBP_mwBDf-cPwZSZZGpi8 → iSrc6429pyTB0lK-EE5oH}/_ssgManifest.js +0 -0
@@ -1,32 +0,0 @@
1
- # TODO
2
-
3
- ## Architecture
4
-
5
- (done)
6
-
7
- ## Deep session nesting
8
-
9
- (done) — detailed analysis in [issue-session-deep-nesting.md](issue-session-deep-nesting.md)
10
-
11
- ## Other
12
-
13
- - [ ] `eventListeners` should support multiple listeners (Map<string, handler> → Map<string, handler[]>) so multiple frontends can subscribe to the same session tree simultaneously
14
-
15
- ## Workspace settings.yaml copy in share-workspace
16
-
17
- share-workspace 当前完全排除 `settings.yaml`(stage.py:63 ALWAYS_EXCLUDED)。
18
- 但 workspace 自己的 `settings.yaml`(如 `<ws>/.halo/settings.yaml`,跟
19
- `~/.halo/secrets/settings.yaml` 不同)其实包含项目特定的非 secret 偏好,
20
- 应该带走。需要区分:
21
-
22
- - `~/.halo/secrets/settings.yaml` — 含密码 / API key,**永远不带**
23
- - `<workspace>/.halo/settings.yaml`(如果存在) — 项目级偏好,**应该带**
24
-
25
- 实施时要看 workspace 是否真的支持 `<ws>/.halo/settings.yaml` 加载;如果
26
- 不支持,这条 todo 是个 feature request 而非修复。
27
-
28
- ## i18n: agent management "Internal" section
29
-
30
- `agent-management-main.tsx` 新加的 internal scope label 当前是硬编码英文
31
- "Internal"。其他 scope label 用了 i18n key (`t('common.global')` /
32
- `t('common.workspace')`)。下次集中补 i18n 时加 `t('common.internal')`。
@@ -1,255 +0,0 @@
1
- # WeChat Channel Integration Plan
2
-
3
- **Status**: shipped. Channel implementation lives in [packages/server/src/channels/wechat/](../../../packages/server/src/channels/wechat/) and routes in [packages/server/src/routes/weixin.ts](../../../packages/server/src/routes/weixin.ts). Runtime behavior is documented in [design/wechat.md](../design/wechat.md). This file is kept as the design log.
4
-
5
- ## Goals
6
-
7
- Let the user talk to Halo from WeChat on their phone, sharing the same workspace + session as the web client.
8
- On the road, exchange messages / see simple results on WeChat; at the desk, use web for the full conversation / code / files.
9
-
10
- ## Architecture
11
-
12
- ```
13
- ┌── ws/ (web channel) ─┐
14
- Halo server (9527) ──┤ ├── SessionManager
15
- └── channels/weixin/ (new) ─┘ (per workspace)
16
- ↕ HTTPS long-poll
17
- ilinkai.weixin.qq.com
18
- ```
19
-
20
- Two channels are peers; both are SessionManager subscribers + callers.
21
-
22
- ## Data model
23
-
24
- ### Workspace ↔ Bot mapping
25
-
26
- One bot (= one `ilink_bot_id` from a QR scan) is bound to one workspace. A workspace can bind multiple bots (redundancy or use-case split), though 1:1 is recommended.
27
-
28
- ### Session strategy
29
-
30
- - **One WeChat user → one long-lived session**
31
- - Session ID: `wx_<normalizedUserId>` (e.g. `a1b2c3@im.wechat` → `wx_a1b2c3-im-wechat`)
32
- - Session lives in the bot's bound workspace
33
- - Uses the `default` agent
34
-
35
- On the web side the workspace's session list shows this session (labelled `wx: <username or userId>`).
36
-
37
- ### Storage
38
-
39
- WeChat bot accounts are **cross-workspace** (they record which workspace a bot binds to). Not in a workspace DB; separate file: `~/.halo/global/weixin.db` (new).
40
-
41
- Table `weixin_accounts`:
42
-
43
- | Field | Type | Notes |
44
- |---|---|---|
45
- | account_id | TEXT PK | Normalised ilink_bot_id (e.g. `abc-im-bot`) |
46
- | bot_token | TEXT | For all subsequent API auth |
47
- | base_url | TEXT | Regional URL from QR scan |
48
- | user_id | TEXT | ilink_user_id of the scanner |
49
- | workspace_path | TEXT | Absolute path of the bound workspace |
50
- | label | TEXT | User-provided name (for UI) |
51
- | enabled | INTEGER | 1 = long-poll active; 0 = disabled |
52
- | created_at | INTEGER | |
53
- | updated_at | INTEGER | |
54
-
55
- ## Code changes
56
-
57
- ### Phase 1: shared foundation
58
-
59
- **1.1 SessionManager multi-listener**
60
- File: `packages/server/src/agents/session-manager.ts`
61
-
62
- Current: `eventListeners: Map<rootId, handler>` stores a single handler; later callers overwrite earlier ones.
63
- Target: `Map<rootId, Set<handler>>`; `emitEvent` iterates the Set.
64
-
65
- API changes:
66
- - `registerEventListener(rootId, handler)` returns `() => void` (unsubscribe)
67
- - `unregisterEventListener(rootId)` removes a specific handler (signature preserved for backwards compat)
68
-
69
- Callers to update:
70
- - ws/handler.ts three `registerEventListener` sites (lines 228, 287, 373, 408)
71
- - ws/handler.ts one `unregisterEventListener` site (line 343)
72
- Use the returned unsubscribe fn instead of clearing by rootId.
73
-
74
- **1.2 SessionManager registry**
75
- File: `packages/server/src/agents/session-manager-registry.ts` (new)
76
-
77
- Extract ws/handler.ts:54's closure-scoped `sessionManagers: Map<string, SessionManager>`:
78
-
79
- ```ts
80
- export class SessionManagerRegistry {
81
- private cache = new Map<string, SessionManager>()
82
- getOrCreate(workspacePath: string): SessionManager
83
- get(workspacePath: string): SessionManager | undefined
84
- list(): Array<{ workspacePath: string; sm: SessionManager }>
85
- }
86
- ```
87
-
88
- `index.ts` creates one `registry` shared by ws setup and weixin setup.
89
-
90
- ws/handler.ts uses the registry (minimal change: `getSessionManager(path)` delegates to the registry).
91
-
92
- ### Phase 2: WeChat channel
93
-
94
- **2.1 Global DB**
95
- File: `packages/server/src/db/weixin-db.ts` (new)
96
-
97
- - `createWeixinDb(globalDir)` → open `~/.halo/global/weixin.db`, create the table
98
- - Drizzle schema for `weixin_accounts`
99
-
100
- **2.2 HTTP client**
101
- File: `packages/server/src/channels/weixin/api.ts` (new)
102
-
103
- Copy from plugin [api.ts](/tmp/weixin-inspect/plugin/package/src/api/api.ts), keep:
104
- - `getUpdates` (long-poll)
105
- - `sendMessage`
106
- - `notifyStart` / `notifyStop`
107
- - `getUploadUrl` + CDN upload (media support later; stub for now)
108
-
109
- Drop:
110
- - `redactUrl/redactBody` (no redacted logs needed)
111
- - `loadConfigRouteTag` + `SKRouteTag` header (internal routing)
112
- - `ilink_appid` reading from package.json → hard-code `"bot"`
113
- - `iLink-App-ClientVersion` → hard-code `buildClientVersion('2.1.10')`
114
-
115
- **2.3 QR login**
116
- File: `packages/server/src/channels/weixin/login.ts` (new)
117
-
118
- Copy from plugin [login-qr.ts](/tmp/weixin-inspect/plugin/package/src/auth/login-qr.ts), export:
119
- - `startWeixinLoginWithQr({ sessionKey })` → `{ qrcodeUrl, sessionKey }`
120
- - `waitForWeixinLogin({ sessionKey })` → `{ connected, botToken, accountId, baseUrl, userId }`
121
-
122
- Preserve concurrent-login via sessionKey (multiple scans in parallel).
123
-
124
- **2.4 Account DAL**
125
- File: `packages/server/src/channels/weixin/accounts.ts` (new)
126
-
127
- Thin DAL:
128
- - `insertAccount(row)`
129
- - `updateAccount(accountId, patch)`
130
- - `deleteAccount(accountId)`
131
- - `listAccounts()` / `getAccount(accountId)`
132
- - `getEnabledAccounts()`
133
- - `normalizeAccountId(rawId)` — from plugin: `abc@im.bot` → `abc-im-bot`
134
-
135
- **2.5 Long-poll handler**
136
- File: `packages/server/src/channels/weixin/handler.ts` (new)
137
-
138
- Main loop:
139
-
140
- ```
141
- per enabled account:
142
- loop {
143
- resp = await getUpdates({ baseUrl, token, sync_buf })
144
- for msg in resp.msgs:
145
- handleInboundMessage(account, msg)
146
- sync_buf = resp.get_updates_buf
147
- }
148
- ```
149
-
150
- `handleInboundMessage(account, msg)`:
151
- 1. Extract `from_user_id` / `text` / `context_token`
152
- 2. sessionId = `wx_` + normalize(from_user_id)
153
- 3. sm = `registry.getOrCreate(account.workspace_path)`
154
- 4. If session doesn't exist: `sm.createSession('default', null, 'WeChat: <userId>', 'default', sessionId)`
155
- 5. Register a one-shot listener (or a long-lived reused one) to collect stream/tool_call/error/complete for this turn
156
- 6. `sm.sendUserMessage(sessionId, text)`
157
- 7. Events → text aggregation (see 2.6) → `api.sendMessage(bot_token, to=from_user_id, text, context_token)`
158
-
159
- On process start, walk `getEnabledAccounts()` and launch a long-poll per account.
160
- Expose `startAccount(accountId)` / `stopAccount(accountId)` for the API layer.
161
-
162
- **2.6 Event adapter**
163
- File: `packages/server/src/channels/weixin/event-adapter.ts` (new)
164
-
165
- WeChat `sendMessage` is whole-block; LLM is streaming — we coalesce. Copy plugin's `StreamingMarkdownFilter` strategy:
166
-
167
- - ≥ 200 chars accumulated → flush
168
- - 3s silence → flush
169
- - `complete` → flush remainder
170
- - Tool calls not forwarded to WeChat (user sees details on web); errors are forwarded
171
-
172
- Pseudocode:
173
-
174
- ```ts
175
- class WeixinResponder {
176
- constructor(sendFn: (text: string) => Promise<void>)
177
- handle(event: OrchestratorEvent) {...}
178
- flush()
179
- }
180
- ```
181
-
182
- **2.7 Wire-up**
183
- File: `packages/server/src/index.ts`
184
-
185
- - Create `registry = new SessionManagerRegistry()`
186
- - Open `weixinDb = createWeixinDb(path.join(HALO_HOME, 'global'))`
187
- - `setupWebSocketHandler({ wss, registry })` (signature change)
188
- - `startWeixinChannels({ registry, db: weixinDb })`
189
- - On SIGTERM, call `stopAllWeixinChannels()` (which sends notifyStop)
190
-
191
- ### Phase 3: API + frontend admin UI
192
-
193
- **3.1 Backend routes**
194
- File: `packages/server/src/routes/weixin.ts` (new)
195
-
196
- - `POST /api/weixin/login/start` → `{ qrcodeUrl, sessionKey }`
197
- - `POST /api/weixin/login/wait` (body: `{ sessionKey, workspacePath, label? }`)
198
- On success, insertAccount + startAccount immediately
199
- - `GET /api/weixin/accounts` → list
200
- - `PATCH /api/weixin/accounts/:id` (label / workspacePath / enabled)
201
- - `DELETE /api/weixin/accounts/:id` → stop long-poll + delete row
202
-
203
- Mount after `index.ts:121`.
204
-
205
- **3.2 Frontend page**
206
- Directory: `packages/admin/src/features/weixin/` (new)
207
-
208
- - `weixin-settings.tsx` — adds "WeChat Bot" to Settings menu
209
- - List each bot: label / workspace / status / [edit] [delete]
210
- - "Add Bot" button → QR modal → poll wait API → on success let the user pick a workspace + name → save
211
- - QR: use client-side [qrcode](https://www.npmjs.com/package/qrcode) (pass the qrcodeUrl string)
212
-
213
- Add the tab inside the settings feature, matching current settings page style.
214
-
215
- ## Execution order
216
-
217
- 1. ✅ Phase 1.1 multi-listener (10 min)
218
- 2. ✅ Phase 1.2 Registry (15 min)
219
- 3. ✅ Phase 2.1 DB + schema (10 min)
220
- 4. ✅ Phase 2.2 HTTP API (30 min)
221
- 5. ✅ Phase 2.3 QR login (20 min)
222
- 6. ✅ Phase 2.4 accounts DAL (15 min)
223
- 7. ✅ Phase 2.6 event adapter (15 min)
224
- 8. ✅ Phase 2.5 handler main loop (45 min)
225
- 9. ✅ Phase 2.7 wire up index.ts (15 min)
226
- 10. ✅ Phase 3.1 API routes (20 min)
227
- 11. ✅ Phase 3.2 frontend page (60 min)
228
- 12. ✅ Compile + manual QR scan test
229
-
230
- ## Test path (manual)
231
-
232
- 1. Deploy, open web, go to Settings → WeChat Bot → Add
233
- 2. Scan the QR on your phone, see the bot friend request
234
- 3. Accept the friend request, pick an existing workspace + enter a name
235
- 4. Send "hi" from WeChat to the bot
236
- 5. Watch the reply (with a ≥ 200 char response, test the chunking)
237
- 6. Open the same workspace on the web, see `wx: xxx` in the session list; click to see the full conversation
238
- 7. Send a message from the web to the same session; confirm it arrives on WeChat
239
-
240
- ## Out of scope (explicit)
241
-
242
- - Image / voice / video messages (plugin supports them; MVP is text-only)
243
- - Group chats (plugin is private-chat only anyway)
244
- - /slash commands (plugin has them; we don't copy)
245
- - Typing indicators (`sendTyping` uncalled)
246
- - CDN media upload
247
- - Complex routing of one bot's messages to multiple sessions in a workspace
248
- - Inter-bot message isolation (assumption: user knows which bot maps to which workspace)
249
-
250
- ## Risks
251
-
252
- - **Tencent may cap bots-per-WeChat-account**: test and see; if exceeded, fall back to "1 bot per user"
253
- - **`bot_type=3` stability**: hard-coded by the plugin; we follow
254
- - **Long-poll connection count**: each bot opens one 35s-long connection; 10 bots is fine
255
- - **Event-coalescing edge cases**: the 200-char / 3s cutoffs can split Markdown mid-block. Start with plugin defaults and tune after real usage.
@@ -1,101 +0,0 @@
1
- # Platform Docs Seeding Test Cases
2
-
3
- Test URL: local server (default `http://localhost:9527/?folder=<absolute-workspace-path>`).
4
- Password: whatever `HALO_PASSWORD` is set to, or the random password printed by the server on first startup.
5
-
6
- Prerequisite: `~/.halo/global/` already exists from a prior run. Pick any workspace that is **not** the halo source repo (e.g. `/path/to/test-workspace`).
7
-
8
- ---
9
-
10
- ## Test matrix
11
-
12
- ### A. First-time seed
13
-
14
- | # | Scenario | Method | Expected |
15
- |---|---|---|---|
16
- | A1 | Fresh install simulation | `rm -rf ~/.halo/global/docs && restart server` | Startup log: `[Init] Seeding platform docs from ...`; `~/.halo/global/docs/` has 13 files |
17
- | A2 | Source detection in monorepo | Start server from `/path/to/halo`, delete docs first | Log: `Seeding platform docs from /path/to/halo/.halo/docs` |
18
- | A3 | Source detection for packaged install | Create `packages/server/bundled-docs/guide/skills.md` with "FAKE"; delete `~/.halo/global/docs/`; restart | Log says `Seeding platform docs from .../bundled-docs`; `~/.halo/global/docs/guide/skills.md` contains "FAKE" |
19
- | A4 | No source at all | Move init.js somewhere isolated (no repo parent, no bundled-docs), run | Log: `[Init] No docs source found — skipping platform docs seed`; no crash; `~/.halo/global/docs/` not created |
20
- | A5 | Allowlist boundary | After A1, check what's in `~/.halo/global/docs/` | Only 13 files: `guide/` (8), `dev/` (3), `requirements/` (2). **No** `INDEX.md`, `design/`, `test/`, `plans/`, or other `dev/`/`requirements/` files (the doc lookup table now lives inline in the `halo` skill) |
21
-
22
- ### B. User edits and upgrades
23
-
24
- | # | Scenario | Method | Expected |
25
- |---|---|---|---|
26
- | B1 | User edit preserved | Edit `~/.halo/global/docs/guide/skills.md` → restart server | Edit intact; no `[Init] Created` log for that file |
27
- | B2 | Deleted file restored | `rm ~/.halo/global/docs/guide/agents.md` → restart | Log: `[Init] Created .../agents.md`; file restored from source |
28
- | B3 | Version bump hint | Set `.template-version` to a value below the current `TEMPLATE_VERSION` (see `packages/server/src/init.ts`), restart | Log: `[Init] Seed templates updated: v<old> → v<current>. To refresh a specific file, delete it from ... and restart.` Plus `[Server] Templates outdated (v<old> → v<current>), refreshing ~/.halo/global/` from the startup version-check |
29
- | B4 | Idempotent second run | Restart the server twice in a row | Second run has **no** `[Init] Created` logs; `.template-version` stays at the current `TEMPLATE_VERSION` |
30
-
31
- ### C. Agent-facing integration
32
-
33
- | # | Scenario | Input | Expected |
34
- |---|---|---|---|
35
- | C1 | `halo` skill carries the doc table | Open any chat with the default agent, Debug mode → first message → Prompt button | System prompt lists the `halo` skill in the available-skills block. Activate it (`activate_skill halo`) and the loaded body contains `### Platform Documentation` with the "User asks … / Read …" lookup table |
36
- | C2 | Agent knows about bundled docs | Open chat in a non-halo workspace, ask: "what platform docs ship with halo?" | Agent activates the `halo` skill and answers from its lookup table (no `file_read` needed for the overview) |
37
- | C3 | Answer to "how do I test my skill?" | New chat, ask: "I just made a skill. How do I test it?" | Agent reads `~/.halo/global/docs/guide/testing-agents-and-skills.md` directly (path from system prompt table), answers with the attach-to-agent → Test → trigger flow |
38
- | C4 | Answer to "where do API keys go?" | New chat, ask: "I want my skill to use an API key. Where do I put it?" | Agent reads `guide/secrets-and-credentials.md`, explains schema in `config.yaml` + value in `settings.yaml` + `<<ENV>>` + skill body using `{{params.<key>}}` short form |
39
- | C5 | Answer for a question NOT in bundled docs | New chat, ask: "how does the file watcher debounce work?" | Agent looks for it in the docs, doesn't find (bundled docs don't have `design/`), falls back to reading code or admits it doesn't know — **does not hallucinate** |
40
- | C6 | Works in any workspace | Repeat C3 in a workspace that has **no** `.halo/docs/` of its own (e.g. `/path/to/test-workspace`) | Same answer — proves docs come from `~/.halo/global/`, not the workspace |
41
-
42
- ### D. Negative / edge cases
43
-
44
- | # | Scenario | Method | Expected |
45
- |---|---|---|---|
46
- | D1 | Permissions issue | `chmod -w ~/.halo/global/docs/` → delete one file → restart | `[Init] Failed to read template` or similar error logged; server still starts (seed is best-effort) |
47
- | D2 | Concurrent starts | Start two server processes at the same time against the same home dir | Only one wins the pid lock; losers exit cleanly (not a seed-specific test, but confirms seed doesn't race) |
48
- | D3 | Partially-populated docs | Remove half the files in `~/.halo/global/docs/guide/`; restart | Exactly the removed files get `[Init] Created` logs; others untouched |
49
- | D4 | Garbage `.template-version` | Write `abc` to `~/.halo/global/.template-version`; restart | Treated as 0; seed runs; version is reset to the current `TEMPLATE_VERSION` at the end |
50
-
51
- ---
52
-
53
- ## Verification helpers
54
-
55
- ### Check seeded file count
56
-
57
- ```bash
58
- find ~/.halo/global/docs -type f | wc -l
59
- # Expected: 13
60
- ```
61
-
62
- ### Check allowlist boundary (A5)
63
-
64
- ```bash
65
- # These should all NOT exist
66
- ls ~/.halo/global/docs/design/ 2>&1 # No such file
67
- ls ~/.halo/global/docs/test/ 2>&1 # No such file
68
- ls ~/.halo/global/docs/plans/ 2>&1 # No such file
69
- ls ~/.halo/global/docs/requirements/terminal.md 2>&1 # No such file
70
- ls ~/.halo/global/docs/dev/api.md 2>&1 # No such file
71
- ```
72
-
73
- ### Check `halo` skill carries the doc table (C1)
74
-
75
- ```bash
76
- grep -A 3 "Platform Documentation" ~/.halo/global/skills/halo/SKILL.md
77
- # Expected: "### Platform Documentation" section with the inline "User asks … / Read …" lookup table
78
- ```
79
-
80
- ### Check source that was used
81
-
82
- ```bash
83
- grep "Seeding platform docs\|No docs source" /tmp/halo.log 2>/dev/null | tail -1
84
- # Or check server's stdout — the log line is emitted once per startup
85
- ```
86
-
87
- ### Force a fresh seed
88
-
89
- ```bash
90
- rm -rf ~/.halo/global/docs ~/.halo/global/.template-version
91
- # Restart server
92
- # Expected: 13 [Init] Created log lines for docs/*
93
- ```
94
-
95
- ---
96
-
97
- ## Known limitations
98
-
99
- - **Cross-links to non-bundled docs** (e.g. `guide/skills.md` → `design/prompt-system.md`) will 404 if an agent tries to follow them. Agents should answer from the doc body rather than chase links. Noted in the `halo` skill body.
100
- - **Version downgrade** not detected: if you set `.template-version` higher than the current `TEMPLATE_VERSION`, nothing happens — no warning, no overwrite. Not a concern in practice.
101
- - **Seed does not overwrite**. If a user edits a file and a newer Halo version ships a fix to the same doc, the user's edit wins. Workaround: `rm` the file and restart.