
state of loswf: the canonical reference
a code-grounded read of how the factory is actually wired today — 20 agents, 15 phases, three layers of dispatch enforcement, an eight-ledger observability graph, and a 15-row discrepancy ledger against the SRS.

the gap that surfaced
specs/SRS.md was written in February. The pipeline it describes had
16 roles and a triage → intake → planning flow. The pipeline on
main today has 20 roles, 15 phases, a Product universal gate that
landed on 2026-05-20, and an observability surface that emits to
eight-plus JSONL ledgers. The SRS was not wrong when it was written.
It was wrong by the time you read it.
Issue #645 set out to
close that drift. It closed on 2026-05-23 — completed. The output is
a code-grounded research pass: 51 Facts, 12 Insights, 7 Recommendations,
and a 15-row discrepancy ledger, plus a canonical architecture
reference that supersedes the SRS sections it disagrees with. The
reference lives at docs/ARCHITECTURE.md;
this post is the engineering essay that goes with it. The 15
discrepancy rows are now individual tracked follow-ups against the SRS.
what the canonical reference does
Three jobs:
- Describe the factory as it runs. Eight sections — overview,
layered architecture, roles, phases, flows, design primitives,
observability, discrepancy ledger. Every section opens with the
Fact / Insight / Recommendation IDs it cites against
Atoms/645-findings.md. Anchor citations arefile:lineagainst the currentmain. - Settle authority order. Code >
docs/ARCHITECTURE.md>specs/SRS.md>specs/VISION.md. The SRS is no longer the single source for “how is the factory wired”; the SRS is the intent document, the architecture reference is the observed document, and the discrepancy ledger is where they meet. - Make drift a tracked artifact. Every disagreement between SRS and code is a numbered row (D1–D15), a severity, a recommended fix, and a follow-up issue. Drift is no longer the kind of debt you discover three iterations after it bit you.
Six deep-dives sit under docs/architecture/ for the parts that
needed their own page: the state machine, the three-layer dispatch
guard, shadow mode, runtime adapters, the renderer + shared prompts,
and observability.

the shape of the factory
loswf2 is the agency operating engine, not a product. The agency takes client intent — a vision, a roadmap, an issue — and turns it into shipped code without an operator at the console.
The architecture has four layers, and they hold each other apart through narrow contracts:
- Distribution.
bin/release.shis the sole writer ofplugin/anddocs/CHANGELOG.md. Hand-edit either between releases and release-bot expectations break. The plugin syncs from.claude/andfactory/; project-scope installs (hooks, ADWs, settings) go through a separate top-level installer. - Runtime adapter. A
loswf_runtimeenv knob (defaultclaude,codexavailable; written all-caps in the actual env) chooses which adapter runs the agent loop. Adapters expose a five-operation contract:headless-run.sh,doctor.sh,run-interactive.sh,enforce-validators.sh, andbootstrap/install-project-scope.sh. Claude ships 5 scripts; Codex ships about 30 — the extra surface is the skill/agent loader Codex lacks natively. - Shared prompts.
factory/prompts/{roles,commands,skills}/plusfactory/manifests/roles.yaml. Four roles are promoted — authored once, rendered into both runtimes bybin/render-runtime-assets.mjs, and verified by a parity validator that fails CI on drift. The remaining 16 roles still ship as per-runtime files. Promotion proceeds one role at a time. - Orchestration. A sweep loop (
adws/adw_sweep.sh+_lib.sh) walks the config-declaredsweep.sequence, fans out parallel builders, and reconciles GitHub state into.loswf/state/. Twenty-two sequence entries are declared today; some ship disabled. Sequence is data. Reordering a phase is a YAML edit, not a code patch.

twenty agents, fifteen phases
The roster has expanded since the SRS was written. Twenty agents
live under .claude/agents/loswf-*.md: architect, architect-review,
builder, curator, decomposer, designer, documenter, editor,
escalation, harvester, intake, investigator, plan-reviewer, planner,
product, red-team, researcher, reviewer, scout, setup.
Thirteen of those are phase-bound — they operate from a specific
factory:phase:* label and the orchestrator routes work to them
when an issue carries that phase. The other eight are unbound:
curator runs on a cadence, harvester drains an inbox, setup is
install-time only, and so on.
The fifteen phases are declared in .loswf/config.yaml lines
116–131. They are not invented in code; they are a YAML list the
orchestrator walks. The Product universal gate (policy 2026-05-20)
is the most recent shape change: every issue that survives intake —
except bug, spike, investigate, and the research-derived path — flows
through product for either a PRD v1 (features, research) or a
lightweight signoff (chore, refactor, docs) before reaching
planning.

three layers, one predicate
The state machine is enforced at three layers but the predicate is
the same — validate_dispatch. A transition that is wrong in one
place is wrong in all three.
- Layer 0 is the orchestrator.
loswf_post_agent.pyreads markers (<!-- loswf:product:prd-v1 -->,<!-- loswf:research:complete -->, shadow verdicts) and decides the label change. - Layer 1 is the DISPATCH table inside the orchestrator — fourteen entries mapping (phase, marker) to (next_phase, agent).
- Layer 2 is the PreToolUse hook
_dispatch_gate.py. Every Bash invocation that looks likegh issue edit … factory:phase:*is screened before it runs. Exit 70 denies; exit 71 signals an internal gate error and appliesneeds-attention. The only bypass is the explicitloswf_skip_dispatch_gate=1env knob — reserved for emergency recovery.loswf_self_hosted=1does not bypass it. The two flags are deliberately separate.
Dispatch resolution itself prefers receipts. A runtime-neutral
phase-receipt schema (factory/schemas/phase-receipt.schema.json)
is the canonical signal that a phase has advanced. The legacy
transcript-scan fallback is still opt-in at
.loswf/config.yaml:221-222 for migration safety. Receipts are
primary; the transcript path is legacy debt the renderer migration
will eventually retire.
shadow mode is the promotion path
Every gating lens in loswf2 lands in shadow before it ever blocks
promotion. The per-step config knob is gating: shadow | enforce.
In shadow mode the lens emits a two-line marker
(<!-- loswf:shadow:<phase>:verdict -->\n<keyword>), an aggregator
parses the corpus, and the catch-rate-vs-noise distribution gets
tuned in the JSON before the lens flips to enforce.
This is how plan-review got tuned. How reviewer verdicts got tuned.
How architect-review will get tuned — it ships disabled today, opt-in
via gating: shadow on the sequence step. The promotion checklist
is in docs/architecture/shadow-mode.md.
R-A007 calls for bin/doctor.sh to surface catch-rate inline so the
shadow → enforce decision does not require a separate aggregator
invocation. That is filed as a follow-up.

observability is a graph
There is no single feed. Eight-plus JSONL ledgers under
.loswf/state/ carry pieces of the truth, and a reconciler stitches
them by issue number on every sweep:
events.jsonl— coarse events.metrics.jsonl— schema_v1 metrics for dashboards.receipts.jsonl+ per-run dir — structured per-phase receipts.devlog-queue/*.json— pending devlog writes.devlog-triggered.jsonl— dedup checkpoint (kept separate from the orchestrator’sdispatched.jsonl— conflating either has caused double-publishes in the past).shadow/*.json— aggregator output.subagent_timing.jsonl+subagent_closed.jsonl— SubagentStop hook output.sessions/*.json— per-session statusline tokens.
Every event carries (code, retry_class, category). retry_class
drives the reconciler’s re-enqueue decision. category is the
dashboard rollup. Reconcile errors never block the sweep — the
system degrades visibly, applies needs-attention, and the sweep
moves on.
the fifteen discrepancies
The ledger is the most operationally useful thing in this pass. Three Highs:
- D1. SRS still draws
triage → intake → planning. The code routes non-bug/spike/research/investigate throughproductfirst. - D2. SRS lists 16 roles. The repo has 20.
- D3.
loswf_label_transition.py’s LEGAL table is missing theresearching,product,red-team, andarchitect-reviewphases. The orchestrator bypasses LEGAL by design (it is the trusted writer), but the validator should still know about every active phase.
Seven Mediums and five Lows fill out the table. Each row is filed as
a docs(srs): or chore(factory): issue with no factory:phase:*
label — the curator triages on the next sweep, no intake storm. The
authority order (code > ARCHITECTURE.md > SRS) is what lets the
ledger sit comfortably as a tracked artifact rather than a backlog
to clear.
#647 — what shored the foundation
A week before this pass landed, PR #647 fixed four primitives the research above depended on observing reliably:
- C1.
snapshot_phases()was using AND-semantic GitHub label search; the empty corpus broke the idle gate, which in turn broke builder fan-out accounting. Replaced with a single full-listing query and a client-sidejqfilter. - C2. Three shell call-sites still ran
gh pr list --search "linked:$issue"after the Python equivalent had been patched in #305. Ship and review were the affected phases — a wrong-PR routing risk. Portedfind_open_pr_numberto bash and replaced the call-sites. - C3.
SHADOW_VERDICT_MARKER_PREFIXwas a duplicated literal inside the orchestrator instead of an import from the canonicalshadow_markers.pymodule. Silent grammar drift was one rename away. - H7.
devlog_write.pyembedded the PAT in the git clone URL, visible in the process table. It leaked into a session stderr on 2026-05-23 and forced a rotation. Replaced with aGIT_CONFIG_COUNT-injected credential helper; the clone URL is clean and subprocess output is captured.
Each fix is observable in the architecture reference — F-A013’s DISPATCH count, F-A026’s idle gate, F-A036’s shadow marker constant, and the operational note on the devlog publishing path.
what to read next
Start with docs/ARCHITECTURE.md.
Read the eight sections top-to-bottom; jump to a deep-dive when a
mechanic surprises you.
If you are operating the factory: §5 (Flows) and §8 (Discrepancy ledger) are the two pages you will return to.
If you are extending the factory: §6 (Design primitives) plus the shadow-mode deep-dive describe the promotion path for any new gating lens. The renderer deep-dive describes how to promote a role from per-runtime to shared.
If you are auditing the factory: every section opens with the atom
IDs it cites. Atoms/645-findings.md is the bottom of the chain —
51 Facts, 12 Insights, 7 Recommendations, all anchored at
file:line against main as of 2026-05-23.
The next iteration’s retro will track how many of the 15 discrepancy rows close. The SRS will catch up, one section at a time.
Update — 2026-05-23 16:03 UTC. #645
closed as completed. The full deliverable set landed on main: the
canonical docs/ARCHITECTURE.md,
six deep-dive pages under docs/architecture/, the
Atoms/645-findings.md
research vault, and the structurally-accurate Mermaid PNGs the figures
on this page render from. Discrepancy ledger rows D1–D15 are tracked
as individual SRS-alignment follow-ups; iteration retros report the
close rate.