Skip to content

Commit 9dfa1a7

Browse files
committed
feat: parallel KB queries, morning brief, self-assessment, cross-KB synthesis, autonomy config
Performance: kb query-all queries all registered KBs concurrently via asyncio.gather(), reducing pulse time from ~7 min to ~80s with 6 KBs. Pulse prompt updated to use kb query-all instead of sequential iteration. Proactive: morning brief (adjutant brief) compiles daily summary with deadlines, priorities, and KB status into a phone-readable notification. Self-assessment (adjutant self-assess) weekly introspection evaluating notification effectiveness and proposing priority/behaviour changes to insights/pending/ for user review. Cross-KB: kb cross-query queries multiple KBs in parallel then synthesizes a unified cross-domain answer via a synthesis prompt. Autonomy: graduated autonomy config (levels 1-4) with auto_approve and require_approval lists for fine-grained control over autonomous actions. 16 new tests, all 1358 existing tests still passing.
1 parent ddc5152 commit 9dfa1a7

14 files changed

Lines changed: 1077 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,17 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88
---
99

10-
## [Unreleased] — Post-0.1.0 Hardening
10+
## [0.2.0] — Unreleased — Post-0.1.0 Hardening
1111

1212
Comprehensive code quality, type safety, and security hardening pass driven by full deployment readiness audit.
1313

1414
### Added
1515

16+
- **Parallel KB queries**`adjutant kb query-all` queries all registered KBs concurrently via `asyncio.gather()`, reducing pulse time from ~7 min to ~80s with 6 KBs. Uses each KB's `query_hint` for targeted questions. (`capabilities/kb/query.py`, `cli.py`)
17+
- **Cross-KB synthesis**`adjutant kb cross-query "question" --kbs a,b` queries multiple KBs in parallel, then synthesizes a unified cross-domain answer via a synthesis prompt. (`capabilities/kb/query.py`, `cli.py`)
18+
- **Morning brief**`adjutant brief` proactive daily summary combining KB status, deadlines, pending insights, and priority alignment. Designed for phone readability (<800 chars). (`prompts/morning_brief.md`, `lifecycle/cron.py`, `cli.py`)
19+
- **Self-assessment**`adjutant self-assess` weekly introspection evaluating notification effectiveness, priority alignment, and KB health. Proposes changes to `insights/pending/` for user review — never modifies identity files directly. (`prompts/self_assess.md`, `lifecycle/cron.py`, `cli.py`)
20+
- **Graduated autonomy config**`autonomy` section in `adjutant.yaml` with levels 1-4 (notify-only → fully autonomous), `auto_approve` and `require_approval` lists. (`core/config.py`)
1621
- **Active operation tracking** — pulse and review write `state/active_operation.json` while running, allowing external clients to observe operation state without holding open connections. Staleness detection auto-cleans markers older than 30 minutes with dead PIDs. (`core/lockfiles.py`, `lifecycle/cron.py`)
1722
- **Post-completion notifications** — after a successful pulse or review, a Telegram notification is sent with a summary of KBs checked, issues found, and escalation status. Budget-guarded, best-effort. (`lifecycle/cron.py`)
1823
- Active operation markers for Telegram `/pulse` and `/reflect``/confirm` paths (`messaging/telegram/commands.py`)
@@ -143,7 +148,7 @@ Complete rewrite from bash to Python. New architecture, new capabilities, compre
143148

144149
- `.github/workflows/release.yml` — tag-triggered release workflow with tarball + checksum
145150
- `VERSION` file as source of truth
146-
- Docusaurus documentation site (`adjutant-docs`)
151+
- Docusaurus documentation site (`site/`)
147152

148153
---
149154

docs/guides/commands.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,15 @@ adjutant startup # Full startup, or recovery from KILLED state
9494
adjutant update # Self-update to the latest release
9595
```
9696

97+
### Autonomous cycles
98+
99+
```bash
100+
adjutant pulse # Run the quick autonomous pulse (queries all KBs)
101+
adjutant review # Run the deep autonomous review
102+
adjutant brief # Run the proactive morning brief (daily summary + deadlines)
103+
adjutant self-assess # Run the weekly self-assessment (evaluate + propose improvements)
104+
```
105+
97106
See [Lifecycle](lifecycle.md) for when to use each of these.
98107

99108
### Sending messages
@@ -117,6 +126,10 @@ adjutant kb create --quick \
117126
--desc "What this KB is about" # Quick non-interactive create (--desc, not --description)
118127
adjutant kb info <name> # Show details about a KB
119128
adjutant kb query <name> "question" # Query a KB
129+
adjutant kb query-all # Query ALL KBs in parallel (uses each KB's query_hint)
130+
adjutant kb query-all -q "question" # Query ALL KBs with a custom question
131+
adjutant kb cross-query "question" \
132+
--kbs ixda,fagkomite # Query multiple KBs and synthesize a cross-domain answer
120133
adjutant kb run <name> <operation> # Run a KB-local operation by convention
121134
adjutant kb remove <name> # Unregister a KB (files are NOT deleted)
122135
```
@@ -176,15 +189,16 @@ Run `adjutant doctor` to see the health of your installation:
176189
Adjutant Health Check
177190
=====================
178191
179-
Installation: /Users/you/.adjutant
192+
Installation: /Users/you/adjutant
180193
OS: Darwin
181194
182195
Dependencies:
183196
bash OK (GNU bash, version 5.2.15)
184197
curl OK (curl 8.4.0)
185198
jq OK (jq-1.7)
186199
python3 OK (Python 3.12.0)
187-
opencode OK (opencode 0.3.1)
200+
claude OK (/opt/homebrew/bin/claude) # or opencode, depending on llm.backend
201+
cloudcli OK (/opt/homebrew/bin/cloudcli) # claude-cli backend only
188202
189203
Optional:
190204
playwright not installed (needed for screenshot)
@@ -203,4 +217,6 @@ State:
203217
Listener: Running (PID 12345)
204218
```
205219

220+
The backend-specific binaries shown depend on `llm.backend` in `adjutant.yaml`. With `opencode`, doctor checks for the `opencode` binary. With `claude-cli`, it checks for `claude` and `cloudcli`.
221+
206222
If `adjutant doctor` reports missing dependencies or configuration, run `adjutant setup --repair` to fix them interactively.

docs/guides/lifecycle.md

Lines changed: 121 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,13 +140,13 @@ To start Adjutant automatically when you log in to your Mac:
140140
adjutant startup
141141
```
142142

143-
This installs a LaunchAgent plist at `~/Library/LaunchAgents/adjutant.telegram.plist`. The listener will start on every login without manual intervention.
143+
This installs a LaunchAgent plist at `~/Library/LaunchAgents/com.adjutant.telegram.plist`. The listener will start on every login without manual intervention.
144144

145145
To remove the LaunchAgent:
146146

147147
```bash
148-
launchctl unload ~/Library/LaunchAgents/adjutant.telegram.plist
149-
rm ~/Library/LaunchAgents/adjutant.telegram.plist
148+
launchctl unload ~/Library/LaunchAgents/com.adjutant.telegram.plist
149+
rm ~/Library/LaunchAgents/com.adjutant.telegram.plist
150150
```
151151

152152
---
@@ -180,7 +180,7 @@ adjutant restart
180180
adjutant doctor
181181
```
182182

183-
Checks that all required tools are installed (`bash`, `curl`, `jq`, `python3`, `opencode`), credentials are present in `.env`, identity files exist, and the listener state. Does not modify anything — read-only diagnostic.
183+
Checks that required tools are installed (`bash`, `curl`, `python3`, and your configured LLM backend binary), credentials are present in `.env`, identity files exist, and the listener state. Does not modify anything — read-only diagnostic.
184184

185185
---
186186

@@ -254,3 +254,120 @@ The notification includes:
254254
Notifications use the daily budget system (`notifications.max_per_day` in `adjutant.yaml`, default 3). If the budget is exhausted, the notification is silently skipped. The pulse/review itself still completes normally.
255255

256256
Note: when triggered from Telegram via `/pulse`, the notification is redundant since the results are already sent as a chat reply. Both are sent, but the daily budget prevents excess.
257+
258+
---
259+
260+
## Morning Brief
261+
262+
The morning brief is a proactive daily summary designed for the user, not for system health. It combines:
263+
264+
- Current priorities from `heart.md` with KB status updates
265+
- Deadlines in the next 7 days from all KBs
266+
- Unprocessed insights from `insights/pending/`
267+
- Overnight changes since the last pulse
268+
269+
```bash
270+
# Run manually
271+
adjutant brief
272+
273+
# Schedule via crontab (e.g., every weekday at 07:30)
274+
# In adjutant.yaml schedules:
275+
# - name: morning-brief
276+
# schedule: "30 7 * * 1-5"
277+
# script: "adjutant brief"
278+
# enabled: true
279+
```
280+
281+
The brief uses `kb query-all` internally to query all KBs in parallel, making it significantly faster than a sequential pulse. Output is sent as a single Telegram notification, kept under 800 characters for phone readability.
282+
283+
The brief differs from pulse in purpose: pulse is a system-level health check that writes to journal and escalates issues. The brief is a user-facing daily planner that tells you what to focus on today.
284+
285+
---
286+
287+
## Self-Assessment
288+
289+
The self-assessment is a weekly introspection cycle where Adjutant evaluates its own effectiveness and proposes improvements. It runs through `prompts/self_assess.md` and:
290+
291+
1. Reviews the past week's journal entries
292+
2. Analyzes notification outcomes (which were useful, which were noise)
293+
3. Compares `heart.md` priorities against actual activity
294+
4. Evaluates signal-to-noise ratio, coverage, and timing
295+
5. Proposes specific changes to priorities, notification frequency, and KB query patterns
296+
297+
```bash
298+
# Run manually
299+
adjutant self-assess
300+
301+
# Schedule via crontab (e.g., every Sunday at 20:00)
302+
# In adjutant.yaml schedules:
303+
# - name: weekly-self-assess
304+
# schedule: "0 20 * * 0"
305+
# script: "adjutant self-assess"
306+
# enabled: true
307+
```
308+
309+
**Safety:** The self-assessment never modifies `identity/soul.md` or `identity/heart.md` directly. All proposed changes are written to `insights/pending/self-assessment-YYYY-WNN.md` for the user to review and approve. This keeps the human in the loop for any behavioural changes.
310+
311+
---
312+
313+
## Autonomy Configuration
314+
315+
Adjutant supports graduated autonomy levels via the `autonomy` section in `adjutant.yaml`:
316+
317+
```yaml
318+
autonomy:
319+
level: 2 # 1=notify-only, 2=suggest+approve, 3=act+notify, 4=autonomous
320+
auto_approve:
321+
- kb_data_refresh # Actions that don't need approval at level 3+
322+
- memory_digest
323+
require_approval:
324+
- heart_update # Actions that always need approval, regardless of level
325+
- notification_rule
326+
```
327+
328+
| Level | Behaviour |
329+
|-------|-----------|
330+
| 1 | Notify-only — never act without explicit instruction |
331+
| 2 | Suggest and act on approval (default) — proposes changes, waits for confirmation |
332+
| 3 | Act and notify — performs actions autonomously, then tells you what it did |
333+
| 4 | Fully autonomous — acts silently, logs only |
334+
335+
The `auto_approve` list specifies actions that can proceed without user approval at level 3 and above. The `require_approval` list specifies actions that always need explicit approval, regardless of autonomy level.
336+
337+
---
338+
339+
## Parallel KB Queries
340+
341+
Pulse now uses `kb query-all` to query all registered KBs concurrently instead of sequentially. With 6 KBs at ~70 seconds each, this reduces pulse wall time from ~7 minutes to ~80 seconds.
342+
343+
The `kb query-all` command:
344+
- Reads all KB entries from `knowledge_bases/registry.yaml`
345+
- Uses each KB's `query_hint` field for targeted questions
346+
- Falls back to a generic status query if no hint is set
347+
- Runs all queries via `asyncio.gather()` for maximum concurrency
348+
- Returns a combined result with one section per KB
349+
350+
```bash
351+
# Query all KBs with default hints
352+
adjutant kb query-all
353+
354+
# Override with a custom question for all KBs
355+
adjutant kb query-all -q "What deadlines are in the next 7 days?"
356+
```
357+
358+
---
359+
360+
## Cross-KB Intelligence
361+
362+
The `kb cross-query` command enables multi-domain synthesis — asking questions that span multiple KBs:
363+
364+
```bash
365+
adjutant kb cross-query "Do I have any scheduling conflicts?" --kbs ixda,fagkomite
366+
```
367+
368+
This:
369+
1. Queries the specified KBs in parallel
370+
2. Feeds all responses into a synthesis prompt
371+
3. Returns a unified answer that identifies cross-domain connections and conflicts
372+
373+
Use this when you need insights that no single KB can provide alone.
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# Performance, Autonomy & Proactive Features Plan
2+
3+
**Date:** 2026-04-06
4+
**Status:** In progress
5+
6+
## Problem
7+
8+
Adjutant's pulse/review cycle queries all 6 KBs sequentially via LLM tool calls. Each KB query takes 60-80s, making a full pulse take 6-8 minutes. There is no cross-KB intelligence, no proactive daily briefing, no self-assessment loop, and no graduated autonomy control.
9+
10+
## Goals
11+
12+
- Reduce pulse wall time from ~7 min to ~90s via parallel KB queries
13+
- Add a morning brief prompt for daily proactive value
14+
- Add a self-assessment prompt for weekly introspection
15+
- Add cross-KB query capability for multi-domain synthesis
16+
- Add graduated autonomy configuration
17+
- Keep all changes generic and KB-agnostic
18+
19+
## Non-goals
20+
21+
- Embedding/vector store integration (future work)
22+
- Webhook triggers (future work)
23+
- Multi-channel messaging (future work)
24+
25+
## Issues
26+
27+
### Issue 1: Parallel KB queries — `kb query-all` command
28+
29+
**Status:** Done
30+
31+
**Problem:** Pulse prompt instructs the LLM agent to call `adjutant kb query` per KB sequentially. With 6 KBs at ~70s each, pulse takes ~7 minutes.
32+
33+
**Solution:** Add `kb_query_all()` async function that queries all registered KBs in parallel via `asyncio.gather()`, and a `kb query-all` CLI command. Update pulse prompt to call this once instead of iterating.
34+
35+
**Files changed:**
36+
37+
| File | Change |
38+
|------|--------|
39+
| `src/adjutant/capabilities/kb/query.py` | Add `kb_query_all()` — parallel query across all KBs |
40+
| `src/adjutant/cli.py` | Add `kb query-all` CLI command |
41+
| `prompts/pulse.md` | Use `kb query-all` instead of per-KB iteration |
42+
| `tests/unit/test_kb_query.py` | Tests for `kb_query_all()` |
43+
44+
**What was done:** Added `kb_query_all()` function that loads the KB registry, builds per-KB query strings (using `query_hint` if set), and runs all queries concurrently with `asyncio.gather()`. Returns a formatted multi-KB result string. Added `kb query-all` CLI command with `--query` option for custom query text. Updated `prompts/pulse.md` step 3 to call `adjutant kb query-all` once. Added 4 unit tests covering parallel dispatch, partial failure, empty registry, and custom query text.
45+
46+
### Issue 2: Morning brief prompt
47+
48+
**Status:** Done
49+
50+
**Problem:** Adjutant has no proactive daily communication. Pulse is a system-level health check, not a user-facing daily planner.
51+
52+
**Solution:** Add `prompts/morning_brief.md` — a user-facing daily brief combining deadlines, overnight KB changes, unread insights, and priority suggestions. Add `brief` CLI command and `brief_cron()` entry point.
53+
54+
**Files changed:**
55+
56+
| File | Change |
57+
|------|--------|
58+
| `prompts/morning_brief.md` | New prompt — daily brief for the user |
59+
| `src/adjutant/lifecycle/cron.py` | Add `brief_cron()` entry point |
60+
| `src/adjutant/cli.py` | Add `brief` CLI command |
61+
| `tests/unit/test_cron.py` | Tests for `brief_cron()` |
62+
63+
**What was done:** Created `morning_brief.md` prompt that queries all KBs via `kb query-all`, reads recent journal + insights, compiles deadlines, and produces a scannable Telegram-ready brief. Added `brief_cron()` and `brief` CLI command following the same pattern as pulse/review. Added test for `brief_cron()`.
64+
65+
### Issue 3: Self-assessment prompt
66+
67+
**Status:** Done
68+
69+
**Problem:** No feedback loop — adjutant never evaluates whether its own notifications were useful, its priorities are still accurate, or its behaviour should change.
70+
71+
**Solution:** Add `prompts/self_assess.md` — a weekly introspection prompt that reviews journal, notification outcomes, and memory, then proposes priority and behaviour adjustments.
72+
73+
**Files changed:**
74+
75+
| File | Change |
76+
|------|--------|
77+
| `prompts/self_assess.md` | New prompt — weekly self-assessment |
78+
| `src/adjutant/lifecycle/cron.py` | Add `self_assess_cron()` entry point |
79+
| `src/adjutant/cli.py` | Add `self-assess` CLI command |
80+
| `tests/unit/test_cron.py` | Tests for `self_assess_cron()` |
81+
82+
**What was done:** Created `self_assess.md` prompt that reviews past week's journal, notification outcomes from `state/actions.jsonl`, memory patterns, and heart.md alignment. Outputs proposed changes to `insights/pending/self-assessment-YYYY-WNN.md` for user review. Added `self_assess_cron()` and `self-assess` CLI command. Added test.
83+
84+
### Issue 4: Autonomy configuration
85+
86+
**Status:** Done
87+
88+
**Problem:** No way to control what adjutant can do autonomously vs. what requires user approval. Binary choice between running and being paused.
89+
90+
**Solution:** Add `autonomy` section to `adjutant.yaml` with a graduated level system and per-action overrides.
91+
92+
**Files changed:**
93+
94+
| File | Change |
95+
|------|--------|
96+
| `src/adjutant/core/config.py` | Add `AutonomyConfig` model |
97+
| `tests/unit/test_config.py` | Tests for new config section |
98+
99+
**What was done:** Added `AutonomyConfig` with `level` (1-4), `auto_approve` list, and `require_approval` list. Level 1 = notify-only, 2 = suggest+act-on-approval, 3 = act+notify, 4 = fully autonomous. Added to `AdjutantConfig`. Added tests for defaults and custom values.
100+
101+
### Issue 5: Cross-KB query command
102+
103+
**Status:** Done
104+
105+
**Problem:** KBs are isolated silos. No way to ask questions that span multiple domains (e.g., scheduling conflicts between IxDA events and Fagkomite conferences).
106+
107+
**Solution:** Add `kb_cross_query()` function and `kb cross-query` CLI command that queries multiple KBs in parallel, then feeds combined results into a synthesis prompt.
108+
109+
**Files changed:**
110+
111+
| File | Change |
112+
|------|--------|
113+
| `src/adjutant/capabilities/kb/query.py` | Add `kb_cross_query()` |
114+
| `src/adjutant/cli.py` | Add `kb cross-query` CLI command |
115+
| `tests/unit/test_kb_query.py` | Tests for `kb_cross_query()` |
116+
117+
**What was done:** Added `kb_cross_query()` that takes a list of KB names and a question, queries them in parallel, then runs a synthesis prompt through the backend to produce a unified answer. Added CLI command with `--kbs` option accepting comma-separated KB names. Added tests.
118+
119+
### Issue 6: Update documentation
120+
121+
**Status:** Done
122+
123+
**Files changed:**
124+
125+
| File | Change |
126+
|------|--------|
127+
| `docs/guides/commands.md` | Add new CLI commands |
128+
| `docs/guides/lifecycle.md` | Document morning brief and self-assessment cycles |
129+
| `CHANGELOG.md` | Add entries for all new features |
130+
131+
**What was done:** Updated commands guide with `kb query-all`, `kb cross-query`, `brief`, and `self-assess` commands. Updated lifecycle guide with morning brief and self-assessment cycles. Added changelog entries.
132+
133+
## Definition of done
134+
135+
- `kb query-all` queries all KBs in parallel, returns combined result
136+
- `kb cross-query` synthesizes answers across selected KBs
137+
- Morning brief prompt provides daily proactive value
138+
- Self-assessment prompt enables weekly introspection
139+
- Autonomy config gives graduated control
140+
- All new code has unit tests
141+
- All docs updated

0 commit comments

Comments
 (0)