pi-crew 0.9.44 → 0.9.47

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 (65) hide show
  1. package/CHANGELOG.md +136 -0
  2. package/README.md +38 -3
  3. package/dist/build-meta.json +349 -203
  4. package/dist/index.mjs +2229 -2968
  5. package/dist/index.mjs.map +4 -4
  6. package/docs/decisions/2026-07-21-broker-phase4-default-on.md +77 -0
  7. package/docs/decisions/2026-07-21-broker-windows-perms.md +91 -0
  8. package/docs/decisions/2026-07-22-broker-phase4-gated-on.md +99 -0
  9. package/docs/decisions/README.md +3 -0
  10. package/docs/publishing.md +26 -0
  11. package/package.json +3 -1
  12. package/scripts/build-bundle.mjs +7 -0
  13. package/scripts/postinstall.mjs +35 -1
  14. package/scripts/pty_probe.py +174 -0
  15. package/skills/real-test-pi-crew/SKILL.md +659 -0
  16. package/src/agents/discover-agents.ts +1 -1
  17. package/src/config/config.ts +42 -1
  18. package/src/config/defaults.ts +45 -1
  19. package/src/config/types.ts +19 -0
  20. package/src/extension/register.ts +6 -1
  21. package/src/extension/registration/context-builder.ts +4 -0
  22. package/src/extension/registration/lifecycle-handlers.ts +200 -6
  23. package/src/extension/registration/registration-types.ts +9 -0
  24. package/src/extension/registration/subagent-manager-setup.ts +178 -59
  25. package/src/extension/run-import.ts +21 -1
  26. package/src/extension/team-tool/api.ts +4 -2
  27. package/src/prompt/prompt-runtime.ts +108 -0
  28. package/src/runtime/async-runner.ts +9 -1
  29. package/src/runtime/broker-issuer.ts +37 -0
  30. package/src/runtime/child-pi-spawn.ts +53 -0
  31. package/src/runtime/child-pi.ts +42 -11
  32. package/src/runtime/crew-broker-child.ts +88 -0
  33. package/src/runtime/crew-broker-client.ts +673 -0
  34. package/src/runtime/crew-broker-tokens.ts +84 -0
  35. package/src/runtime/crew-broker.ts +1276 -0
  36. package/src/runtime/dynamic-workflow-context.ts +7 -3
  37. package/src/runtime/dynamic-workflow-runner.ts +1 -1
  38. package/src/runtime/manifest-cache.ts +30 -0
  39. package/src/runtime/plan-templates.ts +8 -6
  40. package/src/runtime/resilient-edit.ts +16 -15
  41. package/src/runtime/role-permission.ts +27 -2
  42. package/src/runtime/run-coalesced-task-group.ts +72 -15
  43. package/src/runtime/task-packet.ts +1 -1
  44. package/src/schema/config-schema.ts +14 -0
  45. package/src/state/event-log.ts +88 -34
  46. package/src/state/locks.ts +53 -0
  47. package/src/state/mailbox.ts +208 -4
  48. package/src/state/run-metrics.ts +40 -12
  49. package/src/ui/key-utils.ts +42 -0
  50. package/src/ui/keybinding-map.ts +29 -3
  51. package/src/ui/live-run-sidebar.ts +1 -9
  52. package/src/ui/run-dashboard.ts +29 -9
  53. package/src/ui/settings-overlay.ts +42 -22
  54. package/src/utils/incremental-reader.ts +105 -0
  55. package/src/utils/ndjson.ts +115 -0
  56. package/src/utils/session-utils.ts +30 -0
  57. package/src/utils/socket-path.ts +127 -0
  58. package/src/utils/visual.ts +27 -91
  59. package/workflows/default.workflow.md +1 -1
  60. package/workflows/fast-fix.workflow.md +1 -1
  61. package/workflows/plan-execute.workflow.md +1 -1
  62. package/workflows/review.workflow.md +1 -1
  63. package/src/runtime/auto-resume.ts +0 -100
  64. package/src/runtime/notebook-helpers.ts +0 -88
  65. package/src/runtime/orphan-sentinel.ts +0 -7
@@ -0,0 +1,659 @@
1
+ ---
2
+ name: real-test-pi-crew
3
+ description: "End-to-end verification for pi-crew changes: fast critical tests, 3-path kill-switch proof, bundle md5 sync, live TUI probing, and smoke team runs."
4
+ origin: pi-crew
5
+ triggers:
6
+ - "test the change"
7
+ - "verify it works"
8
+ - "is it really working"
9
+ - "live TUI test"
10
+ - "smoke test pi-crew"
11
+ - "run the critical tests"
12
+ - "rebuild bundle"
13
+ - "check bundle md5"
14
+ - "tmux test"
15
+ - "pty probe"
16
+ - "did the verifier hang"
17
+ - "worker timeout"
18
+ - "verifier hangs"
19
+ - "rebuild and retry"
20
+ - "tier 1 / tier 2 / tier 3 / tier 4 / tier 5 / tier 6 / tier 7 / tier 8"
21
+ ---
22
+
23
+ # real-test-pi-crew
24
+
25
+ End-to-end verification discipline for pi-crew changes. Distilled from the broker Phase-4 rollout (commits `1cb2dca` → `d599578` → `612e18b` → `4186284`, July 2026). The pain this skill prevents: shipping code that compiles + unit-tests-green but breaks in the user's live Pi session, or hangs the verifier worker.
26
+
27
+ **When to use**: after any change to `src/runtime/crew-broker*.ts`, `src/ui/`, `src/config/`, `src/extension/registration/lifecycle-handlers.ts`, `src/runtime/child-pi-spawn.ts`, `src/runtime/plan-templates.ts`, `workflows/*.workflow.md`, or before any commit touching these paths.
28
+
29
+ ## Core principle: disk ≠ live Pi
30
+
31
+ Two locations hold pi-crew state:
32
+
33
+ 1. **Source** (`src/`, `test/`, `package.json`, `workflows/`, `src/runtime/plan-templates.ts`) — git-tracked, `git diff` shows it.
34
+ 2. **Bundle** (`dist/index.mjs`) — pre-built, loaded by Pi at **extension cold-start only**.
35
+
36
+ The 3-way resolution order for `dist/index.mjs` (per `index.ts:5-22`):
37
+ ```
38
+ 1. dist/index.mjs (pre-built bundle) if present ← DEFAULT since the v0.9.17 bundle-as-default rollout
39
+ 2. Inline strip-types loading — fallback when bundle missing
40
+ OR PI_CREW_USE_BUNDLE=0
41
+ ```
42
+
43
+ > **Note on version pins**: this skill mentions specific versions (v0.9.17, v0.9.46, v0.9.47) as anchors for *when a behavior was introduced*, not as a constraint on which version the skill applies to. The verification discipline (Tiers 1–8) applies to every pi-crew release. Verify the version pin is still accurate via `git log --oneline -- index.ts` and `git log --oneline -- src/ui/run-dashboard.ts`.
44
+
45
+ **Workflow files are runtime data** — `workflows/*.workflow.md` and task prompt strings inside `src/runtime/plan-templates.ts` are loaded per-call, NOT bundled. Edits take effect immediately, no rebuild needed.
46
+
47
+ **The most common silent-failure mode**: edit `src/`, run `npm test` (pass!), rebuild bundle (good md5!), but the session still has the old code because Pi wasn't `/quit`-ed + reopened.
48
+
49
+ ## Prerequisites
50
+
51
+ Before running any tier, verify these are available:
52
+
53
+ | Tool | Used in | Check |
54
+ |---|---|---|
55
+ | `node` (>=22) | Tiers 1, 2, 3 | `node --version` |
56
+ | `npm` | All tiers | `npm --version` |
57
+ | `bash` | All tiers | `echo $BASH_VERSION` |
58
+ | `md5sum` | Tiers 3, 4, 8 | `which md5sum` (or `md5` on macOS) |
59
+ | `tmux` | Tier 5 | `which tmux` (optional — Tier 6 is the fallback) |
60
+ | `python3` | Tier 6 | `python3 --version` (optional — Tier 5 is the fallback) |
61
+ | `pi` in PATH | Tiers 5, 6 | `which pi` (must be installed via `npx pi install .`) |
62
+ | `git` | Reference lookups | `git log --oneline -1` should work |
63
+
64
+ Working directory should be the pi-crew repo root:
65
+
66
+ ```bash
67
+ cd ${PWD}
68
+ ls package.json # must exist
69
+ ```
70
+
71
+ ### CI integration
72
+
73
+ The skill maps to existing CI gates as follows:
74
+
75
+ | CI gate | Skill tier | File |
76
+ |---|---|---|
77
+ | `npm test:critical` (manual / pre-commit) | Tier 1 | n/a — not in CI by default |
78
+ | `PI_CREW_BROKER=0 npm run test:critical` | Tier 2 (env kill switch path) | n/a — manual |
79
+ | `npm run typecheck` | Tier 3 | `.github/workflows/*.yml` (every PR) |
80
+ | Bundle-staleness check | Tier 3 last step | `scripts/check-bundle-staleness.mjs` |
81
+ | Multi-OS CI | n/a (skill is local) | `.github/workflows/*.yml` — Linux + macOS + Windows |
82
+ | Full `npm test` (>5 min) | n/a — too slow for in-loop | CI only |
83
+
84
+ To add Tier 1 to a pre-commit hook:
85
+
86
+ ```bash
87
+ # .git/hooks/pre-commit (or via husky / pre-commit framework)
88
+ npm run test:critical || {
89
+ echo "✋ test:critical failed — fix before commit"
90
+ exit 1
91
+ }
92
+ ```
93
+
94
+ To add Tier 1 to CI as a fast-feedback gate (under 30s):
95
+
96
+ ```yaml
97
+ # .github/workflows/fast.yml
98
+ - name: Critical unit tests
99
+ run: npm run test:critical
100
+ - name: Disabled-path proof
101
+ run: PI_CREW_BROKER=0 npm run test:critical
102
+ - name: Explicit-on proof
103
+ run: PI_CREW_BROKER=1 npm run test:critical
104
+ ```
105
+
106
+ ---
107
+
108
+ ## Tier 1 — Critical unit tests (~21s, 97 tests, the only suite you need for broker/UI changes)
109
+
110
+ **What**: run the curated 14-file fast subset.
111
+
112
+ **Why this exists**: full `npm run test:unit` runs 642 files, >4 minutes. Verifier worker timeout is 300s → worker killed mid-run, run = "hang". The fix (introduced in commit `1cb2dca`) splits out a `test:critical` subset covering exactly what changed in the broker/UI work.
113
+
114
+ **How**:
115
+
116
+ ```bash
117
+ time npm run test:critical
118
+ ```
119
+
120
+ Expected output: `# tests 97 # pass 97 # fail 0 # duration_ms ~21000`.
121
+
122
+ **References**:
123
+
124
+ | What | Where |
125
+ |---|---|
126
+ | Script definition | `package.json:67` — list of 14 files passed to `node scripts/test-runner.mjs` |
127
+ | Introduced in commit | `1cb2dca fix(verifier): use test:critical instead of test:unit to avoid worker timeout` |
128
+ | Runner wrapper | `scripts/test-runner.mjs` — injects `--test-force-exit`, forwards to `tsx --test` |
129
+ | The 14 files | broker: `crew-broker-{handshake,stale-socket,feature-flag,server-gate,client-fallback,mailbox-observer,close-during-reconnect,steer-dedup,symlink-steering}.test.ts`; UI: `keybinding-map.parity.test.ts`, `pi-tui-dispatch-probe.test.ts`, `session-utils-extract.test.ts`; config: `config-schema-sync.test.ts`, `child-pi-env-spread.test.ts` |
130
+ | Failure mode that motivates it | Worker timeout in `src/runtime/child-pi-constants.ts:23` (`RESPONSE_TIMEOUT_MS = DEFAULT_CHILD_PI.responseTimeoutMs` = 300000); verifier LLM ran `npm test` and got killed at 300s with exit 143 (SIGTERM) |
131
+
132
+ **Run after**: any edit to `src/runtime/crew-broker*.ts`, `src/ui/`, `src/config/`, `src/extension/registration/lifecycle-handlers.ts`, or `src/runtime/child-pi-spawn.ts`.
133
+
134
+ ---
135
+
136
+ ## Tier 2 — Three-path kill-switch proof
137
+
138
+ **What**: prove all three precedence paths in `effectiveEnabled()` still resolve correctly.
139
+
140
+ **Why**: any change to `DEFAULT_BROKER` (in `src/config/defaults.ts:169`) or `effectiveEnabled()` (in `src/extension/registration/lifecycle-handlers.ts:819-833`) can silently break the precedence chain. The chain:
141
+
142
+ ```
143
+ PI_CREW_BROKER=0 → disabled (env always wins)
144
+ broker.enabled=false → disabled (config)
145
+ PI_CREW_BROKER unset → enabled (DEFAULT_BROKER=Phase 4 default-on)
146
+ PI_CREW_BROKER=1 → enabled (explicit; redundant under default-on)
147
+ ```
148
+
149
+ **How**:
150
+
151
+ ```bash
152
+ # 1. default path (whatever DEFAULT_BROKER.enabled is right now)
153
+ npm run test:critical
154
+ # 2. env kill switch
155
+ PI_CREW_BROKER=0 npm run test:critical
156
+ # 3. env explicit-on (must still work under default-on)
157
+ PI_CREW_BROKER=1 npm run test:critical
158
+ ```
159
+
160
+ All three must show `# pass 97 # fail 0`. Measured times in this session: ~20s for default and `PI_CREW_BROKER=0`, ~21s for `PI_CREW_BROKER=1` (varies ±1s run-to-run).
161
+
162
+ **References**:
163
+
164
+ | What | Where |
165
+ |---|---|
166
+ | `DEFAULT_BROKER` constant | `src/config/defaults.ts:169-173` (Phase 4: `enabled: true`) |
167
+ | Precedence function | `src/extension/registration/lifecycle-handlers.ts:819-833` (`return cfg?.enabled !== false;` at line 828) |
168
+ | `resolveBrokerEnvOverride` | `src/config/defaults.ts:186-193` |
169
+ | Env-precedence unit tests | `test/unit/crew-broker-feature-flag.test.ts:31` (default-on assertion), `:54-110` (env=1/env=0/unset/arbitrary cases at lines 54, 66, 78, 90, 103) |
170
+ | Controller-gate tests | `test/unit/crew-broker-server-gate.test.ts:78` (env kill switch under default-on), `:143` (env=1 with no config) |
171
+ | Decision doc | `docs/decisions/2026-07-22-broker-phase4-gated-on.md` |
172
+ | Superseded doc | `docs/decisions/2026-07-21-broker-phase4-default-on.md` (marked SUPERSEDED in commit `4186284`) |
173
+ | Default flip commit | `612e18b feat(broker): Phase 4 gated ON — flip broker.enabled default to true` |
174
+
175
+ ---
176
+
177
+ ## Tier 3 — Typecheck + bundle rebuild + md5 sync
178
+
179
+ **What**: prove the bundle actually contains the source you just edited.
180
+
181
+ **How**:
182
+
183
+ ```bash
184
+ npm run typecheck # ~20s, exits 0 with "strip-types import ok"
185
+ npm run build:bundle # <1s, prints "[build-bundle] dist/index.mjs NNNN KB in NNN ms"
186
+ md5sum dist/index.mjs
187
+ ```
188
+
189
+ Compare the printed md5 against what the user's Pi session loaded. If they differ → the session is running stale bundle.
190
+
191
+ **References**:
192
+
193
+ | What | Where |
194
+ |---|---|
195
+ | `typecheck` script | `package.json` `"typecheck"` — runs `tsc --noEmit && node --experimental-strip-types -e "await import('./index.ts'); ..."` |
196
+ | `build:bundle` script | `package.json` `"build:bundle"` — runs `node scripts/build-bundle.mjs` |
197
+ | Bundle builder | `scripts/build-bundle.mjs` (esbuild-based, bundles `index.bundle.ts` → `dist/index.mjs`) |
198
+ | Bundle resolution rule | `index.ts:5-22` (entrypoint docstring); also `scripts/build-bundle.mjs:14-20` (entrypoint preference); **symlink is live for source files but the bundled `dist/index.mjs` is loaded** |
199
+ | Postinstall hook | `scripts/postinstall.mjs:43` — best-effort bundle rebuild; falls back to strip-types if esbuild missing |
200
+ | Bundle md5 after Phase-4 commit | `1cc4d55e18add7b9a036c569143320b6` (~2.78 MB at the time; bundle size drifts ±5% between releases, check current `ls -la dist/index.mjs`) |
201
+
202
+ ---
203
+
204
+ ## Tier 4 — Bundle sync into a live Pi session
205
+
206
+ **What**: ensure the user's running Pi sees your changes.
207
+
208
+ **The immediate-vs-rebuild rule** (which edits take effect without a rebuild):
209
+ - `workflows/*.workflow.md` edits → **immediate**, no rebuild, no restart
210
+ - `src/runtime/plan-templates.ts` `taskTemplate` strings → **immediate**, runtime data
211
+ - Everything else (`src/` edits, `package.json`) → must `npm run build:bundle` THEN user `/quit` + reopen Pi
212
+
213
+ **How to verify in this session**:
214
+
215
+ ```bash
216
+ md5sum dist/index.mjs
217
+ # then in Pi session, the user runs `md5sum` in a shell tool
218
+ # if they differ, user needs to /quit + reopen
219
+ ```
220
+
221
+ **How to verify in a fresh pty/tmux session without disturbing the user's main Pi**:
222
+
223
+ ```bash
224
+ tmux -S /tmp/sock new-session -d -x 160 -y 50 -s pi \
225
+ "cd ${PWD} && exec pi 2>&1"
226
+ ```
227
+
228
+ **References**:
229
+
230
+ | What | Where |
231
+ |---|---|
232
+ | Bundle resolution | `index.ts:5-22` — "dist/index.mjs (pre-built bundle) if present AND not explicitly disabled — DEFAULT since v0.9.17" |
233
+ | Bundle size impact after Phase-4 flip | `docs/decisions/2026-07-22-broker-phase4-gated-on.md` §Verification: "2.78 MB before and after the flip; the broker code was already in the bundle; only the default boolean changed" |
234
+ | Symlink confirmation | **The symlink lives in the CONSUMING project, not inside pi-crew itself.** From the pi-crew repo, check the parent: `readlink ../node_modules/pi-crew` (returns `../pi-crew` for dev clones). For global installs: `readlink "$(npm root -g)"/pi-crew`. Pattern is always `<consumer>/node_modules/pi-crew → <pi-crew-repo>`. |
235
+
236
+ ---
237
+
238
+ ## Tier 5 — Live TUI probe via tmux send-keys
239
+
240
+ **What**: drive a real Pi session's keystrokes from the shell, capture screen state.
241
+
242
+ **Why tmux and not raw pty**: tmux gives you a clean separation — session persists across your bash commands, capture-pane gives ASCII screenshot, send-keys with hex escapes covers `\x1b[A` (legacy CSI), `\x1bOA` (app-cursor-mode), and Kitty-protocol variants.
243
+
244
+ **How**:
245
+
246
+ ```bash
247
+ # Spawn (160x50 fits ~standard TUI)
248
+ tmux -S /tmp/sock new-session -d -x 160 -y 50 -s pi \
249
+ "cd ${PWD} && exec pi 2>&1"
250
+
251
+ # Wait for pi to start
252
+ sleep 2
253
+
254
+ # Send slash command
255
+ tmux send-keys -t pi '/team-help' Enter
256
+ sleep 1
257
+ tmux capture-pane -t pi -p | tail -40
258
+
259
+ # Send raw escape sequence (app-cursor-mode up arrow)
260
+ tmux send-keys -t pi $'\x1bOA'
261
+ sleep 0.5
262
+ tmux capture-pane -t pi -p > /tmp/screen-after-up.txt
263
+ ```
264
+
265
+ **Key gotcha**: terminals send arrow keys as one of 3 byte sequences. pi-crew's `matchesKey()` helper (`src/ui/key-utils.ts:37-42`, the `keyOf()` function) normalizes all of them — but verify it does in your probe:
266
+
267
+ | Mode | Up arrow | Down arrow | Source |
268
+ |---|---|---|---|
269
+ | Legacy CSI | `\x1b[A` | `\x1b[B` | vt100, xterm |
270
+ | App-cursor-mode | `\x1bOA` | `\x1bOB` | vim, less, full-screen apps |
271
+ | Kitty protocol | `\x1b[1;2A` (Shift+Up) etc. | — | modern terminals (kitty, foot, ghostty) |
272
+
273
+ **References**:
274
+
275
+ | What | Where |
276
+ |---|---|
277
+ | `keyOf()` helper | `src/ui/key-utils.ts:37-42` (import + type alias at lines 16-18) |
278
+ | Dispatch path | `src/ui/keybinding-map.ts` (migrated to `matchesKey()` in commit `f05a10d`) |
279
+ | Golden snapshot test | `test/unit/keybinding-map.parity.test.ts` — 7 `it()` blocks asserting parity against a generated golden snapshot; BINDINGS table has 27 entries (`src/ui/keybinding-map.ts:132-180`) |
280
+ | Live probe test | `test/unit/pi-tui-dispatch-probe.test.ts` — direct probe of dispatch (3 tests) |
281
+ | Probe commit | `84944f7 test(probe): add invalidate() to control object so typecheck passes` |
282
+ | Tab/Space bind | `src/ui/run-dashboard.ts` + commit `15a0ffe fix(ui): also bind Tab/Space/Enter/S to select in dashboard dispatch` |
283
+ | Tmux session file | `/tmp/sock` (created on first `new-session -S`) |
284
+
285
+ ---
286
+
287
+ ## Tier 6 — Live TUI probe via Python pty (bulk keys + diag)
288
+
289
+ **What**: send many keys in sequence + capture per-keystroke diag output.
290
+
291
+ **When to use**: when you need to probe dispatch across multiple keypresses, or want to verify each key reached the component's `handleInput`.
292
+
293
+ **How** (simplified inline example — for the full hardened script with zombie reaping, non-blocking read, and escape-sequence decoding, use `scripts/pty_probe.py` directly):
294
+
295
+ ```python
296
+ #!/usr/bin/env python3
297
+ """pty_probe.py — bulk-key + diag probe for pi-crew TUI components."""
298
+ import os, sys, time
299
+
300
+ CMD = ['pi']
301
+ ENV = {**os.environ, 'PI_CREW_BROKER_DIAG_UI': '1'}
302
+
303
+ pid, fd = pty.fork()
304
+ if pid == 0:
305
+ os.execvpe(CMD[0], CMD, ENV)
306
+ else:
307
+ time.sleep(2) # initial pi startup
308
+ keys = [
309
+ 'j', 'j', 'k', # vim nav (run dashboard)
310
+ '\x1b[A', # legacy CSI up
311
+ '\x1b[B', # legacy CSI down
312
+ '\x1bOA', # app-cursor-mode up
313
+ '\x1bOB', # app-cursor-mode down
314
+ 'q', 'q', # quit (double-tap)
315
+ ]
316
+ for k in keys:
317
+ os.write(fd, k.encode())
318
+ time.sleep(0.3)
319
+ time.sleep(1)
320
+ sys.stdout.write(os.read(fd, 65536).decode(errors='replace'))
321
+ ```
322
+
323
+ > ⚠️ The inline code above is a **teaching example**. For real use, run the bundled script (`scripts/pty_probe.py`, 161 lines) which adds zombie reaping (`_reap_child`), non-blocking read (`select.select` with 5s timeout), exec error handling, and `--keys` escape-sequence decoding:
324
+ > ```bash
325
+ > python3 scripts/pty_probe.py [--keys '\x1bOA,q,q'] [--cwd /path] [--startup-sleep 3]
326
+ > ```
327
+ > The inline code works for a quick one-off but **leaks a zombie `pi` process** on exit.
328
+
329
+ **`PI_CREW_BROKER_DIAG_UI=1`** makes `run-dashboard`'s `handleInput` write a `[PI-CREW-DIAG]` line to stderr for every keystroke. (The diag is currently wired only in `run-dashboard`, not in `settings-overlay` or other overlays — if you need diag in another overlay, port the `process.env.PI_CREW_BROKER_DIAG_UI === "1"` check from `src/ui/run-dashboard.ts:831`.) Pair with `2>&1 | tee /tmp/diag.log`.
330
+
331
+ **References**:
332
+
333
+ | What | Where |
334
+ |---|---|
335
+ | Diag env var | `PI_CREW_BROKER_DIAG_UI=1` — checked at `src/ui/run-dashboard.ts:831` |
336
+ | Reduced-noise commit | `00e8ba0 chore(broker): strip diagnostic noise from focused-field fix` — diag calls left in but no longer noisy |
337
+ | Original probe | `84944f7 test(probe): add invalidate() to control object so typecheck passes` |
338
+
339
+ ---
340
+
341
+ ## Tier 7 — Smoke team run (verifier prompt doesn't hang)
342
+
343
+ **What**: prove the verifier worker completes within `RESPONSE_TIMEOUT_MS` (300s).
344
+
345
+ **Why this is its own tier**: `test:critical` covers unit-level invariants, but the verifier LLM is a separate failure mode — it reads the verifier prompt from `src/runtime/plan-templates.ts:143, 190` (taskTemplate strings) or from `workflows/*.workflow.md:24, 30, 31` (workflow verifier sections), then decides which bash command to run. If the prompt says "Run tests" without specifying which, the LLM runs `npm test` and the worker hangs at 300s with exit 143.
346
+
347
+ **How** (from parent Pi session — `team` is a tool, not a shell command):
348
+
349
+ ```yaml
350
+ # illustrative — the actual tool takes positional + named params:
351
+ # team action='run' team='fast-fix' workflow='fast-fix' goal='...' async=false
352
+ team:
353
+ action: run # run | status | events | cancel | retry | ...
354
+ team: fast-fix # team (a role-set): default / fast-fix / implementation / parallel-research / research / review
355
+ workflow: fast-fix # workflow (a phase DAG): default / fast-fix / plan-execute / implementation / review / research / parallel-research / pipeline / chain
356
+ goal: "Smoke-verify <X>. Run `npm run test:critical && npx tsc --noEmit` once, cache output, report exact pass/fail counts + total time. Confirm verifier completes without hang (must be <300s)."
357
+ async: false # synchronous: wait for completion before returning
358
+ ```
359
+
360
+ The `team` tool is described in the agent's system prompt. Use `team action='status' <runId>` to inspect mid-run, `team action='events' <runId> <limit>` for the event log, `team action='cancel' <runId>` to abort.
361
+
362
+ **Real measured outcomes from this session**:
363
+
364
+ | Run ID | Goal | Result | Wall-clock |
365
+ |---|---|---|---|
366
+ | `team_20260722083504_cae04a2804a24d79` | smoke full-implementation | 3/4 phases, 04_verify hung on `npm test` | 572s |
367
+ | `team_20260722095143_2e58fce2ce91af19` | first smoke-fix smoke | 3/3 PASS, verifier used fast path but ran multiple LLM turns (think→bash→observe→respond) totaling ~907s cumulative | 907s |
368
+ | `team_20260722100811_9bf95bebff2b052a` | re-smoke after workflow prompt fix | 3/3 PASS, verifier used `test:critical` cache | 449s |
369
+
370
+ **References**:
371
+
372
+ | What | Where |
373
+ |---|---|
374
+ | `verificationCommand` for plan-templates | `src/runtime/plan-templates.ts:146, 193` — both templates now `npm run test:critical && npx tsc --noEmit` |
375
+ | `taskTemplate` for verifier | `src/runtime/plan-templates.ts:143, 190` — explicit "Do NOT run `npm test`" + "<2 min" budget |
376
+ | Workflow verifier prompts | `workflows/fast-fix.workflow.md:24`, `workflows/default.workflow.md:31`, `workflows/plan-execute.workflow.md:30`, `workflows/review.workflow.md:31` |
377
+ | Verifier fix commit (plan-templates) | `1cb2dca fix(verifier): use test:critical instead of test:unit to avoid worker timeout` |
378
+ | Verifier fix commit (workflows) | `d599578 fix(workflows): specify fast test:critical command in verifier prompts` |
379
+ | Watchdog constant | `src/runtime/child-pi-constants.ts:23` — `RESPONSE_TIMEOUT_MS = DEFAULT_CHILD_PI.responseTimeoutMs` |
380
+ | Cache directive | `Run FAST checks ONCE (cache output to .crew/cache/)` — anti-re-run safeguard baked into all 4 workflow verifier prompts |
381
+ | Decision doc | `docs/decisions/2026-07-22-broker-phase4-gated-on.md` §Verification (mentions the smoke run `team_20260722100811_9bf95bebff2b052a`) |
382
+
383
+ **Two known failure modes for verifier**:
384
+
385
+ 1. **Verifier LLM runs `npm test`** (full unit + integration suite, >4 min) instead of `npm run test:critical`. Symptom: worker killed with exit 143 after exactly 300s. Fix: rewrite the verifier prompt to specify the exact fast command AND include "Do NOT run `npm test` or `npm run test:unit`".
386
+ 2. **Verifier LLM improvises** with a clean-cache `npm test` run anyway. The cache directive ("cache to `.crew/cache/`", "do NOT re-run") catches this — the second worker that observes a cached log should not re-run.
387
+
388
+ ---
389
+
390
+ ## Tier 8 — Bundle-vs-session md5 sync (operational check)
391
+
392
+ **What**: the concrete md5 comparison step that *proves* Tier 4's claim. Tier 4 explains *when* you need a rebuild; Tier 8 is the *command* you run to confirm the session picked it up. Run Tier 8 as the final integrity check after Tier 3-4.
393
+
394
+ **How**:
395
+
396
+ ```bash
397
+ # Disk
398
+ md5sum dist/index.mjs
399
+
400
+ # Session (ask user to run in their pi shell tool)
401
+ # The symlink is in the CONSUMING project, not inside pi-crew:
402
+ readlink ../node_modules/pi-crew/dist/index.mjs 2>/dev/null \
403
+ || readlink "$(npm root -g)"/pi-crew/dist/index.mjs \
404
+ || md5sum "$(npm root -g)"/pi-crew/dist/index.mjs
405
+ # (the consuming project loads pi-crew via this symlink — see index.ts:5-22)
406
+ ```
407
+
408
+ If the two md5s match → session is on the latest code. If not → user must `/quit` + reopen Pi.
409
+
410
+ **References**:
411
+
412
+ | What | Where |
413
+ |---|---|
414
+ | Symlink path | `index.ts:5-22` — **the symlink lives in the CONSUMING project** (parent dir or global prefix), not inside pi-crew itself. From the repo: `readlink ../node_modules/pi-crew` (dev) or `readlink "$(npm root -g)"/pi-crew` (global). Verify with `readlink` + `npm root -g`. |
415
+ | Session load model | Same file: "dist/index.mjs (pre-built bundle) if present — DEFAULT since v0.9.17" |
416
+
417
+ ---
418
+
419
+ ## Anti-patterns (the cost is real, observed in this session)
420
+
421
+ | Anti-pattern | Cost | Where fixed | Reference |
422
+ |---|---|---|---|
423
+ | `npm test` in verifier prompt | 300s worker timeout, run = "hang" | `1cb2dca` | `src/runtime/plan-templates.ts:143, 190` + 4 workflow files |
424
+ | `npm run test:unit` for in-loop verify | >4 min, same hang | `1cb2dca` | `package.json:67` (`test:critical` script) |
425
+ | Default-off assumption in tests | Break when default flips | `612e18b` | `test/unit/crew-broker-feature-flag.test.ts:31` (`DEFAULT_BROKER.enabled === true`) |
426
+ | Test using real `loadConfig()` to mock config | Flaky when env / disk config changes | `612e18b` | `test/unit/crew-broker-server-gate.test.ts:78` (use `brokerEnv: "0"` instead of `flagOn: false`) |
427
+ | Source edit seen immediately | No, requires bundle rebuild + reload | n/a (permanent) | `index.ts:5-22` — bundle resolution rules |
428
+ | Skip disabled-path proof | `effectiveEnabled()` regression slips through | n/a (permanent) | Tier 2 above |
429
+ | `npm run test:unit` against 642 files | >4 min; mis-judges verifier runtime | n/a (permanent) | Tier 1 above |
430
+ | Skip typecheck | TS errors slip past `test:critical` (which uses `--test-timeout=30000`) | n/a (permanent) | Tier 3 above |
431
+ | Run `pi` from a stale bundle | Session shows old behavior despite src/ edits | n/a (permanent) | `scripts/check-bundle-staleness.mjs` — CI gate |
432
+ | Test by reading code | Proves nothing about runtime | n/a (permanent) | All tiers above |
433
+ | `makeFakeCtx({ flagOn: false })` without `brokerEnv: "0"` | `makeFakeCtx` deletes `PI_CREW_BROKER` env if `brokerEnv` is undefined | `612e18b` (test fix) | `test/unit/crew-broker-server-gate.test.ts:78` — pass `brokerEnv: "0"` to preserve env |
434
+ | Trust green CI on one OS | macOS/Windows regressions slip through | n/a (permanent) | `.crew/knowledge.md` — "CI runs 3 OSes ... A flake on one OS IS a real bug" |
435
+
436
+ ---
437
+
438
+ ## Failure symptoms + recovery
439
+
440
+ When a tier fails, the recovery is usually quick. Match the symptom to the cause:
441
+
442
+ | Symptom | Likely cause | Recovery |
443
+ |---|---|---|
444
+ | `test:critical` returns `# fail N>0` | Regression in touched source | Read the failing test's name + assertion; fix the source; rerun |
445
+ | `test:critical` hangs >60s | One test opened a socket/pty that didn't close | Run individual file: `node --import tsx/esm --test --test-force-exit test/unit/<file>.test.ts`; check for missing `await` or unclosed handle |
446
+ | `typecheck` fails with `TS2xxx` | TS type drift after src/ edit | Fix the type error; do not commit until exit 0 |
447
+ | `build:bundle` fails | esbuild error in `index.bundle.ts` | Run `npx esbuild --bundle src/index.bundle.ts --outfile=dist/index.mjs` for the verbose error |
448
+ | `md5sum dist/index.mjs` differs from session | Stale bundle in user's Pi | User must `/quit` + reopen Pi; new extension cold-start loads new bundle |
449
+ | Tmux probe: keys not reaching component | Wrong terminal encoding | Check `pi-tui` env; use both `\x1b[A` and `\x1bOA`; check `matchesKey` is wired in the dispatched class |
450
+ | `pty_probe.py` errors `OSError: [Errno 6] No such device` | Pty already closed | Reduce `--startup-sleep` or check `pi` actually launched |
451
+ | Smoke team: 04_verify exits with 143 | Verifier ran slow command (typically `npm test`) | Read worker transcript for actual command run; fix the verifier prompt per Tier 7 |
452
+ | Smoke team: worker times out at 300s | Either verifier command slow OR LLM thinking cap | Check `RESPONSE_TIMEOUT_MS` (300s); bump only if you verified the command itself finishes <300s |
453
+ | `stale-ctx` error in worker output | Extension ctx is stale after session replacement | This is runtime noise, not a regression; ignore. (Source: `.crew/knowledge.md` "Process Safety" notes) |
454
+ | Bundle md5 not changing after rebuild | Stale `dist/` cache or esbuild no-op | `rm -rf dist/ && npm run build:bundle`; verify new md5 |
455
+
456
+ ## Performance budget (per-tier soft limits)
457
+
458
+ | Tier | Soft limit | Hard limit | What happens over hard limit |
459
+ |---|---|---|---|
460
+ | 1 (`test:critical`) | 25s | 60s | Worker likely hung — cancel + bisect by file |
461
+ | 2 (3-path proof, total) | 75s | 180s | Same as above |
462
+ | 3 (`typecheck` + `build:bundle`) | 25s | 60s | `typecheck` regression — check imports |
463
+ | 4 (md5 sync check) | <1s | 5s | Disk/symlink issue |
464
+ | 5 (tmux spawn) | 5s | 15s | tmux server issue |
465
+ | 6 (pty probe) | 5s | 15s | `pi` not in PATH |
466
+ | 7 (smoke team) | 60s (verifier only) | 300s (worker hard limit) | Worker killed by `RESPONSE_TIMEOUT_MS` |
467
+ | 8 (final md5 sync) | <1s | 5s | Disk/symlink issue |
468
+
469
+ If a tier runs over the hard limit, **stop and investigate** — don't bump the budget silently. The budget exists precisely so regressions in test runtime (which usually means a regression in test setup/teardown) are caught early.
470
+
471
+ ---
472
+
473
+ ## Edge cases
474
+
475
+ ### macOS specifics
476
+
477
+ | Topic | Linux | macOS | Action |
478
+ |---|---|---|---|
479
+ | `md5sum` | yes | no (use `md5 -r`) | The Prerequisites table notes this. |
480
+ | `XDG_RUNTIME_DIR` | `/run/user/<uid>` | unset by default | pi-crew falls back to `os.tmpdir()` (per-user `/var/folders/.../T/`). Broker works the same. |
481
+ | Unix abstract socket | yes | no | The broker uses **concrete paths** under `$XDG_RUNTIME_DIR`, so it works on both. |
482
+ | `tmux` | usually preinstalled | `brew install tmux` | Same commands; the `pty_probe.py` works on both. |
483
+ | `/tmp/sock` | tmpfs | `/tmp` is `nodeboot`-protected (cleared on reboot but not on logout) | Same. |
484
+
485
+ ### Non-standard paths
486
+
487
+ The skill assumes pi-crew is at `${PWD}` (the directory you `cd`'d into). If you have it elsewhere:
488
+
489
+ ```bash
490
+ export PI_CREW_ROOT=/path/to/pi-crew
491
+ cd $PI_CREW_ROOT
492
+ # Now ${PWD} resolves correctly inside the skill
493
+ ```
494
+
495
+ The `cd ${PWD}` calls appear in the Prerequisites section, Tier 4, Tier 5, and the Quick reference section — all use the same path. Once you `cd` into the repo once, all commands that reference `${PWD}` resolve correctly. Tier 6 uses `scripts/pty_probe.py --cwd` instead, and Tier 8 uses `readlink` (no `cd` needed).
496
+
497
+ ### No-`tmux` fallback
498
+
499
+ If `tmux` is not installed, use Tier 6 (Python pty) instead. Tier 6 doesn't depend on tmux; it spawns `pi` directly under a pty. The trade-off: Tier 5 gives you `capture-pane` for ASCII screenshots; Tier 6 gives you per-keystroke diag output.
500
+
501
+ ### Stale `/tmp/sock` (tmux session already exists)
502
+
503
+ If a previous Tier 5 run left a `/tmp/sock` server running, `tmux new-session -S /tmp/sock` will reuse it instead of creating a fresh session. The new `pi` instance attaches to the existing session, which may have leftover state. To force a fresh session:
504
+
505
+ ```bash
506
+ tmux -S /tmp/sock kill-server 2>/dev/null # clean up
507
+ tmux -S /tmp/sock new-session -d -x 160 -y 50 -s pi "cd ${PWD} && exec pi 2>&1"
508
+ ```
509
+
510
+ ### Multiple concurrent Pi sessions
511
+
512
+ When the user has multiple Pi sessions open (e.g., main + scratch), each loads the same `dist/index.mjs`. The `md5sum` check is global — if any session loaded the old bundle, you need to restart ALL of them, not just the one you're testing in. Tier 8 covers this only for the user's "main" Pi; warn them about siblings.
513
+
514
+ ### Broker on Windows
515
+
516
+ `broker.enabled=true` is silently no-op on native Windows (no unix-domain socket). Users on WSL1/2 get full broker behavior. Don't waste time running Tier 7 smoke tests on native Windows — the verifier will run fine but the broker won't actually do anything. Use `PI_CREW_BROKER=0` to skip the broker entirely.
517
+
518
+ ---
519
+
520
+ ## Cross-skill notes
521
+
522
+ This skill overlaps with these built-in/project skills. Pick the right one:
523
+
524
+ | Skill | When to use instead |
525
+ |---|---|
526
+ | `test` (built-in) | When you want generic test execution guidance (not pi-crew-specific) |
527
+ | `lint` (built-in) | When you only need lint + format (Tier 3's typecheck replaces it for TypeScript) |
528
+ | `verify-before-complete` (project) | When claiming "done" without specific tier discipline; this skill's Tier 1-8 are stricter and pi-crew-specific |
529
+ | `code-optimizer` (built-in) | When auditing for perf, not for verification |
530
+ | `iterative-audit` (project) | When doing a multi-round codebase audit; this skill's "review kỹ" rounds are a different beast — they're verification, not audit |
531
+ | `review` / `security-review` (built-in) | When reviewing someone else's PR diff; this skill is for verifying YOUR OWN changes |
532
+
533
+ The "skill stack" for a typical pi-crew change:
534
+
535
+ ```
536
+ 1. Edit src/
537
+ 2. tier 1 (test:critical) ← this skill
538
+ 3. tier 2 (3-path proof) ← this skill, if broker change
539
+ 4. tier 3 (typecheck + bundle) ← this skill
540
+ 5. tier 5/6 (live TUI) ← this skill, if ui change
541
+ 6. tier 7 (smoke team) ← this skill, if plan/workflow change
542
+ 7. commit + push
543
+ 8. verify-before-complete ← make the "done" claim with evidence
544
+ ```
545
+
546
+ ---
547
+
548
+ ## Maintenance
549
+
550
+ The skill mentions specific commits, line numbers, and version pins. As the code evolves, these will drift. Maintenance playbook:
551
+
552
+ | What | When | How |
553
+ |---|---|---|
554
+ | Verify line refs after each `src/` commit | Every commit touching the cited file | `git log -p -- src/extension/registration/lifecycle-handlers.ts \| grep effectiveEnabled` — if line moved, update the skill |
555
+ | Verify commit hashes still exist | Quarterly or before major edits | `git log --oneline -1 <hash>` — if gone, find the equivalent newer commit |
556
+ | Verify version pins (v0.9.46, etc.) | Each release | `git log --oneline -- src/ui/run-dashboard.ts \| head -5` — find when diag was wired |
557
+ | Verify `test:critical` still has 14 files | Each `src/runtime/crew-broker*.ts` edit | `cat package.json \| grep test:critical` — adjust the file list |
558
+ | Verify Tier 7 verifier prompts still say `test:critical` | Each workflow file edit | `grep "Run FAST checks" workflows/*.workflow.md` |
559
+
560
+ The skill does NOT need to be updated for every commit — only when the cited lines/files move. Consider it a "living reference" not a "live spec".
561
+
562
+ ---
563
+
564
+ ## Quick reference — exact commands
565
+
566
+ ```bash
567
+ # Tier 1 (critical unit, ~21s, 97 tests)
568
+ npm run test:critical
569
+ # Tier 2 (3-path proof, broker changes only)
570
+ PI_CREW_BROKER=0 npm run test:critical
571
+ PI_CREW_BROKER=1 npm run test:critical
572
+ # Tier 3 (compile + bundle)
573
+ npm run typecheck
574
+ npm run build:bundle
575
+ md5sum dist/index.mjs
576
+ # Tier 4 (sync check — symlink is in the CONSUMING project)
577
+ readlink ../node_modules/pi-crew # dev: → ../pi-crew
578
+ readlink "$(npm root -g)"/pi-crew # global install
579
+ # Tier 5 (tmux probe)
580
+ tmux -S /tmp/sock new-session -d -x 160 -y 50 -s pi \
581
+ "cd ${PWD} && PI_CREW_BROKER_DIAG_UI=1 exec pi 2>&1"
582
+ tmux send-keys -t pi '<key>' ; sleep 0.5
583
+ tmux capture-pane -t pi -p
584
+ # Tier 6 (pty probe)
585
+ python3 scripts/pty_probe.py 2>&1 | tee /tmp/diag.log
586
+ # Tier 7 (smoke team)
587
+ # from parent Pi session only — uses the `team` tool, not shell
588
+ # Tier 8 (final md5 sync — compare disk vs loaded bundle)
589
+ md5sum dist/index.mjs
590
+ md5sum "$(npm root -g)"/pi-crew/dist/index.mjs 2>/dev/null \
591
+ || md5sum ../node_modules/pi-crew/dist/index.mjs
592
+ ```
593
+
594
+ ---
595
+
596
+ ## Done-criteria checklist
597
+
598
+ Before claiming "tested":
599
+
600
+ - [ ] Tier 1: `test:critical` fresh-run, 97/97 pass, ~21s
601
+ - [ ] Tier 2: 3-path proof all pass — **required if you touched `src/config/defaults.ts` or `src/extension/registration/lifecycle-handlers.ts`**
602
+ - [ ] Tier 3: `npm run typecheck` exit 0, `npm run build:bundle` exit 0
603
+ - [ ] Tier 4: bundle md5 matches what the session loaded (or user has `/quit`-ed + reopened)
604
+ - [ ] Tier 5/6: live TUI smoke for any `src/ui/` change — keystroke reached `handleInput`
605
+ - [ ] Tier 7: smoke team run for any `src/runtime/plan-templates.ts` or `workflows/*.workflow.md` change — completed, no hang, verifier output under 60s
606
+ - [ ] Tier 8: final md5 sync check passed
607
+
608
+ If any required item is unchecked, the answer to "is it tested?" is **no**.
609
+
610
+ ---
611
+
612
+ ## File-anchored references (full index)
613
+
614
+ Decision docs:
615
+ - `docs/decisions/2026-07-21-broker-phase4-default-on.md` — interim default-off (SUPERSEDED)
616
+ - `docs/decisions/2026-07-22-broker-phase4-gated-on.md` — default-on flip + risk + monitoring + rollback
617
+ - `docs/decisions/2026-07-21-broker-windows-perms.md` — Windows named-pipe perms + Phase-4 update note
618
+
619
+ Source files (critical paths):
620
+ - `src/config/defaults.ts:155-187` — `DEFAULT_BROKER` + `resolveBrokerEnvOverride`
621
+ - `src/extension/registration/lifecycle-handlers.ts:819-833` — `effectiveEnabled()` (precedence)
622
+ - `src/runtime/child-pi-constants.ts:23` — `RESPONSE_TIMEOUT_MS = 300_000`
623
+ - `src/runtime/plan-templates.ts:143, 146, 190, 193` — verifier `taskTemplate` + `verificationCommand`
624
+ - `src/runtime/crew-broker.ts` — broker server (per-connection gate, NDJSON framing)
625
+ - `src/runtime/crew-broker-client.ts` — client (`isEventFrame()` distinguishes event vs response frames)
626
+ - `src/runtime/crew-broker-tokens.ts` — `BrokerTokenRegistry` with `timingSafeEqual`
627
+ - `src/runtime/broker-issuer.ts` — per-run broker issuer (env injection at spawn)
628
+ - `src/runtime/crew-broker-child.ts` — child-side broker client wiring
629
+ - `src/ui/key-utils.ts:37-42` — `keyOf()` using pi-tui `matchesKey()`
630
+ - `src/ui/keybinding-map.ts` — dispatch using `matchesKey()` (commit `f05a10d`)
631
+
632
+ Test files (the 14 in `test:critical`):
633
+ - `test/unit/crew-broker-{handshake,stale-socket,feature-flag,server-gate,client-fallback,mailbox-observer,close-during-reconnect,steer-dedup,symlink-steering}.test.ts`
634
+ - `test/unit/keybinding-map.parity.test.ts`
635
+ - `test/unit/pi-tui-dispatch-probe.test.ts`
636
+ - `test/unit/session-utils-extract.test.ts`
637
+ - `test/unit/config-schema-sync.test.ts`
638
+ - `test/unit/child-pi-env-spread.test.ts`
639
+
640
+ Integration tests (Tier 1 covers none — these are for full E2E):
641
+ - `test/integration/crew-broker-msg.test.ts` — 5 tests (Phases 1)
642
+ - `test/integration/crew-broker-phase2-3.test.ts` — events.subscribe + task.waitStatus + steer.push + escalate
643
+
644
+ Workflow files:
645
+ - `workflows/fast-fix.workflow.md:24` — verifier prompt (commit `d599578`)
646
+ - `workflows/default.workflow.md:31` — verifier prompt
647
+ - `workflows/plan-execute.workflow.md:30` — verifier prompt
648
+ - `workflows/review.workflow.md:31` — verifier prompt
649
+
650
+ Commits (chronological, the patterns they introduced):
651
+ - `1cb2dca` — `test:critical` script + plan-templates verifier fix
652
+ - `d599578` — 4 workflow verifier prompt fixes
653
+ - `612e18b` — Phase 4 default-on flip (code + decision doc)
654
+ - `4186284` — mark default-off doc SUPERSEDED + index update
655
+
656
+ Real team runs (Tier 7 outcomes):
657
+ - `team_20260722083504_cae04a2804a24d79` — full-implementation, 3/4 phases done, 04_verify hung (root cause investigation)
658
+ - `team_20260722095143_2e58fce2ce91af19` — first fast-fix smoke, 3/3 PASS (after `test:critical` introduced)
659
+ - `team_20260722100811_9bf95bebff2b052a` — final fast-fix smoke, 3/3 PASS, verifier used cached output (449s wall-clock)
@@ -120,7 +120,7 @@ const securityEventLog: SecurityEvent[] = [];
120
120
 
121
121
  /**
122
122
  * Log a security event for audit purposes.
123
- * TODO: In production, integrate with project's logging infrastructure
123
+ * TODO(security-siem): tracked — integrate with project's logging infrastructure
124
124
  * (e.g., send to SIEM, log aggregator, or security webhook).
125
125
  */
126
126
  function logSecurityEvent(event: SecurityEvent): void {