📝 Add adept discovery design pipeline artifacts and harness investigation goal
Design, plan, fact-check report, and athanor implementation report for the adept discovery and tiered knowledge folders feature. Also captures the harness behavior investigation as an open goal.
This commit is contained in:
@@ -0,0 +1,376 @@
|
|||||||
|
# Design: Adept discovery and tiered knowledge folders
|
||||||
|
|
||||||
|
**Date**: 2026-09-18
|
||||||
|
**Touches**: `plugins/spire/agents/adept.md`, `plugins/spire/agents/igor.md`, `plugins/spire/skills/igor/SKILL.md`, `plugins/spire/skills/igor/references/journal-template.md`, `plugins/spire/skills/init/SKILL.md`, `plugins/spire/skills/workpad/SKILL.md`, new skill `plugins/spire/skills/adepts/`
|
||||||
|
**Builds on**: `designs/spire-project-override-and-init.md` (which shipped the `_template` scaffolding this design replaces)
|
||||||
|
|
||||||
|
## The problem
|
||||||
|
|
||||||
|
Three gaps, all downstream of the same fact — the adept folder is opaque from outside:
|
||||||
|
|
||||||
|
1. **No discovery.** Igor is told to "check the workpad's `adepts/` folder before spawning." In practice that means `ls`, then reading each `CLAUDE.md` to learn what a folder covers and whether anyone has been there lately. That is exactly the kind of inline work Igor is not supposed to do, and it scales linearly with the number of domains. Nothing records which domains were recently active.
|
||||||
|
2. **Monolithic CLAUDE.md.** The current template puts scope, key files, conventions, and open questions in one file. Every adept appends to it; every successor loads all of it. Because the harness auto-loads a directory's `CLAUDE.md` when files beneath it are read, a fat one is paid for repeatedly. There is no tiering — a successor cannot tell what it must read from what it may read.
|
||||||
|
3. **Weak handoff.** A successor "reads the CLAUDE.md and journals." Journals are append-only and grow without bound; the successor has no cheap way to know what the predecessor's last state was, what is in flight on a branch, or whether the code has moved since the predecessor last verified anything.
|
||||||
|
|
||||||
|
The design principle for all three: **adepts write knowledge; tooling derives metadata.** An adept should never maintain an index, a counter, or a timestamp by hand. Everything the roster needs is either declared once in frontmatter or derived from files that already exist (journal filenames, git).
|
||||||
|
|
||||||
|
## Design
|
||||||
|
|
||||||
|
### A. Domain folder layout
|
||||||
|
|
||||||
|
```
|
||||||
|
<workpad>/adepts/
|
||||||
|
CLAUDE.md # directory conventions (init-scaffolded; rewritten by this design)
|
||||||
|
_template/ # init-scaffolded starter; copied on founding
|
||||||
|
CLAUDE.md
|
||||||
|
handoff.md
|
||||||
|
<domain-slug>/
|
||||||
|
CLAUDE.md # INDEX — frontmatter + tiered reading list. Short. Auto-loaded.
|
||||||
|
handoff.md # rolling state-of-the-domain for the successor. Overwritten, not appended.
|
||||||
|
key-files.md # solid knowledge docs — as many as the domain needs
|
||||||
|
conventions.md
|
||||||
|
architecture.md
|
||||||
|
gotchas.md
|
||||||
|
... # any other doc, provided it is listed in CLAUDE.md
|
||||||
|
journal/
|
||||||
|
YYYY-MM-DD-HH-genesis.md
|
||||||
|
YYYY-MM-DD-HH-<topic>.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Changes from today:
|
||||||
|
|
||||||
|
- Journals move into `journal/` and drop the `journal-` infix (the folder says it). The domain root then reads as a knowledge base: index, handoff, docs. `ls` alone tells a successor what exists.
|
||||||
|
- `handoff.md` is new and is the one file that is *overwritten* rather than appended.
|
||||||
|
- Knowledge docs are plural. The names above are a suggested vocabulary, not a fixed schema — but **any file in the domain root must appear in `CLAUDE.md`'s reading list.** If it is not in the index, it does not exist.
|
||||||
|
|
||||||
|
Where the two knowledge tiers from `adept.md` land:
|
||||||
|
|
||||||
|
| Content | Lives in | Update rule |
|
||||||
|
|---|---|---|
|
||||||
|
| Solid knowledge (verified on main) | `key-files.md`, `conventions.md`, `architecture.md`, `gotchas.md`, … | Correct on contact; dated refinement sections when disputing a predecessor; curator compresses |
|
||||||
|
| Experiences (branch work, observations, dead ends) | `journal/` | Append-only, one file per topic or session |
|
||||||
|
| Current state, open threads, in-flight branch notes | `handoff.md` | Overwritten by the living adept; written early, refreshed often |
|
||||||
|
| Scope, boundaries, what to read when | `CLAUDE.md` | Rarely changes; edited when docs are added or the domain splits |
|
||||||
|
|
||||||
|
### B. `CLAUDE.md` as index
|
||||||
|
|
||||||
|
Frontmatter carries the only hand-declared metadata. Everything temporal is derived (see D).
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
domain: Authentication
|
||||||
|
summary: Login flow, session tokens, the auth middleware chain, and the permission model.
|
||||||
|
covers:
|
||||||
|
- src/auth/
|
||||||
|
- src/middleware/auth*
|
||||||
|
- tests/auth/
|
||||||
|
related: [api-gateway, user-profile]
|
||||||
|
status: active # active | split | merged
|
||||||
|
---
|
||||||
|
# Authentication
|
||||||
|
|
||||||
|
Everything between "a request arrives with credentials" and "a request carries a
|
||||||
|
verified principal." Permission *checks* inside feature code are not this domain —
|
||||||
|
that is whoever owns the feature. Token *issuance* for service accounts is
|
||||||
|
api-gateway's.
|
||||||
|
|
||||||
|
## Must-read
|
||||||
|
|
||||||
|
Every genesis, before answering anything. Keep this list to three or four entries.
|
||||||
|
|
||||||
|
- `handoff.md` — where the previous adept left things. Always first.
|
||||||
|
- `key-files.md` — the files that matter, what each does, where the seams are.
|
||||||
|
- `conventions.md` — rules here that differ from the project-wide ones.
|
||||||
|
|
||||||
|
## Good-to-read-if
|
||||||
|
|
||||||
|
- `architecture.md` — if asked how the pieces fit, or where a new thing should go.
|
||||||
|
- `gotchas.md` — if asked why something behaves oddly, or before recommending any change to the middleware order.
|
||||||
|
- `journal/` — if `handoff.md` points at a specific entry, or a question touches a dead end noted there. Do not read the whole journal.
|
||||||
|
|
||||||
|
## Optional
|
||||||
|
|
||||||
|
- `history.md` — how the domain got this way. Read for "why" questions only.
|
||||||
|
- `../api-gateway/CLAUDE.md` — read only when a question crosses into token issuance.
|
||||||
|
|
||||||
|
## Open questions
|
||||||
|
|
||||||
|
Carried forward by predecessors. Try to resolve one before adding one.
|
||||||
|
|
||||||
|
- Why does `AdminService` bypass the standard middleware? (since 2026-08-30)
|
||||||
|
```
|
||||||
|
|
||||||
|
Rules the adept agent enforces:
|
||||||
|
|
||||||
|
- **Length.** `CLAUDE.md` stays under ~60 lines. It is auto-loaded by the harness whenever an adept reads any file in the folder, so every line costs repeatedly. If it grows, something in it belongs in a doc.
|
||||||
|
- **Tiers are conditional, not ranked.** "Good-to-read-if" entries state the *condition*. A successor answering a "why is this weird" question reads `gotchas.md`; one answering "where do I add X" reads `architecture.md`. Neither reads both at boot.
|
||||||
|
- **Docs split at ~200 lines.** When a doc passes that, the adept splits it and updates the index in the same edit.
|
||||||
|
- **Siblings are introspected, not preloaded.** `related:` names sibling slugs. The adept reads a sibling's `CLAUDE.md` (index only) when a consultation crosses a boundary, and may `grep` across `../*/journal/` when hunting a dead end. Nothing sibling-shaped is read at genesis.
|
||||||
|
|
||||||
|
### C. `handoff.md` — the successor's first read
|
||||||
|
|
||||||
|
Rolling, overwritten, short (≤ ~40 lines). Written *early* in an adept's life and refreshed after every significant consultation — sessions end abruptly; an adept that writes its handoff "at the end" often never does.
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
updated: 2026-09-16-14
|
||||||
|
session: journals/2026-09-16-14-auth-refactor.md
|
||||||
|
branch: main
|
||||||
|
verified-at: 4886539 # HEAD when the solid docs were last checked against code
|
||||||
|
---
|
||||||
|
# Handoff — Authentication
|
||||||
|
|
||||||
|
## State
|
||||||
|
Middleware chain is documented and verified. Permission model doc is complete
|
||||||
|
except for the admin bypass (open question). Nothing in flight on main.
|
||||||
|
|
||||||
|
## In flight (experiences, not solid)
|
||||||
|
- Branch `feat/oauth-pkce` adds a third token type. Observed, journaled in
|
||||||
|
`journal/2026-09-16-14-pkce-branch.md`, NOT in key-files.md until it merges.
|
||||||
|
|
||||||
|
## Open threads
|
||||||
|
- Admin bypass still unexplained. Explore agent found no comment, no commit message. Next: `git log -S AdminService --follow`.
|
||||||
|
- User asked whether refresh-token rotation is enforced; answer pending a test run.
|
||||||
|
|
||||||
|
## Successor: read first
|
||||||
|
1. This file.
|
||||||
|
2. `key-files.md` §Middleware chain — most likely to have drifted.
|
||||||
|
3. `journal/2026-09-16-14-pkce-branch.md` if the OAuth branch is still open.
|
||||||
|
|
||||||
|
## Drift check
|
||||||
|
git log --oneline 4886539..HEAD -- src/auth/ src/middleware/auth* tests/auth/
|
||||||
|
```
|
||||||
|
|
||||||
|
`verified-at` is the mechanism that makes continuity cheap. The successor runs the drift check (paths come from `covers:`) and knows in one command whether any solid knowledge could be stale — and which files to re-verify before trusting `key-files.md`. Without it the successor either trusts blindly or re-reads the domain.
|
||||||
|
|
||||||
|
### D. The roster — discovery without reading
|
||||||
|
|
||||||
|
A new skill, `spire:adepts`, shell-injects a derived roster at load time — same mechanism `spire:project` uses.
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
name: adepts
|
||||||
|
description: Lists the adept domains in the workpad — what each covers, when it was last active, its status. Loaded by Igor at boot; any agent may invoke it for a fresh listing.
|
||||||
|
allowed-tools: Bash(bash:*)
|
||||||
|
---
|
||||||
|
|
||||||
|
!`bash "${CLAUDE_SKILL_DIR}/roster.sh"`
|
||||||
|
|
||||||
|
<!-- Empty roster: no adepts/ folder yet, or /spire:init has not been run. -->
|
||||||
|
```
|
||||||
|
|
||||||
|
`roster.sh` (sketch — the planner decides the exact implementation):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Resolve workpad the same way spire:workpad does, minus the "ask" step.
|
||||||
|
WP="${WORKPAD_FOLDER:-$(just workpad-folder 2>/dev/null)}"
|
||||||
|
[ -z "$WP" ] && { [ -d docs/workpad ] && WP=docs/workpad || WP=.workpad; }
|
||||||
|
[ -d "$WP/adepts" ] || { echo "## Adept roster — none (no $WP/adepts/)"; exit 0; }
|
||||||
|
|
||||||
|
echo "## Adept roster — $WP/adepts/ ($(date +%F))"
|
||||||
|
for dir in "$WP"/adepts/*/; do
|
||||||
|
slug=$(basename "$dir"); [ "$slug" = "_template" ] && continue
|
||||||
|
fm=$(sed -n '2,/^---$/p' "$dir/CLAUDE.md" 2>/dev/null)
|
||||||
|
summary=$(echo "$fm" | sed -n 's/^summary: *//p')
|
||||||
|
status=$(echo "$fm" | sed -n 's/^status: *//p')
|
||||||
|
related=$(echo "$fm" | sed -n 's/^related: *//p')
|
||||||
|
# Recency and session count derive from journal filenames — nothing to maintain.
|
||||||
|
newest=$(ls "$dir"/journal/ "$dir" 2>/dev/null | grep -E '^[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}' | sort | tail -1 | cut -c1-13)
|
||||||
|
sessions=$(ls "$dir"/journal/ "$dir" 2>/dev/null | grep -Eo '^[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}' | sort -u | wc -l)
|
||||||
|
if [ -z "$summary" ]; then
|
||||||
|
echo "- **$slug** — (unmigrated: no frontmatter) · last active ${newest:-never}"
|
||||||
|
else
|
||||||
|
echo "- **$slug** — $summary · last active ${newest:-never} · $sessions session(s) · ${status:-active}${related:+ · related: $related}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
Output Igor sees at boot:
|
||||||
|
|
||||||
|
```
|
||||||
|
## Adept roster — docs/workpad/adepts/ (2026-09-18)
|
||||||
|
- **auth** — Login flow, session tokens, the auth middleware chain, and the permission model. · last active 2026-09-16-14 · 4 session(s) · active · related: [api-gateway, user-profile]
|
||||||
|
- **billing** — Stripe integration and the invoice pipeline. · last active 2026-08-02-09 · 1 session(s) · active
|
||||||
|
- **legacy-importer** — (unmigrated: no frontmatter) · last active 2026-07-02-11
|
||||||
|
```
|
||||||
|
|
||||||
|
Properties:
|
||||||
|
|
||||||
|
- **Never stale.** There is no index file to forget to update. Frontmatter is declared once; recency and session counts fall out of the journal naming convention that already exists.
|
||||||
|
- **No shared mutable file.** Two adepts in parallel sessions each touch only their own folder.
|
||||||
|
- **Tolerates legacy.** A folder without frontmatter still appears, flagged.
|
||||||
|
- **Cheap.** One line per domain. Twenty domains is twenty lines. If a project ever has enough domains for this to matter, the roster can gain a `--recent N` mode; not needed now.
|
||||||
|
- **Available on demand.** Adepts (and athanor) can `Skill("spire:adepts")` when a question crosses a boundary, instead of `ls`-ing and reading.
|
||||||
|
|
||||||
|
Fallback when skill shell execution is disabled: the skill body's comment tells Igor to dispatch a Haiku subagent with the one-liner `bash <plugin>/skills/adepts/roster.sh`. Igor never reads the folders itself.
|
||||||
|
|
||||||
|
Dormancy is *derived*, not declared: Igor reads "last active" and judges. `status:` exists only for the states an adept must declare because tooling cannot infer them — `split` (with successor slugs in `related:`) and `merged` (target in `related:`). A split or merged folder stays on disk; its `CLAUDE.md` shrinks to a pointer.
|
||||||
|
|
||||||
|
### E. Flows
|
||||||
|
|
||||||
|
#### Igor boot
|
||||||
|
|
||||||
|
1. `spire:project` loads `project.md` (unchanged).
|
||||||
|
2. `spire:adepts` injects the roster (new — `agents/igor.md` adds it to `skills:`).
|
||||||
|
3. Igor creates the session journal. The template gains an `## Adepts` section; Igor seeds it with the roster rows relevant to the topic.
|
||||||
|
4. Greet, ask what the user wants (unchanged).
|
||||||
|
5. If the topic clearly matches a roster entry, Igor names it in the greeting — "there's an auth adept, last active two days ago; I'll wake it once I know what we're doing" — and spawns it as soon as the direction is clear. One flourish, then the dispatch.
|
||||||
|
|
||||||
|
#### Spawning — founding vs. successor
|
||||||
|
|
||||||
|
The brief changes shape depending on whether the slug is in the roster:
|
||||||
|
|
||||||
|
```
|
||||||
|
# Successor (slug in roster)
|
||||||
|
Agent(subagent_type: "adept", name: "auth-adept", model: "opus",
|
||||||
|
prompt: "Your domain is Authentication. Your folder is docs/workpad/adepts/auth/ — you are a
|
||||||
|
successor; it has a handoff.md. We are on branch main. Session journal:
|
||||||
|
docs/workpad/journals/2026-09-18-10-oauth-pkce.md.
|
||||||
|
Orient per your genesis protocol and be ready for consultation.")
|
||||||
|
|
||||||
|
# Founding (slug not in roster)
|
||||||
|
Agent(subagent_type: "adept", name: "billing-adept", model: "opus",
|
||||||
|
prompt: "Your domain is Billing (Stripe integration, invoices). Your folder is
|
||||||
|
docs/workpad/adepts/billing/ — it does not exist; you are the founding adept.
|
||||||
|
We are on branch main. Session journal: <path>.
|
||||||
|
Orient and be ready for consultation.")
|
||||||
|
```
|
||||||
|
|
||||||
|
Igor picks the slug from the roster when one fits *strictly* (the gravity-well rule from `adept.md` §4 stays; the roster just makes it possible to apply without reading folders). The session journal path goes in the brief so the adept's journal can link back.
|
||||||
|
|
||||||
|
#### Adept genesis — successor
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Read adepts/CLAUDE.md (conventions, short)
|
||||||
|
2. Read <slug>/CLAUDE.md (index — auto-loaded anyway)
|
||||||
|
3. Read <slug>/handoff.md
|
||||||
|
4. Run the drift check from handoff.md (git log <verified-at>..HEAD -- <covers>)
|
||||||
|
5. Read the Must-read docs
|
||||||
|
6. Read only the journal entries handoff.md names (usually one or two)
|
||||||
|
7. If step 4 showed changes under a key path: verify those claims before anything else;
|
||||||
|
correct on contact; note corrections in the genesis journal entry
|
||||||
|
8. Write journal/<now>-genesis.md — short for a successor: drift found, corrections made,
|
||||||
|
orientation trace. Link the session journal.
|
||||||
|
9. Rewrite handoff.md: bump `updated`, `session`, `verified-at` (if step 7 verified), note "successor active"
|
||||||
|
```
|
||||||
|
|
||||||
|
Steps 1–6 are bounded: two short files, one short handoff, three or four docs, one or two journal entries. A successor never reads the whole journal.
|
||||||
|
|
||||||
|
#### Adept genesis — founding
|
||||||
|
|
||||||
|
As today (`adept.md` §Genesis), plus: copy `_template/CLAUDE.md` *and* `_template/handoff.md`; fill frontmatter (`domain`, `summary`, `covers`, `related`) during orientation; create the docs the orientation produces — at minimum `key-files.md`; write `handoff.md` *before* the first consultation, with `verified-at` = current HEAD. The genesis journal entry keeps its orientation-trace duty.
|
||||||
|
|
||||||
|
#### During consultation
|
||||||
|
|
||||||
|
Unchanged in spirit. Additions:
|
||||||
|
|
||||||
|
- After a consultation that changes state (new open thread, a branch observation, a resolved question), refresh `handoff.md` in the same turn. It costs a few lines; it is the file that survives an abrupt end.
|
||||||
|
- When a question crosses the domain boundary: `Skill("spire:adepts")` for the roster, read the sibling's `CLAUDE.md` index, and — if warranted — tell Igor "this is api-gateway's; consider waking that adept." Do not absorb the sibling's domain.
|
||||||
|
- When adding a doc: add it to the index in the same edit.
|
||||||
|
|
||||||
|
#### Session wrap-up (Igor)
|
||||||
|
|
||||||
|
1. `SendMessage` each live adept: "Session closing. Refresh handoff.md, write your closing journal entry, reply with one line of state." Wait via `ListAgents`, read the `<result>`s.
|
||||||
|
2. Journal `## Adepts`: final state per adept, path to its `handoff.md`.
|
||||||
|
3. Close the journal (existing steps 3–4 of the lifecycle continue unchanged).
|
||||||
|
|
||||||
|
#### Compression mid-session
|
||||||
|
|
||||||
|
Already in `adept.md`: an adept whose context compresses wraps up and asks for a successor. With this design "wrap up" is concrete — refresh `handoff.md`, closing journal entry — and the successor's genesis is the bounded protocol above, so replacement is cheap enough to do without hesitation.
|
||||||
|
|
||||||
|
### F. Journal template changes
|
||||||
|
|
||||||
|
`references/journal-template.md` gains one section between Log and Artifacts:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Adepts
|
||||||
|
|
||||||
|
<!-- Roster rows relevant to this session at boot; then one line per adept as
|
||||||
|
spawned/consulted/closed. Path to handoff.md on close. -->
|
||||||
|
|
||||||
|
- auth — successor (5th session) — spawned 10:12 — consulted 3× — closed 11:40, handoff refreshed
|
||||||
|
```
|
||||||
|
|
||||||
|
This is the "cross-session memory of what was useful": the next Igor grepping `journals/` for `auth —` finds every session that used the adept and what it did. The roster gives recency; the journals give usefulness. Neither requires the adept to keep score.
|
||||||
|
|
||||||
|
### G. `adepts/CLAUDE.md` (directory conventions) — rewritten
|
||||||
|
|
||||||
|
Init writes this; the content changes to describe the layout above. The conventions section becomes:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Conventions
|
||||||
|
|
||||||
|
- **One domain per folder.** Overlap → pick the stricter scope. Broad names are gravity wells.
|
||||||
|
- **CLAUDE.md is an index, not a document.** Frontmatter + tiered reading list + open questions. Under ~60 lines. It is auto-loaded whenever anything in the folder is read — keep it cheap.
|
||||||
|
- **Every file in the domain root is listed in CLAUDE.md.** Unlisted files do not exist.
|
||||||
|
- **handoff.md is overwritten, never appended.** Write it early, refresh it after anything significant. `verified-at` is the commit the solid docs were last checked against.
|
||||||
|
- **journal/ is append-only.** One file per topic or session, `YYYY-MM-DD-HH-<topic>.md`. Recency and session counts are derived from these names — keep the prefix exact.
|
||||||
|
- **Solid knowledge goes in docs; experiences go in the journal.** Branch observations stay in the journal (and handoff.md's "In flight") until the code is on main.
|
||||||
|
- **Don't hoard.** Findings that belong in the project's docs or a workpad report go there; link from here.
|
||||||
|
- **Metadata is derived.** Never hand-maintain counts, dates, or an index file. Declare `domain`, `summary`, `covers`, `related`, `status` once in frontmatter; the roster does the rest.
|
||||||
|
```
|
||||||
|
|
||||||
|
### H. Integration summary
|
||||||
|
|
||||||
|
| Component | Change |
|
||||||
|
|---|---|
|
||||||
|
| `skills/adepts/SKILL.md` + `roster.sh` | **New.** Shell-injected roster; invocable on demand. |
|
||||||
|
| `agents/igor.md` | Add `spire:adepts` to `skills:`. |
|
||||||
|
| `skills/igor/SKILL.md` §Adepts | Replace "check the workpad's `adepts/` folder" with "the roster is in context; spawn as successor when the slug exists." Add the founding/successor brief shapes. Add wrap-up step 1 (close-out message). Add shell-disabled fallback (Haiku runs `roster.sh`). |
|
||||||
|
| `skills/igor/SKILL.md` §Session start | Step 1 seeds the journal's `## Adepts` from the roster; step 2 may name a matching adept in the greeting. |
|
||||||
|
| `skills/igor/references/journal-template.md` | Add `## Adepts` section. |
|
||||||
|
| `agents/adept.md` | Genesis splits into founding/successor protocols; folder structure updated; handoff.md duties added (write early, refresh often, drift check); index-maintenance rule; sibling introspection rule; doc-split threshold. |
|
||||||
|
| `skills/init/SKILL.md` Phase 4 | Rewrite `adepts/CLAUDE.md` content (§G). `_template/` gains `handoff.md`; `_template/CLAUDE.md` becomes the index template (§B with placeholders). Phase 7 commit list gains `_template/handoff.md`. |
|
||||||
|
| `skills/workpad/SKILL.md` | Add `adepts/` to the optional-folders list ("adept domain folders — see `spire:adepts`"). Today it is an unknown folder the skill says to ignore. |
|
||||||
|
| `skills/igor/references/project.md` | `Adepts:` line points at the roster: "listed by `spire:adepts` at boot." |
|
||||||
|
| `skills/curator/SKILL.md` | Scope note: knowledge docs in adept folders accrete refinement sections and are curator targets; `handoff.md` and `journal/` are not. |
|
||||||
|
| `agents/athanor.md` | No change. If athanor ever consults adepts, it invokes `spire:adepts` on demand. |
|
||||||
|
|
||||||
|
## Migration path
|
||||||
|
|
||||||
|
### This repo
|
||||||
|
|
||||||
|
No adept folders exist under `docs/workpad/`. Migration here is template-only: update init, re-run `/spire:init` (templates are `status: generated`, so they regenerate).
|
||||||
|
|
||||||
|
### Projects with existing adept folders
|
||||||
|
|
||||||
|
Lazy migration, done by the next adept spawned into each domain — not a bulk job. The roster flags unmigrated folders so Igor knows to include the instruction in the brief:
|
||||||
|
|
||||||
|
```
|
||||||
|
… Your folder is <workpad>/adepts/legacy-importer/ — it predates the tiered layout
|
||||||
|
(no frontmatter, journals at the root). Migrate it during genesis per adepts/CLAUDE.md,
|
||||||
|
then orient.
|
||||||
|
```
|
||||||
|
|
||||||
|
Migration steps the adept performs, in one commit-sized change:
|
||||||
|
|
||||||
|
1. `git mv` each `YYYY-MM-DD-HH-journal-<topic>.md` → `journal/YYYY-MM-DD-HH-<topic>.md`.
|
||||||
|
2. Split the monolithic `CLAUDE.md`: `## Key files` → `key-files.md`; `## Conventions` → `conventions.md`; remaining prose → `architecture.md` or `gotchas.md` by judgment. Preserve dated refinement sections in whichever doc their parent section landed.
|
||||||
|
3. Write the new `CLAUDE.md` index: frontmatter (`domain`, `summary`, `covers` from the key-files table, `related` from any cross-references found), tiered list of the docs just created, carried-over open questions.
|
||||||
|
4. Write `handoff.md` from the newest journal entry's "what's unclear" plus the open questions. `verified-at` = HEAD *after* the adept has verified `key-files.md` against current code — or, if it has not yet, omit the field and say so in `## State`; the next successor will set it.
|
||||||
|
5. Journal entry `journal/<now>-migration.md`: what moved where, what was verified, what was not.
|
||||||
|
|
||||||
|
If a project has many legacy domains and the user wants them done at once, this is a natural athanor job: one brief, one subagent per domain, same five steps.
|
||||||
|
|
||||||
|
### Plugin update for existing installs
|
||||||
|
|
||||||
|
Re-run `/spire:init`. `adepts/CLAUDE.md` and `_template/*` regenerate if `status: generated`. If a project authored either, init skips it and the summary tells the user which files to merge by hand. `roster.sh` ships with the plugin, so discovery works immediately, including over unmigrated folders.
|
||||||
|
|
||||||
|
## Open questions
|
||||||
|
|
||||||
|
For the planner:
|
||||||
|
|
||||||
|
1. **Journal relocation — worth the churn?** Moving journals to `journal/` is the one change that touches history in existing projects. Alternative: keep them flat with the `journal-` infix and have `roster.sh` match both. The clean `ls` is the argument for moving; zero migration is the argument against. This design says move; the planner may overrule.
|
||||||
|
2. **Doc vocabulary — fixed or free?** `key-files.md` is the only doc this design requires. The rest are suggestions. A fixed vocabulary makes cross-domain grepping predictable; free naming fits odd domains better. Current answer: suggested vocabulary in `_template/CLAUDE.md` comments, free additions, index rule enforces discoverability either way.
|
||||||
|
3. **`## Adepts` in the session journal — is grepping journals enough** for "which adept was useful," or is there appetite for a counter? This design says grep is enough and counters are exactly the hand-maintained metadata the principle forbids.
|
||||||
|
4. **`roster.sh` location.** Inside the skill folder (resolved via `${CLAUDE_SKILL_DIR}`) is the obvious spot. If the planner prefers a `just adepts` recipe scaffolded by init, the skill can call that with the script as fallback — but that adds a Justfile dependency this design otherwise avoids.
|
||||||
|
5. **Parallel sessions in the same domain.** Two Igors in two worktrees could each spawn an `auth-adept`; both would overwrite `handoff.md`. Worktrees mean separate files until merge, where `handoff.md` conflicts. Acceptable for now (rare; the conflict is short and human-resolvable), but the adept should mention its worktree in `handoff.md`'s `branch:` so the loser of the merge knows what it is looking at.
|
||||||
|
|
||||||
|
For the fact-checker:
|
||||||
|
|
||||||
|
6. **Nested `CLAUDE.md` auto-loading.** The design leans on the claim that the harness loads `<dir>/CLAUDE.md` when an agent reads files under `<dir>`. Confirm this holds for subagents and for files read via the Read tool (not just cwd). If it does not, the "keep CLAUDE.md short because it is auto-loaded" argument weakens — the index design still stands on its own, but the length pressure is less acute.
|
||||||
|
7. **Skill shell injection with a script.** `spire:project` injects `cat`. Confirm `!`bash "${CLAUDE_SKILL_DIR}/roster.sh"`` works with `allowed-tools: Bash(bash:*)`, that `${CLAUDE_SKILL_DIR}` resolves for plugin-cached skills at agent-startup preload (not only on `/skill` invocation), and what the behaviour is under `disableSkillShellExecution`.
|
||||||
|
8. **Workpad resolution inside the script.** `roster.sh` cannot ask the user. Confirm `WORKPAD_FOLDER` / `just workpad-folder` / `docs/workpad` / `.workpad` covers every configuration the `spire:workpad` skill supports — the external tickets-folder case (`<tickets>/workpad/`) may need `.claude/spire/project.md` to record the resolved path, which init already knows at scaffold time.
|
||||||
|
9. **`Skill("spire:adepts")` from a subagent.** Confirm adepts can invoke a plugin skill on demand and receive the injected output, so sibling discovery does not fall back to `ls`.
|
||||||
|
10. **`git log <sha>..HEAD -- <paths>` with glob entries in `covers:`.** `src/middleware/auth*` needs the shell to expand or git's pathspec to handle it; confirm which, and whether the handoff should store the literal command (as drafted) or just the paths.
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# Investigate Harness Behavior vs. Igor Delegation
|
||||||
|
|
||||||
|
**Status**: open
|
||||||
|
**Filed**: 2026-09-18
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
|
||||||
|
Fact-check and explore whether Igor's tendency to ignore delegation instructions
|
||||||
|
is partly caused by differences in how the Claude Code harness behaves across
|
||||||
|
interaction modes:
|
||||||
|
|
||||||
|
- **Direct harness** (CLI terminal)
|
||||||
|
- **Remote control** (desktop/web controlling a remote session)
|
||||||
|
- **ACP protocol** (programmatic access)
|
||||||
|
|
||||||
|
## What to investigate
|
||||||
|
|
||||||
|
1. Do skill instructions get loaded/weighted differently across modes?
|
||||||
|
2. Does the system prompt structure change between modes in ways that push
|
||||||
|
the agent toward inline work?
|
||||||
|
3. Are there known behavioral differences in how agents follow multi-section
|
||||||
|
skill files across these modes?
|
||||||
|
4. Does context window pressure differ (e.g., remote control adds more system
|
||||||
|
reminders, leaving less room for skill instructions)?
|
||||||
|
5. Is the model selection or fallback behavior different across modes —
|
||||||
|
could Igor sometimes run on a model that's worse at following complex
|
||||||
|
behavioral instructions?
|
||||||
|
|
||||||
|
## Why this matters
|
||||||
|
|
||||||
|
Igor's core directive is delegation, but in practice it frequently drifts into
|
||||||
|
reading code and doing work itself. Part of this is the skill file's structure
|
||||||
|
(being addressed separately), but part may be environmental — the same skill
|
||||||
|
running in different harness modes may produce different adherence levels. If
|
||||||
|
that's the case, the fix isn't just better instructions but also understanding
|
||||||
|
which mode produces the best results and why.
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
- A report documenting observed behavioral differences across modes
|
||||||
|
- Identification of which factors are fixable (skill-side) vs. environmental
|
||||||
|
- Recommendations for which mode to prefer for Igor sessions, if it matters
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
# Plan: Adept discovery and tiered knowledge folders
|
||||||
|
|
||||||
|
**Design**: `designs/2026-09-18-adept-discovery.md`
|
||||||
|
**Date**: 2026-09-18
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
### Q1: Journal relocation — worth the churn?
|
||||||
|
|
||||||
|
**Decision: Move journals to `journal/` subfolder.** No existing adept folders exist in this repo, so there is zero churn here. The design's lazy migration path (next adept into a legacy folder does the `git mv`) is sound for other projects. The clean `ls` benefit is real and permanent; the migration cost is one-time and bounded.
|
||||||
|
|
||||||
|
### Q2: Doc vocabulary — fixed or free?
|
||||||
|
|
||||||
|
**Decision: Suggested vocabulary, free additions, index rule enforces discoverability.** A fixed vocabulary would be over-prescriptive for domains that don't map cleanly to `key-files / conventions / architecture / gotchas`. The index rule ("if it is not listed in CLAUDE.md, it does not exist") is the enforcement mechanism — it makes free naming safe. The `_template/CLAUDE.md` should list the suggested names as comments so a founding adept sees the vocabulary without being constrained by it.
|
||||||
|
|
||||||
|
### Q3: `## Adepts` in journal — grepping enough, or add a counter?
|
||||||
|
|
||||||
|
**Decision: Grepping is enough.** A counter is exactly the hand-maintained metadata the design principle forbids. The `## Adepts` section in the journal provides a grep-friendly surface (`grep -l 'auth —' journals/`). If someone ever wants counts, a one-liner over journal files derives them.
|
||||||
|
|
||||||
|
### Q4: `roster.sh` location — skill folder vs. Justfile recipe
|
||||||
|
|
||||||
|
**Decision: Skill folder (`${CLAUDE_SKILL_DIR}/roster.sh`).** This avoids a Justfile dependency that the design otherwise does not introduce. The Justfile in this repo is currently unstaged and minimal. Adding a `just adepts` recipe would couple a plugin-internal concern to the consuming project's build tool. The skill folder is the natural home — `spire:project` already uses the same `${CLAUDE_SKILL_DIR}` mechanism for `cat`.
|
||||||
|
|
||||||
|
### Q5: Parallel sessions — acceptable conflict risk?
|
||||||
|
|
||||||
|
**Decision: Acceptable for now; document the worktree in `handoff.md`.** The conflict surface is `handoff.md`, which is short and human-resolvable. The adept should record its worktree/branch in `handoff.md`'s frontmatter `branch:` field (already in the design). Rare enough that engineering a lock or merge protocol would be premature.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Phases
|
||||||
|
|
||||||
|
### Phase 1 — New `spire:adepts` skill (the roster)
|
||||||
|
|
||||||
|
**Dependencies**: None. Foundational new component.
|
||||||
|
|
||||||
|
#### 1.1 Create `plugins/spire/skills/adepts/SKILL.md`
|
||||||
|
|
||||||
|
Content per design §D. Frontmatter:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
name: adepts
|
||||||
|
description: Lists the adept domains in the workpad — what each covers, when it was last active, its status. Loaded by Igor at boot; any agent may invoke it for a fresh listing.
|
||||||
|
allowed-tools: Bash(bash:*)
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
Body: the shell-injection line `` !`bash "${CLAUDE_SKILL_DIR}/roster.sh"` `` followed by the empty-roster comment.
|
||||||
|
|
||||||
|
#### 1.2 Create `plugins/spire/skills/adepts/roster.sh`
|
||||||
|
|
||||||
|
Implement the script from design §D. Key implementation details:
|
||||||
|
|
||||||
|
- Workpad resolution: `WORKPAD_FOLDER` env var → `just workpad-folder 2>/dev/null` → `docs/workpad` → `.workpad`. Also grep `.claude/spire/project.md` for the `Workpad:` line as fallback for the external-tickets-folder case.
|
||||||
|
- Iterate `"$WP"/adepts/*/`, skip `_template`.
|
||||||
|
- Parse frontmatter from each domain's `CLAUDE.md` using `sed` (extract lines between opening and closing `---`).
|
||||||
|
- Derive `newest` and `sessions` from `journal/` filenames (also check the domain root for legacy `YYYY-MM-DD-HH-journal-*` files for backward compatibility with unmigrated folders).
|
||||||
|
- Output one `- **slug** — summary ...` line per domain.
|
||||||
|
- Make the script executable (`chmod +x`).
|
||||||
|
|
||||||
|
#### Verification
|
||||||
|
|
||||||
|
- `bash plugins/spire/skills/adepts/roster.sh` in a repo with no `adepts/` folder produces the "none" fallback line.
|
||||||
|
- Frontmatter parsing handles values with colons, and files with no frontmatter produce empty output (not errors).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 2 — Update `agents/adept.md` (tiered layout + handoff protocol)
|
||||||
|
|
||||||
|
**Dependencies**: None (parallel with Phase 1).
|
||||||
|
|
||||||
|
#### 2.1 Rewrite domain folder structure section
|
||||||
|
|
||||||
|
Replace current `## Domain folder structure` (lines 62-66) with new layout from design §A (showing `journal/` subfolder, `handoff.md`, knowledge docs).
|
||||||
|
|
||||||
|
#### 2.2 Split genesis into founding and successor protocols
|
||||||
|
|
||||||
|
Replace current `## Genesis` section (lines 18-31) with two subsections:
|
||||||
|
|
||||||
|
- **Successor genesis** (design §E): nine numbered steps — read `adepts/CLAUDE.md`, read domain `CLAUDE.md` (index), read `handoff.md`, run drift check, read Must-read docs, read only named journal entries, verify-and-correct if drift found, write genesis journal, rewrite `handoff.md`.
|
||||||
|
- **Founding genesis** (design §E): copy `_template/CLAUDE.md` and `_template/handoff.md`, fill frontmatter during orientation, create at minimum `key-files.md`, write `handoff.md` before first consultation with `verified-at` = current HEAD, write genesis journal entry.
|
||||||
|
|
||||||
|
#### 2.3 Add handoff.md duties
|
||||||
|
|
||||||
|
New section `## Handoff` after genesis: rolling, overwritten, short (≤40 lines). Written early, refreshed after significant consultations. Frontmatter fields: `updated`, `session`, `branch`, `verified-at`. The drift check mechanism.
|
||||||
|
|
||||||
|
#### 2.4 Update consultation section
|
||||||
|
|
||||||
|
Add after existing point 5: refresh `handoff.md` after state-changing consultations; use `Skill("spire:adepts")` for sibling discovery; update index when adding docs.
|
||||||
|
|
||||||
|
#### 2.5 Add index maintenance rules
|
||||||
|
|
||||||
|
`CLAUDE.md` stays under ~60 lines. Tiers are conditional (state the condition). Docs split at ~200 lines. Siblings introspected on demand, not preloaded. Every root file must appear in the index.
|
||||||
|
|
||||||
|
#### 2.6 Update end-of-life and knowledge tiers
|
||||||
|
|
||||||
|
Make "wrap up" concrete (refresh handoff, closing journal). Add four-tier table from design §A.
|
||||||
|
|
||||||
|
#### Verification
|
||||||
|
|
||||||
|
- No reference to flat `YYYY-MM-DD-HH-journal-<topic>.md` at root level remains.
|
||||||
|
- Both founding and successor genesis protocols present as distinct subsections.
|
||||||
|
- `handoff.md` mentioned with overwrite-not-append rule.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 3 — Update `skills/igor/SKILL.md` (roster integration + spawning)
|
||||||
|
|
||||||
|
**Dependencies**: Phase 1 (skill must exist to be referenced).
|
||||||
|
|
||||||
|
#### 3.1 Rewrite `## Adepts` section
|
||||||
|
|
||||||
|
Remove "Check the workpad's `adepts/` folder before spawning." Replace with: roster is loaded by `spire:adepts` at boot; Igor does not read adept folders directly. Two brief shapes (successor vs. founding). Roster matching uses the gravity-well rule. Shell-disabled fallback: Haiku subagent runs `roster.sh`.
|
||||||
|
|
||||||
|
#### 3.2 Update session start steps
|
||||||
|
|
||||||
|
After creating journal: seed `## Adepts` from roster rows relevant to topic. May name a matching adept in the greeting.
|
||||||
|
|
||||||
|
#### 3.3 Add wrap-up step for adepts
|
||||||
|
|
||||||
|
In session lifecycle wrap-up: `SendMessage` each live adept to close, wait via `ListAgents`, journal final state per adept.
|
||||||
|
|
||||||
|
#### Verification
|
||||||
|
|
||||||
|
- No reference to "Check the workpad's `adepts/` folder" remains.
|
||||||
|
- Both brief shapes present. Fallback documented.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 4 — Update `agents/igor.md` (add skill)
|
||||||
|
|
||||||
|
**Dependencies**: Phase 1.
|
||||||
|
|
||||||
|
Add `spire:adepts` to `skills:` list after `spire:workpad`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 5 — Update journal template
|
||||||
|
|
||||||
|
**Dependencies**: None (parallel with other phases).
|
||||||
|
|
||||||
|
Add `## Adepts` section between `## Log` and `## Artifacts` in `skills/igor/references/journal-template.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 6 — Update `skills/init/SKILL.md` (new templates)
|
||||||
|
|
||||||
|
**Dependencies**: Phases 1-2 (references the new layout).
|
||||||
|
|
||||||
|
- Rewrite Phase 4 `adepts/CLAUDE.md` content to match design §G.
|
||||||
|
- Rewrite `_template/CLAUDE.md` to be an index template with frontmatter and tiered reading list.
|
||||||
|
- Add `_template/handoff.md` template.
|
||||||
|
- Update Phase 7 commit file list.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 7 — Update supporting files
|
||||||
|
|
||||||
|
**Dependencies**: None (logically last).
|
||||||
|
|
||||||
|
- `skills/workpad/SKILL.md`: add `adepts/` to optional-folders list.
|
||||||
|
- `skills/igor/references/project.md`: update `Adepts:` line to reference roster.
|
||||||
|
- `skills/curator/SKILL.md`: add scope note distinguishing knowledge docs (curator targets) from handoff/journal (not targets).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Commit Strategy
|
||||||
|
|
||||||
|
**Commit 1** — `✨ Add spire:adepts roster skill`
|
||||||
|
- `plugins/spire/skills/adepts/SKILL.md`, `plugins/spire/skills/adepts/roster.sh`
|
||||||
|
|
||||||
|
**Commit 2** — `🤖 Update adept agent for tiered layout and handoff protocol`
|
||||||
|
- `plugins/spire/agents/adept.md`
|
||||||
|
|
||||||
|
**Commit 3** — `🤖 Wire Igor to use adept roster for discovery and spawning`
|
||||||
|
- `plugins/spire/skills/igor/SKILL.md`, `plugins/spire/agents/igor.md`, `plugins/spire/skills/igor/references/journal-template.md`
|
||||||
|
|
||||||
|
**Commit 4** — `🤖 Update init templates and supporting files for adept discovery`
|
||||||
|
- `plugins/spire/skills/init/SKILL.md`, `plugins/spire/skills/workpad/SKILL.md`, `plugins/spire/skills/igor/references/project.md`, `plugins/spire/skills/curator/SKILL.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fact-Check Items
|
||||||
|
|
||||||
|
### From the design (questions 6-10)
|
||||||
|
|
||||||
|
**FC-1 (Q6)**: Nested `CLAUDE.md` auto-loading — does the harness load `<dir>/CLAUDE.md` when a subagent reads files under `<dir>` via Read? Underpins the "keep CLAUDE.md short" length pressure.
|
||||||
|
|
||||||
|
**FC-2 (Q7)**: Skill shell injection with a script path — does `` !`bash "${CLAUDE_SKILL_DIR}/roster.sh"` `` work with `allowed-tools: Bash(bash:*)`? Does `${CLAUDE_SKILL_DIR}` resolve for plugin skills at agent-startup preload time? Behavior under `disableSkillShellExecution`?
|
||||||
|
|
||||||
|
**FC-3 (Q8)**: Workpad resolution in the script — does the resolution chain cover the external-tickets-folder case?
|
||||||
|
|
||||||
|
**FC-4 (Q9)**: `Skill("spire:adepts")` from a subagent — can an adept invoke a plugin skill on demand?
|
||||||
|
|
||||||
|
**FC-5 (Q10)**: `git log` with glob pathspecs — does `git log --oneline <sha>..HEAD -- src/middleware/auth*` work correctly, or does shell expansion interfere? Should the handoff template quote the paths?
|
||||||
|
|
||||||
|
### Additional items from planning
|
||||||
|
|
||||||
|
**FC-6**: `sed` frontmatter parsing robustness — handles values with colons? Multiline arrays? Files with no frontmatter?
|
||||||
|
|
||||||
|
**FC-7**: `ls` vs. `find` for journal enumeration — behavior when `journal/` doesn't exist? Does `2>/dev/null` on `ls` swallow both arguments' output?
|
||||||
|
|
||||||
|
**FC-8**: `CLAUDE_SKILL_DIR` for the adepts skill — resolves to `plugins/spire/skills/adepts/` (or cached equivalent), not the invoking skill's directory?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Design Adjustments (from planner)
|
||||||
|
|
||||||
|
1. **`roster.sh` should grep `.claude/spire/project.md` for the workpad path** as an additional fallback for the external-tickets-folder case.
|
||||||
|
2. **Keep `covers:` parsing out of `roster.sh`** — it's consumed by the drift check in `handoff.md`, not by the roster. The roster only needs `summary`, `status`, and `related`.
|
||||||
|
3. **Phase parallelism**: Phases 1, 2, and 5 have no dependencies and can be worked in parallel. Phase 3 depends on Phase 1. Phase 6 depends on Phase 2.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Risks and Mitigations
|
||||||
|
|
||||||
|
1. **`roster.sh` fragility** — shell parsing of YAML frontmatter with `sed` is brittle. Mitigation: only extract four single-line fields; "unmigrated" fallback for unparseable frontmatter; convention note that frontmatter values must be single-line.
|
||||||
|
2. **`handoff.md` overwrite breaks git merge** — parallel sessions in same domain. Mitigation: accepted (Q5); rare, short file, `branch:` field helps resolution.
|
||||||
|
3. **Large change surface** — many files change across the plugin. Mitigation: commit strategy isolates additive roster skill from behavior changes; each commit independently verifiable.
|
||||||
|
4. **Backward compatibility** — existing adept folders see "unmigrated" in roster. Mitigation: by design; lazy migration via next adept spawn.
|
||||||
@@ -0,0 +1,229 @@
|
|||||||
|
# Fact-check: Adept discovery and tiered knowledge folders
|
||||||
|
|
||||||
|
**Date**: 2026-09-18
|
||||||
|
**Design**: `designs/2026-09-18-adept-discovery.md`
|
||||||
|
**Plan**: `plans/2026-09-18-adept-discovery-plan.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## FC-1: Nested `CLAUDE.md` auto-loading
|
||||||
|
|
||||||
|
**Verdict: CONFIRMED**
|
||||||
|
|
||||||
|
**Evidence**: During this fact-check session, running `ls plugins/spire/agents/` caused the harness to inject `plugins/spire/CLAUDE.md` as a `<system-reminder>` block. This is direct empirical evidence that the harness auto-loads a subdirectory's `CLAUDE.md` when files beneath it are accessed (including via the Bash tool, not only the Read tool).
|
||||||
|
|
||||||
|
The behavior is consistent with Claude Code's documented CLAUDE.md / memory system, which processes `CLAUDE.md` files at each level of the directory tree. Four `CLAUDE.md` files exist in this repo: the root, `plugins/bevy/`, `plugins/g4b_ai/`, and `plugins/spire/`. The Spire one was injected on first access to files under `plugins/spire/`.
|
||||||
|
|
||||||
|
**Impact**: The design's core assumption holds. Keeping adept `CLAUDE.md` files short (under ~60 lines) is justified because they are auto-loaded whenever any file in the domain folder is read. The "index, not a document" principle is sound.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## FC-2: Skill shell injection with a script path
|
||||||
|
|
||||||
|
**Verdict: CONFIRMED (with caveats)**
|
||||||
|
|
||||||
|
### `${CLAUDE_SKILL_DIR}` resolution
|
||||||
|
|
||||||
|
**Confirmed.** The `spire:project` skill at `plugins/spire/skills/project/SKILL.md` already uses `${CLAUDE_SKILL_DIR}` successfully:
|
||||||
|
|
||||||
|
```
|
||||||
|
!`cat "${CLAUDE_PROJECT_DIR}/.claude/spire/project.md" 2>/dev/null || cat "${CLAUDE_SKILL_DIR}/../igor/references/project.md"`
|
||||||
|
```
|
||||||
|
|
||||||
|
The research doc (`docs/workpad/research/2026-09-17-project-md-override-mechanics.md`) explicitly documents `${CLAUDE_SKILL_DIR}` as a load-time substitution: "Skill subdirectory / plugin install directory (plugin skills only)."
|
||||||
|
|
||||||
|
### `allowed-tools` for `bash` vs `cat`
|
||||||
|
|
||||||
|
**Caveat identified.** Every existing skill in Spire uses `Bash(cat:*)` for its `allowed-tools`, not `Bash(bash:*)`:
|
||||||
|
|
||||||
|
- `spire:project` -- `allowed-tools: Bash(cat:*)`
|
||||||
|
- `spire:init` -- `allowed-tools: Bash(cat:*), Bash(grep:*), Bash(ls:*), Bash(just:*), Bash(git:*), Read, Write, Glob, Grep`
|
||||||
|
|
||||||
|
The project's `settings.json` allow-list includes `Bash(cat:*)` but NOT `Bash(bash:*)`. The skill's `allowed-tools: Bash(bash:*)` should pre-approve the command for injection at load time (per the research doc: "declare `allowed-tools: Bash(cat:*)` so the command is pre-approved in any project"). However, `Bash(bash:*)` has never been tested in this project's plugin skills.
|
||||||
|
|
||||||
|
This is a novel pattern -- all existing injection uses `cat`, not `bash`. The design might alternatively use `Bash(sh:*)` or restructure the injection to source the script rather than invoking bash explicitly. Consider adding `Bash(bash:*)` to the project settings allow-list for defense-in-depth.
|
||||||
|
|
||||||
|
### `disableSkillShellExecution`
|
||||||
|
|
||||||
|
The design documents a fallback (Haiku subagent runs `roster.sh`). The research doc confirms: "`disableSkillShellExecution: true` in settings turns injection off."
|
||||||
|
|
||||||
|
**Impact**: The mechanism is sound in principle but uses an untested permission pattern (`Bash(bash:*)`). Recommend testing the exact `!`bash "${CLAUDE_SKILL_DIR}/roster.sh"`` injection in a scratch skill before committing, and adding `Bash(bash:*)` to the project `settings.json` allow-list.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## FC-3: Workpad resolution in the roster script
|
||||||
|
|
||||||
|
**Verdict: CONFIRMED (with documented gaps)**
|
||||||
|
|
||||||
|
The `spire:workpad` skill (`plugins/spire/skills/workpad/SKILL.md`) has a six-step resolution chain:
|
||||||
|
|
||||||
|
1. Explicit user instruction in conversation
|
||||||
|
2. `WORKPAD_FOLDER` env var (via `just workpad-folder` or `printenv`)
|
||||||
|
3. Project-local tickets folder (external)
|
||||||
|
4. `docs/workpad` if `docs/` exists
|
||||||
|
5. `.workpad`
|
||||||
|
6. Ask the user
|
||||||
|
|
||||||
|
The roster script covers steps 2, 4, and 5:
|
||||||
|
`WORKPAD_FOLDER` -> `just workpad-folder 2>/dev/null` -> `docs/workpad` -> `.workpad`
|
||||||
|
|
||||||
|
**Gaps**:
|
||||||
|
|
||||||
|
- **Step 1 (explicit user instruction)**: Impossible in a non-interactive script. Acceptable -- the script runs at skill load time, not during conversation.
|
||||||
|
- **Step 3 (tickets folder)**: Not directly covered. The plan adds "grep `.claude/spire/project.md` for the `Workpad:` line" as a fallback. However, `.claude/spire/project.md` does NOT exist in this repo (init hasn't been run), and the file format is freeform markdown, not structured YAML -- `grep` extraction would be fragile.
|
||||||
|
- **Step 6 (ask)**: Impossible in a non-interactive script. Acceptable -- the script's "none" fallback handles the missing-workpad case cleanly.
|
||||||
|
- **Justfile `workpad-folder` recipe**: The Justfile in this repo exists but does NOT define a `workpad-folder` recipe (it only has `default` and `claude-rc`). The `2>/dev/null` suppression handles this correctly.
|
||||||
|
|
||||||
|
**Impact**: The gaps are acceptable for a derived-metadata script. The tickets-folder case is the only one that could produce a wrong result (finding no workpad when one exists externally). The plan's mitigation (grep `project.md`) is the right direction but needs a stable format to grep for (suggest a YAML frontmatter field like `workpad:` rather than freeform markdown).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## FC-4: `Skill("spire:adepts")` from a subagent
|
||||||
|
|
||||||
|
**Verdict: UNCERTAIN**
|
||||||
|
|
||||||
|
**Evidence**:
|
||||||
|
|
||||||
|
- The Skill tool is a core tool in Claude Code, available by default to all agents. No agent definition in the repo restricts it.
|
||||||
|
- The adept agent (`plugins/spire/agents/adept.md`) lists `skills: [spire:workpad]` but NOT `spire:adepts`. This means `spire:adepts` would not be preloaded at boot but would need to be invoked on demand via model-driven activation.
|
||||||
|
- Plugin skills are available to all sessions where the plugin is enabled (`"spire@reliquary": true` in `settings.json`). The `description` field in the skill's frontmatter drives model-based activation.
|
||||||
|
|
||||||
|
**Unknowns**:
|
||||||
|
|
||||||
|
- Whether a subagent spawned via the Agent tool has the same plugin skill access as the parent session has not been directly tested.
|
||||||
|
- Whether shell injection in a skill (`!`bash ...``) works correctly when the skill is invoked by a subagent (rather than loaded at agent boot via `skills:` frontmatter) has not been tested.
|
||||||
|
- The adept could alternatively add `spire:adepts` to its `skills:` list to guarantee preloading, but this injects the roster at boot for every adept, which may not be desired.
|
||||||
|
|
||||||
|
**Impact**: If subagent skill invocation doesn't work, the design falls back to the adept doing `ls` and `grep` on the adepts folder -- exactly the pattern the design aims to replace. The safest mitigation is to add `spire:adepts` to the adept agent's `skills:` list. The roster is one line per domain and cheap, so preloading at boot is acceptable. If on-demand invocation works, the `skills:` entry can be removed later.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## FC-5: `git log` with glob pathspecs
|
||||||
|
|
||||||
|
**Verdict: CONFIRMED**
|
||||||
|
|
||||||
|
**Evidence** (tested in this repo):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Quoted glob -- git interprets as pathspec
|
||||||
|
git log --oneline HEAD~5..HEAD -- 'plugins/spire/skills/igor/*'
|
||||||
|
# Output: bea38f0, 728db14, 4d081ae (3 commits)
|
||||||
|
|
||||||
|
# Unquoted glob -- shell expands, git gets explicit paths
|
||||||
|
git log --oneline HEAD~5..HEAD -- plugins/spire/skills/igor/*
|
||||||
|
# Output: identical (3 commits)
|
||||||
|
```
|
||||||
|
|
||||||
|
Both produce identical results. The mechanism differs:
|
||||||
|
|
||||||
|
- **Quoted**: shell passes the literal `plugins/spire/skills/igor/*` to git, which interprets `*` as a pathspec glob (built into git).
|
||||||
|
- **Unquoted**: bash expands the glob to matching files (`references SKILL.md`), then git gets explicit paths.
|
||||||
|
|
||||||
|
**Shell sensitivity**: In bash (which the Bash tool uses), a non-matching glob passes the literal string through. In zsh, a non-matching glob errors out by default. Since the drift check runs via the Bash tool (which uses bash), unquoted globs are safe. The user's login shell (fish) is irrelevant.
|
||||||
|
|
||||||
|
**Recommendation**: The handoff template should quote the paths in the drift check command for robustness:
|
||||||
|
|
||||||
|
```
|
||||||
|
git log --oneline <sha>..HEAD -- 'src/auth/' 'src/middleware/auth*' 'tests/auth/'
|
||||||
|
```
|
||||||
|
|
||||||
|
This ensures correct behavior regardless of which shell ends up running the command, and makes the intent explicit.
|
||||||
|
|
||||||
|
**Impact**: None -- the mechanism works. Quoting is a minor robustness improvement.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## FC-6: `sed` frontmatter parsing robustness
|
||||||
|
|
||||||
|
**Verdict: CONFIRMED (with noted edge cases)**
|
||||||
|
|
||||||
|
**Evidence** (tested with scratch files in `/tmp/`):
|
||||||
|
|
||||||
|
| Test case | `sed -n '2,/^---$/p'` output | Field extraction |
|
||||||
|
|---|---|---|
|
||||||
|
| Normal frontmatter | All fields + closing `---` | Correct |
|
||||||
|
| Colon in value (`summary: Login flow: the auth chain`) | Correct | `Login flow: the auth chain` -- correct |
|
||||||
|
| No frontmatter | Everything from line 2 to EOF | Empty -- no `^field:` matches |
|
||||||
|
| Unclosed frontmatter (no closing `---`) | Everything from line 2 to EOF | Extracts fields correctly |
|
||||||
|
| Empty summary (`summary:`) | Correct | Empty string -- correct |
|
||||||
|
| Multiline YAML array (`covers:\n - src/auth/`) | Correct | `covers:` extraction returns empty (value on next line) |
|
||||||
|
| Empty file | Empty | Empty |
|
||||||
|
| Missing file (with `2>/dev/null`) | Empty, exit code 2 | Empty |
|
||||||
|
|
||||||
|
**Notable behaviors**:
|
||||||
|
|
||||||
|
1. The closing `---` IS included in the sed output (e.g., for normal frontmatter, the output is 5 lines: 4 fields + `---`). This does not affect field extraction because `---` does not match any `^fieldname: ` pattern.
|
||||||
|
2. Files with no frontmatter cause sed to read the entire file through the pipeline. This is wasteful but harmless since (a) `CLAUDE.md` files are small, and (b) the field-extraction sed patterns won't match body content unless someone writes a line starting with `summary: ` in the prose body.
|
||||||
|
3. The roster script only extracts `summary`, `status`, and `related` -- all single-line values. The plan correctly notes keeping `covers:` out of the roster (it's consumed by `handoff.md`'s drift check).
|
||||||
|
|
||||||
|
**Impact**: The parsing works for the design's purposes. The plan's risk note ("convention note that frontmatter values must be single-line") is appropriate. Consider adding a guard: if the first line is not `---`, skip the file (avoids reading entire non-frontmatter files through sed).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## FC-7: `ls` behavior for journal enumeration
|
||||||
|
|
||||||
|
**Verdict: CONFIRMED (with a minor concern)**
|
||||||
|
|
||||||
|
**Evidence** (tested with scratch directories in `/tmp/`):
|
||||||
|
|
||||||
|
| Test case | Behavior |
|
||||||
|
|---|---|
|
||||||
|
| Both `journal/` and domain root exist | Lists contents of both; grep filters correctly |
|
||||||
|
| `journal/` doesn't exist | `2>/dev/null` swallows the error; domain root still listed |
|
||||||
|
| Neither exists | Empty output, no errors |
|
||||||
|
| `ls` with multiple directory args | Prints header labels (`/path/:`) per directory, but these don't match `^[0-9]{4}-` |
|
||||||
|
|
||||||
|
The `newest` extraction (`sort | tail -1 | cut -c1-13`) correctly produces the `YYYY-MM-DD-HH` timestamp from filenames. `cut -c1-13` maps exactly to the 13-character `YYYY-MM-DD-HH` format.
|
||||||
|
|
||||||
|
The `sessions` count (`grep -Eo | sort -u | wc -l`) correctly counts unique timestamps across both journal entries and legacy root-level files.
|
||||||
|
|
||||||
|
**Minor concern**: The `journal` directory entry itself appears in the `ls` output of the domain root, but it does not match the `^[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}` pattern, so it is correctly filtered out. Other non-journal files in the domain root (`CLAUDE.md`, `handoff.md`, `key-files.md`, etc.) are also filtered out by the same regex.
|
||||||
|
|
||||||
|
**Impact**: The mechanism works as designed. The backward-compatibility behavior (counting legacy root-level journal files alongside `journal/` entries) is intentional and correct for the lazy-migration story.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## FC-8: `CLAUDE_SKILL_DIR` scope
|
||||||
|
|
||||||
|
**Verdict: CONFIRMED**
|
||||||
|
|
||||||
|
**Evidence**:
|
||||||
|
|
||||||
|
The `spire:project` skill uses `${CLAUDE_SKILL_DIR}/../igor/references/project.md`. Given:
|
||||||
|
|
||||||
|
- `CLAUDE_SKILL_DIR` = `plugins/spire/skills/project/` (or its cached equivalent)
|
||||||
|
- `../igor/references/project.md` navigates to `plugins/spire/skills/igor/references/project.md`
|
||||||
|
|
||||||
|
This file exists at `/home/g4b/Projects/reliquary/plugins/spire/skills/igor/references/project.md` -- verified. The path resolution is correct, confirming that `${CLAUDE_SKILL_DIR}` resolves to the **skill's own directory**, not the invoking context's directory.
|
||||||
|
|
||||||
|
For the proposed `spire:adepts` skill:
|
||||||
|
- `CLAUDE_SKILL_DIR` would resolve to `plugins/spire/skills/adepts/` (or `~/.claude/plugins/cache/reliquary/spire/<version>/skills/adepts/` when cached)
|
||||||
|
- `roster.sh` sits in the same directory as `SKILL.md`
|
||||||
|
- `bash "${CLAUDE_SKILL_DIR}/roster.sh"` is a trivial same-directory reference -- no relative navigation needed
|
||||||
|
|
||||||
|
**Cache observation**: The current plugin cache (`~/.claude/plugins/cache/reliquary/spire/0.1.0/skills/`) is missing several recently added skills (`curator`, `init`, `project`). The source has 10 skills; the cache has 7. Any new skill (`adepts`) will also be missing until the cache is refreshed. This is an operational concern (re-install the plugin after adding the skill) but not a design flaw.
|
||||||
|
|
||||||
|
**Impact**: The mechanism is sound. `${CLAUDE_SKILL_DIR}/roster.sh` will resolve correctly.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
| Item | Verdict | Design impact |
|
||||||
|
|---|---|---|
|
||||||
|
| FC-1: Nested CLAUDE.md auto-loading | **CONFIRMED** | None -- design assumption holds |
|
||||||
|
| FC-2: Skill shell injection with script | **CONFIRMED** (caveat) | Test `Bash(bash:*)` permission; consider adding to settings.json allow-list |
|
||||||
|
| FC-3: Workpad resolution in script | **CONFIRMED** (gaps) | Tickets-folder case needs structured format in `project.md`; acceptable for now |
|
||||||
|
| FC-4: Skill invocation from subagent | **UNCERTAIN** | Add `spire:adepts` to adept agent's `skills:` list as safety net |
|
||||||
|
| FC-5: git log with glob pathspecs | **CONFIRMED** | Quote paths in the drift check template for robustness |
|
||||||
|
| FC-6: sed frontmatter parsing | **CONFIRMED** (edge cases) | Add first-line `---` guard; document single-line-value convention |
|
||||||
|
| FC-7: ls for journal enumeration | **CONFIRMED** | None |
|
||||||
|
| FC-8: CLAUDE_SKILL_DIR scope | **CONFIRMED** | None; refresh plugin cache after adding the skill |
|
||||||
|
|
||||||
|
**Overall assessment**: The design is well-grounded. Six of eight claims are confirmed outright or with minor caveats. One (FC-4, subagent skill invocation) is uncertain and has a simple mitigation (preload the skill). None are refuted. The plan can proceed with the following adjustments:
|
||||||
|
|
||||||
|
1. **FC-2**: Add `Bash(bash:*)` to the project `settings.json` allow-list or test the skill injection before committing.
|
||||||
|
2. **FC-4**: Add `spire:adepts` to the adept agent's `skills:` frontmatter as a guaranteed-available fallback.
|
||||||
|
3. **FC-5**: Quote glob paths in the `handoff.md` drift-check template.
|
||||||
|
4. **FC-6**: Add a first-line guard (`head -1 | grep -q '^---$'`) to skip files without frontmatter early, avoiding full-file reads.
|
||||||
|
5. **FC-8**: Remember to refresh the plugin cache after adding the new skill.
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# Athanor Report: Adept Discovery and Tiered Knowledge Folders
|
||||||
|
|
||||||
|
**Date**: 2026-09-18
|
||||||
|
**Design**: `designs/2026-09-18-adept-discovery.md`
|
||||||
|
**Plan**: `plans/2026-09-18-adept-discovery-plan.md`
|
||||||
|
**Fact-check**: `reports/2026-09-18-adept-discovery-factcheck.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dispatch
|
||||||
|
|
||||||
|
Single-agent execution (no subagent fan-out needed). All seven phases of the plan were implemented sequentially, with verification between phases.
|
||||||
|
|
||||||
|
## Commits
|
||||||
|
|
||||||
|
| # | SHA | Message | Files |
|
||||||
|
|---|-----|---------|-------|
|
||||||
|
| 1 | `1c4ec76` | `✨ Add spire:adepts roster skill` | `skills/adepts/SKILL.md`, `skills/adepts/roster.sh` |
|
||||||
|
| 2 | `d68db18` | `🤖 Update adept agent for tiered layout and handoff protocol` | `agents/adept.md` |
|
||||||
|
| 3 | `2dc52e6` | `🤖 Wire Igor to use adept roster for discovery and spawning` | `skills/igor/SKILL.md`, `agents/igor.md`, `skills/igor/references/journal-template.md` |
|
||||||
|
| 4 | `c62d92e` | `🤖 Update init templates and supporting files for adept discovery` | `skills/init/SKILL.md`, `skills/workpad/SKILL.md`, `skills/igor/references/project.md`, `skills/curator/SKILL.md` |
|
||||||
|
|
||||||
|
All paths relative to `plugins/spire/`.
|
||||||
|
|
||||||
|
## Fact-check adjustments incorporated
|
||||||
|
|
||||||
|
| FC | Adjustment | Applied in |
|
||||||
|
|----|-----------|-----------|
|
||||||
|
| FC-2 | `allowed-tools: Bash(bash:*)` with comment noting novel pattern | `skills/adepts/SKILL.md` |
|
||||||
|
| FC-4 | `spire:adepts` added to adept agent's `skills:` list | `agents/adept.md` |
|
||||||
|
| FC-5 | Quoted glob paths in drift-check templates | `agents/adept.md` (genesis protocol), `skills/init/SKILL.md` (`_template/handoff.md`) |
|
||||||
|
| FC-6 | First-line `---` guard in `roster.sh` | `skills/adepts/roster.sh` (lines 28-32) |
|
||||||
|
| FC-8 | Plugin cache staleness — noted, not implemented (operational concern) | n/a |
|
||||||
|
|
||||||
|
## Verification results
|
||||||
|
|
||||||
|
- `bash plugins/spire/skills/adepts/roster.sh` produces: `## Adept roster — none (no docs/workpad/adepts/)` — correct fallback
|
||||||
|
- `roster.sh` is executable (mode `755`)
|
||||||
|
- No references to "Check the workpad's adepts/ folder" remain in `skills/igor/SKILL.md`
|
||||||
|
- `## Core Directive` section preserved at line 13 of `skills/igor/SKILL.md`
|
||||||
|
- `spire:adepts` present in both `agents/igor.md` and `agents/adept.md` skill lists
|
||||||
|
- 10 files changed, 293 insertions, 74 deletions across 4 clean commits
|
||||||
|
|
||||||
|
## Deviations from the plan
|
||||||
|
|
||||||
|
None. All phases implemented as specified.
|
||||||
|
|
||||||
|
## What was produced per file
|
||||||
|
|
||||||
|
### New files
|
||||||
|
- **`skills/adepts/SKILL.md`** — Skill frontmatter with `Bash(bash:*)` permission, shell injection line, FC-2 comment noting the novel pattern, empty-roster fallback comment.
|
||||||
|
- **`skills/adepts/roster.sh`** — Workpad resolution chain (env var, just, project.md grep, docs/workpad, .workpad). Iterates domain folders, skips `_template`. FC-6 first-line guard skips non-frontmatter files early. Parses `summary`, `status`, `related` from frontmatter. Derives recency and session count from journal filenames (supports both `journal/` subfolder and legacy root-level files).
|
||||||
|
|
||||||
|
### Modified files
|
||||||
|
- **`agents/adept.md`** — Description updated. `spire:adepts` added to skills (FC-4). Genesis split into successor (9-step bounded protocol with drift check) and founding (9-step protocol with template copy). New Handoff section (rolling, overwritten, frontmatter fields, drift check mechanism). Consultation updated with handoff refresh (step 6), sibling discovery via roster (step 7), index maintenance on doc add (step 8). Knowledge tiers replaced with four-tier table. New Index maintenance section (60-line limit, conditional tiers, 200-line split threshold, index rule, sibling introspection). Journal conventions updated for `journal/` subfolder. Domain folder structure rewritten to show tiered layout. End of life updated (refresh handoff + closing journal).
|
||||||
|
- **`skills/igor/SKILL.md`** — Adepts section rewritten: roster-based discovery, three brief shapes (successor/founding/unmigrated), gravity-well rule, shell-disabled fallback, roster tracking in journal. Session start step 1 seeds journal Adepts section from roster; step 2 may name matching adept in greeting. Wrap-up step 3 adds adept close-out via SendMessage.
|
||||||
|
- **`agents/igor.md`** — `spire:adepts` added to skills list.
|
||||||
|
- **`skills/igor/references/journal-template.md`** — `## Adepts` section added between Log and Artifacts with comment explaining contents.
|
||||||
|
- **`skills/init/SKILL.md`** — Phase 4.2 adepts/CLAUDE.md rewritten with tiered structure and eight conventions from design section G. Phase 4.3 `_template/CLAUDE.md` rewritten as index template with frontmatter fields and tiered reading list. Phase 4.4 added: `_template/handoff.md` template with frontmatter, state sections, drift check (quoted paths per FC-5). Phase 7 file list includes `_template/handoff.md`.
|
||||||
|
- **`skills/workpad/SKILL.md`** — `adepts/` added to optional folders list.
|
||||||
|
- **`skills/igor/references/project.md`** — Adepts line changed from placeholder comment to "Listed by `spire:adepts` at boot."
|
||||||
|
- **`skills/curator/SKILL.md`** — Scope note added: knowledge docs are curator targets; `handoff.md` and `journal/` are not.
|
||||||
|
|
||||||
|
## Open threads
|
||||||
|
|
||||||
|
1. **Plugin cache refresh** — The new `adepts` skill will not be available until the plugin cache is refreshed (FC-8). User should re-install the Spire plugin after this lands.
|
||||||
|
2. **`Bash(bash:*)` in settings.json** — The project `settings.json` allow-list does not include `Bash(bash:*)`. The skill's `allowed-tools` should pre-approve it at load time, but adding it to the project allow-list would provide defense-in-depth. Not done here to avoid touching settings outside the plugin scope.
|
||||||
|
3. **Testing with real adept folders** — The roster script was tested with the "none" fallback only. Full testing with actual frontmatter, journals, and edge cases requires adept folders to exist.
|
||||||
Reference in New Issue
Block a user