brainiac-cli 0.16.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. brain/__init__.py +39 -0
  2. brain/__main__.py +14 -0
  3. brain/_assets/AGENTS.md +564 -0
  4. brain/_assets/overlay/template/brand/brand-guide.md +24 -0
  5. brain/_assets/overlay/template/keywords/glossary.md +15 -0
  6. brain/_assets/overlay/template/people/roster.md +14 -0
  7. brain/_assets/overlay/template/voice/voice-profile.md +34 -0
  8. brain/_assets/routines/manifest.json +257 -0
  9. brain/_assets/scripts/brain-brief-mac.plist +59 -0
  10. brain/_assets/scripts/brain-brief.sh +74 -0
  11. brain/_assets/scripts/brain-synthesis-mac.plist +48 -0
  12. brain/_assets/scripts/brain-synthesis.sh +214 -0
  13. brain/_assets/scripts/install-brief-mac.sh +152 -0
  14. brain/_assets/scripts/install-brief-windows.ps1 +97 -0
  15. brain/_assets/scripts/register_tasks.py +386 -0
  16. brain/_assets/templates/company.md +21 -0
  17. brain/_assets/templates/concept.md +27 -0
  18. brain/_assets/templates/daily.md +20 -0
  19. brain/_assets/templates/decision.md +42 -0
  20. brain/_assets/templates/meeting.md +33 -0
  21. brain/_assets/templates/person.md +20 -0
  22. brain/_assets/templates/project.md +23 -0
  23. brain/_assets/templates/state-moc.md +40 -0
  24. brain/_version.py +12 -0
  25. brain/anchor.py +121 -0
  26. brain/audit.py +422 -0
  27. brain/backup.py +210 -0
  28. brain/brief.py +417 -0
  29. brain/capture.py +117 -0
  30. brain/chunk.py +249 -0
  31. brain/classification.py +134 -0
  32. brain/cli.py +1906 -0
  33. brain/config.py +368 -0
  34. brain/connect.py +362 -0
  35. brain/context.py +108 -0
  36. brain/core.py +3018 -0
  37. brain/doctor.py +1161 -0
  38. brain/egress.py +148 -0
  39. brain/embed.py +857 -0
  40. brain/encryption.py +217 -0
  41. brain/frontmatter.py +102 -0
  42. brain/golden_probe.py +678 -0
  43. brain/graph.py +369 -0
  44. brain/graphify.py +352 -0
  45. brain/index.py +1576 -0
  46. brain/ingest/__init__.py +19 -0
  47. brain/ingest/handlers/__init__.py +43 -0
  48. brain/ingest/handlers/base.py +95 -0
  49. brain/ingest/handlers/docx.py +78 -0
  50. brain/ingest/handlers/email.py +228 -0
  51. brain/ingest/handlers/html.py +142 -0
  52. brain/ingest/handlers/image.py +91 -0
  53. brain/ingest/handlers/pdf.py +99 -0
  54. brain/ingest/handlers/pptx.py +69 -0
  55. brain/ingest/handlers/tables.py +41 -0
  56. brain/ingest/handlers/text.py +43 -0
  57. brain/ingest/handlers/xlsx.py +100 -0
  58. brain/ingest/handlers/zip.py +163 -0
  59. brain/ingest/pipeline.py +839 -0
  60. brain/ingest/transcript.py +158 -0
  61. brain/init.py +870 -0
  62. brain/maintenance.py +2266 -0
  63. brain/mcp_adapter.py +217 -0
  64. brain/multihop.py +232 -0
  65. brain/notes.py +195 -0
  66. brain/overlay.py +183 -0
  67. brain/projection.py +79 -0
  68. brain/rerank.py +425 -0
  69. brain/snapshot.py +231 -0
  70. brain/update.py +743 -0
  71. brain/vectors.py +225 -0
  72. brainiac_cli-0.16.0.dist-info/METADATA +306 -0
  73. brainiac_cli-0.16.0.dist-info/RECORD +77 -0
  74. brainiac_cli-0.16.0.dist-info/WHEEL +5 -0
  75. brainiac_cli-0.16.0.dist-info/entry_points.txt +4 -0
  76. brainiac_cli-0.16.0.dist-info/licenses/LICENSE +202 -0
  77. brainiac_cli-0.16.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,386 @@
1
+ #!/usr/bin/env python3
2
+ """register_tasks.py — idempotent cross-client scheduled-task registrar (CUT-04 / s07).
3
+
4
+ Reads routines/manifest.json and emits per-client registration steps for the
5
+ two task classes the persistence budget (routines/manifest.json locked_counts)
6
+ allows:
7
+
8
+ HOST leg — the ONE locked OS-scheduled task (`brain-nightly`, manifest id
9
+ "brain-nightly", command `brain maintain --json`). Registers via
10
+ macOS launchd or Windows Task Scheduler, reusing the existing
11
+ installer scripts. list-then-create-or-update: never blindly
12
+ re-registers without first checking what's there.
13
+
14
+ COWORK leg — the manifest's vm_eligible ON-INVOKE tasks (promotion-scan,
15
+ ingestion-digest-weekly's on-demand form). autoresearch-cascade
16
+ moved to host-only in AUT-04/s11 (needs the dev repo's eval/ +
17
+ src/, not something a Cowork VM session can run).
18
+ These are NEVER auto-firing: persistence-budget.md locks the
19
+ Cowork/VM OS-scheduled-task count at exactly 0, so this leg
20
+ never emits a cron_expression. It emits ONE paste-ready prompt
21
+ a human pastes into a Cowork chat session; the prompt itself
22
+ instructs the agent inside Cowork to register POKE-ONLY
23
+ triggers (list_scheduled_tasks -> create_scheduled_task only if
24
+ absent, else update_scheduled_task to adopt -- never
25
+ delete_scheduled_task, retire via enabled:false).
26
+
27
+ Default mode is --dry-run: read-only on the host (a `launchctl list` /
28
+ `schtasks` probe is harmless) and the Cowork leg is ALWAYS just printed text
29
+ -- this script has no way to reach Cowork's MCP tools from the Mac host, so
30
+ "apply" for that leg is "paste the printed prompt into a Cowork chat
31
+ yourself". --apply only changes behaviour for the HOST leg (it invokes the
32
+ existing idempotent installer script).
33
+
34
+ Usage:
35
+ python3 scripts/register_tasks.py --dry-run # default-safe report
36
+ python3 scripts/register_tasks.py --apply --client host # actually install
37
+ python3 scripts/register_tasks.py --dry-run --client cowork # just print the prompt
38
+ """
39
+ from __future__ import annotations
40
+
41
+ import argparse
42
+ import json
43
+ import platform
44
+ import shutil
45
+ import subprocess
46
+ import sys
47
+ from pathlib import Path
48
+
49
+ REPO_ROOT = Path(__file__).resolve().parent.parent
50
+ DEFAULT_MANIFEST = REPO_ROOT / "routines" / "manifest.json"
51
+
52
+ LEGACY_MAC_LABEL = "com.profile-a-brain.daily-brief" # pre-per-vault SHARED label; install-brief-mac.sh migrates away from it
53
+ WIN_TASK_NAME = "brain-daily-brief"
54
+ SYNTHESIS_MAC_LABEL = "com.brainiac.synthesis" # ONE per host (not per vault) — brain-synthesis.sh iterates every registered vault
55
+
56
+
57
+ def _vault_slug8(vault: str | None) -> str | None:
58
+ """8-hex per-vault id. Prefers brain.config.vault_slug8 (source of truth);
59
+ falls back to the same sha256(realpath)[:8] if brain isn't importable."""
60
+ if not vault:
61
+ return None
62
+ try:
63
+ from brain.config import vault_slug8
64
+ return vault_slug8(vault)
65
+ except Exception:
66
+ import hashlib
67
+ import os
68
+ return hashlib.sha256(
69
+ os.path.realpath(os.path.expanduser(vault)).encode()
70
+ ).hexdigest()[:8]
71
+
72
+
73
+ def mac_label(vault: str | None) -> str:
74
+ """Per-vault launchd label; matches brain.config.nightly_label."""
75
+ slug = _vault_slug8(vault)
76
+ return f"com.brainiac.nightly.{slug}" if slug else "com.brainiac.nightly.<BRAIN_VAULT-unset>"
77
+
78
+
79
+ def win_task_name(vault: str | None) -> str:
80
+ """Per-vault Windows task name (same clobber fix as the macOS label)."""
81
+ slug = _vault_slug8(vault)
82
+ return f"{WIN_TASK_NAME}-{slug}" if slug else WIN_TASK_NAME
83
+
84
+
85
+ def load_manifest(path: Path) -> dict:
86
+ with path.open() as f:
87
+ d = json.load(f)
88
+ locked = d.get("locked_counts", {})
89
+ # THE LOCK, amended 2026-07-11 (owner decision): host budget is 2 --
90
+ # brain-nightly (maintenance umbrella) + brain-synthesis (weekly
91
+ # model-backed kb-curator session). VM stays 0.
92
+ if locked.get("host_os_scheduled") != 2 or locked.get("vm_os_scheduled") != 0:
93
+ raise SystemExit(
94
+ "manifest locked_counts do not match persistence-budget.md THE LOCK "
95
+ f"(host=2, vm=0) -- got {locked}. Refusing to register against an "
96
+ "unratified budget. Amend routines/manifest.json locked_counts first."
97
+ )
98
+ return d
99
+
100
+
101
+ def find_task(manifest: dict, task_id: str) -> dict:
102
+ for t in manifest["tasks"]:
103
+ if t["id"] == task_id:
104
+ return t
105
+ raise SystemExit(f"manifest has no task id={task_id!r}")
106
+
107
+
108
+ # --------------------------------------------------------------------------
109
+ # HOST leg
110
+ # --------------------------------------------------------------------------
111
+
112
+ def host_probe_mac(label: str) -> tuple[bool, str]:
113
+ """Return (already_registered, detail) by listing this vault's per-vault
114
+ launchd label, read-only."""
115
+ if not shutil.which("launchctl"):
116
+ return False, "launchctl not found (not on macOS, or PATH issue)"
117
+ try:
118
+ out = subprocess.run(
119
+ ["launchctl", "list", label],
120
+ capture_output=True, text=True, timeout=10,
121
+ )
122
+ except Exception as exc: # pragma: no cover - defensive
123
+ return False, f"launchctl probe failed: {exc}"
124
+ if out.returncode == 0:
125
+ return True, out.stdout.strip() or "(label found, no detail)"
126
+ return False, f"label {label!r} not currently loaded"
127
+
128
+
129
+ def host_probe_windows(task_name: str) -> tuple[bool, str]:
130
+ """Return (already_registered, detail) by listing this vault's per-vault
131
+ Task Scheduler task, read-only."""
132
+ if not shutil.which("schtasks") and not shutil.which("powershell"):
133
+ return False, "schtasks/powershell not found (not on Windows, or PATH issue)"
134
+ cmd = ["schtasks", "/Query", "/TN", task_name] if shutil.which("schtasks") else [
135
+ "powershell", "-NonInteractive", "-Command",
136
+ f"Get-ScheduledTask -TaskName '{task_name}' -ErrorAction SilentlyContinue",
137
+ ]
138
+ try:
139
+ out = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
140
+ except Exception as exc: # pragma: no cover - defensive
141
+ return False, f"Task Scheduler probe failed: {exc}"
142
+ if out.returncode == 0 and out.stdout.strip():
143
+ return True, out.stdout.strip().splitlines()[0]
144
+ return False, f"task {task_name!r} not currently registered"
145
+
146
+
147
+ def synthesis_host_report(manifest: dict, system: str) -> dict:
148
+ """Probe/report the SECOND locked host task (brain-synthesis-weekly,
149
+ launchd label com.brainiac.synthesis). install-brief-mac.sh installs BOTH
150
+ tasks in one run, so there is no separate apply step here — this block
151
+ exists so a dry-run visibly accounts for the full host budget (THE LOCK,
152
+ host=2), never just the nightly."""
153
+ synthesis = find_task(manifest, "brain-synthesis-weekly")
154
+ rep: dict = {
155
+ "task_id": synthesis["id"],
156
+ "command": synthesis["command"],
157
+ }
158
+ if system == "Darwin":
159
+ rep["label"] = SYNTHESIS_MAC_LABEL
160
+ already, detail = host_probe_mac(SYNTHESIS_MAC_LABEL)
161
+ rep["already_registered"] = already
162
+ rep["probe_detail"] = detail
163
+ rep["install_script"] = str(REPO_ROOT / "scripts" / "install-brief-mac.sh")
164
+ rep["runtime_script"] = str(REPO_ROOT / "scripts" / "brain-synthesis.sh")
165
+ rep["note"] = "installed by the same install-brief-mac.sh run as brain-nightly"
166
+ else:
167
+ rep["already_registered"] = None
168
+ rep["probe_detail"] = (
169
+ f"no synthesis registration leg for host OS {system!r} yet "
170
+ "(macOS launchd only; run scripts/brain-synthesis.sh via your scheduler by hand)"
171
+ )
172
+ return rep
173
+
174
+
175
+ def register_host_leg(manifest: dict, *, apply: bool) -> dict:
176
+ nightly = find_task(manifest, "brain-nightly")
177
+ system = platform.system() # "Darwin" | "Windows" | "Linux"
178
+ report: dict = {
179
+ "task_id": nightly["id"],
180
+ "command": nightly["command"],
181
+ "detected_os": system,
182
+ "apply": apply,
183
+ # BOTH locked host tasks (host_os_scheduled=2): the nightly fields
184
+ # below keep their original flat shape; the weekly synthesis task is
185
+ # reported alongside so a dry-run accounts for the whole budget.
186
+ "synthesis": synthesis_host_report(manifest, system),
187
+ }
188
+
189
+ if system == "Darwin":
190
+ vault = subprocess_env_brain_vault()
191
+ label = mac_label(vault)
192
+ report["label"] = label
193
+ already, detail = host_probe_mac(label)
194
+ report["already_registered"] = already
195
+ report["probe_detail"] = detail
196
+ report["action"] = "update (re-install, picks up the new `brain maintain` body)" if already else "create"
197
+ report["install_script"] = str(REPO_ROOT / "scripts" / "install-brief-mac.sh")
198
+ if apply:
199
+ if not vault:
200
+ report["apply_result"] = "SKIPPED — BRAIN_VAULT not set; export BRAIN_VAULT=<path> and re-run --apply"
201
+ else:
202
+ proc = subprocess.run(
203
+ ["bash", report["install_script"]],
204
+ cwd=REPO_ROOT, env={**_os_environ(), "BRAIN_VAULT": vault},
205
+ capture_output=True, text=True, timeout=60,
206
+ )
207
+ report["apply_result"] = {
208
+ "exit_code": proc.returncode,
209
+ "stdout": proc.stdout.strip(),
210
+ "stderr": proc.stderr.strip(),
211
+ }
212
+ else:
213
+ report["apply_result"] = "DRY-RUN — would run: bash " + report["install_script"]
214
+
215
+ elif system == "Windows":
216
+ vault = subprocess_env_brain_vault()
217
+ task_name = win_task_name(vault)
218
+ report["label"] = task_name
219
+ already, detail = host_probe_windows(task_name)
220
+ report["already_registered"] = already
221
+ report["probe_detail"] = detail
222
+ report["action"] = "update (Register-ScheduledTask -Force is idempotent — re-running re-points the task at the new `brain maintain` body)" if already else "create"
223
+ report["install_script"] = str(REPO_ROOT / "scripts" / "install-brief-windows.ps1")
224
+ report["apply_result"] = (
225
+ "Run from an elevated-NOT-required PowerShell prompt (Windows leg cannot be "
226
+ "driven from this Mac host): "
227
+ f"powershell -File {report['install_script']} -VaultPath <path-to-vault>"
228
+ )
229
+
230
+ else:
231
+ report["already_registered"] = None
232
+ report["probe_detail"] = f"unsupported/undetected host OS for this leg: {system}"
233
+ report["action"] = "n/a"
234
+ report["apply_result"] = "SKIPPED — host leg targets macOS (launchd) or Windows (Task Scheduler) only"
235
+
236
+ return report
237
+
238
+
239
+ def subprocess_env_brain_vault() -> str | None:
240
+ import os
241
+ return os.environ.get("BRAIN_VAULT")
242
+
243
+
244
+ def _os_environ() -> dict:
245
+ import os
246
+ return dict(os.environ)
247
+
248
+
249
+ # --------------------------------------------------------------------------
250
+ # COWORK leg — paste-ready, idempotent, POKE-ONLY (never cron — budget=0)
251
+ # --------------------------------------------------------------------------
252
+
253
+ def cowork_task_block(task: dict) -> str:
254
+ trigger_name = f"brain-{task['id']}"
255
+ cmd = task["command"].strip()
256
+ return f"""### `{trigger_name}` — {task['id']}
257
+
258
+ **Manifest source:** routines/manifest.json id `{task['id']}` (disposition: {task['disposition']}, cadence: {task['cadence']})
259
+
260
+ Idempotent registration steps for this trigger:
261
+ 1. Call `list_scheduled_tasks` and search for a task/trigger named `{trigger_name}`.
262
+ 2. If ABSENT: call `create_scheduled_task` with name=`{trigger_name}`. Do **NOT**
263
+ set a cron/schedule expression — leave it poke-only / fire-on-demand. The
264
+ locked persistence budget (routines/manifest.json locked_counts, THE LOCK) caps
265
+ Cowork/VM OS-scheduled-task count at exactly **0**; this trigger exists only
266
+ so the prompt below can be re-fired by name instead of retyped.
267
+ 3. If PRESENT: call `update_scheduled_task` on the existing entry to adopt /
268
+ refresh its prompt body to the block below (do not create a duplicate).
269
+ 4. **Never call `delete_scheduled_task`.** To retire this trigger later, call
270
+ `update_scheduled_task(enabled=false)` instead.
271
+ 5. **#29022 caveat:** `create_scheduled_task` is sometimes not injected by the
272
+ MCP layer (silently no-ops). After step 2, re-run `list_scheduled_tasks`
273
+ and confirm `{trigger_name}` now appears. If it does not, fall back to the
274
+ Cowork Schedule UI and register the same prompt body manually.
275
+
276
+ **Trigger prompt body (what `{trigger_name}` runs when manually fired):**
277
+
278
+ ```
279
+ {cmd}
280
+ ```
281
+
282
+ PF-02 export-egress gate (docs/operations/egress-provider-posture.md) — already satisfied
283
+ by this block: every `brain` call above carries `--max-tier Internal`; no
284
+ personal names appear (role-title only); this is a read/draft operation, not a
285
+ `brain project`-style export, so no `brain snapshot` step is required before it
286
+ — but if you extend this trigger to ship results outside the Cowork session
287
+ (an email, a doc, a paste to another tool), run `brain snapshot` first and
288
+ record the gate evidence per export-egress-gate.md Step D before doing so.
289
+ """
290
+
291
+
292
+ def build_cowork_prompt(manifest: dict) -> str:
293
+ vm_tasks = [t for t in manifest["tasks"] if t.get("vm_eligible")]
294
+ blocks = "\n".join(cowork_task_block(t) for t in vm_tasks)
295
+ return f"""# Paste-ready Cowork registrar prompt — brain on-invoke VM tasks (s07 / TSK-04)
296
+
297
+ Paste this entire block into a Cowork chat session that has the
298
+ scheduled-tasks MCP tools available (`list_scheduled_tasks`,
299
+ `create_scheduled_task`, `update_scheduled_task`). It registers
300
+ {len(vm_tasks)} POKE-ONLY triggers — none of them auto-fire on a cron, so
301
+ this paste never increases the Cowork/VM OS-scheduled-task count above the
302
+ locked **0** (routines/manifest.json locked_counts). They exist purely so the
303
+ analyst can re-fire a named, idempotently-registered prompt instead of
304
+ retyping it.
305
+
306
+ Before registering anything, confirm `BRAIN_VAULT` (or the projected
307
+ `brain --vault` path the Cowork sandbox uses) is set in the trigger's own
308
+ command lines, not assumed from the session environment — Cowork sessions are
309
+ ephemeral and do not inherit a persistent shell profile.
310
+
311
+ For EACH of the {len(vm_tasks)} tasks below, run the 5-step idempotent
312
+ sequence (list -> create-if-absent / update-if-present -> never delete):
313
+
314
+ {blocks}
315
+ ---
316
+ **Summary you should report back after running this:** which of the
317
+ {len(vm_tasks)} triggers were CREATED vs UPDATED (adopted), and whether the
318
+ #29022 verify-after-create check passed for each, or required the Schedule UI
319
+ fallback.
320
+ """
321
+
322
+
323
+ # --------------------------------------------------------------------------
324
+ # main
325
+ # --------------------------------------------------------------------------
326
+
327
+ def main(argv: list[str] | None = None) -> int:
328
+ p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
329
+ p.add_argument("--manifest", default=str(DEFAULT_MANIFEST))
330
+ p.add_argument("--client", choices=["host", "cowork", "all"], default="all")
331
+ mode = p.add_mutually_exclusive_group()
332
+ mode.add_argument("--dry-run", action="store_true", help="default: report-only, no mutation (host leg probe is read-only; Cowork leg is always just printed text)")
333
+ mode.add_argument("--apply", action="store_true", help="HOST leg only: actually invoke the installer script. Cowork leg is unaffected (this script cannot reach Cowork's MCP tools)")
334
+ p.add_argument("--json", action="store_true", help="emit machine-readable JSON instead of human text")
335
+ p.add_argument("--save-cowork-prompt", default=None, help="also write the Cowork paste-prompt to this file path")
336
+ args = p.parse_args(argv)
337
+
338
+ apply = bool(args.apply) # dry-run is the default whenever --apply is absent
339
+
340
+ manifest = load_manifest(Path(args.manifest))
341
+
342
+ out: dict = {"manifest": args.manifest, "apply": apply, "client": args.client}
343
+
344
+ if args.client in ("host", "all"):
345
+ out["host"] = register_host_leg(manifest, apply=apply)
346
+
347
+ if args.client in ("cowork", "all"):
348
+ prompt = build_cowork_prompt(manifest)
349
+ out["cowork"] = {
350
+ "vm_eligible_tasks": [t["id"] for t in manifest["tasks"] if t.get("vm_eligible")],
351
+ "prompt": prompt,
352
+ }
353
+ if args.save_cowork_prompt:
354
+ dest = Path(args.save_cowork_prompt)
355
+ dest.parent.mkdir(parents=True, exist_ok=True)
356
+ dest.write_text(prompt)
357
+ out["cowork"]["saved_to"] = str(dest)
358
+
359
+ if args.json:
360
+ print(json.dumps(out, indent=2, default=str))
361
+ else:
362
+ print(f"=== register_tasks.py — mode={'APPLY' if apply else 'DRY-RUN'} client={args.client} ===\n")
363
+ if "host" in out:
364
+ h = out["host"]
365
+ print(f"-- HOST leg ({h['detected_os']}) --")
366
+ print(f" task: {h['task_id']} command: {h['command']}")
367
+ print(f" already_registered: {h.get('already_registered')} ({h.get('probe_detail')})")
368
+ print(f" action: {h.get('action')}")
369
+ print(f" apply_result: {h.get('apply_result')}")
370
+ s = h.get("synthesis")
371
+ if s:
372
+ print(f" task: {s['task_id']} command: {s['command']}")
373
+ print(f" already_registered: {s.get('already_registered')} ({s.get('probe_detail')})")
374
+ print()
375
+ if "cowork" in out:
376
+ c = out["cowork"]
377
+ print(f"-- COWORK leg — {len(c['vm_eligible_tasks'])} on-invoke task(s): {', '.join(c['vm_eligible_tasks'])} --")
378
+ print(c["prompt"])
379
+ if c.get("saved_to"):
380
+ print(f"(prompt also saved to {c['saved_to']})")
381
+
382
+ return 0
383
+
384
+
385
+ if __name__ == "__main__":
386
+ raise SystemExit(main())
@@ -0,0 +1,21 @@
1
+ ---
2
+ id: "{{id}}"
3
+ title: "{{title}}"
4
+ type: company
5
+ classification: Internal
6
+ created: "{{date}}"
7
+ updated: "{{date}}"
8
+ tags: []
9
+ industry: ""
10
+ related: []
11
+ ---
12
+
13
+ # {{title}}
14
+
15
+ ## Overview
16
+
17
+ ## Key People
18
+
19
+ ## Projects & Engagements
20
+
21
+ ## Notes
@@ -0,0 +1,27 @@
1
+ ---
2
+ id: "{{id}}"
3
+ title: "{{title}}"
4
+ type: concept
5
+ classification: Internal
6
+ created: "{{date}}"
7
+ updated: "{{date}}"
8
+ tags: []
9
+ domain: ""
10
+ related: []
11
+ ---
12
+
13
+ # {{title}}
14
+
15
+ ## Definition
16
+
17
+ ## Context & Application
18
+
19
+ ## Counter-Arguments
20
+
21
+ Reasons this concept might be wrong, incomplete, or context-dependent. A
22
+ concept note without this section is warn-flagged (TMP-05) — trustworthy
23
+ notes name their own limits.
24
+
25
+ ## Related Concepts
26
+
27
+ ## Sources
@@ -0,0 +1,20 @@
1
+ ---
2
+ id: "{{id}}"
3
+ title: "{{date}}"
4
+ type: daily
5
+ classification: Internal
6
+ created: "{{date}}"
7
+ updated: "{{date}}"
8
+ tags: []
9
+ related: []
10
+ ---
11
+
12
+ # {{date}}
13
+
14
+ ## Session Summary
15
+
16
+ ## Work Done
17
+
18
+ ## Open Threads
19
+
20
+ ## Next Session
@@ -0,0 +1,42 @@
1
+ ---
2
+ id: "{{id}}"
3
+ title: "Decision - {{title}}"
4
+ type: decision
5
+ classification: Internal
6
+ created: "{{date}}"
7
+ updated: "{{date}}"
8
+ tags: []
9
+ # Bitemporal frontmatter (ADR-0003 ruling 2, optional but recommended for
10
+ # type:decision): two clocks — document_date is when the decision was
11
+ # recorded, effective_date is when it applies in the world.
12
+ document_date: "{{date}}"
13
+ effective_date: "{{date}}"
14
+ context: ""
15
+ project: ""
16
+ stakeholders: []
17
+ # Lint (TMP-05, warn-only): a decision note must anchor its claim to a
18
+ # source — set `source:` to a raw/ wikilink, or cite one inline below.
19
+ source: ""
20
+ related: []
21
+ ---
22
+
23
+ # Decision - {{title}}
24
+
25
+ ## Context
26
+
27
+ Why this decision was needed.
28
+
29
+ ## Decision
30
+
31
+ What was decided.
32
+
33
+ ## Rationale
34
+
35
+ Why this option was chosen over alternatives. Anchor claims to sources, e.g.
36
+ `[[raw/2026-07-05-example-source]]`.
37
+
38
+ ## Consequences
39
+
40
+ What follows from this decision.
41
+
42
+ ## Alternatives Considered
@@ -0,0 +1,33 @@
1
+ ---
2
+ id: "{{id}}"
3
+ title: "{{date}} - {{title}}"
4
+ type: meeting
5
+ classification: Internal
6
+ created: "{{date}}"
7
+ updated: "{{date}}"
8
+ tags: []
9
+ # Bitemporal frontmatter (ADR-0003 ruling 2, optional): meetings rarely
10
+ # supersede each other, but document_date/is_latest_version anchor the note
11
+ # when they do.
12
+ document_date: "{{date}}"
13
+ is_latest_version: true
14
+ people: []
15
+ companies: []
16
+ projects: []
17
+ source: ""
18
+ related: []
19
+ ---
20
+
21
+ # {{date}} - {{title}}
22
+
23
+ ## Attendees
24
+
25
+ ## Key Discussion Points
26
+
27
+ ## Decisions Made
28
+
29
+ ## Action Items
30
+
31
+ - [ ]
32
+
33
+ ## Notes
@@ -0,0 +1,20 @@
1
+ ---
2
+ id: "{{id}}"
3
+ title: "{{title}}"
4
+ type: person
5
+ classification: Internal
6
+ created: "{{date}}"
7
+ updated: "{{date}}"
8
+ tags: []
9
+ organization: ""
10
+ role: ""
11
+ related: []
12
+ ---
13
+
14
+ # {{title}}
15
+
16
+ ## Role & Context
17
+
18
+ ## Key Interactions
19
+
20
+ ## Notes
@@ -0,0 +1,23 @@
1
+ ---
2
+ id: "{{id}}"
3
+ title: "{{title}}"
4
+ type: project
5
+ classification: Internal
6
+ created: "{{date}}"
7
+ updated: "{{date}}"
8
+ tags: []
9
+ owner: ""
10
+ related: []
11
+ ---
12
+
13
+ # {{title}}
14
+
15
+ ## Objective
16
+
17
+ ## Current Status
18
+
19
+ ## Key Stakeholders
20
+
21
+ ## Decisions
22
+
23
+ ## Notes
@@ -0,0 +1,40 @@
1
+ ---
2
+ id: "{{id}}"
3
+ title: "{{title}}"
4
+ type: moc
5
+ classification: Internal
6
+ created: "{{date}}"
7
+ updated: "{{date}}"
8
+ tags: []
9
+ related: []
10
+ ---
11
+
12
+ # {{title}}
13
+
14
+ One live "state of play" note — the navigable spine for this vault/project
15
+ (AGENTS.md §3, HYG-03). Each `## Section:` heading below carries its own
16
+ `Updated: YYYY-MM-DD` stamp on the very next line, so a reader (or
17
+ `tools/validate.py`'s warn-only staleness lint) can see which parts are
18
+ current without touching this note's own top-level `updated:`. A section
19
+ whose stamp is older than the staleness threshold is warn-flagged, never
20
+ blocked — a quality nudge, not a gate.
21
+
22
+ ## Section: Current Priorities
23
+ Updated: {{date}}
24
+
25
+ - What matters right now.
26
+
27
+ ## Section: Open Threads
28
+ Updated: {{date}}
29
+
30
+ - Unresolved questions, in-flight decisions.
31
+
32
+ ## Section: Key Decisions
33
+ Updated: {{date}}
34
+
35
+ - Link to `[[decision-notes]]` rather than re-stating them here.
36
+
37
+ ## Section: Watch List
38
+ Updated: {{date}}
39
+
40
+ - Things to revisit; stale items surface via the lint above.
brain/_version.py ADDED
@@ -0,0 +1,12 @@
1
+ """Committed version stamp — GENERATED by tools/package_clients.py; do not hand-edit.
2
+
3
+ ADR-0005 Ruling 1: the clean-room export ships only git-tracked files, so the
4
+ zero-install VM (staged source, no package metadata) can only report a real
5
+ version if this stamp is COMMITTED. tools/release.py rewrites it (via
6
+ tools/package_clients.py) in the same act as the pyproject.toml bump, so the
7
+ tagged release commit always carries the exact released version. On the
8
+ pip-installed host, importlib.metadata stays primary; brain/__init__.py reads
9
+ this file only as the fallback before 0.0.0+unknown.
10
+ """
11
+
12
+ __version__ = "0.16.0"