Releases: cskwork/oh-my-symphony
Release list
v0.13.0
v0.13.0 — reliability audit batch + live-E2E lifecycle fixes
The 2026-07-09 agent-flow reliability audit (AF-01..AF-16) is fully resolved,
plus two lifecycle defects found by a live opencode file-board E2E loop.
Reliability audit (AF-01..AF-16)
- AF-01 Identity-safe worker exit: a force-ejected zombie's cleanup can no
longer eject the fresh retry entry installed under the same issue id. - AF-02 Force-eject now records and kills the backend process group for
every backend kind (claude/opencode/gemini/agy/kiro/pi), not just Codex. - AF-03 Resumed workers get a fresh stall window; long pauses no longer
burn a retry on the first reconcile tick. - AF-04 State PATCH on a running ticket returns 409 without mutation.
- AF-05 Plain/Gemini/Claude backends expose canonical completion previews;
silent exit-0 empty turns fail instead of looping. - AF-06 Board scans ignore legacy
.tmp-*.mdfiles; startup sweeps only
Symphony-owned temps and spares operator files. - AF-07 Reconcile isolates per-entry force-eject errors; cancelled paused
zombies still eject. - AF-08
stop()bounds cancellation-resistant worker drain and finalizes
their leases before the run registry closes. - AF-09 Corrupt Codex stdout closes the backend, fails later turns fast,
and a failed reap stays retryable by a later stop. - AF-10 Dead-owner lease reclaim kills any recorded live process group
(with a persistedreclaimingfence) before the row is redispatchable;
non-positive persisted pids are rejected at the signalling boundary. - AF-11 Continuous-improvement lifetime cap warns once;
require_idle_board
blocks CI/worker overlap including terminal-persist work. - AF-12 Tracker parse failures warn; duplicate ids collapse
deterministically; delete serializes with mutation; a running id missing
from refresh becomes a visible degraded state. - AF-13 Rewind budget applies to custom and non-English pipelines via
configured active-state order. - AF-14 Closed by research: Codex 0.130/0.144 schemas both require
tokenUsage.lastand.total; no speculative production branch. - AF-15 Dispatch state no longer retains completed ids;
stop()clears
diagnostics. - AF-16 First/continuation/phase-rebuild prompts share one lifetime
turn-budget basis (max_total_turnsdocumented in workflow examples).
Live-E2E lifecycle fixes
- Done-squash preserves merged lineage:
auto_commit_on_donenow squashes
ontomerge-base(HEAD, merge target)instead of the fork point, so a branch
already merged by Verify is no longer rewritten onto an orphan lineage — the
post-Done fallback merge no longer hits guaranteed add/add conflicts that
demoted healthy Done tickets to Blocked. - Verify evidence validation matches the shipped contract: evidence cells
citing an existing artifact plus a qualifier (or several backticked
artifacts) are accepted; Security Audit rows with ann/aresult need no
artifact. Fabricated citations and prose-only evidence still hard-rewind. - Verify stage prompts now show valid/invalid evidence-cell examples.
Hardening
- OpenCode telemetry and worktree setup hardening.
Verification
Full suite 1371 passed / 5 skipped; ruff and pyright clean. The complete
pipeline (Todo → In Progress → Verify → Learn → Done with merge gate) was
exercised end-to-end three times with a real opencode agent building a working
app; the final two runs completed with the Done ticket surviving the terminal
grace and the fallback merge succeeding. Decision log:
docs/changelog/changelog-2026-07-10.md.
v0.12.0 - Continuous improvement and blocked recovery
Continuous improvement heartbeat and blocked-board recovery minor release.
Highlights:
- Added the continuous improvement heartbeat contract/runtime so Symphony can periodically re-check the integrated baseline and file follow-up work as normal Kanban tickets.
- Added blocked-ticket Root Cause Analysis (RCA) recovery. RCA means Symphony opens a separate ticket to investigate and fix why a source ticket became blocked; the source ticket stays Blocked while that RCA ticket resolves the blocker, then reopens to Todo only after successful RCA completion without manual-intervention markers.
- Made Human Review intervention-only in prompts and contracts; normal successful Learn/Done paths no longer route through Human Review by default.
- Hardened file-board recovery for legacy Human Review cards, delimiterless ticket files, stale Blocked RCA notes, idle paused RCA resume, and retryable backend/network failures.
- Added dispatch preflight coverage for total turn-budget misconfiguration and AGY state-directory writability; AGY now defaults to fresh turns unless continuation is explicitly enabled.
- Fixed the reconcile test harness so the pre-push full pytest gate no longer hangs on terminal-cleanup grace behavior.
Verification:
- Real OpenCode E2E passed after the RCA recovery changes.
- Feature branch pre-push hook: 1277 passed, 2 skipped.
- dev merge push hook: 1279 passed, 2 skipped.
- main merge push hook: 1279 passed, 2 skipped.
- v0.12.0 release commit push hook: 1279 passed, 2 skipped.
- Known residual warning: aiohttp NotAppKeyWarning in two startup tests.
v0.11.0 - OpenCode terminal cleanup
OpenCode terminal cleanup minor release.
Highlights:
- Fixed OpenCode terminal reconciliation so terminal cards stop after a bounded grace period even when OpenCode backend heartbeats continue.
- Included the checked-in SMA-20 smoke harness lint cleanup from dev.
- Fixed the local pre-push quality gate to strip Git hook environment before tests create nested temporary repositories.
Verification:
- OpenCode E2E reached Human Review and /api/v1/state reported running=0, retrying=0 after terminal reconciliation.
- ruff check .
- pyright --pythonpath /Users/danny/Documents/PARA/Resource/symphony-multi-agent/.venv/bin/python
- pytest -q: 1153 passed, 2 skipped.
- Real pre-push hook passed on both main and v0.11.0 tag pushes.
v0.10.1 - Prompt compaction default-on
v0.10.1 - Prompt compaction default-on
This patch release turns Symphony's prompt context compaction on by default.
Changed
agent.compact_issue_contextnow defaults totruewhen a workflow omits the setting.- The workflow builder fallback now matches the typed config default, so file, Jira, and other workflow configs get the same default behavior.
- Existing workflows can still opt out with:
agent:
compact_issue_context: falseWhy it matters
Prompt compaction keeps the worker's first-turn prompt focused on the original scope plus the latest state-relevant ticket sections. That reduces repeated stale ticket history without removing the raw ticket: file-backed workers still receive the full ticket path for audit.
This is especially useful for long-running Codex/Symphony cards where old implementation notes, old QA failures, and large logs can otherwise dominate later prompts.
Jira workflow compatibility
Jira-backed workflows use the same agent.compact_issue_context config path. A local jira-symphony workflow was checked and explicitly set to true. Running services started on older Symphony versions still need to be restarted or upgraded before relying on the new package default.
Verification
rtk pytest tests/test_workflow.py -k "compact_issue_context" -q-> 3 passed.rtk pytest tests/test_prompt_context.py tests/test_prompt.py::test_compact_issue_context_changes_rendered_prompt_description -q-> 8 passed.python -m py_compile src/symphony/workflow/config.py src/symphony/workflow/builder.pypassed.git diff --checkpassed.- Full suite passed on
devpre-push: 1110 passed, 2 skipped. - Full suite passed on
mainlocally and pre-push: 1110 passed, 2 skipped. - Fresh remote-clone Codex E2E used a workflow that intentionally omitted
agent.compact_issue_context; loaded config reportedcompact_issue_context=True. - Clean E2E release gate
CODEX-E2E-005reachedHuman Review, exited withworker_exit reason=normal, API reportedrunning=0andretrying=0, and no contract failure/warning headings appeared. - E2E proof artifact contained
compact_issue_context default: true.
Notes
Earlier temporary E2E tickets proved Codex could finish but exposed harness prompt contract issues. They were not used as the release gate. The final release gate is CODEX-E2E-005.
v0.10.0 - Codex E2E reliability hardening
v0.10.0 - Codex E2E reliability hardening
Minor release from v0.9.3 to v0.10.0.
This release focuses on making long-running Symphony boards easier to trust:
workers get better prompt context, operators get clearer cost and dependency
signals, workspace reuse is guarded before hooks run, and the 9999 web app no
longer fails on YAML timestamp frontmatter in issue details.
Highlights
- Stage-local turn watchdogs: workflows can set
agent.max_state_turns_by_stateso reasoning-heavyIn Progresswork and
stricterVerify/Learngates can use different same-state turn limits. - Token attention without surprise stops: productive zero-token reports and
configuredagent.token_attention_threshold_by_statevalues now surface as
operator attention, while high token use remains telemetry unless an explicit
hard cap is configured. - Stage-aware prompt context compaction:
agent.compact_issue_context: true
preserves user scope, the newest evidence/failure context, and a full-ticket
path while dropping repeated history from worker prompts. Measured copied-board
Verify prompt size:TASK-005went from 5,453 to 3,944o200k_basetokens
(27.7% smaller). - Workspace ownership protection: Symphony records workspace owner sidecars
under the workspace root and blocks foreignTASK-*reuse before
after_createhooks can mutate the wrong project. - Hook failure evidence:
after_createstdout/stderr and a JSON manifest are
persisted outside the ticket worktree, so failed setup remains inspectable
even when the worktree is cleaned up. - Stricter contract rewind scope: Verify evidence failures now carry structured
rows, table row numbers, and expected artifact path shapes into the rewind
prompt instead of a vague failure note. - Dependency-aware file-board dispatch: unresolved dependency IDs now remain
visible asblocked_dependencybeyondTodo, so held work does not silently
run. - 9999 admin UI documentation: README surfaces now lead with the browser admin
UI and a sanitized screenshot, while still keeping the terminal TUI visible.
Fixes
- Fixed
GET /api/v1/issues/<ID>for issue files whose YAML frontmatter parses
unquoted timestamps into Pythondateordatetimeobjects. This fixes the
Could not load CODEX-E2E-001: Object of type datetime is not JSON serializabledrawer failure. - OpenCode 1.17 token accounting now follows
step_finish.part.tokens, handles
nested cache and reasoning fields, and avoids double-counting explicit cache
totals. - File ticket parsing auto-heals shallow accidental indentation on canonical
metadata keys such asupdated_at, preventing post-turn refresh failures from
malformed agent-authored YAML. symphony doctornow warns on masked setup commands such as|| trueand
pipedtailby default; workflows can opt into fatal warnings with
hooks.fail_on_warning_patterns: true.
Compatibility Notes
- Prompt compaction is opt-in and remains off unless a workflow sets
agent.compact_issue_context: true. - Token attention thresholds do not block or move tickets by themselves.
symphony doctorwarning-pattern checks are warnings by default.- Verify contract checks are stricter about placeholder or prose-only evidence;
use durable evidence files such asdocs/<ID>/qa/...instead ofn/a,-,
or "validated in source" table cells.
Verification
- Focused release-prep checks on
dev:
rtk pytest tests/test_cli_main_routing.py::test_version_flag_prints_version_and_exits_zero tests/test_webapi.py::test_issue_detail_serializes_unquoted_frontmatter_timestamps -q
-> 2 passed. devpre-push CI-parity gate:
1,109 passed, 2 skipped, 2 warnings.mainfull local gate after fast-forward:
rtk pytest -q-> 1,109 passed, 2 skipped.mainpre-push CI-parity gate:
1,109 passed, 2 skipped, 2 warnings.- Prior fresh remote-clone Codex E2E rerun reached
Human Reviewon
CODEX-E2E-002withworker_exit reason=normal; the issue-detail API
returned string timestamps.
Commit Scope
db55768e6bc2d3d8283ec238a08d566c6e55da5b-
docs(readme): showcase 9999 admin ui6fe480b6abc9c2844d35d9974b7cb52466bee10b-
feat: harden symphony token reliability8710ec736840a08b07e24013681af9f770639a0a-
fix: serialize issue detail frontmatter timestamps1c48d9bb73d82464907e0bfdad69884895fc46b1-
chore: prepare v0.10.0 release
Tag target: 1c48d9bb73d82464907e0bfdad69884895fc46b1.
v0.9.3 — OpenCode telemetry, file-board recovery, and operator UI fixes
2026-07-04 - Fix G2 empty-response-loop false-positive on OpenCode
Goal
Stop the G2 empty-response-loop guard from falsely blocking healthy OpenCode
tickets. A ticket (observed in Learn) was moved to Blocked with
empty_response_loop budget exceeded (consecutive_empty_turns=3, threshold=3)
despite the agent producing real output every turn.
Root cause
G2 counts a turn as empty when RunningEntry.current_turn_message is blank at
EVENT_TURN_COMPLETED (orchestrator/core.py). current_turn_message is only
populated when _preview_from_payload(payload) returns text, and that helper
reads the preview keys message / lastMessage / text / summary / item.
OpenCode's EVENT_TURN_COMPLETED payload delivered its response text only under
result / response (backends/opencode.py) — keys the preview helper never
inspects. So every OpenCode turn looked empty, the counter climbed monotonically
regardless of real work, and G2 escalated after exactly 3 turns.
Why it surfaced now: the recent OpenCode liveness-heartbeat fix (stall-loop)
stopped long turns from being stall-cancelled. Before it, each stall-cancel
minted a fresh RunningEntry (counter reset to 0), so turns never accumulated to
3 on one entry. Removing the false stall-kill exposed the latent G2 miscount —
same bug class (OpenCode's batch-per-turn shape not fitting an event model built
for streaming backends), next guard.
Decisions
1. Emit the response under a preview key from the backend
run_turn's EVENT_TURN_COMPLETED payload now carries message (= the response)
alongside the existing result / response. _preview_from_payload reads
message first, so a productive turn populates current_turn_message and resets
the G2 counter. A genuinely empty turn (response == "") still yields no preview
text, so real empty-loops are still caught. Bonus: the OpenCode TUI preview
(last_codex_message, same code path) was always blank before and now shows the
turn's response.
- Rejected: raising
EMPTY_TURN_LOOP_THRESHOLD(the operator's first
hypothesis). It is a band-aid — OpenCode would still false-block after N turns
instead of 3, and a higher threshold weakens genuine empty-loop detection for
every backend. It does not address the miscount. - Rejected: teaching
_preview_from_payloadto readresult/response.
That is shared code on the hot event path for all backends;result/
responsecan carry non-message content for other drivers, risking masked
empty-turns elsewhere. The backend-local fix keeps the blast radius to OpenCode
and matches the sibling heartbeat fix's "backend quirks live in the backend"
precedent.
Verification
backends/opencode.py— 1 key added to the turn-completed payload.- New
tests/test_backends.py::test_opencode_turn_completed_payload_carries_message_for_preview
asserts the emitted payload carriesmessage. - New
tests/test_orchestrator_dispatch.py::test_g2_opencode_shaped_payload_resets_only_with_message_key
drives the real_on_codex_eventpath:result/response-only payloads still
count as empty (climb 1 -> 2); addingmessageresets the counter to 0. - Full suite: 1058 passed, 2 skipped. No shared code touched;
EMPTY_TURN_LOOP_THRESHOLDunchanged at 3.
Patch bump 0.9.1 -> 0.9.2 (fix restores intended behavior).
Follow-up (0.9.3) — the dominant cause: opencode 1.17 schema drift
Live verification against the paused jira-symphony board (TASK-001, blocked
in Learn by exactly this loop) showed 0.9.2 alone was necessary but not
sufficient. Every agent_turn_completed reported input_tokens=0 output_tokens=0 with an empty last_message — yet opencode had done real work
(glm-5.2, 8 agentic steps, 27-file commits). Input tokens cannot be zero for a
12 KB prompt: Symphony's opencode backend was parsing nothing out of
opencode 1.17.13's output. So response itself was empty, and 0.9.2's
message: response carried an empty string — the preview stayed blank and G2
still counted every turn empty.
Root cause: opencode run --format json (src/cli/cmd/run.ts emit) streams
JSONL frames {"type": ..., "sessionID": ..., "part": {...}}; assistant prose
is in type=="text" frames under part.text. Symphony's _extract_text only
scanned flat keys (response/result/message/text/content/output) plus
data, so it never saw part.text and returned "". (Confirmed by unit probe:
_extract_text({"type":"text","part":{"text":"..."}}) == "".)
Fix: OpenCodeBackend._text_from_event reads part.text from type=="text"
frames, keeping the flat-key scan as a fallback for the raw-stdout and
non-opencode shapes. tool_use/step_start frames carry no prose, so a genuinely
empty turn (no text frame) still counts as empty — G2's real detection is
preserved. New tests/test_backends.py::test_opencode_extracts_text_from_jsonl_part_frames
pins the JSONL shape.
- Rejected: a deep/generic text search across every nested dict — risks
grabbing tool-call arguments and file contents as "response". The
type=="text"predicate is precise to opencode's serialization. - Known follow-up (not G2-blocking, fixed in the v0.9.3 release update below): token usage still
reads 0 for opencode 1.17. This only affectsmax_total_tokensbudgeting and
telemetry for opencode, not dispatch correctness. Tracked in
skills/symphony-skill/reference/troubleshooting.md.
Operator playbook added: "Backend CLI update broke response parsing" in
skills/symphony-skill/reference/troubleshooting.md — the input_tokens=0
smoking gun, the capture-the-real-schema recipe, and the fix pattern, so the
next CLI-upgrade drift is a 2-minute triage instead of a rediscovery.
Patch bump 0.9.2 -> 0.9.3 (restores opencode response parsing for opencode >= 1.x).
Follow-up (v0.9.3 release update) — OpenCode 1.17 token accounting
Live probe:
opencode run --format json --auto "reply with DONE only"OpenCode 1.17.13 emitted usage on a step_finish frame:
part.type == "step-finish" with part.tokens containing input, output,
reasoning, cache.read, cache.write, and total. Symphony's _usage_dicts
never descended through part, so the backend emitted useful message text but
left every token bucket at 0. That matches the TASK-002 monitor evidence:
nonblank last_message, input_tokens=0, then a separate
worker_exit reason=issue_state_refresh_failed after the turn.
Fix: OpenCodeBackend now descends into part and info usage containers,
reads totalTokens as an alias, folds nested cache.read / cache.write into
input-side tokens, and treats reasoning as output-side tokens when totals need
to be derived. New
tests/test_backends.py::test_opencode_extracts_usage_from_jsonl_step_finish_part_tokens
pins the real OpenCode 1.17 step_finish.part.tokens schema.
- Rejected: deep traversal of every nested dict. OpenCode frames also carry
tool and text payloads; scanning all children would risk treating user-facing
content or tool arguments as telemetry. The parser only follows known telemetry
containers. - Not part of this fix:
issue_state_refresh_failed. The failure fires after a
successful turn when tracker state refresh returnsNone; it is a separate
board/workspace reliability issue, not a token parser symptom.
Included in the v0.9.3 release tag update (restores OpenCode token telemetry for opencode >= 1.x).
Follow-up (v0.9.3 release update) — issue-state refresh RCA and operator UI
Live jira-symphony showed TASK-002 stuck in paused retry with
worker_exit reason=issue_state_refresh_failed after a successful nonblank
OpenCode turn. This was separate from the token parser issue. The ticket file
had malformed YAML:
created_at: '2026-07-04T00:00:02Z'
updated_at: '2026-07-04T02:35:57Z'That made the file tracker skip TASK-002, so the worker could not refresh the
post-turn state and paused for operator inspection. Current-board repair was to
unindent updated_at, after which symphony board ls --workflow ... showed
TASK-002 back in Learn, and POST /api/v1/TASK-002/resume moved it from
retrying to a running Learn retry.
Hardening: parse_ticket_file now auto-heals the common case where an agent
accidentally indents a canonical top-level key by one or two spaces. It does not
rewrite the file during read, but the next normal tracker write serializes
canonical YAML. New
tests/test_tracker_file.py::test_parse_ticket_file_auto_heals_misindented_updated_at
pins the observed TASK-002 shape.
Operator UI: Settings now shows the workflow default agent on both web surfaces.
The 9999 Symphony app renders Default agent from /api/v1/workflow.agent.kind
under Settings > Board info; the board-viewer Settings modal renders
Agent default from /api/symphony/state.workflow.default_agent_kind. The value
on the live board is opencode.
- Rejected: only patch the current ticket. That restores one board but lets
the next agent-authored YAML indentation mistake remove a ticket from refresh. - Rejected: deep YAML text surgery on every read. The heal only runs after a
YAML parse failure and only unindents known ticket frontmatter keys with shallow
accidental indentation, avoiding legitimate nested data such asagent.kind
andblocked_by[].state.
Follow-up (operator) — TASK-003 workspace collision RCA
After TASK-002 resumed and completed, live jira-symphony moved to TASK-003
and paused before the first agent turn:
hook before_run exited 42;
workspace kanban points to /Users/danny/Documents/PARA/Resource/learn-codex-kr/kanban,
expected /Users/danny/Documents/PARA/Resource/jira-symphony/kanban
The path /Users/danny/symphony_workspaces/TASK-003 was not a jira-symphony
worktree. It was a clean, older learn-codex-kr worktree registered under
learn-codex-kr/.git/worktrees/TASK-003, with kanban linked to that project's
board. Sympho...
v0.9.2 — OpenCode G2 empty-loop: response via 'message' preview key
Fix — OpenCode G2 empty-response-loop (part 1 of 2)
Healthy OpenCode tickets were moved to Blocked with
empty_response_loop budget exceeded (consecutive_empty_turns=3, threshold=3)
despite producing real output every turn.
- OpenCode delivered its turn response only under the
result/responsekeys of
theEVENT_TURN_COMPLETEDpayload — keys_preview_from_payloadnever reads — so
current_turn_messagestayed blank and the G2 guard counted every turn empty. - Fix: the turn-completed payload now also carries
message, so a productive turn
populates the preview and resets the empty-turn counter. A genuinely empty turn
still counts, so real empty-loop detection is preserved.EMPTY_TURN_LOOP_THRESHOLD
unchanged at 3. Backend-local; no shared-code change.
Necessary but not sufficient on its own for OpenCode >= 1.x — the response text
itself wasn't being parsed. See v0.9.3.
Full test suite green (1058 passed).
v0.9.1
Highlights
- Added and hardened multi-agent release gates for OpenCode, Pi, Claude, and Codex.
- Added AGY / Antigravity and Kiro backends for the Gemini CLI deprecation path.
- Added local Git quality gates: pre-commit checks staged whitespace and Python syntax; pre-push runs whitespace checks plus a CI-parity full pytest run in an isolated dev venv.
- Renamed the operator skill
using-symphony->symphony-skill; branch-specific assets live underskills/symphony-skill/so operator skill discovery has one canonical entry point. - "Open the orchestrator" now defaults to opening the service
--portweb app (http://127.0.0.1:9999/); the--viewer-portboard (8765) is the secondary card board. - Fixed CI-only release blockers: optional Playwright import handling, PID-stable service status checks, branch-reference docs, and skill reference link coverage.
AGY / Kiro Compatibility
agent.kind: agyandagent.kind: antigravitydispatch throughagy --print -with unattended permission and continuation support.agent.kind: kirodispatches throughkiro-cli chat --no-interactive --trust-all-tools "$(cat)", with continuation support.symphony doctoraccepts Kiro auth from eitherKIRO_API_KEYor a successfulkiro-cli whoamilogin check.
Verification
- Release tag
v0.9.1re-cut to commit4b2079a3ff67e11176318f1464da751c48eb3abe(folds in thesymphony-skillrename and the open-orchestrator default). - GitHub
mainTests: run28689395807passed on4b2079a3. - GitHub
devTests: run28689338968passed on4b2079a3. - GitHub
mainPages: run28689395810passed on4b2079a3. - Pre-push gate:
1056 passed, 2 skippedin an isolated dev venv. - Prior cut (
0438da1c) live dispatch proofs (AGY, Claude, Codex, Kiro, OpenCode, Pi throughDone;/api/v1/statestatus=ok; board viewer HTML; TUIshutdown_complete) remain valid in history.
Gemini was not included in the live dispatch proof because symphony doctor failed Gemini auth: /Users/danny/.gemini/settings.json lacks root selectedAuthType, and GEMINI_API_KEY is unset.
v0.8.0 - current runtime bundle
This release tag was updated on 2026-07-03 to point at the current main
runtime bundle.
Highlights
- Built-in web Kanban app served at
/from the Symphony orchestrator port. - Browser-based workflow editing for board columns, stage prompts, and branch
policy. - Ticket create/edit/delete from the web app and TUI, with drag-and-drop state
moves and live run badges. - Default workflow simplified to
Todo -> In Progress -> Verify -> Learn,
followed byHuman ReviewandDone. - OpenCode is now a first-class backend alongside Codex, Claude Code, Gemini,
and Pi. - Local reliability hardening: SQLite run leases, persisted retry/pause/budget
issue flags, file-tracker write locks, and/api/v1/health. - Public docs, Korean README, landing page, package metadata, and architecture
notes now match the current runtime. - GitHub README navigation now has a compact table of contents, mirrored in the
Korean README. - GitHub Pages deployment now has an explicit checked-in static workflow for
docs/, with the repository switched to workflow build mode.
Web board and workflow editing
symphony --port 9999 and symphony service start serve a full browser app at
the orchestrator root. Operators can inspect work, register issues, edit issue
metadata, move cards, and monitor active runs without a separate frontend build
step, CDN dependency, or account flow.
The Workflow page lets operators add, delete, rename, and reorder Kanban
columns, edit each stage prompt, and adjust branch policy from the browser.
Writes round-trip through WORKFLOW.md using comment-preserving YAML handling.
When columns are renamed or removed, affected tickets migrate automatically; if
a column has an active worker, the edit is rejected with a conflict response.
Ticket operations
Tickets can be created from the web app or with the TUI n shortcut, then
edited or deleted through the browser API. The web board supports drag-and-drop
state transitions plus per-column create buttons. Running tickets show live
badges for turn count, token usage, and pause/resume state.
Skills, stats, and API
Local skills/<name>/SKILL.md files can be attached to individual tickets from
the web modal, the TUI form, or skills: frontmatter. On the first agent turn,
Symphony appends the selected skill bodies to the dispatch prompt.
The .symphony/stats.jsonl event store records turns, transitions, and run
outcomes. The web Stats page and the TUI s screen aggregate tokens per day,
completed work per day, per-column dwell time, per-agent totals, and average
cycle time.
REST endpoints now cover issue CRUD, board reads, workflow state editing,
prompt editing, branch policy updates, skill discovery, stats, git branch
discovery, Learn skip, refresh, and runtime health. Mutating endpoints require
JSON content and loopback Host validation.
README and Pages deployment
The GitHub README now includes a compact top-level table of contents for the
major operator sections. The Korean README mirrors the same navigation so the
two entry docs remain structurally aligned.
Pages deployment is now owned by .github/workflows/pages.yml. The workflow
publishes docs/ with explicit permissions, concurrency, and current Pages
actions. The repository Pages setting is configured for workflow build mode.
Runtime and reliability changes
- OpenCode backend support uses
opencode run --format json --auto, with
session continuation when OpenCode reports a session id. - SQLite run leases block duplicate dispatch after restart and reclaim
dead-owner leases. - Retry, pause, and budget-exhausted issue flags survive process exit.
- File tracker writes use locked compare-and-swap mutation paths.
- The operator-facing docs now describe the single-node/local recovery boundary
precisely: persisted leases and flags do not reattach to a worker lost during
a hard crash.
Dependency change
ruamel.yaml is required so Symphony can edit WORKFLOW.md while preserving
comments and formatting better than a plain YAML rewrite.
Release commit: 7be48e2
v0.7.2 — agent-terse workflow prompts + token-budget directive
Prompt-cost release. All 18 stage templates under docs/symphony-prompts/{file,linear}/ (base + 8 stages per tracker) were rewritten as terse agent-directives — the dispatch prompt is read by the worker LLM, not a human, so rationale prose was cut while every behavioral rule stayed.
Highlights
- ~12% smaller dispatch prompts: 282,009 → 247,942 chars (~70.5k → ~62.0k tokens) summed across the 28 tracker × language × stage combinations; Review stages −15%. Every section anchor, state-transition rule, command, table spec, env var, and liquid branch is preserved. Human-facing artefact templates (Plain-language header, caps table, wiki entry template, As-Is→To-Be report, Human Review handoff) are byte-identical.
- Soft token-budget directive (
base.md, both trackers): whenagent.max_total_tokens_by_statesets a budget, prompts now renderToken budget: keep this turn under N completion tokens (stage EMA: M)from the previously unused C3token_ema/token_budgettemplate vars. Off by default; regression tests pin both states. - Chore short-circuits ported to linear boards: Explore/Plan/Review/Learn
chore-label fast paths now exist in the linear prompt set too, so metadata-only tickets skip the full stage contracts regardless of tracker. - Tracker drift removed:
- linear Learn no longer re-does the bulk dup/orphan/stale wiki sweep by hand —
symphony wiki-sweepalready runs it everywiki.sweep_every_nDone transitions for every tracker kind. Per-ticket integrity checks (invalidated-entry update, beginner-block shape,Wiki Conflictflag) stay in the prompt. - linear's wiki entry template gains the Observability hooks block, so file- and linear-authored
docs/llm-wiki/entries share one schema.
- linear Learn no longer re-does the bulk dup/orphan/stale wiki sweep by hand —
Verification
pytest -q: 810 passed; the single failure (tests/test_service.py::test_start_cleans_live_viewer_from_stale_record_before_doctor) pre-exists this release (fails identically atc04d2ad) and is unrelated to prompts.- Prompt contract suite (
tests/test_workflow_pipeline_prompt.py): 51 passed, including 4 new tests anchoring the token-budget directive (proven red against the pre-change templates, green now). - Independent critic audit compared every old/new template atom-by-atom: zero CRITICAL findings; the one MEDIUM finding (linear beginner-block shape check dropped collaterally) was restored before release.
Known follow-ups
The file tracker still has gates the linear set lacks: the Security Audit 7-row table, [no-test] marker scan, ## Acceptance Tests / ## Done Signals + AC Scorecard, and $SYMPHONY_REWIND_SCOPE rewind scoping. Porting them is a behavioral decision deferred to a future release. The OneShot skill's lane prompts (skills/symphony-oneshot/templates/WORKFLOW.oneshot.md) were intentionally left untouched.