create-claude-cabinet 0.45.0 → 0.47.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 +6 -10
- package/lib/cli.js +374 -91
- package/lib/copy.js +108 -6
- package/lib/db-setup.js +122 -17
- package/lib/engagement-server-setup.js +34 -9
- package/lib/metadata.js +51 -2
- package/lib/migrate-from-omega.js +13 -1
- package/lib/mux-setup.js +33 -9
- package/lib/settings-merge.js +52 -10
- package/lib/watchtower-setup.js +230 -0
- package/package.json +5 -1
- package/templates/cabinet/_cabinet-member-template.md +8 -3
- package/templates/cabinet/advisories-state-schema.md +34 -7
- package/templates/cabinet/checklist-stats-schema.md +15 -1
- package/templates/cabinet/composition-patterns.md +4 -3
- package/templates/cabinet/memory-lifecycle-contract.md +135 -0
- package/templates/cabinet/pib-db-access.md +13 -0
- package/templates/cabinet/skill-output-conventions.md +35 -1
- package/templates/cabinet/watchtower-contracts.md +503 -1
- package/templates/cabinet/worktree-invocation-contract.md +87 -0
- package/templates/engagement/OVERVIEW.md +12 -0
- package/templates/engagement/__tests__/engagement.test.mjs +177 -2
- package/templates/engagement/__tests__/invoice-doc.test.mjs +195 -0
- package/templates/engagement/engagement-preview.mjs +100 -0
- package/templates/engagement/engagement-schema.md +240 -0
- package/templates/engagement/engagement.mjs +391 -11
- package/templates/engagement/pib-db-patches/pib-db-lib.mjs +10 -1
- package/templates/engagement/pib-db-patches/pib-db-mcp-server.mjs +5 -44
- package/templates/engagement/pib-db-patches/pib-db.mjs +5 -2
- package/templates/hooks/action-completion-gate.sh +9 -3
- package/templates/hooks/memory-index-guard.sh +17 -11
- package/templates/hooks/watchtower-session-start.sh +24 -2
- package/templates/mcp/pib-db.json +1 -4
- package/templates/mux/__tests__/mux-fail-loud.fixture.sh +44 -0
- package/templates/mux/__tests__/station-liveness.fixture.sh +234 -0
- package/templates/mux/__tests__/station-liveness.test.mjs +47 -0
- package/templates/mux/bin/mux +289 -56
- package/templates/mux/config/help.txt +1 -0
- package/templates/rules/acknowledge-when-corrected.md +33 -0
- package/templates/rules/maintainability.md +11 -0
- package/templates/rules/memory-capture.md +26 -4
- package/templates/rules/plan-before-bulk-or-irreversible-actions.md +48 -0
- package/templates/rules/verify-before-asserting.md +79 -0
- package/templates/scripts/__tests__/advisor-pass.test.mjs +238 -0
- package/templates/scripts/__tests__/advisories.test.mjs +262 -0
- package/templates/scripts/__tests__/ahead-check-origin.test.mjs +355 -0
- package/templates/scripts/__tests__/batch-disposition.test.mjs +235 -0
- package/templates/scripts/__tests__/claude-churn-authored.test.mjs +90 -0
- package/templates/scripts/__tests__/cross-ring-reader.test.mjs +505 -0
- package/templates/scripts/__tests__/dx-captures-briefing.test.mjs +169 -0
- package/templates/scripts/__tests__/feedback-outbox-flush.test.mjs +232 -0
- package/templates/scripts/__tests__/hook-runner.test.mjs +247 -0
- package/templates/scripts/__tests__/narrative-corpus.test.mjs +247 -0
- package/templates/scripts/__tests__/phase-shim.test.mjs +112 -0
- package/templates/scripts/__tests__/qa-handoff-gate.test.mjs +68 -0
- package/templates/scripts/__tests__/qa-handoff-merge-state.test.mjs +162 -0
- package/templates/scripts/__tests__/resolve-cli.test.mjs +314 -0
- package/templates/scripts/__tests__/ring-state-ownership.test.mjs +228 -3
- package/templates/scripts/__tests__/ring1-content-detector.test.mjs +168 -0
- package/templates/scripts/__tests__/ring1-flagged-actions.test.mjs +135 -0
- package/templates/scripts/__tests__/ring1-script-drift.test.mjs +126 -0
- package/templates/scripts/__tests__/ring2-queue-reader.test.mjs +80 -0
- package/templates/scripts/__tests__/ring2-recall-canary.test.mjs +150 -0
- package/templates/scripts/__tests__/ring2-roster-review.test.mjs +311 -0
- package/templates/scripts/__tests__/ring2-thread-context.test.mjs +189 -0
- package/templates/scripts/__tests__/ring3-chunk-merge.test.mjs +122 -0
- package/templates/scripts/__tests__/ring3-close-lenses.test.mjs +359 -0
- package/templates/scripts/__tests__/ring3-dedup.test.mjs +467 -0
- package/templates/scripts/__tests__/ring3-memory-titles.test.mjs +187 -0
- package/templates/scripts/__tests__/ring3-novelty-rescue.test.mjs +148 -0
- package/templates/scripts/__tests__/ring3-recent-slice.test.mjs +101 -0
- package/templates/scripts/__tests__/ring4-reconcile.test.mjs +393 -0
- package/templates/scripts/__tests__/routine-dispatch.test.mjs +312 -0
- package/templates/scripts/__tests__/shared-thread-reader.test.mjs +187 -0
- package/templates/scripts/__tests__/suppression-ledger.test.mjs +148 -0
- package/templates/scripts/__tests__/verify-backfill.test.mjs +103 -0
- package/templates/scripts/__tests__/verify-coverage.test.mjs +80 -0
- package/templates/scripts/__tests__/watchtower-snapshot.test.mjs +261 -0
- package/templates/scripts/__tests__/watchtower-sync.test.mjs +311 -0
- package/templates/scripts/load-triage-history.js +5 -2
- package/templates/scripts/pib-db-mcp-server.mjs +5 -44
- package/templates/scripts/pib-db-path.mjs +61 -0
- package/templates/scripts/pib-db.mjs +5 -2
- package/templates/scripts/validate-memory.mjs +214 -16
- package/templates/scripts/watchtower-advisories.mjs +309 -0
- package/templates/scripts/watchtower-build-context.mjs +301 -18
- package/templates/scripts/watchtower-cross-ring-reader.mjs +700 -0
- package/templates/scripts/watchtower-hook-runner.mjs +422 -0
- package/templates/scripts/watchtower-lib.mjs +447 -2
- package/templates/scripts/watchtower-narrative-corpus.mjs +385 -0
- package/templates/scripts/watchtower-phase-shim.mjs +171 -0
- package/templates/scripts/watchtower-queue.mjs +484 -1
- package/templates/scripts/watchtower-ring1.mjs +533 -53
- package/templates/scripts/watchtower-ring2.mjs +787 -48
- package/templates/scripts/watchtower-ring3-close.mjs +1357 -99
- package/templates/scripts/watchtower-ring4-runner.sh +85 -0
- package/templates/scripts/watchtower-ring4.mjs +753 -0
- package/templates/scripts/watchtower-routines.mjs +358 -0
- package/templates/scripts/watchtower-snapshot.mjs +452 -0
- package/templates/scripts/watchtower-status.sh +1 -1
- package/templates/scripts/watchtower-sync.mjs +393 -0
- package/templates/skills/audit/SKILL.md +5 -1
- package/templates/skills/briefing/SKILL.md +584 -236
- package/templates/skills/cabinet-anthropic-insider/SKILL.md +14 -6
- package/templates/skills/cabinet-historian/SKILL.md +14 -11
- package/templates/skills/cabinet-process-therapist/SKILL.md +28 -0
- package/templates/skills/cabinet-security/SKILL.md +11 -0
- package/templates/skills/cabinet-system-advocate/SKILL.md +22 -21
- package/templates/skills/cabinet-user-advocate/SKILL.md +13 -7
- package/templates/skills/catch-up/SKILL.md +113 -0
- package/templates/skills/cc-publish/SKILL.md +148 -25
- package/templates/skills/cc-remember/SKILL.md +45 -0
- package/templates/skills/close/SKILL.md +107 -0
- package/templates/skills/collab-client/SKILL.md +22 -5
- package/templates/skills/collab-consultant/SKILL.md +110 -2
- package/templates/skills/debrief/SKILL.md +148 -16
- package/templates/skills/debrief-classic/SKILL.md +696 -0
- package/templates/skills/debrief-classic/calibration.md +44 -0
- package/templates/skills/debrief-classic/phases/audit-pattern-capture.md +78 -0
- package/templates/skills/debrief-classic/phases/auto-maintenance.md +48 -0
- package/templates/skills/debrief-classic/phases/checklist-feedback.md +123 -0
- package/templates/skills/debrief-classic/phases/close-work.md +163 -0
- package/templates/skills/debrief-classic/phases/health-checks.md +54 -0
- package/templates/skills/debrief-classic/phases/inventory.md +40 -0
- package/templates/skills/debrief-classic/phases/loose-ends.md +52 -0
- package/templates/skills/debrief-classic/phases/methodology-capture.md +223 -0
- package/templates/skills/debrief-classic/phases/qa-handoff-sweep.md +78 -0
- package/templates/skills/debrief-classic/phases/record-lessons.md +177 -0
- package/templates/skills/debrief-classic/phases/report.md +59 -0
- package/templates/skills/debrief-classic/phases/update-state.md +48 -0
- package/templates/skills/debrief-classic/phases/upstream-feedback.md +185 -0
- package/templates/skills/debrief-classic/phases/verify-coverage.md +101 -0
- package/templates/skills/execute/SKILL.md +77 -6
- package/templates/skills/execute/phases/commit-and-deploy.md +8 -0
- package/templates/skills/execute-group/SKILL.md +23 -26
- package/templates/skills/generate-plan-groups/SKILL.md +20 -0
- package/templates/skills/inbox/SKILL.md +124 -7
- package/templates/skills/memory/SKILL.md +22 -6
- package/templates/skills/orient/SKILL.md +140 -52
- package/templates/skills/orient-classic/SKILL.md +770 -0
- package/templates/skills/orient-classic/phases/auto-maintenance.md +52 -0
- package/templates/skills/orient-classic/phases/briefing.md +53 -0
- package/templates/skills/orient-classic/phases/cabinet.md +46 -0
- package/templates/skills/orient-classic/phases/checklist-status.md +54 -0
- package/templates/skills/orient-classic/phases/context.md +88 -0
- package/templates/skills/orient-classic/phases/data-sync.md +35 -0
- package/templates/skills/orient-classic/phases/deferred-check.md +55 -0
- package/templates/skills/orient-classic/phases/dx-captures.md +53 -0
- package/templates/skills/orient-classic/phases/health-checks.md +50 -0
- package/templates/skills/orient-classic/phases/verify-backfill.md +109 -0
- package/templates/skills/orient-classic/phases/work-scan.md +69 -0
- package/templates/skills/plan/SKILL.md +8 -0
- package/templates/skills/qa-drain/SKILL.md +157 -0
- package/templates/skills/qa-handoff/SKILL.md +287 -46
- package/templates/skills/session-handoff/SKILL.md +175 -6
- package/templates/skills/setup-accounts/SKILL.md +38 -16
- package/templates/skills/threads/SKILL.md +4 -0
- package/templates/skills/triage-audit/SKILL.md +6 -0
- package/templates/skills/validate/phases/validators.md +41 -0
- package/templates/skills/watchtower/SKILL.md +199 -2
- package/templates/verify-runtime/CONVENTIONS.md +9 -0
- package/templates/verify-runtime/README.md +37 -0
- package/templates/watchtower/config.json.template +10 -2
- package/templates/watchtower/queue/items/item.json.schema +1 -1
- package/templates/workflows/execute-group-complete.js +10 -1
|
@@ -1,31 +1,83 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: briefing
|
|
3
3
|
description: |
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
The one daily surface. A portfolio-wide curator: walks the landscape
|
|
5
|
+
project by project — plain-English state, what deserves attention,
|
|
6
|
+
and the project's pending inbox items batched into proposed
|
|
7
|
+
dispositions you approve in-conversation. Decisions are chunked by
|
|
8
|
+
project, never interleaved. Also handles /recap (factual-only mode)
|
|
9
|
+
and a per-project decision-lineage drill-down ("why is this project
|
|
10
|
+
the way it is — what still constrains it").
|
|
8
11
|
Use when: "briefing", "brief me", "/briefing", "what's the landscape",
|
|
9
|
-
"recap", "/recap", "what happened", "what did I do yesterday"
|
|
10
|
-
|
|
12
|
+
"recap", "/recap", "what happened", "what did I do yesterday",
|
|
13
|
+
"decision lineage", "why is this the way it is".
|
|
14
|
+
argument-hint: "scope — e.g., 'today', 'this week', 'maginnis only', 'recap', 'lineage'"
|
|
11
15
|
---
|
|
12
16
|
|
|
13
|
-
# /briefing —
|
|
17
|
+
# /briefing — The Daily Surface
|
|
18
|
+
|
|
19
|
+
One run, from any desk, covers the whole portfolio. The briefing is a
|
|
20
|
+
**curator**, not a dashboard: it reads everything watchtower knows,
|
|
21
|
+
selects what actually matters, narrates it in plain English, and lets
|
|
22
|
+
the operator disposition pending inbox items in bulk without leaving
|
|
23
|
+
the conversation. The success test: the operator runs this daily
|
|
24
|
+
because it is pleasant, and the inbox drains through batch decisions
|
|
25
|
+
here instead of item-by-item `/inbox` walks.
|
|
26
|
+
|
|
27
|
+
Tone contract for every section below — **non-negotiable, every run**:
|
|
28
|
+
plain English, conversational, concept-first. No insider jargon, no
|
|
29
|
+
fid/jargon walls. Say "the background system files one note when the
|
|
30
|
+
same mistake keeps happening" — not "Ring 2 emitPatternPromotion
|
|
31
|
+
dedups by failure class." Name the thing in words the operator would
|
|
32
|
+
use, then add the id only as a trailing reference. Item ids and `act:`
|
|
33
|
+
fids appear on request or in disposition confirmations — never as the
|
|
34
|
+
narrative spine. Lead with what matters; counts and mechanics trail.
|
|
35
|
+
This recurred live (2026-06-14): a briefing slipped into "Ring 2
|
|
36
|
+
meta-critiques / generator leak" and the operator could not act on it
|
|
37
|
+
until it was re-said plainly. If you catch yourself writing a term a
|
|
38
|
+
non-author wouldn't recognize, rewrite the sentence. See also the
|
|
39
|
+
decision/context structure rule under Step 3c.
|
|
40
|
+
|
|
41
|
+
## The Anti-Accretion Invariant (structural rule)
|
|
42
|
+
|
|
43
|
+
Every "the briefing should show X" duty — present and future — routes
|
|
44
|
+
through the curator's top-N selection inside the owning project's
|
|
45
|
+
chunk. **No duty may append its own always-rendered section.** This is
|
|
46
|
+
how the briefing stays a pleasure for years: new signal classes (QA
|
|
47
|
+
filed gaps, advisor findings, hygiene nudges, DX captures, verify
|
|
48
|
+
warnings, whatever ships next) become candidates the curator weighs,
|
|
49
|
+
not permanent real estate. A duty that earns attention today may
|
|
50
|
+
earn one line tomorrow and silence next week. When editing this skill,
|
|
51
|
+
reject any change that adds an unconditional section — give the new
|
|
52
|
+
signal a candidate class in Step 3b instead.
|
|
53
|
+
|
|
54
|
+
Two narrow floors temper curation — both are "never silently dropped"
|
|
55
|
+
rules, not sections:
|
|
56
|
+
|
|
57
|
+
1. **Gated items are never curated out.** A pending item in a gated
|
|
58
|
+
category (`GATED_CATEGORIES` in `scripts/watchtower-queue.mjs`;
|
|
59
|
+
today: `qa-handoff`) always appears in its project's chunk. The
|
|
60
|
+
queue lib already guarantees these cannot leave the queue silently;
|
|
61
|
+
the briefing must not hide what the gate keeps alive.
|
|
62
|
+
2. **Open QA filed-gaps get at least a one-line roll-up** in the
|
|
63
|
+
cross-portfolio close (Step 4) while any remain open. "Filed" must
|
|
64
|
+
not become "forgotten" — the qa-handoff contract promises this
|
|
65
|
+
re-surfacing.
|
|
14
66
|
|
|
15
67
|
## Mode Detection
|
|
16
68
|
|
|
17
|
-
Determine the briefing mode from how the skill was invoked:
|
|
18
|
-
|
|
19
69
|
- If `$ARGUMENTS` contains the word "recap", or the skill was invoked
|
|
20
|
-
as `/recap`: **Factual mode.**
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
|
|
70
|
+
as `/recap`: **Factual mode.** Deliver Step 2's summary, write the
|
|
71
|
+
timestamp (Step 5), and stop. No walk, no dispositions, no cabinet.
|
|
72
|
+
- If `$ARGUMENTS` contains "lineage", "why is", or "decision history":
|
|
73
|
+
**Lineage mode.** Render THIS desk's project decision lineage (see
|
|
74
|
+
"Lineage Mode" below), then stop. No walk, no dispositions, no cabinet.
|
|
75
|
+
- Otherwise: **Full mode.** Summary, then the project walk with batch
|
|
76
|
+
dispositions, then the cross-portfolio close.
|
|
24
77
|
|
|
25
78
|
## Scope Detection
|
|
26
79
|
|
|
27
|
-
Parse the arguments
|
|
28
|
-
time window and project filter:
|
|
80
|
+
Parse the arguments to determine the time window and project filter:
|
|
29
81
|
|
|
30
82
|
| Input | Scope |
|
|
31
83
|
|-------|-------|
|
|
@@ -39,214 +91,498 @@ time window and project filter:
|
|
|
39
91
|
|
|
40
92
|
When a project name is provided, match against project directory names
|
|
41
93
|
in `state/projects/`. Use fuzzy prefix matching — "maginnis" matches
|
|
42
|
-
"maginnis-howard". If multiple projects match, list them and ask
|
|
43
|
-
|
|
94
|
+
"maginnis-howard". If multiple projects match, list them and ask.
|
|
95
|
+
The time window scopes the NARRATIVE (sessions, threads, what
|
|
96
|
+
happened); pending inbox items are always loaded in full — old pending
|
|
97
|
+
items are exactly what batch dispositions exist to drain.
|
|
98
|
+
|
|
99
|
+
## Lineage Mode: Decision-Lineage Drill-Down (act:a2efc0ce)
|
|
100
|
+
|
|
101
|
+
On-demand, read-only. Answers "why is THIS desk's project the way it is —
|
|
102
|
+
what past decisions still constrain it, and what's safely reversible?"
|
|
103
|
+
This is a **projection** over existing stores (the inbox, threads, memory
|
|
104
|
+
`decision_*.md`, `.claude/methodology/`, pib-db), never a new store, and
|
|
105
|
+
it **writes nothing**. It is current-project-scoped in Stage 1: it reads
|
|
106
|
+
the project the current desk's cwd belongs to (worktree-aware). If the
|
|
107
|
+
operator named a *different* project, say plainly that lineage is
|
|
108
|
+
per-desk for now — run it from that project's desk — then either run it
|
|
109
|
+
for the current project or stop. (Cross-project lineage is deferred.)
|
|
110
|
+
|
|
111
|
+
Distinct from `/threads arc <slug>`, which tells **one work thread's**
|
|
112
|
+
evolution. Lineage tells the **whole project's decision causality**.
|
|
113
|
+
|
|
114
|
+
**L1 — corpus (inbox + threads).** Run the read-only helper (it lives
|
|
115
|
+
beside the queue lib, in the project-root `scripts/` dir):
|
|
116
|
+
```bash
|
|
117
|
+
node scripts/watchtower-narrative-corpus.mjs --corpus --project-path "$(pwd)"
|
|
118
|
+
```
|
|
119
|
+
It returns `project_identity`, `inbox_decisions` (knowledge-extraction
|
|
120
|
+
items, captured-to-memory duplicates already dropped, `superseded`
|
|
121
|
+
included), `thread_cursors` (full `cursor_history` timelines),
|
|
122
|
+
`per_store_counts`, and `skipped_unresolved`. If `reason` is
|
|
123
|
+
`project-unresolved`, say the desk isn't a watchtower project and stop.
|
|
124
|
+
|
|
125
|
+
**L2 — memory decisions + recorded edges.** Resolve the project's
|
|
126
|
+
built-in memory dir the way orient does (prefer
|
|
127
|
+
`node -e "console.log(require('./scripts/project-context.cjs').resolveMemoryDir())"`
|
|
128
|
+
when that script exists; else `~/.claude/projects/<dashified-cwd>/memory`),
|
|
129
|
+
then have the SAME helper parse the decision files (the consumer resolves
|
|
130
|
+
the path; the helper owns the edge rule):
|
|
131
|
+
```bash
|
|
132
|
+
node scripts/watchtower-narrative-corpus.mjs --memory "<memory-dir>"
|
|
133
|
+
```
|
|
134
|
+
Each entry carries `supersedes` (RECORDED, directional edges) and
|
|
135
|
+
`see_also` (undirected wikilinks — **never** render a see-also as a
|
|
136
|
+
supersession).
|
|
137
|
+
|
|
138
|
+
**L3 — methodology "why" (richest substrate).** If `.claude/methodology/`
|
|
139
|
+
exists in the cwd, read its `README.md` index and the `-internal.md`
|
|
140
|
+
bodies relevant to the load-bearing decisions — these carry the
|
|
141
|
+
Claim→Evidence→Skeptic→Counter reasoning the other stores summarize away.
|
|
142
|
+
|
|
143
|
+
**L4 — pib-db design rationale.** `pib_list_actions` for the current
|
|
144
|
+
project (resolve the `prj:` fid via `pib_list_projects`; if the name is
|
|
145
|
+
ambiguous, ask — never guess). Action notes carry `absorbs act:…` /
|
|
146
|
+
`Supersedes …` prose — these are **inferred** edges (prose, not a
|
|
147
|
+
structural field), mark them so.
|
|
148
|
+
|
|
149
|
+
**L5 — synthesize, ANSWER-FIRST** (plain English, concept-first; the tone
|
|
150
|
+
contract above applies in full):
|
|
151
|
+
|
|
152
|
+
1. **What still constrains this project right now** — lead with this. The
|
|
153
|
+
decisions/constraints from earlier that later work still depends on;
|
|
154
|
+
for the ones the operator might want to revisit, say whether anything
|
|
155
|
+
still hangs on them (reversible vs load-bearing). This is the answer
|
|
156
|
+
the drill-down exists to give.
|
|
157
|
+
2. **Supersession chains** — what replaced what, with **provenance**:
|
|
158
|
+
*recorded* (a parsed `**Supersedes:**` block or a pib `absorbs act:`
|
|
159
|
+
marker — citable) vs *inferred* (read from prose) — labeled, never
|
|
160
|
+
conflated. A narrative is a trust surface: do not present an inferred
|
|
161
|
+
link as recorded history, and never render a `[[wikilink]]` as a
|
|
162
|
+
supersede.
|
|
163
|
+
3. **The causal timeline** — OPTIONAL deep-read, placed **below** the
|
|
164
|
+
answer. Chapter by causal **pivot / phase transition** (the moments
|
|
165
|
+
direction changed — a constraint discovered, an approach superseded),
|
|
166
|
+
not by raw time gaps. Skip this entirely when the corpus is sparse.
|
|
167
|
+
|
|
168
|
+
**Honesty footer.** Report `per_store_counts` and `skipped_unresolved` in
|
|
169
|
+
plain words when either is notable: "history may be incomplete — N inbox
|
|
170
|
+
items couldn't be attributed to a project and may belong here." Never
|
|
171
|
+
present a silently-truncated history as complete.
|
|
172
|
+
|
|
173
|
+
**Sparse / empty.** If the combined decision count (inbox_kept + memory
|
|
174
|
+
decisions) is under ~3, say "not enough decision history to narrate yet
|
|
175
|
+
(N found)" and stop — a story needs enough points to have an arc.
|
|
176
|
+
|
|
177
|
+
Lineage mode renders inline and stops. It writes nothing — no state file,
|
|
178
|
+
no inbox item, no memory write.
|
|
44
179
|
|
|
45
180
|
## Step 1: Gather Data
|
|
46
181
|
|
|
47
|
-
No Claude API calls
|
|
48
|
-
are under `~/.claude-cabinet/watchtower
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
-
|
|
83
|
-
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
"forgotten."
|
|
95
|
-
|
|
96
|
-
### 1d. Portfolio State
|
|
97
|
-
|
|
98
|
-
Read project state files from `state/projects/*.md`. For each project
|
|
99
|
-
within scope, extract the key sections: Active Plans, Last Session,
|
|
100
|
-
Standing Issues. If a project filter was specified, only read that
|
|
101
|
-
project's file.
|
|
102
|
-
|
|
103
|
-
### 1e. Active Threads
|
|
104
|
-
|
|
105
|
-
Read `state/threads/*.json`. For each thread file, parse the JSON and
|
|
106
|
-
collect threads with `status: "active"`. The **current cursor** is the
|
|
107
|
-
last entry of the `cursor_history` array
|
|
108
|
-
(`cursor_history[length-1].cursor`); older thread files may instead carry
|
|
109
|
-
a single `cursor` object — treat that lone object as the current cursor.
|
|
110
|
-
For each active thread, extract from the current cursor:
|
|
111
|
-
- `thread` (slug name)
|
|
112
|
-
- `cursor.what` (one-line description)
|
|
113
|
-
- `cursor.where_left_off` (current state)
|
|
114
|
-
- `last_updated` (timestamp)
|
|
115
|
-
- `sessions` array length (how many sessions have touched it)
|
|
116
|
-
|
|
117
|
-
If a project filter is active, filter to threads whose `sessions` array
|
|
118
|
-
contains at least one entry with a matching `project` slug. If no thread
|
|
119
|
-
files exist, skip silently — threads accumulate over time.
|
|
120
|
-
|
|
121
|
-
Sort by `last_updated` descending (most recent first).
|
|
122
|
-
|
|
123
|
-
### 1f. Last Briefing Timestamp
|
|
124
|
-
|
|
125
|
-
Read `state/last-briefing.json` if it exists. Use this to determine
|
|
126
|
-
what's "new since last briefing" for the default scope. If the file
|
|
127
|
-
doesn't exist, fall back to "last 24 hours."
|
|
128
|
-
|
|
129
|
-
## Step 2: Top-Level Brief
|
|
130
|
-
|
|
131
|
-
Present a scannable summary in **10 lines or fewer.** This is the
|
|
132
|
-
executive view — the user should know the state of everything in one
|
|
133
|
-
glance.
|
|
134
|
-
|
|
135
|
-
Format:
|
|
136
|
-
|
|
182
|
+
No Claude API calls — pure filesystem reads plus the queue lib. State
|
|
183
|
+
paths are under `~/.claude-cabinet/watchtower/`; the queue lib is the
|
|
184
|
+
project-root `scripts/watchtower-queue.mjs` (any watchtower-installed
|
|
185
|
+
project's copy operates on the shared global queue — this is what
|
|
186
|
+
makes "run from any desk" work).
|
|
187
|
+
|
|
188
|
+
### 1a. Ring health
|
|
189
|
+
|
|
190
|
+
Read `state/ring1-health.json`, `state/ring2-fast-health.json`,
|
|
191
|
+
`state/ring2-slow-health.json`, `state/ring3-health.json`. A missing
|
|
192
|
+
file means "not yet active", not an error. Thresholds: **ok** — last
|
|
193
|
+
success within expected cadence (Ring 1: 5 min, Ring 2 fast: 15 min,
|
|
194
|
+
Ring 2 slow: 6 hours, Ring 3: since last session end); **stale** —
|
|
195
|
+
exceeded 2x cadence; **down** — missing after previously running, or
|
|
196
|
+
last run failed.
|
|
197
|
+
|
|
198
|
+
### 1b. Sessions processed
|
|
199
|
+
|
|
200
|
+
Scan `ring3/processed/` for session files within the scope window:
|
|
201
|
+
project, date, duration, summary snippet.
|
|
202
|
+
|
|
203
|
+
### 1c. The inbox, in full
|
|
204
|
+
|
|
205
|
+
Run `runExpiry()` first (warn 14d, expire 30d — the mechanical decay
|
|
206
|
+
floor; gated categories never expire, they warn). Then load ALL
|
|
207
|
+
pending items via `listPending()` and group by `item.project`. Items
|
|
208
|
+
carrying `project_unresolved: true` form their own group (see Step 3).
|
|
209
|
+
Collect per project: counts by category and urgency, age distribution,
|
|
210
|
+
and titles. Also collect resolved `qa-handoff` items whose
|
|
211
|
+
`qa_verdict.filed_gaps` is non-empty; check each filed `act:` fid in
|
|
212
|
+
pib-db (`pib_get_action` or `node scripts/pib-db.mjs get <fid>`) and
|
|
213
|
+
keep only still-open ones — these are the open QA debts for Step 4.
|
|
214
|
+
|
|
215
|
+
### 1d. Cross-ring portfolio view (state, threads, git attention, resolution history)
|
|
216
|
+
|
|
217
|
+
Run the cross-ring reader — ONE gather call that replaces the old
|
|
218
|
+
per-store reads (state files + threads) and adds two signals nothing
|
|
219
|
+
here consulted before: the inbox **resolution history** (with
|
|
220
|
+
`resolution_mix` ratios — what the operator's past decisions actually
|
|
221
|
+
engaged vs discarded, per project) and the structured **git-attention
|
|
222
|
+
facts**:
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
XRING="$(mktemp)"
|
|
226
|
+
node scripts/watchtower-cross-ring-reader.mjs --portfolio > "$XRING"
|
|
227
|
+
# with a project filter active: --project "<name or slug>" instead
|
|
228
|
+
# (config-key name preferred; a unique slug match also resolves)
|
|
137
229
|
```
|
|
138
|
-
## Briefing — [scope description]
|
|
139
230
|
|
|
140
|
-
**
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
231
|
+
**Consume by Reading the file, never by echoing it through Bash** — a
|
|
232
|
+
live portfolio view runs to a few hundred KB and Bash tool output
|
|
233
|
+
truncates long before that (a truncated JSON parse is the failure this
|
|
234
|
+
instruction exists to prevent). Read the envelope's top (counts +
|
|
235
|
+
honesty fields) for Step 2, then Read each project's view slice as you
|
|
236
|
+
walk that chunk in Step 3; the JSON is pretty-printed, so each
|
|
237
|
+
project's view is one contiguous block.
|
|
238
|
+
|
|
239
|
+
Per project it returns the parsed Ring 1 state sections (Active Plans,
|
|
240
|
+
Last Session, Standing Issues, Tech Stack), git-attention facts, recent
|
|
241
|
+
session summaries, thread cursor timelines (capped to the newest 3
|
|
242
|
+
snapshots with `history_total` — the full evolution is lineage mode's
|
|
243
|
+
job; a legacy lone-`cursor` file arrives as a single-entry history;
|
|
244
|
+
current cursor = the LAST history entry), pending counts by
|
|
245
|
+
category/urgency, and the resolution history (`resolution_mix` carries
|
|
246
|
+
full counts; the event list is capped with `resolution_events_total`).
|
|
247
|
+
Every cap reports its total — "3 of 85" is signal, not truncation. The
|
|
248
|
+
envelope's honesty fields (`unattributed_items`, `orphan_threads`,
|
|
249
|
+
`orphaned_state_files`) are real signal — surface them in plain words
|
|
250
|
+
in Step 4 when notable. Pending COUNTS here are a narration snapshot;
|
|
251
|
+
1c's live `listPending()` load stays the disposition source of truth.
|
|
252
|
+
|
|
253
|
+
**Fallback (script absent — a consumer that hasn't reinstalled since
|
|
254
|
+
the reader shipped):** read the raw stores directly as 1e describes,
|
|
255
|
+
and proceed without the resolution-history/git-attention signals.
|
|
256
|
+
|
|
257
|
+
### 1e. Active threads (fallback path only)
|
|
258
|
+
|
|
259
|
+
Normally covered by the 1d view. When the 1d fallback is in effect,
|
|
260
|
+
read `state/threads/*.json`; collect `status: "active"` threads. The
|
|
261
|
+
current cursor is `cursor_history[last].cursor` (older files may carry
|
|
262
|
+
a lone `cursor` object — treat it as current). Extract `what`,
|
|
263
|
+
`where_left_off`, `next_steps`, `last_updated`, session count. Sort by
|
|
264
|
+
`last_updated` descending. (Also read `state/projects/*.md` — Active
|
|
265
|
+
Plans, Last Session, Standing Issues per project, scope-filtered if a
|
|
266
|
+
project filter is active.)
|
|
267
|
+
|
|
268
|
+
### 1f. Last briefing timestamp
|
|
269
|
+
|
|
270
|
+
Read `state/last-briefing.json` if present — it anchors "new since
|
|
271
|
+
last briefing" for the default scope.
|
|
272
|
+
|
|
273
|
+
### 1g. Spawn the live advisor panel (full mode only)
|
|
274
|
+
|
|
275
|
+
The standing session advisors hold their start-side seat here — this is
|
|
276
|
+
where intent exists, the thing background runs are blind to. Discover
|
|
277
|
+
the panel from `.claude/skills/_index.json`: entries whose
|
|
278
|
+
`standingMandate` includes `"briefing"`, each with a
|
|
279
|
+
`directives.briefing` scoped task (a mandate without a directive is a
|
|
280
|
+
data error — skip that member, say so). Spawn all members in parallel
|
|
281
|
+
NOW, right after the Step 1 reads, so they work while you compose Step
|
|
282
|
+
2. Each agent gets: its SKILL.md (from the index `path`), the
|
|
283
|
+
portfolio data gathered above (state summaries, active threads, pending
|
|
284
|
+
counts), and its `directives.briefing` as the task — constrained to a
|
|
285
|
+
**one-to-few-line observation**. The constraint is load-bearing.
|
|
286
|
+
|
|
287
|
+
Collect results before Step 3. Route them through the curation seam —
|
|
288
|
+
never as their own section: an observation about a specific project
|
|
289
|
+
becomes a Step 3b candidate in that project's chunk; a portfolio-level
|
|
290
|
+
observation is Step 4 close material. Include only members who said
|
|
291
|
+
something; silence is fine and is the common case. In recap mode, skip
|
|
292
|
+
the panel entirely — it spawns only on full-mode invocation.
|
|
293
|
+
|
|
294
|
+
### 1h. Unprocessed DX captures (mux's global capture list)
|
|
295
|
+
|
|
296
|
+
Read mux's global DX-capture store — `~/.local/share/mux/dx.json` — the
|
|
297
|
+
single cross-project list where `mux dx "..."` and `/dx-feedback` drop
|
|
298
|
+
developer-experience ideas and friction. A pure filesystem read with no
|
|
299
|
+
ordering dependency on the panel spawn. If the file is absent or
|
|
300
|
+
unparseable (mux not installed, or no captures yet), skip silently — it
|
|
301
|
+
is a mux-owned store and its absence is normal, not an error.
|
|
302
|
+
|
|
303
|
+
An item is **active** (unprocessed) only when BOTH `done` is false AND
|
|
304
|
+
`filed` is false. `done` items were cleared; `filed` items already live
|
|
305
|
+
in pib-db (an action or feedback) — resurfacing either is exactly the
|
|
306
|
+
write-only-telemetry bug this duty exists to kill. Collect each active
|
|
307
|
+
item's `text`, `created_at`, `origin`, and its **global active-list
|
|
308
|
+
position** (1-based index among active items — the position
|
|
309
|
+
`manage-dx.py` addresses when draining). Resolve each item's `origin` to
|
|
310
|
+
a walk project where you can: labels like `"claude-cabinet"` or `"flow"`
|
|
311
|
+
map to a project chunk; labels like `"session-observation"` or
|
|
312
|
+
`"unknown"` do not — those are portfolio-level (Step 4). If there are
|
|
313
|
+
zero active items, skip silently. (This was orient's job; post-orient
|
|
314
|
+
nothing reads these unless the briefing does — see
|
|
315
|
+
`lesson_write_only_telemetry_wire_a_reader`.)
|
|
316
|
+
|
|
317
|
+
## Step 2: Top Brief
|
|
318
|
+
|
|
319
|
+
Ten lines or fewer — the whole portfolio in one glance, curated. Not a
|
|
320
|
+
fixed template: lead with the one or two things that genuinely matter
|
|
321
|
+
today (a gate waiting, an urgent item, a fresh advisor finding from
|
|
322
|
+
last session's close, a thread left mid-flight, a ring down), then the
|
|
323
|
+
compact vitals:
|
|
145
324
|
|
|
146
|
-
[If any urgent queue items exist, list them here — one line each.]
|
|
147
|
-
[If any resolved qa-handoff verdicts carry still-open filed_gaps, add:
|
|
148
|
-
**QA debt:** N gaps filed by past QA verdicts still open — act:fid (gap), one line each.]
|
|
149
325
|
```
|
|
326
|
+
## Briefing — [scope, in words]
|
|
150
327
|
|
|
151
|
-
|
|
152
|
-
- **ok** — last successful run within expected cadence (Ring 1: 5 min,
|
|
153
|
-
Ring 2 fast: 15 min, Ring 2 slow: 6 hours, Ring 3: since last session end)
|
|
154
|
-
- **stale** — last run exceeded 2x expected cadence
|
|
155
|
-
- **down** — health file missing or last run was a failure
|
|
156
|
-
|
|
157
|
-
## Step 3: Progressive Disclosure (Full mode only)
|
|
158
|
-
|
|
159
|
-
In **factual/recap mode, STOP here.** The summary is the deliverable.
|
|
160
|
-
|
|
161
|
-
In **full mode**, offer drill-down using `AskUserQuestion`:
|
|
162
|
-
|
|
163
|
-
- `header`: `"Briefing"`
|
|
164
|
-
- `question`: `"What would you like to drill into?"`
|
|
165
|
-
- `options`:
|
|
166
|
-
- Sessions — "Review processed sessions in detail"
|
|
167
|
-
- Queue — "Walk through pending inbox items"
|
|
168
|
-
- Threads — "Active work threads and their cursors"
|
|
169
|
-
- Health — "Ring health details and diagnostics"
|
|
170
|
-
- Portfolio — "Per-project deep dive"
|
|
171
|
-
- Looks good — "Nothing to drill into, move on"
|
|
172
|
-
- `multiSelect: false`
|
|
173
|
-
|
|
174
|
-
### Sessions drill-down
|
|
175
|
-
|
|
176
|
-
List each session processed within scope: project, date, duration, and
|
|
177
|
-
the Ring 3 summary. Group by project if multiple projects are in scope.
|
|
178
|
-
|
|
179
|
-
### Threads drill-down
|
|
180
|
-
|
|
181
|
-
Present all active threads sorted by `last_updated` (most recent first).
|
|
182
|
-
For each thread, show:
|
|
183
|
-
|
|
184
|
-
- Thread slug and `cursor.what`
|
|
185
|
-
- `cursor.where_left_off` (the understanding that survives across sessions)
|
|
186
|
-
- `cursor.open_questions` if any
|
|
187
|
-
- `cursor.next_steps` if any
|
|
188
|
-
- Session count and last updated date
|
|
189
|
-
|
|
190
|
-
If a project filter is active, show only threads touching that project,
|
|
191
|
-
with the primary thread (most recently touched by a session in that
|
|
192
|
-
project) expanded with full cursor detail, and other threads as one-line
|
|
193
|
-
summaries.
|
|
194
|
-
|
|
195
|
-
If there are dormant threads (status: "dormant"), mention the count but
|
|
196
|
-
don't list them unless the user asks.
|
|
197
|
-
|
|
198
|
-
### Queue drill-down
|
|
199
|
-
|
|
200
|
-
Present pending queue items sorted by urgency (urgent first), then by
|
|
201
|
-
age (oldest first). For each item, show: id, title, category, urgency,
|
|
202
|
-
age, summary. If the item has enrichment (`enrichment_status: complete`),
|
|
203
|
-
note that enrichment is available.
|
|
204
|
-
|
|
205
|
-
After presenting, offer to open the `/inbox` skill if the user
|
|
206
|
-
wants to act on queue items.
|
|
207
|
-
|
|
208
|
-
### Health drill-down
|
|
209
|
-
|
|
210
|
-
For each ring, show: last run timestamp, success/failure history,
|
|
211
|
-
items processed, any error messages from the health file. If a ring
|
|
212
|
-
is stale or down, suggest diagnostic steps.
|
|
213
|
-
|
|
214
|
-
### Portfolio drill-down
|
|
215
|
-
|
|
216
|
-
Present the full project state file for each project in scope. If the
|
|
217
|
-
user specified a project filter, show only that project. Otherwise,
|
|
218
|
-
list projects and let the user pick one.
|
|
219
|
-
|
|
220
|
-
After any drill-down completes, offer the drill-down menu again (loop)
|
|
221
|
-
until the user selects "Looks good."
|
|
222
|
-
|
|
223
|
-
## Step 4: Cabinet Lens
|
|
224
|
-
|
|
225
|
-
**Only activates** when the user selects "Portfolio" drill-down with
|
|
226
|
-
scope "all" — meaning they want the broadest view.
|
|
227
|
-
|
|
228
|
-
Discover cabinet members with `standing-mandate` that includes `orient`
|
|
229
|
-
(same discovery mechanism as /orient's cabinet consultation phase: read
|
|
230
|
-
`.claude/skills/_index.json`, filter by `standingMandate` containing
|
|
231
|
-
`"orient"`).
|
|
232
|
-
|
|
233
|
-
Spawn each matching member with a constrained task: **ONE LINE
|
|
234
|
-
observation** about the portfolio state gathered in Step 1. No full
|
|
235
|
-
audit, no detailed analysis. The constraint is load-bearing — without
|
|
236
|
-
it, cabinet members produce pages of analysis that bury the briefing's
|
|
237
|
-
signal.
|
|
238
|
-
|
|
239
|
-
Collect responses and present them after the portfolio detail:
|
|
328
|
+
[1-3 curated headline lines — what actually deserves attention today.]
|
|
240
329
|
|
|
330
|
+
**Since [last briefing/time]:** N sessions across M projects. Last: [project] ([when]).
|
|
331
|
+
**Inbox:** N pending across M projects ([K urgent, G gated]). [Expiry note if items expired.]
|
|
332
|
+
**Health:** one line — "all rings ok" when true; name only the stale/down ones otherwise.
|
|
241
333
|
```
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
334
|
+
|
|
335
|
+
In **factual/recap mode, stop here** (after Step 5's timestamp). The
|
|
336
|
+
summary is the deliverable.
|
|
337
|
+
|
|
338
|
+
In **full mode**, continue straight into the walk — no menu, no
|
|
339
|
+
ceremony. One transition sentence: name the chunk order and why
|
|
340
|
+
("Three projects have items waiting; starting with X — it has the
|
|
341
|
+
gate.").
|
|
342
|
+
|
|
343
|
+
## Step 3: The Project Walk
|
|
344
|
+
|
|
345
|
+
One project at a time, in curated order: lead with the chunks the
|
|
346
|
+
operator can actually act on from this session — the current desk and
|
|
347
|
+
any project with dispositionable (ungated, this-desk) items — then
|
|
348
|
+
projects with pending gated items, then urgent items, then by thread
|
|
349
|
+
recency and pending count; quiet projects last (and a project with
|
|
350
|
+
nothing to say gets one line or silence — absence of news is news the
|
|
351
|
+
operator doesn't need). A project filter scopes the walk to that
|
|
352
|
+
project. **Why actionable-first:** a cross-desk gated item can only be
|
|
353
|
+
"left queued for its own desk" from here — it is read-only from this
|
|
354
|
+
session, so surfacing it (and a wall of another desk's state) before
|
|
355
|
+
the operator's own decisions just front-loads context they must hold.
|
|
356
|
+
Surface it, but later. A gated item is never dropped (floor #1); it is
|
|
357
|
+
only re-ordered.
|
|
358
|
+
|
|
359
|
+
**Decisions are never interleaved.** Each project's chunk completes —
|
|
360
|
+
narrative, attention items, dispositions executed — before the next
|
|
361
|
+
project is named. Cross-project observations wait for Step 4.
|
|
362
|
+
|
|
363
|
+
Items in the `project_unresolved` group form a final chunk of their
|
|
364
|
+
own, presented explicitly as "unresolved project" with each item's
|
|
365
|
+
stored fallback `project` value inline — never blended into real
|
|
366
|
+
project chunks, never silently dropped (fail-loud contract shared
|
|
367
|
+
with `/inbox`).
|
|
368
|
+
|
|
369
|
+
Each chunk has three movements:
|
|
370
|
+
|
|
371
|
+
### 3a. State, in plain English
|
|
372
|
+
|
|
373
|
+
Two to four sentences synthesized from the project's threads, Last
|
|
374
|
+
Session, and standing issues — all already in hand from the Step 1d
|
|
375
|
+
cross-ring view (no second read; its parsed sections, session bullets,
|
|
376
|
+
and cursor timelines are the input). Concept-first — "the publish
|
|
377
|
+
pipeline is built but has never run against a live tag" — not file
|
|
378
|
+
paths and fids. This
|
|
379
|
+
is the operator-facing register (`cabinet/skill-output-conventions.md`
|
|
380
|
+
§9) as the briefing's whole default; keep it that way. Offer nothing
|
|
381
|
+
here; drill-down happens by the operator asking.
|
|
382
|
+
|
|
383
|
+
### 3b. Curated attention (top-N selection)
|
|
384
|
+
|
|
385
|
+
Select **at most five** attention items for this project. This selection
|
|
386
|
+
step is where ALL "briefing should show X" duties land — see the
|
|
387
|
+
invariant. The weighable **candidate classes** (source · freshness/decay ·
|
|
388
|
+
floor-or-not) — a new duty adds an entry here, never a section:
|
|
389
|
+
|
|
390
|
+
- **Gated items** (`qa-handoff`) · pending in queue · **floor #1, always
|
|
391
|
+
included** (specific handling below).
|
|
392
|
+
- **Urgent-urgency items** · pending · weigh high.
|
|
393
|
+
- **Moment-is-now items** · a `routine` still within `stale_after_hours`,
|
|
394
|
+
a deferred trigger that fired · weigh high while fresh (handling below).
|
|
395
|
+
- **Open QA filed-gaps** belonging to this project · until the `act:` fid
|
|
396
|
+
closes · floor in Step 4, weighable here.
|
|
397
|
+
- **Advisor signals** · fresh `advisor-finding` items + live panel
|
|
398
|
+
observations · weigh fresh near the top, then decay (sources below).
|
|
399
|
+
- **Backlog-hygiene nudge** · the per-project state file's Standing Issues
|
|
400
|
+
shows ≥3 stale project(s) OR ≥2 completion candidate(s) (Ring 1 renders
|
|
401
|
+
these counts) · one line max, "backlog showing cruft — consider
|
|
402
|
+
/spring-clean" · decays once the counts fall; never its own section.
|
|
403
|
+
- **Environment advisories** · `suggested` entries in
|
|
404
|
+
`.claude/cabinet/advisories-state.json` (the watchtower context builder's
|
|
405
|
+
advisory pass already wrote them) for this chunk's project · one line
|
|
406
|
+
each, weigh low — they self-quiet after two surfacings · no probe here,
|
|
407
|
+
just read the surfacing state.
|
|
408
|
+
- **Verify signals** · shipped-uncovered warnings · weigh as they arrive.
|
|
409
|
+
- **State-file flags** · anything in Standing Issues that needs a human.
|
|
410
|
+
- **Unprocessed DX captures** · active items in mux's global `dx.json`
|
|
411
|
+
(1h) whose `origin` resolves to this chunk's project · weigh fresh,
|
|
412
|
+
then decay like any aged item — one line each, with a process/dismiss
|
|
413
|
+
path so the list actually drains (handling below) · not a floor:
|
|
414
|
+
captures are freely drainable and dismissable. Captures whose origin
|
|
415
|
+
resolves to no walk project go to the Step 4 close — never silently
|
|
416
|
+
dropped.
|
|
417
|
+
|
|
418
|
+
Everything else stays in the batch counts below.
|
|
419
|
+
|
|
420
|
+
Advisor signals come from two sources, both weighed here:
|
|
421
|
+
|
|
422
|
+
- **Fresh close-pass findings** — pending `advisor-finding` items filed
|
|
423
|
+
since the last briefing (Ring 3's session advisor pass, or an
|
|
424
|
+
/orient / /debrief consultation that persisted an actionable
|
|
425
|
+
finding — same channel). These are
|
|
426
|
+
the re-surfacing path the close pass promises: weigh a fresh one near
|
|
427
|
+
the top of this project's selection so it surfaces at the briefing
|
|
428
|
+
after the session that produced it, then let it decay into batch
|
|
429
|
+
fodder like anything else. Stale advisor findings are ordinary
|
|
430
|
+
ungated items — legal batch material in 3c.
|
|
431
|
+
- **Live panel observations** (Step 1g) that named this project.
|
|
432
|
+
|
|
433
|
+
Three categories get specific handling when selected:
|
|
434
|
+
|
|
435
|
+
- **Gated items (`qa-handoff`):** present what merged and what hangs
|
|
436
|
+
on it, then offer exactly two paths — run the recipient gate now in
|
|
437
|
+
this session (the qa-handoff skill's "The recipient gate" section is
|
|
438
|
+
the playbook; the stamped verdict is the only exit), or leave it
|
|
439
|
+
queued for its desk's QA station. Never fold a gated item into any
|
|
440
|
+
batch; never dismiss it to tidy the count (the lib will throw, by
|
|
441
|
+
design).
|
|
442
|
+
- **Routine items:** a declared routine fired and wasn't picked up at
|
|
443
|
+
its desk. If its moment is still live (within `stale_after_hours`),
|
|
444
|
+
offer to run its `evidence.script` conversation here and resolve
|
|
445
|
+
`acted-on`. If the moment passed (yesterday's morning briefing),
|
|
446
|
+
propose it for the stale batch below — routine items are dispatched
|
|
447
|
+
but not gated, and the engine supersedes stale ones anyway.
|
|
448
|
+
- **DX captures:** write-only until something drains them. Surface each
|
|
449
|
+
active capture as one plain line (the idea · when · where it came
|
|
450
|
+
from) and offer exactly two paths, one decision at a time — **process**
|
|
451
|
+
it (act on it now, or file it to pib-db as an action or feedback, then
|
|
452
|
+
mark it filed) or **dismiss** it as no-longer-relevant. Both paths
|
|
453
|
+
drain the list through mux's own tooling — single source of truth,
|
|
454
|
+
never hand-edit `dx.json`:
|
|
455
|
+
|
|
456
|
+
```bash
|
|
457
|
+
# dismiss (clear) a capture — n is its position in the active list
|
|
458
|
+
python3 ~/.config/mux/manage-dx.py done <origin> <n>
|
|
459
|
+
# after filing a capture to pib-db, mark it filed (drops it from the panel)
|
|
460
|
+
python3 ~/.config/mux/manage-dx.py mark-filed <origin> <n>
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
`<n>` is the global active-list position, and positions renumber as
|
|
464
|
+
items leave — when draining several, clear from the **highest position
|
|
465
|
+
down** (or re-read positions between marks) so an earlier removal can't
|
|
466
|
+
shift the index out from under you. `<origin>` is context only; the
|
|
467
|
+
number addresses the global active list regardless of origin.
|
|
468
|
+
|
|
469
|
+
### 3c. Batch dispositions
|
|
470
|
+
|
|
471
|
+
**First, the sign-off batch — the SAVE direction (M4, act:ffae4eec).**
|
|
472
|
+
Before any decay clustering, partition this project's ungated pending
|
|
473
|
+
items with `partitionForBatchSignoff` (the lib owns the predicate). Its
|
|
474
|
+
`signoff` set is the high-confidence pre-drafted knowledge-extraction
|
|
475
|
+
items (non-empty `draft_artifact`, not urgent, not low-confidence) —
|
|
476
|
+
work the rings already did that needs a nod, not a judgment. Offer the
|
|
477
|
+
whole set as ONE sign-off decision ("the background system drafted these
|
|
478
|
+
N memory entries; approving writes them to memory — sign off on all?").
|
|
479
|
+
On approval, write each `draft_artifact` to memory (`/cc-remember` or a
|
|
480
|
+
direct write), THEN one `applyBatch` resolve with
|
|
481
|
+
`resolution_type: 'captured-to-memory'`. This is the throughput valve:
|
|
482
|
+
M1/M2 raise inflow and success = filed × approval-rate (the M5 canary
|
|
483
|
+
tracks it), so a fast sign-off keeps drafts from ageing out
|
|
484
|
+
unadjudicated. Pulled-out items get per-item `/inbox` treatment after
|
|
485
|
+
the walk; everything else falls to the decay flow below — still one
|
|
486
|
+
decision at a time, the sign-off batch presented first.
|
|
487
|
+
|
|
488
|
+
**Then, decay clustering.** Cluster this project's REMAINING pending
|
|
489
|
+
items into **at most three
|
|
490
|
+
proposed batches** by theme — category × age × thread is the usual
|
|
491
|
+
axis. For each batch, propose a disposition in plain English with the
|
|
492
|
+
reasoning visible. Decay lives here: the curator's default proposal
|
|
493
|
+
for an aged, ungated cluster is dismissal-as-stale ("these 14 are
|
|
494
|
+
knowledge-extraction drafts from May; the threads they fed have moved
|
|
495
|
+
on — archive?"), always scoped to this project, never a global sweep.
|
|
496
|
+
A batch proposal must name: what's in it (count + a few example
|
|
497
|
+
titles), the proposed disposition, and the typed reason that will land
|
|
498
|
+
on every item.
|
|
499
|
+
|
|
500
|
+
Ask for approval **one decision at a time** — never a stack of
|
|
501
|
+
questions under a wall of context, never a same-turn dialog. Two
|
|
502
|
+
failure modes, both observed live: a same-turn dialog eclipses the
|
|
503
|
+
reasoning above it, so the operator approves logic they cannot see
|
|
504
|
+
(recurred 2026-06-13); and a long narrative followed by several
|
|
505
|
+
questions at the end forces the operator to hold every batch's
|
|
506
|
+
rationale in their head at once, which they cannot (recurred
|
|
507
|
+
2026-06-14). **The decision/context structure rule:** the only thing
|
|
508
|
+
that should require working memory is the single decision in front of
|
|
509
|
+
the operator, with its reason immediately beside it.
|
|
510
|
+
|
|
511
|
+
The shape: present **one batch** — what's in it (count + a few example
|
|
512
|
+
titles), the proposed disposition, the typed reason — then a one-line
|
|
513
|
+
question ("Dismiss these 14 as stale? y / n / show me") and the turn
|
|
514
|
+
STOPS. The operator answers in prose. Apply it, confirm in one line,
|
|
515
|
+
then present the next batch the same way. Pure read-only material —
|
|
516
|
+
portfolio state for desks the operator isn't on, platform notes,
|
|
517
|
+
anything needing no decision — is delivered AFTER all decisions are
|
|
518
|
+
made; it costs nothing to hold, so it never sits above a decision.
|
|
519
|
+
Reserve `AskUserQuestion` only for a self-contained choice whose
|
|
520
|
+
options stand on their own inside the dialog (e.g. a layout pick with
|
|
521
|
+
previews); batch dispositions never qualify.
|
|
522
|
+
|
|
523
|
+
Execute approved batches immediately via the queue lib, then say what
|
|
524
|
+
happened in one line each and move to the next project:
|
|
525
|
+
|
|
526
|
+
```bash
|
|
527
|
+
node --input-type=module -e "
|
|
528
|
+
import { applyBatch, partitionForBatchSignoff, listPending } from './scripts/watchtower-queue.mjs';
|
|
529
|
+
// Sign-off batch (M4): partitionForBatchSignoff(listPending({project})).signoff
|
|
530
|
+
// → resolve 'captured-to-memory' (write each draft_artifact FIRST). Decay='dismiss'.
|
|
531
|
+
const r = applyBatch([/* ids */], {
|
|
532
|
+
disposition: 'dismiss', // or 'resolve' (the sign-off batch)
|
|
533
|
+
resolution_type: 'stale', // typed reason — required ('captured-to-memory' for sign-off)
|
|
534
|
+
notes: '<the reason shown to the operator, verbatim>',
|
|
535
|
+
});
|
|
536
|
+
console.log(JSON.stringify(r));
|
|
537
|
+
"
|
|
245
538
|
```
|
|
246
539
|
|
|
540
|
+
`applyBatch` pre-validates the whole batch and throws before writing
|
|
541
|
+
anything if a gated item slipped in — the carve-out is structural, not
|
|
542
|
+
etiquette. Choose `resolution_type` honestly per batch: `stale`
|
|
543
|
+
(overtaken by events), `noise` (shouldn't have been filed),
|
|
544
|
+
`captured-to-memory` / `acted-on` for resolve batches — these feed the
|
|
545
|
+
trust-ladder ratios. Items the operator pulls out of a batch get the
|
|
546
|
+
`/inbox` per-item treatment (offer it after the walk, not mid-chunk).
|
|
547
|
+
|
|
548
|
+
## Step 4: Cross-Portfolio Close
|
|
549
|
+
|
|
550
|
+
The only non-project section, and it is curated like everything else:
|
|
551
|
+
include a part only when it has signal.
|
|
552
|
+
|
|
553
|
+
- **Cross-cutting synthesis** — patterns spanning projects (the same
|
|
554
|
+
friction filed from two desks, a theme across threads, a decision
|
|
555
|
+
made in one project that answers a question open in another). The
|
|
556
|
+
substrate is the Step 1d cross-ring portfolio view — synthesize from
|
|
557
|
+
its per-project views (git-attention facts across projects,
|
|
558
|
+
`resolution_mix` ratios, thread/session convergence) instead of
|
|
559
|
+
re-deriving from raw stores. Its honesty fields belong here too, in
|
|
560
|
+
plain words when notable: "N inbox items couldn't be attributed to
|
|
561
|
+
any project", "one active thread matches no configured project".
|
|
562
|
+
The synthesis itself stays live — the reader gathers, this session
|
|
563
|
+
thinks.
|
|
564
|
+
- **Standing debts** — floor #2: while any QA filed-gaps remain open,
|
|
565
|
+
one roll-up line each ("X still owes the migration test filed by
|
|
566
|
+
last week's QA — act:fid"). Same treatment for unmerged-worktree
|
|
567
|
+
warnings surfaced by the state files.
|
|
568
|
+
- **Advisor panel, portfolio-level** — the Step 1g observations that
|
|
569
|
+
span projects rather than belonging to one chunk land here (a
|
|
570
|
+
historian precedent that answers an open question, a platform delta
|
|
571
|
+
from the insider, a legibility flag from the user-advocate). One line
|
|
572
|
+
each, only members who said something — never a roll call.
|
|
573
|
+
- **Unprocessed DX captures, portfolio-level** — active `dx.json`
|
|
574
|
+
captures (1h) whose `origin` resolved to no walk project land here,
|
|
575
|
+
one line each, with the same process/dismiss drain path as the
|
|
576
|
+
per-project handling (3b). Only when some exist; never a standing
|
|
577
|
+
section.
|
|
578
|
+
|
|
579
|
+
Close with one sentence: dispositions applied this run (N resolved, M
|
|
580
|
+
dismissed, across K projects), what remains pending, and — only if
|
|
581
|
+
something is genuinely waiting on the operator — the single next thing.
|
|
582
|
+
|
|
247
583
|
## Step 5: Record Timestamp
|
|
248
584
|
|
|
249
|
-
After the briefing completes (
|
|
585
|
+
After the briefing completes (any mode), atomically write
|
|
250
586
|
`~/.claude-cabinet/watchtower/state/last-briefing.json`:
|
|
251
587
|
|
|
252
588
|
```json
|
|
@@ -255,42 +591,54 @@ After the briefing completes (regardless of mode), write
|
|
|
255
591
|
"scope": "<the scope that was used>",
|
|
256
592
|
"mode": "full" | "recap",
|
|
257
593
|
"sessions_covered": <count>,
|
|
258
|
-
"queue_pending_at_time": <count
|
|
594
|
+
"queue_pending_at_time": <count>,
|
|
595
|
+
"dispositions": { "resolved": <n>, "dismissed": <n> }
|
|
259
596
|
}
|
|
260
597
|
```
|
|
261
598
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
599
|
+
## Edge Cases
|
|
600
|
+
|
|
601
|
+
- No watchtower install (`~/.claude-cabinet/watchtower/` missing):
|
|
602
|
+
say so and point at `/watchtower install`. Don't improvise state.
|
|
603
|
+
- Queue lib missing at `scripts/watchtower-queue.mjs` (project without
|
|
604
|
+
the watchtower module): deliver the read-only briefing from global
|
|
605
|
+
state and say dispositions need a watchtower-installed project.
|
|
606
|
+
- Corrupt/unparseable item files: skip with a one-line warning count.
|
|
607
|
+
- No mux DX store (`~/.local/share/mux/dx.json` missing, or mux not
|
|
608
|
+
installed): skip the DX-capture candidate silently — a mux-owned
|
|
609
|
+
store's absence is normal, not an error.
|
|
610
|
+
- An `applyBatch` throw is a stop-and-show, not a retry loop: name the
|
|
611
|
+
item that tripped the gate, pull it into per-item handling, re-offer
|
|
612
|
+
the rest as a new batch.
|
|
265
613
|
|
|
266
614
|
## Calibration
|
|
267
615
|
|
|
268
|
-
**Core failure this targets:**
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
616
|
+
**Core failure this targets:** attention flow-control. The background
|
|
617
|
+
rings file faster than item-by-item triage drains, so the inbox grows
|
|
618
|
+
until the operator stops looking — and the things that DO need eyes
|
|
619
|
+
(a QA gate, a fired routine, a dying ring) drown in batchable noise.
|
|
272
620
|
|
|
273
621
|
### Without Skill (Bad)
|
|
274
622
|
|
|
275
|
-
The
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
Twenty minutes of manual archaeology to answer "what happened while I
|
|
283
|
-
was gone?" — and they still miss the urgent queue item filed by Ring 2
|
|
284
|
-
because it was in a subdirectory they didn't check.
|
|
623
|
+
The operator returns after a day. The inbox shows 174 pending. They
|
|
624
|
+
open `/inbox`, review four items one at a time, lose patience, and
|
|
625
|
+
close it. The qa-handoff filed yesterday sits unworked behind 50
|
|
626
|
+
knowledge-extraction drafts. Next week the inbox shows 230 and the
|
|
627
|
+
operator has stopped running anything at all — the surfaces produced
|
|
628
|
+
signal but no decision throughput.
|
|
285
629
|
|
|
286
630
|
### With Skill (Good)
|
|
287
631
|
|
|
288
|
-
The
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
632
|
+
The operator says "/briefing". Ten lines: one gate waiting on
|
|
633
|
+
claude-cabinet, two rings ok one stale, 174 pending of which ~150 are
|
|
634
|
+
three stale batches. The walk starts with claude-cabinet: state in two
|
|
635
|
+
sentences, the gate presented with its two paths, then "these 40
|
|
636
|
+
knowledge drafts predate the threads they fed — archive?" Three
|
|
637
|
+
plain-prose approvals later, 120 items are dispositioned with typed
|
|
638
|
+
reasons, the gate is queued deliberately for the QA station, and the
|
|
639
|
+
close notes one cross-project pattern worth a future action. Eight
|
|
640
|
+
minutes, decisions made where the operator already was — and nothing
|
|
641
|
+
gated or filed-as-debt was waved away in the bulk.
|
|
642
|
+
|
|
643
|
+
Or the operator just wants the facts — "/recap" delivers the summary
|
|
644
|
+
and stops.
|