chore(claude): update lead skill

This commit is contained in:
2026-11-02 10:37:19 +01:00
parent bf75b8de96
commit a134b7fa93
7 changed files with 44 additions and 14 deletions

View File

@@ -6,7 +6,7 @@ user-invocable: false
# Builder
You build one part of a unit of work that an integrator owns, from its brief. Stay inside the brief: anything outside it is a leftover in your reply, not a change on your branch. When the brief leaves a choice, take the simplest option and list it under "decided along the way".
You build one part of a unit of work that an integrator owns, from its brief. Stay inside the brief: anything outside it is a leftover in your reply, not a change on your branch. When the brief leaves a choice, take the simplest option and list it under "decided along the way"; when it depends on another part or on context the brief left out, ask your integrator instead, by SendMessage to the id the brief names, and `main` only if that gets no answer or it's the lead's call.
Read `~/.config/claude/CLAUDE.md`, the project's AGENTS.md and the house-rule skills it points to before writing code. Work on the branch and worktree the brief names; a few logical commits, never sign, merge or push. Run the checks the brief lists, only for what you touched, and stop your own servers and containers before replying.

View File

@@ -31,12 +31,12 @@ The pair types carry their model in their frontmatter; never pass a `model` when
- Discuss, lock, relay. Only locked decisions go to agents, in the words agreed. When a relay replaces an earlier one, say what it replaces, without restating the brief; when unsure what Matej meant, ask first.
- Agents keep going while the next point is discussed; new locked items join the open branch.
- Agents decide what the brief doesn't settle the simple way and list it as "decided along the way". Only the lead asks Matej.
- Agents decide what the brief doesn't settle the simple way and list it as "decided along the way". Only the lead asks Matej. The lead relays a builder's question to its integrator rather than answering it.
- Once an agent has replied, it is done. A fix-up, rename or check goes to a fresh agent with a short brief naming the branch; a message to a running agent is only for something inside the brief it is still on.
## Spawning
Use the agent types above, not a general-purpose agent, so Matej sees who is doing what. The label is set once, at spawn: make it the unit of work ("graph redesign", "signing agent commits"), never its first step.
Use the agent types above, not a general-purpose agent, so Matej sees who is doing what. The label is set once, at spawn: make it the unit of work ("graph redesign", "signing agent commits"), never its first step. Right after spawning an integrator, SendMessage it its own agent id: integrators have no name, so its builders can't find it otherwise.
## A brief carries
@@ -63,7 +63,7 @@ A change to this skill, the agent definitions or the notes conventions is kept w
- Leftovers: `followups/`, by area, at close-out. `questions/` is Matej's own.
- Visual design to discuss: the Design canvas (an Artifact), kept current; old frames removed, not piled up.
- Prototypes and finished work: branches. The repo's `.git` is on Matej's host, so he can `git worktree add --detach` any branch.
- Bulk files for Matej (screenshots, logs): the host handoff mount once it exists; never the project repo, never bulk in `.notes/`.
- Bulk files for Matej (screenshots, logs): `$SANDBOX_VM_STATE/<wNNN>-<part>/`, with the full paths in the reply; never the project repo, never bulk in `.notes/`.
## Notes
@@ -72,7 +72,7 @@ Plans, decisions, work handoffs, follow-ups and Matej's questions live in the pr
## Ownership and clean-up
- Everything a unit of work creates carries its work id: branches `<wNNN>/<name>` (see Git), worktrees under `$SANDBOX_VM_STATE/wt/<wNNN>/`, containers `<wNNN>-<name>`. Agents remove what they created and nothing else.
- The lead keeps `made.md` in the work item: each branch, worktree, container and handoff folder, with its state (open, merged, prototype); whether a branch is in main, `notes.py status` reads from git.
- The lead keeps `made.md` in the work item: each branch, worktree, container and state folder, with its state (open, merged, prototype); whether a branch is in main, `notes.py status` reads from git.
- Close-out removes the list, then checks nothing under the work id is left.
## Interruptions

View File

@@ -97,7 +97,7 @@ Settling: check the repo first, fold the outcome into a doc or work item if wort
## Other
`other/out/` holds documents for someone else, written for that reader, and the few small files that go with them; no ids, no status line. HTML there follows the doc skeleton with `../../notes.css`; the eyebrow names the reader and date (`for the team · 2026-10-14`). What gets sent is the generated `_out/<file>`; look at it before handing it over, link it from the work item's `## Your move` (the index doesn't list `other/`), then `commit "other: <file>"`. Bulk output for a person (screenshots, builds, logs) goes to the host handoff mount, not `.notes/`.
`other/out/` holds documents for someone else, written for that reader, and the few small files that go with them; no ids, no status line. HTML there follows the doc skeleton with `../../notes.css`; the eyebrow names the reader and date (`for the team · 2026-10-14`). What gets sent is the generated `_out/<file>`; look at it before handing it over, link it from the work item's `## Your move` (the index doesn't list `other/`), then `commit "other: <file>"`. Bulk output for a person (screenshots, builds, logs) stays in `$SANDBOX_VM_STATE`, named by full path, not `.notes/`.
## Never

View File

@@ -1034,7 +1034,26 @@ def file_reply(notes: Path, work: str, part: str, ref: str, checked: str) -> Non
# serve: every project's notes on one port
SKIP_DIRS = {"node_modules", "target", "dist", "build", "vendor", "__pycache__"}
SKIP_DIRS = {
"node_modules",
"target",
"dist",
"build",
"vendor",
"__pycache__",
"cache",
}
# browser profiles under ~/.config: thousands of directories, never a project
SKIP_PATHS = {"config/chromium", "config/google-chrome", "config/BraveSoftware"}
def is_claude_config(d: Path) -> bool:
"""claude code's config dir: its subdirectories are transcripts, sessions and skills"""
return (
d.name.lstrip(".") == "claude"
and (d / "settings.json").is_file()
and (d / "projects").is_dir()
)
def find_notes(roots: list[Path], depth: int = 6) -> dict[str, Path]:
@@ -1049,14 +1068,18 @@ def find_notes(roots: list[Path], depth: int = 6) -> dict[str, Path]:
# a project's subdirectories are its code, even when the project is a root
return
# a repo's own subdirectories are its code, not more projects
if left == 0 or ((d / ".git").exists() and d != root):
if left == 0 or ((d / ".git").exists() and d != root) or is_claude_config(d):
return
try:
children = sorted(d.iterdir())
except OSError:
return
for c in children:
skip = c.name.startswith(".") or c.name in SKIP_DIRS
skip = (
c.name.startswith(".")
or c.name in SKIP_DIRS
or f"{d.name.lstrip('.')}/{c.name}" in SKIP_PATHS
)
if c.is_dir() and not c.is_symlink() and not skip:
walk(c, root, left - 1)
@@ -1298,8 +1321,10 @@ def serve(roots: list[Path], host: str, port: int) -> None:
page = (notes / "index.html").read_text()
page = page.replace("</header>", "</header>" + search_form("search"), 1)
return self.send_text(page, "text/html; charset=utf-8")
# self.path stays the full request path, so the base class's redirect of a
# directory without its trailing slash keeps the project prefix
self.serve_from = notes
self.path = "/" + rest
self.serve_prefix = prefix
return super().do_GET()
def do_POST(self) -> None:
@@ -1351,7 +1376,8 @@ def serve(roots: list[Path], host: str, port: int) -> None:
def translate_path(self, path: str) -> str:
base = self.serve_from.resolve()
target = (base / unquote(urlsplit(path).path).lstrip("/")).resolve()
rest = unquote(urlsplit(path).path)[len(self.serve_prefix) + 1 :]
target = (base / rest.lstrip("/")).resolve()
return str(target if target.is_relative_to(base) else base / "__outside__")
def log_message(self, format: str, *args) -> None: