Full design review — 2026-07-21¶
Adversarial self-audit of the complete spec (8 docs, ~1,800 lines; Go skeleton, ~2,500 lines) after four design sessions. Severities use the review ladder from the SPM layer: major = fix not obvious, needs work; moderate = fix clear, needs review; minor = fix obvious.
Method note: every document under review was written by the same author now reviewing them, in the same week. The consistency defects below are what that method catches; what it structurally cannot catch is a shared blind spot, which is an argument for the multi-runtime verification panel applied to this repo's own docs, someday.
Companion piece: the structural verdict is normative in ARCHITECTURE.md, not repeated here.
1. Consistency defects — found and fixed in this pass¶
| # | Sev | Defect | Fix |
|---|---|---|---|
| C1 | major | help ask / help answer / help escalate were unreachable: Main intercepts args[0] == "help" for usage before dispatch runs. An entire feature namespace, dead on arrival, and the duplicate-path test couldn't see it. |
Renamed to ask / answer / escalate; added a reserved-word reachability test. |
| C2 | major | Queues carried mutable state (cursor) with no owner — the one object anybody could rewrite, silently violating the single-writer invariant the whole concurrency design rests on. |
Queues now have owner:; only the owner advances. A queue is a checklist walked by one agent, not a work-distribution mechanism. |
| C3 | major | Shortcut uses: was an incremented-in-place counter, which would make the shortcut file the one object every agent writes concurrently — the exact contention the event log exists to prevent. |
uses is derived: recomputed from run events at sync, read-only cache otherwise. |
| C4 | moderate | FORMAT.md's event-kind table was missing help, answer, and run — three kinds added to the model but never to the format spec that claims to be the stable interface. |
Table updated. |
| C5 | moderate | Two unreconciled stage vocabularies: project stage: uses cone names (definition…design), template stages use process names (inception…transition), and TEMPLATES.md claimed they "map onto" each other without saying how. An implementer would have guessed. |
Every template stage now declares an explicit cone:. |
| C6 | moderate | "Five object types" claims in DESIGN.md § 4 and the model package doc, three layers of satellites later. |
Reworded: five collaboration-core types plus named satellites. |
| C7 | minor | Stale RUNTIMES.md § 14/§ 17 cross-references after section insertion. |
Renumbered. |
The pattern behind C1–C3 is worth naming: all three are invariants enforced nowhere. The single-writer rule and the command-dispatch rule lived in prose, so violating them compiled cleanly and passed tests. Where an invariant can be a test (C1's reserved-word check) it now is; the ownership rules become enforceable only when the spine exists, which is one more argument for building it first.
2. Gaps — open, with proposed resolutions¶
| # | Sev | Gap | Proposed resolution |
|---|---|---|---|
| G1 | major | No MCP spec. "Two front ends, one core" since day one, agents are the primary audience, and the agent-preferred surface is the only one without a document. | docs/MCP.md next, before any L5 implementation. Contract sketch in ARCHITECTURE § 4: one tool per command, same JSON, generated from the same table. |
| G2 | major | No worked example of a brief existed anywhere — 1,800 lines of spec for a tool whose entire output is one artifact, and the artifact was never shown. | Fixed in this pass: ARCHITECTURE § 6 is now the canonical example, including attributed-quote rendering of third-party content. |
| G3 | moderate | No exit-code or machine-output contract; agents would have parsed stderr. | Fixed in this pass: ARCHITECTURE § 4. The refusal/failure distinction (3 vs 1) is the load-bearing part. |
| G4 | moderate | Task-ID collision under concurrent creation — open since the first session. | Resolved: ULID is the true id (collision-free at creation); NNN is a filename alias assigned by the single materializer (the owner) at sync. FORMAT.md updated, DESIGN open-question 5 closed. |
| G5 | moderate | No lifecycle: nothing closes a project, archives done tasks, or bounds workspace growth beyond event compaction ("sketched, not designed"). | Propose dacli archive <project>: moves to projects/_archive/, compacts its events, excludes it from status/next. Needs a spec pass. |
| G6 | moderate | Same-identity multi-process races: two shells acting as one agent can interleave writes to that agent's own objects. Atomic rename means last-write-wins, not corruption — but it was stated nowhere. | Stated in ARCHITECTURE § 5 as honest scope. Optional advisory flock later; not v0.1. |
| G7 | moderate | Ambiguity-linter noise: qualifiers/positional/temporal at moderate fire on ordinary prose ("move the file after tests pass"). A linter that cries wolf trains agents to skip it — the exact failure it exists to prevent. | Resolved: asymmetric scope policy in SPM.md — titles + acceptance at moderate, bodies at major-only, --strict widens. |
| G8 | minor | Route() dead-ends when a role owns the path but no escalate_to edge reaches it — a config gap indistinguishable from "no owner." |
Resolved in spec (TEAM.md): the error names the owning role and the missing edge. |
| G9 | minor | dacli next behavior when tasks lack estimates (CPM needs durations) was unspecified. |
Degrade to MoSCoW-then-dependency order, announced per axiom 6. One line, needs to land in SPM.md. |
| G10 | minor | Panel verdicts (verify --require 2) have no recorded form. |
A verdict field on finding events; counts derived. Spec with runtimes. |
| G11 | minor | Config layering (workspace / ~/.dacli / env) and format migration beyond "additive after 1" unspecified. |
Defer both until v0.2 forces them; note here so they're not forgotten. |
| G12 | minor | No test story for runtime adapters without paid API calls. | Ship a mock runtime — generic-exec pointed at a fixture script. Cheap, and CI needs it the day L5 starts. |
3. Feature proposals from the review¶
Ranked; none are commitments.
dacli context --explain— show why each item made the brief and what was cut at what cost. The brief is the product; when it's wrong, this is the only debugger. (Small, pure-L4.)dacli task doneenforces the definition of done — the template DoD currently binds nothing; done-time is where it has teeth.- Attributed-quote fencing of third-party content in briefs — now in the ARCHITECTURE example; makes injection visible, not impossible. Cheapest available mitigation for the design's worst open problem.
- Brief caching by content hash — briefs and gate checks re-derive identical output; a hash key makes panels and repeated spawns near-free. (Optimization; after dogfooding proves the shape.)
dacli template diff— vendored templates drift silently from their source; promote from TEMPLATES open-questions to planned.- Unified
dacli doctor— workspace, runtime, and github doctors under one health surface with one exit-code convention.
4. What the review did not find¶
Stated so the clean bill is explicit, not an oversight: the event-log concurrency model survived adversarial reading intact once C2/C3 were fixed — every mutation is either owner-writes-own-file or a new ULID-named file. The permission story's honesty (cooperative → enforced-when-spawned) held up. The three-surfaces framing (Obsidian/GitHub/dacli over one store) generated no contradictions anywhere in the eight docs. And the "runs agents, not work" boundary, restated once, absorbed both later feature waves (slash commands, templates) without further erosion.
The worst open problem remains what it was: cross-tree prompt injection (RUNTIMES.md § 18). Nothing in this review shrank it; proposal 3 above only makes it auditable.
5. Dispositions — upgrade pass, later the same day¶
The register above is append-only; what changed since it was written:
- G1 resolved — MCP.md written. It overturned ARCHITECTURE § 4's one-tool-per-command promise (a 50-schema catalog is the per-agent tax this design refuses elsewhere) in favor of a tiered surface: fourteen core tools + a
cliescape hatch, refusals as results so clients never retry a policy "no", identity bound at server launch so tokens stay out of transcripts. ARCHITECTURE § 4 amended to match, with the correction owned in place. - G7 was already landed in SPM.md at review time; G9 (estimate-less
nextdegrades to MoSCoW-then-dependency, announced), G10 (panel verdicts recorded asverdict:-bearing finding events, tallies derived — same rule as shortcutuses), and G12 (themockfixture-script adapter as the entire L5 CI story) are now landed in SPM.md and RUNTIMES.md. - Proposal 2 landed — the DoD binds at
dacli task done, refusal names the unmet criterion (TEMPLATES.md). - New: G13 (moderate) — found by writing WALKTHROUGH.md, which is exactly what it was for: no command spec anywhere defines flag surfaces (
task add --accept/--estimate,risk add --indicator/--action, …). The command tables list verbs, not signatures; the walkthrough's flags are the proposal. Resolve when the CLI grows real arg parsing — the--jsonoutput shapes and these flag names should be specified together, as one contract. - Also added: docs/README.md index with per-doc status labels; status headers on SPM/TEAM/SHORTCUTS distinguishing implemented engines from stubbed commands.
Still open after this pass: G5 (lifecycle/archive), G6 (same-identity races — scoped honestly in ARCHITECTURE § 5), G11 (config layering, deferred until v0.2 forces it), G13 (above), and the injection problem, unchanged.