frizz 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -20
- package/dist/claude-agent-broker.js +72 -27
- package/dist/dev-child.js +8302 -4628
- package/dist/frizz.js +1302 -701
- package/package.json +10 -4
- package/runtime/cc-worker/.claude-plugin/plugin.json +1 -1
- package/runtime/cc-worker/DECISIONS.md +81 -4
- package/runtime/cc-worker/bin/frizz-mcp.mjs +472 -58
- package/runtime/cc-worker/hooks/agent-dispatch.mjs +1 -1
- package/runtime/cc-worker/hooks/deny-plan.mjs +1 -1
- package/runtime/cc-worker/hooks/hooks.json +0 -7
- package/runtime/cc-worker/hooks/precompact-instructions.mjs +39 -28
- package/runtime/cc-worker/hooks/scratchpad.mjs +142 -169
- package/runtime/cc-worker/hooks/session-seed.mjs +16 -15
- package/runtime/cc-worker/skills/waits/SKILL.md +2 -2
- package/web-dist/apple-touch-icon.png +0 -0
- package/web-dist/assets/{TerminalPane-ROKHp1ib.js → TerminalPane-sd3XXjx5.js} +2 -2
- package/web-dist/assets/{abnfDiagram-VRR7QNED-DcpdhBs3.js → abnfDiagram-VRR7QNED-Bry_FIqg.js} +1 -1
- package/web-dist/assets/architecture-TIHT7OUA-CoMJQ5Ya.js +1 -0
- package/web-dist/assets/{architectureDiagram-ZJ3FMSHR-CUAKf0mn.js → architectureDiagram-ZJ3FMSHR-Baok4OeI.js} +1 -1
- package/web-dist/assets/{blockDiagram-677ZJIJ3-BPwpJIzx.js → blockDiagram-677ZJIJ3-BgHIO9lI.js} +1 -1
- package/web-dist/assets/{c4Diagram-LMCZKHZV-1lptuHzZ.js → c4Diagram-LMCZKHZV-rfj9Z17Z.js} +1 -1
- package/web-dist/assets/channel-D6uS1y7I.js +1 -0
- package/web-dist/assets/{chunk-32BRIVSS-CFR9AKjY.js → chunk-32BRIVSS-BwaHfK1Y.js} +1 -1
- package/web-dist/assets/{chunk-52WLFC77-CM9uct7m.js → chunk-52WLFC77-BmdQDZOA.js} +1 -1
- package/web-dist/assets/{chunk-C7G6YPKG-DiveJARw.js → chunk-C7G6YPKG-ByQFjoZe.js} +1 -1
- package/web-dist/assets/{chunk-EX3LRPZG-BE1CBw8F.js → chunk-EX3LRPZG-DQFudBKr.js} +1 -1
- package/web-dist/assets/{chunk-FWX5IMBZ-DL42uXiO.js → chunk-FWX5IMBZ-OW9qEPiq.js} +2 -2
- package/web-dist/assets/{chunk-HOUHSVGY-DPhJWgDw.js → chunk-HOUHSVGY-BdKLF8nO.js} +1 -1
- package/web-dist/assets/{chunk-ICXQ74PX-CwYy-6AP.js → chunk-ICXQ74PX-C1roqKR1.js} +1 -1
- package/web-dist/assets/{chunk-MOJQB5TN-Ds5I9wxq.js → chunk-MOJQB5TN-Cx0u1ZDK.js} +1 -1
- package/web-dist/assets/{chunk-OGEWGWER-DHiZJwQD.js → chunk-OGEWGWER-rxv9xtNR.js} +1 -1
- package/web-dist/assets/{chunk-PUDLZKDR-B-eyQTsF.js → chunk-PUDLZKDR-BXWPPPPi.js} +1 -1
- package/web-dist/assets/{chunk-Q4XR5HBZ-DK7dB3Ti.js → chunk-Q4XR5HBZ-CpqbD101.js} +1 -1
- package/web-dist/assets/{chunk-V7JOEXUC-Dn59m74L.js → chunk-V7JOEXUC-CP8_3qVt.js} +1 -1
- package/web-dist/assets/{chunk-VAUOI2AC-DK7x36hd.js → chunk-VAUOI2AC-Dl7xahpk.js} +1 -1
- package/web-dist/assets/{chunk-VR4S4FIN-D7-CI3Yl.js → chunk-VR4S4FIN-BKtMX64l.js} +1 -1
- package/web-dist/assets/{chunk-WYO6CB5R-B3l-mLCs.js → chunk-WYO6CB5R-DmLOdJcu.js} +1 -1
- package/web-dist/assets/{chunk-ZGVPDNZ5-Dobxlxie.js → chunk-ZGVPDNZ5-5wN7eXvS.js} +1 -1
- package/web-dist/assets/classDiagram-OUVF2IWQ-BW5YiBf4.js +1 -0
- package/web-dist/assets/classDiagram-v2-EOCWNBFH-BW5YiBf4.js +1 -0
- package/web-dist/assets/{cynefin-VYW2F7L2-Dh7RuEUJ.js → cynefin-VYW2F7L2-DUYahBXb.js} +1 -1
- package/web-dist/assets/{cynefinDiagram-TSTJHNR4-ClPi2mZZ.js → cynefinDiagram-TSTJHNR4-Rm0i30nE.js} +1 -1
- package/web-dist/assets/{dagre-VKFMJZFB-52_WP1QV.js → dagre-VKFMJZFB-3uhsB-W3.js} +1 -1
- package/web-dist/assets/{diagram-FQU43EPY-D_1zVsTL.js → diagram-FQU43EPY-BUvflNRQ.js} +1 -1
- package/web-dist/assets/{diagram-G47NLZAW-CdZxuGUy.js → diagram-G47NLZAW-DUluIZel.js} +1 -1
- package/web-dist/assets/{diagram-NH7WQ7WH-C8pSFu0P.js → diagram-NH7WQ7WH-BBrwsbed.js} +1 -1
- package/web-dist/assets/{diagram-OA4YK3LP-C5bjZLre.js → diagram-OA4YK3LP-DWnTQBw4.js} +1 -1
- package/web-dist/assets/{diagram-WEI45ONY-Bxzhiuzn.js → diagram-WEI45ONY-BjEavoC0.js} +1 -1
- package/web-dist/assets/{ebnfDiagram-CCIWWBDH-g-Z0J2wP.js → ebnfDiagram-CCIWWBDH-CW4xZAZH.js} +1 -1
- package/web-dist/assets/{erDiagram-Q63AITRT-DVNkgIHp.js → erDiagram-Q63AITRT-BemQ1R4B.js} +1 -1
- package/web-dist/assets/eventmodeling-45OFAUF4-BF5KB7ja.js +1 -0
- package/web-dist/assets/flowDiagram-23GEKE2U-BYo4Eytx.js +1 -0
- package/web-dist/assets/{ganttDiagram-NO4QXBWP-_71pQYEK.js → ganttDiagram-NO4QXBWP-lDdG5I_z.js} +1 -1
- package/web-dist/assets/{gitGraph-TEB2WS4Q-ChIZiGZS.js → gitGraph-TEB2WS4Q-Bw87a_Se.js} +1 -1
- package/web-dist/assets/{gitGraphDiagram-IHSO6WYX-DCHAFI0l.js → gitGraphDiagram-IHSO6WYX-B762r0yi.js} +1 -1
- package/web-dist/assets/index-B3QArXu2.css +1 -0
- package/web-dist/assets/index-CAapfhMv.js +365 -0
- package/web-dist/assets/{info-DKCQHKI2-BW-n_T1j.js → info-DKCQHKI2-DGOv6Gx0.js} +1 -1
- package/web-dist/assets/{infoDiagram-FWYZ7A6U-CgDYsKi9.js → infoDiagram-FWYZ7A6U-DL92kAzf.js} +1 -1
- package/web-dist/assets/{ishikawaDiagram-FXEZZL3T-ClzGNt9N.js → ishikawaDiagram-FXEZZL3T-CFYd4OtU.js} +1 -1
- package/web-dist/assets/{journeyDiagram-5HDEW3XC-DSCQxkHC.js → journeyDiagram-5HDEW3XC-QHuYL5zq.js} +1 -1
- package/web-dist/assets/{kanban-definition-HUTT4EX6-CdrdX9N8.js → kanban-definition-HUTT4EX6-DwmMluQ8.js} +1 -1
- package/web-dist/assets/{line-ha38Dc-1.js → line-Je_PFFHm.js} +1 -1
- package/web-dist/assets/{mermaid-parser.core-Z4uMcpip.js → mermaid-parser.core-BeiGWsn3.js} +3 -3
- package/web-dist/assets/{mermaid.core-iZRq3hbu.js → mermaid.core-BP8JrW48.js} +3 -3
- package/web-dist/assets/{mindmap-definition-LN4V7U3C-DmhInJO_.js → mindmap-definition-LN4V7U3C-BsmjCIrM.js} +1 -1
- package/web-dist/assets/{packet-7NZHBO7P-DBPB36Kl.js → packet-7NZHBO7P-D6ekYYGJ.js} +1 -1
- package/web-dist/assets/{pegDiagram-2B236MQR-CAH3ljfj.js → pegDiagram-2B236MQR-CQc2Kt0u.js} +1 -1
- package/web-dist/assets/{pie-RZYD4A2V-_h_eX4Ca.js → pie-RZYD4A2V-C_45fO8J.js} +1 -1
- package/web-dist/assets/{pieDiagram-ENE6RG2P-DFBPus8j.js → pieDiagram-ENE6RG2P-Dg5z6iWt.js} +1 -1
- package/web-dist/assets/{quadrantDiagram-ABIIQ3AL-DMvOCjt8.js → quadrantDiagram-ABIIQ3AL-c7gZxuCX.js} +1 -1
- package/web-dist/assets/{radar-I7S5WNFK-2EzoPHEZ.js → radar-I7S5WNFK-D80XNJQq.js} +1 -1
- package/web-dist/assets/{railroad-3IZDKUUU-BPJnn-hm.js → railroad-3IZDKUUU-Brz7eZQ0.js} +1 -1
- package/web-dist/assets/railroad-abnf-AHOZXSZD-D44pcGhJ.js +1 -0
- package/web-dist/assets/railroad-ebnf-EBAXGLYW-CiWVH5SP.js +1 -0
- package/web-dist/assets/railroad-peg-LSFZ7HO6-Cy1B6-pN.js +1 -0
- package/web-dist/assets/{railroadDiagram-RFXS5EU6-DKq5FagA.js → railroadDiagram-RFXS5EU6-Db9K7bty.js} +1 -1
- package/web-dist/assets/{requirementDiagram-TGXJPOKE-BJ5tGazp.js → requirementDiagram-TGXJPOKE-m1ipMrDe.js} +1 -1
- package/web-dist/assets/{sankeyDiagram-HTMAVEWB-XSJjcBhX.js → sankeyDiagram-HTMAVEWB-4Zz9ry0k.js} +1 -1
- package/web-dist/assets/{sequenceDiagram-DBY2YBRQ-CBb8emSe.js → sequenceDiagram-DBY2YBRQ-CZeGHw4X.js} +1 -1
- package/web-dist/assets/{stateDiagram-2N3HPSRC-DDfRW94V.js → stateDiagram-2N3HPSRC-rLUVs1Pg.js} +1 -1
- package/web-dist/assets/stateDiagram-v2-6OUMAXLB-w7QDFEvx.js +1 -0
- package/web-dist/assets/{swimlanes-5IMT3BWC-DvRYbkZi.js → swimlanes-5IMT3BWC-BAeBdscz.js} +1 -1
- package/web-dist/assets/swimlanesDiagram-G3AALYLV-UhdgWpNZ.js +8 -0
- package/web-dist/assets/{timeline-definition-FHXFAJF6-BNUa_DwI.js → timeline-definition-FHXFAJF6-DL0eWMF-.js} +1 -1
- package/web-dist/assets/{treeView-QDETBFTQ-I6-IW6nJ.js → treeView-QDETBFTQ-gEtzLMA8.js} +1 -1
- package/web-dist/assets/{treemap-6X3UGDF4-CWWmEUYJ.js → treemap-6X3UGDF4-CCym1Gz6.js} +1 -1
- package/web-dist/assets/{vennDiagram-L72KCM5P-DTDrPGLk.js → vennDiagram-L72KCM5P-cF97-nb5.js} +1 -1
- package/web-dist/assets/{wardley-OPB4EBWU-CNsdgXXA.js → wardley-OPB4EBWU-DQJHCGbM.js} +1 -1
- package/web-dist/assets/{wardleyDiagram-EHGQE667-YE0tq3Kh.js → wardleyDiagram-EHGQE667-0CnHzXLX.js} +1 -1
- package/web-dist/assets/{xychartDiagram-FW5EYKEG-D0ofMX8C.js → xychartDiagram-FW5EYKEG-9YDOp4m5.js} +1 -1
- package/web-dist/favicon-16.png +0 -0
- package/web-dist/favicon-32.png +0 -0
- package/web-dist/favicon.svg +17 -61
- package/web-dist/icon-192.png +0 -0
- package/web-dist/icon-512.png +0 -0
- package/web-dist/icon-maskable-512.png +0 -0
- package/web-dist/index.html +19 -8
- package/web-dist/manifest.webmanifest +3 -3
- package/runtime/cc-worker/hooks/scratchpad-stop.mjs +0 -125
- package/runtime/cc-worker/skills/handoff/SKILL.md +0 -209
- package/web-dist/assets/architecture-TIHT7OUA-CAviNivx.js +0 -1
- package/web-dist/assets/channel-CqKDIFQF.js +0 -1
- package/web-dist/assets/classDiagram-OUVF2IWQ-B_-6iXYY.js +0 -1
- package/web-dist/assets/classDiagram-v2-EOCWNBFH-B_-6iXYY.js +0 -1
- package/web-dist/assets/eventmodeling-45OFAUF4-MpmeH5YZ.js +0 -1
- package/web-dist/assets/flowDiagram-23GEKE2U-D-QgjjhF.js +0 -1
- package/web-dist/assets/index-w4v-GZEc.js +0 -358
- package/web-dist/assets/index-zyi22LPz.css +0 -1
- package/web-dist/assets/railroad-abnf-AHOZXSZD-YeUoiySk.js +0 -1
- package/web-dist/assets/railroad-ebnf-EBAXGLYW-Ddw1SuGG.js +0 -1
- package/web-dist/assets/railroad-peg-LSFZ7HO6-Dd8BOGeW.js +0 -1
- package/web-dist/assets/stateDiagram-v2-6OUMAXLB-hc41W5Lx.js +0 -1
- package/web-dist/assets/swimlanesDiagram-G3AALYLV-BZyGdgSG.js +0 -8
|
@@ -1,49 +1,44 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// @ts-check
|
|
3
|
-
//
|
|
4
|
-
// (`.frizz/threads/<sid
|
|
5
|
-
// node (zero deps, max Node compat), mirroring the other hooks in this plugin.
|
|
3
|
+
// SCRATCH-DIRECTORY hook (frizz-worker) — keeps a worker aware of the per-thread scratch directory
|
|
4
|
+
// (`.frizz/threads/<sid>/`) it may use, and re-orients it on that directory when its context is lost.
|
|
5
|
+
// Run directly with node (zero deps, max Node compat), mirroring the other hooks in this plugin.
|
|
6
6
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
7
|
+
// WHAT THIS USED TO BE, AND WHY IT IS NOT ANY MORE. Until 2026-08-06 frizz provisioned ONE canonical
|
|
8
|
+
// `scratch.md` per thread and this hook spliced its HEAD into the context window after every compaction,
|
|
9
|
+
// on the argument that a bare "remember to read your scratchpad" routes recovery through a decision the
|
|
10
|
+
// model can skip. That argument was sound and the mechanism still lost: it made a maintained file the
|
|
11
|
+
// price of admission for every worker, it needed a merge-only contract per backend to keep sub-agents
|
|
12
|
+
// from clobbering it, and the injection was invisible to the operator, who could neither see nor change
|
|
13
|
+
// what their worker would be told.
|
|
12
14
|
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
15
|
+
// The maintainer's replacement (chosen deliberately over keeping a canonical doc): the thread gets a
|
|
16
|
+
// free-form scratch DIRECTORY, and compaction recovery moves to `mcp__frizz__recurring_prompt`'s
|
|
17
|
+
// post_compaction trigger — the worker writes whatever doc it likes and LINKS it in a prompt frizz
|
|
18
|
+
// re-sends when the context is summarized away. Durable in SQLite, visible in the thread footer,
|
|
19
|
+
// editable by the human. This hook's job is therefore reduced to two honest things:
|
|
18
20
|
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
// `subagent-start` mode preserves collaborative writes while requiring merge-only, scoped edits. It
|
|
25
|
-
// also carries the codex half of the default-off nesting rule (2026-08-04): a native child does the
|
|
26
|
-
// work itself and does not `spawn_agent` a layer of its own unless its task said to. SubagentStart is
|
|
27
|
-
// the only structural seam that reaches a native child, the way agent-dispatch.mjs's epilogue is for
|
|
28
|
-
// Claude — the same rule, stated once per backend.
|
|
21
|
+
// 1. TELL the worker the directory exists, and that the arming is what makes anything in it come back.
|
|
22
|
+
// 2. On compact/resume, say what is IN the directory — a listing, not the content. That is the
|
|
23
|
+
// degradation the maintainer accepted when choosing this over a canonical doc, and it is stated
|
|
24
|
+
// here rather than quietly re-implemented as an injection: a worker that never armed the trigger
|
|
25
|
+
// gets a pointer it may skip. Naming the files it already wrote is the most a pointer can do.
|
|
29
26
|
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
// session start.
|
|
27
|
+
// CODEX CHILD EPILOGUE — native Codex sub-agents inherit the root conversation's system/user
|
|
28
|
+
// instructions even with `fork_turns:"none"`. The `subagent-start` mode is what tells such a child to
|
|
29
|
+
// write its OWN file rather than treating a document it did not create as its own. It also carries the
|
|
30
|
+
// codex half of the default-off nesting rule (2026-08-04): a native child does the work itself and does
|
|
31
|
+
// not `spawn_agent` a layer of its own unless its task said to. SubagentStart is the only structural
|
|
32
|
+
// seam that reaches a native child, the way agent-dispatch.mjs's epilogue is for Claude.
|
|
37
33
|
//
|
|
38
|
-
// THE WRITE
|
|
34
|
+
// THE WRITE-SIDE NUDGE — two channels:
|
|
39
35
|
// UserPromptSubmit — the turn boundary.
|
|
40
36
|
// PostToolUse — MID-TURN, and this is the one that matters. A frizz worker runs enormous
|
|
41
37
|
// autonomous turns (dozens of tool calls between human prompts), so a
|
|
42
|
-
// turn-boundary-only nudge can miss an entire session's worth of work
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
// to per-tool-call firing does NOT multiply the number of nudges.
|
|
38
|
+
// turn-boundary-only nudge can miss an entire session's worth of work. PostToolUse
|
|
39
|
+
// additionalContext was verified live against cli 2.1.220: a real session quoted a
|
|
40
|
+
// sentinel injected after a Bash call. Both channels share one state file, so the
|
|
41
|
+
// interval is global — firing per tool call does NOT multiply the nudges.
|
|
47
42
|
//
|
|
48
43
|
// NO HOOK FIRES ON CONTEXT PRESSURE — measured, not assumed. Claude Code 2.1.220 exposes 31 hook
|
|
49
44
|
// events and not one of them signals an approaching context limit; no hook input carries a token
|
|
@@ -59,22 +54,19 @@
|
|
|
59
54
|
// tried and REMOVED on 2026-07-02 (maintainer's call): the block-until-file-edited nag forced even
|
|
60
55
|
// trivial workers into Read/Edit dances that render as noise in the chat UI. This nudges; it never
|
|
61
56
|
// blocks.
|
|
62
|
-
import { readFileSync, writeFileSync, mkdirSync, statSync, openSync, readSync, closeSync } from 'node:fs';
|
|
57
|
+
import { readFileSync, writeFileSync, mkdirSync, statSync, readdirSync, openSync, readSync, closeSync } from 'node:fs';
|
|
63
58
|
import { join } from 'node:path';
|
|
64
59
|
import { currentSessionId } from '../scripts/frizz/config.mjs';
|
|
65
60
|
|
|
66
|
-
|
|
61
|
+
/** How many filenames a listing names before it summarizes the rest. A worker with 200 scratch files
|
|
62
|
+
* needs to know that, not to be handed 200 lines of them. */
|
|
63
|
+
const MAX_LISTED_FILES = intFromEnv('FRIZZ_SCRATCH_MAX_LISTED', 40);
|
|
67
64
|
|
|
68
|
-
/**
|
|
69
|
-
* a
|
|
70
|
-
*
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
/** Context-token growth since the last scratchpad write that marks it stale. 60k is ~a third of a
|
|
74
|
-
* 200k window and ~6% of a 1M one: frequent enough that the pad is never many turns behind, rare
|
|
75
|
-
* enough not to be chatter. Also the first-write trigger — an untouched template counts as
|
|
76
|
-
* unwritten, so the baseline is zero and the first nudge lands once a session has accumulated 60k
|
|
77
|
-
* tokens actually worth persisting. */
|
|
65
|
+
/** Context-token growth since the last scratch write that marks the directory stale. 60k is ~a third of
|
|
66
|
+
* a 200k window and ~6% of a 1M one: frequent enough that a long effort is reminded while there is
|
|
67
|
+
* still something to record, rare enough not to be chatter. Also the first-write trigger — an EMPTY
|
|
68
|
+
* directory has no clock of its own, so the baseline is zero and the first nudge lands once a session
|
|
69
|
+
* has accumulated 60k tokens actually worth persisting. */
|
|
78
70
|
const STALE_TOKENS = intFromEnv('FRIZZ_SCRATCHPAD_STALE_TOKENS', 60000);
|
|
79
71
|
|
|
80
72
|
/** @param {string} name @param {number} fallback */
|
|
@@ -140,8 +132,7 @@ try {
|
|
|
140
132
|
if (!sid) process.exit(0);
|
|
141
133
|
|
|
142
134
|
const threadDir = join(projectDir, '.frizz', 'threads', sid);
|
|
143
|
-
const relPath = '.frizz/threads/' + sid + '/'
|
|
144
|
-
const absPath = join(threadDir, SCRATCH_FILE);
|
|
135
|
+
const relPath = '.frizz/threads/' + sid + '/';
|
|
145
136
|
|
|
146
137
|
// ── mode: subagent-start (Codex child-only merge epilogue) ───────────────────────────────────────
|
|
147
138
|
if (mode === 'subagent-start') {
|
|
@@ -149,18 +140,16 @@ if (mode === 'subagent-start') {
|
|
|
149
140
|
hookSpecificOutput: {
|
|
150
141
|
hookEventName: 'SubagentStart',
|
|
151
142
|
additionalContext:
|
|
152
|
-
'⟦
|
|
153
|
-
'the top-level `/root` worker
|
|
154
|
-
'
|
|
155
|
-
'
|
|
156
|
-
'
|
|
157
|
-
'project deliverable or source edit:
|
|
158
|
-
'phrases such as “write only <path>” or “do not modify the repo
|
|
159
|
-
'as unauthorized or
|
|
160
|
-
'
|
|
161
|
-
'
|
|
162
|
-
'location alone neither permits nor forbids editing. If the file is absent or you cannot ' +
|
|
163
|
-
'merge safely, return your state to the parent without writing.\n' +
|
|
143
|
+
'⟦scratch directory — write your OWN file⟧ `' + relPath + '` is a scratch directory shared with ' +
|
|
144
|
+
'the top-level `/root` worker and any sibling agents. If you want notes on disk, create your ' +
|
|
145
|
+
'own file in it — never edit, replace or delete a file another agent wrote, and never treat a ' +
|
|
146
|
+
'document you did not create as yours to rewrite. One file per writer means there is nothing ' +
|
|
147
|
+
'to merge and nothing to clobber. Writing your own file here is Frizz coordination state, not ' +
|
|
148
|
+
'a project deliverable or source edit: it remains allowed when a delegated task limits your ' +
|
|
149
|
+
'deliverable paths with phrases such as “write only <path>” or “do not modify the repo”, and ' +
|
|
150
|
+
'must never be classified as unauthorized or rolled back. Other project files, including files ' +
|
|
151
|
+
'at the repository root, remain governed by your delegated authority; their location alone ' +
|
|
152
|
+
'neither permits nor forbids editing.\n' +
|
|
164
153
|
'⟦no fan-out of your own⟧ Do the work yourself: do not spawn agents of your own ' +
|
|
165
154
|
'(`spawn_agent`) unless the task you were given explicitly tells you to. You are already one ' +
|
|
166
155
|
'prong of the root worker’s fan-out, and another layer below you splits the context you were ' +
|
|
@@ -179,54 +168,41 @@ try {
|
|
|
179
168
|
/* a read-only or racing FS just means the agent's Write creates it instead */
|
|
180
169
|
}
|
|
181
170
|
|
|
182
|
-
/**
|
|
183
|
-
|
|
171
|
+
/** The worker's own files in the scratch directory, newest first — name, size, and how long ago it was
|
|
172
|
+
* touched. Dotfiles are excluded: frizz keeps its own per-thread bookkeeping in here
|
|
173
|
+
* (`.scratchpad-state.json`), and reporting that back to the worker as its own notes would be a lie.
|
|
174
|
+
* @returns {{ name: string, size: number, mtimeMs: number }[]} */
|
|
175
|
+
function listScratch() {
|
|
176
|
+
let names;
|
|
184
177
|
try {
|
|
185
|
-
|
|
186
|
-
return raw.trim() ? raw : null;
|
|
178
|
+
names = readdirSync(threadDir);
|
|
187
179
|
} catch {
|
|
188
|
-
return
|
|
180
|
+
return [];
|
|
189
181
|
}
|
|
182
|
+
const out = [];
|
|
183
|
+
for (const name of names) {
|
|
184
|
+
if (name.startsWith('.')) continue;
|
|
185
|
+
try {
|
|
186
|
+
const st = statSync(join(threadDir, name));
|
|
187
|
+
if (!st.isFile()) continue;
|
|
188
|
+
out.push({ name, size: st.size, mtimeMs: st.mtimeMs });
|
|
189
|
+
} catch {
|
|
190
|
+
// vanished between the listing and the stat — simply not listed
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return out.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
190
194
|
}
|
|
191
195
|
|
|
192
|
-
/**
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (!text) return 0;
|
|
200
|
-
return text
|
|
201
|
-
.split('\n')
|
|
202
|
-
.filter((line) => {
|
|
203
|
-
const t = line.trim();
|
|
204
|
-
if (!t) return false;
|
|
205
|
-
if (t.startsWith('#')) return false; // headings
|
|
206
|
-
if (/^[-*]\s*\[\s*\]\s*$/.test(t)) return false; // an empty task box
|
|
207
|
-
// The visible legend/collaboration guide provisioned in every new pad. They teach the shared
|
|
208
|
-
// editing contract but are not evidence that the worker has recorded any task state yet.
|
|
209
|
-
if (/^>\s*(?:Status legend:|Collaboration:)/.test(t)) return false;
|
|
210
|
-
// The provisioned orientation line. Matched on the CONCEPT rather than a leading word, because
|
|
211
|
-
// the wording has changed once already and pads written under the old shape are still on disk —
|
|
212
|
-
// anchoring on a prefix silently reclassified a template as "written", which made an empty pad
|
|
213
|
-
// skip its re-grounding and made the summarizer swallow a skeleton.
|
|
214
|
-
if (/compaction-survival mechanism|compaction-proof working memory/.test(t)) return false;
|
|
215
|
-
return true;
|
|
216
|
-
})
|
|
217
|
-
.join('')
|
|
218
|
-
.trim().length;
|
|
196
|
+
/** The listing as text: what the worker actually has to go back to. NAMES ONLY, never content — see
|
|
197
|
+
* the header for why this hook points rather than injects.
|
|
198
|
+
* @param {{ name: string, size: number }[]} files */
|
|
199
|
+
function describe(files) {
|
|
200
|
+
const shown = files.slice(0, MAX_LISTED_FILES).map((f) => ' - `' + relPath + f.name + '` (' + f.size + ' bytes)');
|
|
201
|
+
if (files.length > shown.length) shown.push(' - …and ' + (files.length - shown.length) + ' more');
|
|
202
|
+
return shown.join('\n');
|
|
219
203
|
}
|
|
220
204
|
|
|
221
|
-
|
|
222
|
-
function capped(text) {
|
|
223
|
-
if (text.length <= MAX_INJECT_CHARS) return text;
|
|
224
|
-
return (
|
|
225
|
-
text.slice(0, MAX_INJECT_CHARS) +
|
|
226
|
-
'\n\n[…clipped at ' + MAX_INJECT_CHARS.toLocaleString('en-US') + ' characters — read `' + relPath +
|
|
227
|
-
'` for the rest.]'
|
|
228
|
-
);
|
|
229
|
-
}
|
|
205
|
+
|
|
230
206
|
|
|
231
207
|
/** @param {string} additionalContext @param {'SessionStart'|'UserPromptSubmit'|'PostToolUse'} hookEventName */
|
|
232
208
|
function emitJson(additionalContext, hookEventName) {
|
|
@@ -236,13 +212,10 @@ function emitJson(additionalContext, hookEventName) {
|
|
|
236
212
|
|
|
237
213
|
// ── mode: session-start ──────────────────────────────────────────────────────────────────────────
|
|
238
214
|
if (mode === 'session-start') {
|
|
239
|
-
const
|
|
240
|
-
const written =
|
|
241
|
-
const parts = [];
|
|
215
|
+
const files = listScratch();
|
|
216
|
+
const written = files.length > 0;
|
|
242
217
|
|
|
243
|
-
// The sources where the deep model of the work is actually GONE.
|
|
244
|
-
// re-grounded on its scratchpad — whether or not there is anything in it yet, because "your pad is
|
|
245
|
-
// empty and you just lost your context" is itself the most urgent thing the next turn can be told.
|
|
218
|
+
// The sources where the deep model of the work is actually GONE.
|
|
246
219
|
const lostContext = input.source === 'compact' || input.source === 'resume' || input.source === 'clear'
|
|
247
220
|
|
|
248
221
|
// Claude Code opens every compaction summary with the fixed preamble "This session is being
|
|
@@ -252,8 +225,7 @@ if (mode === 'session-start') {
|
|
|
252
225
|
// auto-compaction at line 20239 and then declared "I'm out of context" / "I'm at the end of this
|
|
253
226
|
// context window" on 13 consecutive turns at fills of 176k-244k, before self-diagnosing at line
|
|
254
227
|
// 20628 — "I've been treating 'low context' as a stopping condition ... and winding down instead of
|
|
255
|
-
// working."
|
|
256
|
-
// preamble gets answered. Kept to two sentences: the re-grounding instruction is the payload.
|
|
228
|
+
// working." Kept to two sentences: the re-grounding instruction is the payload.
|
|
257
229
|
const compactedNote =
|
|
258
230
|
input.source === 'compact'
|
|
259
231
|
? ' The summary opens "a previous conversation that ran out of context" — that describes the ' +
|
|
@@ -262,41 +234,46 @@ if (mode === 'session-start') {
|
|
|
262
234
|
'a reason to wind down, hand off, or leave the next step to a fresh session.'
|
|
263
235
|
: ''
|
|
264
236
|
|
|
237
|
+
const parts = [];
|
|
265
238
|
if (lostContext && written) {
|
|
266
|
-
//
|
|
267
|
-
//
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
239
|
+
// NAMES, NOT CONTENT. This is the pointer the maintainer accepted in place of an injection when
|
|
240
|
+
// the canonical pad was dropped; the guaranteed channel is now the recurring prompt's
|
|
241
|
+
// post_compaction trigger, which the worker arms for itself. Saying which files exist is the most
|
|
242
|
+
// a pointer can do, and it is worth doing: a worker that wrote three docs and lost its context
|
|
243
|
+
// otherwise has no idea they are there.
|
|
244
|
+
parts.push(
|
|
245
|
+
'⟦scratch directory⟧ Context was just ' + (input.source === 'compact' ? 'compacted' : 'lost') +
|
|
246
|
+
'. You have files in your scratch directory `' + relPath + '`:\n' + describe(files) +
|
|
247
|
+
'\n\nRead whichever of them bears on what you were doing BEFORE acting, and treat what you ' +
|
|
248
|
+
'wrote there as authoritative over anything the summary implies. If you have not already, arm ' +
|
|
249
|
+
'mcp__frizz__recurring_prompt with post_compaction: true and a prompt linking the one that ' +
|
|
250
|
+
'matters, so the next compaction hands it back to you without relying on this note.' +
|
|
251
|
+
compactedNote,
|
|
252
|
+
);
|
|
278
253
|
} else if (lostContext) {
|
|
279
|
-
// Context is gone and
|
|
280
|
-
//
|
|
281
|
-
//
|
|
254
|
+
// Context is gone and the worker left itself nothing. Say so plainly and constrain reconstruction:
|
|
255
|
+
// searching neighbouring threads' directories is both expensive and unsafe — they belong to
|
|
256
|
+
// unrelated workers.
|
|
282
257
|
parts.push(
|
|
283
|
-
'⟦
|
|
284
|
-
relPath + '` is
|
|
285
|
-
'
|
|
286
|
-
'`.frizz/threads/*/scratch.md` files for a substitute, and do not broadly reload repo docs or ' +
|
|
258
|
+
'⟦scratch directory⟧ Context was just compacted or resumed, and your scratch directory `' +
|
|
259
|
+
relPath + '` is EMPTY — you left yourself nothing to recover from. Do not search other ' +
|
|
260
|
+
'`.frizz/threads/*/` directories for a substitute, and do not broadly reload repo docs or ' +
|
|
287
261
|
'skills merely to reconstruct context. Recover from the retained compaction summary and any ' +
|
|
288
|
-
'task-specific handoff it directly names
|
|
289
|
-
'
|
|
290
|
-
'believed,
|
|
262
|
+
'task-specific handoff it directly names. If this effort is long enough to be compacted again, ' +
|
|
263
|
+
'write the account down this time — the problem, the approach and what you rejected, the ' +
|
|
264
|
+
"human's decisions, what is verified versus believed, the next action — and arm " +
|
|
265
|
+
'mcp__frizz__recurring_prompt with post_compaction: true and a prompt linking it.' +
|
|
266
|
+
compactedNote,
|
|
291
267
|
);
|
|
292
268
|
} else {
|
|
293
|
-
// A fresh start has lost nothing — teach the
|
|
269
|
+
// A fresh start has lost nothing — teach the arrangement while there is still time to make it.
|
|
294
270
|
parts.push(
|
|
295
|
-
'⟦
|
|
296
|
-
'
|
|
297
|
-
'
|
|
298
|
-
'
|
|
299
|
-
'
|
|
271
|
+
'⟦scratch directory⟧ `' + relPath + '` is yours: any files you like, no format expected, and ' +
|
|
272
|
+
'nothing in it is read automatically. On an effort long enough to be compacted, write the doc ' +
|
|
273
|
+
'you would want if you lost your context — the approach, what you rejected, the decisions the ' +
|
|
274
|
+
'human made, what is VERIFIED versus believed, the next action — then arm ' +
|
|
275
|
+
'mcp__frizz__recurring_prompt with post_compaction: true and a prompt that LINKS it. That ' +
|
|
276
|
+
'arming, not the file, is what makes it come back.',
|
|
300
277
|
);
|
|
301
278
|
}
|
|
302
279
|
emitJson(parts.join('\n\n'), 'SessionStart');
|
|
@@ -307,15 +284,13 @@ if (mode === 'session-start') {
|
|
|
307
284
|
// `Additional Instructions:`. Worded as an ordinary editorial note: precompact-instructions.mjs
|
|
308
285
|
// records that a summarizer REFUSES instructions that read like prompt-hijacking.
|
|
309
286
|
if (mode === 'precompact') {
|
|
310
|
-
const
|
|
311
|
-
if (
|
|
287
|
+
const files = listScratch();
|
|
288
|
+
if (files.length === 0) process.exit(0);
|
|
312
289
|
process.stdout.write(
|
|
313
|
-
'The worker
|
|
314
|
-
'
|
|
315
|
-
'
|
|
316
|
-
'
|
|
317
|
-
'prefer it.\n\n' +
|
|
318
|
-
capped(/** @type {string} */ (pad)) + '\n',
|
|
290
|
+
'The worker kept working notes for this effort in `' + relPath + '`:\n' + describe(files) +
|
|
291
|
+
'\nThose files are the hand-written account of the problem, the chosen approach and the ' +
|
|
292
|
+
'decisions behind them. Make sure the summary preserves the substance of the work they describe, ' +
|
|
293
|
+
'and name their paths in it so the continuing session can open them.\n',
|
|
319
294
|
);
|
|
320
295
|
process.exit(0);
|
|
321
296
|
}
|
|
@@ -392,18 +367,15 @@ if (mode === 'nudge') {
|
|
|
392
367
|
// silence is always safe.
|
|
393
368
|
if (!tokens) process.exit(0);
|
|
394
369
|
|
|
395
|
-
const
|
|
396
|
-
const written = substanceLength(pad) > 0;
|
|
370
|
+
const files = listScratch();
|
|
397
371
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
mtimeMs = 0;
|
|
403
|
-
}
|
|
372
|
+
// The NEWEST write across the whole directory is the clock. A worker with several docs has "written
|
|
373
|
+
// recently" if it touched ANY of them — the nudge asks whether the effort is being recorded at all,
|
|
374
|
+
// not whether one particular file moved. Zero when the directory is empty.
|
|
375
|
+
const mtimeMs = files.length ? Math.max(...files.map((f) => f.mtimeMs)) : 0;
|
|
404
376
|
|
|
405
377
|
let state = readState();
|
|
406
|
-
// A changed mtime means
|
|
378
|
+
// A changed mtime means something was just written — rebase the baseline to NOW and go quiet. This is
|
|
407
379
|
// also the first-ever observation, and it is why a fresh write buys a full interval of silence. It
|
|
408
380
|
// fires for a human's hand-edit exactly as for the agent's Write: both are just an mtime change.
|
|
409
381
|
if (state.mtimeMs !== mtimeMs) {
|
|
@@ -411,8 +383,8 @@ if (mode === 'nudge') {
|
|
|
411
383
|
writeState(state);
|
|
412
384
|
}
|
|
413
385
|
|
|
414
|
-
// An
|
|
415
|
-
//
|
|
386
|
+
// An EMPTY directory measures growth from ZERO: the whole session is unrecorded, so the clock starts
|
|
387
|
+
// at the beginning, not at whenever this first looked.
|
|
416
388
|
const baseline = mtimeMs ? (state.tokensAtWrite ?? tokens) : 0;
|
|
417
389
|
const grown = tokens - baseline;
|
|
418
390
|
if (grown < STALE_TOKENS) process.exit(0);
|
|
@@ -428,17 +400,18 @@ if (mode === 'nudge') {
|
|
|
428
400
|
);
|
|
429
401
|
emitJson(
|
|
430
402
|
mtimeMs
|
|
431
|
-
? '⟦
|
|
432
|
-
relPath + '`. If this effort is long enough that losing your reasoning would hurt,
|
|
433
|
-
'in passing — the approach, what you rejected, the human\'s decisions, what is
|
|
434
|
-
'versus believed
|
|
435
|
-
'NOT a task and NOT a reason to pause: do not stop working to service
|
|
436
|
-
'turn on it while the human\'s instruction still has parts left.'
|
|
437
|
-
: '⟦
|
|
438
|
-
'That is fine for a single direct task —
|
|
439
|
-
'the work. If this effort is long or branching,
|
|
440
|
-
'decisions
|
|
441
|
-
'
|
|
403
|
+
? '⟦scratch notes stale⟧ Your context has grown ~' + k + 'k tokens since you last wrote anything ' +
|
|
404
|
+
'in `' + relPath + '`. If this effort is long enough that losing your reasoning would hurt, ' +
|
|
405
|
+
'top it up in passing — the approach, what you rejected, the human\'s decisions, what is ' +
|
|
406
|
+
'verified versus believed — and make sure a post_compaction recurring prompt links it. This ' +
|
|
407
|
+
'is a background note, NOT a task and NOT a reason to pause: do not stop working to service ' +
|
|
408
|
+
'it, and never end a turn on it while the human\'s instruction still has parts left.'
|
|
409
|
+
: '⟦scratch directory empty⟧ This session is ~' + k + 'k tokens deep and `' + relPath + '` is ' +
|
|
410
|
+
'empty. That is fine for a single direct task — notes are optional and writing them is not ' +
|
|
411
|
+
'doing the work. If this effort is long or branching, write down the approach and the ' +
|
|
412
|
+
'human\'s decisions and arm mcp__frizz__recurring_prompt with post_compaction: true linking ' +
|
|
413
|
+
'that file, or a compaction takes your reasoning with it. This is a background note, NOT a ' +
|
|
414
|
+
'task and NOT a reason to pause: keep going with what you were asked to do.',
|
|
442
415
|
event,
|
|
443
416
|
);
|
|
444
417
|
}
|
|
@@ -5,14 +5,15 @@
|
|
|
5
5
|
//
|
|
6
6
|
// A frizz worker is a top-level interactive `claude` the UI spawns per effort; the slug arrives in
|
|
7
7
|
// env FRIZZ_THREAD (and a `THREAD:` line in the first prompt). There are NO thread files, no
|
|
8
|
-
// frontmatter, no status field — a worker SIGNALS through its final message (fences) and
|
|
9
|
-
//
|
|
8
|
+
// frontmatter, no status field — a worker SIGNALS through its final message (fences), and anything that
|
|
9
|
+
// must outlive its context window is an arrangement it makes for itself. This hook injects, on every
|
|
10
|
+
// session start (startup/resume/clear/compact):
|
|
10
11
|
// 1. `core` — a runtime re-grounding + pointer, NOT a second copy of the contract: the full worker
|
|
11
12
|
// contract lives ONCE in the system prompt (workerPrompt.ts) the server injects at spawn. This
|
|
12
|
-
// carries only what a static system prompt can't: the runtime
|
|
13
|
+
// carries only what a static system prompt can't: the runtime scratch-directory PATH + an essential
|
|
13
14
|
// signal-at-rest anchor + a pointer to the system-prompt contract.
|
|
14
|
-
// 2. the
|
|
15
|
-
// 3. on `compact` — a short re-grounding (compaction drops the deep model +
|
|
15
|
+
// 2. the SCRATCH DIRECTORY — `.frizz/threads/<session_id>/`, a folder the worker may use as it likes.
|
|
16
|
+
// 3. on `compact` — a short re-grounding (compaction drops the deep model + this orientation).
|
|
16
17
|
//
|
|
17
18
|
// GATE: everything is gated on FRIZZ_THREAD being set, so the plugin is completely inert when
|
|
18
19
|
// loaded outside a frizz worker (e.g. a plain `claude --plugin-dir cc-worker` smoke run).
|
|
@@ -44,7 +45,7 @@ if (!thread) process.exit(0);
|
|
|
44
45
|
const dir = process.env.CLAUDE_PROJECT_DIR ?? '.';
|
|
45
46
|
|
|
46
47
|
// Neutralize the orchestrator cc plugin for THIS session (defensive; see header + DECISIONS.md).
|
|
47
|
-
// The session id also names the worker's
|
|
48
|
+
// The session id also names the worker's scratch directory (`.frizz/threads/<session_id>/`).
|
|
48
49
|
let sid = null;
|
|
49
50
|
try {
|
|
50
51
|
sid = currentSessionId(input.session_id);
|
|
@@ -53,24 +54,24 @@ try {
|
|
|
53
54
|
/* best-effort — a failed sentinel write just leaves cc at its dormant default */
|
|
54
55
|
}
|
|
55
56
|
const scratch = sid
|
|
56
|
-
? '.frizz/threads/' + sid + '/
|
|
57
|
-
: '.frizz/threads/<session-id>/
|
|
57
|
+
? '.frizz/threads/' + sid + '/'
|
|
58
|
+
: '.frizz/threads/<session-id>/';
|
|
58
59
|
|
|
59
60
|
// A RUNTIME re-grounding + pointer, NOT a second copy of the contract. The full worker contract
|
|
60
|
-
// (signal fences,
|
|
61
|
+
// (signal fences, scratch-directory rules, sub-agent rules, the question handback) lives
|
|
61
62
|
// ONCE in the system prompt frizz injects at spawn (workerPrompt.ts / loadWorkerPrompt) — which is
|
|
62
63
|
// re-applied on every resume and survives compaction. This hook adds only what a static system prompt
|
|
63
|
-
// CANNOT carry: the runtime-derived
|
|
64
|
+
// CANNOT carry: the runtime-derived scratch-directory PATH, an essential signal-at-rest anchor, and (below)
|
|
64
65
|
// the compaction re-read nudge, gh guidance, and the defensive cc-orchestrator off-sentinel.
|
|
65
66
|
const core =
|
|
66
|
-
'⟦frizz worker contract⟧ You are a frizz WORKER driving EXACTLY ONE effort. Your FULL operating contract — the end-of-turn signal fences,
|
|
67
|
-
'
|
|
68
|
-
'DO NOT REST WHILE THE INSTRUCTION HAS PARTS LEFT — finish them in THIS turn; a milestone, a green test run and a long turn are none of them stopping points, and announcing the next step or
|
|
69
|
-
'
|
|
67
|
+
'⟦frizz worker contract⟧ You are a frizz WORKER driving EXACTLY ONE effort. Your FULL operating contract — the end-of-turn signal fences, scratch-directory rules, sub-agent rules, and the question handback — lives in your SYSTEM PROMPT; follow it there (this is a runtime re-grounding, not a second copy). The human + the frizz app are the ORCHESTRATOR; you drive ONE effort and never scan the board or touch other efforts. There is no orchestrator mode and no fleet to run: doing the work yourself is the default, and you dispatch a sub-agent only when the work genuinely decomposes into independent prongs.\n' +
|
|
68
|
+
'SCRATCH DIRECTORY (OPTIONAL): `' + scratch + '` — a folder kept FOR YOU: any files you like, no format expected, nothing in it read automatically, and never a substitute for doing the work. A single direct task usually needs nothing. On a long effort write the doc you would want if you lost your context — the approach, what you rejected, the human\'s decisions — AS YOU GO, mid-work, then KEEP WORKING, and arm mcp__frizz__recurring_prompt with post_compaction: true and a prompt LINKING that file, because the arming is what brings it back. Give each sub-agent its OWN file rather than a shared one.\n' +
|
|
69
|
+
'DO NOT REST WHILE THE INSTRUCTION HAS PARTS LEFT — finish them in THIS turn; a milestone, a green test run and a long turn are none of them stopping points, and announcing the next step or writing it into a scratch file is not doing it.\n' +
|
|
70
|
+
'ALWAYS SIGN OFF WITH A FENCE, per the fence rules in your system prompt. A rest with NO fence is not a handoff — it is an item nobody can triage, and frizz will tell you so. ```done only when the effort\'s real work is COMPLETE (code LANDED on the mainline — an open PR is NOT done, park it on ```awaiting until it MERGES) and is a DISMISSAL (its card files the thread away where nobody looks again), so if the thread points at future work AT ALL — a pre-fix investigation, a live code-change discussion, follow-up work you DISCOVERED even when someone else will do it — ask a ```question instead, and uncertain is not done; a verdict that ends in something the HUMAN must now do (post this comment, merge or decline, pick one of these) is a ```question carrying your recommendation as option A, never a done card, because a draft you wrote but did not send is filed away with the thread; when your OWN mandate is complete and what is left is a separable effort, mcp__frizz__spawn_thread gives it its own card and only THEN is done honest — link the returned thread in the body; the ONE exception is a planning session whose plan file is fully written and persisted, because that artifact outlives the thread; ```awaiting parks a human:/timer:/pr-watch: gate or your OWN background work, never CI/releases/merge progression (those stay ACTIVE); ```question is the operator ask. Waiting on your own background shell? Your shells and sub-agents are watched AUTOMATICALLY — frizz wakes you when one finishes, fence or no fence. NAME it on a `watch: <id>` line anyway and rest: that is what lets you stop without frizz asking you for a handoff, and what shows the human what you are waiting for. Frizz checks the name against what you actually have running. Keep the write-up SHORT: 1-3 sentences, then bullets starting with a **bolded verb phrase**. The fence and the prose above it are TWO SURFACES, not one message written twice — the card is the ledger of what shipped, the prose is only what a ledger cannot hold, and a sentence that would read the same in either belongs in exactly one. And a done message says what HAPPENED: delete every dangling "one thing to carry forward" or "a follow-up could" — do it, spawn it onto its own card, ask about it, or DROP it, and if it is not worth a card it is not worth a sentence.\n' +
|
|
70
71
|
'DECIDE rather than ask: anything derivable from the code, the conventions, or ordinary engineering judgment is YOURS to settle — asking permission to do the work you were dispatched to do is not a question, it is the job. Reserve the operator for the irreversible and the genuinely human-owned.';
|
|
71
72
|
|
|
72
73
|
const grounding =
|
|
73
|
-
'⟦frizz worker re-grounding (post-compaction)⟧ Context was just compacted. You are still the frizz worker for effort `' + thread + '` —
|
|
74
|
+
'⟦frizz worker re-grounding (post-compaction)⟧ Context was just compacted. You are still the frizz worker for effort `' + thread + '` — read whatever you left yourself in `' + scratch + '` NOW to recover your working state and to-do list before asserting anything, and re-read any code before claiming how it is structured. Signal at rest through your FINAL MESSAGE, always with a fence — a fenceless rest is an item nobody can triage: ```done queues a checked completion until Archive and is a DISMISSAL — completed work only, and never when the thread still points at future work (a pre-fix investigation, a live code-change discussion); use a question or bare rest; ```awaiting parks only a human:/timer: gate (or a pr-watch: PR watcher, which wakes on any new review/comment, bot or human); ```question is the explicit higher-priority operator ask. CI/releases/merge progression stay active through Monitor/background Bash.';
|
|
74
75
|
|
|
75
76
|
// AUTH-GATED gh guidance — teach the worker to use `gh` well, but ONLY when signed in.
|
|
76
77
|
// Shell `gh auth status --active`: exit 0 = an active gh account is authenticated. The whole gate is
|
|
@@ -55,7 +55,7 @@ while its own watcher is still live — the wait IS its work.
|
|
|
55
55
|
Give the child, literally, in the prompt:
|
|
56
56
|
|
|
57
57
|
- the exact command to run and the terminal condition to stop on;
|
|
58
|
-
- the repo path and the
|
|
58
|
+
- the repo path and the thread's scratch-directory path;
|
|
59
59
|
- what to return: the verdict plus enough detail to act on a failure (job name, failing step, log tail);
|
|
60
60
|
- an instruction not to fix anything — it observes and reports; you decide.
|
|
61
61
|
|
|
@@ -67,7 +67,7 @@ also inspect the workflow runs for the exact PR head, and treat `ACTION_REQUIRED
|
|
|
67
67
|
not passing.
|
|
68
68
|
|
|
69
69
|
**PR review activity.** Do not build a watcher — emit `awaiting` with `pr-watch: owner/repo#N` and frizz
|
|
70
|
-
polls it for you, waking on any new review, approval, or comment, bot or human.
|
|
70
|
+
polls it for you, waking on any new review, approval, or comment, bot or human.
|
|
71
71
|
|
|
72
72
|
**A release or deploy.** Poll the artifact that proves it, not the pipeline that promises it — the
|
|
73
73
|
published version on the registry, the health endpoint, the deployed asset hash.
|
|
Binary file
|