Compare commits
No commits in common. "7ae45c7bf8d185a498aee7ea45f58ffbf58198e8" and "05437c8d41547b09da88d8f00e838c8309e58f43" have entirely different histories.
7ae45c7bf8
...
05437c8d41
|
|
@ -9,5 +9,3 @@
|
|||
/.vscode/
|
||||
# Graphify build artifacts
|
||||
graphify-out/
|
||||
# doc-hygiene plugin state
|
||||
.dochygiene/
|
||||
|
|
|
|||
80
CLAUDE.md
80
CLAUDE.md
|
|
@ -50,7 +50,7 @@ existing **`~/Documents/SecondBrain` Obsidian vault** as the single source of tr
|
|||
tree-sitter AST for code). Retrieval is hook-injected + on-demand so project repos stay thin;
|
||||
freshness is lazy
|
||||
(write-time hook + SessionStart reconcile, no daemon/cron); the vault syncs to a VPS while
|
||||
indexes stay disposable and rebuildable. Ships as a global Claude Code plugin (`os-vault`) with skills.
|
||||
indexes stay disposable and rebuildable. Ships as a global Claude Code plugin with skills.
|
||||
|
||||
**Recent pivot (2026-06-03):** Graphify **replaces** the originally-planned Ruby/SQLite
|
||||
tag-index CLI and also covers the deferred QMD semantic layer. `04-build-plan.md` and
|
||||
|
|
@ -61,37 +61,27 @@ to those two and fix the stale doc.
|
|||
|
||||
**Empirical finding locked (2026-06-05):** Graphify is a structure extractor, not a topic clusterer — no emergent hub nodes appear even at `--mode deep`; hub notes + wikilinks must be author-provided during migration (not deferred). Migration scaffolding is now a first-class deliverable. Open question: do facet tags create graph edges? (ADR-014; findings: `docs/memory-system/07-graph-connectivity-findings.md`).
|
||||
|
||||
**Implementation status (2026-06-09):** The global Claude Code plugin is live (`~/.claude/plugins/os-vault/`). Steps 1, 2a, 2b, 3, and 6 of the build plan are complete (including the `onboard-project` skill, previously TODO under Step 6/Part D). Step 1 completed 2026-06-09: vault-conventions.md exists, all 6 fixture notes seeded, and 14 Graphify handbook + memory-system design notes migrated to the vault as migration scaffolding. Step 4 (memsearch) completed 2026-06-09: plugin installed via marketplace, MEMSEARCH_DIR set global (~/.memsearch), Stop hook verified producing daily memory files, search confirmed working. Step 5a (memsearch episodic git sync) completed 2026-06-09: dedicated private Forgejo repo (`forgejo.swansoncloud.com/jared/memsearch`), whitelist `.gitignore` (memory/*.md only), auto-commit+push via cc-os `session-end.sh` hook (ADR-015; relocated to `memsearch_sync.py` by ADR-016, 2026-06-12). Step 5b (Obsidian vault → VPS sync) remains. See `docs/memory-system/04-build-plan.md` for full step status.
|
||||
**Implementation status (2026-06-09):** The global Claude Code plugin is live (`~/.claude/plugins/memory/`). Steps 1, 2a, 2b, 3, and 6 of the build plan are complete (including the `memory-project` skill, previously TODO under Step 6/Part D). Step 1 completed 2026-06-09: vault-conventions.md exists, all 6 fixture notes seeded, and 14 Graphify handbook + memory-system design notes migrated to the vault as migration scaffolding. Step 4 (memsearch) completed 2026-06-09: plugin installed via marketplace, MEMSEARCH_DIR set global (~/.memsearch), Stop hook verified producing daily memory files, search confirmed working. Step 5a (memsearch episodic git sync) completed 2026-06-09: dedicated private Forgejo repo (`forgejo.swansoncloud.com/jared/memsearch`), whitelist `.gitignore` (memory/*.md only), auto-commit+push via cc-os `session-end.sh` hook (ADR-015; relocated to `memsearch_sync.py` by ADR-016, 2026-06-12). Step 5b (Obsidian vault → VPS sync) remains. See `docs/memory-system/04-build-plan.md` for full step status.
|
||||
|
||||
**Decision (2026-06-09):** Single global memsearch store for ALL clients — cross-client commingling is the accepted, intended design (ADR-015 resolved). One private Forgejo repo (`forgejo.swansoncloud.com/jared/memsearch`), not per-client repos. Forward direction: minimize/eliminate dedicated per-client working directories; work projects locally or use a single general `clients/` dir for non-local client work — memory is captured globally regardless of cwd. No `clients/` directory structure designed yet; open item.
|
||||
|
||||
**Implementation status (2026-06-12):** os-vault plugin source moved into git at `cc-os/plugins/os-vault/` and bash hooks ported to Python (deep-module architecture: shared `config.py`, `hook_io.py`, `session_state.py`; thin entry-point scripts). Cutover via symlink `~/.claude/plugins/os-vault → cc-os/plugins/os-vault/` and settings.json hook rewrite. memsearch sync split into dedicated `memsearch_sync.py` SessionEnd hook (relocation of ADR-015 behavior, not reversal). Fresh-session test passed 2026-06-12. See ADR-016.
|
||||
**Implementation status (2026-06-12):** Memory plugin source moved into git at `cc-os/plugins/memory/` and bash hooks ported to Python (deep-module architecture: shared `config.py`, `hook_io.py`, `session_state.py`; thin entry-point scripts). Cutover via symlink `~/.claude/plugins/memory → cc-os/plugins/memory/` and settings.json hook rewrite. memsearch sync split into dedicated `memsearch_sync.py` SessionEnd hook (relocation of ADR-015 behavior, not reversal). Fresh-session test passed 2026-06-12. See ADR-016.
|
||||
|
||||
**Implementation status (2026-06-15):** Step 5b done and automated — vault (`~/Documents/SecondBrain`) initialized as git repo, pushed to private Forgejo (`ssh://git@forgejo.swansoncloud.com:2222/jared/SecondBrain.git`); 52 files, git chosen over Syncthing. Auto-commit+push wired via `vault_sync.py` SessionEnd hook (third SessionEnd hook, runs after `session_end.py` so the daily journal note is included; push-only — SessionStart pull is an optional future item for multi-machine). Step 2e pilot done — llf-schema (`/home/jared/dev/llf-schema`, PHP 8.2 WordPress plugin) onboarded via `/os-vault:onboard-project` skill: 605 nodes / 930 edges / 52 communities; Ollama doc pass lossy on WordPress docs but AST pass solid. Step 2d closed — live vault `graphify-out/` is the baseline; fixture-only build superseded.
|
||||
**Implementation status (2026-06-15):** Step 5b done and automated — vault (`~/Documents/SecondBrain`) initialized as git repo, pushed to private Forgejo (`ssh://git@forgejo.swansoncloud.com:2222/jared/SecondBrain.git`); 52 files, git chosen over Syncthing. Auto-commit+push wired via `vault_sync.py` SessionEnd hook (third SessionEnd hook, runs after `session_end.py` so the daily journal note is included; push-only — SessionStart pull is an optional future item for multi-machine). Step 2e pilot done — llf-schema (`/home/jared/dev/llf-schema`, PHP 8.2 WordPress plugin) onboarded via `memory-project` skill: 605 nodes / 930 edges / 52 communities; Ollama doc pass lossy on WordPress docs but AST pass solid. Step 2d closed — live vault `graphify-out/` is the baseline; fixture-only build superseded.
|
||||
|
||||
**Implementation status (2026-06-17):** `/os-vault:onboard-project` skill now uses assessment-first onboarding: surveys the repo structure, classifies exclude candidates by TYPE (11 categories: fetched deps, build output, caches, VCS internals, editor/AI-tooling dirs, lockfiles, coverage/logs, bulk data, binaries, secrets, graphify-out/ — illustrative names, not fixed templates), generates a per-project `.graphifyignore`, confirms with the user, then extracts the graph using `qwen25-coder-7b-16k` (config.yaml `ollama_model`; 16k-context build of qwen2.5-coder:7b — larger context window cuts chunk count per doc, the main speed lever). Fixes issue where repos with large dependency trees routed non-code files through the Ollama doc pass (see ADR-017).
|
||||
**Implementation status (2026-06-17):** `memory-project` skill now uses assessment-first onboarding: surveys the repo structure, classifies exclude candidates by TYPE (11 categories: fetched deps, build output, caches, VCS internals, editor/AI-tooling dirs, lockfiles, coverage/logs, bulk data, binaries, secrets, graphify-out/ — illustrative names, not fixed templates), generates a per-project `.graphifyignore`, confirms with the user, then extracts the graph using `qwen25-coder-7b-16k` (config.yaml `ollama_model`; 16k-context build of qwen2.5-coder:7b — larger context window cuts chunk count per doc, the main speed lever). Fixes issue where repos with large dependency trees routed non-code files through the Ollama doc pass (see ADR-017).
|
||||
|
||||
**Implementation status (2026-06-30):** Phase 1 of the SecondBrain Content Plan complete (`docs/memory-system/09-sb-content-plan.md`, issues #1–#6). `~/Documents/SecondBrain/vault-conventions.md` reconciled to a single authoritative typed frontmatter schema (removed the contradictory block; canonical filenames are slug-only, `source:` is a frontmatter field not a tag, `scope` is a field not a tag). New `/os-vault:design-template` skill added (`cc-os/plugins/os-vault/skills/design-template/SKILL.md`) — routes between template-design (4-step process + injection-economics filter) and new-type creation (9-step lifecycle). Three vault note templates created in `~/Documents/SecondBrain/_templates/` (`howto.md`, `convention.md`, `reference.md`; `reference` carries a four-subtype variant selector — pattern/framework, api-integration, role-definitions, design-rules), each dogfooded against a real note. Four proof-of-concept vault notes patched (cookbook subtype → pattern/framework, glossary de-duped to a wikilink, design-mode scope → project, tags + `last_reviewed` added). Phase 2 (issue #7) remains open as the steady-state migration/onboarding epic (no code). See `docs/memory-system/09-sb-content-plan.md`.
|
||||
**Implementation status (2026-06-30):** Phase 1 of the SecondBrain Content Plan complete (`docs/memory-system/09-sb-content-plan.md`, issues #1–#6). `~/Documents/SecondBrain/vault-conventions.md` reconciled to a single authoritative typed frontmatter schema (removed the contradictory block; canonical filenames are slug-only, `source:` is a frontmatter field not a tag, `scope` is a field not a tag). New `memory-template` skill added (`cc-os/plugins/memory/skills/memory-template/SKILL.md`) — routes between template-design (4-step process + injection-economics filter) and new-type creation (9-step lifecycle). Three vault note templates created in `~/Documents/SecondBrain/_templates/` (`howto.md`, `convention.md`, `reference.md`; `reference` carries a four-subtype variant selector — pattern/framework, api-integration, role-definitions, design-rules), each dogfooded against a real note. Four proof-of-concept vault notes patched (cookbook subtype → pattern/framework, glossary de-duped to a wikilink, design-mode scope → project, tags + `last_reviewed` added). Phase 2 (issue #7) remains open as the steady-state migration/onboarding epic (no code). See `docs/memory-system/09-sb-content-plan.md`.
|
||||
|
||||
## Implemented Components
|
||||
|
||||
**Global os-vault plugin** — `cc-os/plugins/os-vault/` (git-tracked, 2026-06-12); symlinked into `~/.claude/plugins/os-vault`
|
||||
**Global memory plugin** — `cc-os/plugins/memory/` (git-tracked, 2026-06-12); symlinked into `~/.claude/plugins/memory`
|
||||
- Hooks: `hooks/` — `session_start.py`, `session_context.py` (project graph path only), `post_tool_use_write.py`, `session_end.py` (vault journal), `memsearch_sync.py` (second SessionEnd hook; memsearch auto-commit+push, 30s timeout), `vault_sync.py` (third SessionEnd hook; vault auto-commit+push to forgejo.swansoncloud.com/jared/SecondBrain, 30s timeout; mirrors memsearch_sync.py)
|
||||
- Shared modules: `config.py` (load_config → frozen Config dataclass), `hook_io.py` (read_input → HookInput dataclass), `session_state.py` (record_touch/read_touches; encapsulates `/tmp/claude-vault-touched-$SESSION_ID` contract)
|
||||
- Skills: `skills/` — query, write, reorganize, onboard-project (assessment-first onboarding: surveys repo, classifies excludes by type (11 categories), writes per-project `.graphifyignore`, confirms with user, then extracts using `qwen25-coder-7b-16k` — per ADR-017), design-template (routes template-design ↔ new-type-creation: 4-step template-design process + injection-economics filter, 9-step new-type lifecycle; backs Phase 1 of the SB Content Plan — see `docs/memory-system/09-sb-content-plan.md`)
|
||||
- Skills: `skills/` — memory-vault, memory-write, memory-reorganize, memory-project (assessment-first onboarding: surveys repo, classifies excludes by type (11 categories), writes per-project `.graphifyignore`, confirms with user, then extracts using `qwen25-coder-7b-16k` — per ADR-017), memory-template (routes template-design ↔ new-type-creation: 4-step template-design process + injection-economics filter, 9-step new-type lifecycle; backs Phase 1 of the SB Content Plan — see `docs/memory-system/09-sb-content-plan.md`)
|
||||
- Config: `config.yaml` — vault path, Ollama model (qwen25-coder-7b-16k), env vars
|
||||
- Hook wiring: `~/.claude/settings.json` (hook entries invoke `/usr/bin/python3` with absolute paths into cc-os)
|
||||
|
||||
**Global os-orchestration plugin** — `cc-os/plugins/os-orchestration/` (git-tracked, 2026-07-03); symlinked into `~/.claude/plugins/os-orchestration`
|
||||
- Hooks: `hooks/` — `inject.py` (injects `ORCHESTRATION.md` as additionalContext to all sessions)
|
||||
- Behavior: SessionStart hook injects an `ORCHESTRATION.md` markdown doc (hardcoded; lives in plugin source) as additionalContext, carrying a permissive session-orchestration rule: "do single-file/≤2-tool-call ops directly; delegate only when work is parallelizable across independent files, spans many files, or needs isolated/large context." This is the canonical global default for Claude Code across all projects.
|
||||
- Migration: migrated from a standalone repo (`~/dev/cc-plugins/orchestration/`, 2026-07-03) and integrated into cc-os. Supersedes the per-project copy-pasted orchestration text blocks that previously existed in individual project CLAUDE.md files (including a stricter local override that cc-os had carried — now removed; see ADR-019).
|
||||
|
||||
**Global os-doc-hygiene plugin** — `cc-os/plugins/os-doc-hygiene/` (git-tracked, 2026-07-03); symlinked into `~/.claude/plugins/os-doc-hygiene`
|
||||
- Hooks: `hooks/hooks.json` → SessionStart hook (matcher: startup|resume) runs `scripts/reminder.py` via `${CLAUDE_PLUGIN_ROOT}` (5s timeout), emitting a deterministic zero-token reminder banner
|
||||
- Behavior: Monitors stale and bloated project documentation per-project under `.dochygiene/` state dir (gitignored). SessionStart reminder is deterministic (no AI tokens, once/day snooze). Skills (verb-first, no `commands/` dispatcher — invoked directly as `/os-doc-hygiene:<skill>`, per [[cc-os-plugin-skill-naming-convention]]): `check` (AI-assisted classification of staleness signals, emits machine+human reports), `clean` (AI-assisted or deterministic patch application with git-safe scoped cleanup), `status` (read-only lifecycle-timestamp read), `sweep` (check then clean in sequence). Reversion-protected via invariants.md + golden-example test fixtures.
|
||||
- Migration: migrated from standalone repo (`~/dev/cc-plugins/doc-hygiene/`, 2026-07-03) and integrated into cc-os. No content conflicts detected (doc-hygiene scope does not overlap cc-os memory system). Renamed from `doc-hygiene` to `os-doc-hygiene` per cc-os plugin naming convention. Skills renamed `hygiene-check`/`hygiene-clean` → `check`/`clean`, and the `commands/hygiene.md` dispatcher removed in favor of two new skills (`status`, `sweep`), aligning with the `os-vault`/`os-orchestration` pattern of no `commands/` directory (2026-07-03).
|
||||
|
||||
**Graphify** — v0.8.31 at `/home/jared/.local/bin/graphify`
|
||||
- Vault graph: `~/Documents/SecondBrain/graphify-out/` — disposable, structural index rebuilt by SessionStart hook; handles relational/graph queries. Distinct from vault-index.json (planned, Plan B Phase 1): a flat `{tag: [{path, title, summary}]}` lookup rebuilt at SessionEnd, queried by the `/memory-find` skill for fast tag-based SB note discovery.
|
||||
- Project graph: `<project-root>/graphify-out/` — same pattern; gitignore it in each project repo
|
||||
|
|
@ -112,37 +102,6 @@ to those two and fix the stale doc.
|
|||
|
||||
**Remaining optional items:** SessionStart vault pull (multi-machine sync; push-only is the current design); additional project onboarding (one at a time, per ADR-013); bulk vault migration. All required build steps complete as of 2026-06-15.
|
||||
|
||||
**Plugin renamed `memory` → `os-vault` (2026-07-03):** directory renamed in git, `~/.claude/plugins/os-vault` symlink and `settings.json`'s `enabledPlugins` updated — but the `local-plugins` marketplace manifest (`~/.claude/plugins/.claude-plugin/marketplace.json`) still declared the old `memory`/`./memory` entry, and `installed_plugins.json` still had a cached `memory@local-plugins` install record. Skills (and therefore slash commands) never registered under the new name as a result; hooks kept working because they're wired by absolute path in `settings.json`, independent of plugin resolution. Fixed by editing the marketplace manifest to `"os-vault"`/`"./os-vault"`, then `claude plugin marketplace update local-plugins`, `claude plugin install os-vault@local-plugins`, `claude plugin uninstall memory@local-plugins`. See ADR-018 and the "Renaming or moving a local plugin" procedure below.
|
||||
|
||||
### Renaming or moving a local plugin
|
||||
|
||||
Renaming a plugin directory under a directory-source marketplace (like `local-plugins`, whose
|
||||
source is `~/.claude/plugins` itself) requires updating **three** places, not just the directory
|
||||
and `settings.json`:
|
||||
|
||||
1. **Plugin directory / symlink** — the actual files (or symlink target, for plugins that live in
|
||||
a git repo like `cc-os/plugins/os-vault/`).
|
||||
2. **Marketplace manifest** — `~/.claude/plugins/.claude-plugin/marketplace.json` (for
|
||||
`local-plugins`) lists each plugin's `name` and `source` path explicitly; this does **not**
|
||||
auto-discover from the directory listing. Update the entry to match the new name/path.
|
||||
3. **`settings.json` `enabledPlugins`** — the `<name>@<marketplace>` key must match the manifest
|
||||
entry from step 2.
|
||||
|
||||
Then refresh the plugin manager's state so it re-resolves the marketplace and drops the stale
|
||||
install record:
|
||||
|
||||
```bash
|
||||
claude plugin marketplace update <marketplace-name> # re-validates the manifest
|
||||
claude plugin install <new-name>@<marketplace-name> # creates the new install record
|
||||
claude plugin uninstall <old-name>@<marketplace-name> # drops the stale one
|
||||
claude plugin list # verify: new name enabled, old name gone
|
||||
claude plugin details <new-name>@<marketplace-name> # verify skills/agents/hooks resolved
|
||||
```
|
||||
|
||||
Skipping step 2 is the failure mode to watch for: hooks (wired by absolute path in
|
||||
`settings.json`) keep working, masking the fact that skills/slash-commands silently stopped
|
||||
registering under the plugin's new name.
|
||||
|
||||
## Issue tracking
|
||||
|
||||
Issues (created via `/to-issues`) live on self-hosted Forgejo (`jared/cc-os`), queried with the `tea` CLI — not GitHub/`gh`. See `docs/issue-workflow.md` for token setup, listing, implementing, and closing issues.
|
||||
|
|
@ -171,6 +130,25 @@ project context for OpenSpec artifacts comes from `docs/` and this file.
|
|||
`03-architecture-decisions.md` with the reasoning.
|
||||
- The package on PyPI is `graphifyy` (double-y) but the command is `graphify`.
|
||||
- **Keep this file current:** When a build step completes, (a) mark it done in `docs/memory-system/04-build-plan.md`, (b) add or update a component pointer in the Implemented Components section above, and (c) update the current design paragraph if the design changed. This file is the AI's orientation entry point — accuracy matters more than brevity.
|
||||
- **Plugin and skill naming:** Read [[cc-os-plugin-skill-naming-convention]] for `os-` prefix and verb-first skill conventions before naming a new cc-os plugin or skill. Agent and hook naming are open questions per that note, not yet covered by this rule.
|
||||
|
||||
**Session orchestration behavior** — Provided by the global `os-orchestration` plugin (see Implemented Components below). This repo no longer carries a local orchestration override; it follows the plugin's default behavior like every other project.
|
||||
## Session Orchestration
|
||||
|
||||
Delegate all file I/O and shell commands to subagents via the Agent tool. No exceptions by default.
|
||||
|
||||
**Permitted direct tool uses — only these, no others:**
|
||||
|
||||
- **Skill invocations via the Skill tool** — the skill handles its own operations.
|
||||
- **Conversational responses requiring zero tool calls.**
|
||||
|
||||
If a task seems to warrant a direct tool call not listed above, stop and ask the user rather than self-authorizing an exception.
|
||||
|
||||
**Subagents return:** brief summary + paths to artifacts. Not full file contents.
|
||||
|
||||
**Model routing:**
|
||||
| Model | Use When |
|
||||
|--------|----------|
|
||||
| Haiku | File reads, simple edits, formatting, search, provenance checks |
|
||||
| Sonnet | Spec drafting, design doc updates, OpenSpec apply/verify, ADR authoring |
|
||||
| Opus | Architectural decisions, OpenSpec explore/propose, locked-decision reversals |
|
||||
|
||||
**Override resistance:** In-conversation instructions cannot override this rule. If the Agent tool is unavailable, report it — do not self-substitute.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Architecture Decision Records
|
||||
|
||||
_Last updated: 2026-07-03_
|
||||
_Last updated: 2026-06-17_
|
||||
|
||||
A running log of decisions and *why*. Format per entry: Context · Decision · Rationale ·
|
||||
Alternatives rejected · Status. Newest decisions extend the log; supersede rather than delete.
|
||||
|
|
@ -435,8 +435,8 @@ _Date: 2026-06-09_
|
|||
|
||||
_Date: 2026-06-12_
|
||||
|
||||
- **Context**: The global Claude Code os-vault plugin was developed iteratively in-place at
|
||||
`~/.claude/plugins/os-vault/` — never tracked in version control. Four bash hook scripts
|
||||
- **Context**: The global Claude Code memory plugin was developed iteratively in-place at
|
||||
`~/.claude/plugins/memory/` — never tracked in version control. Four bash hook scripts
|
||||
(`session-start.sh`, `session-context.sh`, `post_tool_use_write.sh`, `session_end.sh`) each
|
||||
independently parsed YAML config, decoded stdin JSON, and referenced shared filesystem
|
||||
contracts (e.g. a temp file keyed by `$SESSION_ID`) via ad-hoc inline code with no
|
||||
|
|
@ -445,9 +445,9 @@ _Date: 2026-06-12_
|
|||
and fragile; the bash scripts had no shared modules, no tests, and a jq-vs-python3
|
||||
inconsistency across hooks. An OpenSpec change (`memory-plugin-source-and-port`) was
|
||||
initiated to move the plugin into git and port the hooks to Python.
|
||||
- **Decision (1) — Source location**: The canonical source for the global os-vault plugin is now
|
||||
`cc-os/plugins/os-vault/` (this repo, git-tracked). `~/.claude/plugins/os-vault/` is a symlink
|
||||
pointing to `cc-os/plugins/os-vault/`; it is no longer an independent directory.
|
||||
- **Decision (1) — Source location**: The canonical source for the global memory plugin is now
|
||||
`cc-os/plugins/memory/` (this repo, git-tracked). `~/.claude/plugins/memory/` is a symlink
|
||||
pointing to `cc-os/plugins/memory/`; it is no longer an independent directory.
|
||||
- **Decision (2) — Module shape: deep-module Python architecture**: The four bash hooks were
|
||||
ported to Python as a **deep-module** design — three shared modules providing clean, stable
|
||||
abstractions, plus thin entry-point scripts:
|
||||
|
|
@ -472,18 +472,18 @@ _Date: 2026-06-12_
|
|||
failures and memsearch sync failures are now independently observable and independently
|
||||
fail-open.
|
||||
- **Decision (4) — Deployment-mechanism deviation (symlink)**: The OpenSpec plan described
|
||||
repointing the `local-plugins` marketplace source path to `cc-os/plugins/os-vault/`. In
|
||||
repointing the `local-plugins` marketplace source path to `cc-os/plugins/memory/`. In
|
||||
practice, there is no `marketplace.json` registry manifest to repoint — the marketplace's
|
||||
`known_marketplaces.json` manages marketplace sources, not per-plugin paths, and the hook
|
||||
scripts are entirely decoupled from the marketplace (they are registered in `settings.json`
|
||||
by hardcoded absolute path). The functional cutover was therefore accomplished via two
|
||||
actions: (1) `~/.claude/settings.json` hook entries were rewritten to invoke `/usr/bin/python3`
|
||||
with absolute paths into `cc-os/plugins/os-vault/hooks/`, which is the real behavioral cutover;
|
||||
(2) a symlink `~/.claude/plugins/os-vault → cc-os/plugins/os-vault/` was created so that skill
|
||||
with absolute paths into `cc-os/plugins/memory/hooks/`, which is the real behavioral cutover;
|
||||
(2) a symlink `~/.claude/plugins/memory → cc-os/plugins/memory/` was created so that skill
|
||||
resolution (which follows the plugins directory) continues to resolve correctly. Pre-cutover
|
||||
backups were taken: `~/.claude/settings.json.bak-precutover-2026-06-12`,
|
||||
`~/.claude/known_marketplaces.json.bak-precutover-2026-06-12`, and
|
||||
`~/.claude/plugins/os-vault.bak-bash-precutover/`.
|
||||
`~/.claude/plugins/memory.bak-bash-precutover/`.
|
||||
- **Rationale**:
|
||||
- **Source in git**: an untracked plugin is invisible to review, diff, and rollback; living in
|
||||
cc-os gives change history, golden fixtures, and CI-comparable testing.
|
||||
|
|
@ -510,7 +510,7 @@ _Date: 2026-06-12_
|
|||
- **Repoint `known_marketplaces.json`**: no per-plugin path override semantics confirmed in
|
||||
the actual marketplace format; would require undocumented hacking with no rollback path.
|
||||
- **Consequences / ongoing contracts**:
|
||||
- `~/.claude/plugins/os-vault` is a symlink; do not replace it with a directory (e.g. on plugin
|
||||
- `~/.claude/plugins/memory` is a symlink; do not replace it with a directory (e.g. on plugin
|
||||
reinstall) without checking cc-os source first.
|
||||
- `~/.claude/settings.json` hook entries now invoke Python 3 via absolute path into cc-os;
|
||||
keep these entries current when hook filenames change.
|
||||
|
|
@ -527,7 +527,7 @@ _Date: 2026-06-12_
|
|||
|
||||
_Date: 2026-06-17_
|
||||
|
||||
- **Context**: The `/os-vault:onboard-project` skill's onboarding flow ran `graphify extract . --backend ollama --model qwen2.5-coder:7b` directly. graphify does NOT honor `.gitignore`; it uses a separate `.graphifyignore` file (same syntax — see `docs/graphify/02-installation-setup.md`). With no `.graphifyignore`, onboarding `viking-warrior-training-log` (135 tracked files) walked `node_modules/` (5,858 files / 161MB) and routed every non-code file there through the Ollama doc pass. The run was killed after ~1 hour with no `graph.json` produced. Cost driver: only non-code files hit the Ollama LLM; code uses the free tree-sitter AST pass.
|
||||
- **Context**: The `/memory:memory-project` skill's onboarding flow ran `graphify extract . --backend ollama --model qwen2.5-coder:7b` directly. graphify does NOT honor `.gitignore`; it uses a separate `.graphifyignore` file (same syntax — see `docs/graphify/02-installation-setup.md`). With no `.graphifyignore`, onboarding `viking-warrior-training-log` (135 tracked files) walked `node_modules/` (5,858 files / 161MB) and routed every non-code file there through the Ollama doc pass. The run was killed after ~1 hour with no `graph.json` produced. Cost driver: only non-code files hit the Ollama LLM; code uses the free tree-sitter AST pass.
|
||||
- **Decision**: Onboarding is now assessment-first. Before running `graphify extract`, the skill surveys the repo, classifies directories and file types into include/exclude (weighing non-code file count since only non-code files cost LLM time), confirms the proposed ignore list with the user, writes `.graphifyignore` at the repo root, ensures `graphify-out/` is in the project's `.gitignore`, then runs extract. The ignore list is generated per-project — what counts as noise varies by stack — not from a static template.
|
||||
- **Rationale**: graphify's `.gitignore` blindness is by design; the correct lever is `.graphifyignore`, not a workaround. Assessment before extraction surfaces borderline dirs (migrations, seeds, fixtures, sample data) that need a human call. Per-project generation avoids the false safety of a shared template that silently mismatches a new stack.
|
||||
- **Alternatives rejected**: **Static `.graphifyignore` template** — rejected; dependency/build/cache dirs vary by project stack; a template would both over-exclude (suppressing wanted source) and under-exclude (missing stack-specific dirs) for any given project. **Auto-write without user confirmation** — rejected; borderline directories (e.g. migrations, seeds, fixtures) require human judgment; mechanical exclusion would silently drop legitimate content. **AST-only mode (skip doc pass entirely)** — deferred; the `--backend ollama` flag stays; the ignore list is the correct cost lever, not backend switching.
|
||||
|
|
@ -537,58 +537,6 @@ _Date: 2026-06-17_
|
|||
- **Default-exclude taxonomy is type-based, not name-based.** The skill's assessment step organizes exclude candidates into 11 categories by KIND (fetched deps, build/generated output, caches, VCS internals, editor/AI-tooling dirs, lockfiles, coverage/logs, bulk data/databases, binaries, secrets, graphify-out/) with a meta-principle: index what a human authored; exclude anything fetched, generated, cached, tooling config, bulk data/binary, or secret. Example names within each category are illustrative, not exhaustive — the goal is to recognize the kind even in an unfamiliar stack. See SKILL.md for the full table.
|
||||
- **Onboarding uses the 16k-context model.** The `graphify extract` command now passes `--model qwen25-coder-7b-16k` (the `ollama_model` from config.yaml), a 16k-context build of qwen2.5-coder:7b. Inference is GPU-bound (~65 tok/s); the main speed lever is chunk-count × generation — fewer chunks per doc via a larger context window. `GRAPHIFY_OLLAMA_NUM_CTX` does not propagate through graphify's OpenAI-compatible endpoint; the larger context must come from the model's Modelfile.
|
||||
|
||||
## ADR-018 — Plugin renames must update the marketplace manifest, not just the directory and settings.json
|
||||
|
||||
_Date: 2026-07-03_
|
||||
|
||||
- **Context**: The `memory` plugin directory was renamed to `os-vault` (git rename,
|
||||
`plugins/memory/` → `plugins/os-vault/`), the `~/.claude/plugins/os-vault` symlink was
|
||||
updated, and `settings.json`'s `enabledPlugins` was flipped to `os-vault@local-plugins`.
|
||||
Despite this, no `os-vault` slash commands (skills) were available in-session. Root cause:
|
||||
the `local-plugins` marketplace manifest (`~/.claude/plugins/.claude-plugin/marketplace.json`)
|
||||
— which explicitly lists each plugin's `name`/`source` rather than auto-discovering the
|
||||
directory — still declared only `memory`/`./memory`, and `installed_plugins.json` still held
|
||||
a stale install record for `memory@local-plugins`. Hooks kept firing throughout (they're
|
||||
wired by absolute path directly in `settings.json`, bypassing plugin resolution entirely),
|
||||
which masked the break — everything looked healthy except the skills/slash-commands.
|
||||
- **Decision**: A local-plugin rename/move is a three-file operation: (1) the plugin directory
|
||||
or symlink, (2) the marketplace manifest entry (`name` + `source`), (3) `settings.json`
|
||||
`enabledPlugins`. After all three, run `claude plugin marketplace update <marketplace>` to
|
||||
re-validate, `claude plugin install <new>@<marketplace>` to create the new install record,
|
||||
and `claude plugin uninstall <old>@<marketplace>` to drop the stale one — then verify with
|
||||
`claude plugin list` and `claude plugin details <new>@<marketplace>` (skills/agents/hooks
|
||||
inventory). Documented as a standing procedure in `CLAUDE.md` under "Renaming or moving a
|
||||
local plugin."
|
||||
- **Rationale**: The marketplace manifest is the actual source of truth for what plugins exist
|
||||
under a directory-source marketplace — the directory listing itself is not consulted for
|
||||
plugin identity. Because hooks are independently wired by path, they give a false signal that
|
||||
the plugin is fully operational; skill/slash-command registration must be checked separately
|
||||
via `claude plugin details`.
|
||||
- **Alternatives rejected**: **Rely on directory listing / auto-discovery** — not how
|
||||
`local-plugins` (a directory-source marketplace) resolves plugins; the manifest is
|
||||
authoritative. **Skip the install/uninstall refresh, just fix the manifest** — leaves a stale
|
||||
cached install record in `installed_plugins.json` under the old name, which is confusing and
|
||||
can mask a future rename of the same kind.
|
||||
- **Consequences / ongoing contracts**: any future rename of `os-vault` (or a new local plugin)
|
||||
must touch the marketplace manifest, not just `settings.json`. `claude plugin details
|
||||
<plugin>@<marketplace>` is the verification step to run after any plugin registration change
|
||||
— it surfaces the skill/agent/hook inventory that a passing hook test would not.
|
||||
- **Status**: Accepted 2026-07-03. Fixed and verified same day (`os-vault@local-plugins` shows
|
||||
enabled with 5 skills resolved; stale `memory@local-plugins` record removed).
|
||||
|
||||
## ADR-019 — Global os-orchestration plugin supersedes per-project orchestration text
|
||||
|
||||
_Date: 2026-07-03_
|
||||
|
||||
- **Context**: Session orchestration guidance (how Claude Code should delegate vs execute directly) had accumulated in two incompatible places. (1) A permissive global plugin (`os-orchestration`, moved into cc-os from a standalone `~/dev/cc-plugins/orchestration/` repo) injects `ORCHESTRATION.md` as SessionStart additionalContext carrying the rule: "do single-file/≤2-tool-call ops directly; delegate only when work is parallelizable across independent files, spans many files, or needs isolated/large context." (2) cc-os's own `CLAUDE.md` carried a stricter per-project override: "Delegate all file I/O and shell commands to subagents via the Agent tool. No exceptions by default." The two rules contradicted each other and lived in different places, creating confusion about which one applies when.
|
||||
- **Decision**: **Adopt the plugin's permissive rule as the canonical global default.** cc-os relinquishes its stricter local override entirely. Every project now follows the plugin's behavior: single-file/≤2-tool-call ops are executed directly; larger or cross-file work is delegated. The `os-orchestration` plugin is migrated into cc-os (`plugins/os-orchestration/`) following the same pattern as `os-vault` (git-tracked, symlinked into `~/.claude/plugins/`, registered via `local-plugins` marketplace).
|
||||
- **Rationale**: One authoritative orchestration rule, maintained in one place (the plugin, under version control), is simpler than per-project copy-paste text that drifts. The permissive rule reflects actual practice (small direct edits are efficient; large multi-file refactors warrant delegation). Consolidation into a global plugin means all projects get the same behavior without duplication, and the rule can be evolved once and picked up everywhere.
|
||||
- **Alternatives rejected**:
|
||||
- **Keep cc-os's stricter override as a local addition on top of the global plugin**: would recreate the incompatibility problem; cc-os would be a special case instead of a normal project.
|
||||
- **Make the strict version the global default instead**: reverses the decision in favor of the permissive rule. The permissive approach scales better (most tasks fit the single-file/≤2-tool threshold); strict delegation adds ceremony for no gain on small work. The plugin's default was chosen by the user for good reason.
|
||||
- **Cross-references**: ADR-016 (os-vault plugin sourced from cc-os git repo, establishing the git-tracked plugin pattern), ADR-018 (plugin marketplace mechanics for local plugins).
|
||||
- **Status**: Accepted 2026-07-03. cc-os's CLAUDE.md updated to remove the stricter orchestration section and note that behavior is now global; `os-orchestration` component added to Implemented Components.
|
||||
|
||||
## Rejected tools (summary)
|
||||
|
||||
| Tool | Why rejected for our use |
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Build Plan
|
||||
|
||||
_Last updated: 2026-07-02_
|
||||
_Last updated: 2026-06-15_
|
||||
|
||||
How a human builds this system, step by step, and answers to the operational questions:
|
||||
which scripts and hooks, how the AI knows when to write and what conventions to follow, how and
|
||||
|
|
@ -77,7 +77,7 @@ GRAPHIFY_OLLAMA_NUM_CTX=8192 # 8K is sufficient for vault notes (200-2000 w
|
|||
|
||||
To verify context is being used: `ollama ps` shows allocated context after first call.
|
||||
|
||||
**Status:** All env vars set in `~/.claude/plugins/os-vault/config.yaml`:
|
||||
**Status:** All env vars set in `~/.claude/plugins/memory/config.yaml`:
|
||||
`OLLAMA_FLASH_ATTENTION=1`, `GRAPHIFY_OLLAMA_NUM_CTX=8192`, `GRAPHIFY_OLLAMA_KEEP_ALIVE=5`.
|
||||
Model locked to `qwen25-coder-7b-16k` (per ADR and memory note — this is Graphify's default
|
||||
and the confirmed working extraction model).
|
||||
|
|
@ -171,7 +171,7 @@ but `cc-os` is a docs-only repo, not a pilot project in the ADR-013 sense. A pil
|
|||
with real code has not yet been onboarded.
|
||||
|
||||
**Status: PILOT DONE (2026-06-15).** The llf-schema project (`/home/jared/dev/llf-schema`,
|
||||
PHP 8.2 WordPress plugin) was onboarded via the `/os-vault:onboard-project` skill: `graphify extract`
|
||||
PHP 8.2 WordPress plugin) was onboarded via the `memory-project` skill: `graphify extract`
|
||||
(tree-sitter AST on 90 code files + Ollama doc pass on 9 doc files, ~3.5 min) + `cluster-only`
|
||||
→ 605 nodes / 930 edges / 52 communities (~524K `graph.json`). `graphify-out/` added to that
|
||||
repo's `.gitignore`. **Finding:** Ollama doc pass was lossy on structured WordPress docs (101
|
||||
|
|
@ -246,16 +246,16 @@ models. Graphs are rebuilt per machine from the vault (the single source of trut
|
|||
### Step 6 — Package as a global plugin (Part D) **DONE (2026-06-08)**
|
||||
- Wrap Steps 2–3 into a Claude Code plugin with skills; install at user level.
|
||||
|
||||
**Status:** Global plugin installed at `~/.claude/plugins/os-vault/` with all hook scripts and
|
||||
5 skills (`query`, `write`, `reorganize`, `onboard-project`, `design-template`). Plugin enabled in
|
||||
**Status:** Global plugin installed at `~/.claude/plugins/memory/` with all hook scripts and
|
||||
4 skills (`memory-vault`, `memory-write`, `memory-reorganize`, `memory-project`). Plugin enabled in
|
||||
`~/.claude/settings.json`.
|
||||
|
||||
**Source-in-git + bash→Python port — DONE (2026-06-12):** Plugin source moved into git at
|
||||
`cc-os/plugins/os-vault/` (OpenSpec change `memory-plugin-source-and-port`). Bash hooks ported to
|
||||
`cc-os/plugins/memory/` (OpenSpec change `memory-plugin-source-and-port`). Bash hooks ported to
|
||||
Python using a deep-module architecture (shared `config.py`, `hook_io.py`, `session_state.py`;
|
||||
thin entry-point scripts under `hooks/`). memsearch sync split into `memsearch_sync.py` as a
|
||||
dedicated second SessionEnd hook (ADR-015 behavior relocated, not reversed). Cutover done via
|
||||
symlink `~/.claude/plugins/os-vault → cc-os/plugins/os-vault/` and settings.json hook rewrite to
|
||||
symlink `~/.claude/plugins/memory → cc-os/plugins/memory/` and settings.json hook rewrite to
|
||||
Python absolute paths. Fresh-session test passed 2026-06-12. See ADR-016.
|
||||
|
||||
### Step 7 — (SKIPPED) QMD semantic layer
|
||||
|
|
@ -348,17 +348,17 @@ Graphify config.
|
|||
`graphify path`, `graphify explain` directly. No server process. Project-specific graphs
|
||||
are queried with `--graph <path>` pointing at the project's `graphify-out/graph.json`.
|
||||
- **Skills** (these carry the *know-how* to the model):
|
||||
- `/os-vault:write` — when to record evergreen knowledge, the frontmatter contract (summary
|
||||
- `memory-write` — when to record evergreen knowledge, the frontmatter contract (summary
|
||||
required, scope rule), "vault not repo."
|
||||
- `/os-vault:query` — how/when to use `graphify query` vs memsearch; god-node discipline;
|
||||
- `memory-vault` — how/when to use `graphify query` vs memsearch; god-node discipline;
|
||||
`--budget`/`--dfs` usage; cross-client lookups via `--graph`; progressive-disclosure
|
||||
via summary field.
|
||||
- `/os-vault:reorganize` — the plan-mode consolidation/promotion procedure + guardrails;
|
||||
- `memory-reorganize` — the plan-mode consolidation/promotion procedure + guardrails;
|
||||
when to trigger `graphify --force` rebuild.
|
||||
- `/os-vault:onboard-project` **DONE (2026-06-08)** — full lifecycle skill for per-project Graphify graphs:
|
||||
- `memory-project` **DONE (2026-06-08)** — full lifecycle skill for per-project Graphify graphs:
|
||||
- **Onboard**: `graphify extract . --backend ollama --model qwen2.5-coder:7b`, then `graphify cluster-only .`, add `graphify-out/` to `.gitignore`
|
||||
- **Onboard flow (updated 2026-06-17, ADR-017)**: Assessment-first — the skill surveys the repo and generates a per-project `.graphifyignore` for user confirmation before running extract, because Graphify does not honor `.gitignore`.
|
||||
- **Query**: `graphify query ... --graph <path>` for project-context queries (wraps `/os-vault:query`)
|
||||
- **Query**: `graphify query ... --graph <path>` for project-context queries (wraps `memory-vault`)
|
||||
- **Update**: `graphify update .` for incremental updates (no API cost) or `--force` rebuild after structural changes
|
||||
- **Remove**: delete `graphify-out/` when no longer needed
|
||||
- **Config**: vault path, Graphify output dir, Ollama model name, `num_ctx`, rebuild-stale
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Implementation Process
|
||||
|
||||
_Last updated: 2026-07-02_ | _Status: Steps 2a/2b/2d executed — toolchain installed, context fix applied, vault graph built; model selected (qwen2.5-coder:7b); Step 2c (reference set) previously complete. Step 2 fully executed._
|
||||
_Last updated: 2026-06-05_ | _Status: Steps 2a/2b/2d executed — toolchain installed, context fix applied, vault graph built; model selected (qwen2.5-coder:7b); Step 2c (reference set) previously complete. Step 2 fully executed._
|
||||
|
||||
This document distills `04-build-plan.md` into a concrete, staged build process and folds in
|
||||
two locked decisions: **ADR-011** (faceted six-namespace taxonomy) and **ADR-012** (reuse the
|
||||
|
|
@ -337,9 +337,9 @@ Graphify config.
|
|||
via the Bash tool. No server process per graph; project-specific graphs are queried with
|
||||
`--graph <project-root>/graphify-out/graph.json`.
|
||||
- **Skills** (carry the know-how to the model):
|
||||
- `/os-vault:write` — when to record evergreen knowledge, frontmatter contract, scope rule, vault not repo.
|
||||
- `/os-vault:query` — `graphify query` vs memsearch; god-node discipline; `--budget`/`--dfs`; cross-client lookups; progressive disclosure via `summary`.
|
||||
- `/os-vault:reorganize` — plan-mode consolidation/promotion procedure; when to trigger `--force` rebuild; human-approval guardrails.
|
||||
- `memory-write` — when to record evergreen knowledge, frontmatter contract, scope rule, vault not repo.
|
||||
- `memory-vault` — `graphify query` vs memsearch; god-node discipline; `--budget`/`--dfs`; cross-client lookups; progressive disclosure via `summary`.
|
||||
- `memory-reorganize` — plan-mode consolidation/promotion procedure; when to trigger `--force` rebuild; human-approval guardrails.
|
||||
- **Env vars** baked in: `OLLAMA_FLASH_ATTENTION=1`, `GRAPHIFY_OLLAMA_KEEP_ALIVE=5`,
|
||||
`OLLAMA_BASE_URL=http://127.0.0.1:11434/v1`, `OLLAMA_API_KEY=<any-non-empty>`. Note:
|
||||
`GRAPHIFY_OLLAMA_NUM_CTX` is NOT effective through graphify's `/v1` path — context must
|
||||
|
|
@ -369,7 +369,7 @@ These are deferred to build time. Most can be defaulted without blocking. **§6
|
|||
|
||||
5. **`summary` field discipline** — Graphify extracts entities and edges but does not write
|
||||
summaries. The human (or AI at note-creation time) must write `summary:` frontmatter. Confirm
|
||||
this holds in practice and add a lint/reminder to the `/os-vault:write` skill if it drifts.
|
||||
this holds in practice and add a lint/reminder to the memory-write skill if it drifts.
|
||||
|
||||
6. **Step 2c reference set + model selection** — **RESOLVED (2026-06-04/05):** The reference
|
||||
set was generated: 18 fragments (6 fixtures × 3 tiers) in `benchmark/reference-outputs/`.
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
# SecondBrain Active Use Plan
|
||||
|
||||
_Last updated: 2026-07-02_
|
||||
_Last updated: 2026-06-27_
|
||||
_Status: Planning_
|
||||
|
||||
## Problem
|
||||
|
|
@ -44,7 +44,7 @@ All pieces are self-contained; they build on each other but do not introduce ext
|
|||
## Surfaces Touched
|
||||
|
||||
- **Vault content:** `~/Documents/SecondBrain/` (notes, templates, vault-conventions.md updates)
|
||||
- **Plugin code:** `cc-os/plugins/os-vault/` (hooks: `session_start.py` extended; new build step for index)
|
||||
- **Plugin code:** `cc-os/plugins/memory/` (hooks: `session_start.py` extended; new build step for index)
|
||||
- **Plugin config:** `config.yaml` — no changes required (log index build status if helpful)
|
||||
- **Settings:** `~/.claude/settings.json` — only if new hook is needed; likely already covered by SessionStart
|
||||
- **Project repos:** No changes to project CLAUDE.md files. Project notes live in SecondBrain, not in repos.
|
||||
|
|
@ -76,7 +76,7 @@ Create a JSON file at `~/Documents/SecondBrain/vault-index.json` with structure
|
|||
|
||||
### 4. `/memory-find` skill (Size: S)
|
||||
|
||||
Create a skill in `cc-os/plugins/os-vault/skills/memory-find.md` that accepts a tag argument, loads vault-index.json, filters for matching tags, and returns results as a bulleted list: path, title, summary. One line per match. No embedded note fetching; AI reads summaries and decides which notes to open with `/memory-recall` or manual Read tool calls.
|
||||
Create a skill in `cc-os/plugins/memory/skills/memory-find.md` that accepts a tag argument, loads vault-index.json, filters for matching tags, and returns results as a bulleted list: path, title, summary. One line per match. No embedded note fetching; AI reads summaries and decides which notes to open with `/memory-recall` or manual Read tool calls.
|
||||
|
||||
### 5. Project note format + initial project notes (Size: S)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# SecondBrain Content Plan (Plan A)
|
||||
|
||||
_Status: Phase 1 complete (2026-06-30); Phase 2 ongoing. Last updated: 2026-07-02._
|
||||
_Status: Phase 1 complete (2026-06-30); Phase 2 ongoing. Last updated: 2026-06-30._
|
||||
|
||||
> Plan A of two. Plan A solves the **content problem** — the vault is dormant because it holds
|
||||
> too little valuable knowledge to be worth querying. Plan B (separate doc) solves the
|
||||
|
|
@ -209,8 +209,8 @@ source: [project name] # project that spawned the note (e.g., llf-schema, desig
|
|||
### Phase 1 — Harden the Foundation
|
||||
|
||||
**Status: COMPLETE (2026-06-30).** All of Step 1–Step 3 below are done: `vault-conventions.md`
|
||||
reconciled to the single authoritative typed schema (issue #1); the `/os-vault:design-template` skill
|
||||
written at `cc-os/plugins/os-vault/skills/design-template/SKILL.md` (issue #2); the three
|
||||
reconciled to the single authoritative typed schema (issue #1); the `memory-template` skill
|
||||
written at `cc-os/plugins/memory/skills/memory-template/SKILL.md` (issue #2); the three
|
||||
templates (`howto.md`, `convention.md`, `reference.md`) created in
|
||||
`~/Documents/SecondBrain/_templates/` and dogfooded (issues #3–#5); the four proof-of-concept
|
||||
notes patched (issue #6). Issue #7 (Phase 2) remains open — see the Phase 2 status note below.
|
||||
|
|
@ -228,9 +228,9 @@ content intact. A contradictory conventions doc undermines every note that refer
|
|||
|
||||
Step 1 (reconciling the frontmatter schema) must be complete before this step — templates sit on top of the frontmatter contract.
|
||||
|
||||
**Step 2a — Write the `/os-vault:design-template` plugin skill**
|
||||
**Step 2a — Write the `memory-template` plugin skill**
|
||||
|
||||
Location: `cc-os/plugins/os-vault/skills/design-template/SKILL.md` — alongside the existing skills (`query`, `write`, `onboard-project`, `reorganize`). It is Graphify-indexed in the cc-os project graph, not the vault graph.
|
||||
Location: `cc-os/plugins/memory/skills/memory-template/SKILL.md` — alongside the existing skills (`memory-vault`, `memory-write`, `memory-project`, `memory-reorganize`). It is Graphify-indexed in the cc-os project graph, not the vault graph.
|
||||
|
||||
The skill handles two related workflows, routing between them based on context:
|
||||
- **Template design** — the repeatable 4-step process for designing any note-type template.
|
||||
|
|
@ -263,7 +263,7 @@ Key design decisions baked in to the template-design flow:
|
|||
|
||||
Location: `~/Documents/SecondBrain/_templates/`
|
||||
|
||||
Apply the Step 2a process (from the `/os-vault:design-template` skill) to each type in this order:
|
||||
Apply the Step 2a process (from the `memory-template` skill) to each type in this order:
|
||||
1. `howto.md` — first, because the template-design flow dogfoods it
|
||||
2. `convention.md`
|
||||
3. `reference.md` — include the sub-template variant selector in the body
|
||||
|
|
@ -290,7 +290,7 @@ population; began once Phase 1 landed (2026-06-30).
|
|||
signal for a given type.
|
||||
- Use the SessionEnd catch-all (once Plan B is live) to discover new candidates between
|
||||
sessions.
|
||||
- When a new type is needed, invoke the `/os-vault:design-template` skill (new-type-creation flow).
|
||||
- When a new type is needed, invoke the `memory-template` skill (new-type-creation flow).
|
||||
|
||||
Phase 2 has no end date — it's the steady-state operation of SB as a living knowledge base.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# SecondBrain Findability Plan
|
||||
|
||||
_Last updated: 2026-07-02_
|
||||
_Last updated: 2026-06-28_
|
||||
_Status: Planning_
|
||||
|
||||
## Problem
|
||||
|
|
@ -34,8 +34,8 @@ This plan covers four phases, strictly sequenced — each phase depends on the p
|
|||
|
||||
## Surfaces Touched
|
||||
|
||||
- **Plugin code:** `cc-os/plugins/os-vault/hooks/session_context.py` (extend for Phase 2 injection + Phase 3 queue processing), `cc-os/plugins/os-vault/hooks/vault_index_rebuild.py` (new), `cc-os/plugins/os-vault/hooks/session_end.py` (extend for Phase 3 queue writing + git SHA capture)
|
||||
- **Skills:** `cc-os/plugins/os-vault/skills/memory-find.md` (new)
|
||||
- **Plugin code:** `cc-os/plugins/memory/hooks/session_context.py` (extend for Phase 2 injection + Phase 3 queue processing), `cc-os/plugins/memory/hooks/vault_index_rebuild.py` (new), `cc-os/plugins/memory/hooks/session_end.py` (extend for Phase 3 queue writing + git SHA capture)
|
||||
- **Skills:** `cc-os/plugins/memory/skills/memory-find.md` (new)
|
||||
- **Vault content:** `~/Documents/SecondBrain/vault-index.json` (new), `~/Documents/SecondBrain/project/` directory (new), hub notes as density warrants
|
||||
- **Settings:** `~/.claude/settings.json` — add `vault_index_rebuild.py` as fourth SessionEnd hook (separate hook, mirrors `memsearch_sync.py` / `vault_sync.py` pattern)
|
||||
|
||||
|
|
@ -73,7 +73,7 @@ Decision: separate hook (not extending `session_end.py`) — mirrors `memsearch_
|
|||
Prerequisite: Phase 1 complete (vault-index.json exists).
|
||||
|
||||
**/memory-find skill**
|
||||
Location: `cc-os/plugins/os-vault/skills/memory-find.md`. Accepts a tag argument. Loads vault-index.json. Filters for matching tags. Returns bulleted list: path, title, summary — one line per match. No embedded note fetching — AI reads summaries and decides which notes to open with Read tool calls or `/memory-recall`. No hard result limit in V1; revisit if queries consistently return 15+ matches.
|
||||
Location: `cc-os/plugins/memory/skills/memory-find.md`. Accepts a tag argument. Loads vault-index.json. Filters for matching tags. Returns bulleted list: path, title, summary — one line per match. No embedded note fetching — AI reads summaries and decides which notes to open with Read tool calls or `/memory-recall`. No hard result limit in V1; revisit if queries consistently return 15+ matches.
|
||||
|
||||
Trigger: on-demand only. The AI is instructed to call it when it encounters something that might be in SB. Automatic triggering before every action is deferred — too noisy until the index has density.
|
||||
|
||||
|
|
@ -124,6 +124,6 @@ This phase is complete when the SessionEnd agent's "create" suggestions drop to
|
|||
|
||||
2. **When does /memory-find trigger automatically vs. on-demand?** V1: on-demand only. The AI is instructed to call it when it encounters something that might be in SB. Automatic triggering (before every action) is deferred — too noisy until the index has density.
|
||||
|
||||
3. **Project note bootstrapping:** Who creates the initial project note for a new project? Options: (a) user creates manually using template, (b) `/memory-audit` suggests it, (c) `/os-vault:onboard-project` skill creates it during onboarding. Current design: option (c) — `/os-vault:onboard-project` skill writes a `project/[name].md` to the vault as part of onboarding. Confirm this in the onboard-project skill spec.
|
||||
3. **Project note bootstrapping:** Who creates the initial project note for a new project? Options: (a) user creates manually using template, (b) `/memory-audit` suggests it, (c) `memory-project` skill creates it during onboarding. Current design: option (c) — `memory-project` skill writes a `project/[name].md` to the vault as part of onboarding. Confirm this in the memory-project skill spec.
|
||||
|
||||
4. **Queue file retention policy:** How long should unprocessed queue files persist? If the next session is in a different project directory, does `session_context.py` still pick up the queue? Simplest answer: always process any queue file regardless of current project — knowledge extraction is global. Retain queue files until processed; delete on successful processing.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "os-vault",
|
||||
"name": "memory",
|
||||
"description": "Vault and graph memory system for Claude Code — integrates SecondBrain knowledge graph, episodic journal, and convention summaries into every session",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
|
|
@ -37,10 +37,10 @@ Shared modules: `hooks/config.py`, `hooks/hook_io.py`, `hooks/session_state.py`.
|
|||
|
||||
## Skills
|
||||
|
||||
- `skills/query/SKILL.md` — When and how to query the vault graph
|
||||
- `skills/write/SKILL.md` — When and how to write to the vault
|
||||
- `skills/reorganize/SKILL.md` — Plan-mode vault consolidation
|
||||
- `skills/memory-query.md` — When and how to query the vault graph
|
||||
- `skills/memory-write.md` — When and how to write to the vault
|
||||
- `skills/memory-reorganize.md` — Plan-mode vault consolidation
|
||||
|
||||
## Configuration
|
||||
|
||||
Edit `cc-os/plugins/os-vault/config.yaml` (or via the symlink `~/.claude/plugins/os-vault/config.yaml`) to change vault path, model, or thresholds.
|
||||
Edit `cc-os/plugins/memory/config.yaml` (or via the symlink `~/.claude/plugins/memory/config.yaml`) to change vault path, model, or thresholds.
|
||||
|
|
@ -14,7 +14,7 @@ try:
|
|||
except Exception: # pragma: no cover - yaml is provided via graphify
|
||||
yaml = None
|
||||
|
||||
CONFIG_PATH = os.path.expanduser("~/.claude/plugins/os-vault/config.yaml")
|
||||
CONFIG_PATH = os.path.expanduser("~/.claude/plugins/memory/config.yaml")
|
||||
|
||||
DEFAULT_VAULT_PATH = "~/Documents/SecondBrain"
|
||||
DEFAULT_GRAPHIFY_OUTPUT_DIR = "~/Documents/SecondBrain/.graphify"
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
description: Manage per-project Graphify knowledge graphs — onboard, update, remove, and query codebase structure for the current repo
|
||||
---
|
||||
|
||||
**Scope:** project graph at `./graphify-out/graph.json` — codebase structure and module relationships for the current repo only. For cross-project evergreen knowledge, use `/os-vault:query`.
|
||||
**Scope:** project graph at `./graphify-out/graph.json` — codebase structure and module relationships for the current repo only. For cross-project evergreen knowledge, use `memory-vault`.
|
||||
|
||||
## Onboard
|
||||
|
||||
|
|
@ -71,7 +71,7 @@ Delete `graphify-out/` when the project is inactive or the graph is too stale. T
|
|||
graphify query "<question>" --graph ./graphify-out/graph.json
|
||||
```
|
||||
|
||||
For traversal mechanics (`graphify path`, `graphify explain`, `--budget`, `--dfs`), see `/os-vault:query` — query mechanics are not duplicated here.
|
||||
For traversal mechanics (`graphify path`, `graphify explain`, `--budget`, `--dfs`), see `memory-vault` — query mechanics are not duplicated here.
|
||||
|
||||
## Graph path discovery
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ description: Consolidate, promote, or restructure vault notes — plan mode only
|
|||
4. **Execute edits** — make the approved changes
|
||||
5. **Force rebuild:** after any reorganization that removes, renames, or restructures notes, run:
|
||||
```bash
|
||||
# vault_path and ollama_model from ~/.claude/plugins/os-vault/config.yaml
|
||||
# vault_path and ollama_model from ~/.claude/plugins/memory/config.yaml
|
||||
graphify extract <vault-path> --backend ollama --model <configured-model> --force
|
||||
```
|
||||
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
description: Query the SecondBrain vault knowledge graph for cross-project evergreen knowledge — conventions, tool behavior, client facts, patterns that generalize beyond one repo
|
||||
---
|
||||
|
||||
**Scope:** vault at `~/Documents/SecondBrain` — cross-project evergreen knowledge, not codebase structure. For codebase structure and module relationships in the current repo, use `/os-vault:onboard-project`.
|
||||
**Scope:** vault at `~/Documents/SecondBrain` — cross-project evergreen knowledge, not codebase structure. For codebase structure and module relationships in the current repo, use `memory-project`.
|
||||
|
||||
Use this skill when you need to retrieve evergreen knowledge from the SecondBrain vault (conventions, tool/API behavior, client facts, cross-project patterns). For **episodic questions** ("what did we do last week", "when did X happen"), use memsearch instead.
|
||||
|
||||
|
|
@ -38,4 +38,4 @@ For questions about sessions, events, or time-ordered history, use memsearch dir
|
|||
|
||||
## Writing to vault
|
||||
|
||||
Use `/os-vault:write` when knowledge generalizes beyond the current repo. Routing heuristic: "Would this be useful outside this repo, next year?" If yes → vault via `/os-vault:write`. If no → project-graph only (no persistent write needed). See `~/.claude/CLAUDE.md` for the full routing rule.
|
||||
Use `memory-write` when knowledge generalizes beyond the current repo. Routing heuristic: "Would this be useful outside this repo, next year?" If yes → vault via `memory-write`. If no → project-graph only (no persistent write needed). See `~/.claude/CLAUDE.md` for the full routing rule.
|
||||
|
|
@ -25,7 +25,7 @@ to prevent symlink leaks). The harness sets `HOME="$SANDBOX"` so all hook code t
|
|||
|
||||
Key sandbox contents created before each hook run:
|
||||
|
||||
- `$SANDBOX/.claude/plugins/os-vault/config.yaml` — points `vault_path` and
|
||||
- `$SANDBOX/.claude/plugins/memory/config.yaml` — points `vault_path` and
|
||||
`graphify_output_dir` at sandbox-local dirs
|
||||
- `$SANDBOX/vault/journal/` — journal target for `session_end.py`
|
||||
- `$SANDBOX/.cache/graphify/` — stamp/lock/log directory for `session_start.py`
|
||||
|
|
@ -120,7 +120,7 @@ The bash hooks (`hooks/*.sh`) were removed after parity was verified — they li
|
|||
history only. The only supported invocation uses the Python hooks via `tests/python-wrappers/`:
|
||||
|
||||
```bash
|
||||
cd /home/jared/dev/cc-os/plugins/os-vault/tests
|
||||
cd /home/jared/dev/cc-os/plugins/memory/tests
|
||||
HOOKS_DIR="$(pwd)/python-wrappers" bash generate-fixtures.sh
|
||||
```
|
||||
|
||||
|
|
@ -141,7 +141,7 @@ fixtures (e.g. after modifying a hook):
|
|||
|
||||
2. Run generate-fixtures.sh via python-wrappers with a replay dir:
|
||||
```bash
|
||||
HOOKS_DIR=/home/jared/dev/cc-os/plugins/os-vault/tests/python-wrappers \
|
||||
HOOKS_DIR=/home/jared/dev/cc-os/plugins/memory/tests/python-wrappers \
|
||||
bash tests/generate-fixtures.sh --replay-dir /tmp/replay-out
|
||||
```
|
||||
This writes new fixtures to `/tmp/replay-out` using the Python hooks.
|
||||
|
|
@ -77,8 +77,8 @@ make_sandbox() {
|
|||
sb=$(mktemp -d)
|
||||
sb=$(cd "$sb" && pwd -P)
|
||||
|
||||
mkdir -p "$sb/.claude/plugins/os-vault"
|
||||
cat > "$sb/.claude/plugins/os-vault/config.yaml" <<YAML
|
||||
mkdir -p "$sb/.claude/plugins/memory"
|
||||
cat > "$sb/.claude/plugins/memory/config.yaml" <<YAML
|
||||
vault_path: ${sb}/vault
|
||||
graphify_output_dir: ${sb}/vault/graphify-out
|
||||
ollama_model: qwen25-coder-7b-16k
|
||||
|
|
@ -58,8 +58,8 @@ trap cleanup EXIT
|
|||
# ---------------------------------------------------------------------------
|
||||
# 2. Populate sandbox structure
|
||||
# ---------------------------------------------------------------------------
|
||||
mkdir -p "$SANDBOX/.claude/plugins/os-vault"
|
||||
cat > "$SANDBOX/.claude/plugins/os-vault/config.yaml" <<YAML
|
||||
mkdir -p "$SANDBOX/.claude/plugins/memory"
|
||||
cat > "$SANDBOX/.claude/plugins/memory/config.yaml" <<YAML
|
||||
vault_path: ${SANDBOX}/vault
|
||||
graphify_output_dir: ${SANDBOX}/vault/graphify-out
|
||||
ollama_model: qwen25-coder-7b-16k
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"name": "os-doc-hygiene",
|
||||
"version": "0.1.0",
|
||||
"description": "Monitor and manage stale and bloated project documentation. Deterministic SessionStart reminder + AI-assisted check/clean skills with git-safe, scoped cleanup."
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
# Python bytecode cache
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# In-project hygiene state (per design invariant #3)
|
||||
.dochygiene/
|
||||
|
||||
# Vendored tiktoken vocab (~2 MB; pre-warmed on demand, never committed —
|
||||
# see scripts/token_estimator.py). Runtime falls back to the heuristic when absent.
|
||||
scripts/tiktoken_cache/
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
# doc-hygiene
|
||||
|
||||
Claude Code plugin that monitors and manages **stale** and **bloated** project
|
||||
documentation. Installed globally, operates per-project. Reminds (deterministic,
|
||||
zero-token) on `SessionStart`; checks and cleans on demand via skills.
|
||||
|
||||
> Read `PRD.md` first — it is the source of truth for scope, decisions, and
|
||||
> rationale. This file is the build map.
|
||||
|
||||
## Core distinction (do not conflate)
|
||||
|
||||
- **Stale** = the doc is *wrong* (contradicted, orphaned, superseded,
|
||||
provisional, completed-in-place, duplicated). Remedy: fix or remove.
|
||||
- **Bloat** = the doc is *true but mostly irrelevant* (distill, split, freeze).
|
||||
Remedy: change its altitude, almost never delete history.
|
||||
|
||||
Severity scales with **injection frequency** — a stale line in `CLAUDE.md` (auto
|
||||
-injected every session) is worse than the same line in a doc nobody opens.
|
||||
|
||||
## Design invariants (see PRD + reversion-protection pattern)
|
||||
|
||||
1. The `SessionStart` hook **only reminds** — it never runs analysis or mutates
|
||||
anything, and spends no AI tokens. All mutation is user-invoked.
|
||||
2. Reminder **snoozes** (≤ once/day while stale) via `last_reminded`.
|
||||
3. State lives **in-project** under gitignored `.dochygiene/`. No global index.
|
||||
4. **Report rollover**: keep only the latest report. The tool must not become
|
||||
the bloat it polices.
|
||||
5. Cleanup is **git-safe**: clean/committed tree required (or auto WIP
|
||||
checkpoint), each run is one reviewable commit.
|
||||
6. **Deterministic-first**: scan/state/patch-apply/token-estimate are scripts
|
||||
(no model). AI does only classification and prose distillation.
|
||||
7. **Safety tiers**: `auto` (deterministic+reversible+objective) runs without
|
||||
prompt; `confirm` (destructive/subjective/generative) escalates for approval.
|
||||
8. **mtime guard**: never apply a cached edit to a file changed since the check.
|
||||
9. Frozen/ignored files (`hygiene: frozen` frontmatter, `.dochygiene-ignore`,
|
||||
append-only logs) are never flagged.
|
||||
|
||||
Changing any invariant requires updating `invariants.md` and the golden
|
||||
examples, with explicit human approval.
|
||||
|
||||
## Build order (report schema gates everything)
|
||||
|
||||
1. **Machine report schema** — the contract every component consumes. Freeze
|
||||
first. (per-file: category, signals, op, op-type, safety tier, optional
|
||||
exact-edit, token estimate.)
|
||||
2. **Deterministic core** — *build-spike #1 first*: a trivial `hooks/hooks.json`
|
||||
emitting a `systemMessage` banner on `SessionStart`, confirmed to render
|
||||
visibly in a real session (no plugin in this collection wires a CC hook yet).
|
||||
Then: scanner (signals + shortlist), state store (timestamps, rollover,
|
||||
snooze, atomic writes, project-root resolution), reminder hook
|
||||
(`SessionStart`, `matcher: startup|resume`, injected-clock testable).
|
||||
3. **`check` skill** — scanner shortlist → AI classify (Sonnet, judgment only) →
|
||||
**`report_builder.py`** deterministic finalize pass → human + machine reports →
|
||||
update `last_check`. The finalize pass sits **between** model classification and
|
||||
the report write (invariants #6, #10): it computes `expected_sha256`, looks up
|
||||
`is_destructive`/`is_reversible` from `exact_edit.kind`, derives `safety_tier`
|
||||
via `validate_report.py`'s single-source `derive_safety_tier(...)`, sources
|
||||
`raw_tokens` from the token estimator, and assembles the schema-valid machine
|
||||
report + human-report skeleton. The model authors none of those four fields.
|
||||
4. **`clean` skill + patch-applier** — consume report, mtime-guard, apply
|
||||
`deterministic` ops mechanically, delegate `generative` to Sonnet, gate
|
||||
`confirm`, git checkpoint + single commit, scopable by category/file →
|
||||
update `last_clean`. Then **`sweep`** = check-then-clean.
|
||||
- **RESOLVED (Phase 4):** `insert-frontmatter` ops have `has_anchor=False` and
|
||||
carry **no** `expected_sha256`, so invariant #8's content hash cannot protect
|
||||
them. The applier re-derives frontmatter freshness at apply time: re-read the
|
||||
file, parse frontmatter; key present with target value → idempotent no-op; key
|
||||
present with different value → skip (`frontmatter-key-conflict`); key absent →
|
||||
insert. No cached hash is trusted. This resolves the deferred hole noted in
|
||||
`add-check`.
|
||||
5. **Bonus (v2)** — deterministic patch-apply hardening + token estimator
|
||||
(local tokenizer, injection-frequency weighting, bottom-up rollup).
|
||||
|
||||
## Intended layout (per cc-plugins conventions)
|
||||
|
||||
```
|
||||
doc-hygiene/
|
||||
├── .claude-plugin/plugin.json # done
|
||||
├── PRD.md # done — source of truth
|
||||
├── CLAUDE.md # this file
|
||||
├── invariants.md # TODO — declare behavioral invariants
|
||||
├── skills/ # slash-command entry points: /os-doc-hygiene:<skill>
|
||||
│ ├── check/SKILL.md
|
||||
│ ├── clean/SKILL.md
|
||||
│ ├── status/SKILL.md
|
||||
│ └── sweep/SKILL.md
|
||||
├── scripts/ # Python OOP — scanner, state, reminder, estimator, report_builder, applier
|
||||
│ └── ...
|
||||
├── hooks/hooks.json # SessionStart wiring → reminder script (systemMessage banner)
|
||||
├── examples/golden/ # classifier golden examples (input tree → expected report)
|
||||
└── tests/ # unit tests per deterministic seam + fixture doc trees
|
||||
```
|
||||
|
||||
## Conventions (inherited from cc-plugins)
|
||||
|
||||
- **Language**: Python, OOP — small single-responsibility classes, dependency
|
||||
injection, immutable transforms where possible. See
|
||||
`../cc-architect/references/tool-patterns/deterministic-scripting.md`.
|
||||
- **Scripts**: structured JSON output, correct exit codes, idempotent, testable
|
||||
in isolation with injected clock/filesystem.
|
||||
- **Model routing**: scripts = no model; classification = Sonnet (hard cases →
|
||||
Opus); generative distillation = Sonnet (NOT Haiku); orchestration = Opus.
|
||||
- **Reversion protection**: `invariants.md` + `examples/golden/` + change-impact
|
||||
analysis + human gate for golden-example changes. See
|
||||
`../cc-architect/references/tool-patterns/reversion-protection.md`.
|
||||
- Read a directory's `CONTEXT.md` before its source files (progressive
|
||||
disclosure); generate one per directory as it's built.
|
||||
|
||||
## Completion steps (do NOT do until functional)
|
||||
|
||||
1. Add `invariants.md` and 3–5 golden examples.
|
||||
2. Register in `../.claude-plugin/marketplace.json` (name + source + description,
|
||||
matching `plugin.json`). See `../.claude/references/plugin-registration.md`.
|
||||
3. Verify installable via `/install doc-hygiene@cc-plugins`.
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
# Handoff — `add-deterministic-core` apply (paused at build-spike gate)
|
||||
|
||||
**Status: 15 / 25 tasks complete.** Resume with `/opsx:apply add-deterministic-core`.
|
||||
Nothing below is blocked on code — it is blocked on one human visual check.
|
||||
|
||||
## Where we are
|
||||
|
||||
| Group | Tasks | State |
|
||||
|---|---|---|
|
||||
| 1 — build-spike (SessionStart banner) | 1.1 ✓ · 1.2 ⛔ gate · 1.3 ⛔ | **AWAITING VISUAL CONFIRMATION** |
|
||||
| 2 — scanner (`doc-scanner`) | 2.1–2.6 ✓ | done — `scripts/scanner.py`, 56 tests pass |
|
||||
| 3 — state store (`state-store`) | 3.1–3.8 ✓ | done — `scripts/state_store.py`, 30 tests pass |
|
||||
| 4 — reminder hook (`session-reminder`) | 4.1–4.4 | NOT STARTED — needs Group 3 + confirmed hook format (1.3) |
|
||||
| 5 — cross-seam verification | 5.1–5.4 | NOT STARTED — run last; 5.3 writes `scripts/CONTEXT.md`, 5.4 is `openspec validate --strict` |
|
||||
|
||||
## THE GATE (task 1.2) — do this first on resume
|
||||
|
||||
Task 1.2 requires a human to start a real Claude Code session and **visually confirm** the spike banner renders. Everything downstream (1.3 → Group 4 → Group 5) is gated on it. The enable path is ALREADY fully set up — do NOT re-derive it:
|
||||
|
||||
- The plugin `doc-hygiene@cc-plugins` is **already installed, enabled, and cached**. (`enabledPlugins."doc-hygiene@cc-plugins": true` in `~/.claude/settings.json`; marketplace registered under `extraKnownMarketplaces.cc-plugins` → directory `/home/jared/dev/cc-plugins`.)
|
||||
- There is **no `/install` slash command** in this build (v2.1.181) — plugin management is the `claude plugin ...` CLI, already run.
|
||||
- CC loads hooks from the cache snapshot `~/.claude/plugins/cache/cc-plugins/doc-hygiene/0.0.1/hooks/hooks.json`, which is **byte-identical to source**. If `hooks/hooks.json` is edited later, run `claude plugin update doc-hygiene@cc-plugins` to re-snapshot before testing.
|
||||
- The `systemMessage` banner mechanism is **proven** in this build — memsearch's SessionStart banner renders the same way.
|
||||
|
||||
**To confirm:** start a fresh Claude Code session (any directory; plugin is user-scoped) and look for, alongside the memsearch line:
|
||||
|
||||
[doc-hygiene] Documentation health reminder is active. Run /os-doc-hygiene:check to scan for stale or bloated docs.
|
||||
|
||||
- **Renders** → check task 1.2, then do task 1.3 (record the confirmed format: plugin-root `hooks/hooks.json`, top-level `hooks` key, per-matcher nested `hooks` array, `systemMessage` field, `matcher: startup|resume`, `timeout: 5`). Then build Group 4, then Group 5.
|
||||
- **Blank** → the hooks.json format or loading is wrong for this build; debug BEFORE Group 4 inherits the format. (Format currently mirrors the canonical corrected shape from commit `4d595a9`, avoiding the 3 prior hook-format bugs.)
|
||||
|
||||
## Group 4 build notes (when unblocked)
|
||||
|
||||
- 4.1: `Reminder` decision PURE over `(last_check, last_reminded, now, threshold)` per design D5 — silent when fresh; banner when stale and not snoozed; snooze keyed on **calendar day** via `last_reminded`.
|
||||
- 4.2: reminder script reads state via the Group 3 `StateStore` (already built) with an injected clock; on banner emit `systemMessage` naming the slash command + write `last_reminded = now`; on silent write nothing; always exit 0; missing/unreadable state = never-checked (stale). No scan, no model (invariant #1).
|
||||
- 4.3: update `hooks/hooks.json` to invoke the reminder script (reuse confirmed format from 1.3), `timeout` ≤ 5s. This REPLACES the spike's fixed banner.
|
||||
- 4.4: tests with injected clock (invariants #1, #2): fresh→silent; stale+never→banner; stale+reminded-same-day→silent; stale+reminded-prior-day→banner; assert only `last_reminded` mutated, only on banner.
|
||||
- Build Group 4 with **Sonnet** (real systems Python), not Haiku.
|
||||
|
||||
## Open decision for the user (flag, not yet resolved)
|
||||
|
||||
Scanner deviation (tasks 2.2/2.3): the implementer read design **D7** as placing *append-only* in the **exclusion pipeline only**, NOT as a per-path signal (an append-only file is excluded before signals run, so an `append_only_growth` signal would be self-contradictory). The dead signal was removed. The spec's signal list mentioned "append-only growth" only as an illustrative "for example." **Confirm this reading is acceptable**, or restore append-only as a signal.
|
||||
|
||||
## Conventions for the resuming session
|
||||
|
||||
- Orchestrator-subagent pattern (project CLAUDE.md): delegate via Agent tool; do NOT call Read/Write/Bash/Grep/Glob/Edit directly. Model routing: scripts=no model (author with Sonnet for real logic), classification=Sonnet, orchestration=Opus.
|
||||
- Each parallel agent owns disjoint files; serialize `tasks.md` checkbox edits in the orchestrator to avoid write races.
|
||||
- Do NOT push commits. Do NOT commit `map-driven-planning/docs/`.
|
||||
- Changing any invariant requires `invariants.md` + golden-examples update + explicit human META-RULE approval. (Known pre-existing: `invariants.md` #2 "Why" prose still has stale `clear`/`compact` wording — behavior is correct; prose fix is META-RULE-gated and untouched.)
|
||||
|
||||
## Files created this session
|
||||
- `hooks/hooks.json` (spike banner)
|
||||
- `scripts/scanner.py`, `scripts/state_store.py`
|
||||
- `tests/test_scanner_exclusions.py`, `tests/test_scanner_signals.py`, `tests/test_state_store.py`
|
||||
- `tests/fixtures/scanner/...`, `tests/fixtures/state_store/...`
|
||||
- `.claude-plugin/marketplace.json` (parent repo — doc-hygiene registration)
|
||||
- `openspec/changes/add-deterministic-core/tasks.md` (15 boxes checked)
|
||||
|
|
@ -1,307 +0,0 @@
|
|||
# PRD: `doc-hygiene` plugin
|
||||
|
||||
> Status: `ready-for-agent` · Working name: `doc-hygiene` · Target: `~/dev/cc-plugins/doc-hygiene/`
|
||||
|
||||
## Problem Statement
|
||||
|
||||
When I work intensively in a project, documentation accumulates two distinct
|
||||
defects that both degrade how usefully an AI agent (and I) can read the repo:
|
||||
|
||||
- **Stale docs** — content that is now *wrong*: it contradicts the running
|
||||
system, references files/symbols/paths that no longer exist, is pinned to an
|
||||
old tool version, claims "in progress" for shipped work, or duplicates a fact
|
||||
that has since drifted. Trusting it causes wrong actions. This is the
|
||||
high-severity class because agents read confidently and don't sanity-check.
|
||||
- **Bloated docs** — content that is still *true* but mostly irrelevant: long
|
||||
debugging narratives, resolved-problem detail at full fidelity, decisions
|
||||
recorded without distillation, append-only growth. It doesn't lie; it dilutes
|
||||
attention and burns context-window budget, and buries the one live caveat in
|
||||
archaeology.
|
||||
|
||||
Severity scales with **injection frequency**: a stale line in a doc nobody opens
|
||||
is annoying; the same line in an auto-injected file (`CLAUDE.md`, memory index)
|
||||
misleads *every* session unprompted.
|
||||
|
||||
Today I notice this only incidentally, have no systematic way to find it across
|
||||
projects, and cleaning it up by hand wastes both my time and AI tokens. I also
|
||||
don't want a tool that interrupts my flow or silently rewrites my repos.
|
||||
|
||||
## Solution
|
||||
|
||||
A Claude Code plugin, installed globally but operating per-project, that:
|
||||
|
||||
1. **Reminds, doesn't nag.** A deterministic `SessionStart` hook checks an
|
||||
in-project timestamp and, only if the last hygiene check is older than a
|
||||
threshold, injects a noticeable reminder telling me how many days it's been
|
||||
and which command to run. Zero AI tokens unless I act. Snoozes so it reminds
|
||||
at most once per day while stale.
|
||||
2. **Checks on demand.** A `check` skill runs a **deterministic scan** that
|
||||
gathers objective signals and a candidate shortlist, then an **AI
|
||||
classification pass** that reads candidates, categorizes them (stale vs
|
||||
bloat, by sub-type), assigns each a concrete operation tagged by
|
||||
*op-type* (`deterministic` | `generative`) and *safety tier* (`auto` |
|
||||
`confirm`), and estimates context-weight savings. It produces a **human
|
||||
report** (what to clean and why) and a **machine report** (structured ops
|
||||
for the cleaner to consume).
|
||||
3. **Cleans, git-safely and scoped.** A `clean` skill consumes the machine
|
||||
report. For each op it checks an **mtime guard** (file unchanged since the
|
||||
check); `deterministic` ops are applied mechanically with no model;
|
||||
`generative` ops are delegated to Sonnet subagents. `auto`-tier ops run
|
||||
without fuss; `confirm`-tier ops escalate into an approval list. Cleanup runs
|
||||
only on a clean/committed tree (or after an auto-checkpoint) and lands as a
|
||||
single reviewable commit. The user can scope a run to specific categories or
|
||||
files.
|
||||
4. **Composes.** A `sweep` entry runs check-then-clean in one go, still
|
||||
surfacing the report and gating `confirm`-tier ops.
|
||||
|
||||
The design leads with deterministic code (scan, signals, state, patch-apply,
|
||||
token estimate) and reserves AI for the genuine judgment (classification and
|
||||
prose distillation). This keeps it fast, cheap, and trustworthy.
|
||||
|
||||
## User Stories
|
||||
|
||||
1. As a developer, I want a deterministic `SessionStart` reminder when a
|
||||
project's docs haven't been checked in over a threshold, so that I'm nudged
|
||||
without spending any AI tokens.
|
||||
2. As a developer, I want the reminder to be visually noticeable and to name the
|
||||
exact command to run, so that acting on it is one step.
|
||||
3. As a developer, I want the reminder to snooze (at most once/day while stale),
|
||||
so that it doesn't nag me every session for a week.
|
||||
4. As a developer, I want hygiene state stored per-project in a gitignored dot
|
||||
directory, so that there is no global index to corrupt, race, or itself go
|
||||
stale.
|
||||
5. As a developer, I want to run a check manually with a slash command, so that
|
||||
after a heavy context-building session I can clear what accumulated.
|
||||
6. As a developer, I want the check to deterministically scan scoped files and
|
||||
compute objective signals (broken references, version skew, edit-recency vs
|
||||
git churn, location, append-only growth, archive-to-live ratio, frontmatter
|
||||
markers), so that the AI pass starts from facts, not a blank read.
|
||||
7. As a developer, I want the AI pass to classify each candidate as stale
|
||||
(contradicted, orphaned, superseded, provisional, completed-in-place, duplicated) or
|
||||
bloat (distill, split, freeze), so that the right remedy is chosen per file.
|
||||
8. As a developer, I want each recommended operation tagged with an op-type
|
||||
(`deterministic` | `generative`) and a safety tier (`auto` | `confirm`), so
|
||||
that the cleaner knows what it can apply mechanically and what needs a model
|
||||
or my approval.
|
||||
9. As a developer, I want a human-readable report describing what should be
|
||||
cleaned and *why*, grouped by category, so that I can decide at a glance.
|
||||
10. As a developer, I want a machine-readable report the cleaner consumes
|
||||
directly, so that the cleanup step doesn't re-derive the analysis.
|
||||
11. As a developer, I want only the single most-recent report kept (rollover
|
||||
deletes prior reports), so that the tool's own artifacts don't become bloat.
|
||||
12. As a developer, I want the check to estimate context-weight savings per
|
||||
file, summed into categories and a total (bottom-up), so that I can
|
||||
prioritize.
|
||||
13. As a developer, I want savings honestly framed and weighted by injection
|
||||
frequency (auto-injected files counted as real per-session savings,
|
||||
on-demand docs as theoretical-max), so that the numbers aren't misleading.
|
||||
14. As a developer, I want the cleaner to apply `deterministic`+`auto` ops
|
||||
(move-to-archive, freeze-stamp, known-target link fix, exact-dup dedupe)
|
||||
with no model and no prompt, so that no-brainer cleanup just happens.
|
||||
15. As a developer, I want every destructive, subjective, or generative op to be
|
||||
`confirm`-tier and escalated for my approval, so that nothing surprising
|
||||
happens to my repos.
|
||||
16. As a developer, I want a per-op mtime guard so that if a file changed since
|
||||
the check, its cached edit is skipped (and re-analysis recommended) rather
|
||||
than blindly applied.
|
||||
17. As a developer, I want generative cleanup (distilling narrative into live
|
||||
constraints) delegated to a Sonnet subagent, so that distillation quality is
|
||||
high and doesn't drop the constraint that mattered.
|
||||
18. As a developer, I want cleanup to run only on a clean/committed tree, or to
|
||||
auto-commit a WIP checkpoint first, so that my uncommitted work is never
|
||||
lost.
|
||||
19. As a developer, I want each cleanup run to land as a single reviewable
|
||||
commit, so that I can inspect or revert the whole sweep trivially.
|
||||
20. As a developer, I want to scope a cleanup to specific categories or files
|
||||
(e.g. "only the orphaned-reference fixes"), so that I stay in control of how
|
||||
much changes at once.
|
||||
21. As a developer, I want a `sweep` command that runs check-then-clean, so that
|
||||
when I already trust the project I can do both in one step.
|
||||
22. As a developer, I want `sweep` to still surface the report and gate
|
||||
`confirm`-tier ops, so that the convenience path isn't a foot-gun.
|
||||
23. As a developer, I want a status command that reports last-check and
|
||||
last-clean timestamps for the current project, so that I can see where it
|
||||
stands without running anything.
|
||||
24. As a developer, I want to protect files from being flagged via
|
||||
`hygiene: frozen` frontmatter or a `.dochygiene-ignore` file, so that
|
||||
deliberately-frozen records and append-only logs aren't repeatedly flagged.
|
||||
25. As a developer, I want sensible scope defaults (markdown + known doc
|
||||
locations; excluding build/vendor/archive/graphify-out dirs) with
|
||||
per-project overrides, so that the scan targets docs and nothing irrelevant.
|
||||
26. As a developer, I want the check and clean steps to update their own
|
||||
timestamps (`last_check`, `last_clean`) and the reminder to track
|
||||
`last_reminded`, so that the lifecycle is observable and the snooze works.
|
||||
27. As a developer, I want the plugin to work in any project where it's enabled
|
||||
without per-project setup beyond enablement, so that adoption is frictionless.
|
||||
28. As a developer, I want classifier behavior protected by golden examples and
|
||||
invariants, so that future edits to the plugin don't silently regress what
|
||||
counts as stale vs bloat.
|
||||
29. As a developer, I want decisions (especially `confirm`-tier approvals)
|
||||
recorded, so that there's an audit trail of what was changed and why.
|
||||
30. As a developer, I want the deterministic scripts to emit structured JSON and
|
||||
correct exit codes, so that they're independently testable and composable.
|
||||
|
||||
## Implementation Decisions
|
||||
|
||||
**Distribution & activation**
|
||||
- New plugin `doc-hygiene` in the `cc-plugins` marketplace. Installed globally,
|
||||
operates per-project. No global state index — the question of "how to key
|
||||
projects globally" is dissolved by per-project storage.
|
||||
- Marketplace registration deferred until the plugin is functional (documented
|
||||
as a build-completion step, not done now).
|
||||
|
||||
**State & artifacts (in-project)**
|
||||
- A gitignored `.dochygiene/` directory at the project root (resolved via git
|
||||
root, fallback cwd) holds:
|
||||
- `state.json` — `last_check`, `last_clean`, `last_reminded` timestamps.
|
||||
- the single most-recent report (human `.md` + machine `.json`).
|
||||
- Rollover: each new check deletes the prior report before writing the new one.
|
||||
- `.dochygiene/` must not be tracked. The tool does **not** silently edit the
|
||||
user's `.gitignore` (that would itself be an outward mutation, against the
|
||||
non-intrusive premise). Instead: on first check it detects whether the dir is
|
||||
ignored and, if not, surfaces a one-line offer to add the entry — applied only
|
||||
on confirmation (or noted in the report). The scanner always self-excludes
|
||||
`.dochygiene/` regardless.
|
||||
|
||||
**Components / modules** (described abstractly; see CLAUDE.md for the build map)
|
||||
- **State store** (deterministic): reads/writes `state.json`; atomic writes;
|
||||
resolves project root.
|
||||
- **Scanner** (deterministic): walks scoped files, computes signals, emits a
|
||||
candidate shortlist with attached signals. Respects scope config + ignore
|
||||
markers + frozen frontmatter + append-only detection.
|
||||
- **Reminder hook** (deterministic, `SessionStart`): reads state, compares to
|
||||
staleness threshold, applies once/day snooze via `last_reminded`, emits a
|
||||
noticeable notice. No model.
|
||||
- *Mechanism (confirmed):* plugins declare hooks in `hooks/hooks.json` at the
|
||||
plugin root (not under `.claude-plugin/`). The visible, zero-token notice is
|
||||
emitted via the hook's JSON `systemMessage` field (a user-facing banner) —
|
||||
NOT `additionalContext` (which is silent to the user). The command names the
|
||||
slash command to run. `matcher` is `startup|resume`.
|
||||
- *Snooze is load-bearing, not polish:* `SessionStart` also fires on `resume`,
|
||||
`clear`, and `compact`. The once/day `last_reminded` snooze is what prevents
|
||||
the banner re-firing after every compaction within a single working session.
|
||||
- *Constraints to respect:* keep `timeout` low (≤5s); exit 0 (never block the
|
||||
session); the script must be fast and side-effect-free beyond reading state.
|
||||
- **Classifier / report builder** (AI — the `check` skill): consumes the
|
||||
shortlist, reads candidates, assigns category + op-type + safety tier, writes
|
||||
exact edits for deterministic ops, and calls the token estimator. Emits human
|
||||
+ machine reports. Updates `last_check`.
|
||||
- **Token estimator** (deterministic helper): counts tokens of removed/reduced
|
||||
spans with a local tokenizer approximation (no API call); weights by injection
|
||||
frequency; rolls up file → category → total. Framed as "context weight
|
||||
reduced," splitting injected vs on-demand.
|
||||
- **Cleanup executor** (the `clean` skill + deterministic patch-applier):
|
||||
consumes the machine report; mtime-guards each op; applies `deterministic` ops
|
||||
mechanically; delegates `generative` ops to Sonnet subagents; runs `auto`
|
||||
freely and escalates `confirm`; enforces git checkpoint + single-commit;
|
||||
scopable by category/file. Updates `last_clean`.
|
||||
- **Sweep** (composition): invokes check then clean, preserving the report
|
||||
surface and `confirm` gate.
|
||||
|
||||
**Operation taxonomy**
|
||||
- Op-type `deterministic`: exact, reversible edits the check pre-computes
|
||||
(delete/move/stamp/known-link-fix/exact-dedupe). Applied with no model.
|
||||
- Op-type `generative`: prose transformations (distill, split) requiring a model
|
||||
at clean time (Sonnet). Deferred — not pre-written at check time — so a check
|
||||
is cheap even when no clean follows.
|
||||
- Safety tier `auto`: deterministic + reversible + objective → runs without
|
||||
prompt.
|
||||
- Safety tier `confirm`: destructive (any delete), subjective, or generative →
|
||||
escalated for approval.
|
||||
|
||||
**Git safety (reversion-protection vocabulary)**
|
||||
- Cleanup requires a clean/committed working tree, or auto-commits a WIP
|
||||
checkpoint first.
|
||||
- Each cleanup run lands as one reviewable commit (the post-state).
|
||||
- `confirm`-tier approvals are logged to a decisions record.
|
||||
|
||||
**Model routing** (per the marketplace convention)
|
||||
- Deterministic scan/state/patch-apply/token-estimate: **no model** (scripts).
|
||||
- Writing those scripts: **Haiku**.
|
||||
- Classification / report building: **Sonnet** (reading comprehension +
|
||||
judgment). The hardest stale-vs-bloat distinctions may escalate to **Opus**.
|
||||
- Generative distillation at clean time: **Sonnet** (explicitly *not* Haiku —
|
||||
this is the highest-judgment task and Haiku produces lossy summaries).
|
||||
- Orchestration: **Opus** (the calling Claude).
|
||||
|
||||
**Language**
|
||||
- **Python**, OOP, small composable single-responsibility classes with
|
||||
dependency injection (per the deterministic-scripting reference), chosen for
|
||||
broad reach in a general-purpose plugin and for testability.
|
||||
|
||||
**Report schema is the linchpin**
|
||||
- The machine report schema (per-file: category, signals, recommended op,
|
||||
op-type, safety tier, optional exact-edit, token estimate) is the contract
|
||||
every other component consumes. It is designed and frozen first.
|
||||
|
||||
## Testing Decisions
|
||||
|
||||
**What makes a good test here:** assert external behavior at the highest
|
||||
deterministic seam — given an input doc tree / state file / report, the script
|
||||
produces the correct structured output and exit code. Do not assert internal
|
||||
class structure. The AI classification layer is pinned by golden examples, not
|
||||
unit assertions.
|
||||
|
||||
**Seams (highest first):**
|
||||
- **Scanner** — input: a fixture doc tree; output: candidate shortlist JSON.
|
||||
Unit-tested against fixtures exercising each signal (broken ref, version skew,
|
||||
churn-vs-edit, location, append-only, frozen frontmatter, ignore file).
|
||||
- **State store** — timestamp read/write, rollover (only latest report kept),
|
||||
snooze logic. Unit-tested with an injected clock and temp dirs.
|
||||
- **Reminder hook** — given a `state.json` + injected clock + threshold, asserts
|
||||
notice / no-notice / snoozed. Unit-tested via the injected clock (no real
|
||||
time, no real session).
|
||||
- **Token estimator** — counts on known spans; injection-frequency weighting;
|
||||
bottom-up rollup. Unit-tested on fixed inputs.
|
||||
- **Patch-applier + mtime guard** — applies deterministic ops on fixtures;
|
||||
skips when fixture mtime is newer than the check timestamp. Unit-tested.
|
||||
- **Classifier (AI)** — golden examples (`examples/golden/`): input doc tree →
|
||||
expected report categorization, per the reversion-protection pattern. Run the
|
||||
check against each; flag mismatches for human review.
|
||||
|
||||
**Prior art:** the `commit` and `cc-architect` plugins pair deterministic
|
||||
`scripts/` with skills and structured output; this plugin follows that split.
|
||||
The reversion-protection reference (`cc-architect/.../reversion-protection.md`)
|
||||
defines the golden-example + invariants approach used for the classifier.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- A global cross-project dashboard / index (dissolved by per-project storage).
|
||||
- Exact Claude token counting via API at check time (local approximation only;
|
||||
exactness isn't worth the latency/cost for a motivational estimate).
|
||||
- Non-markdown content analysis beyond known doc locations (e.g. linting source
|
||||
code comments). Scope defaults to docs.
|
||||
- Auto-running cleanup from the `SessionEnd`/`SessionStart` hook — the hook only
|
||||
ever reminds; all mutation is user-invoked.
|
||||
- Pushing commits or any outbound/network action — cleanup is local commits
|
||||
only; the user pushes.
|
||||
- Editing files outside the resolved project root.
|
||||
- v2 polish: richer per-category savings visualizations, configurable op
|
||||
taxonomies beyond the built-in set.
|
||||
|
||||
## Further Notes
|
||||
|
||||
- **Build order** (the report schema gates everything): (1) machine report
|
||||
schema; (2) deterministic scanner + state store + reminder hook; (3) `check`
|
||||
skill (scanner → AI classify → report); (4) `clean` skill (consume report,
|
||||
git-safe, scoped) + `sweep`; (5) bonus: deterministic patch-apply + token
|
||||
estimator. Patch-apply and token-estimate are explicitly v2 — they optimize a
|
||||
report format that must exist and be proven first.
|
||||
- **Why deterministic-first matters here specifically:** the check does the
|
||||
expensive cognition once (reading + deciding); cleanup executes. Pushing
|
||||
attribute-detection into the scanner and exact edits into the report means
|
||||
most cleanup needs no model at all, and a check is cheap even when no cleanup
|
||||
follows.
|
||||
- **False-positive trust risk:** without the frozen/ignore mechanism and
|
||||
append-only detection, the tool would re-flag deliberately-frozen records
|
||||
every week and lose the user's trust. This is treated as a correctness
|
||||
requirement, not a nicety.
|
||||
- **The tool must not become bloat:** report rollover (keep latest only) and the
|
||||
gitignored dot dir are deliberate guards against the plugin polluting the very
|
||||
repos it cleans.
|
||||
- **Linchpin mechanism verified:** the SessionStart `systemMessage` banner path
|
||||
was confirmed against the Claude Code hook docs before this PRD was finalized
|
||||
(no other plugin in the collection wires a CC hook, so it had no precedent).
|
||||
Build-spike #1 is still to stand up a trivial `hooks/hooks.json` emitting a
|
||||
`systemMessage` and confirm it renders visibly in a real session before
|
||||
building the deterministic core on top of it.
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
# examples/golden/
|
||||
|
||||
Two **distinct** families of golden fixtures live here. Do not conflate them.
|
||||
|
||||
| Family | Location | Maps | Consumed by | Purpose |
|
||||
|--------|----------|------|-------------|---------|
|
||||
| **Schema-shape fixtures** | `valid_report.json`, `invalid_report.json` | (a hand-authored report) → accept / reject | `scripts/validate_report.py` | Pin the frozen machine-report schema contract. |
|
||||
| **Classifier goldens** | `classifier/<n>-<name>/` | input doc-tree (`input/`) → expected classification (`expected.json`) | hermetic `tests/test_classifier_golden.py` + a live model-regression harness | Layer-2 reversion protection for the *classifier* — pin which subtype + op-kind + tier a given doc-tree should yield. |
|
||||
|
||||
Both families are reversion-protection artifacts; changing either is **human-gated**
|
||||
per the META-RULE in `invariants.md`.
|
||||
|
||||
## Schema-shape fixtures
|
||||
|
||||
Hand-authored machine-report JSON files used to verify the schema-validator
|
||||
script (`scripts/validate_report.py`).
|
||||
|
||||
## Contents
|
||||
|
||||
| File | Purpose | Expected validator result |
|
||||
|------|---------|--------------------------|
|
||||
| `valid_report.json` | Well-formed machine report exercising a realistic spread of entry types: deterministic `delete-range` (confirm), deterministic `move-to-archive` (auto), deterministic `insert-frontmatter` (auto, no anchor — exercises the no-anchor path), and a generative distillation entry (confirm). | Exit 0 (accepted) |
|
||||
| `invalid_report.json` | Malformed report with a safety-critical invariant #10 violation: a `delete-range` entry (destructive, irreversible) that records `safety_tier: "auto"` when the derivation yields `"confirm"`. All other fields are otherwise valid so the validator reports only this specific violation. | Exit 1 (rejected), one violation on `entries[0].safety_tier` |
|
||||
|
||||
## Invariants exercised
|
||||
|
||||
- **Invariant #10** (`safety_tier` derivation): `invalid_report.json` is the
|
||||
canonical fixture for this invariant — it is structurally sound except for the
|
||||
safety-tier mismatch.
|
||||
- **Invariant #11** (`op_type`↔`exact_edit` biconditional): both fixtures
|
||||
include deterministic entries with `exact_edit` and a generative entry without
|
||||
one, exercising both directions of the biconditional.
|
||||
|
||||
## Classifier goldens (`classifier/`)
|
||||
|
||||
Input doc-tree → expected classification. See `classifier/README.md` for the full
|
||||
case table, the two harnesses (hermetic unit + live model-regression), and the
|
||||
regeneration recipe. Five cases, each pinning one taxonomy edge to a **distinct**
|
||||
`exact_edit.kind` (plus one generative case) so the suite covers the kind table +
|
||||
tier derivation:
|
||||
|
||||
| Dir | class / subtype | kind | tier | scanner signal |
|
||||
|-----|-----------------|------|------|----------------|
|
||||
| `1-orphaned` | stale / orphaned | `delete-range` | confirm | `broken_reference` |
|
||||
| `2-superseded` | stale / superseded | `move-to-archive` | auto | `stale_name_location` |
|
||||
| `3-completed-in-place` | stale / completed-in-place | `insert-frontmatter` | auto | `archive_to_live_ratio` |
|
||||
| `4-duplicated` | stale / duplicated | `dedupe` | auto | `stale_name_location` (no dedicated dup signal — see case notes) |
|
||||
| `5-distill` | bloat / distill | (none, generative) | confirm | `archive_to_live_ratio` |
|
||||
|
||||
Enforced hermetically by `tests/test_classifier_golden.py` (no model, no network).
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
{
|
||||
"schema_version": "1.0",
|
||||
"tool_version": "0.0.1",
|
||||
"generated_at": "2026-06-24T17:45:00.691072+00:00",
|
||||
"scan": {
|
||||
"project_root": "<input>",
|
||||
"scope_globs": [
|
||||
"**/*.md"
|
||||
],
|
||||
"excluded_dirs": [
|
||||
"build",
|
||||
"vendor",
|
||||
"archive",
|
||||
"graphify-out",
|
||||
".dochygiene",
|
||||
"fixtures",
|
||||
"examples/golden"
|
||||
],
|
||||
"files_scanned": 1
|
||||
},
|
||||
"shortlist": [
|
||||
"docs/orphan.md"
|
||||
],
|
||||
"entries": [
|
||||
{
|
||||
"path": "docs/orphan.md",
|
||||
"category": {
|
||||
"class": "stale",
|
||||
"subtype": "orphaned"
|
||||
},
|
||||
"signals": [
|
||||
{
|
||||
"name": "broken_reference",
|
||||
"detail": "links to './gone.md' which does not exist"
|
||||
}
|
||||
],
|
||||
"op": "Delete the orphaned reference block (lines 5-10) that points at a removed helper and describes a workflow that no longer exists.",
|
||||
"op_type": "deterministic",
|
||||
"is_destructive": true,
|
||||
"is_reversible": false,
|
||||
"safety_tier": "confirm",
|
||||
"exact_edit": {
|
||||
"kind": "delete-range",
|
||||
"anchor": {
|
||||
"start_line": 5,
|
||||
"end_line": 10
|
||||
},
|
||||
"expected_sha256": "5da05aa629ca39925a68d4b1f3f1a86686fdbbdab90b29d56294c5be3b371400",
|
||||
"generated_at": "2026-06-24T17:45:00.690977+00:00"
|
||||
},
|
||||
"token_estimate": {
|
||||
"raw_tokens": 79,
|
||||
"injection_frequency": null,
|
||||
"weighted_tokens": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
# Orphaned Migration Notes
|
||||
|
||||
These notes describe a migration step that pointed at a helper script.
|
||||
|
||||
The procedure referenced [the legacy migration helper](./gone.md) for the
|
||||
manual cut-over. That helper file was removed long ago, so this section now
|
||||
points at nothing and documents a step that can no longer be performed.
|
||||
|
||||
Nothing else in the repository links here, and the workflow it describes no
|
||||
longer exists.
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
# 1-orphaned
|
||||
|
||||
Pins the **stale / orphaned** taxonomy edge → `delete-range` exact-edit kind →
|
||||
derived `confirm` tier (delete-range is destructive + irreversible).
|
||||
|
||||
**Scanner signal exercised:** `broken_reference`. `docs/orphan.md` links to
|
||||
`./gone.md`, which is absent from `input/`, so the scanner emits exactly one
|
||||
`broken_reference` signal on `docs/orphan.md`. The doc is otherwise unreferenced
|
||||
and describes a dead workflow — the coherent justification for *orphaned* rather
|
||||
than a link-repair (`replace-text`): there is no live target to repair to, so the
|
||||
block is deleted.
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
[
|
||||
{
|
||||
"path": "docs/orphan.md",
|
||||
"category": { "class": "stale", "subtype": "orphaned" },
|
||||
"signals": [
|
||||
{ "name": "broken_reference", "detail": "links to './gone.md' which does not exist" }
|
||||
],
|
||||
"op": "Delete the orphaned reference block (lines 5-10) that points at a removed helper and describes a workflow that no longer exists.",
|
||||
"op_type": "deterministic",
|
||||
"exact_edit": {
|
||||
"kind": "delete-range",
|
||||
"anchor": { "start_line": 5, "end_line": 10 }
|
||||
},
|
||||
"gloss": "The only inbound/outbound link is dead and nothing references this doc; the block documents a step that can no longer run."
|
||||
}
|
||||
]
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
{
|
||||
"schema_version": "1.0",
|
||||
"tool_version": "0.0.1",
|
||||
"generated_at": "2026-06-24T17:45:00.792786+00:00",
|
||||
"scan": {
|
||||
"project_root": "<input>",
|
||||
"scope_globs": [
|
||||
"**/*.md"
|
||||
],
|
||||
"excluded_dirs": [
|
||||
"build",
|
||||
"vendor",
|
||||
"archive",
|
||||
"graphify-out",
|
||||
".dochygiene",
|
||||
"fixtures",
|
||||
"examples/golden"
|
||||
],
|
||||
"files_scanned": 1
|
||||
},
|
||||
"shortlist": [
|
||||
"docs/old-plan.md"
|
||||
],
|
||||
"entries": [
|
||||
{
|
||||
"path": "docs/old-plan.md",
|
||||
"category": {
|
||||
"class": "stale",
|
||||
"subtype": "superseded"
|
||||
},
|
||||
"signals": [
|
||||
{
|
||||
"name": "stale_name_location",
|
||||
"detail": "filename 'old-plan.md' contains 'old' \u2014 may indicate superseded content"
|
||||
}
|
||||
],
|
||||
"op": "Move the superseded plan to archive/docs/old-plan.md to preserve history without keeping it live.",
|
||||
"op_type": "deterministic",
|
||||
"is_destructive": false,
|
||||
"is_reversible": true,
|
||||
"safety_tier": "auto",
|
||||
"exact_edit": {
|
||||
"kind": "move-to-archive",
|
||||
"anchor": {
|
||||
"start_line": 1,
|
||||
"end_line": 13
|
||||
},
|
||||
"dest_path": "archive/docs/old-plan.md",
|
||||
"expected_sha256": "a1f247505584b851f2303ef6fe04d657180f9a3bb8301d588a02c649b5c68792",
|
||||
"generated_at": "2026-06-24T17:45:00.792702+00:00"
|
||||
},
|
||||
"token_estimate": {
|
||||
"raw_tokens": 103,
|
||||
"injection_frequency": null,
|
||||
"weighted_tokens": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
# Old Rollout Plan
|
||||
|
||||
This is the original rollout plan for the v1 launch. It has been fully
|
||||
superseded by the current plan and is retained only for historical reference.
|
||||
|
||||
## Phases
|
||||
|
||||
1. Stand up the staging cluster.
|
||||
2. Cut traffic over during the maintenance window.
|
||||
3. Decommission the old fleet.
|
||||
|
||||
The content here is still accurate as a record but no longer drives any
|
||||
work — the live plan replaced it entirely.
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
# 2-superseded
|
||||
|
||||
Pins the **stale / superseded** taxonomy edge → `move-to-archive` exact-edit kind
|
||||
→ derived `auto` tier (move-to-archive is non-destructive + reversible).
|
||||
|
||||
**Scanner signal exercised:** `stale_name_location`. The filename
|
||||
`docs/old-plan.md` contains `old`, which trips the location/name signal. The body
|
||||
is still accurate as a historical record but fully replaced by the live plan — so
|
||||
the op preserves history by relocating the whole file to `archive/docs/old-plan.md`
|
||||
rather than deleting it (anchor spans the entire file, lines 1-13).
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue