diff --git a/.claude/rules/rill-autonomous-execution.md b/.claude/rules/rill-autonomous-execution.md index 3438193..ff9e00a 100644 --- a/.claude/rules/rill-autonomous-execution.md +++ b/.claude/rules/rill-autonomous-execution.md @@ -1,139 +1,100 @@ # Autonomous Execution Rules — Rill -`/solve` (and any future autonomous-execution skill) operates in one of two **lanes** depending on what the task touches. This document defines lane detection, the single Plan gate, the worktree convention, Codex output interpretation, the two-channel write invariant, and the three-tier classification of destructive operations. +`/solve` (and any future autonomous-execution skill) operates in one of two **lanes** depending on what the task touches. Loaded automatically via `.claude/rules/*.md`. -Loaded automatically via `.claude/rules/*.md`. See `rill-core.md` for the index. +## 1. Two Lanes -## 1. Two Lanes — Detection and Definition - -PKM updates (knowledge / tasks / journal) and Rill system development (skills / rules / CLI / GUI) are physically separated into two lanes. - -### 1.1 Lane definitions +PKM updates and Rill system development are physically separated. | Axis | Lane A: PKM Operations | Lane B: Rill System Development | |---|---|---| -| Purpose | Daily knowledge / tasks / journal updates | Adding / fixing Rill itself (skills, rules, CLI, GUI app) | +| Purpose | Daily knowledge / tasks / journal updates | Adding / fixing Rill itself (skills, rules, CLI, GUI) | | Target paths | `inbox/`, `knowledge/`, `workspace/`, `tasks/`, `reports/`, `pages/`, `taxonomy.md`, `activity-log.md` | `.claude/`, `bin/rill`, `~/src/rillmd/rill/**`, sibling repos, vault's `personal-*.md` / `settings.json` | -| Branch | **main directly** | feature branch in worktree | +| Branch | main directly | feature branch in worktree | | Push | Commit + push immediately (`rill push`) | PR → Codex review → auto-merge | -| Review | None (use `codex review --uncommitted` only for sensitive distills) | **Codex `codex review --base main` required** | -| Worktree | None | **Required** (`.claude/worktrees/{slug}/`) | -| Concurrent sessions | Multiple OK, all see main | Each task gets its own worktree | +| Review | None (use `codex review --uncommitted` only for sensitive distills) | `codex review --base main` required | +| Worktree | None | Required (`.claude/worktrees/{slug}/`) | | Conflict resolution | `git pull --rebase` + AI two-sided merge | PR mergeable check + rebase | -### 1.2 Lane detection at Plan time - -Detect from the Plan's "files to touch", evaluating in order (first match wins): - -1. **One or more Lane B paths *and* one or more Lane A paths** → **Mixed** (must be checked first, otherwise step 3 swallows mixed tasks). Run as a Lane A path + Lane B path in parallel within the single task. The two-channel write invariant in §5 makes this safe: PKM updates run on main; code changes run in the Lane B worktree. -2. All targets in Lane A paths → **Lane A** -3. All targets in Lane B paths → **Lane B** - -The Mixed case is the default for any task that updates both a `_task.md` artifact and code, which is most non-trivial Lane B work. - -### 1.3 No single-file exception - -Single-file Lane B changes (a one-line `.gitignore` addition, an ADR-only PR, a typo fix in a rule) still get a worktree. Bypassing worktree "for trivial changes" empirically drags unrelated working-tree state into the commit; the cost of one extra `git worktree add` / `git worktree remove` pair is small enough that the consistency is worth more. - -## 2. The Plan Gate — Single Point of User Consent +### Lane detection at Plan time -Phase 3 Plan approval is the **only required user gate**. Once the Plan is approved, Phase 4 and Phase 5 run end-to-end without further user breakpoints, with three exceptions. +Detect from the Plan's "files to touch", first match wins: -### 2.1 The three remaining `[User]` breakpoints +1. **Lane B paths *and* Lane A paths** → **Mixed** (check first; the two-channel write invariant in §5 makes this safe) +2. All Lane A → **Lane A** +3. All Lane B → **Lane B** -1. **External messaging** — sending an email, Slack message, or any communication to a human outside the user's own systems. The AI drafts; the user sends -2. **Real-world / physical action** — anything that moves matter in the physical world (mailing a parcel, calling a vendor outside automated channels) -3. **Human-input-required knowledge gap** — `/solve`'s knowledge-gap blocker handling (see `solve.md`) classifies a gap as outside Rill's reach. The AI asks the user; on response, it resumes +Mixed is the default for any task updating both `_task.md` and code. -All three must be **explicitly declared in the Plan** at Phase 3. Implicit breakpoints during Phase 4 are not allowed. +Single-file Lane B changes still get a worktree (consistency > the cost of one `git worktree add` / `remove` pair). -### 2.2 The Plan-gap blocker (`status: open` exit) +## 2. The Plan Gate -When Phase 4 encounters a decision branch the Plan did not anticipate, the AI: +Phase 3 Plan approval is the **only required user gate**. Phase 4 / 5 run end-to-end with three exceptions only: -- Writes "Plan replan needed: {what}" to `## Current Position` -- Exits Phase 4 with `status: open` unchanged -- The next `/solve {slug}` resumes from Phase 3 (Planning), not Phase 4 +1. **External messaging** — email, Slack, anything to a human outside the user's systems (AI drafts, user sends) +2. **Real-world / physical action** — moves matter in the physical world +3. **Human-input-required knowledge gap** — see `solve.md` knowledge-gap blocker; AI asks, user answers, AI resumes -This is distinct from the knowledge-gap blocker (which is about missing facts, not missing decisions). +All three must be **explicitly declared in the Plan**. -### 2.3 Plan quality requirements +### Plan-gap blocker (`status: open` exit) -A Plan that passes the gate must contain (Phase 3 will not present it for approval otherwise): +When Phase 4 hits an unanticipated decision branch: write "Plan replan needed: {what}" to `## Current Position`, exit Phase 4 with `status: open`. Next `/solve {slug}` resumes from Phase 3 (Planning), not Phase 4. Distinct from the knowledge-gap blocker (missing facts, not missing decisions). -1. **Completion criteria** — a verifiable end-state (mechanically checkable via diff, file existence, command exit code) -2. **Verification commands** — how to confirm Completion criteria (`npm test`, `rill validate`, `diff`, etc.) plus any required manual steps -3. **Review method** — `codex review --base main` (Lane B default) / none (Lane A default) / explicit "double review" -4. **Merge policy** — `auto-squash` / manual / draft PR -5. **Branch name** — `feature/{slug}` (Lane B). Lane A has no branch -6. **Files in scope** — exhaustive list (any file outside the list that gets touched is a Plan-gap signal) -7. **Target repositories** — for Lane B (`my-rill`, `rillmd/rill`, `rill-dev`, etc.) -8. **Lane** — A / B / mixed +### Plan quality requirements -If any of (1)–(8) is missing, Phase 3 reworks the draft before asking for approval. +A Plan must contain: (1) Completion criteria (verifiable end-state), (2) Verification commands, (3) Review method (codex / none / explicit), (4) Merge policy, (5) Branch name (Lane B), (6) Files in scope (exhaustive — anything outside is a Plan-gap signal), (7) Target repositories (Lane B), (8) Lane. Missing any → Phase 3 reworks before requesting approval. ## 3. Worktree Convention (slug-identity) **Principle**: 1 task = 1 slug = 1 worktree = 1 branch = 1 PR, identical across all target repositories. -### 3.1 Naming - -| Element | Value | Example | -|---|---|---| -| Task slug | from `tasks/{slug}/_task.md` directory name | `add-codex-plan-review` | -| Worktree directory | `.claude/worktrees/{slug}` (inside each target repo) | `~/src/rillmd/rill/.claude/worktrees/add-codex-plan-review` | -| Feature branch | `feature/{slug}` | `feature/add-codex-plan-review` | -| PR title prefix | `{slug}:` (searchable) | `add-codex-plan-review: ...` | -| PR head | `feature/{slug}` | — | +| Element | Value | +|---|---| +| Task slug | from `tasks/{slug}/_task.md` directory name | +| Worktree | `$REPO/.claude/worktrees/{slug}` | +| Feature branch | `feature/{slug}` | +| PR title prefix | `{slug}:` (searchable) | -Cross-repo: the **same slug** is used in every target repo's worktree and branch names. Branch name collisions across repos do not occur because each repo is a separate remote. +Cross-repo: the same slug in every target repo; branch collisions don't occur (each repo is a separate remote). -### 3.2 Idempotent resume (five-case branching) +### Idempotent resume (five-case branching) -Phase 1.5 of `/solve` checks each target repo before doing anything. Let `WT = $REPO/.claude/worktrees/{slug}` and `BR = feature/{slug}`: +Phase 1.5 checks each target repo. Let `WT = $REPO/.claude/worktrees/{slug}`, `BR = feature/{slug}`: | Case | Condition | Action | |---|---|---| | 1 | WT exists, HEAD = BR | Reuse silently | -| 2 | WT exists, HEAD ≠ BR (corrupted state) | Halt with `[User]` breakpoint: "worktree {WT} is on unexpected branch {actual}" | +| 2 | WT exists, HEAD ≠ BR | Halt with `[User]` breakpoint | | 3 | WT missing, BR on remote | `git fetch origin BR && git worktree add WT BR` | | 4 | WT missing, BR local-only | `git worktree add WT BR` | -| 5 | Neither WT nor BR (fresh task) | `git worktree add WT -b BR` | - -PR check is parallel: `gh pr list --head feature/{slug} --state open --json number` — reuse the existing PR if found; do not create a duplicate. +| 5 | Neither | `git worktree add WT -b BR` | -### 3.3 Lifecycle +PR check: `gh pr list --head feature/{slug} --state open --json number` — reuse if found. -- **Created** in Phase 1.5 (or Phase 4 if the Plan deferred creation) -- **Reused** across `/clear` and resume — surviving the entire task lifetime -- **Removed** in Phase 5.6 *only* after auto-merge succeeds. Interrupted tasks keep the worktree (for resume) -- **GC**: not automated. Stale worktrees from cancelled tasks accumulate; clean them by hand when noticed +### Lifecycle -### 3.4 Sibling repo prerequisites +Created in Phase 1.5. Reused across `/clear`. Removed in Phase 5.6 only after auto-merge succeeds (interrupted tasks keep the worktree). GC is manual. Each sibling repository must have `.claude/worktrees/` in its `.gitignore`. -## 4. Codex Dual Usage — Plan Review and Code Review - -`/solve` calls Codex CLI twice in the lifecycle. +## 4. Codex Dual Usage ### 4.1 Phase 3.3 — Plan review (`codex exec`) -Verify the Plan's quality before user approval. Run after Phase 3.2 drafts the Plan into `_task.md`: - ```bash codex exec --skip-git-repo-check --sandbox read-only \ --cd $RILL_HOME --color never "$PROMPT" = 2` | | Material — halt for Plan replan | `FAIL >= 2`, *or* a single FAIL on a core invariant (slug-identity, two-channel write, PII guard) | -Save output as `tasks/{slug}/NNN-codex-plan-review.md` from the main worktree. - -**Operational notes**: +Save as `tasks/{slug}/NNN-codex-plan-review.md` from the main worktree. -- `= 1 | -Save output as `tasks/{slug}/NNN-codex-review-{repo}.md` from the main worktree. - -**Operational notes**: +Save as `tasks/{slug}/NNN-codex-review-{repo}.md` from the main worktree. -- `--color` flag is not accepted (different from `codex exec`) -- `--base` and positional `[PROMPT]` are mutually exclusive — review uses the built-in prompt -- File paths in `[Pn] ... — /abs/path:line` are absolute; normalize to repo-relative before forwarding to PR comments -- Workdir must be a git repo (no `--skip-git-repo-check` flag here) +**Operational**: `--color` flag not accepted (differs from `codex exec`). `--base` and positional `[PROMPT]` are mutually exclusive. Paths in `[Pn] ... — /abs/path:line` are absolute; normalize to repo-relative for PR comments. Workdir must be a git repo (no `--skip-git-repo-check` here). ## 5. Two-Channel Write Invariant -In a mixed task (Lane A + Lane B), both lanes write concurrently into the **same `_task.md`** without conflict, because each lane writes through a different channel that touches disjoint file sets. - -### 5.1 The invariant - -A Lane B feature branch must **never** modify files under any of: - -- `tasks/` -- `knowledge/` -- `workspace/` -- `pages/` -- `reports/` -- `inbox/` -- `taxonomy.md` -- `activity-log.md` - -These files are reserved for the **main worktree** (Lane A channel). The Lane B worktree only touches: - -- `.claude/` -- `bin/` -- `app/` -- sibling repos' equivalent locations - -### 5.2 Why this works +A Lane B feature branch **must never modify** files under `tasks/`, `knowledge/`, `workspace/`, `pages/`, `reports/`, `inbox/`, `taxonomy.md`, `activity-log.md`. Those are the main worktree (Lane A) channel. The Lane B worktree only touches `.claude/`, `bin/`, `app/`, sibling repos' equivalents. -Squash-merge is a 3-way merge. For any file `F` that the feature branch did not touch: +This works because squash-merge is a 3-way merge: for any file F the feature branch didn't touch, `theirs` (feature HEAD) = base ancestor, so git picks `ours` (main HEAD with Lane A updates) automatically — no conflict. -- base ancestor = `F` at branch-off -- ours (main HEAD) = `F` with all Lane A updates since branch-off -- theirs (feature HEAD) = `F` at branch-off (unchanged) +Consequences: `_task.md` Current Position / History stay current on main throughout Phase 4. Interrupted Lane B tasks show `status: open` on main, so other sessions and `/briefing` see them. PR squash carries only code. -→ git automatically picks ours; no conflict. - -### 5.3 Consequences - -- A task's `_task.md` status / Current Position / History stay current on main throughout Phase 4, even mid-task -- Interrupted Lane B tasks show as `status: open` on main, so other Claude Code sessions and `/briefing` see them -- PR squash-merge of the Lane B branch carries only code; `_task.md` updates were already on main - -### 5.4 Enforcement - -- `solve.md` Phase 4 forbids Lane B Edit of forbidden paths (procedural enforcement) -- The PostToolUse hook (`rill activity-log on-write` / `rill touch`) is structurally no-op for worktree-resident paths — Lane B writes to forbidden paths produce no hook side-effect, but the *commit* on the feature branch is still a violation that this rule disallows -- A future guard could add a pre-commit hook that fails when a Lane B branch stages a forbidden path; the procedural rule is currently sufficient +Enforcement is procedural (`solve.md` Phase 4 forbids Lane B Edit of forbidden paths). A future pre-commit hook could enforce structurally. ## 6. Three-Tier Destructive Operations -Operations are classified by reversibility and blast radius. +### Tier 1 — AI autonomous -### 6.1 Tier 1 — AI autonomous (no user confirmation) +`git add` / `commit` / `push` to feature branches. `git pull` / `fetch` / `rebase` / `merge` (conflicts: AI two-sided merge). `git worktree add` / `remove` (current task only). `git checkout` / `switch` (within a worktree). `gh pr create` / `merge --(auto-)squash --delete-branch` / `comment`. `codex review` / `exec`. File `Edit` / `Write` / `Read`. `rill push` / `mkfile` / `activity-log add`. -- `git add` / `git commit` / `git push` to feature branches -- `git pull` / `git fetch` / `git rebase` / `git merge` (conflicts: AI two-sided merge) -- `git worktree add` / `git worktree remove` (only for worktrees owned by the current task) -- `git checkout` / `git switch` (within a worktree) -- `gh pr create` / `gh pr merge --(auto-)squash --delete-branch` / `gh pr comment` -- `codex review` / `codex exec` -- File `Edit` / `Write` / `Read` -- `rill push` / `rill mkfile` / `rill activity-log add` - -### 6.2 Tier 2 — User confirmation required (even if Plan declared) +### Tier 2 — User confirmation required (even if Plan declared) - Bulk file deletion (3+ files, or any deletion under the personal-entities layer) -- `.claude/` config changes (`settings.json`, rules) committed via the Lane A channel — this is a misclassification signal -- Direct `status: cancelled` transition on `_task.md` (whereas `done` is autonomous) +- `.claude/` config changes (`settings.json`, rules) committed via the Lane A channel — misclassification signal +- Direct `status: cancelled` transition on `_task.md` (`done` is Tier 1) - Removal of a worktree the current task did not create - Non-squash merge commits to main -### 6.3 Tier 3 — Deny (absolutely forbidden, enforced via settings.json) +### Tier 3 — Deny (forbidden, enforced via settings.json) | Pattern | Reason | |---|---| -| `Bash(git push --force*)` | Overwrites remote history | -| `Bash(git push -f *)` | Same | -| `Bash(git push * main*)` | Direct main push (use PR) | -| `Bash(git push * master*)` | Same | +| `Bash(git push --force*)` / `Bash(git push -f *)` | Overwrites remote history | +| `Bash(git push * main*)` / `Bash(git push * master*)` | Direct main push (use PR) | | `Bash(git reset --hard*)` | Discards working tree | | `Bash(git clean -f*)` | Removes untracked files unrecoverably | | `Bash(git branch -D *)` | Removes unmerged branch state | | `Bash(gh pr merge --admin*)` | Bypasses branch protection | | `rm -rf` on absolute paths | Catastrophic blast radius | -`/solve` relies on these being denied in `.claude/settings.json`. The matcher syntax must follow space-form (`Bash(... *)`); colon-form (`Bash(...:*)`) is not documented for Bash and should not be used. - -Settings rules are evaluated in order **deny → ask → allow** (first match wins), so a Tier 3 deny correctly overrides a Tier 1 allow. +`/solve` relies on these being denied in `.claude/settings.json`. Matcher syntax: space-form (`Bash(... *)`), not colon-form (`Bash(...:*)`). Rules evaluate **deny → ask → allow** (first match wins) — Tier 3 deny correctly overrides Tier 1 allow. ## 7. PUBLIC Repository Guard -Repositories that are PUBLIC (the OSS core repo and the demo vault) must clear the following BLOCKING scans before `git push`: - -1. **CJK / Japanese scan**: `git diff HEAD~1 HEAD | LC_ALL=C grep -nP '[\x{3000}-\x{9fff}\x{ff00}-\x{ffef}]'` — halt on hit -2. **PII mapping scan**: grep for the entries listed in the vault's local PII mapping table (held in the vault's `personal-*.md`, not in this repo) — halt on hit -3. **Email / phone scan**: regex for `\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b` and `\b\d{2,4}-\d{2,4}-\d{4}\b` — halt and confirm (false-positive prone) +PUBLIC repos (OSS core, demo vault) must clear all three BLOCKING scans before `git push`: -All three are BLOCKING — push proceeds only if all pass. +1. **CJK / Japanese**: `git diff HEAD~1 HEAD | LC_ALL=C grep -nP '[\x{3000}-\x{9fff}\x{ff00}-\x{ffef}]'` +2. **PII mapping**: grep for entries in the vault's local `personal-*.md` mapping table (not in this repo) +3. **Email / phone**: `\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b` and `\b\d{2,4}-\d{2,4}-\d{4}\b` -PRIVATE repos do not require these scans. +Push only when all pass. PRIVATE repos do not require these scans. ## 8. Cross-Reference -- `solve.md` — the procedural skill that implements this rule +- `solve.md` — procedural skill implementing this rule - `rill-tasks.md` — `## Plan` section format - `rill-claude-code-integration.md` — `rill push` behavior, hook semantics -- the vault's `personal-dev.md` (per-vault) — cross-repo routing + PII mapping +- vault's `personal-dev.md` (per-vault) — cross-repo routing + PII mapping diff --git a/.claude/rules/rill-claude-code-integration.md b/.claude/rules/rill-claude-code-integration.md index c0d964e..f2a9099 100644 --- a/.claude/rules/rill-claude-code-integration.md +++ b/.claude/rules/rill-claude-code-integration.md @@ -1,37 +1,23 @@ # Claude Code Integration Rules — Rill -Rill operates as a companion tool for Claude Code. This document defines integration boundaries, CLI usage, shell compatibility, and other cross-cutting rules. +Rill is a companion tool for Claude Code. Integration boundaries, CLI usage, shell compatibility. ## Claude Code Integration Boundary (ADR-068, critical) -Rill is a Claude Code CLI companion tool. **The following are prohibited**: +**Prohibited**: -1. **Adopting Agent SDK (`@anthropic-ai/claude-agent-sdk`)** -2. **Extracting or managing OAuth tokens** -3. **Using `--bare` mode** -4. **Using API Keys as default authentication** +1. Adopting Agent SDK (`@anthropic-ai/claude-agent-sdk`) +2. Extracting or managing OAuth tokens +3. Using `--bare` mode +4. Using API Keys as default authentication -### Automated Execution Sessions -- Use `claude -p --output-format stream-json` for automation -- Designed for Max Plan - -### Alternative Architecture -When automation is needed: -- macOS: `launchd` (plist) -- Linux: systemd timer or `cron` -- Execution: `claude -p --output-format stream-json "/distill"` etc. (invoke existing skills) -- Auth: Uses the user's Max Plan auth (held by Claude Code itself) -- Logging: `reports/` or dedicated log file + 1-line entry in `activity-log.md` +For automation: `claude -p --output-format stream-json` (designed for Max Plan auth held by Claude Code itself). Alternative architecture: macOS `launchd` / Linux `systemd timer` or `cron` calling `claude -p --output-format stream-json "/distill"` etc. Log to `reports/` or dedicated log file plus 1-line `activity-log.md` entry. ## File Creation: `rill mkfile` Required (ADR-060) -**Use `rill mkfile` for all new file creation.** Ensures timestamp accuracy. - -- Format: `rill mkfile {dir} --slug {slug} --type {type}` -- `rill mkfile` auto-assigns `created` field in ISO 8601 format -- **LLMs must never write `created` values directly** +**Use `rill mkfile` for all new file creation.** Ensures timestamp accuracy. Auto-assigns `created` in ISO 8601 — **LLMs must never write `created` directly**. -### Examples +Format: `rill mkfile {dir} --slug {slug} --type {type}`. ```bash rill mkfile knowledge/notes --slug whisper-api-comparison --type insight @@ -40,61 +26,30 @@ rill mkfile tasks --slug review-contract --type task rill mkfile pages --slug rill-roadmap --type page ``` -### Exceptions - -- System files like `.claude/rules/*.md`, subdirectory `CLAUDE.md` etc. have no frontmatter, so `rill mkfile` is not needed -- Not needed for editing existing files (use Edit tool) +Exceptions: system files without frontmatter (`.claude/rules/*.md`, subdirectory `CLAUDE.md`); editing existing files (use Edit tool). ## GUI Integration: show paths, don't auto-navigate -When you want to point the user at a file you've explored, created, or analyzed, **display the repo-relative path as text** — as a Markdown link `[display name](relative/path.md)` or in backticks. The user opens it themselves via the header search box (or `Cmd+P` palette) in the Rill GUI, which accepts a pasted path directly. - -**Do not run `rill open` to force-navigate the GUI.** Forcible navigation disrupts the user while they are reading a different document. The header search box is the user-controlled entry point — keep the decision to switch views on their side. +Point users at files by displaying repo-relative paths as text — Markdown link `[display name](relative/path.md)` or backticks. The user opens them via the GUI header search box (or `Cmd+P`). -This applies to: -1. Artifacts produced by `/distill`, `/focus`, `/solve`, etc. — list the resulting paths at the end of the run -2. Files the user asked "where is this?" about — reply with the path, not an open command -3. Related files you want to highlight — mention the path in prose +**Do not run `rill open`** to force-navigate. The header search box is the user-controlled entry point — keep the decision to switch views on their side. -The `rill open` CLI still exists for manual / scripted use, but skills and ad-hoc assistant turns must not invoke it. +Applies to: artifacts produced by `/distill`, `/focus`, `/solve` (list paths at end of run); files the user asked about; related files highlighted in prose. `rill open` CLI exists for manual / scripted use only — skills and ad-hoc turns must not invoke it. ## Activity Log -When adding new skills, consider activity-log support. User-initiated activities that don't leave file traces should be recorded in `activity-log.md`: -- Add path detection patterns to the PostToolUse hook -- Or call `rill activity-log add` at the end of the skill (ADR-034) - -## zsh Compatibility (when generating Bash commands) - -Claude Code's shell environment is zsh, so be aware of: - -### 1. Glob Zero-Match Error Prevention -- `ls dir/*.md 2>/dev/null` -- Or use the Glob tool (recommended) -- `for f in dir/*.md; do ...` errors on zero matches → replace with Glob tool - -### 2. Reserved Variable Name Avoidance -- `status` (read-only) — cannot be used → use `file_status` etc. -- `aliases` (associative array) — cannot be used → use `alias_list` etc. +When adding skills, consider activity-log support. User-initiated activities without file traces should be recorded in `activity-log.md`: add path patterns to the PostToolUse hook, or call `rill activity-log add` at end of skill (ADR-034). -### 3. Prefer Glob Tool for File Collection -- Use Glob tool instead of Bash `for f in dir/*.md` -- Use Grep tool instead of running `rg` or `grep` directly +## zsh Compatibility -## Other Cross-Cutting Rules +Claude Code's shell is zsh: -### docs/ vs PKM Distinction -- `docs/` is **documentation about the Rill system itself** -- Distinct from PKM data (inbox/, knowledge/, workspace/, etc.) -- Technical decisions are recorded as ADRs in `docs/decisions/` -- Update `SPEC.md` on system design changes +- **Glob zero-match**: prefer the Glob tool, or `ls dir/*.md 2>/dev/null`. `for f in dir/*.md; do ...` errors on zero matches. +- **Reserved variable names**: `status` (read-only), `aliases` (associative array) — use `file_status`, `alias_list`. +- **Prefer the Glob tool** over `for f in dir/*.md`; prefer Grep tool over running `rg` or `grep` directly. -### Binary Assets -- **PII-bearing source binaries** (business cards, meeting slides, scanned contracts containing real names / emails / phone numbers) — do not commit. Default `.gitignore` excludes `inbox/sources/*.{jpg,jpeg,png,heic,pdf}` per ADR-047 D47-2. -- **Non-PII asset binaries** (app icons, logos, documentation screenshots, generated figures) — commit when reasonably small (~2 MB soft cap) and the user has approved the asset. Prefer SVG / Markdown over raster when equivalent. -- The gate is PII-content, not file format. A 100 KB app icon is fine; a screenshot leaking real email addresses is not. +## Other Cross-Cutting -### `_distill/` Internal Templates -- `.claude/commands/_distill/` contains internal templates for /distill -- Underscore prefix means not user-invocable -- Agents Read and use them (ADR-048) +- **`docs/` vs PKM**: `docs/` is documentation about Rill itself (distinct from inbox/, knowledge/, workspace/). ADRs live in `docs/decisions/`. Update `SPEC.md` on system design changes. +- **Binary assets**: PII-bearing source binaries (business cards, scanned contracts, screenshots with real names/emails/phones) — don't commit; excluded by default `.gitignore` per ADR-047 D47-2. Non-PII asset binaries (app icons, logos, doc screenshots, figures) — commit if small (~2 MB soft cap) and user-approved. Prefer SVG / Markdown. Gate is PII content, not format. +- **`_distill/` internal templates**: `.claude/commands/_distill/` holds internal templates for /distill (underscore = not user-invocable, agents Read them, ADR-048). diff --git a/.claude/rules/rill-core.md b/.claude/rules/rill-core.md index c4f63b1..e9910cb 100644 --- a/.claude/rules/rill-core.md +++ b/.claude/rules/rill-core.md @@ -1,22 +1,18 @@ # Rill Core Rules -**Entry point** rules for working in a Rill-managed vault. Detailed rules are split into `.claude/rules/rill-*.md` files, auto-loaded each turn by Claude Code. +Entry point for working in a Rill-managed vault. Detailed rules are split into `.claude/rules/rill-*.md` files, auto-loaded each turn. ## What is Rill -Rill is a personal voice journal + knowledge management system. +Personal voice journal + knowledge management system. Core flow: **voice / text → Markdown → GitHub → Claude Code → knowledge / tasks**. All data is plain Markdown; GitHub is the single source of truth. -Core flow: **voice / text → Markdown → GitHub → Claude Code → knowledge / tasks** - -All data is plain Markdown files. GitHub is the single source of truth. - -## Directory Map (standard vault structure) +## Directory Map ``` / ├── inbox/ # Input layer (immutable): journal, meetings, tweets, web-clips, sources ├── knowledge/ # Accumulation layer: self/, notes/, people/, orgs/ -├── projects/ # Execution hub layer (ADR-080): per-project directory +├── projects/ # Execution hub layer (ADR-080) ├── workspace/{id}/ # Working layer (stateful) ├── tasks/{slug}/ # Per-task directory (_task.md + optional artifacts, ADR-076) ├── reports/ # Claude Code outputs: daily/, newsletter/ @@ -24,46 +20,41 @@ All data is plain Markdown files. GitHub is the single source of truth. ├── taxonomy.md # Tag vocabulary management └── .claude/ ├── commands/ # Claude Code skills - └── rules/ # Split rules (including this file, auto-loaded) + └── rules/ # Split rules (including this file) ``` -## Critical Invariants (must be guaranteed every turn) +## Critical Invariants -1. **Original files in inbox/ are read-only**. Never modify them (no appending either). Creating organized versions in `_organized/` is allowed -2. **Use `rill mkfile` for new file creation**. LLMs must never write `created` values directly (for timestamp accuracy) -3. **Frontmatter is required**. See `rill-data-model.md` for schema -4. **Claude Code integration boundary**: Agent SDK / OAuth token management / `--bare` mode / API Key default auth are **prohibited**. Use `claude -p --output-format stream-json` for automation -5. **Contact information (email, phone) must only be written to `knowledge/people/` or `knowledge/orgs/`** +1. Original files in `inbox/` are read-only (no appending). Organized versions in `_organized/` are allowed. +2. Use `rill mkfile` for new files — LLMs must never write `created` directly. +3. Frontmatter required. See `rill-data-model.md`. +4. Claude Code integration boundary: Agent SDK / OAuth tokens / `--bare` mode / API Key default auth are prohibited. Use `claude -p --output-format stream-json` for automation. +5. Contacts (email, phone) only in `knowledge/people/` or `knowledge/orgs/`. -## Detailed Rules (index to split files) +## Detailed Rules (index) -Each area's detailed rules are in the following split files. All auto-loaded from `.claude/rules/*.md`: +- **Data model**: [rill-data-model.md](rill-data-model.md) — frontmatter, tags, links, mentions +- **inbox/**: [rill-inbox.md](rill-inbox.md) — immutability, `_organized/`, `.processed` +- **knowledge/**: [rill-knowledge.md](rill-knowledge.md) — notes pool, entities, contact rules +- **workspace/**: [rill-workspace.md](rill-workspace.md) — completion conditions, file-first +- **tasks/**: [rill-tasks.md](rill-tasks.md) — status, due/scheduled, subtasks +- **projects/**: [rill-projects.md](rill-projects.md) — execution hub layer (ADR-080) +- **reports/ + pages/**: [rill-outputs.md](rill-outputs.md) — Daily Note, Newsletter, recipe pairs +- **Claude Code integration**: [rill-claude-code-integration.md](rill-claude-code-integration.md) +- **Autonomous execution**: [rill-autonomous-execution.md](rill-autonomous-execution.md) — lanes, Plan gate, worktrees, Codex usage, two-channel write, three-tier destructive ops -- **Data model**: [rill-data-model.md](rill-data-model.md) — frontmatter schema, tag management, link conventions, mentions -- **inbox/ input layer**: [rill-inbox.md](rill-inbox.md) — immutability principle, `_organized/`, `.processed` -- **knowledge/ accumulation layer**: [rill-knowledge.md](rill-knowledge.md) — notes pool, entity principles, contact rules -- **workspace/ working layer**: [rill-workspace.md](rill-workspace.md) — completion conditions, artifact numbering, file-first principle -- **tasks/ tickets**: [rill-tasks.md](rill-tasks.md) — status values, due/scheduled, subtasks -- **projects/ execution hubs**: [rill-projects.md](rill-projects.md) — top-level execution hub layer, frontmatter, body structure, section ownership (ADR-080) -- **reports/ + pages/**: [rill-outputs.md](rill-outputs.md) — Daily Note, Newsletter, pages recipe pairs -- **Claude Code integration**: [rill-claude-code-integration.md](rill-claude-code-integration.md) — `rill mkfile`, GUI path-display convention, zsh compatibility -- **Autonomous execution**: [rill-autonomous-execution.md](rill-autonomous-execution.md) — two-lane detection (PKM main-direct vs dev worktree), single Plan gate, worktree slug-identity, Codex dual usage, two-channel write invariant, three-tier destructive operations - -Additionally, container directory `CLAUDE.md` files (e.g., `inbox/meetings/`, `knowledge/people/`) are loaded on-demand, providing type-specific rules. +Container `CLAUDE.md` files (e.g., `inbox/meetings/`, `knowledge/people/`) load on-demand for type-specific rules. ## Language Rules -- **Body text**: User's preferred language -- **Technical terms**: Keep in English (Markdown, API, frontmatter, etc.) -- **File/directory names**: English kebab-case -- **Frontmatter keys**: English -- **Commit messages**: English +- Body text: user's preferred language +- Technical terms: English (Markdown, API, frontmatter) +- File/directory names: English kebab-case +- Frontmatter keys, commit messages: English ## Customization -Users can add their own rules via: - -- `.claude/rules/personal-*.md` — Personal rules in separate files (auto-loaded) -- Root `CLAUDE.md` — Project-specific instructions (`rill update` does not touch it) +- `.claude/rules/personal-*.md` — personal rules (auto-loaded) +- Root `CLAUDE.md` — project-specific (untouched by `rill update`) -Rill-managed `rill-*.md` files may be overwritten by `rill update`. Do not edit them directly. +Rill-managed `rill-*.md` files may be overwritten by `rill update` — do not edit directly. diff --git a/.claude/rules/rill-data-model.md b/.claude/rules/rill-data-model.md index 3a88647..2f24388 100644 --- a/.claude/rules/rill-data-model.md +++ b/.claude/rules/rill-data-model.md @@ -1,70 +1,66 @@ # Data Model Rules — Rill -Common frontmatter schema, reference conventions, and link rules for all Rill files. Directory-specific rules are in `inbox.md` / `knowledge.md` / `workspace.md` / `tasks.md` / `outputs.md`. +Common frontmatter schema, references, links. Directory-specific rules: `rill-inbox.md` / `rill-knowledge.md` / `rill-workspace.md` / `rill-tasks.md` / `rill-outputs.md`. -## Common Frontmatter Schema +## Frontmatter Schema -### Required Fields -- `created`: Required. ISO 8601 format (e.g., `2026-04-07T18:30+09:00`). **LLMs must never write this directly** (auto-assigned via `rill mkfile`) -- `type`: Required. File type identifier (`record` / `insight` / `reference` / `task` / `workspace` / `page` / `person` / `org` / `project` / `self` / `daily-note` / `interest-profile`, etc.) +### Required -### Optional Fields -- `source`: Source file path (relative to repo root, no leading `/`). Required for knowledge/notes/ -- `tags`: AI-assigned. Max 3. Inline array. Topic discrimination only (entities go in `mentions`) -- `mentions`: Typed entity reference array. Format `[people/id, orgs/id, projects/id]` (ADR-053). Usable in all file types (ADR-066) -- `related`: Array of related file paths -- `updated`: Auto-set (used in pages/) +- `created`: ISO 8601 (e.g., `2026-04-07T18:30+09:00`). **LLMs must never write directly** — auto-assigned via `rill mkfile`. +- `type`: File type identifier (`record` / `insight` / `reference` / `task` / `workspace` / `page` / `person` / `org` / `project` / `self` / `daily-note` etc.) + +### Optional + +- `source`: relative path (no leading `/`). Required for knowledge/notes/. +- `tags`: AI-assigned, max 3, inline array. Topic discrimination only — entities go in `mentions`. +- `mentions`: typed entity array `[people/id, orgs/id, projects/id]` (ADR-053). Usable in all file types (ADR-066). +- `related`: array of related file paths. +- `updated`: auto-set (used in pages/). ## Tag Management -- When assigning tags, Read `taxonomy.md` to check existing tags. Add new tags if none match -- When passing tag vocabulary to sub-agents, use **YAML list format (name + desc)**. Inline format like `tag(description)` is prohibited (ADR-046 D46-3) -- Tags are kebab-case, English -- Using entity IDs as tags is prohibited (`acme-saas` is not a tag — use `mentions: [projects/acme-saas]`) -- Deprecated tags are listed in taxonomy.md's "Deprecated Tags" table. If found, remove via Edit +- Read `taxonomy.md` before assigning tags. Add new tags only if none match. +- Passing tag vocabulary to sub-agents: use YAML list format (name + desc). Inline `tag(description)` prohibited (ADR-046 D46-3). +- kebab-case, English. +- Entity IDs as tags are prohibited (`acme-saas` is not a tag → use `mentions: [projects/acme-saas]`). +- Deprecated tags in taxonomy.md's "Deprecated Tags" table — remove via Edit when found. ## Reference / Link Rules -- **In-body links**: Use standard Markdown `[text](path)`. Wiki links `[[]]` are not used -- **Backtick-only ID references are prohibited**: No `` `task-xxx` `` style references — always use `[display name](tasks/xxx/_task.md)` links (ADR-064, ADR-076) -- **Frontmatter fields** (source, related, mentions, etc.) are structured data — do not use Markdown links. Keep as plain path strings +- **In-body links**: standard Markdown `[text](path)`. Wiki links `[[]]` not used. +- **Backtick-only ID references prohibited**: no `` `task-xxx` `` — always `[display name](tasks/xxx/_task.md)` (ADR-064, ADR-076). +- **Frontmatter fields** (source, related, mentions): structured data — plain path strings, no Markdown links. -## File Creation Rules +## File Creation -- **Use `rill mkfile` for new file creation** (ADR-060). Ensures timestamp accuracy -- LLMs must never write `created` values directly -- See `claude-code-integration.md` for details +Use `rill mkfile` for new files (ADR-060) — ensures timestamp accuracy. LLMs never write `created` directly. See `rill-claude-code-integration.md`. -## `source:` Read Priority Rule +## `source:` Read Priority -- When reading a `source:` file, if an identically-named file exists in `_organized/`, prefer reading that one -- Example: `source: inbox/meetings/2026-02-16-X.md` → if `inbox/meetings/_organized/2026-02-16-X.md` exists, Read that instead +When reading a `source:` file, prefer the same-named file in `_organized/` if present. Example: `source: inbox/meetings/2026-02-16-X.md` → if `inbox/meetings/_organized/2026-02-16-X.md` exists, Read that. ## Entity References (mentions) -- Project linkage: `mentions: [projects/{id}]` (ADR-066, ADR-080) - - Resolution path: `projects/{id}/_project.md` (top-level, moved from the legacy `knowledge/projects/`) - - The dedicated `project` field is deprecated -- Person linkage: `mentions: [people/{id}]` - - Resolution path: `knowledge/people/{id}.md` -- Organization linkage: `mentions: [orgs/{id}]` - - Resolution path: `knowledge/orgs/{id}.md` +- Project: `mentions: [projects/{id}]` → `projects/{id}/_project.md` (ADR-066, ADR-080 — top-level, moved from old `knowledge/projects/`). Dedicated `project` field deprecated. +- Person: `mentions: [people/{id}]` → `knowledge/people/{id}.md` +- Org: `mentions: [orgs/{id}]` → `knowledge/orgs/{id}.md` - Multiple allowed: `mentions: [people/alex-chen, orgs/sunrise-hotel, projects/acme-saas]` -The mention text itself stays the same across all entity types; only the resolution path differs. +Mention text is the same across entity types; only resolution path differs. ## Note Metadata Repair (ADR-046 D46-7) -When reading knowledge/notes/ files, perform the following auto-repairs: +When reading knowledge/notes/ files: + +**Mode A — direct fix** (no judgment, 1-2 Edits): + +1. Remove deprecated tags +2. Move entity IDs from `tags` to `mentions` in typed format (`{type}/{id}`); skip if already there -**Mode A — Direct fix (no AI judgment needed, 1-2 Edits)**: -1. If deprecated tags are found, remove via Edit -2. If entity IDs are found in tags, move to `mentions` in typed format (`{type}/{id}`) (skip if already in mentions) +**Mode B — append to `knowledge/.refresh-queue`** (detection only, dedup against existing): -**Mode B — .refresh-queue addition (detection only)**: -Append file paths matching any of the following to `knowledge/.refresh-queue` (check for existing entries): -- `tags` is empty `[]` -- `tags` has only 1 tag and that tag has 50+ uses (generic tag only) -- `mentions` field does not exist -- `related` field does not exist -- `type` is not one of `record` / `insight` / `reference` +- `tags` empty `[]` +- `tags` has only 1 tag with 50+ uses (generic-only) +- `mentions` field missing +- `related` field missing +- `type` is not `record` / `insight` / `reference` diff --git a/.claude/rules/rill-inbox.md b/.claude/rules/rill-inbox.md index 93d452e..4acd940 100644 --- a/.claude/rules/rill-inbox.md +++ b/.claude/rules/rill-inbox.md @@ -1,53 +1,39 @@ # Inbox Rules — Rill -`inbox/` is the **input layer (immutable)**. Stores incoming information from external sources and personal journals. - -## Overall Structure +`inbox/` is the **input layer (immutable)** — incoming information from external sources and personal journals. ``` inbox/ -├── journal/ # Shallow Think: chronological journal (your thoughts) +├── journal/ # Shallow Think: chronological journal ├── meetings/ # Meeting notes (source-type: meeting) ├── tweets/ # Tweets (source-type: tweet) ├── web-clips/ # Web Clips (source-type: web-clip) -├── think-outputs/ # Think output recirculation (future) -└── sources/ # Other external inputs (uncategorized) +├── think-outputs/ # Think output recirculation +└── sources/ # Other external inputs ``` -Each subdirectory has `.processed` (tracking list) and `_organized/` (organized versions). +Each subdirectory has `.processed` (tracking) and `_organized/` (organized versions). ## Core Principle: Immutability -**Original files in inbox/ are read-only. Never modify them.** Creating organized versions in `_organized/` is allowed. - -- No appending to existing files -- No modifying content of existing files -- New input must be added as new files (especially strict for inbox/journal/ — microfile strategy) +**Original files in inbox/ are read-only.** No appending, no modifying. New input → new file (especially strict for journal/ — microfile strategy). Creating organized versions in `_organized/` is allowed. -Violations break the design where Git functions as a transaction log, and corrupt `.processed` tracking and `/distill` idempotency. +Violations break Git-as-transaction-log, corrupt `.processed` tracking, and break `/distill` idempotency. ## `_organized/` Convention -- Each subdirectory has an `_organized/` subdirectory -- Organized file names match the original file names -- `source:` references prefer `_organized/` (if an identically-named file exists in `_organized/`, read that one) +Each subdirectory has `_organized/`. Organized filenames match originals. `source:` references prefer `_organized/` if a same-named file exists there. ## `.processed` Format -- `.processed` in each subdirectory tracks processed files -- journal/: One filename per line (simple list) -- Others (meetings/, tweets/, web-clips/, sources/): `filename:status` format (status: `organized` / `extracted` / `skipped`) -- Tracked by Git (shared processing state) +Tracks processed files (Git-tracked, shared state): -## Subdirectory-Specific Rules +- `journal/`: one filename per line +- Others (`meetings/`, `tweets/`, `web-clips/`, `sources/`): `filename:status` (status: `organized` / `extracted` / `skipped`) -Each subdirectory has its own `CLAUDE.md` with type-specific rules (on-demand loaded): +## Subdirectory-Specific Rules -- `inbox/journal/CLAUDE.md` — Shallow Think microfile strategy -- `inbox/meetings/CLAUDE.md` — Meeting notes participants frontmatter, organized format -- `inbox/tweets/CLAUDE.md` — Tweet archive, URL metadata -- `inbox/web-clips/CLAUDE.md` — Web Clip formatting -- `inbox/sources/CLAUDE.md` — Other external inputs +Each subdirectory has its own `CLAUDE.md` (on-demand loaded): `journal/` (microfile strategy), `meetings/` (participants + organized format), `tweets/`, `web-clips/`, `sources/`. ## Frontmatter Basics @@ -59,15 +45,13 @@ original-source: "Google Meet Gemini Notes" # optional --- ``` -See `data-model.md` for details. +See `rill-data-model.md` for details. -## File Naming Convention +## File Naming -- journal: `YYYY-MM-DD-HHmmss.md` (auto-generated by `rill log`) -- meetings/tweets/web-clips: `YYYY-MM-DD-description.md` or `YYYY-MM-DD-HHmmss-slug.md` +- journal: `YYYY-MM-DD-HHmmss.md` (auto via `rill log`) +- meetings / tweets / web-clips: `YYYY-MM-DD-description.md` or `YYYY-MM-DD-HHmmss-slug.md` ## PII / Contact Information -- **Do not write contact information (email, phone numbers) in inbox/** -- Contact information belongs only in knowledge/people/ or knowledge/orgs/ (ADR-047) -- See `knowledge.md` for details +**Do not write contacts (email, phone) in inbox/.** They belong only in `knowledge/people/` or `knowledge/orgs/` (ADR-047). See `rill-knowledge.md`. diff --git a/.claude/rules/rill-knowledge.md b/.claude/rules/rill-knowledge.md index 871a743..18f257f 100644 --- a/.claude/rules/rill-knowledge.md +++ b/.claude/rules/rill-knowledge.md @@ -1,35 +1,27 @@ # Knowledge Rules — Rill -`knowledge/` is the **accumulation layer (Evergreen)**. Stores distilled atomic knowledge and entity information. +`knowledge/` is the **accumulation layer (Evergreen)** — distilled atomic knowledge and entity information. ## Structure ``` knowledge/ -├── self/ # Interest Profile + state layer (8 files; see "knowledge/self/" section below) +├── self/ # Interest Profile + state layer (8 files; see below) ├── notes/ # Distilled atomic knowledge (pool, flat) ├── people/ # Person entities └── orgs/ # Organization entities ``` -Each container directory (people/orgs/self/) has its own `CLAUDE.md` (on-demand loaded). +Each container (people/orgs/self/) has its own `CLAUDE.md` (on-demand loaded). Projects (execution hubs) live under top-level `projects/` (ADR-080) — see `rill-projects.md`. -Projects (execution hubs) live under top-level `projects/` (ADR-080), not under `knowledge/`. See `.claude/rules/rill-projects.md` for details. +## knowledge/notes/ -## knowledge/notes/ Principles +- 1 file = 1 atomic piece of knowledge. English kebab-case filename. +- **Evergreen**: if content overlaps an existing file, update it rather than creating a new one. +- **Filename prefix convention**: entity-tied notes prefix with entity ID (`acme-saas-pricing-model.md`); generic knowledge uses topic only (`saas-manual-operation-bootstrap.md`). +- **Stays flat** — no subdirectories. Categorization via filename prefix + `mentions` + `tags`. -### Atomic Units -- 1 file = 1 atomic piece of knowledge -- File names: English kebab-case, reflecting content (e.g., `whisper-api-comparison.md`) -- **Evergreen**: If content overlaps with an existing file, update the existing file rather than creating a new one - -### File Name Prefix Convention -- **Notes tied to a specific entity should use the entity ID as prefix (recommended)** - - Examples: `acme-saas-pricing-model.md`, `alex-chen-contract-details.md` -- Generic knowledge uses topic only - - Example: `saas-manual-operation-bootstrap.md` - -### Required Frontmatter +### Frontmatter ```yaml --- @@ -43,74 +35,43 @@ related: --- ``` -- `type`: `record` (facts/data) / `insight` (observations/interpretations) / `reference` (external citations) -- `source`: **Required**. knowledge/notes/ always has a source - -### Duplicate Check +- `type`: `record` (facts/data) / `insight` (observations) / `reference` (external citations) +- `source`: required — knowledge/notes/ always has one -- Check for duplicates against existing files before creating new ones (Grep/Glob) -- Update existing files if overlapping (Evergreen principle) - -### Keep Flat - -- knowledge/notes/ has many files but **stays flat**. Do not create subdirectories (breaking change, conflicts with fluid mention/tag classification) -- Categorization is done via **filename prefix + mentions + tags** +Duplicate-check before creating (Grep/Glob); update existing on overlap. ## Entity Files (people/orgs/) -### Common Principles - - **frontmatter** = search anchor + normalization hub -- **body** = distilled key facts -- File name matches the `id` field (kebab-case) -- `id` is a unique short identifier. Used in task `mentions` +- **body** = distilled key facts (~20 max, Evergreen) +- Filename matches `id` (kebab-case); `id` is the short identifier used in task `mentions` -### What to Write -- Key facts (/distill auto-accumulates) -- Guideline: max ~20 items -- Updated under Evergreen principle - -### What NOT to Write (important) -- Interaction history -- Task lists -- Artifact links -- Aggregation results - -→ These belong in `journal/`, `tasks/`, `workspace/`, `pages/` as appropriate. Dynamic aggregation is executed by Claude Code via grep/read. - -## projects/ (execution hub layer) - -Projects (execution hubs) are placed under top-level `projects/`, not under `knowledge/` (ADR-080). See `.claude/rules/rill-projects.md` for details. - -Within `knowledge/`, only people and orgs remain as entities (self/ is a singleton entity with its own scope). +**Do NOT write**: interaction history, task lists, artifact links, aggregation results. Those belong in `journal/`, `tasks/`, `workspace/`, `pages/`. Dynamic aggregation runs via grep/read. ## knowledge/self/ (Interest Profile + state layer) -- Singleton-entity directory (`type: self`), located directly under `knowledge/` alongside `notes/` / `people/` / `orgs/` -- **8 files**, each owned by a distinct velocity / skill (per `workspace/2026-05-07-dream-system-rill-application/006-self-knowledge-layer-design.md`): - - `profile.md` — Core Identity (year-scale) - - `interests.md` — Deep Interests / Curiosity / Obligations / Career (monthly) - - `direction.md` — Active Projects + cross-project meta-direction + Career direction (monthly) - - `current-state.md` — pulse snapshot (high-velocity, updated by `/pulse`, capped at 80 lines) - - `decisions.md` — curated decision digest (3-month window, updated by `/retrospective`) - - `observations.md` — longitudinal self-observations (updated by `/retrospective` with user `[x]` approval) - - `history.md` — career / event history (manual) - - `constraints.md` — constraints (family / financial / health, etc., manual) -- Referenced by `/briefing`, `/newsletter`, `/pulse`, `/solve`, `/eval`, `/distill profile-agent` -- **Guide, not constraint**: LLMs may suggest adjacent areas outside listed categories -- `interests.md` / `direction.md` are auto-updated by `/distill profile-agent` (with conservative rules) +Singleton entity (`type: self`). **8 files**, each owned by a distinct velocity / skill: + +- `profile.md` — Core Identity (year-scale) +- `interests.md` — Deep Interests / Curiosity / Obligations / Career (monthly) +- `direction.md` — Active Projects + cross-project meta-direction + Career direction (monthly) +- `current-state.md` — pulse snapshot (high-velocity, by `/pulse`, capped at 80 lines) +- `decisions.md` — curated decision digest (3-month window, by `/retrospective`) +- `observations.md` — longitudinal self-observations (by `/retrospective` with user `[x]` approval) +- `history.md` — career / event history (manual) +- `constraints.md` — constraints (family / financial / health, manual) -## Contact Information (ADR-047, important) +Referenced by `/briefing`, `/newsletter`, `/pulse`, `/solve`, `/eval`, `/distill profile-agent`. **Guide, not constraint** — LLMs may suggest adjacent areas. `interests.md` / `direction.md` auto-update via `/distill profile-agent` (conservative rules). Design rationale: `workspace/2026-05-07-dream-system-rill-application/006-self-knowledge-layer-design.md`. -**Contact information (email addresses, phone numbers) must only be written in knowledge/people/ or knowledge/orgs/.** Do not write them elsewhere (notes/, workspace/, inbox/, tasks/, etc.). +## Contact Information (ADR-047) -This minimizes the PII exposure surface. +**Email addresses and phone numbers may only live in `knowledge/people/` or `knowledge/orgs/`** — nowhere else (notes/, workspace/, inbox/, tasks/, etc.). Minimizes PII exposure surface. ## Binary Assets -The rule gates on **whether the binary embeds personal data**, not on file format. +The rule gates on **PII content**, not file format. -- **PII-bearing source binaries** (business cards, scanned contracts, meeting PDFs / slides / screenshots with real names, emails, or phone numbers) must not be committed. These typically land under `inbox/sources/*.{jpg,jpeg,png,heic,pdf}` and are excluded by the default `.gitignore` per [ADR-047 D47-2](../../docs/decisions/047-git-crypt-pii-encryption.md). -- **Non-PII asset binaries** (app icons, logos, UI screenshots intended for documentation, generated figures) **may** be committed when they are reasonably small (soft cap ~2 MB per file) and the user has approved the asset. Prefer text-based formats (SVG, Markdown tables) when equivalent; fall back to PNG / JPG only when a binary format is genuinely required. +- **PII-bearing source binaries** (business cards, scanned contracts, meeting PDFs/slides/screenshots with real names/emails/phones): do not commit. Typically `inbox/sources/*.{jpg,jpeg,png,heic,pdf}`, excluded by default `.gitignore` per ADR-047 D47-2. +- **Non-PII asset binaries** (app icons, logos, doc UI screenshots, generated figures): may be committed when small (~2 MB soft cap) and user-approved. Prefer SVG / Markdown when equivalent. -A 100 KB app icon is not PII. A screenshot of an inbox view showing real email addresses is. When in doubt, strip the PII before committing, or keep the file out of Git. +A 100 KB app icon is fine; a screenshot leaking real email addresses is not. Strip PII or keep out of Git. diff --git a/.claude/rules/rill-outputs.md b/.claude/rules/rill-outputs.md index 7fc489a..2e39b00 100644 --- a/.claude/rules/rill-outputs.md +++ b/.claude/rules/rill-outputs.md @@ -1,27 +1,20 @@ # Output Rules — Rill -`reports/` and `pages/` are Rill's **output layer**. Claude Code-generated artifacts and human-facing aggregated documents. +`reports/` and `pages/` are the **output layer** — Claude Code-generated artifacts and human-facing aggregated documents. ## reports/ -### Structure - ``` reports/ -├── daily/ # Daily Note (generated by /briefing) -├── newsletter/ # Research reports (generated by /newsletter) -└── eval/ # Metadata quality analysis (generated by /eval) +├── daily/ # Daily Note (/briefing) +├── newsletter/ # Research reports (/newsletter) +└── eval/ # Metadata quality reports (/eval) ``` ### reports/daily/ -- Filename: `YYYY-MM-DD.md` -- Daily Note generated by `/briefing` -- Integrates forward-looking (today's focus) + backward-looking (yesterday's activity) -- Prose report quality -- Tasks are collected from ticket files (`type: task`) with context-rich summaries (ADR-063) +`YYYY-MM-DD.md`. Daily Note from `/briefing`. Integrates forward-looking (today's focus) + backward-looking (yesterday's activity), prose-quality. Tasks collected from `type: task` tickets with context-rich summaries (ADR-063). -#### Frontmatter ```yaml --- created: 2026-03-11T08:00+09:00 @@ -33,37 +26,24 @@ journal-count: 3 ### reports/newsletter/ -- Filename: `YYYY-MM-DD.md` (or with versioning) -- Daily research report generated by `/newsletter` (ADR-031) -- 3-layer context model (Identity/Attention/Impulse) -- Alerts limited to past 2 weeks +`YYYY-MM-DD.md`. From `/newsletter` (ADR-031). 3-layer context model (Identity/Attention/Impulse). Alerts limited to past 2 weeks. ### reports/eval/ -- Metadata quality analysis reports generated by `/eval` -- Do not confuse with `eval/` directory (eval/ contains structured evaluation metrics data, reports/eval/ is the human-readable report) -- Two layouts are supported: - - **Flat**: `reports/eval/{YYYY-MM-DD}.md` — one-file eval report (default, matches `/eval` output) - - **Harness directory**: `reports/eval/{slug}/` — multi-file eval session (harness runners, fixtures, outputs, and one or more human-readable `*.md` reports co-located). The `*.md` files at the directory root are the canonical reports; `fixtures/`, `outputs/`, and other subdirectories hold harness assets and are not surfaced in Reports View. Use this layout when a report needs its generator (shell runners, fixture data, output captures) to stay alongside it for reproducibility. +Metadata quality reports from `/eval`. Not to be confused with `eval/` (structured metrics data; `reports/eval/` is the human-readable report). Two layouts: -## reports/ Are Searchable (ADR-061) +- **Flat**: `reports/eval/{YYYY-MM-DD}.md` — default one-file report +- **Harness directory**: `reports/eval/{slug}/` — multi-file session with runners, fixtures, outputs co-located. `*.md` at root = canonical reports; subdirs (`fixtures/`, `outputs/`) hold harness assets and are not surfaced in Reports View -- Include in skill search targets -- `/focus` context collection and `/briefing` report references search reports/ via Grep -- However, reports/ content is **not auto-distilled** to knowledge/notes/ +`reports/` are searchable (ADR-061) — `/focus` context collection and `/briefing` Grep them. But reports/ content is **not auto-distilled** to knowledge/notes/. ## pages/ -### Structure - -- Flat directory (`pages/{id}.md`) -- Each page has a **recipe pair**: `{id}.recipe.md` +Flat directory (`pages/{id}.md`). Each page has a **recipe pair** (`{id}.recipe.md`). -### Principles - -- **Human-facing aggregated documents** (Materialized Views. Wiki page metaphor. ADR-062) -- **Excluded from AI search** (/distill, /briefing, /eval do not Grep pages/) -- Direct edits (data additions) should be written to canonical sources (inbox/, knowledge/) first, then reflected in Pages (D62-5) +- **Human-facing aggregated documents** (Materialized Views, wiki metaphor, ADR-062) +- **Excluded from AI search** (`/distill`, `/briefing`, `/eval` do not Grep pages/) +- Direct edits should first fix canonical sources (inbox/, knowledge/), then reflect in pages (D62-5) ### Frontmatter @@ -81,27 +61,18 @@ sources: # optional --- ``` -- `created`, `type: page`, `id`, `name`, `description` **required** -- `id` matches filename (kebab-case, no date prefix) +Required: `created`, `type: page`, `id`, `name`, `description`. `id` matches filename (kebab-case, no date prefix). ### Recipe File (required) -- `{id}.recipe.md`: Describes the page's purpose, source hints, and fixed sections -- `type: recipe` -- Structure is not prescribed — AI judges from the purpose - -### Rules for Updating pages/ +`{id}.recipe.md` describes purpose, source hints, fixed sections. `type: recipe`. Structure not prescribed — AI judges from purpose. -**Always Read the corresponding `.recipe.md` first to understand the page's purpose.** Recipes describe purpose and source hints, not rigid structures (ADR-062). +### Updating pages/ -### Creation / Update Methods +**Read the corresponding `.recipe.md` first.** Recipes describe purpose and source hints, not rigid structures (ADR-062). -- CRUD via `/page` skill -- When editing directly, fix the canonical source first, then reflect in pages/ -- Generate file with `rill mkfile pages --slug {id} --type page` +CRUD via `/page` skill. When editing directly, fix canonical source first then reflect. Generate via `rill mkfile pages --slug {id} --type page`. ### Writing Style -- Prose-based -- Tables only for comparisons -- Maintain a chronological axis +Prose-based. Tables only for comparisons. Maintain a chronological axis. diff --git a/.claude/rules/rill-projects.md b/.claude/rules/rill-projects.md index 4e63bf8..585ef75 100644 --- a/.claude/rules/rill-projects.md +++ b/.claude/rules/rill-projects.md @@ -1,6 +1,6 @@ # Project Rules — Rill -`projects/` is the **execution hub layer**. The management unit for initiatives that bundle multiple tasks (ADR-080). +`projects/` is the **execution hub layer** — management unit for initiatives bundling multiple tasks (ADR-080). ## Structure @@ -8,17 +8,11 @@ projects/ └── {slug}/ ├── _project.md # meta + state + auto-generated sections - ├── _summary.md # generated on completion (optional, convention) + ├── _summary.md # generated on completion (optional) └── NNN-description.md # optional artifacts (chronological) ``` -Directory name: `{slug}` (kebab-case). `_project.md` is the project's main file. - -## File Names - -- Directory: `{slug}/` (kebab-case) -- Main: `{slug}/_project.md` -- Examples: `projects/rill/_project.md`, `projects/acme-saas/_project.md` +Directory name: `{slug}` (kebab-case). Examples: `projects/rill/_project.md`, `projects/acme-saas/_project.md`. ## Frontmatter @@ -29,24 +23,21 @@ type: project id: rill # required, matches directory name name: Rill # required, display name status: active # required: planning | active | paused | done -goal: Personal PKM OSS release # short one-line version for GUI list (optional) +goal: Personal PKM OSS release # one-line version for GUI list (optional) paused_until: 2026-07-01 # optional when status: paused tags: [rill] mentions: [orgs/rillmd] --- ``` -### Required Fields -- `type: project` -- `id` -- `name` -- `status` +Required: `type: project`, `id`, `name`, `status`. ### State Values -- `planning`: tasks not yet defined, scope being shaped + +- `planning`: scope being shaped - `active`: tasks in progress -- `paused`: temporarily paused (`paused_until` may indicate resume target date) -- `done`: DoD met, completed +- `paused`: temporarily paused (`paused_until` may indicate resume target) +- `done`: DoD met ## Body Structure @@ -59,94 +50,75 @@ mentions: [orgs/rillmd] - {Completion condition (DoD), verifiable end-state} ## Current Focus -{Current status, free-form 1-3 paragraphs} +{Free-form 1-3 paragraphs} ## Active Tasks - [ ] [task title](../../tasks/{slug}/_task.md) — status / due / depends-on -- ... ## Related Workspaces - [WS name](../../workspace/{id}/_workspace.md) — status / last updated -- ... ## Watch ### Competitors -- ... ### Keywords -- ... ## Key Facts -- (up to 20, overflow moves out to knowledge/notes/) +- (up to 20, overflow → knowledge/notes/) ## Repository (optional) - `repo-name` — purpose. `~/path` ## See Also -- (manual links, things the auto-generated sections don't capture) +- (manual links not captured by auto-generated sections) ``` -## Section Ownership (which skill writes which) +## Section Ownership -| Section | Owner skill | Update timing | +| Section | Owner | Update timing | |---|---|---| -| Goal | `/promote` (workspace crystallization) | At creation and on scope changes | +| Goal | `/promote` | At creation and on scope changes | | Current Focus | `/distill` profile-agent | Weekly | | Active Tasks | `/refresh-project` | Before `/project` invocation | | Related Workspaces | `/refresh-project` | Same as above | -| Watch | `/distill` knowledge-agent | Accumulated from research output | +| Watch | `/distill` knowledge-agent | Accumulated from research | | Key Facts | `/distill` knowledge-agent | Accumulated, moveOut at 20 cap | -| Repository | bootstrap + manual correction | Rare | -| See Also | Manual correction | As needed | +| Repository | bootstrap + manual | Rare | +| See Also | Manual | As needed | -Concurrent writes are guarded by skill-level locks (`.claude/state/{skill-name}.lock`). Section-level locks are not required. +Concurrent writes are guarded by skill-level locks (`.claude/state/{skill-name}.lock`). Section-level locks not required. ## Artifacts (optional) -Under `projects/{slug}/`, optional `NNN-{description}.md` artifacts may be placed. The `type` field is not forced — artifacts emerge only when needed. - -Examples: current-state snapshots, scope-change decisions, reviews, plans. When skills generate artifacts, the recommended convention is to assign `type:` automatically (snapshot / decision / review / plan, etc.). +Under `projects/{slug}/`, optional `NNN-{description}.md` artifacts may be placed when needed (snapshots, decisions, reviews, plans). When skills generate them, assign `type:` automatically. ## Completion Summary — `_summary.md` -When a project transitions to `status: done`, generate a `_summary.md` (same convention as workspace's `_summary.md`). - -- Generated by a skill (e.g. `/project complete`) at completion time -- Retrospective, accomplishments, candidates for distillation into knowledge/notes/ +When a project transitions to `status: done`, generate `_summary.md` (same convention as workspace's). Retrospective + distillation candidates. ## Relationship with Workspaces -- One project ↔ **N workspaces** (1:N, preserving ADR-042) -- Workspaces are for divergent thinking; projects manage execution -- Results crystallized in a workspace are decanted into a project via `/promote` +One project ↔ N workspaces (ADR-042). Workspaces = divergent thinking; projects = execution. Results crystallize via `/promote`. ## Relationship with Tasks -- Tasks live independently in `tasks/{slug}/_task.md` (ADR-076, no physical move) -- Tasks and projects are linked virtually via mention (`mentions: [projects/{id}]`) -- The `Active Tasks` section is auto-generated by mention reverse-lookup -- Task dependencies are expressed via frontmatter `depends-on` / `blocks` (see rill-tasks.md) +Tasks live in `tasks/{slug}/_task.md` (ADR-076, no physical move). Linked virtually via `mentions: [projects/{id}]`. The `Active Tasks` section auto-generates by mention reverse-lookup. Task dependencies use frontmatter `depends-on` / `blocks` (see rill-tasks.md). ## Completion Criteria (DoD) Required -Every project **must have completion criteria (DoD)**. Write a verifiable end-state in the Goal section. This ensures the project surface meets the granularity needed for future `/solve {project}` automation (supersedes ADR-049). +Every project **must have DoD**. Write a verifiable end-state in Goal — ensures the project surface meets the granularity needed for future `/solve {project}` automation (supersedes ADR-049). ## mention Namespace -- Referenced via `mentions: [projects/{id}]` (text unchanged from prior convention) -- Resolution path: `projects/{id}/_project.md` (changed from the old `knowledge/projects/{id}.md`) +`mentions: [projects/{id}]`. Resolution: `projects/{id}/_project.md` (changed from old `knowledge/projects/{id}.md`). ## Creation ```bash rill mkfile projects --slug rill --type project -# → generates projects/rill/_project.md with frontmatter ``` -Or via the `/promote` skill, crystallizing from a workspace. +Or via `/promote` from a workspace. ## After Completion -- Remain at `status: done` -- Generate `_summary.md` -- The Active Tasks section becomes empty (automatically, as dependencies resolve) -- Archive if needed (no file move; the directory stays in place with `status: done`) +Remain at `status: done`. Generate `_summary.md`. Active Tasks empties automatically. No file move on archive — directory stays with `status: done`. diff --git a/.claude/rules/rill-tasks.md b/.claude/rules/rill-tasks.md index 8a1d87c..5573031 100644 --- a/.claude/rules/rill-tasks.md +++ b/.claude/rules/rill-tasks.md @@ -1,6 +1,6 @@ # Task Rules — Rill -`tasks/` is the **action layer**. Individual tasks are managed as per-task directories (ADR-063, ADR-076). +`tasks/` is the **action layer**. Tasks are managed as per-task directories (ADR-063, ADR-076). ## Structure @@ -11,15 +11,14 @@ tasks/ └── NNN-description.md # Optional per-task artifacts (time-ordered) ``` -**Directory per task** (ADR-076). Each task lives in its own directory so /solve can accumulate research notes, plans, decisions, and other artifacts under it without spawning a separate workspace. Simple tasks remain a one-file directory (`_task.md` only). +Each task is a directory so /solve can accumulate research notes, plans, decisions under it without spawning a workspace. Simple tasks remain a one-file directory. -- Sub-directories under a task are not allowed (flat artifact layout). -- Binary artifacts (HTML mock, image, PDF) may live at the same level as `_task.md`. +Sub-directories under a task are not allowed (flat artifact layout). Binary artifacts (HTML mock, image, PDF) may live at the same level as `_task.md`. ## File Names -- Directory: `{slug}/` (kebab-case). The ticket body is always `_task.md` inside it. -- **No `task-` prefix needed** on the slug (ADR-063) +- Directory: `{slug}/` (kebab-case). Body is always `_task.md`. +- No `task-` prefix on the slug (ADR-063). - Examples: `tasks/rill-voice-task-management-skill/_task.md`, `tasks/smith-trading-followup/_task.md` ## Frontmatter @@ -29,7 +28,7 @@ tasks/ created: 2026-04-07T10:00+09:00 # auto-assigned by rill mkfile type: task status: open # required -source: inbox/journal/2026-04-07-X.md # required +source: inbox/journal/2026-04-07-X.md # required when there's a discrete upstream tags: [rill, crm] # optional mentions: [projects/rill, people/alex-chen] # optional due: 2026-04-15 # optional: deadline @@ -41,40 +40,31 @@ related: # optional --- ``` -### Required Fields -- `type: task` -- `status` -- `source` — when the task has a discrete upstream (journal, meeting, note). For tasks born from live conversation with no discrete origin, omit it rather than fabricate a link to an unrelated recent file. +`source` is required when there's a discrete upstream (journal, meeting, note); omit it for tasks born from live conversation rather than fabricating a link. ### Status Values -- `draft`: AI-generated unapproved task (approved/rejected via Electron app Review mode, ADR-069) + +- `draft`: AI-generated unapproved task (approved/rejected via Electron Review mode, ADR-069) - `open`: Awaiting start - `waiting`: Waiting on others or events - `someday`: Future/low priority - `done`: Completed - `cancelled`: Cancelled -### Difference Between due and scheduled -- `due`: Deadline ("by when") -- `scheduled`: Planned start date or event date ("when to work on it") -- They are independent. Tasks with a future `scheduled` are excluded from urgent lists (already planned) -- **No `priority` field** (urgency is calculated from due/scheduled) +### due vs scheduled + +`due` is deadline ("by when"); `scheduled` is planned start or event date ("when to work on it"). Independent. Tasks with a future `scheduled` are excluded from urgent lists. No `priority` field — urgency is calculated from due/scheduled. -### Dependency Fields (optional) +### Dependency Fields -- **depends-on**: list of tasks that must be completed before this task can be started - - Example: `depends-on: [tasks/foo, tasks/bar]` - - Format: `tasks/{slug}` (kebab-case task id) -- **blocks**: downstream tasks that this task blocks (the inverse direction, optional) - - Complementary to `depends-on`; either field alone is sufficient +- `depends-on: [tasks/foo, tasks/bar]` — prerequisite tasks +- `blocks: [tasks/baz]` — downstream tasks this one blocks (inverse of depends-on; either alone is sufficient) -`/project {slug} continue` resolves `depends-on` and picks an unblocked task. -`/refresh-project` renders the project's `Active Tasks` section in dependency order. +`/project {slug} continue` resolves `depends-on` to pick an unblocked task. `/refresh-project` renders Active Tasks in dependency order. ## Project Linkage -- Use `mentions: [projects/{id}]` (ADR-066) -- **The dedicated `project` field is deprecated** +Use `mentions: [projects/{id}]` (ADR-066). The dedicated `project` field is deprecated. ## Body Structure @@ -82,139 +72,83 @@ related: # optional # Title ## Goal -(completion condition — what must be true for this task to be done) +(completion condition — what must be true for done) ## Background -(why this task exists, what triggered it, what's at stake) +(why this exists, what triggered it, what's at stake) ## Context -(optional: related notes, workspaces, sources — one link per line with a role descriptor) +(optional: related notes, workspaces, sources — one link per line with role descriptor) ## Request (optional: creator's note to executor — approach hints, pitfalls, constraints) ## History -- 2026-04-07: Task created. (provenance — how it came into existence) +- YYYY-MM-DD: Task created. (provenance) ``` -Scaffolded by `rill task`. See Substance below for how to fill each field. +Scaffolded by `rill task`. ### Dynamic sections (managed by /solve) -When /solve runs against a task, it writes two additional sections into `_task.md`: - -- `## Current Position` — placed directly under the title; updated at each Phase / Step transition. Removed when the task reaches `status: done`. -- `## Plan` — written between `## Context` and `## Request` after the user approves the Phase 3 Plan. Stays even after completion (it documents how the task was solved). - -These sections are managed by /solve and do not need to be hand-authored. +- `## Current Position` — directly under title; updated each Phase / Step transition; removed at `status: done` +- `## Plan` — between `## Context` and `## Request` after Phase 3 approval; stays even after completion (documents how solved) ## Substance -The task file is the primary handoff between creator and executor (human or AI). Thin fields force the executor to re-derive intent from scratch — which defeats the point of persisting a ticket. - -Aim for a body the executor can work from without re-interviewing you. Write as richly as the task's actual complexity warrants: a multi-party commercial task needs real context; a one-line reminder does not. Empty fields and placeholder text are the anti-pattern, not short fields. +The task file is the primary handoff between creator and executor (human or AI). Thin fields force the executor to re-derive intent — defeating the point of a ticket. Write as richly as the task's complexity warrants. Empty fields and placeholder text are the anti-pattern, not short fields. -Per-field notes: +Per-field: -- **Goal**: State the completion condition — something that can be checked true/false. Not a plan. If it genuinely can't be stated at capture time, say so explicitly rather than leaving it blank. -- **Background**: Whatever the executor needs to pick this up cold — the trigger, the stakes, non-obvious prior context. The test is whether someone who wasn't in the room can work from Background alone. -- **Context**: One link per line with a short role descriptor ("why this link matters for this task"). Do not use the `Title::path,Title::path` inline format — it compresses role context to nothing. -- **Request**: Creator's note to executor — approach hints, pitfalls, constraints. Not a plan. Legacy "Action Items" headings (in any language) are deprecated: put intent in Request and concrete checkboxes under Subtasks. +- **Goal**: A verifiable completion condition. Not a plan. If it can't be stated at capture, say so explicitly rather than leaving it blank. +- **Background**: What the executor needs to pick this up cold. The test: can someone who wasn't in the room work from Background alone? +- **Context**: One link per line with a short role descriptor ("why this link matters"). Do not use the legacy `Title::path,Title::path` inline format. +- **Request**: Approach hints, pitfalls, constraints — not a plan. Legacy "Action Items" headings are deprecated. - **History**: Provenance at creation; grows as the task evolves. -- **Frontmatter**: `source` must point to the actual upstream file, or be omitted — do not fill it with an unrelated recent journal. `tags` and `mentions` reflect what the task is actually about. +- **Frontmatter**: `source` points to the real upstream or is omitted. `tags` and `mentions` reflect what the task is about. -## Good Example +## Good Example (abridged) -```markdown ---- -created: 2026-04-17T10:00+09:00 -type: task -status: open -source: inbox/meetings/_organized/2026-04-15-acme-saas-kickoff.md -tags: [onboarding, integrations] -mentions: [projects/acme-saas, people/alex-chen] -due: 2026-05-15 -related: - - knowledge/notes/acme-saas-imap-connector-design.md - - knowledge/notes/sunrise-hotel-imap-retrospective.md ---- +Frontmatter: `source`, `tags`, `mentions: [projects/..., people/...]`, optional `due`, optional `related: [knowledge/notes/...]`. -# Confirm IMAP connectivity for acme-saas trial inbox +Body: -## Goal -IMAP/SMTP access to the acme-saas trial mailbox is confirmed working end-to-end (outbound auth, inbound polling, TLS), with a documented setup procedure their IT team can execute without our help. - -## Background -Alex Chen's team agreed to a 1–2 month trial on their customer-support mailbox during the 2026-04-15 kickoff. Their mail is hosted on an internal groupware suite, so the standard setup docs don't apply. The trial's go/no-go depends on whether the mailbox can be connected without IT policy exceptions. A prior project (sunrise-hotel) used IMAP on a different stack and hit TLS/auth issues that took a week to resolve — we want to pre-empt the same class of bugs before the trial starts. Their IT contact is available only on Thursdays, so discovery calls need to be batched. - -## Context -- [Acme-saas IMAP connector design](knowledge/notes/acme-saas-imap-connector-design.md) — prior design covering TLS config and polling cadence -- [Sunrise-hotel IMAP retrospective](knowledge/notes/sunrise-hotel-imap-retrospective.md) — failure modes on a similar stack -- [Kickoff notes](inbox/meetings/_organized/2026-04-15-acme-saas-kickoff.md) — original commitment - -## Request -Before the first discovery call, draft a specific yes/no checklist (IMAP enabled? external forwarding allowed? TLS version?) so we don't burn their IT window on open-ended questions. - -## History -- 2026-04-17: Created from 2026-04-15 acme-saas kickoff. Connector design doc already exists from prior exploration. -``` +- **Goal** — verifiable end-state ("IMAP/SMTP access confirmed working end-to-end, with a procedure their IT can execute without our help"). +- **Background** — trigger + stakes + non-obvious prior context (e.g., "prior project hit TLS/auth issues for a week; pre-empt that"). The test: someone not in the room can work from this alone. +- **Context** — one link per line with role descriptor: `[Acme-saas IMAP connector design](knowledge/notes/...) — TLS config + polling cadence`. +- **Request** — prescriptive hint, not a plan ("draft a yes/no checklist before the first discovery call so we don't burn their IT window on open-ended questions"). +- **History** — provenance: `2026-04-17: Created from 2026-04-15 kickoff.` -Why this passes: Goal states a verifiable end-state; Background covers trigger, stakes, constraints, and a prior incident informing approach; Context links have role descriptors; Request is prescriptive but not a plan; History records provenance. - -## Bad Example (anti-pattern) - -```markdown ---- -created: 2026-04-16T20:55+09:00 -type: task -status: open ---- - -# Personalize onboarding by asking about interests upfront - -## Goal - -## Background -Empty journal entries come in as meaningless text. Ask interest questions at the start, generate a personal profile, then personalize the flow. - -## Context -Friend test debrief::workspace/2026-04-13-X/006-debrief.md,Task brushup::workspace/2026-04-13-X/007-brushup.md - -## Request - -## History -``` +Passes: Goal verifiable, Background covers trigger/stakes/prior incident, Context links have role descriptors, Request prescriptive but not a plan. -What's wrong: +## Anti-patterns -- No `source`, no `tags`, no `mentions` — executor can't locate the trigger or related entities -- Goal empty — executor has to guess what "done" means -- Background compressed to a sketch; loses the *why* (which friction? which users? how often?) -- Context uses the legacy `Title::path` inline format with no role descriptors -- Request / History empty — creator's intent and provenance are lost +- No `source`, `tags`, or `mentions` → executor can't locate trigger or entities +- Empty `Goal` → executor guesses what "done" means +- Background as a sketch without the *why* (which friction? which users? how often?) +- Context as `Title::path,Title::path` inline (loses role descriptors) +- Empty Request / History → creator's intent and provenance lost ## Subtasks -- Managed as checkboxes within the ticket body -- Promote to separate tickets if independent tracking is needed +Checkboxes within the ticket body. Promote to separate tickets if independent tracking is needed. -## Creation Methods +## Creation 1. AI suggestions during `/focus` (user approval required) -2. `rill task "title" --slug {slug}` CLI — creates `tasks/{slug}/_task.md` +2. `rill task "title" --slug {slug}` CLI 3. `/distill` (interactive only) -Per-task artifacts (research notes, plans, decisions produced by /solve) are scaffolded inside the task directory with `rill mkfile tasks/{slug} --slug {description} --type {research|analysis|decision|progress|review}`. +Per-task artifacts (research, plans, decisions from /solve): `rill mkfile tasks/{slug} --slug {description} --type {research|analysis|decision|progress|review}`. ## Duplicate Check -Check for duplicates against existing tickets before creating new ones (Evergreen principle for tasks/). +Check for duplicates before creating (Evergreen principle). ## After Completion -- Keep as `status: done` -- Move to knowledge/notes/ as `type: record` if appropriate +Keep as `status: done`. Move to `knowledge/notes/` as `type: record` if appropriate. ## In-Session Status Updates -When the user reports task completion, cancellation, or status changes during a session, **update the ticket file's frontmatter `status` immediately**. +When the user reports completion, cancellation, or status changes mid-session, update `_task.md` frontmatter `status` immediately. diff --git a/.claude/rules/rill-workspace.md b/.claude/rules/rill-workspace.md index faff22b..aa6f4dd 100644 --- a/.claude/rules/rill-workspace.md +++ b/.claude/rules/rill-workspace.md @@ -1,25 +1,25 @@ # Workspace Rules — Rill -`workspace/` is the **working layer (stateful)**. Manages projects, areas, and Deep Think sessions in a unified structure. +`workspace/` is the **working layer (stateful)** — projects, areas, and Deep Think sessions in a unified structure. ## Structure ``` workspace/ └── {id}/ - ├── _workspace.md # MOC + state management (unified file) - ├── _summary.md # Summary generated on completion - ├── _log.md # Session/decision history (optional) - └── NNN-description.md # Numbered artifacts + ├── _workspace.md # MOC + state management + ├── _summary.md # generated on completion + ├── _log.md # session / decision history (optional) + └── NNN-description.md # numbered artifacts ``` -Directory name: `{id}/` (kebab-case). Date-prefixed `{YYYY-MM-DD}-{topic}/` or ID only `acme-saas/`. +Directory name: `{id}/` (kebab-case). Date-prefixed `{YYYY-MM-DD}-{topic}/` or ID only. -## Creation Rules +## Creation -1. **Create the date-prefixed topic directory first**: `workspace/{YYYY-MM-DD}-{topic}/` -2. Generate `_workspace.md` with `rill mkfile workspace --slug {id} --type workspace` -3. Artifacts emerge naturally during conversation (separate files from `_workspace.md`) +1. Create the date-prefixed topic directory first +2. Generate `_workspace.md` via `rill mkfile workspace --slug {id} --type workspace` +3. Artifacts emerge naturally as separate files ## `_workspace.md` Frontmatter @@ -37,11 +37,11 @@ client: Client Name # optional --- ``` -### Status Transition Boundary (important) +### Status Transition Boundary -- **Only `/close` may set `status: completed`.** This transition is the trigger for knowledge distillation via the two-layer sub-agent architecture (ADR-073). Any other skill or ad-hoc edit that writes `completed` bypasses distillation and silently loses the session's knowledge -- `/focus` and other interactive skills must **never** set `status: completed` directly. When a session feels done, propose `/close` to the user via AskUserQuestion instead -- Allowed non-`/close` transitions: `completed` → `active` (reopen), `on-hold` → `active` (resume), `active` → `on-hold` (when the user explicitly pauses). These do not trigger distillation and are safe +- **Only `/close` may set `status: completed`.** This triggers knowledge distillation via the two-layer sub-agent architecture (ADR-073). Any other skill or ad-hoc edit that writes `completed` bypasses distillation and silently loses session knowledge. +- `/focus` and other interactive skills must never set `completed` directly — propose `/close` via AskUserQuestion instead. +- Allowed non-`/close` transitions: `completed` → `active` (reopen), `on-hold` ↔ `active`. Safe — no distillation. ## Artifact File Frontmatter @@ -55,59 +55,32 @@ mentions: [projects/rill] # optional --- ``` -### Artifact Types +Types: `progress`, `research`, `analysis`, `decision`, `review`. -- `progress`: Progress records -- `research`: Research/investigation -- `analysis`: Analysis -- `decision`: Decision records -- `review`: Review/critique +## Completion Conditions Required -## Completion Conditions Are Required - -- **Every workspace must have completion conditions** -- Do not use workspaces for areas (responsibility zones with no end condition) -- If an area is needed, decompose it into concrete projects (ADR-029 D29) +Every workspace must have completion conditions. Don't use workspaces for areas (no end condition); decompose into concrete projects (ADR-029). ## Relationship with Tasks -- **1:1 correspondence with workspaces is not required** -- Only create workspaces when artifact accumulation or deep exploration is needed -- Tasks are independently managed as tickets in `tasks/` -- Multiple workspaces may link to one project (`mentions: [projects/xxx]`) +1:1 correspondence with tasks is not required. Create workspaces only when artifact accumulation or deep exploration is needed. Tasks are independent tickets in `tasks/`. Multiple workspaces may link to one project via `mentions: [projects/xxx]`. ## Session Flow ### File-First Principle -Save artifacts to files whenever possible: -- Analysis, research, investigation results -- Reports, surveys, comparison tables -- Decisions and rationale -- Frameworks, design proposals -- Structured output of 3+ paragraphs - -**Text output alone is fine for**: -- Confirmations, questions, brief suggestions (1-2 paragraphs of conversational exchange) -- Directional discussion, brainstorming -- Summary preview before saving to file +Save artifacts to files: analyses, research, comparison tables, decisions, frameworks, design proposals, structured output of 3+ paragraphs. -When in doubt, write it to a file. A workspace's value lives in its accumulated artifacts. +Text-only is fine for: brief confirmations / suggestions (1-2 paragraphs), directional discussion, brainstorming, summary preview before saving. When in doubt, write a file — a workspace's value lives in its accumulated artifacts. ### Updating `_workspace.md` -Update `_workspace.md` at each conversation milestone: -- Add new artifacts to "Related Files (MOC)" -- Update checkboxes for completed issues -- Append progress to "Session History" -- Update "Next Steps" +At each milestone: add new artifacts to "Related Files (MOC)", update checkboxes, append "Session History", update "Next Steps". ## Handling Existing Artifacts -- Once created, artifact files are **generally not modified** -- Additions and corrections go in new files -- `NNN-` numbers increase chronologically +Once created, artifacts are generally not modified — additions / corrections go in new files. `NNN-` numbers increase chronologically. ## Backward Compatibility -In addition to `_workspace.md`, older workspaces may contain `_session.md` or `_project.md`. Treat these as meta files as well (do not rename during Phase 3). +Older workspaces may contain `_session.md` or `_project.md` instead of `_workspace.md`. Treat as meta files (do not rename during Phase 3). diff --git a/.gitignore b/.gitignore index 09bad66..ce3e999 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ # Git worktree checkouts (local working copies, not vault content) .claude/worktrees/ + +# Test execution artifacts (timestamp dirs from bash test/skills/test-*.sh) +test/results/ diff --git a/test/run-all.sh b/test/run-all.sh index 8b7726c..d695cd2 100755 --- a/test/run-all.sh +++ b/test/run-all.sh @@ -22,18 +22,25 @@ run_test() { echo " → $name: PASSED" else echo " → $name: FAILED" - ((TOTAL_FAIL++)) + # Use assignment form (not `((TOTAL_FAIL++))`) — post-increment when + # TOTAL_FAIL=0 evaluates the expression to 0, which under `set -e` would + # exit the script on the first failing suite instead of running the rest. + TOTAL_FAIL=$((TOTAL_FAIL + 1)) fi echo "" } -# Currently only /distill test is implemented -run_test "/distill" "$SCRIPT_DIR/skills/test-distill.sh" - -# Future tests: -# run_test "/briefing" "$SCRIPT_DIR/skills/test-briefing.sh" -# run_test "/focus" "$SCRIPT_DIR/skills/test-focus.sh" -# run_test "/close" "$SCRIPT_DIR/skills/test-close.sh" +run_test "/distill" "$SCRIPT_DIR/skills/test-distill.sh" +run_test "/briefing" "$SCRIPT_DIR/skills/test-briefing.sh" +run_test "/clip-tweet" "$SCRIPT_DIR/skills/test-clip-tweet.sh" +run_test "/close" "$SCRIPT_DIR/skills/test-close.sh" +run_test "/focus" "$SCRIPT_DIR/skills/test-focus.sh" +run_test "/newsletter" "$SCRIPT_DIR/skills/test-newsletter.sh" +run_test "/page" "$SCRIPT_DIR/skills/test-page.sh" +run_test "/solve" "$SCRIPT_DIR/skills/test-solve.sh" +run_test "/inspect" "$SCRIPT_DIR/skills/test-inspect.sh" +run_test "/repair" "$SCRIPT_DIR/skills/test-repair.sh" +run_test "/eval" "$SCRIPT_DIR/skills/test-eval.sh" echo "============================================" if (( TOTAL_FAIL == 0 )); then diff --git a/test/skills/test-briefing.sh b/test/skills/test-briefing.sh index 0f63bba..9db496f 100755 --- a/test/skills/test-briefing.sh +++ b/test/skills/test-briefing.sh @@ -39,8 +39,8 @@ if [[ -z "$VAULT_DIR" ]]; then cp -r "$REPO_DIR/.claude" "$VAULT_DIR/.claude" cp -r "$REPO_DIR/bin" "$VAULT_DIR/bin" [[ -d "$REPO_DIR/plugins" ]] && cp -r "$REPO_DIR/plugins" "$VAULT_DIR/plugins" - cp "$REPO_DIR/taxonomy.md" "$VAULT_DIR/taxonomy.md" - cp "$REPO_DIR/CLAUDE.md" "$VAULT_DIR/CLAUDE.md" + [[ -f "$REPO_DIR/taxonomy.md" ]] && cp "$REPO_DIR/taxonomy.md" "$VAULT_DIR/taxonomy.md" + [[ -f "$REPO_DIR/CLAUDE.md" ]] && cp "$REPO_DIR/CLAUDE.md" "$VAULT_DIR/CLAUDE.md" [[ -f "$REPO_DIR/SPEC.md" ]] && cp "$REPO_DIR/SPEC.md" "$VAULT_DIR/SPEC.md" cd "$VAULT_DIR" git init -q @@ -119,19 +119,19 @@ if [[ -f "$DAILY_NOTE" ]]; then assert_true "[[ -n '$TITLE_LINE' ]]" "SC-01: Has H1 title" # SC-02: Yesterday's Activity - HAS_ACTIVITY=$(grep -ci '^## .*Yesterday\|^## .*Activity' "$DAILY_NOTE" 2>/dev/null || echo "0") + HAS_ACTIVITY=$({ grep -ci '^## .*Yesterday\|^## .*Activity' "$DAILY_NOTE" 2>/dev/null || echo "0"; } | tr -d '[:space:]') assert_gt "$HAS_ACTIVITY" 0 "SC-02: Has activity section" # SC-03: Today's Focus - HAS_FOCUS=$(grep -ci '^## .*Today\|^## .*Focus' "$DAILY_NOTE" 2>/dev/null || echo "0") + HAS_FOCUS=$({ grep -ci '^## .*Today\|^## .*Focus' "$DAILY_NOTE" 2>/dev/null || echo "0"; } | tr -d '[:space:]') assert_gt "$HAS_FOCUS" 0 "SC-03: Has focus section" # SC-04: Situation Analysis - HAS_ANALYSIS=$(grep -ci '^## .*Situation\|^## .*Analysis' "$DAILY_NOTE" 2>/dev/null || echo "0") + HAS_ANALYSIS=$({ grep -ci '^## .*Situation\|^## .*Analysis' "$DAILY_NOTE" 2>/dev/null || echo "0"; } | tr -d '[:space:]') assert_gt "$HAS_ANALYSIS" 0 "SC-04: Has analysis section" # SC-05: Notes (optional — only check if content warrants it) - HAS_NOTES=$(grep -ci '^## .*Notes\|^## .*Attention\|^## .*Caution' "$DAILY_NOTE" 2>/dev/null || echo "0") + HAS_NOTES=$({ grep -ci '^## .*Notes\|^## .*Attention\|^## .*Caution' "$DAILY_NOTE" 2>/dev/null || echo "0"; } | tr -d '[:space:]') echo " INFO: Notes section present: $HAS_NOTES (optional)" fi echo "" diff --git a/test/skills/test-clip-tweet.sh b/test/skills/test-clip-tweet.sh index 16116e0..881f211 100755 --- a/test/skills/test-clip-tweet.sh +++ b/test/skills/test-clip-tweet.sh @@ -41,8 +41,8 @@ if [[ -z "$VAULT_DIR" ]]; then # Remove empty plugins fixture dir to avoid nested copy [[ -d "$VAULT_DIR/plugins" ]] && rmdir "$VAULT_DIR/plugins" 2>/dev/null || true [[ -d "$REPO_DIR/plugins" ]] && cp -r "$REPO_DIR/plugins" "$VAULT_DIR/plugins" - cp "$REPO_DIR/taxonomy.md" "$VAULT_DIR/taxonomy.md" - cp "$REPO_DIR/CLAUDE.md" "$VAULT_DIR/CLAUDE.md" + [[ -f "$REPO_DIR/taxonomy.md" ]] && cp "$REPO_DIR/taxonomy.md" "$VAULT_DIR/taxonomy.md" + [[ -f "$REPO_DIR/CLAUDE.md" ]] && cp "$REPO_DIR/CLAUDE.md" "$VAULT_DIR/CLAUDE.md" [[ -f "$REPO_DIR/SPEC.md" ]] && cp "$REPO_DIR/SPEC.md" "$VAULT_DIR/SPEC.md" mkdir -p "$VAULT_DIR/inbox/tweets/_organized" cd "$VAULT_DIR" diff --git a/test/skills/test-close.sh b/test/skills/test-close.sh index 2edaaf4..b14cd49 100755 --- a/test/skills/test-close.sh +++ b/test/skills/test-close.sh @@ -44,8 +44,8 @@ if [[ -z "$VAULT_DIR" ]]; then cp -r "$REPO_DIR/bin" "$VAULT_DIR/bin" [[ -d "$REPO_DIR/plugins" ]] && cp -r "$REPO_DIR/plugins" "$VAULT_DIR/plugins" # Overlay system files from repo - cp "$REPO_DIR/taxonomy.md" "$VAULT_DIR/taxonomy.md" - cp "$REPO_DIR/CLAUDE.md" "$VAULT_DIR/CLAUDE.md" + [[ -f "$REPO_DIR/taxonomy.md" ]] && cp "$REPO_DIR/taxonomy.md" "$VAULT_DIR/taxonomy.md" + [[ -f "$REPO_DIR/CLAUDE.md" ]] && cp "$REPO_DIR/CLAUDE.md" "$VAULT_DIR/CLAUDE.md" [[ -f "$REPO_DIR/SPEC.md" ]] && cp "$REPO_DIR/SPEC.md" "$VAULT_DIR/SPEC.md" # Initialize git cd "$VAULT_DIR" diff --git a/test/skills/test-distill.sh b/test/skills/test-distill.sh index 1077a81..30c0398 100755 --- a/test/skills/test-distill.sh +++ b/test/skills/test-distill.sh @@ -40,8 +40,8 @@ if [[ -z "$VAULT_DIR" ]]; then [[ -d "$REPO_DIR/plugins" ]] && cp -r "$REPO_DIR/plugins" "$VAULT_DIR/plugins" # Overlay system files from repo (taxonomy, CLAUDE.md, SPEC.md) # These are the files being localized — must use repo versions, not fixture copies - cp "$REPO_DIR/taxonomy.md" "$VAULT_DIR/taxonomy.md" - cp "$REPO_DIR/CLAUDE.md" "$VAULT_DIR/CLAUDE.md" + [[ -f "$REPO_DIR/taxonomy.md" ]] && cp "$REPO_DIR/taxonomy.md" "$VAULT_DIR/taxonomy.md" + [[ -f "$REPO_DIR/CLAUDE.md" ]] && cp "$REPO_DIR/CLAUDE.md" "$VAULT_DIR/CLAUDE.md" [[ -f "$REPO_DIR/SPEC.md" ]] && cp "$REPO_DIR/SPEC.md" "$VAULT_DIR/SPEC.md" # Initialize git (rill mkfile may need it) cd "$VAULT_DIR" diff --git a/test/skills/test-eval.sh b/test/skills/test-eval.sh new file mode 100644 index 0000000..ace21cc --- /dev/null +++ b/test/skills/test-eval.sh @@ -0,0 +1,181 @@ +#!/bin/bash +# test/skills/test-eval.sh — /eval integration test (smoke, single-query) +# +# Usage: bash test/skills/test-eval.sh [--skip-execute] [--vault=PATH] +# +# Smoke test for /eval: runs against a minimal one-query eval/queries.yaml so +# the Deep Path agent fan-out is bounded. Asserts that eval/results/*-deep.yaml +# is produced and contains expected fields. Full 20-query benchmarks live in +# the real vault, not here. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +TEST_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +ASSERTIONS_DIR="$TEST_DIR/assertions" +FIXTURES_DIR="$TEST_DIR/fixtures" +REPO_REAL_DIR="$(cd "$TEST_DIR/.." && pwd)" + +source "$ASSERTIONS_DIR/lib.sh" + +SKIP_EXECUTE=false +VAULT_DIR="" +for arg in "$@"; do + case "$arg" in + --skip-execute) SKIP_EXECUTE=true ;; + --vault=*) VAULT_DIR="${arg#--vault=}" ;; + esac +done + +# --- Setup --- +if [[ -z "$VAULT_DIR" ]]; then + VAULT_DIR=$(mktemp -d -t rill-test-XXXXXX) + echo "=== Setting up test vault: $VAULT_DIR ===" + cp -r "$FIXTURES_DIR"/* "$VAULT_DIR/" + REPO_DIR="$(cd "$TEST_DIR/.." && pwd)" + cp -r "$REPO_DIR/.claude" "$VAULT_DIR/.claude" + cp -r "$REPO_DIR/bin" "$VAULT_DIR/bin" + [[ -d "$REPO_DIR/plugins" ]] && cp -r "$REPO_DIR/plugins" "$VAULT_DIR/plugins" + [[ -f "$REPO_DIR/taxonomy.md" ]] && cp "$REPO_DIR/taxonomy.md" "$VAULT_DIR/taxonomy.md" + [[ -f "$REPO_DIR/CLAUDE.md" ]] && cp "$REPO_DIR/CLAUDE.md" "$VAULT_DIR/CLAUDE.md" + [[ -f "$REPO_DIR/SPEC.md" ]] && cp "$REPO_DIR/SPEC.md" "$VAULT_DIR/SPEC.md" + cd "$VAULT_DIR" + + # Provide a minimal eval/queries.yaml in the real /eval format + # (top-level sequence; entries with id/query/type/scope). + # 7 queries span the 4 supported types (factual / synthesis / entity / exploratory) + # to satisfy Phase 2 stratified sampling; agent fan-out is still bounded. + mkdir -p eval + cat > eval/queries.yaml <<'EOF' +# Minimal smoke fixture for /eval integration test. +# Real vaults have ~20 queries; this minimal set keeps agent fan-out bounded +# while still meeting Phase 2 stratified-sampling type coverage. +- id: SQ1 + query: "Which OAuth provider was selected for sample-project?" + type: factual + scope: narrow +- id: SQ2 + query: "What concerns does Alex Chen have about onboarding?" + type: entity + scope: narrow +- id: SQ3 + query: "What is the silent refresh strategy decision?" + type: factual + scope: narrow +- id: SQ4 + query: "How do Auth0, Clerk, and Supabase compare for B2B usage?" + type: synthesis + scope: medium +- id: SQ5 + query: "What follow-up tasks remain on the OAuth migration?" + type: synthesis + scope: medium +- id: SQ6 + query: "What are emerging risks for the auth migration?" + type: exploratory + scope: medium +- id: SQ7 + query: "What patterns exist across recent provider-decision notes?" + type: exploratory + scope: medium +EOF + if [[ -f "$REPO_DIR/eval/concept.md" ]]; then + cp "$REPO_DIR/eval/concept.md" eval/concept.md + fi + + git init -q + git add -A + git commit -q -m "initial test fixtures (minimal eval queries)" +else + echo "=== Using existing vault: $VAULT_DIR ===" + cd "$VAULT_DIR" +fi + +TIMESTAMP=$(date +%Y%m%d-%H%M%S) +RESULTS_DIR="$TEST_DIR/results/$TIMESTAMP" +mkdir -p "$RESULTS_DIR" + +# --- Snapshot inbox + knowledge/notes hashes (INV-01) --- +HASH_FILE="$RESULTS_DIR/inbox-hashes.txt" +{ + find inbox/ -type f -name "*.md" 2>/dev/null + find knowledge/notes -type f -name "*.md" 2>/dev/null +} | sort | while read -r f; do + echo "$(file_hash "$f") $f" +done > "$HASH_FILE" + +# --- Execute /eval --- +if ! $SKIP_EXECUTE; then + echo "" + echo "=== Executing /eval --refresh-deep (single-query smoke) ===" + echo " (this may take 2-5 minutes)" + echo "" + + isolate_test_vault "$VAULT_DIR" + + claude -p "/eval --refresh-deep" \ + --output-format text \ + --max-turns 100 \ + 2>&1 | tee "$RESULTS_DIR/eval-output.log" + + check_real_repo_contamination "$REPO_REAL_DIR" "$VAULT_DIR" + + echo "" + echo "=== /eval execution complete ===" +fi + +# --- Layer 1 Assertions --- +echo "" +echo "===========================================" +echo " Layer 1: Structural Validation" +echo "===========================================" +echo "" + +# 1. Inbox + knowledge/notes immutability — /eval is read-only on note bodies +echo "=== INV-01: Inbox + knowledge/notes immutability (/eval is read-only) ===" +bash "$ASSERTIONS_DIR/check-no-mutation.sh" "$HASH_FILE" || true +echo "" + +# 2. Deep Path result file emitted +echo "=== EV-01: Deep Path result file emitted ===" +DEEP_FILES=( eval/results/*-deep.yaml ) +if [[ -f "${DEEP_FILES[0]}" ]]; then + echo " Found: ${DEEP_FILES[0]}" + assert_true "[[ -s '${DEEP_FILES[0]}' ]]" "EV-01: deep yaml is non-empty" +else + echo " FAIL: EV-01: no eval/results/*-deep.yaml produced" + _FAIL=$((_FAIL + 1)) + _TOTAL=$((_TOTAL + 1)) +fi +echo "" + +# 3. Metrics file (eval/results/{date}.yaml) emitted +echo "=== EV-02: Metrics result file emitted ===" +METRIC_FILES=( eval/results/*.yaml ) +HAS_METRIC=0 +for f in "${METRIC_FILES[@]}"; do + case "$f" in + *-deep.yaml) ;; + *) [[ -f "$f" ]] && HAS_METRIC=1 ;; + esac +done +if (( HAS_METRIC == 1 )); then + _PASS=$((_PASS + 1)) + _TOTAL=$((_TOTAL + 1)) + echo " PASS: EV-02: metrics yaml present" +else + echo " INFO: EV-02: no plain metrics yaml — acceptable when Deep Path only" +fi +echo "" + +# --- Summary --- +echo "" +echo "===========================================" +echo " Test Summary" +echo "===========================================" +echo " Vault: $VAULT_DIR" +echo " Results: $RESULTS_DIR" +echo " NOTE: Smoke test with 1-query fixture. Full 20-query benchmark is out of scope" +echo "" + +report_results "$VAULT_DIR" "$RESULTS_DIR" diff --git a/test/skills/test-focus.sh b/test/skills/test-focus.sh index 2aea6a1..a75b791 100755 --- a/test/skills/test-focus.sh +++ b/test/skills/test-focus.sh @@ -43,8 +43,8 @@ if [[ -z "$VAULT_DIR" ]]; then cp -r "$REPO_DIR/.claude" "$VAULT_DIR/.claude" cp -r "$REPO_DIR/bin" "$VAULT_DIR/bin" [[ -d "$REPO_DIR/plugins" ]] && cp -r "$REPO_DIR/plugins" "$VAULT_DIR/plugins" - cp "$REPO_DIR/taxonomy.md" "$VAULT_DIR/taxonomy.md" - cp "$REPO_DIR/CLAUDE.md" "$VAULT_DIR/CLAUDE.md" + [[ -f "$REPO_DIR/taxonomy.md" ]] && cp "$REPO_DIR/taxonomy.md" "$VAULT_DIR/taxonomy.md" + [[ -f "$REPO_DIR/CLAUDE.md" ]] && cp "$REPO_DIR/CLAUDE.md" "$VAULT_DIR/CLAUDE.md" [[ -f "$REPO_DIR/SPEC.md" ]] && cp "$REPO_DIR/SPEC.md" "$VAULT_DIR/SPEC.md" cd "$VAULT_DIR" git init -q diff --git a/test/skills/test-inspect.sh b/test/skills/test-inspect.sh new file mode 100644 index 0000000..7e0b2de --- /dev/null +++ b/test/skills/test-inspect.sh @@ -0,0 +1,141 @@ +#!/bin/bash +# test/skills/test-inspect.sh — /inspect integration test +# +# Usage: bash test/skills/test-inspect.sh [--skip-execute] [--vault=PATH] +# +# Verifies that /inspect detects taxonomy / metadata issues and appends +# affected files to knowledge/.refresh-queue. /inspect is read-only on file +# bodies; it only updates .refresh-queue and emits a diagnostic report. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +TEST_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +ASSERTIONS_DIR="$TEST_DIR/assertions" +FIXTURES_DIR="$TEST_DIR/fixtures" +REPO_REAL_DIR="$(cd "$TEST_DIR/.." && pwd)" + +source "$ASSERTIONS_DIR/lib.sh" + +SKIP_EXECUTE=false +VAULT_DIR="" +for arg in "$@"; do + case "$arg" in + --skip-execute) SKIP_EXECUTE=true ;; + --vault=*) VAULT_DIR="${arg#--vault=}" ;; + esac +done + +# --- Setup --- +if [[ -z "$VAULT_DIR" ]]; then + VAULT_DIR=$(mktemp -d -t rill-test-XXXXXX) + echo "=== Setting up test vault: $VAULT_DIR ===" + cp -r "$FIXTURES_DIR"/* "$VAULT_DIR/" + REPO_DIR="$(cd "$TEST_DIR/.." && pwd)" + cp -r "$REPO_DIR/.claude" "$VAULT_DIR/.claude" + cp -r "$REPO_DIR/bin" "$VAULT_DIR/bin" + [[ -d "$REPO_DIR/plugins" ]] && cp -r "$REPO_DIR/plugins" "$VAULT_DIR/plugins" + [[ -f "$REPO_DIR/taxonomy.md" ]] && cp "$REPO_DIR/taxonomy.md" "$VAULT_DIR/taxonomy.md" + [[ -f "$REPO_DIR/CLAUDE.md" ]] && cp "$REPO_DIR/CLAUDE.md" "$VAULT_DIR/CLAUDE.md" + [[ -f "$REPO_DIR/SPEC.md" ]] && cp "$REPO_DIR/SPEC.md" "$VAULT_DIR/SPEC.md" + cd "$VAULT_DIR" + git init -q + git add -A + git commit -q -m "initial test fixtures" +else + echo "=== Using existing vault: $VAULT_DIR ===" + cd "$VAULT_DIR" +fi + +TIMESTAMP=$(date +%Y%m%d-%H%M%S) +RESULTS_DIR="$TEST_DIR/results/$TIMESTAMP" +mkdir -p "$RESULTS_DIR" + +# --- Snapshot inbox + knowledge hashes for mutation check (INV-01) --- +HASH_FILE="$RESULTS_DIR/inbox-hashes.txt" +{ + find inbox/ -type f -name "*.md" 2>/dev/null + find knowledge/notes -type f -name "*.md" 2>/dev/null +} | sort | while read -r f; do + echo "$(file_hash "$f") $f" +done > "$HASH_FILE" + +# --- Execute /inspect --- +if ! $SKIP_EXECUTE; then + echo "" + echo "=== Executing /inspect ===" + echo " (this may take 1-3 minutes)" + echo "" + + isolate_test_vault "$VAULT_DIR" + + claude -p "/inspect" \ + --output-format text \ + --max-turns 80 \ + 2>&1 | tee "$RESULTS_DIR/inspect-output.log" + + check_real_repo_contamination "$REPO_REAL_DIR" "$VAULT_DIR" + + echo "" + echo "=== /inspect execution complete ===" +fi + +# --- Layer 1 Assertions --- +echo "" +echo "===========================================" +echo " Layer 1: Structural Validation" +echo "===========================================" +echo "" + +# 1. Inbox + knowledge/notes immutability (INV-01) +echo "=== INV-01: Inbox + knowledge/notes immutability (/inspect must not edit bodies) ===" +bash "$ASSERTIONS_DIR/check-no-mutation.sh" "$HASH_FILE" || true +echo "" + +# 2. .refresh-queue: file created and contains paths +echo "=== RQ-01: .refresh-queue exists or inspect reported clean ===" +REFRESH_QUEUE="knowledge/.refresh-queue" +if [[ -f "$REFRESH_QUEUE" ]]; then + QUEUE_LINES=$({ wc -l < "$REFRESH_QUEUE" 2>/dev/null || echo 0; } | tr -d '[:space:]') + echo " .refresh-queue exists with $QUEUE_LINES line(s)" + if (( QUEUE_LINES > 0 )); then + # Validate each queued path exists and is under knowledge/notes/ + while read -r path; do + [[ -z "$path" ]] && continue + if [[ "$path" =~ ^knowledge/notes/.+\.md$ ]]; then + assert_true "[[ -f '$path' ]]" "RQ-02: queued path exists: $path" + else + echo " FAIL: RQ-02: queued path not under knowledge/notes/: $path" + _FAIL=$((_FAIL + 1)) + _TOTAL=$((_TOTAL + 1)) + fi + done < "$REFRESH_QUEUE" + fi +else + echo " INFO: .refresh-queue not produced. Acceptable when inspect found nothing to repair" +fi +echo "" + +# 3. Diagnostic report content checks (PH: phases echoed in output) +echo "=== PH: Phase markers in inspect output ===" +OUTPUT_LOG="$RESULTS_DIR/inspect-output.log" +if [[ -f "$OUTPUT_LOG" ]]; then + HAS_TAXONOMY=$({ grep -ci 'taxonomy\|Topic Tags\|Deprecated Tags' "$OUTPUT_LOG" 2>/dev/null || echo "0"; } | tr -d '[:space:]') + assert_gt "$HAS_TAXONOMY" 0 "PH-01: output mentions taxonomy" + + HAS_METADATA=$({ grep -ci 'metadata\|sampling\|frontmatter' "$OUTPUT_LOG" 2>/dev/null || echo "0"; } | tr -d '[:space:]') + assert_gt "$HAS_METADATA" 0 "PH-02: output mentions metadata/sampling" +fi +echo "" + +# --- Summary --- +echo "" +echo "===========================================" +echo " Test Summary" +echo "===========================================" +echo " Vault: $VAULT_DIR" +echo " Results: $RESULTS_DIR" +[[ -f "$REFRESH_QUEUE" ]] && echo " Queue file: $REFRESH_QUEUE ($QUEUE_LINES line(s))" +echo "" + +report_results "$VAULT_DIR" "$RESULTS_DIR" diff --git a/test/skills/test-newsletter.sh b/test/skills/test-newsletter.sh index 8474df7..a2cffc9 100755 --- a/test/skills/test-newsletter.sh +++ b/test/skills/test-newsletter.sh @@ -42,8 +42,8 @@ if [[ -z "$VAULT_DIR" ]]; then cp -r "$REPO_DIR/.claude" "$VAULT_DIR/.claude" cp -r "$REPO_DIR/bin" "$VAULT_DIR/bin" [[ -d "$REPO_DIR/plugins" ]] && cp -r "$REPO_DIR/plugins" "$VAULT_DIR/plugins" - cp "$REPO_DIR/taxonomy.md" "$VAULT_DIR/taxonomy.md" - cp "$REPO_DIR/CLAUDE.md" "$VAULT_DIR/CLAUDE.md" + [[ -f "$REPO_DIR/taxonomy.md" ]] && cp "$REPO_DIR/taxonomy.md" "$VAULT_DIR/taxonomy.md" + [[ -f "$REPO_DIR/CLAUDE.md" ]] && cp "$REPO_DIR/CLAUDE.md" "$VAULT_DIR/CLAUDE.md" [[ -f "$REPO_DIR/SPEC.md" ]] && cp "$REPO_DIR/SPEC.md" "$VAULT_DIR/SPEC.md" # Ensure newsletter output directory exists mkdir -p "$VAULT_DIR/reports/newsletter" diff --git a/test/skills/test-page.sh b/test/skills/test-page.sh index f4c3f13..e2b8a2f 100755 --- a/test/skills/test-page.sh +++ b/test/skills/test-page.sh @@ -36,8 +36,8 @@ if [[ -z "$VAULT_DIR" ]]; then cp -r "$REPO_DIR/.claude" "$VAULT_DIR/.claude" cp -r "$REPO_DIR/bin" "$VAULT_DIR/bin" [[ -d "$REPO_DIR/plugins" ]] && cp -r "$REPO_DIR/plugins" "$VAULT_DIR/plugins" - cp "$REPO_DIR/taxonomy.md" "$VAULT_DIR/taxonomy.md" - cp "$REPO_DIR/CLAUDE.md" "$VAULT_DIR/CLAUDE.md" + [[ -f "$REPO_DIR/taxonomy.md" ]] && cp "$REPO_DIR/taxonomy.md" "$VAULT_DIR/taxonomy.md" + [[ -f "$REPO_DIR/CLAUDE.md" ]] && cp "$REPO_DIR/CLAUDE.md" "$VAULT_DIR/CLAUDE.md" [[ -f "$REPO_DIR/SPEC.md" ]] && cp "$REPO_DIR/SPEC.md" "$VAULT_DIR/SPEC.md" mkdir -p "$VAULT_DIR/pages" cd "$VAULT_DIR" diff --git a/test/skills/test-repair.sh b/test/skills/test-repair.sh new file mode 100644 index 0000000..140004b --- /dev/null +++ b/test/skills/test-repair.sh @@ -0,0 +1,120 @@ +#!/bin/bash +# test/skills/test-repair.sh — /repair integration test (smoke) +# +# Usage: bash test/skills/test-repair.sh [--skip-execute] [--vault=PATH] +# +# Smoke test: runs /repair against an empty .refresh-queue and verifies the +# skill exits cleanly with the "Queue is empty" path. A richer test that +# pre-populates the queue and verifies frontmatter repairs requires additional +# fixture preparation and is left as future work. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +TEST_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +ASSERTIONS_DIR="$TEST_DIR/assertions" +FIXTURES_DIR="$TEST_DIR/fixtures" +REPO_REAL_DIR="$(cd "$TEST_DIR/.." && pwd)" + +source "$ASSERTIONS_DIR/lib.sh" + +SKIP_EXECUTE=false +VAULT_DIR="" +for arg in "$@"; do + case "$arg" in + --skip-execute) SKIP_EXECUTE=true ;; + --vault=*) VAULT_DIR="${arg#--vault=}" ;; + esac +done + +# --- Setup --- +if [[ -z "$VAULT_DIR" ]]; then + VAULT_DIR=$(mktemp -d -t rill-test-XXXXXX) + echo "=== Setting up test vault: $VAULT_DIR ===" + cp -r "$FIXTURES_DIR"/* "$VAULT_DIR/" + REPO_DIR="$(cd "$TEST_DIR/.." && pwd)" + cp -r "$REPO_DIR/.claude" "$VAULT_DIR/.claude" + cp -r "$REPO_DIR/bin" "$VAULT_DIR/bin" + [[ -d "$REPO_DIR/plugins" ]] && cp -r "$REPO_DIR/plugins" "$VAULT_DIR/plugins" + [[ -f "$REPO_DIR/taxonomy.md" ]] && cp "$REPO_DIR/taxonomy.md" "$VAULT_DIR/taxonomy.md" + [[ -f "$REPO_DIR/CLAUDE.md" ]] && cp "$REPO_DIR/CLAUDE.md" "$VAULT_DIR/CLAUDE.md" + [[ -f "$REPO_DIR/SPEC.md" ]] && cp "$REPO_DIR/SPEC.md" "$VAULT_DIR/SPEC.md" + cd "$VAULT_DIR" + + # Ensure an empty .refresh-queue exists so /repair has a defined input state + mkdir -p knowledge + : > knowledge/.refresh-queue + + git init -q + git add -A + git commit -q -m "initial test fixtures (empty refresh-queue)" +else + echo "=== Using existing vault: $VAULT_DIR ===" + cd "$VAULT_DIR" +fi + +TIMESTAMP=$(date +%Y%m%d-%H%M%S) +RESULTS_DIR="$TEST_DIR/results/$TIMESTAMP" +mkdir -p "$RESULTS_DIR" + +# --- Snapshot inbox + knowledge/notes hashes (INV-01 immutability check) --- +HASH_FILE="$RESULTS_DIR/inbox-hashes.txt" +{ + find inbox/ -type f -name "*.md" 2>/dev/null + find knowledge/notes -type f -name "*.md" 2>/dev/null +} | sort | while read -r f; do + echo "$(file_hash "$f") $f" +done > "$HASH_FILE" + +# --- Execute /repair --- +if ! $SKIP_EXECUTE; then + echo "" + echo "=== Executing /repair (empty queue smoke) ===" + echo " (this may take 1-2 minutes)" + echo "" + + isolate_test_vault "$VAULT_DIR" + + claude -p "/repair" \ + --output-format text \ + --max-turns 40 \ + 2>&1 | tee "$RESULTS_DIR/repair-output.log" + + check_real_repo_contamination "$REPO_REAL_DIR" "$VAULT_DIR" + + echo "" + echo "=== /repair execution complete ===" +fi + +# --- Layer 1 Assertions --- +echo "" +echo "===========================================" +echo " Layer 1: Structural Validation" +echo "===========================================" +echo "" + +# 1. Inbox + knowledge/notes immutability for empty-queue run +echo "=== INV-01: Inbox + knowledge/notes immutable on empty queue ===" +bash "$ASSERTIONS_DIR/check-no-mutation.sh" "$HASH_FILE" || true +echo "" + +# 2. Empty queue path emits the documented message +echo "=== EQ: Empty-queue exit message ===" +OUTPUT_LOG="$RESULTS_DIR/repair-output.log" +if [[ -f "$OUTPUT_LOG" ]]; then + EMPTY_HIT=$({ grep -ci 'queue is empty\|empty.*queue\|no entries' "$OUTPUT_LOG" 2>/dev/null || echo "0"; } | tr -d '[:space:]') + assert_gt "$EMPTY_HIT" 0 "EQ-01: output mentions empty queue / no entries" +fi +echo "" + +# --- Summary --- +echo "" +echo "===========================================" +echo " Test Summary" +echo "===========================================" +echo " Vault: $VAULT_DIR" +echo " Results: $RESULTS_DIR" +echo " NOTE: This is a smoke test. Populated-queue repair behavior is not asserted here" +echo "" + +report_results "$VAULT_DIR" "$RESULTS_DIR" diff --git a/test/skills/test-solve.sh b/test/skills/test-solve.sh index 208cad9..f0c5c1b 100755 --- a/test/skills/test-solve.sh +++ b/test/skills/test-solve.sh @@ -38,8 +38,8 @@ if [[ -z "$VAULT_DIR" ]]; then cp -r "$REPO_DIR/.claude" "$VAULT_DIR/.claude" cp -r "$REPO_DIR/bin" "$VAULT_DIR/bin" [[ -d "$REPO_DIR/plugins" ]] && cp -r "$REPO_DIR/plugins" "$VAULT_DIR/plugins" - cp "$REPO_DIR/taxonomy.md" "$VAULT_DIR/taxonomy.md" - cp "$REPO_DIR/CLAUDE.md" "$VAULT_DIR/CLAUDE.md" + [[ -f "$REPO_DIR/taxonomy.md" ]] && cp "$REPO_DIR/taxonomy.md" "$VAULT_DIR/taxonomy.md" + [[ -f "$REPO_DIR/CLAUDE.md" ]] && cp "$REPO_DIR/CLAUDE.md" "$VAULT_DIR/CLAUDE.md" [[ -f "$REPO_DIR/SPEC.md" ]] && cp "$REPO_DIR/SPEC.md" "$VAULT_DIR/SPEC.md" cd "$VAULT_DIR" git init -q @@ -75,7 +75,7 @@ if ! $SKIP_EXECUTE; then claude -p "/solve $TASK_FILE" \ --output-format text \ - --max-turns 50 \ + --max-turns 200 \ 2>&1 | tee "$RESULTS_DIR/solve-output.log" echo "" @@ -110,16 +110,21 @@ echo "" echo "=== P4-01: Task status ===" TASK_STATUS_AFTER=$(fm_get "$TASK_FILE" "status") echo " Task status: $TASK_STATUS_BEFORE -> $TASK_STATUS_AFTER" -# /solve should change status to 'waiting' (research complete, awaiting review) -if [[ "$TASK_STATUS_AFTER" == "waiting" ]]; then - assert_true "true" "P4-01: Task status changed to 'waiting'" -elif [[ "$TASK_STATUS_AFTER" == "open" ]]; then - # Also acceptable if /solve determined human action needed - echo " INFO: Task remains 'open' (may require human action)" - assert_true "true" "P4-01: Task status is 'open' (human action needed)" -else - assert_true "false" "P4-01: Task status is 'waiting' or 'open' (got '$TASK_STATUS_AFTER')" -fi +# /solve may end in 'waiting' (research complete, awaiting review), +# 'open' (human action needed, breakpoint reached), or +# 'done' (Plan completion criteria met within the run). +case "$TASK_STATUS_AFTER" in + waiting) + assert_true "true" "P4-01: Task status changed to 'waiting' (research complete)" ;; + open) + echo " INFO: Task remains 'open' (breakpoint or human action needed)" + assert_true "true" "P4-01: Task status is 'open' (breakpoint reached)" ;; + done) + echo " INFO: Task transitioned to 'done' (Plan completion criteria met)" + assert_true "true" "P4-01: Task status is 'done' (autonomous completion)" ;; + *) + assert_true "false" "P4-01: Task status in {waiting, open, done} (got '$TASK_STATUS_AFTER')" ;; +esac echo "" # 3. Workspace creation (P3-01, P3-02, P3-03)