Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ This file covers working *on* scc. The product's own rules and methodology are n

Note: this repo is not itself an scc workspace (no harness directory, `specs/`, `plans/`, or `docs/` are committed) — those trees only exist in workspaces the binary scaffolds, and in test temp dirs.

**Status: v0.4.0-shaped.** Everything through `design/plan.md` phase 10 is built and green: scaffolding (`init`), artifact creation (`spec`, `plan`), and all eight validators behind `scc validate`. `init` also scaffolds the six knowledge-base skills named in `design/orchestration.md` §6 — one per `docs/` artifact a validator checks, plus `prd` — each with a `scc-`-prefixed slash command derived from the same list (`assets.KnowledgeSkills`), wherever the harness has a command surface.
**Status: v0.4.0-shaped.** Everything through `design/plan.md` phase 10 is built and green: scaffolding (`init`), artifact creation (`spec`, `plan`), and all eight validators behind `scc validate`. `init` also scaffolds the seven skills named in `design/orchestration.md` §6 — the six knowledge-base authors (one per `docs/` artifact a validator checks, plus `prd`) and the `plan-run` workflow skill — each with a `scc-`-prefixed slash command derived from the same list (`assets.Skills()`, which is `KnowledgeSkills` + `WorkflowSkills`), wherever the harness has a command surface.

Three things landed after phase 10 and all are documented in `design/orchestration.md` §6 and §12:

Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ Installed globally (`npm i -g @protonspy/scc`) the same commands are just `scc i

### Three harnesses, one methodology

The same rules, review agents, and knowledge-base skills are scaffolded into
whichever tool you work in — only the paths and the frontmatter dialect change.
The same rules, review agents, and skills — the knowledge base's authors, plus
`plan-run`, which drives a whole plan group by group — are scaffolded into whichever
tool you work in. Only the paths and the frontmatter dialect change.

| | Claude Code | Codex | opencode |
|---|---|---|---|
Expand Down
87 changes: 83 additions & 4 deletions design/orchestration.md
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ merge base stays exact.

**Codex ships no slash commands, deliberately.** Its custom prompts are user-global
and now deprecated in favor of skills, so there is nothing project-scoped to write
and scc does not write into anyone's home directory. The six skills are the whole
and scc does not write into anyone's home directory. The skills are the whole
surface there, which is what Codex itself recommends.

**Codex and opencode share `AGENTS.md`, and the first one initialized wins it.** A
Expand All @@ -431,10 +431,10 @@ rewriting a file the user owns.
`scc update` keeps both trees current regardless, because it works from each
harness's own manifest rather than from the entry file.

### Which skills ship — the authors of `docs/`
### Which skills ship — the authors of `docs/`, plus one runner

Six, and the rule that picks them is mechanical: **a skill ships for each `docs/`
artifact a validator checks, and for nothing else.**
Seven, in two lists, and each list has its own rule. The first is mechanical: **a
skill ships for each `docs/` artifact a validator checks, and for nothing else.**

| Skill | Authors | Checked by |
|---|---|---|
Expand All @@ -455,6 +455,61 @@ delivery are rules under `.claude/rules/` (§3, §7, §9) — read when the conc
live. csdd shipped `tdd-cycle`, `unit-cycle`, and `verify-change` as skills; here
that would be a second copy of a rule, and the copy is the one that goes stale.

**The one exception, and the line it draws — `plan-run`.** The second list holds
skills that *run* the methodology rather than describe it, and it holds exactly one.

| Skill | Runs | Owned by no rule |
|---|---|---|
| `plan-run` | a whole `plans/<name>.md`, group by group | the loop **across** units of work |

The rule above is what makes it admissible rather than a leak. §9's delivery sequence
ends at one merged pull request, because that is where one unit of work ends — a spec,
or a plan's leaf. A plan is many of those in sequence, and running one to the end means
picking the next group, branching it from the merge the previous group produced,
holding CI green because every later group inherits that base, and recovering the
position from `main` when a session dies mid-plan. None of that is in any rule, and
none of it belongs in one: a rule is read when a concern is live, and this is a
procedure a person starts on purpose and lets run.

So the line is not "methodology may now be a skill." It is: **a skill may hold a
procedure no rule holds; it may never restate one that a rule does.** `plan-run`
therefore cites `delivery.md` for the single-group mechanics instead of copying
them — if it ever grows its own account of how to open a PR, it has crossed back over.

**It has its own kickoff, and that is not a violation of §4 — it is §4 applied.** The
rule there is *ask once, before doing anything, and record the answer*; the mistake it
forbids is asking repeatedly, not asking at all. A plan's recorded `autonomy` and `ci`
were given when the plan was **written**. Starting a loop that will open and merge pull
requests for hours is a different and larger thing to agree to, and it raises a
question authoring never had — worktree per group, or the checkout you are in.

So `plan-run` asks three questions, once, **after reporting the groups**, because the
answers are only meaningful to someone who can see what they are agreeing to. Anything
already in the frontmatter is shown as a proposed answer to confirm, never as a
decision already taken. Four keys go back into the plan, and a resumed session reads
them instead of asking again:

| Key | Values | Decides |
|---|---|---|
| `autonomy` | `auto` · `gated` | straight through, or stop at each group boundary |
| `worktree` | `per-group` · `in-place` | a worktree per group, or a branch in the current checkout |
| `ci` | `wait` · `no-wait` | whether the checks have to settle before the merge |
| `merge` | `auto` · `manual` | whether the loop merges, or stops at the PR for the developer |

`worktree` and `merge` are new, plan-only, and validated by `plan` on exactly the terms
`autonomy` and `ci` are — **only when present**, since a plan no loop has run over is
not a plan with a defect. They are validated at all because the skill writes them and
every later session reads them, so `worktree: yes` would silently decide how the next
ten groups get built.

The tempting alternative was for the loop to **override** `ci: no-wait`, on the
argument that the next group branches from the merge and an unverified base poisons
everything after it. Rejected: the argument is sound and the developer is still the one
who gets to weigh it. Stating the cost at the moment of the question is how a tool
respects a decision it disagrees with; overriding is how it stops being trusted.
`no-wait` therefore stays available inside a loop, and the skill says out loud what it
buys and what it costs before accepting it.

**One slash command per skill, namespaced `scc-`.** A skill is model-invoked through
its description; a command is the human saying *now*. Both are cheap, they are derived
from one list so they cannot drift, and the prefix is not decoration — slash commands
Expand All @@ -465,6 +520,30 @@ The skills carry the *procedure and the judgment*; the format they produce stays
`.claude/rules/knowledge-base.md`, stated once. A skill that restated the format would
be the same duplication the methodology rules are being kept out of skills to avoid.

**How a skill names a rule — the path from the root, and never a link.** Every
template writes a rule's path the same way, `<harness>/rules/<rule>.md`, expanded at
render time so it is correct in each of the three trees. A `SKILL.md` is no exception,
but it must write that path as **inline code rather than as a Markdown link**, and the
two constraints that force it point in opposite directions:

- An agent reads files from the workspace root. `<harness>/rules/delivery.md` is the
path it can act on directly; a relative walk like `../../rules/delivery.md` is one
the agent has to resolve against a directory it was never told it was in.
- `skill` resolves a Markdown link **against the skill's own directory** and holds it
to the Agent Skills spec's one-level-deep rule. The same path written as a link
would resolve to `<harness>/skills/<n>/<harness>/rules/…`, and scc would report two
findings against its own scaffold.

Inline code satisfies both: correct to read, and not a link, so the check that would
misjudge it never sees it. This was got wrong once in the other direction — skills
linking relatively, which conformed but handed the agent a path it could not use.

Guarded from both sides, because neither guard alone is enough.
`TestFreshArtifactsPassTheirOwnValidators` catches the link form, since a validator
firing on scc's own output is the one defect that teaches users to disbelieve the
other seven. `TestSkillsNameRulesByTheirHarnessPath` catches what no validator can
see — a `../..` sitting inside a code span is silently useless to whoever reads it.

### The four seeded anchors — `docs/` is not an empty tree

`init` writes `docs/glossary.md`, `docs/stack.md`, `docs/wiki/index.md`, and
Expand Down
39 changes: 30 additions & 9 deletions internal/assets/assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ import (
// report in a fixed shape the orchestrator can act on.
// 5: one template set for three harnesses — Claude Code, Codex, opencode.
// 6: the knowledge base states the stack check without naming an ecosystem.
const Version = "6"
// 7: the plan-run workflow skill and its command — running a whole plan, group by
// group, is a procedure with an entry point rather than a rule read in passing.
const Version = "7"

// The embedded tree. "all:" so nothing is silently dropped for having a name the
// default embed pattern skips.
Expand Down Expand Up @@ -148,11 +150,10 @@ func Workspace(h paths.Harness) []File {
Kind: Agent,
})
}
// The knowledge base's authors. Every one of these produces an artifact
// `scc validate` already checks — a workspace that ships the eight validators
// and no skill teaching the formats would demand conformance to documents
// nobody was told how to write.
for _, skill := range KnowledgeSkills {
// The knowledge base's authors, and the workflow skills that run the
// methodology rather than describe it. Why each list holds what it holds is on
// the lists themselves; here they are one set of files on identical terms.
for _, skill := range Skills() {
set = append(set, File{
Name: "skills/" + skill + "/SKILL.md",
Rel: under(h.SkillsSeg, skill, "SKILL.md"),
Expand Down Expand Up @@ -224,16 +225,36 @@ func Seeds() []Seed {
// review is where a cold context is worth paying for, and authorship is not.
var ReviewAgents = []string{"code-review", "security-review"}

// KnowledgeSkills names the skills scc ships, each one the author of a `docs/`
// artifact a validator checks. Both the skill directory and its slash command are
// derived from this list, so the two cannot drift apart.
// KnowledgeSkills names the skills that author a `docs/` artifact a validator
// checks — one per artifact, so a workspace shipping the eight validators never
// demands conformance to a document nobody was told how to write.
//
// The methodology is deliberately absent from it: the cycles, verification, and
// delivery are rules under the harness's rules directory, read when the concern
// is live. A skill restating a rule is a second copy of one fact, and the copy
// goes stale.
var KnowledgeSkills = []string{"adr", "codewiki", "glossary", "prd", "stack", "wiki"}

// WorkflowSkills names the skills that drive the methodology instead of authoring a
// document. There is one, and the bar for a second is the rule above: if a skill
// would restate what a rule already says, it should be the rule.
//
// plan-run clears that bar because it holds what no rule does — the loop *across*
// units of work. delivery.md ends at one merged pull request, which is exactly where
// one unit of work ends; running a whole plan means picking the next group, branching
// it from the merge the last group produced, and recovering the position from `main`
// when a session dies mid-plan. That is a procedure, and a procedure a person invokes
// needs an entry point they can type.
var WorkflowSkills = []string{"plan-run"}

// Skills is every skill scc ships, knowledge first. Both the skill directory and its
// slash command are derived from this one list, so the two cannot drift apart, and a
// skill added to either half above reaches workspaces that already exist through
// `scc update` on the same terms as any other managed file.
func Skills() []string {
return append(append([]string{}, KnowledgeSkills...), WorkflowSkills...)
}

// commandPrefix namespaces the scaffolded slash commands.
const commandPrefix = "scc-"

Expand Down
53 changes: 43 additions & 10 deletions internal/assets/assets_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package assets

import (
"path"
"strings"
"testing"

Expand Down Expand Up @@ -376,11 +377,11 @@ func TestAgentProseIsSafeToEmbedInTOML(t *testing.T) {
}
}

// Every skill in KnowledgeSkills ships a SKILL.md in every harness, and its slash
// command wherever the harness has a command surface. The two are derived from one
// list so they cannot drift, and this is the assertion that keeps the derivation
// honest — including for Codex, where the answer is deliberately no commands at
// all.
// Every skill in Skills() — knowledge and workflow alike — ships a SKILL.md in every
// harness, and its slash command wherever the harness has a command surface. The two
// are derived from one list so they cannot drift, and this is the assertion that
// keeps the derivation honest — including for Codex, where the answer is
// deliberately no commands at all.
func TestEverySkillShipsWithItsCommand(t *testing.T) {
for _, h := range paths.Harnesses() {
skills, commands := map[string]bool{}, map[string]bool{}
Expand All @@ -394,19 +395,20 @@ func TestEverySkillShipsWithItsCommand(t *testing.T) {
commands[name] = true
}
}
if len(skills) != len(KnowledgeSkills) {
t.Fatalf("%s: %d skills, want %d", h.ID, len(skills), len(KnowledgeSkills))
all := Skills()
if len(skills) != len(all) {
t.Fatalf("%s: %d skills, want %d", h.ID, len(skills), len(all))
}
wantCommands := len(KnowledgeSkills)
wantCommands := len(all)
if h.CommandsSeg == "" {
wantCommands = 0
}
if len(commands) != wantCommands {
t.Fatalf("%s: %d commands, want %d", h.ID, len(commands), wantCommands)
}
for _, name := range KnowledgeSkills {
for _, name := range all {
if !skills[name] {
t.Errorf("%s: %s is in KnowledgeSkills and ships no SKILL.md", h.ID, name)
t.Errorf("%s: %s is in Skills() and ships no SKILL.md", h.ID, name)
}
if wantCommands > 0 && !commands[commandPrefix+name] {
t.Errorf("%s: %s ships no %s%s command", h.ID, name, commandPrefix, name)
Expand All @@ -415,6 +417,37 @@ func TestEverySkillShipsWithItsCommand(t *testing.T) {
}
}

// A skill names a rule by its path from the workspace root — `.claude/rules/x.md`,
// expanded per harness — and never by a relative walk out of its own directory.
//
// Two things have to hold at once, and only one form satisfies both. An agent reads
// files from the root, so the harness-relative path is the one it can act on. But
// `skill` resolves a Markdown link against the skill's own directory and holds it to
// the Agent Skills spec's one-level-deep rule, so writing that same path as a *link*
// would make scc fail its own validator. Inline code is the form that is both correct
// to read and invisible to the link check.
//
// TestFreshArtifactsPassTheirOwnValidators in internal/cli catches the link half of
// this; this test catches the other half, which no validator can see — a `../..` that
// stays inside a code span is silently useless to the agent reading it.
func TestSkillsNameRulesByTheirHarnessPath(t *testing.T) {
for _, h := range paths.Harnesses() {
for _, f := range Workspace(h) {
if !strings.HasSuffix(f.Rel, "/SKILL.md") {
continue
}
out, err := Render(h, f)
if err != nil {
t.Fatalf("%s %s: %v", h.ID, f.Rel, err)
}
if strings.Contains(out, "../") {
t.Errorf("%s %s: reaches out of the skill directory with \"../\"; name the rule as %s/<rule>.md",
h.ID, f.Rel, path.Join(h.Dir, h.RulesSeg))
}
}
}
}

// The Agent Skills contract is what `scc skill validate` enforces on everyone else,
// and the two fields it can break loading on are checked here at the source. The
// full conformance run happens against a scaffolded workspace in internal/cli — a
Expand Down
22 changes: 22 additions & 0 deletions internal/assets/templates/commands/scc-plan-run.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
description: Run a plan under plans/ to completion group by group — worktree, implement, review, PR, CI, merge, then the next group
argument-hint: [the plan, plus any standing instruction for every group]
---

Use the `plan-run` skill.

Plan, and how to run it: $ARGUMENTS

Read the plan and name the groups back, numbered and in order, before writing any
code. The order is the one thing the user can correct cheaply now and expensively
after three merges.

Then ask how this loop should run — automatic or gated, a worktree per group or the
current checkout, and what happens once each PR is open. Three questions, one
exchange, after they can see the groups they are agreeing to. **These are the
developer's calls.** Anything the plan's frontmatter already records is a proposed
answer to confirm, not a decision already made.

Anything said above about *how* to implement is a standing instruction: it applies
to every group, and you carry it into each one explicitly rather than trusting it to
survive from the first group to the last.
4 changes: 2 additions & 2 deletions internal/assets/templates/entry.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ plans/<name>.md one file: structure, plus a checklist and/or spec references
docs/ the knowledge base — wiki, adr, codewiki, glossary, stack

{{.Rules}}/ — the methodology above
{{.Skills}}/ — how to author each part of docs/, invoked when it applies
{{.Skills}}/ — how to author each part of docs/, and how to run a plan to the end
{{.Agents}}/ — code-review and security-review, run before the PR
{{- if .HasCommands}}
{{.Commands}}/ — the same skills on demand, as /scc-wiki, /scc-adr, /scc-prd, …
{{.Commands}}/ — the same skills on demand, as /scc-plan-run, /scc-wiki, /scc-adr, …
{{- end}}
```

Expand Down
2 changes: 1 addition & 1 deletion internal/assets/templates/skills/adr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: Record a decision that is hard to reverse as a numbered ADR under d

You write the record of a decision, so that the reasoning survives the people who
were in the room. The filename shape, the frontmatter, and the citation form are in
[knowledge-base.md](../../rules/knowledge-base.md). This skill is when to write one
`{{.Rules}}/knowledge-base.md`. This skill is when to write one
and how to write one worth reading.

## Is this an ADR?
Expand Down
2 changes: 1 addition & 1 deletion internal/assets/templates/skills/codewiki/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ is about. The citation is what makes this different from a wiki page: it goes st
loudly. When the code moves, `scc validate` fails instead of the page quietly
becoming a lie.

The format is in [knowledge-base.md](../../rules/knowledge-base.md). This skill is
The format is in `{{.Rules}}/knowledge-base.md`. This skill is
the procedure and the judgment.

## The citation form
Expand Down
2 changes: 1 addition & 1 deletion internal/assets/templates/skills/glossary/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ working in parallel produce three names for one thing inside a week, and every o
of them ends up in code, requirements, and the wiki, where nobody can tell whether
two documents are describing the same concept or two different ones.

The entry format is in [knowledge-base.md](../../rules/knowledge-base.md). This
The entry format is in `{{.Rules}}/knowledge-base.md`. This
skill is when to reach for it and how to decide.

## The entry
Expand Down
Loading
Loading