get-claudia 1.62.0 → 1.64.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/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  All notable changes to Claudia will be documented in this file.
4
4
 
5
+ ## 1.64.0 (2026-06-13)
6
+
7
+ ### Added
8
+
9
+ - **Status-only wrap for daemon scheduled jobs** (Proposal 11, E5). The seven background jobs (importance decay, pattern detection, consolidation, daily and weekly backups, vault sync, observation ingest) now each write `~/.claudia/loops/<job>_status.md` and check deterministic invariants (the backup jobs assert the backup file exists and is non-empty). It is status-only: a failed invariant is flagged but never halts the job, and a job that raises is recorded and then re-raised, so daemon behavior is unchanged. `memory.system_health` and the `/status` endpoint now surface each job's last verdict plus a flagged count. New module `claudia_memory/loops/job_wrapper.py` (6 tests); health surfacing has 2 tests; full daemon suite stays green (803 passed). This completes Proposal 11.
10
+
11
+ ## 1.63.0 (2026-06-13)
12
+
13
+ ### Added
14
+
15
+ - **`/build-team` skill** (Proposal 11, E6). A user-invoked counterpart to `hire-agent`: it reads the user's profile, judgment rules, and real task history, then proposes a minimal, tailored agent team in one pass. The proposal runs through the independent `loop-checker` (scored against goal alignment, right-sizing, and a "progressive, not overwhelming" hard constraint, bounded to 2 revisions), is written to a `team_status.md` control file, and is gated on explicit user approval. On approval it scaffolds agent definitions and writes `.bak` siblings for any modified file so the change is reversible. Reuses the existing roster first and never auto-spawns agents or takes external actions. New files: `template-v2/.claude/skills/build-team/SKILL.md` (+ `skill-index.json` entry).
16
+ - **Proactive team review** (Proposal 11, E7). `hire-agent` now distinguishes "add one agent" from "the whole team has drifted." When it detects an archetype shift, a new recurring task class the roster does not cover, or two-plus unused agents, it gently suggests reviewing the team as a unit and routes to `/build-team` for the validated, approval-gated, reversible change. One suggestion at a time; never auto-applied; a review can shrink a team as readily as grow it.
17
+
5
18
  ## 1.62.0 (2026-06-13)
6
19
 
7
20
  ### Loop engineering foundation (Proposal 11, Phases 1-2)
@@ -152,6 +152,34 @@ def build_status_report(*, db=None) -> dict:
152
152
  except Exception:
153
153
  report["components"]["scheduler"] = "error"
154
154
 
155
+ # Loop status files (Proposal 11, E5): last verdict per wrapped daemon job.
156
+ # Purely observational; the overall status is not changed by a flagged job.
157
+ try:
158
+ from ..loops.job_wrapper import default_loops_dir
159
+ from ..loops.status import read_status
160
+
161
+ loops = []
162
+ loops_dir = default_loops_dir()
163
+ if loops_dir.exists():
164
+ for status_file in sorted(loops_dir.glob("*_status.md")):
165
+ try:
166
+ fields, _ = read_status(status_file)
167
+ except Exception:
168
+ continue
169
+ loops.append(
170
+ {
171
+ "job": fields.get("loop_id", status_file.stem.replace("_status", "")),
172
+ "verified": fields.get("verified"),
173
+ "verdict": fields.get("checker_verdict"),
174
+ "updated_at": fields.get("updated_at"),
175
+ }
176
+ )
177
+ report["loops"] = loops
178
+ report["loops_flagged"] = sum(1 for entry in loops if entry.get("verified") is False)
179
+ except Exception:
180
+ report["loops"] = []
181
+ report["loops_flagged"] = 0
182
+
155
183
  return report
156
184
 
157
185
 
@@ -23,10 +23,24 @@ from ..services.consolidate import (
23
23
  run_full_consolidation,
24
24
  )
25
25
  from ..services.vault_sync import run_vault_sync
26
+ from ..loops.job_wrapper import run_with_status
26
27
 
27
28
  logger = logging.getLogger(__name__)
28
29
 
29
30
 
31
+ def _file_nonempty(path) -> "tuple[bool, str]":
32
+ """Invariant: a backup path must exist and be a non-empty file."""
33
+ try:
34
+ p = Path(path)
35
+ if not p.exists():
36
+ return False, f"backup file missing: {p}"
37
+ if p.stat().st_size == 0:
38
+ return False, f"backup file is empty: {p}"
39
+ return True, ""
40
+ except Exception as e: # noqa: BLE001
41
+ return False, f"could not stat backup: {e!r}"
42
+
43
+
30
44
  # Tools whose invocations are always relevant for Claudia's memory
31
45
  RELEVANT_TOOL_PREFIXES = {
32
46
  "gmail", "google_workspace", "slack", "telegram",
@@ -282,72 +296,99 @@ class MemoryScheduler:
282
296
  """Run importance decay daily"""
283
297
  try:
284
298
  logger.debug("Running daily decay")
285
- result = run_decay()
299
+ result = run_with_status(
300
+ "daily_decay",
301
+ run_decay,
302
+ invariants=[("completed", lambda r: (r is not None, "decay returned no result"))],
303
+ )
286
304
  logger.debug(f"Daily decay complete: {result}")
287
- except Exception as e:
305
+ except Exception:
288
306
  logger.exception("Error in daily decay")
289
307
 
290
308
  def _run_pattern_detection(self) -> None:
291
309
  """Run pattern detection"""
292
310
  try:
293
311
  logger.debug("Running pattern detection")
294
- patterns = detect_patterns()
312
+ patterns = run_with_status(
313
+ "pattern_detection",
314
+ detect_patterns,
315
+ invariants=[("is_list", lambda r: (isinstance(r, (list, tuple)), "patterns not a list"))],
316
+ )
295
317
  logger.info(f"Pattern detection complete: {len(patterns)} patterns detected")
296
- except Exception as e:
318
+ except Exception:
297
319
  logger.exception("Error in pattern detection")
298
320
 
299
321
  def _run_full_consolidation(self) -> None:
300
322
  """Run full overnight consolidation"""
301
323
  try:
302
324
  logger.info("Running full consolidation")
303
- result = run_full_consolidation()
325
+ result = run_with_status(
326
+ "full_consolidation",
327
+ run_full_consolidation,
328
+ invariants=[("completed", lambda r: (r is not None, "consolidation returned no result"))],
329
+ )
304
330
  logger.info(f"Full consolidation complete: {result}")
305
- except Exception as e:
331
+ except Exception:
306
332
  logger.exception("Error in full consolidation")
307
333
 
308
334
  def _run_daily_backup(self) -> None:
309
335
  """Create a labeled daily backup with 7-day retention."""
310
336
  try:
311
337
  from ..database import get_db
312
- backup_path = get_db().backup(label="daily")
338
+ backup_path = run_with_status(
339
+ "daily_backup",
340
+ lambda: get_db().backup(label="daily"),
341
+ invariants=[("backup_nonempty", lambda p: _file_nonempty(p))],
342
+ )
313
343
  logger.info(f"Daily backup created: {backup_path}")
314
- except Exception as e:
344
+ except Exception:
315
345
  logger.exception("Error in daily backup")
316
346
 
317
347
  def _run_weekly_backup(self) -> None:
318
348
  """Create a labeled weekly backup with 4-week retention."""
319
349
  try:
320
350
  from ..database import get_db
321
- backup_path = get_db().backup(label="weekly")
351
+ backup_path = run_with_status(
352
+ "weekly_backup",
353
+ lambda: get_db().backup(label="weekly"),
354
+ invariants=[("backup_nonempty", lambda p: _file_nonempty(p))],
355
+ )
322
356
  logger.info(f"Weekly backup created: {backup_path}")
323
- except Exception as e:
357
+ except Exception:
324
358
  logger.exception("Error in weekly backup")
325
359
 
326
360
  def _run_vault_sync(self) -> None:
327
361
  """Run Obsidian vault sync + canvas regeneration"""
328
362
  try:
329
- logger.info("[Safety-net full sync] Running after 4R Reweave inline in consolidation")
330
363
  logger.info("Running vault sync")
331
364
  from ..config import _project_id
332
365
  from ..services.vault_sync import get_vault_path
333
366
  from ..services.canvas_generator import CanvasGenerator
334
367
 
335
- result = run_vault_sync(project_id=_project_id)
336
- logger.info(f"Vault sync complete: {result}")
337
-
338
- # Regenerate canvases after sync
339
- vault_path = get_vault_path(_project_id)
340
- gen = CanvasGenerator(vault_path)
341
- canvas_result = gen.generate_all()
342
- logger.info(f"Canvas regeneration complete: {canvas_result}")
343
- except Exception as e:
368
+ def _sync_and_canvas():
369
+ result = run_vault_sync(project_id=_project_id)
370
+ logger.info(f"Vault sync complete: {result}")
371
+ vault_path = get_vault_path(_project_id)
372
+ canvas_result = CanvasGenerator(vault_path).generate_all()
373
+ logger.info(f"Canvas regeneration complete: {canvas_result}")
374
+ return result
375
+
376
+ run_with_status(
377
+ "vault_sync",
378
+ _sync_and_canvas,
379
+ invariants=[("completed", lambda r: (r is not None, "vault sync returned no result"))],
380
+ )
381
+ except Exception:
344
382
  logger.exception("Error in vault sync")
345
383
 
346
384
  def _run_observation_ingest(self) -> None:
347
385
  """Ingest observations from PostToolUse hook captures."""
348
386
  try:
349
387
  from ..database import get_db
350
- _ingest_observations(get_db(), self.config)
388
+ run_with_status(
389
+ "observation_ingest",
390
+ lambda: _ingest_observations(get_db(), self.config),
391
+ )
351
392
  except Exception as e:
352
393
  logger.debug(f"Error in observation ingestion: {e}")
353
394
 
@@ -0,0 +1,106 @@
1
+ """Status-only job wrapper for daemon scheduled jobs (Proposal 11, E5).
2
+
3
+ Wraps a scheduled job so it writes a status file and flags invariant failures,
4
+ without ever changing the job's behavior. This is the "status-only" form of the
5
+ daemon wrap: a failed invariant is recorded but does NOT halt the job, and a job
6
+ that raises is recorded and then re-raised so the daemon's existing error
7
+ handling is preserved.
8
+
9
+ Each invariant is a ``(name, check)`` pair where ``check(result)`` returns
10
+ ``(ok: bool, detail: str)``. A check may inspect the job's return value or
11
+ external state (a backup file on disk, an entity count); it is deterministic, not
12
+ an LLM (see Proposal 11 Decision D2: the daemon has no agent context).
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from datetime import datetime, timezone
18
+ from pathlib import Path
19
+ from typing import Any, Callable, Iterable
20
+
21
+ from claudia_memory.loops.status import write_status
22
+
23
+ Invariant = tuple[str, Callable[[Any], "tuple[bool, str]"]]
24
+
25
+
26
+ def default_loops_dir() -> Path:
27
+ """Where daemon job status files live."""
28
+ return Path.home() / ".claudia" / "loops"
29
+
30
+
31
+ def _now_iso() -> str:
32
+ return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
33
+
34
+
35
+ def run_with_status(
36
+ job_id: str,
37
+ fn: Callable[[], Any],
38
+ invariants: Iterable[Invariant] = (),
39
+ status_dir: "str | Path | None" = None,
40
+ now: "str | None" = None,
41
+ ) -> Any:
42
+ """Run ``fn``, check ``invariants``, write ``<job_id>_status.md``, return result.
43
+
44
+ Status-only contract:
45
+ - A failed invariant flags the run (``verified: false``) but does not halt it;
46
+ the job's result is still returned.
47
+ - A job that raises is recorded as unverified and then re-raised, so the
48
+ daemon's existing error handling is unchanged.
49
+ """
50
+ base = Path(status_dir) if status_dir is not None else default_loops_dir()
51
+
52
+ error: BaseException | None = None
53
+ result: Any = None
54
+ try:
55
+ result = fn()
56
+ except Exception as e: # noqa: BLE001 - recorded below, then re-raised
57
+ error = e
58
+
59
+ checks: list[tuple[str, bool, str]] = []
60
+ if error is None:
61
+ for name, check in invariants:
62
+ try:
63
+ ok, detail = check(result)
64
+ except Exception as e: # noqa: BLE001 - a raising check is a failed check
65
+ ok, detail = False, f"invariant raised: {e!r}"
66
+ checks.append((name, bool(ok), str(detail)))
67
+
68
+ verified = error is None and all(ok for _, ok, _ in checks)
69
+ summary, body = _format_verdict(job_id, error, checks)
70
+
71
+ write_status(
72
+ base / f"{job_id}_status.md",
73
+ {
74
+ "loop_id": job_id,
75
+ "verified": verified,
76
+ "checker_verdict": summary,
77
+ "next_action": "none" if verified else "review flagged job run",
78
+ "updated_at": now or _now_iso(),
79
+ },
80
+ body=body,
81
+ )
82
+
83
+ if error is not None:
84
+ raise error
85
+ return result
86
+
87
+
88
+ def _format_verdict(job_id: str, error: "BaseException | None", checks) -> "tuple[str, str]":
89
+ if error is not None:
90
+ summary = f"job raised {type(error).__name__}: {error}"
91
+ return summary, f"# Loop status: {job_id}\n\n{summary}"
92
+
93
+ failed = [(name, detail) for name, ok, detail in checks if not ok]
94
+ if not checks:
95
+ summary = "ran; no invariants defined"
96
+ elif not failed:
97
+ summary = f"all {len(checks)} invariant(s) held"
98
+ else:
99
+ names = ", ".join(name for name, _ in failed)
100
+ summary = f"{len(failed)} of {len(checks)} invariant(s) failed: {names}"
101
+
102
+ lines = [f"# Loop status: {job_id}", "", summary]
103
+ if failed:
104
+ lines.append("")
105
+ lines.extend(f"- {name}: {detail}" for name, detail in failed)
106
+ return summary, "\n".join(lines)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "get-claudia",
3
- "version": "1.62.0",
3
+ "version": "1.64.0",
4
4
  "description": "An AI assistant who learns how you work.",
5
5
  "keywords": [
6
6
  "claudia",
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "1.62.0",
3
- "generated": "2026-06-14T02:37:03.060Z",
2
+ "version": "1.64.0",
3
+ "generated": "2026-06-14T03:04:35.117Z",
4
4
  "algorithm": "sha256",
5
5
  "files": {
6
6
  ".claude/rules/claudia-principles.md": "939e9720421628e7f2e4c8dfbaa4aeb9c1e18e8c6a5379cd6b772a6835b812e5",
@@ -26,6 +26,7 @@
26
26
  ".claude/skills/auto-research/references/safety-rules.md": "ae036270e86949ed0f9bface582f6038276adafd3f40a29aa733698111a26683",
27
27
  ".claude/skills/brain/SKILL.md": "b87dd93c85923c676292bea67f7a0580ee71eea2bea05426ea6eda951a140d5c",
28
28
  ".claude/skills/brain-monitor/SKILL.md": "6e9ce1e6e40bc612a5126d84d743c1fd7ebc596f60575873533479fdcae5b608",
29
+ ".claude/skills/build-team/SKILL.md": "38a33c8f3e84d0b27f4a4a6ac10cc8318e684b0611a9052a1dd47acbd26874d9",
29
30
  ".claude/skills/capability-suggester.md": "06235c8ab062d356cf9b6c18c7aa44ea667e7694983a46e8ae0a9fe32e734944",
30
31
  ".claude/skills/capture-meeting/SKILL.md": "815e7587fcd925323c379a6c57e458b3484d034338c1237378641d142c126175",
31
32
  ".claude/skills/capture-meeting/evals/basic.yaml": "47c4eb1479aeda8067bc877cebfe8688c9b2bb3ce09297e42375cdf558f412e3",
@@ -44,7 +45,7 @@
44
45
  ".claude/skills/fix-duplicates/SKILL.md": "bd3d6aae0bce09bc844ba93f76abb4a97ff0286c6a4da29cd369763864fd4a86",
45
46
  ".claude/skills/follow-up-draft/SKILL.md": "49212946cb7b2db39cecd435ab6fcb7cb26cc49666306084d27676543be67281",
46
47
  ".claude/skills/growth-check/SKILL.md": "a3ed3f2427f3639f81d16b1a2adfa268bafed795d2b0d02621bc6be318784c24",
47
- ".claude/skills/hire-agent.md": "2d60d241592db7d5f30a635d11164802f907b02616fb25170f3f867d5235f067",
48
+ ".claude/skills/hire-agent.md": "8f46632d6c9b02563a41c00c811e1bcd365614bb5e64d770482f3536fa30e6fe",
48
49
  ".claude/skills/inbox-check/SKILL.md": "e2cddc91f72ffc0443d105385d2e8b464f86404a4825d308bd03a68e4ee43726",
49
50
  ".claude/skills/ingest-sources/SKILL.md": "938b93d299649db156ad19e9a7bd5d09e950f53bc2eaf8a85386f9fdbca350d0",
50
51
  ".claude/skills/ingest-sources/references/extraction-patterns.md": "28d6dc604c4a11eacfe4523e705ad430ba8fe26632bafcccaef656e9618e5aea",
@@ -69,7 +70,7 @@
69
70
  ".claude/skills/research/SKILL.md": "0d317e6350b043ee774916b923957f61dbc174e3caafc0187198fa236a2fd28d",
70
71
  ".claude/skills/research/references/source-evaluation.md": "64614b7eff83468d7ff76dd640252579f23e69e760969672e9aebe5ceb00f695",
71
72
  ".claude/skills/risk-surfacer.md": "b01e6bc7a22df414a7ee7a5e4a108088b5d049646723cc7513fac27a3ba4856c",
72
- ".claude/skills/skill-index.json": "043c86f1b07f28bb54db80117437f60fb3d79ad0d18549fdc2c74921b996a56f",
73
+ ".claude/skills/skill-index.json": "576ebd98fe5f597a45871366d286cd6765d6bdabefe49633c2d00514518c54bc",
73
74
  ".claude/skills/skill-router/SKILL.md": "12fc45aebc94d3ead65843cd336a88797a40856e82908cda47256c9cbb4cb60f",
74
75
  ".claude/skills/skill-router/references/overlap-clusters.md": "bc819b4892cbdf9919db392825230f4cd4eec546cd7f0c9de6c29a2468ab489a",
75
76
  ".claude/skills/structure-generator.md": "dbe70841ab60599a632687aaa3c3652361483df2fe854640c56982b60622eb19",
@@ -0,0 +1,167 @@
1
+ ---
2
+ name: build-team
3
+ description: Propose a personalized team of specialized agents based on the user's profile, goals, and how they actually work. Runs the proposal through an independent Checker, gates on the user's approval, and applies with rollback. Use when the user says "build my team", "set up my agents", "what agents should I have", "design my team", "/build-team", or asks Claudia to tailor her team to their work. See also: `hire-agent` for proactive single-agent suggestions; `structure-generator` for folder scaffolding; `capability-suggester` for command-level additions.
4
+ argument-hint: "[optional: focus area, e.g. 'around client work']"
5
+ invocation: explicit
6
+ effort-level: high
7
+ ---
8
+
9
+ # Build Team
10
+
11
+ The user-invoked counterpart to `hire-agent`. Where `hire-agent` suggests one
12
+ agent reactively when it spots a repeated pattern, `build-team` looks at the
13
+ whole picture (the user's profile, judgment rules, and real task history) and
14
+ proposes a tailored team in one pass. It is a Maker-Checker flow (Proposal 11,
15
+ E6): Claudia proposes, an independent Checker validates, the user approves, and
16
+ only then is anything written.
17
+
18
+ ## What this is, and is not
19
+
20
+ - It **proposes and, on approval, scaffolds** agent definitions. It never
21
+ auto-spawns agents or takes external actions.
22
+ - It **reuses the existing roster first** (the agents in `.claude/agents/`) and
23
+ adds a new role only when the user's work clearly justifies one.
24
+ - It **starts minimal.** A small team that covers the real work beats a sprawling
25
+ org chart the user will never use. Growth comes later, from `hire-agent` and
26
+ the proactive team-update suggestions (Proposal 11, E7).
27
+ - It does **not** duplicate `agent-dispatcher` routing logic, and it does not
28
+ change how Claudia delegates. It only proposes which agents exist.
29
+
30
+ ## When to fire
31
+
32
+ Trigger phrases: "build my team", "set up my agents", "what agents should I
33
+ have", "design my team", "/build-team", "tailor your team to my work".
34
+
35
+ Also the landing spot when a proactive suggestion (from `capability-suggester`
36
+ or `hire-agent`) escalates from "add one agent" to "let's set up your whole
37
+ team".
38
+
39
+ Do NOT fire for: adding a single agent for one observed pattern (use
40
+ `hire-agent`), folder/structure changes (use `structure-generator`), or
41
+ command/workflow additions (use `capability-suggester`).
42
+
43
+ ## The flow
44
+
45
+ ### Step 1: Read the profile
46
+
47
+ Gather, silently:
48
+
49
+ - `context/me.md`: role, archetype, priorities, the shape of a typical week.
50
+ - `context/judgment.yaml` (if it exists): priorities, delegation preferences,
51
+ surfacing rules. A user who said "just auto-process transcripts" wants a
52
+ processing agent; a user who said "always run things by me" wants a smaller,
53
+ more ask-first team.
54
+ - Recent task patterns: what the user has actually asked Claudia to do
55
+ repeatedly (lean on the same signals `hire-agent` uses, plus `memory_recall`
56
+ for recurring work). The team should map to real work, not a generic template.
57
+
58
+ If `context/me.md` does not exist, the user has not onboarded. Route to
59
+ onboarding first; do not propose a team into a vacuum.
60
+
61
+ ### Step 2: Maker proposes the team
62
+
63
+ Claudia (the Maker) drafts a team:
64
+
65
+ 1. Start from the **minimal seed for the user's archetype** (see the seed table
66
+ below).
67
+ 2. Adjust to the user's actual work: drop a seed role they will not use, add a
68
+ role a recurring task clearly needs.
69
+ 3. For each role, write a one-line **rationale tied to the user's real work**
70
+ ("You process client transcripts most weeks, so a Document Processor handles
71
+ the extraction while I keep the judgment"). A role with no concrete rationale
72
+ does not belong in the proposal.
73
+ 4. Every new (non-roster) role must pass the `hire-agent` candidate test:
74
+ compute-heavy, judgment-light, structured in and out, repeatable. If it needs
75
+ relationship context or would take external actions, it is not an agent.
76
+
77
+ ### Step 3: The Checker validates (bounded)
78
+
79
+ Dispatch the `loop-checker` agent with the team proposal as the `artifact` and
80
+ the team rubric below. The Checker scores independently and returns the standard
81
+ verdict JSON (`verified`, `score`, `issues`, `hard_constraint_violated`).
82
+
83
+ If the Checker returns `verified: false` (most often: too large, or a role that
84
+ needs judgment), the Maker **revises once** based on the issues (usually by
85
+ trimming) and re-checks. Cap at **2 revisions**. This is the same bounded
86
+ Maker-Checker discipline every loop follows; the team proposal is not exempt.
87
+
88
+ **The team rubric:**
89
+
90
+ | Dimension | 10 | 0 |
91
+ |-----------|----|----|
92
+ | Goal alignment | Every role maps to a stated priority or a recurring task in the profile | Roles are generic, not tied to this user |
93
+ | Right-sized | The minimum team that covers the work (progressive, not overwhelming) | Sprawling; roles the user will not use |
94
+ | Judgment-safe | Every role is compute-heavy and judgment-light | A role would take external actions or need relationship judgment |
95
+ | Reuse-first | Uses existing roster agents where they fit; new roles only when justified | Invents roles that duplicate existing agents |
96
+ | Tier-correct | Haiku for structured extraction, Sonnet for multi-turn research | Mismatched model tiers |
97
+
98
+ **Hard constraints** (any one forces `verified: false`): more than 5 roles in a
99
+ first team, or any role that would require relationship judgment or take external
100
+ actions.
101
+
102
+ ### Step 4: Status file + approval gate
103
+
104
+ Write `team_status.md` in the standard schema (`docs/loop-status-schema.md`):
105
+ `last_input` (one-line profile summary), `maker_proposal` (the team), `score`,
106
+ `checker_verdict`, `verified`, `next_action: await user approval`. Write it to
107
+ the workspace, atomically (temp sibling then rename).
108
+
109
+ Then present the validated proposal to the user: each role, its model tier, and
110
+ its one-line rationale, plus what is reused vs new. **Write nothing to
111
+ `.claude/agents/` or `.claude/skills/` yet.** This is a Human-Approved action
112
+ (see `claudia-principles.md`). Ask plainly: "Want me to set this team up?"
113
+
114
+ ### Step 5: Apply and rollback (only after explicit approval)
115
+
116
+ On a clear yes:
117
+
118
+ 1. For each **new** role, scaffold `.claude/agents/<name>.md` using the agent
119
+ definition format from `hire-agent` (frontmatter: `name`, `description`,
120
+ `model`, `dispatch-category`, `dispatch-tier`, `auto-dispatch`; body: role,
121
+ job, output format, constraints).
122
+ 2. For any **existing** file you modify (for example, adding the team to
123
+ `.claude/agents/README.md`), write a `.bak` sibling first, the same
124
+ mechanism the installer upgrade flow uses. New files need no `.bak`.
125
+ 3. Report exactly what was created or changed, and how to undo it: "Rollback by
126
+ restoring the `.bak` files and deleting the new agent files, or just say
127
+ 'undo the team setup' and I will."
128
+ 4. Do not edit `agent-dispatcher.md` routing here. The new agents exist; routing
129
+ them is the dispatcher's job and can be tuned separately.
130
+
131
+ Rollback on request: restore each `.bak`, delete each newly created agent file,
132
+ and report what was reverted.
133
+
134
+ ## Minimal seed teams (Proposal 11, D5)
135
+
136
+ Starting points, not prescriptions. Every seed gets pruned and adjusted to the
137
+ user's actual work in Step 2. All roles are drawn from the existing roster.
138
+
139
+ | Archetype | Minimal seed |
140
+ |-----------|-------------|
141
+ | Consultant / Advisor | Document Archivist (intake), Document Processor (extract from client docs and transcripts), Research Scout (client and market research) |
142
+ | Executive / Manager | Document Processor (reports and decks), Schedule Analyst (calendar patterns), Research Scout |
143
+ | Founder / Entrepreneur | Research Scout (market and competitor), Document Processor (investor and product docs), Document Archivist |
144
+ | Solo Professional | Document Archivist, Document Processor |
145
+ | Content Creator | Research Scout (topic research), Document Processor (transcript and draft extraction), Canvas Generator (visual) |
146
+
147
+ Two or three roles is a healthy first team. Five is the ceiling, not the target.
148
+
149
+ ## Safety
150
+
151
+ - **Approval gate is non-negotiable.** Nothing is written to `agents/` or
152
+ `skills/` without an explicit yes (Step 4).
153
+ - **Reversible.** `.bak` siblings for any modified file; new files are
154
+ deletable; "undo the team setup" restores the prior state.
155
+ - **Judgment stays human.** Agents handle processing. Relationship judgment,
156
+ strategy, and external actions never get delegated to a generated agent.
157
+ - **Minimal by default.** When the Checker and the Maker disagree on size, the
158
+ smaller team wins. The user can always add more later.
159
+
160
+ ## See also
161
+
162
+ - `hire-agent` for proactive, single-agent suggestions from one observed pattern.
163
+ - `structure-generator` for the archetype folder structures this composes with.
164
+ - `capability-suggester` for command and workflow additions (not agents).
165
+ - `.claude/agents/README.md` for the current roster and the two-tier model.
166
+ - `.claude/skills/_loop/checker.md` for the Checker brief the validation uses.
167
+ - `docs/loop-status-schema.md` for the `team_status.md` format.
@@ -147,3 +147,49 @@ After creating a new agent, I monitor:
147
147
  If an agent isn't being used or consistently needs my intervention, I might suggest retiring it:
148
148
 
149
149
  "The LinkedIn Processor hasn't been used in 3 weeks. Want me to remove it to keep things simple?"
150
+
151
+ ## Proactive team review (Proposal 11, E7)
152
+
153
+ Single-agent suggestions (above) cover incremental growth. Sometimes the whole
154
+ team has drifted from how the user now works, and the right move is to review the
155
+ team as a unit, not bolt on one more agent.
156
+
157
+ ### Drift triggers
158
+
159
+ I raise a team review (not just a single hire) when I notice:
160
+
161
+ - **Archetype shift.** The user's `context/me.md` archetype has changed, or their
162
+ described work no longer matches it (a consultant who is now mostly building a
163
+ product).
164
+ - **A new recurring task class.** A whole category of repeated work has appeared
165
+ that the current roster does not cover, not just one task type.
166
+ - **Roster drift.** Two or more agents have gone unused for weeks while the user
167
+ keeps doing a kind of work by hand.
168
+
169
+ These are about the shape of the team, not a single gap. One missing agent is a
170
+ `hire-agent` suggestion; a team that no longer fits is a team review.
171
+
172
+ ### Suggest a diff, then route to /build-team
173
+
174
+ When a drift trigger fires, I suggest the change as a **team diff**, gently and
175
+ as one suggestion:
176
+
177
+ ```
178
+ "The way you work has shifted toward [X] over the last while. Your current team
179
+ was set up for [Y]. Want me to review the whole team? Roughly, I'd add [role],
180
+ retire [unused role], and keep the rest."
181
+ ```
182
+
183
+ If the user says yes, I route to `/build-team`, which does the real work: it reads
184
+ the current profile, proposes the adjusted team, validates it through the Checker,
185
+ shows it for approval, and applies it with `.bak` rollback. I do not re-implement
186
+ the proposal, validation, or apply logic here. This skill only notices the drift
187
+ and offers the review; `build-team` owns the change.
188
+
189
+ ### Discipline
190
+
191
+ - One suggestion, not a flood. If the user declines, I drop it and do not re-raise
192
+ until a new, distinct drift signal appears.
193
+ - Never auto-apply. A team review is always proposed, never performed silently.
194
+ - Minimal still wins. A review can shrink a team as readily as grow it; an unused
195
+ agent is friction, not value.
@@ -17,6 +17,21 @@
17
17
  "I have an idea for Claudia"
18
18
  ]
19
19
  },
20
+ {
21
+ "name": "build-team",
22
+ "path": "build-team/SKILL.md",
23
+ "description": "Propose a personalized team of specialized agents based on the user profile and real work, validated by an independent Checker, applied on approval with rollback.",
24
+ "invocation": "explicit",
25
+ "effort_level": "high",
26
+ "triggers": ["build my team", "set up my agents", "what agents should I have", "design my team", "/build-team"],
27
+ "examples": [
28
+ "build my team",
29
+ "what agents should I have for my work?",
30
+ "set up my agents",
31
+ "design a team around my client work",
32
+ "tailor your team to how I work"
33
+ ]
34
+ },
20
35
  {
21
36
  "name": "onboarding",
22
37
  "path": "onboarding.md",