fix(web): saving a workflow from the builder destroys the author's comments and YAML anchors
Problem
Loading an existing workflow into a builder and saving it overwrites the author's file with a document reconstructed from the parsed WorkflowDefinition. Everything the definition does not carry is destroyed: comments, YAML anchors and aliases, block scalars, and line formatting. The rewritten file is also a single flow-style line — archon-review.yaml comes back as 4243 characters on one line.
PUT /api/workflows/:name serializes with Bun.YAML.stringify(definition) (packages/server/src/routes/api.ts:4440) and writes the result to the existing file path (packages/server/src/routes/api.ts:4462). Both builders save through it: the console builder (packages/web/src/experiments/console/builder/BuilderConnected.tsx:251) and the legacy builder (packages/web/src/components/workflows/WorkflowBuilder.tsx:301).
Why
YAML anchors are the only mechanism a workflow author has for declaring one output_format schema and reusing it across nodes. Our own delivery pack depends on it: .archon/workflows/sdlc/deliver/archon-deliver.yaml:75 defines &green_gate and aliases it at lines 293, 368 and 482, with &ci_state at 408 aliased at 436. A builder save silently expands each alias into an independent copy, reintroducing exactly the drift the anchor exists to prevent — and the author gets no indication it happened.
Comments are how the bundled packs explain their gates and their non-obvious wiring. A save strips all of them. A single-line flow document also makes the file unreadable in a diff and hostile to a merge.
Why now: the console is the default UI and /builder/:name is a live route (packages/web/src/experiments/console/ConsoleApp.tsx:78). We are also telling users publicly that anchors are the supported way to share a schema across nodes, so a surface that quietly undoes them is a contradiction we should not ship.
Desired outcome
Saving an existing workflow from either builder preserves the parts of the file the builder did not change. Comments and anchors survive a no-op save, and the saved file stays block-style and diff-readable.
Acceptance
- Open
.archon/workflows/sdlc/deliver/archon-deliver.yamlin the builder, save without editing, andgit diffon that file is empty. - After that save the file still has one
&green_gatedefinition and three*green_gatealiases, not four independent copies. - Comments present before a save are present after it.
- The saved file is block style — no single-line flow document.
- Changing one field on one node changes only that region of the file.
- A workflow with no comments and no anchors still saves as it does today.
Evidence
Reproduction
- Run the server and open
/builder/archon-deliveragainst a project whose workflows include the sdlc pack. - Save without making any edit.
git diff .archon/workflows/sdlc/deliver/archon-deliver.yaml.
Measured directly against the save path's serializer (bun 1.4.2):
$ bun -e 'const p = Bun.YAML.parse(await Bun.file(".archon/workflows/sdlc/review/archon-review.yaml").text());
const out = Bun.YAML.stringify(JSON.parse(JSON.stringify(p)));
console.log(out.split("\n").length, "lines,", out.length, "chars")'
1 lines, 4243 charsThe anchor loss comes from the JSON hop, not from the serializer:
Bun.YAML.parse keeps alias identity nodes[0].output_format === nodes[1].output_format → true
Bun.YAML.stringify(parsed) → emits &output_format / *output_format
Bun.YAML.stringify(JSON round-trip) → two independent duplicated copiesThe definition crosses HTTP as JSON (saveWorkflowBodySchema), so the shared identity that Bun.YAML.parse preserved is already gone before the server serializes. Comments are never carried at all — the parsed definition has no place to hold them.
The single-line output is a missing argument rather than a Bun limitation: Bun.YAML.stringify(value, null, 2) emits block style.
Environment
- Archon version or commit: a7e300b2a
- Platform or adapter: web (console builder and legacy builder)
- Database: SQLite
- OS: macOS (Darwin 25.6.0), bun 1.4.2
Constraints and related work
- Must remain true: the save path validates with
parseWorkflowbefore writing (packages/server/src/routes/api.ts:4445); a save can never write a file that no longer loads. - Must remain true: the refusal to overwrite a bundled default (
packages/server/src/routes/api.ts:4458) stays. - Related: a community question about sharing one
output_formatschema across several nodes. The answer is YAML anchors, which is what this bug destroys. - Solution steering: Hint — the formatting half is one argument to
Bun.YAML.stringify, but that fix alone does not preserve comments or anchors. The preservation half needs a decision about whether the builder edits the existing file text rather than regenerating the whole document. Do not let the cheap formatting fix close this issue.
Source: coleam00/Archon