Skip to content

Latest commit

 

History

407 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Symphony

Context-free agents that write the documents around code — issues, pull request bodies, feature plans, code reviews and repo audits — each to a fixed set of rules, and each stamped with the versions that produced it.

Every agent here is spawned in isolation. It cannot narrate work it remembers doing, because it did not do any: it has to read the diff, the repo, or the issue. That is the whole idea, and it is why what comes out describes what is actually there.

What is in it

Agent Writes
issue-scribe One standalone issue from a spec
pr-scribe A pull request body, from the real diff
plan-scribe A feature plan in four stages, from reading the repo
review-scribe A pull request review, drafted before it is posted
audit-scribe An audit of a section of a repo, with no pull request open

They share one list of checks in rules/review-checks.md, one writing style in rules/writing-style.md, and one stamp generator that names the versions behind every document.

Before a draft goes out, a second reader reads it with nothing else in front of it. The reader says what each sentence means and flags any it had to read twice. The scribe then rewrites those sentences, as rules/draft-reading.md sets out. Every scribe does this before its draft is posted, filed, published or returned.

A plan's page is built by bin/render-plan.sh from the plan issue. Every plan gets the same layout, from templates/plan-page.css, so no page is designed by hand.

Three commands drive them: /feature-plan, /review and /audit.

Setting it up

Needs git, zsh, jq, and the GitHub CLI gh already authenticated.

The quickest way, with no clone:

claude plugin marketplace add DYB-Development/symphony
claude plugin install symphony@symphony

Or clone it, which lets you run either install and edit the rules in place:

git clone https://github.com/DYB-Development/symphony.git
cd symphony
./install.sh --help

There are two ways to install from a clone, and you want one, not both. Installing both registers every hook twice, so whichever you run second refuses.

Linked

./install.sh

Links rules, agents and bin into ~/.claude, links each command into ~/.claude/commands one file at a time, and merges the hooks into ~/.claude/settings.json. Anything already at one of those paths is moved aside to <path>.backup first, and running it again changes nothing. Set CLAUDE_CONFIG_DIR to install somewhere other than ~/.claude.

Commands are run as /review. A file edited in this clone takes effect in the next session, with no reinstall, which is what makes this the mode to use while changing the rules themselves.

As a plugin

./install.sh --plugin

Registers this clone as a marketplace and installs it. Commands are namespaced, so a review is /symphony:review, and updates come through claude plugin update rather than git pull.

What the links do

Rules under ~/.claude/rules are read in every session on the machine, so the writing style and the development process apply everywhere, not only here.

The commands are linked file by file rather than as a directory, so your own commands can live alongside these.

What the hooks do

The plugin carries its hooks itself. A linked install merges the same entries into your settings, replacing any it put there before, and leaving hooks and settings that are not its own alone:

Event Runs Why
SessionStart writing-style-hook.sh Carries the writing rules and the banned phrase list into the session
SubagentStart writing-style-hook.sh A subagent receives no rules of its own, so it gets them here
PostToolUse decision-gate.sh arm Answering a question settles a choice, which has to be recorded
PreToolUse main-clone-gate.sh check Refuses an edit or a branch-changing git command aimed at a repo's main clone, which is kept for its owner
PreToolUse decision-gate.sh check Refuses a commit while that choice is still unrecorded
PreToolUse agent-progress.sh record Logs the step a subagent marks and each script it runs
SubagentStop agent-progress.sh record Logs that a subagent finished
Stop turn-sound.sh play Plays a sound when a session finishes its turn
Stop stray-test-workers.sh Stops any Rails test worker whose test run has gone, since nothing else will and it can run for days
Notification turn-sound.sh play Plays the same sound when a session asks permission to run a tool

Skip the hooks and the package still loads, but the writing rules never reach a subagent, the decision gate never fires, agents can work in the main clone, and no progress is logged, all without saying so.

To stop the sound in every open session, and to bring it back:

~/.claude/bin/turn-sound.sh off
~/.claude/bin/turn-sound.sh on

It plays with afplay, so it is silent on a machine without it.

To see which step each running scribe is on:

~/.claude/bin/agent-progress.sh

Two more steps

The rules keep three working-tree files that must never reach a commit — the decision log, the ticket reference and the resume bookmark. Add them to your global gitignore:

.decisions.md
.ticket
start_here.md

Then, once per repo you use the issue schema in, create its labels:

~/.claude/bin/issue-bootstrap.sh

Using it

Three commands start the work. Each one spawns exactly one scribe, and the scribe reads the repo rather than the conversation.

Run What happens
/feature-plan A request too big for one issue becomes a plan in four stages, filed as an issue with its units already written as issue bodies
/review <n> A pull request is reviewed against every check, drafted for you to read, and posted only when you say so
/audit One named section of a repo is measured and scored, and nothing is posted or filed

The other two scribes are spawned by name when you need them: issue-scribe writes one standalone issue from a spec, and pr-scribe writes a pull request body from the real diff. Ask for either and one is spawned.

Three files appear in a repo as you work, and none of them should be committed:

File Holds
.decisions.md A choice settled while working, which the pull request scribe renders into the body
.ticket The ticket the branch's hours are billed to
start_here.md Where to pick up, written only when stopping mid-issue

Two things worth knowing. Nothing posts without being read first — a review is drafted and rendered for a person, and an audit posts nothing at all. Nothing merges or approves — a review is always a comment.

Releasing a gem

One workflow in this repo releases every gem, so no gem holds a copy of the release steps and no release needs an API key typed in.

.github/workflows/gem-release.yml is called by a gem rather than run on its own. Registering it against a gem on rubygems.org lets GitHub hand RubyGems a short-lived token for that one run, which is what replaces the key.

Wiring a gem to it

Run the setup script against the gem's working copy:

~/.claude/bin/gem-release-setup.sh ~/projects/gems/tally

It writes .github/workflows/release.yml into the gem and prints the trusted publisher to register, field by field. Register it at the address the script prints, commit the workflow, and the gem is wired.

The fields matter in one non-obvious way: the workflow filename RubyGems wants is gem-release.yml, the workflow in this repo, not release.yml, the one in the gem. The token names the workflow that actually ran, and that is this one.

Releasing

Raise the version in the gem's version.rb, open a pull request, and merge it. The workflow reads the gemspec on every push to main, compares the version with rubygems.org, and releases when it is higher. A push that does not change the version is a run that says there is nothing to release and stops.

The release builds the gem, pushes it to rubygems.org, and then tags the commit it was built from. Pushing first is deliberate: a rejected gem leaves no tag behind, so the next run is a clean retry. Nothing is done by hand and nothing is typed in.

The same run can be started from the gem's Actions tab, under Release, with Run workflow. It reads the gemspec and compares it with rubygems.org exactly as a merge does, so a version already published is still a run that stops. Reach for it when a release failed and the fix was somewhere other than the gem, or when a version was merged before the gem was wired to the workflow.

When it fails

Before publishing, every check runs and one run names every problem it found rather than the first. It refuses a version whose tag already exists, a gemspec Ruby cannot read, and a push host trusted publishing cannot authenticate against.

A failed release opens an issue in the gem's own repository, labelled release-failure, carrying what the release printed before it stopped. A second failure of the same version comments on that issue rather than opening another.

Configuring it

Everything works unset. These change what the package reads, and they belong in your own shell or settings rather than in a file here, so an update never overwrites them.

Variable Changes
SYMPHONY_OVERLAY_DIR Where your own rules files are read from, instead of ~/.config/symphony/rules
CLAUDE_CONFIG_DIR Where the installer links to, instead of ~/.claude
CLAUDE_WRITING_STYLE_FILE One writing rules file, ahead of the overlay and the shipped one
CLAUDE_BANNED_PHRASES_FILE One phrase list, on the same terms
AUDIT_RUBRIC_FILE The cost bands and horizons an audit is scored against
ISSUE_BOOTSTRAP_OWNERS The accounts issue-bootstrap.sh --all-repos syncs labels across
SYMPHONY_IDENTIFIERS A file of terms no shipped file may name, checked by the suite
READ_DRAFT_SMOKE Set to 1 and the suite runs one real read through claude
RENDER_PLAN_SMOKE Set to a plan issue as owner/repo#N and the suite renders it through GitHub

ISSUE_BOOTSTRAP_OWNERS is empty by default and --all-repos does nothing until you set it, because that flag writes to every non-archived repo of every account named.

SYMPHONY_IDENTIFIERS is unset by default, so the suite reports nothing to check and passes. A list of names cannot be kept here without carrying the names the check exists to keep out.

Making it yours

Every rules file this package ships can be replaced without editing this package, so claude plugin update and git pull never overwrite your version.

Put a file of the same name in ~/.config/symphony/rules/ and it wins:

mkdir -p ~/.config/symphony/rules
cp rules/pr-body.md ~/.config/symphony/rules/pr-body.md

Edit that copy. Every session and every scribe is told at startup that the overlay exists and that a file in it replaces the shipped one, so the pull request scribe now follows your template and everything else follows the rules here.

To change Replace
How a pull request body reads pr-body.md
What an issue must contain, and its labels issue-schema.md
What a review looks for review-checks.md
How a review is written and posted pr-review.md
What an audit measures and reports repo-audit.md
What an audit's findings cost audit-rubric.json
The shape of a feature plan feature-plan.md
How everything is written writing-style.md
Who a draft is read for before it goes out draft-reading.md
The phrases nothing may use banned-phrases.txt
The development process followed develop_process_rules.md
When a decision is recorded decision-log.md

Replace a whole file, not part of one — the overlay swaps files, it does not merge them. Take a copy of the shipped one and edit it, so nothing a scribe expects to find goes missing.

Set SYMPHONY_OVERLAY_DIR to keep the overlay somewhere else, such as a directory your team shares.

Running the tests

./run_tests.sh

Every file named *_test.zsh anywhere under the repo root is a suite and is picked up with no registration.

No suite calls the network by default. READ_DRAFT_SMOKE=1 ./run_tests.sh also sends one sentence to a real reader, which needs claude logged in and is billed like any other run. RENDER_PLAN_SMOKE=owner/repo#N ./run_tests.sh renders that plan issue's page through GitHub's markdown service.

Licence

MIT. See LICENSE.

About

Context-free scribes that write issues, pull request bodies, feature plans, code reviews and repo audits to a fixed set of rules.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages