Merge branch 'worktree-os-plugin-standards-audit' (os-plugin standards audit)
Conflicts in CLAUDE.md and docs/implementation-status.md resolved keep-both: main's Planka-retirement changes + the branch's standards-audit additions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014HWNfGWoWnhrub4EJa1M1Y
This commit is contained in:
commit
65cc751c6f
|
|
@ -22,7 +22,7 @@
|
|||
{
|
||||
"name": "os-context",
|
||||
"source": "./plugins/os-context",
|
||||
"description": "Prompt-composer SessionStart plugin: concatenates prompts/session-start/*.md (currently session-orchestration rules — delegation threshold, explicit model routing on Agent spawns) into one additionalContext block, injected at session start and after compaction."
|
||||
"description": "Prompt-composer SessionStart plugin: concatenates prompts/session-start/*.md (session-orchestration + delegation-economics rules, kickoff conventions, standing safety rules, decision-memo format) into one additionalContext block, injected at session start and after compaction."
|
||||
},
|
||||
{
|
||||
"name": "os-doc-hygiene",
|
||||
|
|
|
|||
|
|
@ -80,8 +80,9 @@ before modifying any of these:
|
|||
design-template skills, SessionStart/End hooks, memsearch + vault git sync. Write-behavior
|
||||
eval harness in `plugins/os-vault/eval/`.
|
||||
- **os-context** (`plugins/os-context/`, renamed 2026-07-13 from os-orchestration) — a
|
||||
prompt-composer SessionStart plugin: concatenates `prompts/session-start/*.md` (currently
|
||||
just `10-orchestration.md`, the session-orchestration + delegation-economics rules) into
|
||||
prompt-composer SessionStart plugin: concatenates `prompts/session-start/*.md`
|
||||
(orchestration/delegation rules, kickoff conventions, standing safety, decision-memo
|
||||
format) into
|
||||
one additionalContext block for all sessions; this repo carries no local override. Eval
|
||||
harness in `plugins/os-context/eval/`.
|
||||
- **os-status** (`plugins/os-status/`) — aggregated deterministic SessionStart checks
|
||||
|
|
@ -148,7 +149,8 @@ OpenSpec artifacts comes from `docs/` and this file.
|
|||
and never let the index file regrow into a changelog — the os-doc-hygiene `file_length`
|
||||
signal (400 lines / ~4k tokens) enforces this.
|
||||
- **Plugin and skill naming:** before naming ANY new cc-os plugin, skill, or slash command,
|
||||
Read `~/Documents/SecondBrain/cc-os-plugin-skill-naming-convention.md` and follow it. In
|
||||
Read `plugins/cc-architect/references/conventions/cc-os-naming.md` (canonical repo copy;
|
||||
the SecondBrain vault note of the same name now defers to it) and follow it. In
|
||||
brief: plugins are `os-[domain]`; skills are verb-first kebab-case, invoked as
|
||||
`/os-[domain]:[verb]`; no `commands/` dispatcher directories; **never set a `name:` field
|
||||
in SKILL.md frontmatter** (it collapses the slash command to a bare unnamespaced form).
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Repo-level test runner for the mixed minitest/pytest suites under plugins/.
|
||||
#
|
||||
# bin/test # run every plugin suite
|
||||
# bin/test os-backlog # run one plugin's suites (repeatable)
|
||||
#
|
||||
# Per plugin it runs `ruby tests/all.rb` when present (minitest) and
|
||||
# `pytest tests/` when any tests/*_test.py or tests/test_*.py exists.
|
||||
# pytest runs once per plugin so same-named test files (hook_test.py in
|
||||
# several plugins) never collide on module import.
|
||||
# os-vault's tests/ is an eval harness (harness.sh), not a unit suite,
|
||||
# and is intentionally not run here.
|
||||
|
||||
require "open3"
|
||||
|
||||
class PluginSuite
|
||||
RUNNERS = {
|
||||
minitest: ->(dir) { ["ruby", "tests/all.rb"] },
|
||||
pytest: ->(dir) { ["pytest", "-q", "tests"] }
|
||||
}.freeze
|
||||
|
||||
attr_reader :name
|
||||
|
||||
def initialize(dir)
|
||||
@dir = dir
|
||||
@name = File.basename(dir)
|
||||
end
|
||||
|
||||
def kinds
|
||||
kinds = []
|
||||
kinds << :minitest if File.exist?(File.join(@dir, "tests", "all.rb"))
|
||||
kinds << :pytest if pytest_files?
|
||||
kinds
|
||||
end
|
||||
|
||||
def run(kind)
|
||||
command = RUNNERS.fetch(kind).call(@dir)
|
||||
stdout, status = Open3.capture2e(*command, chdir: @dir)
|
||||
[status.success?, stdout]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def pytest_files?
|
||||
Dir.glob(File.join(@dir, "tests", "{test_*,*_test}.py")).any?
|
||||
end
|
||||
end
|
||||
|
||||
repo_root = File.expand_path("..", __dir__)
|
||||
requested = ARGV
|
||||
suites = Dir.glob(File.join(repo_root, "plugins", "*"))
|
||||
.select { |d| File.directory?(d) }
|
||||
.map { |d| PluginSuite.new(d) }
|
||||
.select { |s| requested.empty? || requested.include?(s.name) }
|
||||
.sort_by(&:name)
|
||||
|
||||
unknown = requested - suites.map(&:name)
|
||||
abort "bin/test: no such plugin(s): #{unknown.join(", ")}" unless unknown.empty?
|
||||
|
||||
failures = []
|
||||
ran = 0
|
||||
suites.each do |suite|
|
||||
suite.kinds.each do |kind|
|
||||
ran += 1
|
||||
ok, output = suite.run(kind)
|
||||
puts "#{ok ? "PASS" : "FAIL"} #{suite.name} (#{kind})"
|
||||
unless ok
|
||||
failures << "#{suite.name} (#{kind})"
|
||||
puts output.lines.map { |l| " #{l}" }.join
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if ran.zero?
|
||||
puts "bin/test: no test suites found"
|
||||
elsif failures.empty?
|
||||
puts "#{ran} suites passed"
|
||||
else
|
||||
abort "#{failures.size}/#{ran} suites failed: #{failures.join(", ")}"
|
||||
end
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# cc-os implementation status — index
|
||||
|
||||
_Last updated: 2026-07-13. This file is an **index with progressive disclosure** (see the
|
||||
_Last updated: 2026-07-16. This file is an **index with progressive disclosure** (see the
|
||||
index+PD convention ADR): headline timeline entries and per-component one-liners live here;
|
||||
full per-component detail lives in leaf files under `docs/implementation-status/`, read on
|
||||
demand. When a build step completes, add a **one-line** timeline entry here and put the
|
||||
|
|
@ -84,6 +84,11 @@ entry is in the leaf file named.
|
|||
ovh-vps `~/planka-final-snapshot-2026-07-16/`). Server decommissioned same day
|
||||
(containers/cron/creds removed); planka gem repo archived read-only. Detail:
|
||||
os-backlog leaf.
|
||||
- **2026-07-16** — os-* standards audit vs cc-architect (post Phase B): every plugin got an
|
||||
index-style CLAUDE.md, os-doc-hygiene's cc-plugins-era docs reconciled, invariants.md
|
||||
added (os-shortcuts, os-vault), naming convention folded into cc-architect references,
|
||||
repo-level `bin/test` runner added. Detail:
|
||||
[implementation-status/standards-audit.md](implementation-status/standards-audit.md).
|
||||
|
||||
**Remaining optional items:** additional project onboarding (one at a time, per ADR-0013);
|
||||
bulk vault migration; os-adr rollout to pilot projects; os-backlog routing rollout (#14);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
# os-* standards audit vs cc-architect — 2026-07-16
|
||||
|
||||
One-time audit executed after cc-architect's Phase B migration into cc-os (commit
|
||||
3f76755), per the backlog ticket "Audit os-* plugins against cc-architect standards
|
||||
(post standards-centralization)". Ran cc-architect's `workflows/audit-plugin.md`
|
||||
Phase 1–2 checklist against all eight os-* plugins, then remediated.
|
||||
|
||||
## Results
|
||||
|
||||
All eight plugins pass manifest validation (name/version/description, kebab-case,
|
||||
semver), have no `name:` fields in SKILL.md frontmatter, and have no `commands/`
|
||||
dispatcher directories.
|
||||
|
||||
Remediation shipped:
|
||||
|
||||
- **Per-plugin CLAUDE.md indexes** (ADR-0032 index+PD style) created for os-adr,
|
||||
os-backlog (minimal — Planka-retirement rework in flight), os-context, os-sdlc,
|
||||
os-shortcuts, os-status, os-vault; os-doc-hygiene's stale pre-build cc-plugins-era
|
||||
CLAUDE.md rewritten.
|
||||
- **os-doc-hygiene cc-plugins references reconciled**: PRD.md target path and
|
||||
marketplace naming corrected; broken `../.claude/references/` path and retired
|
||||
`/install doc-hygiene@cc-plugins` instructions removed.
|
||||
- **invariants.md** added for os-shortcuts (4 invariants) and os-vault (8); already
|
||||
existed for os-adr, os-doc-hygiene, os-status. os-backlog deferred (rework in
|
||||
flight); os-sdlc/os-context not warranted yet.
|
||||
- **ADR-0025 / os-adr**: no action — the ADR itself records the per-verb-`bin/`
|
||||
exemption ("grandfathered... until os-adr is next touched for other reasons").
|
||||
- **Naming convention** folded into the repo:
|
||||
`plugins/cc-architect/references/conventions/cc-os-naming.md` is now canonical;
|
||||
the SecondBrain vault note and root CLAUDE.md point to it.
|
||||
- **Repo-level test runner**: `bin/test` runs every plugin's minitest
|
||||
(`tests/all.rb`) and pytest suite, one pytest invocation per plugin (avoids
|
||||
same-named `hook_test.py` module collisions). os-vault's `tests/` is an eval
|
||||
harness, deliberately excluded.
|
||||
- **os-vault hook wiring documented**: hooks are intentionally wired via user-level
|
||||
`~/.claude/settings.json` pointing at repo source scripts, not a plugin
|
||||
`hooks/hooks.json`; recorded in os-vault/CLAUDE.md so the absent hooks.json stops
|
||||
reading as a defect.
|
||||
|
||||
## Deferred (filed, not fixed)
|
||||
|
||||
- os-backlog ADR-0025 deviations (second entry point `bin/wakeup-poll`; no README
|
||||
naming gem deps) → Forgejo issue jared/cc-os#74, to be folded into the
|
||||
retire-planka-git-issues-only rework.
|
||||
- os-context `skills/audit-sessions/SKILL.md` stale Planka references and os-status
|
||||
`planka:` tracker-scheme handling — both already being fixed by the in-flight
|
||||
ADR-0042 rework on main; not touched here to avoid conflicts.
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
# cc-os Plugin and Skill Naming Convention
|
||||
|
||||
Canonical repo copy of the convention established 2026-07-02 (previously only in the
|
||||
SecondBrain vault note `cc-os-plugin-skill-naming-convention.md`, which now points here).
|
||||
Read this before naming ANY new cc-os plugin, skill, or slash command.
|
||||
|
||||
## Core principles
|
||||
|
||||
1. **Plugins use an `os-` prefix**: `os-[domain]` (e.g. `os-vault`, `os-backlog`). The
|
||||
prefix ties the plugin to cc-os and distinguishes it from unrelated tools
|
||||
(`memsearch` is NOT cc-os).
|
||||
2. **Skills are verb-first**: the plugin namespace answers "what is this about?", the
|
||||
skill name answers "what action?" — `query`, `write`, `reorganize`, producing
|
||||
invocations like `/os-vault:query`.
|
||||
3. **Multi-word skills are kebab-case, verb + noun**: `design-template`,
|
||||
`onboard-project`. Kebab-case in plugin `name` fields is a Claude Code requirement;
|
||||
cc-os extends it to skill names by convention.
|
||||
4. **No `commands/` dispatcher directories** — skills are the only invocation surface.
|
||||
|
||||
## Registration mechanics
|
||||
|
||||
**Never set a `name:` field in SKILL.md frontmatter.** The directory name is the skill
|
||||
name; with `name:` absent, Claude Code registers the namespaced `/plugin:skill` form.
|
||||
An explicit `name: find` collapses the command to bare unnamespaced `/find`
|
||||
(discovered 2026-07-04 in os-adr and os-doc-hygiene; fixed by deleting the lines).
|
||||
After any SKILL.md edit, run `bin/refresh-plugins` — installs copy files into
|
||||
`~/.claude/plugins/cache/`, so source edits don't reach sessions until refreshed.
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
- Plugin name without the `os-` prefix (`vault` instead of `os-vault`).
|
||||
- Noun-stacked skill names (`vault-query` instead of `query`).
|
||||
- Generic verbs duplicating the namespace context (`/memory-find` instead of
|
||||
`/os-vault:find` — the namespace provides the context).
|
||||
- CamelCase or snake_case in plugin or skill names.
|
||||
|
||||
## Open questions
|
||||
|
||||
Agent-name and hook-script-name governance are deferred until their use patterns
|
||||
stabilize; hook scripts currently use snake_case (Python convention) and are not
|
||||
externally visible.
|
||||
|
||||
## Cross-references
|
||||
|
||||
- ADR-0025 (Ruby structure standard — the structural layer below this naming layer)
|
||||
- `references/layout/plugin-structure.md`, `references/layout/skill-structure.md`
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
# os-adr
|
||||
|
||||
ADR (architecture decision record) system for cc-os and pilot projects: numbered
|
||||
`docs/adr/` files + generated index, migration of legacy decision logs, deterministic
|
||||
find/create/init workflows. Full design/build history: `docs/implementation-status/os-adr.md`.
|
||||
|
||||
## Component map
|
||||
|
||||
- `skills/` — four verbs, each `/os-adr:<verb>`: `create`, `find`, `init`, `migrate`.
|
||||
- `lib/adr.rb` + `lib/adr/{detector,finder,index,migration_report,migrator,record,
|
||||
repository,template}.rb` — single `Adr` namespace, `require_relative` loading,
|
||||
stdlib-only (no external gems).
|
||||
- `bin/` — **per-verb scripts** (`adr-new`, `adr-find`, `adr-init`, `adr-migrate`,
|
||||
`adr-detect`), invoked as `ruby ${CLAUDE_PLUGIN_ROOT}/bin/adr-<verb>` from skills.
|
||||
This is **not** the single-dispatcher pattern ADR-0025 requires of newer plugins —
|
||||
os-adr is explicitly grandfathered (see below).
|
||||
- `templates/adr.md` — the ADR template migration and creation write against.
|
||||
- `hooks/hooks.json` — SessionStart entry is intentionally empty; the check itself
|
||||
moved to os-status's `adr-system-present` (`hooks/session_start.py` here is kept
|
||||
only as the wording source of record).
|
||||
- `tests/` — minitest, `ruby tests/all.rb` (glob-runs `*_test.rb`) + `test_helper.rb`
|
||||
tmpdir fixtures; `hook_test.py` covers the hooks-parity case.
|
||||
- `invariants.md` — reversion-protection contract; read before touching detection,
|
||||
migration, indexing, or find-filtering logic.
|
||||
- `eval/`, `eval-b/`, `eval-c/` — Eval A/B/C harnesses (prompted grid, held-out
|
||||
unprompted-behavior, ambiguity-ladder). See `docs/implementation-status/os-adr.md`
|
||||
for what each measures; eval discipline (reserves never read informally) per root
|
||||
`CLAUDE.md`.
|
||||
|
||||
## ADR-0025 exemption
|
||||
|
||||
The per-verb `bin/` layout (rather than a single `bin/os-adr` dispatcher) is
|
||||
**grandfathered by
|
||||
[ADR-0025](../../docs/adr/0025-standard-ruby-structure-for-cc-os-plugins-lib-single-dispatcher-bin-fail-soft-installed-gem-only-dependencies.md)**:
|
||||
the exemption is recorded there explicitly ("not worth a retrofit until os-adr is
|
||||
next touched for other reasons"), not an oversight. Don't "fix" the dispatcher shape
|
||||
as a drive-by — fold it into whatever unrelated work next touches os-adr's `bin/`.
|
||||
|
||||
## Pointers
|
||||
|
||||
- Decisions: `docs/adr/0025-...md` (Ruby structure), `docs/adr/0020` and its
|
||||
amendment (cc-os retrofit).
|
||||
- Status/build history: `docs/implementation-status/os-adr.md`.
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# os-backlog
|
||||
|
||||
Git-issues backlog surface: per-repo tracker routing (`forgejo:`/`github:`/`repo:` in
|
||||
`.cc-os/config`), issue create/list/route skills, ten-label taxonomy.
|
||||
|
||||
Entry points: `bin/os-backlog` (dispatcher CLI), skills `capture`/`list`/`route`,
|
||||
`hooks/` (SessionStart wiring).
|
||||
|
||||
Full detail: `docs/implementation-status/os-backlog.md`. No `invariants.md` yet —
|
||||
see NOTE below.
|
||||
|
||||
NOTE (2026-07-16): a major rework retiring Planka in favor of git-issues-only
|
||||
(ADR-0042, OpenSpec change retire-planka-git-issues-only) is in flight on main;
|
||||
this index intentionally avoids enumerating internals that rework replaces.
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "os-context",
|
||||
"version": "0.1.0",
|
||||
"description": "Prompt-composer SessionStart plugin: concatenates prompts/session-start/*.md (currently session-orchestration rules — delegation threshold, explicit model routing on Agent spawns) into one additionalContext block, injected at session start and after compaction."
|
||||
"description": "Prompt-composer SessionStart plugin: concatenates prompts/session-start/*.md (session-orchestration + delegation-economics rules, kickoff conventions, standing safety rules, decision-memo format) into one additionalContext block, injected at session start and after compaction."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
# os-context
|
||||
|
||||
Prompt-composer `SessionStart` plugin (renamed 2026-07-13 from os-orchestration):
|
||||
concatenates `prompts/session-start/*.md` into one `additionalContext` block,
|
||||
injected at session start and after compaction/resume.
|
||||
|
||||
## Component map
|
||||
|
||||
- `prompts/session-start/` — 4 files, concatenated in filename order:
|
||||
`10-orchestration.md` (session-orchestration + delegation-economics rules — do
|
||||
not edit without explicit approval, per eval-wording sensitivity),
|
||||
`20-kickoff-conventions.md`, `30-standing-safety.md`, `40-decision-memos.md`.
|
||||
- `hooks/inject.py` — the composer; enforces the line-budget guard (120/240).
|
||||
- `hooks/hooks.json` — SessionStart wiring, matcher `startup|resume|clear|compact`.
|
||||
- `skills/audit-sessions/SKILL.md` — `/os-context:audit-sessions`, the biweekly
|
||||
orchestration IRL audit against `10-orchestration.md`'s shipped rules; see
|
||||
`skills/audit-sessions/references/rubric.md`.
|
||||
- `audit/bin/{audit-stats,extract}` — deterministic driver scripts the audit
|
||||
skill calls; skill-local, not a plugin-root `bin/`.
|
||||
- `tests/hook_test.py` — unittest, covers compose order + budget logic.
|
||||
- `eval/` — Eval harness (prompted grid, scenarios + `scenarios-reserve/`); eval
|
||||
discipline (reserves never read informally) per root `CLAUDE.md`.
|
||||
|
||||
## Pointers
|
||||
|
||||
- Decision: [docs/adr/0031](../../docs/adr/0031-os-context-prompt-composer-plugin-supersedes-single-file-os-orchestration.md)
|
||||
(prompt-composer plugin, supersedes the single-file os-orchestration approach).
|
||||
- Status/build history: `docs/implementation-status/os-context.md`.
|
||||
|
|
@ -4,21 +4,13 @@ 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.
|
||||
> `PRD.md` is the historical source-of-truth design doc (see its top-of-file
|
||||
> migration note for current location). This file is a current-state index,
|
||||
> not a build map — the plugin is fully built (5 skills, tests passing).
|
||||
|
||||
> Vault user guide (nuance/mental-model, not a how-to):
|
||||
> `~/Documents/SecondBrain/user-guide/os-doc-hygiene-user-guide.md`.
|
||||
|
||||
## Lifecycle layer (2026-07-15)
|
||||
|
||||
A rulebook layer sits on top of the scan/classify/clean core: global
|
||||
`rulebook.json` + optional per-project `.dochygiene-rules.json` declare
|
||||
lifetime rules for known-temporary artifacts, feeding tier derivation and a
|
||||
`/os-doc-hygiene:calibrate` skill for tuning rules against a real project.
|
||||
Read `lifecycle-spec.md` before touching `rulebook.py`, the scanner's
|
||||
lifecycle signals, or the calibrate skill; decisions are ADR-0038–0041.
|
||||
|
||||
## Core distinction (do not conflate)
|
||||
|
||||
- **Stale** = the doc is *wrong* (contradicted, orphaned, superseded,
|
||||
|
|
@ -29,99 +21,39 @@ lifecycle signals, or the calibrate skill; decisions are ADR-0038–0041.
|
|||
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)
|
||||
## Component map
|
||||
|
||||
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 `.cc-os/dochygiene/` (ADR-0027;
|
||||
legacy `.dochygiene/` is fallback-read only). 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.
|
||||
- `skills/` — five skills: `check`, `clean`, `status`, `sweep`, `calibrate`
|
||||
(each `SKILL.md` + `workflows/`); see `skills/CONTEXT.md` for the index.
|
||||
- `scripts/` — deterministic Python core (scanner, state store, reminder,
|
||||
report builder, validator, patch applier, token estimator, rulebook,
|
||||
calibrate helpers); see `scripts/CONTEXT.md`.
|
||||
- `hooks/hooks.json` — `SessionStart` → `scripts/reminder.py` (banner only,
|
||||
no analysis, no mutation beyond `last_reminded`).
|
||||
- `tests/` — pytest suite covering every deterministic seam; see
|
||||
`tests/CONTEXT.md`.
|
||||
- `invariants.md` — the reversion-protection contract (numbered behavioral
|
||||
invariants); read before changing scanner, state, or safety-tier logic.
|
||||
- `examples/golden/` — golden classifier fixtures (Layer 2 of reversion
|
||||
protection, paired with `invariants.md`).
|
||||
- `lifecycle-spec.md` — the rulebook layer (global `rulebook.json` +
|
||||
per-project `.dochygiene-rules.json`) feeding tier derivation and the
|
||||
`calibrate` skill; decisions are ADR-0038–0041.
|
||||
- `openspec/` — this plugin's own OpenSpec changes/specs tree (archived
|
||||
changes correspond to shipped features).
|
||||
|
||||
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)
|
||||
## Conventions
|
||||
|
||||
- **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
|
||||
Opus); generative distillation = Sonnet (NOT Haiku).
|
||||
- **Reversion protection**: `invariants.md` + `examples/golden/` + human gate
|
||||
for changes to either. 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.
|
||||
disclosure).
|
||||
|
||||
## 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`.
|
||||
Changing any invariant requires updating `invariants.md` and the golden
|
||||
examples, with explicit human approval — do not restate the invariants here.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
# PRD: `doc-hygiene` plugin
|
||||
|
||||
> Status: `ready-for-agent` · Working name: `doc-hygiene` · Target: `~/dev/cc-plugins/doc-hygiene/`
|
||||
> Status: shipped · Built at `plugins/os-doc-hygiene/` in cc-os, not the
|
||||
> `cc-plugins` target/marketplace named below. Historical design doc — kept
|
||||
> as-is except this note and the location/marketplace corrections below.
|
||||
|
||||
## Problem Statement
|
||||
|
||||
|
|
@ -144,7 +146,8 @@ prose distillation). This keeps it fast, cheap, and trustworthy.
|
|||
## Implementation Decisions
|
||||
|
||||
**Distribution & activation**
|
||||
- New plugin `doc-hygiene` in the `cc-plugins` marketplace. Installed globally,
|
||||
- New plugin `os-doc-hygiene` in the cc-os marketplace (built at
|
||||
`plugins/os-doc-hygiene/`; see migration note above). 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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
# os-sdlc
|
||||
|
||||
Status: scaffold only — no skills/agents/hooks/scripts exist yet. Only
|
||||
`.claude-plugin/plugin.json` and `OVERVIEW.md` are present.
|
||||
|
||||
This will be cc-os's lifecycle/SDLC plugin: a lightweight
|
||||
grill → to-spec → to-tickets → implement → review → ship pipeline, adapted
|
||||
from Matt Pocock's skill set and Delta Refinery's multi-level handoff
|
||||
discipline, wired to `os-backlog` (tickets) and `os-adr` (decisions) rather
|
||||
than reinventing either.
|
||||
|
||||
Read `OVERVIEW.md` for the full design brief (why this plugin exists, scope,
|
||||
working philosophy, first-iteration build plan, open questions) and
|
||||
[docs/adr/0037](../../docs/adr/0037-os-sdlc-lives-inside-cc-os-as-a-new-plugin-not-a-separate-cc-sdlc-marketplace.md)
|
||||
for why it lives inside cc-os as a plugin rather than a separate marketplace.
|
||||
|
||||
Expand this file into a real component-map index once skills/agents/hooks
|
||||
first land, per this repo's plugin conventions.
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# os-shortcuts
|
||||
|
||||
The "keybindings" of cc-os: user-invoked QOL shortcut commands — ritual
|
||||
multi-step sequences run on demand, never via hook injection. See
|
||||
[docs/adr/0028](../../docs/adr/0028-os-shortcuts-plugin-for-user-invoked-qol-commands-bounded-against-hook-injection-plugins.md)
|
||||
for the boundary this plugin is scoped against: it exists specifically to
|
||||
hold user-invoked commands so hook-injection plugins (os-context, os-status,
|
||||
etc.) don't accumulate them.
|
||||
|
||||
## Component map
|
||||
|
||||
- `skills/wrap/SKILL.md` — the only skill so far: the session-close ritual
|
||||
(review changes → commit → sweep affected docs/todos → handoff summary).
|
||||
Invoked by `/os-shortcuts:wrap`. No hooks — runs only when explicitly
|
||||
invoked.
|
||||
- `invariants.md` — reversion-protection contract for `wrap`'s guarantees.
|
||||
|
||||
More shortcuts are expected to accumulate here (per `plugin.json`'s own
|
||||
description) — add a line above per skill as they land, and a matching
|
||||
section in `invariants.md` once its behavior is worth protecting.
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
# os-shortcuts behavioral invariants
|
||||
|
||||
Reversion protection for the `wrap` skill. Any change that breaks one of these
|
||||
is a regression, not a refactor. No automated test suite exists yet (the
|
||||
skill is prompt-only, no scripts) — enforced by review of `skills/wrap/SKILL.md`
|
||||
against this list.
|
||||
|
||||
1. **Never auto-invoked.** `wrap` has no hook wiring and runs only when the
|
||||
user explicitly triggers it ("wrap up", "wrap", end-of-session). It must
|
||||
never fire from a `SessionStart`/`SessionEnd`/other hook.
|
||||
2. **Never force-pushes or skips hooks.** Commits made during `wrap` follow
|
||||
this repo's standing git-safety rules: never `--no-verify`, never force-push,
|
||||
new commits rather than amends.
|
||||
3. **Session-close ritual runs in order: review → commit → sweep → handoff.**
|
||||
The handoff summary (step 4) is always the final message, and always
|
||||
covers Done / In flight / Next steps — it must not be skipped even when
|
||||
steps 2–3 found nothing to do.
|
||||
4. **No fabricated doc-update conventions.** The docs/todo sweep (step 3)
|
||||
only touches files a project's own `CLAUDE.md` (or discernible existing
|
||||
convention) identifies as a source of truth; if none exists, `wrap` skips
|
||||
the step and says so rather than inventing structure.
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
# os-status
|
||||
|
||||
Aggregated, in-process `SessionStart` status checks for the cc-os plugin
|
||||
family: per-project artifact checks (ADR system, vault hub note, project
|
||||
graph, tracker config, config version) and environment hazard checks
|
||||
(subagent model env override). See
|
||||
[docs/adr/0022](../../docs/adr/0022-os-status-plugin-aggregated-in-process-sessionstart-status-checks.md)
|
||||
for why checks are aggregated into one plugin instead of scattered per-plugin
|
||||
hooks.
|
||||
|
||||
At most one `systemMessage` banner per session (one line per surviving warn);
|
||||
`note`-level results bypass snooze/suppress and go out as `additionalContext`.
|
||||
Snooze/suppress state lives under `.cc-os/status/` (ADR-027, with legacy
|
||||
top-level fallback read).
|
||||
|
||||
## Component map
|
||||
|
||||
- `hooks/checks.py` — the check registry (`REGISTRY` at the bottom of the
|
||||
file) plus each check function and the `--json` CLI entry point the `fix`
|
||||
skill drives. Current checks: `subagent-model-env-override`,
|
||||
`adr-system-present`, `vault-hub-note-present`, `orchestration-audit-due`,
|
||||
`tracker-configured`, `config-version-current`, `project-graph-present`.
|
||||
Read `checks.py` directly for exact check logic — this list is a pointer,
|
||||
not a restatement.
|
||||
- `hooks/session_start.py` — the `SessionStart` hook entry point; runs the
|
||||
registry, applies snooze/suppress, emits the single banner.
|
||||
- `hooks/state.py` — project-root resolution, `.cc-os/config` read, snooze/
|
||||
suppress state read/write.
|
||||
- `hooks/hooks.json` — `SessionStart` wiring.
|
||||
- `skills/fix/SKILL.md` — `/os-status:fix`: remediates whatever the registry
|
||||
flags, idempotent, doubles as the update-to-current-approach path
|
||||
(ADR-026).
|
||||
- `invariants.md` — the reversion-protection contract (8 numbered
|
||||
invariants, including byte-identical wording parity with os-adr's
|
||||
session_start.py); enforced by `tests/hook_test.py`.
|
||||
- `tests/hook_test.py` — 80 tests, model-free (`python3 tests/hook_test.py`).
|
||||
|
||||
Adding a check means adding a function + a `Check(...)` entry to `REGISTRY` in
|
||||
`checks.py`, following the uniform `check(ctx) -> CheckResult` signature
|
||||
(invariant #8) — do not special-case a new check's wiring elsewhere.
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
# os-vault
|
||||
|
||||
Integrates the SecondBrain Obsidian vault (semantic/knowledge memory, ADR-012) and
|
||||
per-project Graphify knowledge graphs into Claude sessions. Episodic memory is a
|
||||
separate system (memsearch, ADR-015) — see `README.md` for the requirements/version
|
||||
pins and `config.yaml` for vault path, model, and threshold knobs.
|
||||
|
||||
## Component map
|
||||
|
||||
- `skills/` — five skills: `query`, `write`, `reorganize`, `onboard-project`,
|
||||
`design-template`.
|
||||
- `hooks/` — `session_start.py` (SessionStart), `session_context.py`
|
||||
(UserPromptSubmit), `post_tool_use_write.py` (PostToolUse), `session_end.py` +
|
||||
`memsearch_sync.py` + `vault_sync.py` (SessionEnd); shared `config.py`,
|
||||
`hook_io.py`, `session_state.py`. See "Hook wiring" below.
|
||||
- `tests/` — golden-file harness (`harness.sh`, `golden/`, `inputs/`,
|
||||
`python-wrappers/`); own `tests/README.md`.
|
||||
- `eval/` — write-behavior eval harness (scenarios + `scenarios-reserve/`,
|
||||
judge-rubric, results); eval discipline (reserves never read informally) per
|
||||
root `CLAUDE.md`.
|
||||
- `invariants.md` — reversion-protection contract; read before changing frontmatter
|
||||
facets, hook fail-soft behavior, or sync timing.
|
||||
- `config.yaml` — vault_path, graphify model, thresholds, env overrides.
|
||||
|
||||
## Hook wiring
|
||||
|
||||
os-vault's hooks are **not** wired via a plugin `hooks/hooks.json` — there isn't
|
||||
one, and this is deliberate, not a defect. They're registered at user level in
|
||||
`~/.claude/settings.json`, pointing at the repo source scripts directly:
|
||||
`SessionStart` → `hooks/session_start.py`, `UserPromptSubmit` →
|
||||
`hooks/session_context.py`, `PostToolUse` → `hooks/post_tool_use_write.py`,
|
||||
`SessionEnd` → `hooks/session_end.py` + `hooks/memsearch_sync.py` +
|
||||
`hooks/vault_sync.py`.
|
||||
|
||||
## Pointers
|
||||
|
||||
- Design: `docs/memory-system/02-system-design.md`. Decisions: ADR-011 (facets),
|
||||
ADR-012 (vault as semantic source of truth), ADR-015 (memsearch split).
|
||||
- Status/build history: `docs/implementation-status/os-vault.md`.
|
||||
|
|
@ -40,6 +40,10 @@ Shared modules: `hooks/config.py`, `hooks/hook_io.py`, `hooks/session_state.py`.
|
|||
- `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/onboard-project/SKILL.md` — Onboard, update, remove, or query the
|
||||
per-project Graphify knowledge graph (codebase structure, this repo only)
|
||||
- `skills/design-template/SKILL.md` — Design or revise SecondBrain note-type
|
||||
templates, including new-type creation
|
||||
|
||||
## Configuration
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
# os-vault invariants
|
||||
|
||||
Behavioral invariants of the plugin. Changing any of these requires explicit human approval.
|
||||
Enforced by `tests/harness.sh` (golden fixtures) unless noted otherwise.
|
||||
|
||||
1. **Episodic and semantic memory stay separate systems.** Vault notes hold evergreen,
|
||||
cross-project knowledge (ADR-012); session/event history goes to memsearch (ADR-015).
|
||||
`/os-vault:query` never answers "what happened when" questions — those route to memsearch.
|
||||
|
||||
2. **Six flat namespaced facets, plus `scope`.** Note frontmatter tags are
|
||||
`type/`/`client/`/`project/`/`domain/`/`tool/`/`convention/` (ADR-011); `scope`
|
||||
(`global`/`project`/`client`) is a frontmatter field, not a tag. `type/` is required on
|
||||
every note. Source of truth for the schema is `vault-conventions.md` at the vault root —
|
||||
never duplicated or paraphrased into plugin code or skills.
|
||||
|
||||
3. **Hooks are fail-open, never block a session.** Every hook
|
||||
(`session_start.py`, `session_context.py`, `post_tool_use_write.py`, `session_end.py`,
|
||||
`memsearch_sync.py`, `vault_sync.py`) degrades silently on error; none may disrupt session
|
||||
startup or shutdown.
|
||||
|
||||
4. **SessionStart returns sub-second.** Heavy work (graph rebuild) is detached to the
|
||||
background; `session_start.py` only does the staleness check and kicks off the detached
|
||||
rebuild. Context injection is a separate hook (`session_context.py`, UserPromptSubmit).
|
||||
|
||||
5. **Sync happens only at SessionEnd, in a fixed order.** `vault_sync.py` must run after
|
||||
`session_end.py` so the daily journal note it writes is included in the commit;
|
||||
`memsearch_sync.py` is a separate, independent SessionEnd entry (split by ADR-016) for the
|
||||
memsearch repo. Neither auto-commit runs mid-session.
|
||||
|
||||
6. **Vault-not-repo rule.** `/os-vault:write` writes only to the resolved vault root
|
||||
(`${OS_VAULT_PATH:-$HOME/Documents/SecondBrain}`), never to a project repository. The vault
|
||||
path is resolved mechanically at the start of every write — never hardcoded.
|
||||
|
||||
7. **Write is query-first.** Before creating a new note, check for an existing note on the
|
||||
same subject; a contradicting/extending fact updates the existing note (bumping
|
||||
`last_updated`) rather than creating a duplicate.
|
||||
|
||||
8. **No silent new note types.** A fact that fits no existing type is filed under the closest
|
||||
existing type with a visible `## Type-fit note` misfit marker, or escalated to
|
||||
`/os-vault:design-template` — never given an invented type silently.
|
||||
Loading…
Reference in New Issue