Per plugin-structure spec, plugin-internal references in component
files (commands, agents, skills) must use \${CLAUDE_PLUGIN_ROOT} so
they resolve correctly regardless of where the plugin is installed
(local repo, marketplace cache, npm-installed, etc.).
Updated:
- symphony/skills/symphony-init/SKILL.md: WORKFLOW.md template ref
- composer/skills/ui-system/SKILL.md: all six template refs in the
Write phase
- composer/skills/composer/SKILL.md: added a Templates section that
enumerates each phase's template path with \${CLAUDE_PLUGIN_ROOT},
plus the ADR template ref in phase 8
Composer's CLAUDE.md gains a clarifying note that templates/design-
system/SKILL.md and templates/interaction-design/SKILL.md are template
skeletons, not actual plugin skills — they're copied into a consuming
project's .claude/skills/ at install-time of the design system. Per
the auto-discovery rule (scans skills/ only), they are not loaded as
plugin skills.
Removed empty composer/agents/ directory (composer has no agents;
workers are owned by symphony).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both plugins now have .claude-plugin/plugin.json manifests and are
listed in the root marketplace.json so they can be installed via the
plugin marketplace UI alongside council-of-experts and genealogy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Earlier framing assumed each /schedule-fired tick was a cold start
needing on-disk reconciliation. The actual model is a long-lived
orchestrator session where /schedule fires ticks within the running
session — TaskList sees prior-tick tasks just fine. Reframed
accordingly, while leaving the door open to other trigger sources
(Discord channel, webhook, second user session) which DO need
cross-session reconciliation.
Reconciliation logic now has a fast path and a slow path:
- Fast path: task in our TaskList → it's ours → reconcile via TaskGet.
- Slow path: task NOT in our TaskList (another session, or our session
restarted) → check the worker's heartbeat file. Fresh → another
session owns it, leave alone. Stale or missing → consider abandoned,
retry; do not call TaskStop on a task we don't own.
Workers now write .symphony/heartbeats/<issue-id>.json on start,
refresh it at heartbeat_interval_ms cadence (default 60s), and delete
it on graceful exit. Stale heartbeats are how abandonment is detected
across sessions and after crashes.
Also added session_id to state.json running entries so cross-session
reconcilers know who spawned what.
WORKFLOW.md gains heartbeat_interval_ms and heartbeat_stale_ms knobs
under agent: with documented defaults.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sketch of a Claude Code port of openai/symphony — a daemon-style
orchestrator that polls an issue tracker, dispatches isolated worker
agents per issue, and reconciles state across ticks. Tracker-agnostic
by design: the project's CLAUDE.md documents how to talk to whatever
tracker is in use (GitHub Issues, Gitea, tracker CLI, Linear, etc.);
the plugin reads those instructions rather than shipping per-tracker
adapters.
Includes:
- /symphony-init for first-time setup (detects gh/gitea/tracker, writes
the tracker section into project CLAUDE.md, drops WORKFLOW.md, offers
to schedule the tick)
- /symphony-tick fired by /schedule cron entries (idempotent, silent,
never prompts)
- per-issue worker agent in isolated worktrees
- on-disk state in .symphony/state.json (survives cron cold starts)
Known divergences from the spec are documented in the plugin CLAUDE.md
(no streaming agent telemetry; no codex.* knobs; hooks run inside the
worker rather than the orchestrator).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>