@indigoai-us/hq-cli 5.345.190 → 5.345.193

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 (122) hide show
  1. package/CHANGELOG.md +420 -0
  2. package/assets/bot-workers/setup/worker.yaml +91 -34
  3. package/dist/command-catalog.generated.d.ts +96 -19
  4. package/dist/command-catalog.generated.js +124 -21
  5. package/dist/commands/bot-jobs.d.ts +21 -18
  6. package/dist/commands/bot-jobs.js +127 -39
  7. package/dist/commands/bot.d.ts +7 -4
  8. package/dist/commands/bot.js +289 -140
  9. package/dist/commands/core-jobs-schedule.js +10 -0
  10. package/dist/commands/daemon.js +37 -0
  11. package/dist/commands/files.d.ts +41 -6
  12. package/dist/commands/files.js +108 -27
  13. package/dist/commands/pack-start-bot.d.ts +4 -6
  14. package/dist/commands/pack-start-bot.js +4 -8
  15. package/dist/commands/signals-run.d.ts +45 -0
  16. package/dist/commands/signals-run.js +175 -0
  17. package/dist/commands/signals.d.ts +1 -0
  18. package/dist/commands/signals.js +3 -0
  19. package/dist/commands/sources.d.ts +11 -0
  20. package/dist/commands/sources.js +137 -25
  21. package/dist/commands/staff/reset-test-account.d.ts +10 -0
  22. package/dist/commands/staff/reset-test-account.js +18 -2
  23. package/dist/hq-cloud-sync-runner/bin/sync-runner.js +31171 -52512
  24. package/dist/hq-cloud-sync-runner/package.json +1 -1
  25. package/dist/lib/bot/api.d.ts +3 -2
  26. package/dist/lib/bot/config.d.ts +2 -0
  27. package/dist/lib/bot/fleet-cron.d.ts +121 -0
  28. package/dist/lib/bot/fleet-cron.js +319 -0
  29. package/dist/lib/bot/fleet-local.d.ts +113 -2
  30. package/dist/lib/bot/fleet-local.js +597 -35
  31. package/dist/lib/bot/fleet-owner-companies.d.ts +12 -0
  32. package/dist/lib/bot/fleet-owner-companies.js +30 -0
  33. package/dist/lib/bot/fleet-watch.d.ts +25 -0
  34. package/dist/lib/bot/fleet-watch.js +51 -0
  35. package/dist/lib/bot/index.d.ts +1 -1
  36. package/dist/lib/bot/index.js +1 -1
  37. package/dist/lib/bot/job-run.d.ts +5 -4
  38. package/dist/lib/bot/job-run.js +5 -4
  39. package/dist/lib/bot/jobs/hot-signal-sync.d.ts +57 -3
  40. package/dist/lib/bot/jobs/hot-signal-sync.js +163 -4
  41. package/dist/lib/bot/local-jobs.d.ts +13 -2
  42. package/dist/lib/bot/local-jobs.js +17 -4
  43. package/dist/lib/bot/progress.d.ts +14 -0
  44. package/dist/lib/bot/progress.js +47 -1
  45. package/dist/lib/bot/run.d.ts +55 -2
  46. package/dist/lib/bot/run.js +192 -10
  47. package/dist/lib/bot/setup-tool-offer.d.ts +1 -0
  48. package/dist/lib/bot/setup-tool-offer.js +16 -0
  49. package/dist/lib/bot/signals-engine-flag.d.ts +18 -0
  50. package/dist/lib/bot/signals-engine-flag.js +30 -0
  51. package/dist/lib/daemon/catalog.d.ts +4 -0
  52. package/dist/lib/daemon/catalog.js +29 -9
  53. package/dist/lib/daemon/legacy.d.ts +6 -0
  54. package/dist/lib/daemon/legacy.js +16 -10
  55. package/dist/lib/daemon/run.d.ts +1 -0
  56. package/dist/lib/daemon/run.js +10 -0
  57. package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
  58. package/dist/lib/flag-registry-worker-source.generated.js +1 -1
  59. package/dist/lib/flag-registry.d.ts +13 -1
  60. package/dist/lib/flag-registry.js +46 -0
  61. package/dist/lib/hot-channels/adapters/attio.d.ts +75 -2
  62. package/dist/lib/hot-channels/adapters/attio.js +367 -29
  63. package/dist/lib/hot-channels/adapters/gmail.d.ts +14 -0
  64. package/dist/lib/hot-channels/adapters/gmail.js +59 -2
  65. package/dist/lib/hot-channels/adapters/linear.d.ts +24 -0
  66. package/dist/lib/hot-channels/adapters/linear.js +71 -1
  67. package/dist/lib/hot-channels/adapters/meetings.js +4 -1
  68. package/dist/lib/hot-channels/adapters/slack.js +7 -3
  69. package/dist/lib/hot-channels/alert.js +1 -1
  70. package/dist/lib/hot-channels/contract.d.ts +11 -0
  71. package/dist/lib/hot-channels/contract.js +1 -0
  72. package/dist/lib/hot-channels/cursors.js +1 -1
  73. package/dist/lib/hot-channels/landing.d.ts +6 -2
  74. package/dist/lib/hot-channels/landing.js +13 -6
  75. package/dist/lib/hot-channels/ledger.js +2 -0
  76. package/dist/lib/hot-channels/loop.js +18 -4
  77. package/dist/lib/hot-channels/registry.js +9 -1
  78. package/dist/lib/hot-channels/run.d.ts +18 -0
  79. package/dist/lib/hot-channels/run.js +15 -4
  80. package/dist/lib/hot-channels/signals-pass.d.ts +1 -0
  81. package/dist/lib/hot-channels/signals-pass.js +7 -5
  82. package/dist/lib/hot-channels/sync-lock.js +22 -6
  83. package/dist/lib/mesh/live/daemon/install.d.ts +22 -0
  84. package/dist/lib/mesh/live/daemon/install.js +117 -30
  85. package/dist/lib/signals/adapters-catalog.d.ts +77 -0
  86. package/dist/lib/signals/adapters-catalog.js +194 -0
  87. package/dist/lib/signals/engine.d.ts +196 -0
  88. package/dist/lib/signals/engine.js +495 -0
  89. package/dist/lib/signals/failure-alert.d.ts +89 -0
  90. package/dist/lib/signals/failure-alert.js +175 -0
  91. package/dist/lib/signals/local-date.d.ts +3 -0
  92. package/dist/lib/signals/local-date.js +6 -0
  93. package/dist/lib/signals/projection.d.ts +42 -0
  94. package/dist/lib/signals/projection.js +67 -0
  95. package/dist/lib/signals/reply.d.ts +112 -0
  96. package/dist/lib/signals/reply.js +368 -0
  97. package/dist/lib/signals/run-lock.d.ts +36 -0
  98. package/dist/lib/signals/run-lock.js +148 -0
  99. package/dist/lib/signals/run-record.d.ts +24 -0
  100. package/dist/lib/signals/run-record.js +82 -0
  101. package/dist/lib/signals/sanitize.d.ts +105 -0
  102. package/dist/lib/signals/sanitize.js +439 -0
  103. package/dist/lib/signals/settings.d.ts +10 -0
  104. package/dist/lib/signals/settings.js +48 -0
  105. package/dist/lib/signals/steps/adapters.d.ts +14 -0
  106. package/dist/lib/signals/steps/adapters.js +156 -0
  107. package/dist/lib/signals/steps/entity-similarity.d.ts +10 -0
  108. package/dist/lib/signals/steps/entity-similarity.js +47 -0
  109. package/dist/lib/signals/steps/extract.d.ts +113 -0
  110. package/dist/lib/signals/steps/extract.js +765 -0
  111. package/dist/lib/signals/steps/garden.d.ts +24 -0
  112. package/dist/lib/signals/steps/garden.js +250 -0
  113. package/dist/lib/signals/steps/types.d.ts +112 -0
  114. package/dist/lib/signals/steps/types.js +4 -0
  115. package/dist/lib/signals/steps/upload.d.ts +43 -0
  116. package/dist/lib/signals/steps/upload.js +324 -0
  117. package/dist/lib/signals/write-guard.d.ts +63 -0
  118. package/dist/lib/signals/write-guard.js +287 -0
  119. package/dist/utils/resident-package-preload.js +16 -0
  120. package/package.json +9 -9
  121. package/dist/lib/bot/fleet-default-flag.d.ts +0 -31
  122. package/dist/lib/bot/fleet-default-flag.js +0 -127
package/CHANGELOG.md CHANGED
@@ -2,6 +2,426 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.345.193] — 2026-11-03
6
+ - HQ's setup assistant answers the desktop app's end-of-setup handoff as the
7
+ assistant it is: who it is and what it can help with (questions, finding
8
+ things, running work, and whatever is left of setup), then exactly which
9
+ setup steps are still open, or that none are, and one question. HQ puts
10
+ the company folders on this computer and the assistant's progress note
11
+ under the note, so that first reply needs no command: the tool checks and
12
+ installs that used to run inline in the first turn now wait until the
13
+ person answers. The note can no longer be answered with silence. On a
14
+ fresh install on 2026-11-02 the owner saw the
15
+ app's hello ("As soon as you finish setting up in the app, I'll pick up from
16
+ there"), then a spinner for over three minutes with nothing behind it: the
17
+ note ran as a late, silenceable setup note with no working line.
18
+ - Before slow work (the tool checks, an install, the import), the setup
19
+ assistant says what it is doing and about how long ("Give me about two
20
+ minutes to check what's on this computer, then I'll ask my first
21
+ question."); that one line now reaches the person even though the
22
+ assistant's other notes before a command do not. When it says nothing, or
23
+ goes quiet for 30 seconds after that line, it posts "Still working on it.
24
+ I'll post here as soon as I'm done." once. Every bot's kickoff turn posts
25
+ the usual working line after a silent minute, like other turns do.
26
+ - `hq bot create --auto-approve` turns an HQ Fleet Runtime bot's command approval prompts off for
27
+ every sender (`autoApprove: true`, from hq-fleet-local 0.4.19). By default the prompts stay on
28
+ and the owner's own turns are always allowed. This replaces the `--no-auto-approve` flag and the
29
+ approvals-off default described in the 0.4.18 note above.
30
+ - `hq bot jobs` and the local bot job code no longer describe local jobs as
31
+ EventBridge or cloud schedules. Comments describe the bot's own `jobs.json`
32
+ and inbox loop, and say the stored six-field `cron(...)` string is the
33
+ format hosted agent jobs also use. The internal `toEventBridgeCron` name is
34
+ kept.
35
+ - The refusal for a bare six-field cron expression now says the last field
36
+ would be a year (the form bot jobs are stored in) or the first one
37
+ seconds (Quartz), instead of naming EventBridge.
38
+ - The personal-bot message "the cloud only accepts them from company bots"
39
+ is removed. `hq bot jobs` answers from the bot's own `jobs.json` for every
40
+ bot kind, so a personal bot schedules local jobs like any other bot.
41
+ - Dependency updates: AWS SDK clients 3.1157 -> 3.1160, @sentry/node 10.77.1 -> 10.78.0, and
42
+ varlock 1.23.0 -> 1.24.0. @tobilu/qmd stays at 2.5.3 so better-sqlite3 stays on 12, which
43
+ has prebuilt binaries; 13 would need a C++ toolchain to install.
44
+ - CI: pnpm/action-setup 4 -> 5 in hq-cli workflows. v6 stays ignored because it fails on the
45
+ self-hosted runners (libatomic.so.1, see #2323); v5 runs on them.
46
+ - `hq files purge` now reports that the version will be permanently deleted on a date 7 days out and can be cancelled until then; `hq files purge --cancel <path> --version-id <id>` cancels it. Against a server without the 7-day window, it reports that the version was deleted immediately.
47
+ - `hq-hook`: the native `block-hq-glob` port (flag still off) now reads a planted
48
+ `.git` folder's HEAD and config the way git does. A HEAD git rejects, such as one
49
+ with a form feed after `ref:`, no longer stops the search for the repository, and
50
+ a config line git cannot parse makes the port ask git. It now blocks the same
51
+ Glob calls as the bash hook in these cases.
52
+ - As of #2347, `hq bot create` is Fleet-only: every new local bot runs on the
53
+ HQ Fleet Runtime, and `--legacy-runtime` is refused. Bots that already run on
54
+ the model-CLI runner keep running as before.
55
+ - On start, an HQ Fleet bot moves the jobs in its old `jobs.json` into the
56
+ Fleet schedule. `jobs.json` is renamed to `jobs.migrated.json` only when
57
+ every job moved. If some jobs could not move (for example a schedule pinned
58
+ to one year), `jobs.json` is kept with those jobs still in it, the jobs that
59
+ did move are taken out of it, and `hq` prints a warning naming the jobs left
60
+ behind. They do not run until you add them again with `hq bot jobs --add`.
61
+ - Bots no longer run the signals engine by default. Both the daily 06:30 Fleet
62
+ signals job (`hq signals run --trigger job`) and the engine path in the
63
+ built-in hot-signal-sync job (#2306) are behind the default-off flag
64
+ `cli.bot-signals-engine-job`. With the flag off, a Fleet bot registers no
65
+ signals job and removes an existing one on its next start, and the built-in
66
+ job runs the hot-channel sync alone, as it did before. Turn it on for a
67
+ company in the flag registry, or on one computer by starting the bot with
68
+ `HQ_FLAG_CLI_BOT_SIGNALS_ENGINE_JOB=true`.
69
+ - With `cli.bot-signals-engine-job` off (the default), HQ Fleet bots keep their
70
+ daily 06:30 hot-signal-sync job, and it runs the plain hot-channel sync
71
+ (`hq sources sync`). Before this fix, turning the flag off dropped the whole
72
+ job. The plain sync's report goes to the bot's `logs/hot-signal-sync.log`,
73
+ and the owner gets a DM only when the sync does not finish (for example,
74
+ when nobody is signed in). Turning the flag on switches the same job's script
75
+ to the signals engine on the next bot start.
76
+ - Bump `@indigoai-us/hq-cloud` to 6.18.108. It accepts a delegated machine
77
+ session file (`HQ_MACHINE_SESSION_FILE`), so company Outpost jobs can run hq
78
+ as the Outpost (hq-cloud #1078). It also adds `--size` to Outpost
79
+ provisioning (hq-cloud #960).
80
+ - `hq bot create` now always creates an HQ Fleet Runtime bot, including HQ's
81
+ setup assistant and the setup bots `hq pkg install --start-bot` starts. The
82
+ `agents.local-bots-hq-fleet-default` company flag is no longer read.
83
+ `--hq-fleet-runtime` is still accepted and changes nothing; `--legacy-runtime`
84
+ is refused; `--effort` and `--lean` are gone from `create`. A bot with no
85
+ `--provider` answers with Claude. Company bots still belong to exactly one
86
+ company. Bots that already run on the model-CLI runner keep running as before.
87
+ - `hq bot jobs` on an HQ Fleet bot lists, adds, reschedules and removes jobs in
88
+ the runtime's own schedule, so they fire, and posts each result to your HQ DM.
89
+ `--quiet` and a `--tz` other than this computer's are refused for these bots.
90
+ On start, jobs an HQ Fleet bot had in `jobs.json` move into the runtime once;
91
+ the old file is kept as `jobs.migrated.json`.
92
+ - HQ Fleet bots get the daily hot-signal-sync job at 06:30 as a runtime job. It
93
+ runs `hq signals run --trigger job --report-found` and posts to your DM only
94
+ when it found new signals. A personal bot gets one such job for each company
95
+ it serves, skipping a company another local bot already covers. A removed job
96
+ stays removed. `hq bot jobs` prints next-run times in UTC.
97
+ `hq signals run --report-found` prints the run reply only when signals were
98
+ found.
99
+ - HQ Fleet bots move to newer HQ Fleet Local releases on their own: hq reads the
100
+ release HQ publishes (`/v1/fleet-local/releases/latest`), never moves to an
101
+ older one, and still requires a signature from a trusted key. The hq daemon
102
+ checks every 30 minutes and shortly after it starts, waits until a bot has no
103
+ turn or scheduled job running, then updates and restarts it. The check shows
104
+ in `hq daemon status` as `fleet-bots-update`.
105
+ - HQ Fleet setup bots: the pack setup section is now part of a pack bot's
106
+ instructions, and HQ's setup assistant again offers to continue setup in the
107
+ Claude or Codex app when you use it a lot.
108
+ - `hq bot status <name> --json` reports `channels: { slack, telegram }` for HQ
109
+ Fleet bots. `hq bot start`, `stop` and `restart` refuse an HQ Fleet bot held
110
+ for cloud promotion, as they already did for other bots.
111
+ - HQ Fleet Local releases signed with the new v3 release key (from 0.4.20 on)
112
+ are trusted, next to the v1 and v2 keys. New HQ Fleet bots start on 0.4.20,
113
+ which adds Slack and Telegram for local bots and lets them see pictures and
114
+ files attached to HQ DMs.
115
+ - Installing an HQ Fleet Local release now checks every file in the download
116
+ against the signed manifest, and the runtime archive against the hash the
117
+ manifest names, so a release can only install what its signature covers.
118
+ - The hq daemon updates only HQ Fleet bots that are running, leaves a bot you
119
+ stopped alone, and restarts an updated bot only once it is idle; if a turn
120
+ starts during the download, the restart waits for the next check.
121
+ - Jobs saved by older hq versions in the cron(...) or rate(...) form move into
122
+ the HQ Fleet Runtime with the same schedule. A job the runtime cannot run is
123
+ left in jobs.migrated.json and named in the bot log.
124
+ - `hq bot`: HQ Fleet Local runtime package installs and service installs no longer
125
+ open a console window on Windows. Node hides a child only when none of its
126
+ stdio handles are inherited, so those steps now pipe their output and copy
127
+ both stdout and stderr to stderr. The output now appears when each step
128
+ finishes, not live while it runs.
129
+ - The daemon now moves a running HQ Fleet bot to a new runtime only when HQ's latest-release pointer names one. With no pointer, a failed lookup, or a pointer that needs a newer hq-cli, running bots stay on the release they have. New bots, `hq bot start`, `restart` and `adopt` still use the release built into hq-cli.
130
+ - `hq-hook`: the native `block-hq-glob` port (flag still off) now checks the
131
+ tools it relies on. It runs script tools where bash would, so it gives bash's
132
+ answer for them, including when a bad script turns a block into an allow. It
133
+ models compiled tools without starting them. It leaves the call to bash when the
134
+ environment exports bash functions or sets posix-mode options, when PATH is unset
135
+ or empty or has an entry starting with `~`, or when a tool on PATH cannot be
136
+ opened or is a file only a shell can read. This replaces the earlier note that it
137
+ answers every call without bash.
138
+ - `hq-hook`: add a native port of the `block-hq-glob` hook behind its flag
139
+ (`hook-engine-port-block-hq-glob`, off). It answers every call without bash
140
+ and matches the bash hook's exit code and output, apart from the Windows path
141
+ spelling cases named in the hook-03 report.
142
+ - `hq-hook`: the `block-credential-file-read` port follows the round-2 bash
143
+ guard (sha256 8abd1702) and its pin moves to it. The changes:
144
+ - cygpath gets the alias, PATH and trusted-path rules;
145
+ - `eval`, `source` and `.` turn off trust in command names;
146
+ - alias bodies are analysed where they are defined and where they are used;
147
+ - zsh glob forms are unknown word parts: a group inside a word, `^`, `#`
148
+ inside a word, and `~` after a glob character or with extended glob on;
149
+ - archivers may not take a directory that holds credentials;
150
+ - `~/.hq/secrets-cache/` is a credential store.
151
+ The vendored fixture and corpus are refreshed. The port flag stays off.
152
+ - `hq-hook`: the `block-credential-file-read` port follows the round-3 bash
153
+ guard and its pin moves to it. zsh glob forms on an absolute home path or
154
+ relative to a cwd in HOME, a group at the start of a word (also after a
155
+ redirect or empty quotes), and quoted parts next to a group are refused;
156
+ `^` and `#` count only after `.`, `~` or `_`, so git revisions such as
157
+ `origin/main^` pass. The vendored fixture and corpus are refreshed. The
158
+ port flag stays off.
159
+ - `hq-hook`: the `block-credential-file-read` port follows the round-4 bash
160
+ guard and its pin moves to it. `function f ( body )` bodies are checked
161
+ again, keywords before a word-start `(` count only in command position,
162
+ any unquoted `^` or mid-word `#` in a read operand blocks except for git
163
+ and npm-family commands, and the zsh numeric range `<N-M>` is a glob part.
164
+ The vendored fixture and corpus are refreshed. The port flag stays off.
165
+ For git and npm-family commands a `^`/`#` operand is checked as the
166
+ wildcard pattern zsh can expand it to, and a keyword after other prefix
167
+ keywords (`if ! ( .. )`, `time -p ( .. )`) still opens a subshell.
168
+ An unassigned variable in such a pattern is a wildcard (fails closed),
169
+ and `time -p -- ( .. )` opens a subshell.
170
+ The same applies to any git/npm operand where an unassigned expansion
171
+ sits next to a glob character (`${p}*`).
172
+ - `hq-hook`: the `block-credential-file-read` port follows the round-6 bash
173
+ guard (hq-core-staging #1767) and its pin moves to it. A `function NAME (`
174
+ body is checked wherever `function` is in command position (inside `{ }`,
175
+ after `then`), the zsh short for loop `for f (LIST) BODY` fails closed, a
176
+ word with an unquoted `^` or mid-word `#` blocks in any command except git,
177
+ npm-family tools and `[[ ]]` (so `echo id^x | xargs cat` and
178
+ `for f in i#d_rsa; ...` block), a glob in a for/select list is checked as
179
+ a read operand, and the `://` exemption needs a network URL with a host
180
+ and no `..` given to a URL tool (`cat a://../i#d_rsa` blocks). `^`/`#`
181
+ inside `$(( ))` with no nested substitution is arithmetic, and words after
182
+ `]]` (zsh `while [[ c ]] { body }`) are analysed as a command. The
183
+ vendored fixture and corpus are refreshed. The port flag stays off.
184
+ - `hq sources sync`: the hot-channel push-buffer drain now checks each buffer
185
+ file with the vault write guard before reading it. Before this, a buffer file
186
+ that was a link into another company was read and landed in the current
187
+ company before the per-file check refused it. The drain now fails closed on
188
+ such a file, with nothing read, landed, moved, or deleted. Follow-up to #2338.
189
+ - The hot-channel writers now check every company-data write, rename, link and delete against the company-scoped write guard right before it happens, not only in the adapters step's preflight. This covers landing (documents, raw sidecars, the sources index), the cursor store, the run ledger, the sync lock and its takeover files, the signals pass (signal documents, the signals index, `_audience.yaml`), the signals alert state, the meetings notes writer and push-buffer cleanup. The push-buffer folder is also checked before it is read. A link that would carry any of these into another company's folder makes the run fail closed, and nothing is written there.
190
+ - New write-guard helper `assertVaultPath(vaultRoot, file)`. For a `companies/{co}` vault it applies `assertAllowedPath` scoped to that company. For any other vault it requires the file to stay inside the vault after links are followed, and refuses a linked vault root or a linked file.
191
+ - A `companies/{co}` folder that is itself a link to a folder outside the HQ root now stops the hot-channel sync for that company, as it already stopped `hq signals run`.
192
+ - `writeFileAtomic` in `src/lib/hot-channels/landing.ts` now takes the vault root as its first argument.
193
+ - `validateNewCandidates` requires its company scope. The unscoped plain delete branch, which production never used, is removed.
194
+ - Hot signal sync: the Slack and Linear adapters now key items as `slack:{channel}:{ts}` and
195
+ `linear:{issueId}:{updatedAt}`. Slack reads only the push buffer and never polls history, so one
196
+ thread lands as one item. Linear backs off on a 429 (honoring Retry-After), keeps its watermark when
197
+ the limit outlasts the retries, and stays under 2,500 requests an hour.
198
+ - Slack and Linear are on for every company. The hq-flags kill switches `hq.hot-channel-slack` and
199
+ `hq.hot-channel-linear` turn them off for a company when set to false; an absent flag keeps them on.
200
+ When the flag service cannot be read or times out, each switch keeps the last value read on this
201
+ machine for that company (saved in `workspace/signals/flags/{company}.json`), and stays on when it
202
+ was never read here. A default-off gate still fails closed on a failed read. `hq sources sync`, the bot's hot-signal-sync job and `hq signals run` all apply the
203
+ same gate, including adapters turned off in `core/settings/signals-adapters.yaml`.
204
+ - Hot-channel Attio and Gmail adapters meet the hot-signal-sync US-010 bar.
205
+ - Attio still drains the webhook buffer, then pulls records (companies,
206
+ people, deals) and notes created since its watermark through the HQ
207
+ gateway, 50 per page. Its cursor is now JSON `{drain, pull}`; an old
208
+ plain cursor is read as the drain cursor.
209
+ - Attio event keys are `attio:{recordId}:{eventId}`. A create keys on the
210
+ Attio record or note id and the event type on both paths, so a record or
211
+ note that arrives by webhook and by the pull lands once. Other webhook
212
+ deliveries key on their record and the receiver's delivery hash.
213
+ - Upgrading keeps what the older Attio drain already landed: a delivery or
214
+ a pulled create whose document the older drain landed keeps that older
215
+ key, so the first pull after the upgrade lands no duplicates. Creates the
216
+ older drain never landed still land.
217
+ - Attio and Gmail retry a 429 up to three times, honor Retry-After, cap
218
+ requests per run, and leave the watermark where it was when the limit
219
+ persists. Gmail always calls the mailbox as `userId: "me"`.
220
+ - Gmail follows the hq-flags row `hq.hot-channel-gmail` (registered off by
221
+ default under an owner hold). Set on for a company, Gmail runs through
222
+ `hq sources sync` and `hq signals run`; off it is skipped with reason
223
+ `flag-off` and no mailbox call. When the flag cannot be read, Gmail is off
224
+ (fail closed), whatever was read before.
225
+ - The signals run record now holds items in and the token level
226
+ (workspace or user) for each adapter.
227
+ - Signals: `runSignals` accepts `registeredAdapterIds`, so any hot channel added as one adapter plus one catalog entry is gated, synced and counted in the run record without changes to the adapters step.
228
+ - `hq mesh daemon install` on Windows no longer fails with "Access is denied" for a standard user when the
229
+ shared task name `ai.getindigo.hq-mesh-daemon` already belongs to another account's task. It now registers
230
+ a per-user task (`ai.getindigo.hq-mesh-daemon.<user>`), and status, restart, uninstall and
231
+ `hq daemon migrate` follow that name.
232
+ - `hq signals run` adapters step reads `core/settings/signals-adapters.yaml` when present.
233
+ - An adapter with `enabled: false`, or whose company flag is off, is skipped and recorded with reason `flag-off`. A channel hq-pro reports as gated is recorded the same way.
234
+ - The enabled adapters run in one sync. An enabled adapter that the hq-pro catalog does not list has nothing to run and no longer fails the step.
235
+ - Without the file every registered adapter runs, as before.
236
+ - After each run the signed-in person's vault cursors are copied to `workspace/signals/watermarks/{company}/{cursorKey}.json`. Teammates' cursors are not copied.
237
+ - Hot-channel adapters declare `displayName`, `provider` and `watermarkKind`, and a contract test checks every registered adapter against the hq-pro hot-channel schema.
238
+ - Signals: a malformed or unreadable `core/settings/signals-adapters.yaml` no longer breaks
239
+ `hq sources sync` or the bot's hot-signal-sync job. hq prints one warning and runs the adapters
240
+ without yaml gating (Slack and Linear on), as when the file is missing. Follow-up to #2309.
241
+ - The bot's built-in hot-signal-sync job now runs the signals engine
242
+ (`runSignals` with `trigger: "job"`): garden, adapters, extract, garden,
243
+ upload, with the caps from `core/settings/signals.yaml` and a run record.
244
+ The job kind, company resolution and stale-company removal are unchanged.
245
+ - The owner report uses the storyboarded reply format: prose plus one
246
+ `hq-block` fence, with run complete, catch-up, late-start, finished with
247
+ problems, skipped, running and run-failed variants
248
+ (`src/lib/signals/reply.ts`), read from the v2 run record. A partial run
249
+ gets the amber "Finished with problems" badge with its reason, never the
250
+ green Complete badge. Error replies carry the fence too.
251
+ - When a run fails with a transient error (the run record has
252
+ `nextRetryAt`), the bot runs the job once more at that time, in the same
253
+ process. Only then does the failure reply offer "Wait for <time>"; config
254
+ and sign-in failures, and a failed retry, offer only "Try now". A stop or
255
+ restart cancels a pending retry.
256
+ - The run started right after the job is registered uses the `manual`
257
+ trigger, so a bot first started in the early morning still runs that
258
+ morning's job.
259
+ - Reply times use the job's own time zone from `jobs.json`.
260
+ - `formatJobCreatedReply` writes the job's own time zone in words (for
261
+ example "Mountain Time"), never a fixed zone.
262
+ - `hq bot jobs --every` accepts "every morning at 6" (and afternoon,
263
+ evening, night), giving `0 6 * * *`.
264
+ - `hq signals run` extract step now runs the ontology worker's process-source
265
+ skill on each unprocessed item of every local-run source channel.
266
+ - One model turn per item under the main local bot's coding-tool login
267
+ (claude or codex; grok is refused). Candidates are written only through
268
+ `ontology-candidate.sh`; an item is marked done only after its turn succeeds.
269
+ - Each turn is confined to its own item. It runs from a temp folder outside
270
+ HQ, can read only the item's channel folder, and writes candidates only
271
+ through a per-item command that fixes the company, audience and source ref
272
+ and refuses `--body-file`. Claude runs with `--permission-mode dontAsk`,
273
+ `--restricted`, `--tools Read,Bash`, no MCP servers and no settings files;
274
+ codex runs with a permission profile that reads only that folder, writes
275
+ only the company's `_candidates` folders and has no network.
276
+ - After a turn the step touches only the new candidates that carry the
277
+ item's source ref. One written into another company is deleted and the
278
+ item fails. Candidates from other writers that appear during the turn
279
+ (other companies' bots, other sources, garden moving files into `_done`)
280
+ are left alone. Worklist items whose path or audience could inject text
281
+ into the prompt or leave the channel folder are refused.
282
+ - `--max-turns` applies per item for claude (codex has no turn cap);
283
+ `--max-budget-usd` is a run budget summed from each turn's reported cost.
284
+ When it runs out the step stops with `budget-exceeded` and leaves the rest
285
+ for the next run.
286
+ - Failed items are listed under `failures` and retried next run; a logged-out
287
+ claude is recorded as `signed-out`. The item's own new candidates without
288
+ `audience_key`, with another audience or with an unknown type are deleted
289
+ and counted.
290
+ - `hq signals run` garden step reports accurate counts and adds two guards.
291
+ - `itemsIn` is the number of pending entity and signal candidates before
292
+ the run. `itemsOut` is promoted entities, signals and facts.
293
+ - Candidates whose `source_ref` or `path` points at the Build Your Own AGI
294
+ manuscript (`companies/indigo/knowledge/book/`) are not promoted. They stay
295
+ in place and are counted as `skipped`. Candidates left in `_held/` by a
296
+ run that stopped part-way are put back at the start of the next run.
297
+ - An entity candidate whose name is 0.8 to 0.95 similar to an existing
298
+ entity of the same type, or to an earlier pending candidate of the same
299
+ type, is not merged. It moves to `ontology/_candidates/_review/<date>/`
300
+ (local date) and a line is appended to `ontology/review/merges.jsonl`,
301
+ with `against` set to `entity` or `candidate`. These are counted as
302
+ `review`.
303
+ - `hq signals run`: every write in a run is now held to that run's company.
304
+ The run record, lock, step writes, member projection, flag state and alert
305
+ state are checked against the company being run, and the write guard now
306
+ refuses any write that a link moves out of the root it names. A linked
307
+ `workspace/signals` or `companies/{co}/...` folder that resolves into another
308
+ company's folder fails the run with a write-guard error instead of writing
309
+ one company's data into another company's folder.
310
+ - Behavior change: company-scoped signals writes under `workspace/` or
311
+ `personal/` now refuse to pass through any symlink inside the HQ root (for
312
+ example `workspace/signals` linked to another folder in HQ). A link that
313
+ points outside the HQ root, such as to an external disk, was already
314
+ refused. The error names the linked path. Replace the link with a real
315
+ folder to run signals again.
316
+ - `hq signals run` runs the local signals engine for each company with
317
+ signals or ontology capture on.
318
+ - Fixed step order: garden, adapters, extract, garden, upload. `--step`
319
+ runs one step. Extract and upload are placeholders for now.
320
+ - The adapters step runs the same hot-channel sync as `hq sources sync`
321
+ and the bot's hot-signal-sync job. Without an HQ login it is skipped.
322
+ - Writes a run record per company (`schemaVersion: 2`) under
323
+ `workspace/signals/runs/<date>/`, updated at each step boundary. It has
324
+ `status` (`running`, `complete`, `partial`, `failed` or `skipped`), `pid`,
325
+ `jobRanAt`, `errorKind` (`transient`, `config` or `auth`),
326
+ `failureReason`, `nextRetryAt`, `partialReason`, `skipReason` and
327
+ `counts` (`sourcesIn`, `signalsOut`). `summary.json` in the same folder
328
+ keeps every company's latest entry.
329
+ - A `--trigger job` run counts as the day's job only when it finishes
330
+ complete or partial. A second job that day does nothing unless `--force`
331
+ is passed; chat, brief and manual runs do not reset it, and a failed job
332
+ does not block the next job fire. A transient failure records a retry
333
+ time 15 minutes after it finished.
334
+ - Each company runs under a lock file next to its record. A second run for
335
+ the same company exits 3 and prints the running run's start time and
336
+ trigger. A lock whose process is gone, or that is older than 3 hours, is
337
+ taken over and the takeover is noted in the record.
338
+ - Exit codes: 0 complete, partial, skipped or already ran; 1 failed; 2 usage
339
+ or config error; 3 another run holds the lock.
340
+ - A job that starts after `lateStartHour` runs garden only and is recorded
341
+ as partial (`late-start`), or failed if garden fails.
342
+ - A missing or failing `knowledge-prefs.sh` fails the company with a config
343
+ error instead of skipping it as if capture were off.
344
+ - `--max-turns` and `--max-budget-usd` default to `core/settings/signals.yaml`,
345
+ else 40 turns and $2.00. Invalid values in that file (for example
346
+ `maxTurns: 0`) are ignored with a warning.
347
+ - The engine's own writes (records, summary, locks) are checked against an
348
+ allow-list of signal folders and
349
+ written atomically through a temp file opened with O_EXCL; symlinks at
350
+ the target, dangling ones included, are refused. Deviation from the PRD:
351
+ the allow-list also includes each company's `sources/` folder, where the
352
+ hot-channel adapters land documents and cursors.
353
+ - `hq signals run` now checks each company's run record for a silent stall: the extract step read
354
+ source items and produced no signals, or extract failed after the adapters step landed new
355
+ sources. Hot-channel landings that no declared source channel covers do not count on their own.
356
+ When it sees a stall, it sends the same signals-failure alert `hq sources sync` sends, deduped to
357
+ one owner DM per company per hour in `sources/_runs/@{personKey}/_alerts.json`. In the terminal
358
+ the alert prints to stderr and does not use the hourly dedupe, so it never holds back the bot's
359
+ owner DM. Healthy runs, quiet days, dry runs, a repeated job run on the same day, and an extract
360
+ skipped for `no-sources` or `ontology-worker-missing` send nothing. Programmatic callers pass the
361
+ owner-DM sender as `signalsAlert` on `runSignals`.
362
+ - `hq signals run` writes `catchUp` into each company's summary.json entry, taken from that company's run record.
363
+ - New `isRunRecord` type guard in `src/lib/signals/run-record.ts` checks a signals run record (contract v2) before the brief gate or desktop reads it. Its status and error-kind lists are typed against the engine's types, so they cannot drift.
364
+ - `hq signals run` upload step posts sanitized meeting observations to the
365
+ caller's own `member-signals/{memberUid}/` prefix.
366
+ - One `POST /v1/files/contributions/company/{slug}/meetings` per signal
367
+ promoted since the last upload, with body `{ destination, contribution }`
368
+ where `contribution` is a `sanitized-contribution.v1` object built by the
369
+ new TypeScript sanitizer (`local-sanitizer/v1`). Signals without a
370
+ meeting source, an audience receipt that includes the caller, or
371
+ citation timestamps are skipped locally.
372
+ - The watermark lives at `workspace/signals/upload/<company>/<memberUid>.json`.
373
+ - A 403 `CONTRIBUTION_DISABLED` marks the step skipped (`flag-off`); a 401
374
+ or no HQ login marks it skipped with the step `errorKind: signed-out`.
375
+ - A 404 `WRITE_COMPANY_NOT_FOUND` stops the step after one request with a
376
+ config error. A 422 `WRITE_BUCKET_NOT_PROVISIONED` or a
377
+ `source_unavailable` rejection is retried on a later run instead of
378
+ being recorded as rejected.
379
+ - The step pulls the member's own projection
380
+ (`ontology/projections/members/<memberUid>/v1/`) into
381
+ `workspace/signals/projections/` and records `missing`, `stale` or
382
+ `ready`. hq-pro has no route that triggers the projection export yet;
383
+ the signals engine is intended to be its only caller once one exists,
384
+ and the desktop no longer calls it.
385
+ - `--dry-run` lists what would upload and sends nothing.
386
+ - The run record carries `uploaded: { count, bytes }`.
387
+ - Vault sync is unchanged. `hq sync`, `hq sync push` and the background sync
388
+ runner push and pull exactly what they did before, and hq-cli does not
389
+ write `.hqignore` or `.hqsyncignore`. Promoted signals
390
+ (`companies/<co>/signals/@<key>/...`) and company `sources/` stay local
391
+ today, because hq-cloud refuses client uploads of `signals/**` and
392
+ `sources/**`. Sharing promoted signals through audience ACLs is a later,
393
+ separate hq-cloud change.
394
+ - `hq signals run` holds three more writers to the run's company. The garden step checks both candidate trees before it starts and moves or deletes a candidate only through the company-scoped write guard. The adapters step refuses to start the hot-channel sync when a link under `companies/{co}/sources/` or `companies/{co}/signals/` leads out of that company's folder. The extract step refuses a channel when a link under either `_candidates/` folder does the same, for every runtime. In all three cases the step fails and writes nothing.
395
+ - The extract step now works out which company owns a new candidate from its real path, with links followed. A file written through a linked folder into another company's tree counts as that company's file, so it is deleted there and the item fails. Every candidate delete goes through the guard.
396
+ - The extract step's candidate wrapper checks the candidate path again on every call, right before it writes. It refuses when a link sits on or under either `_candidates/` folder, so a link the model turn creates after the step's preflight check cannot send a candidate into another company.
397
+ - New write-guard helpers: `guardedRemove`, `guardedRmdir`, `realRelPath` and `assertNoEscapingLinks`.
398
+ - `hq staff reset-test-account <email> --handoff-shared-companies` wipes a test
399
+ account that owns a company with real members. The company is kept and passes
400
+ to its next real member, and the plan names who gets it. Without the option,
401
+ the reset still refuses as before.
402
+
403
+ ## [5.345.191] — 2026-11-02
404
+ - `hq bot list` and `hq bot status --json` rows now carry `engine: "hq-fleet-runtime"` for a bot on
405
+ the HQ Fleet Runtime, so the desktop app can tell a Fleet bot from a model-CLI bot (Create brief
406
+ uses this to prefer a Fleet bot).
407
+ - `hq bot auth` on an HQ Fleet Runtime bot gives the restarted gateway up to 15 s to come up before
408
+ reporting; it used to say "signed in, but did not start" while the bot was in fact starting.
409
+ - `hq bot status` on an HQ Fleet Runtime bot shows whether its gateway is installed as a login
410
+ service instead of a "launchd: not installed" line that was about the legacy bot daemon.
411
+ - `hq bot create --no-auto-approve` now reaches an HQ Fleet Runtime bot's profile (`autoApprove:
412
+ false`); with hq-fleet-local 0.4.18 the default profile runs with command approvals off, like a
413
+ model-CLI bot, so a headless bot no longer stalls on an approval nobody can answer.
414
+ - HQ Fleet Runtime pin moves to hq-fleet-local 0.4.18, which stores the bot's
415
+ machine credential with Windows DPAPI under PowerShell 5.1 and no longer
416
+ flashes a console on Windows. The CLI now trusts two release signing keys
417
+ (the 2026-10-30 key and a second key added 2026-11-01); a manifest signed by
418
+ either verifies, and any other key is rejected.
419
+ - `hq bot create` / `hq bot start` (HQ Fleet Runtime): every helper, uv and
420
+ Python child process now always starts with `windowsHide`, which a caller's
421
+ options can no longer turn off, so no console window flashes on Windows.
422
+ A new test records every child process in a Windows create, start, stop and
423
+ status run and checks each one.
424
+
5
425
  ## [5.345.190] — 2026-11-01
6
426
  - `hq files purge <path> --version-id <id>` permanently deletes one old version of a vault file, after a confirmation prompt.
7
427
  - `hq bot create` / `hq bot start` on Windows: the shared HQ Fleet Runtime now
@@ -107,17 +107,17 @@ instructions: |
107
107
  one), follow "When the app hands you settled steps" below instead of the
108
108
  explain-or-jump question.
109
109
 
110
- When you receive the kickoff, do the state check and start the first
111
- unfinished step immediately, ending with exactly one concrete question or
112
- action for the user:
113
-
114
- 1. The state check, quietly: your progress note; the "Owner context" block
115
- (who they are and which companies they belong to in HQ Cloud);
116
- `companies/manifest.yaml` and `companies/` (is there a company folder on
117
- this computer); and the tools this HQ leans on (see step 1).
118
- 2. Do the part of step 1 you can do yourself right now (install a missing
119
- tool, fix a broken one). Do not start step 2 yet.
120
- 3. Reply with one line on what you found or fixed, then ask, in exactly
110
+ When you receive the kickoff, reply within seconds, ending with exactly
111
+ one question:
112
+
113
+ 1. The quick state check: your progress note (if the app put it under
114
+ the kickoff, it is already in front of you; otherwise one read), the
115
+ "Owner context" block (who they are and which companies they belong to
116
+ in HQ Cloud), and whether `companies/` has a company folder. Nothing
117
+ else. Do not check, install or repair tools in this turn: that is
118
+ step 1, and it starts after they answer, with a heads-up line first
119
+ (see "Before slow work" below).
120
+ 2. Reply with one line on where things stand, then ask, in exactly
121
121
  these words:
122
122
 
123
123
  > Do you want me to explain HQ to you, and then continue with the setup, or just jump straight in?
@@ -168,7 +168,7 @@ instructions: |
168
168
  the tools on this Mac, HQ Cloud, their company, the work they already have,
169
169
  their business and the tools it runs on, a little about them, and their first bot.
170
170
  3. The explain-or-jump question, in the exact words from "When you receive
171
- the kickoff", after doing step 1's checks quietly.
171
+ the kickoff". No tool checks before it.
172
172
 
173
173
  Then stop and wait. Do not preview later steps, do not number the steps, and
174
174
  do not ask them to confirm that they want to start.
@@ -217,11 +217,10 @@ instructions: |
217
217
  `Setup note from the HQ desktop app:` and carries the full handoff:
218
218
  everything the visual setup settled, the import included. The same note
219
219
  goes to a setup bot that already existed (a reinstall or a second
220
- computer). That note is your kickoff: do the quiet state check from
221
- "When you receive the kickoff", then reply with one line on what you
222
- found or fixed and the first unfinished step's one question, with the
223
- changes listed below. A step that is not in its `done` was not done in
224
- the app, so it is yours to do.
220
+ computer). That note is your kickoff, and your reply to it is the first
221
+ thing the person hears from you: write it as "Your first message after
222
+ the handoff" below says. A step that is not in its `done` was not done
223
+ in the app, so it is yours to do.
225
224
  - **Older builds of the app** sent the kickoff, which begins `Kickoff:`,
226
225
  while the person was still in the visual setup, and then later notes
227
226
  that begin `Setup note from the HQ desktop app:` and carry any subset of
@@ -230,6 +229,40 @@ instructions: |
230
229
  last message asked about, say so in one line and carry on with the next
231
230
  unfinished step's question; never ask the person to answer the old one.
232
231
 
232
+ ### Your first message after the handoff
233
+
234
+ The person has just left the app's setup screens and is looking at this
235
+ chat. The app's hello already went out under your name; this reply is the
236
+ first thing you say yourself, and it is where you stop being a setup
237
+ script and become their assistant. It has to arrive within seconds, so
238
+ write it from what is already in front of you: the handoff, the "Owner
239
+ context" block, and the facts the app puts under the note (the company
240
+ folders on this computer and your progress note). Run no command and
241
+ read no file first. Do not install, repair or check tools, and do not
242
+ run anything else that takes time; all of that happens after they
243
+ answer, and you say how long it takes before you start (see "Before slow
244
+ work"). Never answer this note with `no_reply`.
245
+
246
+ Three short parts, in plain prose, no list:
247
+
248
+ 1. Who you are and what you do: their HQ assistant, here for anything in
249
+ HQ. They can ask you a question, have you find something, or give you
250
+ work to run, and you also finish whatever is left of setup. One
251
+ sentence, in your own words; not the hello the app sent.
252
+ 2. Where setup stands, concretely. Name the steps still open in the
253
+ person's words ("two things are still open: the apps your business
254
+ runs on, and a little about you"), never as step numbers and never as
255
+ the whole plan. If every step is done, say so in one line, naming who
256
+ they are signed in as and their company.
257
+ 3. One question. With steps open: offer to do the first open one now and
258
+ ask that step's one question. With none open: offer one concrete first
259
+ task drawn from this HQ and ask whether to start it.
260
+
261
+ Never say "as soon as you finish setting up", never send them back to the
262
+ app's screens, and never name a step they cannot act on from this chat.
263
+ If a step really does need a click in the app (an invite to accept, a
264
+ sign-in to finish), say exactly where that button is.
265
+
233
266
  Read it like this. Every field is optional except `from` and `v`, and fields
234
267
  or `done` entries you do not recognize are ignored, not an error (the app
235
268
  adds more over time). Several handoffs add up: a step is settled once any of
@@ -289,11 +322,11 @@ instructions: |
289
322
  in `done`, never offer to bring in the work on this computer and never
290
323
  scan it again: the app already did, and the person just watched it.
291
324
  When you need what the scan found, read the report the handoff names.
292
- - Start at the first step that is not settled. By plan that is the business
293
- interview in step 4, carrying on after the note taker and project
294
- management tools: what kind of business they run, and what else a
295
- business like theirs should set up. For someone who joined a team it is
296
- the tour.
325
+ - Once they answer your first message, start at the first step that is
326
+ not settled. By plan that is the business interview in step 4, carrying
327
+ on after the note taker and project management tools: what kind of
328
+ business they run, and what else a business like theirs should set up.
329
+ For someone who joined a team it is the tour.
297
330
  - When the team step is not settled (`company` is not in `done`, or
298
331
  `team` is missing or null), end your first message with this block, as
299
332
  the very last thing, so the app shows its "Who do you work with?" card:
@@ -307,16 +340,19 @@ instructions: |
307
340
  only.
308
341
  - Do not ask the explain-or-jump question. The person already chose to jump
309
342
  straight in, in the app; write `Intro: skipped` in your progress note.
310
- - Do not greet again and do not repeat the plan: the app already sent your
311
- hello.
343
+ - Do not repeat the app's hello word for word and do not list the plan.
344
+ Say who you are and what you can do in your own words, as "Your first
345
+ message after the handoff" says.
312
346
  - Write every settled step into your progress note as done, with the facts
313
347
  the handoff gave you (the name, the coding tool, the team choice and
314
348
  company, the import counts and report path, the note taker and project
315
349
  management apps), before you reply.
316
- - The quiet state check still runs, and the HQ itself wins over the
317
- handoff: if something the handoff calls settled is plainly not true on
318
- this computer (no HQ Cloud sign-in, say), fix that part without re-asking
319
- the question the person already answered.
350
+ - The quiet state check still runs: the quick part (your note, the
351
+ "Owner context" block, the manifest) before your first message, the
352
+ tool checks from step 1 on the turn after it. The HQ itself wins over
353
+ the handoff: if something the handoff calls settled is plainly not true
354
+ on this computer (no HQ Cloud sign-in, say), fix that part without
355
+ re-asking the question the person already answered.
320
356
 
321
357
  If the handoff is malformed (the JSON does not parse, `from` or `v` is wrong,
322
358
  or `done` is not a list), ignore it and run the normal flow, exactly as
@@ -475,11 +511,12 @@ instructions: |
475
511
 
476
512
  ## How you talk
477
513
 
478
- - Write nothing while you work. Anything you write right before running a
479
- command is never shown to them, so a heads-up there is lost; the app
480
- tells them by itself when a step takes more than half a minute. Never
481
- narrate commands, checks, retries or problems you are fixing. They see
482
- the result.
514
+ - Write nothing while you work, except the one heads-up line "Before
515
+ slow work" asks for. Anything else you write right before running a
516
+ command is never shown to them; if you then stay silent for half a
517
+ minute, HQ posts a short "still working" line for you. Never narrate
518
+ commands, checks, retries or problems you are fixing. They see the
519
+ result.
483
520
  - Never recommend a slash command, a skill, or an `hq` command to the
484
521
  person, and never tell them to type one ("type `/handoff`", "run
485
522
  `/setup`"). Commands are yours to run. When they should do something
@@ -686,6 +723,25 @@ instructions: |
686
723
  timer), never run a slow command twice to see if it changes, and never run a
687
724
  sync to check something you can read directly.
688
725
 
726
+ ## Before slow work
727
+
728
+ Some steps take real time: the tool checks and installs in step 1, the
729
+ import, a connection test, anything that calls out to the internet. A
730
+ person watching a spinner for minutes with no words assumes you are
731
+ stuck. So before any work that takes more than a few seconds, write one
732
+ plain line first, on its own, saying what you are about to do and about
733
+ how long, with a real number, then do the work:
734
+
735
+ > Give me about two minutes to check what's on this computer, then I'll ask my first question.
736
+
737
+ That line reaches the person (it is the one message HQ keeps when you
738
+ write right before a command); any other note you write before a
739
+ command is dropped. One such line per turn. When the work is done, post
740
+ the result in one line and the next question in the same message. Prefer
741
+ fewer, faster checks: run the tool checks as one command, not seven;
742
+ check only what the next step needs; never re-check what your note says
743
+ is done.
744
+
689
745
  ## The seven steps
690
746
 
691
747
  These are the same path the `/setup` skill walks
@@ -716,7 +772,8 @@ instructions: |
716
772
 
717
773
  ### 1. Tools
718
774
 
719
- Make sure the machine has what HQ leans on, and fix what it does not. Start
775
+ Say first how long this takes ("Before slow work"), then make sure the
776
+ machine has what HQ leans on, and fix what it does not. Start
720
777
  from the installer's manifest (`~/.hq/install-manifest.json`) when there is
721
778
  one, put HQ's managed toolchain on the path the way the skill shows, and
722
779
  check `node`, `hq`, `qmd`, `git`, `yq`, `jq` and `gh`. Install missing