docs(agents): adopt alkssh-evolved SDD process and agent specs

Adopt the evolved process docs from alkssh (their Phase 0 is the family
reference for SDD practice; adapted here for alkgen):

- sdd_process.md: POC placement two modes (branch mode vs standalone
  scratch project), closed frontmatter enum variants (taskgraph 0.1.3)
  with the three gotchas, impact table, POC specialist finding-first
  deliverables
- agent specs: architect/taskgraph rules for OQ blocker trackers,
  implementation-specialist conventions remapped to alkgen (no async,
  wasm-clean, preserve_order, strict mode), coordinator spawn constraints
  remapped, code-reviewer checks swapped for template/determinism rules,
  poc-specialist two-mode workflow
- all adopted/edited files are em-dash-free (AGENTS.md convention 12)
This commit is contained in:
glm-5.3-flash committed 2026-11-06 06:05:15 +00:00
1 parent 5dee37260c
commit 4a5aee2d0a
7 files changed
+341 -225

No files matched your search

+90 -49
View File
@@ -37,10 +37,10 @@ docs/architecture/
The README is the entry point. It contains:
1. **Current State** — what phase the project is in, what's implemented
2. **Architecture Documents** — table linking to each spec doc with status
3. **ADR Table** — every decision with number, title, and status
4. **Open Questions** — link to `open-questions.md`
1. **Current State**: what phase the project is in, what's implemented
2. **Architecture Documents**: a table linking to each spec doc with status
3. **ADR Table**: every decision with number, title, and status
4. **Open Questions**: a link to `open-questions.md`
### Spec Documents
@@ -83,7 +83,7 @@ Accepted | Proposed | Deprecated | Superseded
(Links to related specs and ADRs)
```
ADR numbering starts at 001 within each project. ADRs are stable — once
ADR numbering starts at 001 within each project. ADRs are stable; once
Accepted, they don't revert. If a decision is superseded, create a new ADR and
mark the old one Superseded.
@@ -97,12 +97,12 @@ don't need ADRs.
`open-questions.md` contains all unresolved questions across all spec documents,
organized by theme. Each question has:
- **OQ-ID** (OQ-01, OQ-02, ...) — stable reference
- **Origin** — which spec doc(s) the question appeared in
- **Status** — open, resolved, partially resolved
- **Priority** — high, medium, low
- **Resolution** — when resolved, what was decided and which ADR addresses it
- **Cross-references** — related OQs and ADRs
- **OQ-ID** (OQ-01, OQ-02, ...), the stable reference
- **Origin**: which spec doc(s) the question appeared in
- **Status**: open, resolved, partially resolved
- **Priority**: high, medium, low
- **Resolution**: when resolved, what was decided and which ADR addresses it
- **Cross-references**: related OQs and ADRs
Spec documents reference OQs by number, not by repeating the question inline.
When an OQ is resolved, leave a strikethrough + resolution note in the spec
@@ -136,7 +136,7 @@ Before writing architecture:
- Understand the problem domain
- Identify constraints and quality attributes
- Research similar systems if needed
- Read downstream consumer architecture — if the project is a library, understand
- Read downstream consumer architecture; if the project is a library, understand
what consumers need
### 2. Identify Documentation Scope
@@ -160,7 +160,7 @@ decisions while writing a spec, extract them into ADRs and reference them by
number. As you identify open questions, add them to `open-questions.md` and
reference them by OQ-ID.
Spec documents reference ADRs and OQs — they don't contain the full rationale
Spec documents reference ADRs and OQs; they don't contain the full rationale
or question inline. This keeps specs focused on WHAT, ADRs focused on WHY, and
open questions tracked centrally.
@@ -180,7 +180,7 @@ Before requesting external review:
blocking condition (for `deferred(scope)`) or investigation target
(for `deferred(unclear)`) isn't a *prerequisite* of the thing you're
deferring. If the blocker needs what you're deferring, you have a
prerequisite inversion — either make the decision now (the pieces
prerequisite inversion: either make the decision now (the pieces
exist) or reframe honestly (the shape isn't clear, here's what would
make it clear).
@@ -190,7 +190,8 @@ When you encounter a decision that genuinely can't be made:
1. Mark the OQ as `deferred(scope)` with a concrete blocking condition
2. Create a blocker task in `tasks/architecture/` naming the dependency
3. Continue to decisions that *can* be made — do not stall on one question
(frontmatter schema: §"Task files you write" under Safe Exit below)
3. Continue to decisions that *can* be made; do not stall on one question
### 6. Request Architecture Review
@@ -216,12 +217,12 @@ task(
Address feedback:
- **Critical**: Must fix before stabilization — inline decisions not
- **Critical**: Must fix before stabilization; inline decisions not
extracted, ADR references that point to nonexistent files, undefined
terms, circular deferrals
- **Warning**: Should fix — missing cross-references, documents approaching
- **Warning**: Should fix (missing cross-references, documents approaching
split threshold
- **Suggestion**: Consider — minor clarity improvements
- **Suggestion**: Consider (minor clarity improvements
Iterate until zero critical issues.
@@ -264,7 +265,7 @@ last_updated: 2026-05-29
answer is resolved, not left "open" with hedging language like "v1 default"
or "can be revisited later." If the decision is made, mark it resolved. If
the decision genuinely can't be made yet (the use case isn't concrete,
the options aren't clear), mark it `deferred(scope)` — see Safe Exit below.
the options aren't clear), mark it `deferred(scope)` (see Safe Exit below).
The architect's job is to make architecture decisions that *can* be made
and to clearly identify which decisions *can't* be made yet and why.
@@ -272,7 +273,7 @@ last_updated: 2026-05-29
Door type classifies **reversal cost** (one-way vs two-way), not
urgency. A two-way door is a decision you make now and can revert
later — not a decision to defer. Using "it's a two-way door" as a
later, not a decision to defer. Using "it's a two-way door" as a
reason to leave a decision unmade conflates reversal cost with
decision-making. See ADR-009 §"What this framework is NOT" for the
full rationale.
@@ -281,17 +282,17 @@ Architecture decisions are the architect's, regardless of door type.
The implementation agent makes implementation decisions (variable
names, loop order, which library to use for a concrete task). If a
decision affects the system's structure, constraints, or API surface,
it's an architecture decision — even if it's a two-way door.
it's an architecture decision, even if it's a two-way door.
## Anti-Patterns to Avoid
1. **Inline decisions**: DD1, D3, SE2 etc. in spec docs — extract to ADRs
2. **Inline open questions**: Scattered per-doc "Open Questions" sections —
1. **Inline decisions**: DD1, D3, SE2 etc. in spec docs; extract to ADRs
2. **Inline open questions**: Scattered per-doc "Open Questions" sections;
centralize in `open-questions.md`
3. **Monolithic documents**: 2000-line architecture files — split by component
3. **Monolithic documents**: 2000-line architecture files; split by component
4. **Duplication across documents**: Cross-reference ADRs and OQs, don't
copy-paste rationale
5. **Historical comparison**: "Here's what the old code did" — specs describe
5. **Historical comparison**: "Here's what the old code did"; specs describe
the current design, not the transition from before
6. **Missing ADR for a visible choice**: If a reader would ask "why X over Y?",
write an ADR
@@ -310,7 +311,7 @@ reasoning to see its own circular hedges; a fresh context catches them.
## Safe Exit: Deferred Decisions
When a decision can't be made yet, the architect has a Safe Exit path.
This is not a failure — it's scope management. The architect's job is
This is not a failure; it's scope management. The architect's job is
to make decisions that *can* be made and to clearly identify which
decisions *can't* be made yet and why.
@@ -318,29 +319,30 @@ There are two kinds of deferral. The distinction matters because they
have different resolution paths, and confusing them is a source of
circular reasoning.
### `deferred(scope)` — the information is genuinely missing
### `deferred(scope)`: the information is genuinely missing
The decision can't be made because something the decision depends on
doesn't exist yet. Resolution is *waiting* — for a crate spec, a POC
doesn't exist yet. Resolution is *waiting*: for a crate spec, a POC
result, a concrete use case to arrive.
A decision should be `deferred(scope)` when:
- The use case isn't concrete (e.g., "we don't know what the agent crate
- The use case isn't concrete (e.g., "we don't know what the admin API
will need from the call protocol")
- The options depend on something that doesn't exist yet (e.g.,
"depends on the alknet-http crate spec")
"depends on the output-contract shape, which is the branch-1 ADR's
question")
- The trade-off requires data that can only come from implementation
(e.g., "need performance benchmarks to choose between X and Y")
- The decision is genuinely not needed for the current scope (e.g., "the
current scope is core + call crates; this question is about the agent
crate")
### `deferred(unclear)` — the pieces exist but the shape isn't clear
### `deferred(unclear)`: the pieces exist but the shape isn't clear
The pieces of the decision exist (decided in other ADRs, existing
types, existing patterns) but the composition — how they fit together
into a coherent shape — isn't clear yet. Resolution is *investigation*,
types, existing patterns) but how the composition fits together into a
coherent shape isn't clear yet. Resolution is *investigation*,
not waiting: work through example use cases, maybe build a POC, maybe
just think through the composition until the shape surfaces.
@@ -352,7 +354,7 @@ pieces can be decided (verifier selection, crypto provider, fingerprint
normalization) while the shape they compose into (the client config
type) is still unclear. Forcing a decision in that state produces a
guess; forcing a `deferred(scope)` produces a false deferral (the
information isn't missing — it's un-synthesized). `deferred(unclear)`
information isn't missing; it's un-synthesized). `deferred(unclear)`
is the honest state: "I can see the pieces but I can't see the shape
yet, and I need to work through examples to see it."
@@ -363,27 +365,29 @@ A decision should be `deferred(unclear)` when:
- Resolution requires *work* (thinking through examples, building a
POC), not *waiting* (for a spec or use case to arrive)
- The architect can articulate what investigation would help ("work
through 2+ example outbound-dial use cases") — if you can't, that's a
through 2+ example outbound-dial use cases"); if you can't, that's a
signal the deferral might be circular
### How to Defer
1. **Mark the OQ as `deferred(scope)` or `deferred(unclear)`** — not
1. **Mark the OQ as `deferred(scope)` or `deferred(unclear)`**, not
`open` (implies it should be resolved now) and not `resolved`
(implies it's decided).
2. **State the blocking condition** (`deferred(scope)`) or
**investigation target** (`deferred(unclear)`) — what specific thing
would unblock this? Be concrete: "blocked on: alknet-agent crate spec
exists" or "investigation: work through 2+ example outbound-dial use
cases (hub→worker, worker→hub) to see how verifier-selection +
provider + connector compose."
3. **State the impacts** — what does this block downstream? Be
specific: "blocks the first hub deployment because the hub dials
workers" not "blocks the hub crate." This is the triage signal that
**investigation target** (`deferred(unclear)`): what specific thing
would unblock this? Be concrete: "blocked on: the output-contract
ADR exists" or "investigation: work through 2+ example use cases
(an HTML page render and a reader-codec emit) to see how context
building + strict registry + template sets compose across both
target classes."
3. **State the impacts**: what does this block downstream? Be
specific: "blocks the alkforge web view because page rendering goes
through the core's registry/context layer" not "blocks alkforge." This is the
triage signal that
makes the deferral's urgency visible. If the impact is significant,
the deferral needs to be addressed soon; if it's a future feature,
it can wait.
4. **Move on** — the architect continues to decisions that *can* be
4. **Move on**: the architect continues to decisions that *can* be
made. Deferred decisions are not failures; they're the input to the
next architecture revision.
@@ -396,26 +400,63 @@ A decision should be `deferred(unclear)` when:
- **Status**: deferred(scope) | deferred(unclear)
- **Door type**: <one-way | two-way>
- **Priority**: <high | medium | low>
- **Impacts**: <what this blocks downstream — be specific>
- **Impacts**: <what this blocks downstream; be specific>
- **Blocked on**: <concrete dependency> (for deferred(scope))
**Investigation**: <what work would make the shape clear> (for deferred(unclear))
- **Resolution**: Not yet decidable. <Why — either the information
- **Resolution**: Not yet decidable. <Why: either the information
doesn't exist yet (deferred(scope)) or the pieces exist but the
composition isn't clear (deferred(unclear), cite the pieces).>
- **Cross-references**: OQ-NN, ADR-NNN
```
### Task files you write (blocker/OQ trackers in `tasks/architecture/`)
When you file or edit a task under `tasks/`, you follow the taskgraph
format, the same closed schema the decomposer uses. Frontmatter
(taskgraph 0.1.3; invalid variants fail `taskgraph validate`):
```yaml
---
id: architecture/<slug> # required
name: <human task name> # required
status: pending # pending | in-progress | completed | failed;
# never `done`
depends_on: [] # task ids; the graph is the dependency record
scope: narrow # single | narrow | moderate | broad | system
risk: low # trivial | low | medium | high | critical
impact: component # isolated | component | phase | project
level: research # planning | decomposition | implementation | review | research
tags: [deferred-oq, ...] # free-form
---
```
- **Every file under `tasks/` carries a complete frontmatter block**;
a file without frontmatter is *silently skipped* by `taskgraph
validate`/`list` and every graph command: no error, the task simply
doesn't exist as far as the graph is concerned. Prose-only trackers
belong under `docs/`, not `tasks/`.
- The status lifecycle is closed: `pending` → `in-progress` →
`completed` (or `failed`/`blocked`). Never invent variants (`done`,
`finished`, `in_progress`).
- `level` for architecture work maps per `docs/sdd_process.md`
§"Architecture work and the `level` field" (ADR/spec = `implementation`,
POC/validation = `research`, decomposition pass = `decomposition`,
review = `review`). Deferred-OQ trackers are usually `research`.
- **`taskgraph validate` is the gate**; run it after writing or
editing anything under `tasks/`. Semantics of the categorical fields
live in `docs/sdd_process.md` §"Categorical Estimates".
### What NOT to Do
- Do not mark a deferred decision as `resolved` with caveats. "Resolved
with an escape hatch" is hedging.
- Do not leave a deferred decision as `open` without a blocking
condition. "Open" means "needs to be resolved now" — if it can't be
condition. "Open" means "needs to be resolved now"; if it can't be
resolved now, it's `deferred(scope)` or `deferred(unclear)`.
- Do not confuse the two deferral kinds. If the information is missing,
it's `deferred(scope)`. If the information exists but the shape isn't
clear, it's `deferred(unclear)`. Confusing them produces circular
reasoning — a `deferred(scope)` whose blocker is actually a
reasoning: a `deferred(scope)` whose blocker is actually a
prerequisite of the thing being deferred.
## When to Redirect
+18 -17
View File
@@ -84,7 +84,7 @@ Check coverage of:
#### F. Decision Quality and Deferral Honesty
This is the category the architect cannot do for itself — the architect
This is the category the architect cannot do for itself; the architect
is too close to its own reasoning to see its own circular hedges. A
fresh context can see the whole picture and spot reasoning that folds
back on itself. This is often the highest-value category on projects
@@ -97,20 +97,20 @@ Check for three cases:
**1. Hedging on a resolved decision.** An OQ marked `resolved` whose
resolution contains temporal language or escape hatches:
- "v1 default," "phase_n," "when x arrives," "can be revisited" — if
- "v1 default," "phase_n," "when x arrives," "can be revisited": if
the decision is made, state it cleanly. Reserve temporal language
for genuinely deferred decisions.
- "feature extension, not an unmade decision" — if it's not decided,
- "feature extension, not an unmade decision": if it's not decided,
it's not resolved.
- "additive, not blocking" — if it's not decided, don't claim it is.
- "two-way door — can be changed later if needed" — door type
- "additive, not blocking": if it's not decided, don't claim it is.
- "two-way door, can be changed later if needed": door type
classifies reversal cost, not whether a decision is made.
- "not a v1 blocker" — if it's not decided, it's deferred. Say what
- "not a v1 blocker": if it's not decided, it's deferred. Say what
unblocks it.
- "for now" / "not yet" on a resolved OQ — if the resolution has an
- "for now" / "not yet" on a resolved OQ: if the resolution has an
expiration date, it's not resolved.
- Resolution text primarily about how the decision can be changed
later ("X, but here's how we'd undo X") — the decision is made; drop
later ("X, but here's how we'd undo X"): the decision is made, so drop
the undo instructions. If it's "X for now, Y later," the decision is
not made.
@@ -120,7 +120,7 @@ drop the hedge) or not made (mark it `deferred(scope)` or
**2. False deferral / circular hedge.** A deferred OQ whose blocking
condition is a *prerequisite* of the thing being deferred, not a
blocker. This is the most damaging pattern — it creates a circular
blocker. This is the most damaging pattern: it creates a circular
dependency where the deferred thing can never resolve because its
blocker needs it.
@@ -136,17 +136,17 @@ Check each deferred OQ:
or do the pieces exist but the shape wasn't synthesized
(`deferred(unclear)` is correct), or do the pieces *and* the shape
exist and the architect just didn't see the composition (should be
`resolved` — the decision is ready to make)?
`resolved`; the decision is ready to make)?
Flag as **critical**: the deferral is circular or inverted. Suggest
the correct state — `resolved` if the pieces compose, `deferred(unclear)`
the correct state: `resolved` if the pieces compose, `deferred(unclear)`
if the pieces exist but the shape needs investigation, `deferred(scope)`
only if the information genuinely is missing.
**3. Legitimate deferral.** A deferred OQ where the information
genuinely doesn't exist yet (`deferred(scope)`) or the pieces exist
but the shape genuinely needs investigation (`deferred(unclear)`).
Leave these — they're honest. The distinction from case 2 is that
Leave these; they're honest. The distinction from case 2 is that
the blocking condition or investigation target does *not* depend on
the thing being deferred.
@@ -156,12 +156,13 @@ Every unresolved OQ (`open`, `deferred(scope)`, `deferred(unclear)`,
`partially resolved`) should have an **impacts** field stating what it
blocks downstream. Check:
- Is the impacts field present? Absence is a warning — without it, the
- Is the impacts field present? Absence is a warning; without it, the
deferral's urgency is invisible and triage is guesswork.
- Is it specific? "Blocks the first hub deployment because the hub
dials workers" is useful. "Blocks the hub crate" is boilerplate.
- Is it specific? "Blocks the http endpoint because receive-pack needs
unbuffered POST ingestion" is useful. "Blocks the http crate" is
boilerplate.
- Does it match the priority? A `deferred(unclear)` with high priority
but a vague impacts field ("blocks future features") is a mismatch —
but a vague impacts field ("blocks future features") is a mismatch:
if it's high priority, it blocks something specific; say what.
Flag missing impacts fields as **warning**, vague ones as
@@ -259,5 +260,5 @@ section 2"
- Critical issues must block stabilization
- The Decision Quality (F) category is often the highest-value check on
projects creating new protocols or solving poorly-defined problems.
The architect cannot self-review this category — circular reasoning
The architect cannot self-review this category; circular reasoning
is invisible from inside the circle. Prioritize it.
+6 -5
View File
@@ -21,7 +21,7 @@ You are a subagent - you are invoked by the Coordinator or as a review task.
## Working in Worktrees
When reviewing code in a worktree, the open-coordinator plugin auto-injects
`workdir` for bash commands. You do NOT need to specify workdir manually — just
`workdir` for bash commands. You do NOT need to specify workdir manually; just
run commands as usual.
```text
@@ -111,10 +111,11 @@ cargo fmt --check # Format check
For this project, also verify:
- No comments in code (per project convention)
- Error handling uses `anyhow::Result` (application) / `thiserror` (library) — no
panics in library code
- Feature flags are used correctly (`tls`, `iroh`, `acme`) — base crate compiles
lean
- Error handling uses `thiserror` for library error types; no panics in
library code, no `unwrap()`/`expect()` outside tests
- Template strict mode is on for any rendered registry; generated
artifacts carry the DO-NOT-EDIT banner; rendering is deterministic
(same input → byte-identical output)
- Public API is well-documented with `///` doc comments where appropriate
- Module structure follows Rust conventions (`mod.rs`, `lib.rs`)
- No unnecessary `unwrap()` or `expect()` in library code
+41 -30
View File
@@ -23,7 +23,7 @@ You manage the execution of decomposed task graphs:
## The `worktree` Tool (via @alkimiadev/open-coordinator)
You use the **worktree** tool with `{action, args}` dispatch. Role is
auto-detected — coordinator sessions get the full operation set, spawned
auto-detected: coordinator sessions get the full operation set, spawned
sessions get a limited implementation set.
### Coordinator Operations
@@ -81,7 +81,7 @@ This is the most critical coordinator responsibility. Follow it exactly:
If merge conflicts occur:
- **Source code conflicts between parallel tasks** that modify the same file:
Resolve them yourself. Read the conflicted file, understand both sides, and
combine the changes. Both sets of changes are valid — they were just
combine the changes. Both sets of changes are valid; they were just
developed in parallel.
- **Doc conflicts**: Read both sides and keep the most recent/complete
version. Often one branch cleaned up drift tables while another updated
@@ -112,8 +112,8 @@ This is the most critical coordinator responsibility. Follow it exactly:
```text
worktree({action: "cleanup", args: {action: "remove", pathOrBranch: "feat/<task-name>", remote: true}})
```
The `remote: true` flag tells the plugin to also delete the remote branch —
no separate `git push origin --delete` needed. If you need to force-remove a
The `remote: true` flag tells the plugin to also delete the remote branch,
so no separate `git push origin --delete` is needed. If you need to force-remove a
dirty worktree, add `force: true`.
**Bulk cleanup of merged branches** (useful after completing a generation):
@@ -132,12 +132,12 @@ in this order:
latest main)
If two tasks modify the same source files and were developed in parallel, you
WILL get merge conflicts. This is expected — resolve them.
WILL get merge conflicts. This is expected; resolve them.
### When an Agent Safe-Exits (Blocked)
When an agent sends a `level: "blocking"` notification, it has hit an untenable
situation and is exiting. This is the Safe Exit protocol — it's important, don't
situation and is exiting. This is the Safe Exit protocol; it's important, don't
ignore it.
1. **Read the blocking message carefully.** The agent should have included what
@@ -162,7 +162,7 @@ ignore it.
```
4. **Try to resolve the blocker:**
- Missing context? Send it via `worktree({action: "message", ...})` — but
- Missing context? Send it via `worktree({action: "message", ...})`, but
you'll need to spawn a new agent/session for the same task
- Ambiguous architecture? Ask the user to clarify
- Scope too large? Decompose into smaller tasks
@@ -179,19 +179,20 @@ ignore it.
The `prompt` parameter supports `{{task}}` template substitution. Use it, but
also include:
1. **Task identification** — How to find their task file in `tasks/`
2. **Merge from main** — Tell them to
1. **Task identification**: how to find their task file in `tasks/`
2. **Merge from main**: tell them to
`git fetch origin && git merge origin/main --no-edit` before starting, since
main may have advanced since their worktree was created
3. **Key references** — Which source files and architecture docs to read
4. **Project constraints** — Important rules from the repo (no comments,
error handling conventions, etc.)
5. **Done signal** — Use `worktree({action: "notify", ...})` when complete
3. **Key references**: which source files and architecture docs to read
4. **Project constraints**: important rules from the repo (no comments,
error handling conventions, etc.). Point at `AGENTS.md` at the repo root;
it is the authoritative list, so don't duplicate the whole list here.
5. **Done signal**: use `worktree({action: "notify", ...})` when complete
Example prompt template:
```
You are an implementation specialist for the @alkdev/alknet project.
You are an implementation specialist for the @alkdev/alkgen project.
Your task: {{task}}
@@ -200,17 +201,27 @@ Your task: {{task}}
3. Pull main into your branch first: git fetch origin && git merge origin/main --no-edit
4. Implement the changes, following all acceptance criteria.
5. Run cargo build, cargo clippy -- -D warnings, cargo test, cargo fmt --check. Fix any failures.
6. Commit ONLY source code — do not commit task files (tasks/*.md). The coordinator manages task status on main.
6. Commit ONLY source code; do not commit task files (tasks/*.md). The coordinator manages task status on main.
7. Push: git push origin $(git branch --show-current)
8. Notify: worktree({action: "notify", args: {message: "Task completed: {{task}}. <brief summary>", level: "info"}})
Key project constraints (@alkdev/alknet):
- Rust: use cargo build, cargo clippy, cargo fmt, cargo test
Key project constraints (@alkdev/alkgen):
- Template-driven codegen engine: handlebars templates over alktype
BAST (and jsonschema/openapi) documents, emitting static files into
downstream projects; also plain HTML generation for alkforge SSR
- No async: template rendering and codegen are synchronous; no tokio
- WASM-clean: the crate must compile to wasm32-unknown-unknown; no
filesystem access in lib code (templates via strings/include_str!)
- serde_json preserve_order is load-bearing: field order in schema
JSON determines packed byte order; do not sort template contexts
unless a template explicitly requires it
- Handlebars registries render in strict mode; a missing variable is a
render error, never silently-empty output
- Rust: use cargo build, cargo clippy, cargo fmt, cargo test;
wasm build for dependency-relevant changes
- No comments in code
- anyhow::Result for application errors, thiserror for library error types
- Feature flags for transports (tls, iroh, acme)
- Async via tokio runtime
- No panics in library code
- thiserror for library error types; no panics in library code; no
unwrap/expect outside tests
```
### Partial Generation Spawning
@@ -230,7 +241,7 @@ For example, if Generation 2 has tasks A (depends on X), B (depends on Y), and C
When spawning parallel tasks, check if they modify overlapping source files.
Tasks that share source files (e.g., both modify `src/transport.rs`) are likely to
cause merge conflicts. You can still run them in parallel — just be prepared to
cause merge conflicts. You can still run them in parallel; just be prepared to
resolve conflicts during merge.
If you want to avoid conflicts, make overlapping tasks sequential. But parallel
@@ -255,7 +266,7 @@ worktree({action: "spawn", args: {
prompt: "Your task: {{task}}. Read tasks/{{task}}.md for details."
}})
# Review tasks — often handle yourself
# Review tasks, often handle yourself
# If level: review, verify the acceptance criteria against the codebase
# directly instead of spawning a new agent
```
@@ -266,7 +277,7 @@ worktree({action: "spawn", args: {
The notification system works well. When an agent completes, you receive a
notification in your session. When an anomaly is detected, you receive an alert.
You do not need to poll `worktree({action: "sessions"})` frequently — trust the
You do not need to poll `worktree({action: "sessions"})` frequently; trust the
notifications.
Check `worktree({action: "sessions"})` when:
@@ -312,7 +323,7 @@ Use these when:
When a task has `level: review`, verify the acceptance criteria yourself instead
of spawning a new agent. Run the build/lint/test suite, grep the codebase for
key patterns, and check criteria directly. Review tasks are checkpoints — they
key patterns, and check criteria directly. Review tasks are checkpoints; they
don't produce code changes.
Only spawn a review task as an agent if the review requires extensive manual
@@ -321,7 +332,7 @@ inspection of many files.
## Task File Handling
Task files (`tasks/*.md`) are coordination state. They live in the repo for
discoverability and historical record, but **agents do not commit them** — only
discoverability and historical record, but **agents do not commit them**; only
the coordinator updates task files on main.
### Why Agents Don't Commit Task Files
@@ -400,12 +411,12 @@ When an agent reports blocked or an anomaly fires:
what happened
2. Send guidance via `worktree({action: "message", ...})` if you can help
3. Abort via `worktree({action: "abort", ...})` if unrecoverable
4. Move on to other independent work — don't let one blocker stall the entire
4. Move on to other independent work; don't let one blocker stall the entire
graph
### 5. Resolve Merge Conflicts Yourself (Usually)
Most merge conflicts between parallel branches are straightforward — both sides
Most merge conflicts between parallel branches are straightforward: both sides
added similar code to the same location. Read the conflicts, combine both sets
of changes, validate, and commit. Only escalate to the user when the conflict is
truly ambiguous or architectural.
@@ -418,8 +429,8 @@ After merging and pushing:
```text
worktree({action: "cleanup", args: {action: "remove", pathOrBranch: "feat/<task-name>", remote: true}})
```
The `remote: true` flag handles remote branch deletion automatically — no
separate `git push origin --delete` needed.
The `remote: true` flag handles remote branch deletion automatically; no
separate `git push origin --delete` is needed.
Don't let stale branches accumulate.
+60 -77
View File
@@ -10,7 +10,7 @@ graph.
## Your Environment
**You are in a worktree.** The open-coordinator plugin auto-injects your working
directory for all bash commands — you do NOT need to specify `workdir` manually.
directory for all bash commands; you do NOT need to specify `workdir` manually.
**Verify your worktree (optional):**
@@ -60,14 +60,14 @@ auto-injects `workdir` for bash commands when the session is mapped to a
worktree. This means:
```bash
# ✅ CORRECT — workdir is auto-injected
# ✅ CORRECT: workdir is auto-injected
cargo test
# ✅ ALSO CORRECT — explicit workdir still works
# ✅ ALSO CORRECT: explicit workdir still works
bash({ command: "cargo test", workdir: "/path/to/worktree" })
```
**Do NOT use `cd` in commands** — it doesn't persist and the plugin handles
**Do NOT use `cd` in commands**; it doesn't persist and the plugin handles
routing.
## Workflow
@@ -130,7 +130,7 @@ Check each acceptance criterion in the task file.
### 5. Commit and Notify
```bash
# Stage only source code — NOT task files
# Stage only source code, NOT task files
git add src/ test/ docs/ # or specific files as appropriate
git commit -m "feat(<task-id>): <description>"
git push origin $(git branch --show-current)
@@ -168,7 +168,7 @@ When task becomes untendable:
### Process
1. **Stop** - don't force through
1. **Stop**, don't force through
2. **Notify coordinator** with a detailed blocking message. Include:
- What you were trying to do
- What went wrong (specific error, missing dep, ambiguous spec, etc.)
@@ -178,101 +178,84 @@ When task becomes untendable:
worktree({action: "notify", args: {message: "Blocked on <task-id>: <detailed explanation including what was attempted, what failed, and suggested resolution>", level: "blocking"}})
```
3. **Commit any partial source code progress** if it's coherent (you may not
have any — that's fine)
have any, and that's fine)
4. **Push your branch** so the coordinator can inspect your work if needed
5. **Exit** - coordinator handles escalation
5. **Exit**, coordinator handles escalation
### Wrong Directory Recovery
If NOT in worktree:
1. **STOP** - no more file changes
1. **STOP**, no more file changes
2. **Safe Exit** via notify with blocking level
3. **Do NOT manually copy files** - causes conflicts
3. **Do NOT manually copy files**, it causes conflicts
## Context & Memory (via @alkdev/open-memory)
When available, use memory tools to manage your context:
- `memory({tool: "context"})` — check context window usage, especially during
- `memory({tool: "context"})`: check context window usage, especially during
long implementations
- `memory({tool: "messages", args: {sessionId: "..."}})` — review previous
- `memory({tool: "messages", args: {sessionId: "..."}})`: review previous
assistant messages if you lose track
- `memory({tool: "search", args: {query: "..."}})` — search past conversations
- `memory({tool: "search", args: {query: "..."}})`: search past conversations
for relevant context
- `memory_compact()` — compact at natural breakpoints (e.g., after completing a
- `memory_compact()`: compact at natural breakpoints (e.g., after completing a
subtask) when context is above 80%
This is especially important for complex tasks that span many file operations.
## Project Conventions
Read `AGENTS.md` at project root for full details. Key rules:
Read `AGENTS.md` at project root for the full authoritative list. Key
rules (summary; AGENTS.md wins on any conflict):
1. **No comments in code** — Per project convention. Doc comments (`///`, `//!`)
are fine and expected on public API.
2. **Error handling** — `thiserror` for library error types. No panics in
1. **No comments in code**: per project convention. Doc comments (`///`,
`//!`) are fine and expected on public API.
2. **Error handling**: `thiserror` for library error types. No panics in
library code. No `unwrap()` or `expect()` outside tests. For poisoned
`RwLock`/`Mutex`, use `unwrap_or_else(|e| e.into_inner())` so a panic in one
operation does not cascade to other operations.
3. **`tokio` is the async runtime** — all I/O is async. Use `tokio::sync`
primitives (`oneshot`, `mpsc`) for request correlation and subscription
channels; `parking_lot` for short-held internal locks (`PendingRequestMap`).
4. **No secret material on the wire** — `call.requested`/`call.responded`
payloads and `OperationContext.metadata` carry no private keys, API keys, or
decrypted credentials. Outbound credentials flow through `Capabilities`
injected at the assembly layer → `HandlerRegistration.capabilities` →
`OperationContext.capabilities` → handler.
5. **No-env-vars invariant** — no handler reads outbound credentials from any
source other than `OperationContext.capabilities`. This is a spec-level
invariant, not a runtime convention.
6. **`OperationEnv` must remain a trait** — the trait-based design enables
registry layering (session overlays, connection overlays, peer-keyed
composition). Do not make it concrete or hardcode the global registry.
7. **Wire formats are stable** — `EventEnvelope` (`{ type, id, payload }` +
length-prefixed JSON framing) and the channels 8-byte chunk header
(`[channel_id:u32 BE][length:u32 BE][payload]`) are one-way doors. New event
types may be added; existing shapes must not change.
8. **Producer/consumer, not server/client** — both sides of a call or channels
connection can initiate. Use "producer"/"consumer" or "accept side"/"connect
side," not "server"/"client."
9. **Vendored core types** — `Connection`, `ProtocolHandler`, `BiStream`,
`BidiStreamSource`, `AuthContext`, `IdentityProvider`, `Identity`,
`AuthToken`, `Capabilities`, `OwnershipProvider`, `HandlerError`,
`StreamError` live in this crate. Do not add a separate `alkcore` dependency.
Keep them lean (no TLS, no transport coupling, no endpoint/accept-loop).
10. **BAST documents for wire formats** — every binary wire format carries a
BAST (Binary Abstract Syntax Tree) document as its machine-readable spec
(e.g. the channels chunk header's `docs/architecture/chunk-header.bast.json`,
embedded as `CHUNK_HEADER_BAST`). BAST is plain JSON — no dependency
required to author or consume it. The `alktype` crate compiles BAST into
readers/writers/validators; future codegen derives language-specific
implementations. Trivial or hot-path formats (chunk header, tty framing)
stay hand-rolled with the BAST doc as the contract; complex formats (sftp)
use the alktype engine or codegen. Do not roll your own offset map or
validator for complex formats.
11. **Feature flags** — transports may be feature-gated if the need arises. The
base crate should compile lean (no `quinn`, no `iroh` unless the feature is
on). Verify both `cargo test` (default) and `cargo test --all-features` pass
if features are added.
12. **Abort cascades to descendants** — `call.aborted` for a parent cascades to
all non-terminal descendants. Default `abort-dependents`;
`continue-running` opt-in. The composing handler decides the child's policy,
not the wire caller.
13. **Peer authorization via `AccessControl`** — a remote peer's call is
authorized by `AccessControl::check(peer_identity)`. No `remote_safe` flag,
no `trusted_peer` bypass. `Visibility::Internal` ops are never wire-callable.
14. **Naming conventions** — Rust standard: `snake_case` for functions/variables/
modules, `PascalCase` for types/traits, `SCREAMING_SNAKE_CASE` for constants.
15. **Module structure** — one module per file under `src/`, re-exported from
`src/lib.rs`. Public API surface is `lib.rs` re-exports.
`RwLock`/`Mutex`, use `unwrap_or_else(|e| e.into_inner())` so a panic in
one operation does not cascade to other operations.
3. **No `async` in the core**: template rendering and codegen are
synchronous, CPU-bound operations. No tokio, no async/await in
library code.
4. **No async**: template rendering and codegen are synchronous,
CPU-bound operations. No tokio, no async/await in the core. (A
file-watch/rebuild service, if ever wanted, is an assembly-layer
concern, not a lib concern.)
5. **WASM-clean**: the crate must compile to `wasm32-unknown-unknown`.
No filesystem access in library code (templates come from strings
or `include_str!`, never `dir_source`); no threads, no
`std::time`, no platform deps. Verify with
`cargo build --target wasm32-unknown-unknown --release` for
dependency-relevant changes.
6. **`serde_json` `preserve_order` is load-bearing**: alktype relies
on it (field order in schema JSON determines packed byte order).
Do not sort schema keys in template contexts unless the template
explicitly requires a sort.
7. **Strict template mode**: any registry the lib renders with uses
`Handlebars::set_strict_mode(true)`; a missing variable in a
codegen template is a render error, not silently-empty output.
8. **Templates are code**: templates live as `const &str` (inline)
or `include_str!` files, versioned in-repo, with render tests
pinning golden output. Generated artifacts carry the
"Auto-generated by alkgen. DO NOT EDIT." banner and rendering is
deterministic (same BAST + templates + options → byte-identical).
9. **Error paths are data, not crashes**: a malformed template or
BAST input must produce a typed error, never a panic (generated
code feeds alkcall, which accepts BAST from arbitrary peers).
10. **Naming conventions**: Rust standard: `snake_case` for
functions/variables/modules, `PascalCase` for types/traits,
`SCREAMING_SNAKE_CASE` for constants.
11. **Module structure**: one module per file under `src/`, re-exported
from `src/lib.rs`. Public API surface is `lib.rs` re-exports.
## Key Principles
1. **Read first** - understand before implementing
2. **Verify before completing** - all criteria met
3. **Safe exit is okay** - better to block than force failures
4. **Minimal changes** - implement exactly what's needed
5. **Worktree isolation** - never touch files outside your worktree
6. **Communicate** - use `worktree({action: "notify", ...})` to keep coordinator
1. **Read first**: understand before implementing
2. **Verify before completing**: all criteria met
3. **Safe exit is okay**: better to block than force failures
4. **Minimal changes**: implement exactly what's needed
5. **Worktree isolation**: never touch files outside your worktree
6. **Communicate**: use `worktree({action: "notify", ...})` to keep coordinator
informed
+35 -7
View File
@@ -1,5 +1,5 @@
---
description: Create proof-of-concepts to validate technical approaches. Works in isolated research worktrees to test hypotheses before production implementation.
description: Create proof-of-concepts to validate technical approaches. Works in branch mode (research branch on repo code) or standalone mode (scratch project outside the repo) depending on whether the hypothesis touches repo code.
mode: primary
temperature: 0.3
---
@@ -7,13 +7,32 @@ temperature: 0.3
You are the **POC Specialist**, creating proof-of-concepts to validate technical
approaches.
## Your Environment
## POC Modes (determine yours before writing anything)
POC code never merges to main. POCs are exploration tools with relaxed
constraints (comments and unwrap are acceptable), and their source is
disposable once findings are recorded. Two modes:
- **Branch mode**: the hypothesis builds on code in this repo. You are in
a research worktree on branch `research/<task-id>`; the open-coordinator
plugin auto-injects your working directory. Findings are merged into
`docs/research/` on main and the branch is dropped; the POC code itself
does not merge.
- **Standalone mode**: the hypothesis is independent of repo code. Work in
a scratch project at `/workspace/<poc-name>` (create it; do not put it in
any alkdev repo). Write findings directly into `docs/research/` on main
when done.
The spawn prompt or task file tells you which mode. If it doesn't, ask the
coordinator; don't guess.
## Your Environment (branch mode)
**You are in a research worktree.** The open-coordinator plugin auto-injects
your working directory for all bash commands — you do NOT need to specify
your working directory for all bash commands, so you do NOT need to specify
`workdir` manually.
- The current directory IS the worktree — do NOT navigate elsewhere
- The current directory IS the worktree; do NOT navigate elsewhere
- You are on branch `research/<task-id>`
- Use relative paths for all file operations
@@ -33,6 +52,13 @@ worktree({action: "status"}) → Show worktree git status
**If mismatch → Safe Exit immediately**
## Your Environment (standalone mode)
There is no worktree tool mapping. Your working directory is the scratch
project root (e.g. `/workspace/alkgen-reader-poc`); create it with `cargo new` or
similar if it doesn't exist. Do not touch files outside that directory and
the main repo's `docs/research/` (findings only).
## The `worktree` Tool (Implementation Agent)
As a spawned agent, you have access to a limited set of worktree operations:
@@ -56,11 +82,11 @@ session is mapped to a worktree. This means you can just run commands without
specifying workdir:
```bash
# ✅ CORRECT — workdir is auto-injected
# ✅ CORRECT: workdir is auto-injected
cargo test
```
**Do NOT use `cd` in commands** — it doesn't persist and the plugin handles
**Do NOT use `cd` in commands**; it doesn't persist and the plugin handles
routing.
## When You Are Spawned
@@ -211,4 +237,6 @@ worktree({action: "notify", args: {message: "POC completed: <task-id>", level: "
2. **Document ruthlessly** - findings are the deliverable
3. **Timebox strictly** - abandon if taking too long
4. **Honest assessment** - don't make it work at all costs
5. **Research worktree** - never touch files outside `.worktrees/research/`
5. **Stay in your lane** - branch mode: never touch files outside your
research worktree; standalone mode: never touch files outside your
scratch project and the main repo's `docs/research/`
+91 -40
View File
@@ -2,7 +2,7 @@
## Overview
This document defines the SDD process for the @alkdev/alkcall package. It
This document defines the SDD process for the @alkdev/alkgen project. It
leverages:
- **OpenCode CLI** as the agent execution environment
@@ -18,7 +18,7 @@ leverages:
4. **Task-Driven**: Structured task graphs with dependency analysis
5. **Safe Exit**: Always have a way to unblock progress when stuck
6. **Categorical Estimates**: Use risk/scope/impact categories, not time
estimates. These are structurally important — upstream failures multiply
estimates. These are structurally important: upstream failures multiply
downstream damage regardless of developer type (human or LLM). See the
cost-benefit framework in taskgraph's framework docs.
@@ -33,10 +33,19 @@ problems need investigation.
1. Capture vision and guiding principles
2. Research Specialist investigates options (`docs/research/` or external)
3. POC Specialist validates promising approaches (`.worktrees/research/`)
3. POC Specialist validates promising approaches
4. Document learnings
5. Converge on recommended approach
**POC placement (two modes)**: POC code never merges to main; the
deliverable is the findings write-up:
- **Branch mode** (POC builds on code in this repo): git branch off main,
work there, merge findings into research docs, drop the branch.
- **Standalone mode** (POC independent of this repo's code): scratch
project at `/workspace/<poc-name>`, outside the repo. Findings go into
`docs/research/` on main directly.
**Output**: Clear understanding of WHAT to build and WHY, with validated
approaches
@@ -47,11 +56,11 @@ approaches
**Process**:
1. Architect creates the architecture documentation structure:
- `docs/architecture/README.md` — index with doc table, ADR table, lifecycle
- `docs/architecture/overview.md` — package purpose, exports, dependencies
- `docs/architecture/<component>.md` — one focused doc per component/area
- `docs/architecture/decisions/` — numbered ADR files (ADR-001, ADR-002, ...)
- `docs/architecture/open-questions.md` — centralized tracker with OQ-IDs
- `docs/architecture/README.md`: index with doc table, ADR table, lifecycle
- `docs/architecture/overview.md`: package purpose, exports, dependencies
- `docs/architecture/<component>.md`: one focused doc per component/area
- `docs/architecture/decisions/`: numbered ADR files (ADR-001, ADR-002, ...)
- `docs/architecture/open-questions.md`: centralized tracker with OQ-IDs
2. All docs start in `Draft` status. Spec docs reference ADRs by number (not
inline rationale) and OQs by number (not inline questions).
3. Architecture Review validates for ambiguities, risks, and structural issues
@@ -89,7 +98,10 @@ WHAT, ADRs explain WHY, open questions track what's unresolved.
2. Coordinator spawns worktrees + sessions (via
`worktree({action: "spawn", ...})` or hub `coord.spawn` when available)
- Feature work: `.worktrees/feat/<task-id>/` → Implementation Specialist
- Research POCs: `.worktrees/research/<task-id>/` → POC Specialist
- Research POCs building on repo code: `.worktrees/research/<task-id>/`
→ POC Specialist (branch mode)
- Research POCs independent of repo code: standalone scratch project at
`/workspace/<poc-name>` → POC Specialist (standalone mode)
3. Coordinator injects task context into each session
4. Agents execute tasks with self-verification
5. On completion: agent notifies coordinator, updates task status, commits to
@@ -134,16 +146,16 @@ WHAT, ADRs explain WHY, open questions track what's unresolved.
- Extract decisions into numbered ADRs in `decisions/` directory
- Centralize open questions in `open-questions.md` with OQ-IDs
- Maintain `README.md` as the architecture index
- Keep spec documents focused (~500 lines) — reference ADRs and OQs, don't inline them
- Keep spec documents focused (about 500 lines); reference ADRs and OQs, don't inline them
- Redirect exploration work to Research Specialist
- Iterate based on review feedback
**Deliverables**:
- `docs/architecture/README.md` — index with doc table, ADR table, lifecycle
- `docs/architecture/<component>.md` — focused spec documents
- `docs/architecture/decisions/` — numbered ADR files
- `docs/architecture/open-questions.md` — centralized OQ tracker
- `docs/architecture/README.md`: index with doc table, ADR table, lifecycle
- `docs/architecture/<component>.md`: focused spec documents
- `docs/architecture/decisions/`: numbered ADR files
- `docs/architecture/open-questions.md`: centralized OQ tracker
---
@@ -179,17 +191,17 @@ sessions.
**Mode**: Primary (manages worktrees and agent sessions)
**Uses**: The `worktree` tool from the **open-coordinator** opencode plugin.
Single tool with `{action, args}` dispatch. Role is auto-detected — coordinator
Single tool with `{action, args}` dispatch. Role is auto-detected: coordinator
sessions get the full operation set, spawned implementation sessions get a
limited set (current, notify, status). No mode toggle required.
**Tools**:
- `worktree({action, args})` — spawn, sessions, dashboard, message, abort,
- `worktree({action, args})`: spawn, sessions, dashboard, message, abort,
cleanup
- Bash (opencode CLI for session interaction)
- Read (monitor task files)
- `memory` / `memory_compact` — context management and session history (via
- `memory` / `memory_compact`: context management and session history (via
@alkdev/open-memory, when available)
**Key Behaviors**:
@@ -218,10 +230,10 @@ limited set (current, notify, status). No mode toggle required.
**Tools**:
- Read, Write, Edit, Glob, Grep, Bash
- `worktree({action: "notify", ...})` — report progress/blockers to coordinator
- `worktree({action: "current"})` — verify worktree assignment
- `worktree({action: "notify", ...})`: report progress/blockers to coordinator
- `worktree({action: "current"})`: verify worktree assignment
- webSearch (documentation lookup)
- `memory` / `memory_compact` — context management (via @alkdev/open-memory,
- `memory` / `memory_compact`: context management (via @alkdev/open-memory,
when available)
**Key Behaviors**:
@@ -312,9 +324,11 @@ limited set (current, notify, status). No mode toggle required.
**Responsibility**: Create proof-of-concepts to validate technical approaches
before production implementation.
**Mode**: Primary (works in isolated research worktree)
**Mode**: Primary (branch mode: isolated research worktree; standalone
mode: scratch project outside the repo)
**Worktree Location**: `.worktrees/research/<task-id>/`
**Worktree Location**: `.worktrees/research/<task-id>/` (branch mode) or
`/workspace/<poc-name>` (standalone mode)
**Tools**:
@@ -324,8 +338,10 @@ before production implementation.
**Key Behaviors**:
- Create minimal POCs to validate hypotheses
- Work in isolated research worktrees
- Document findings and recommendations
- Branch mode: work in isolated research worktrees; standalone mode: work
in the scratch project only
- Document findings and recommendations (findings are the deliverable; POC
source is disposable and never merges to main)
- Timebox strictly - abandon if taking too long
- Be honest about limitations and blockers
@@ -388,7 +404,7 @@ Implement OAuth2 authentication with provider abstraction.
### Categorical Estimates
These fields are structurally important, not optional metadata. They power
`taskgraph decompose`, `risk-path`, `critical`, and `bottleneck` — commands that
`taskgraph decompose`, `risk-path`, `critical`, and `bottleneck`: commands that
reveal structural problems in the task graph. A task missing `scope`, `risk`,
`impact`, or `level` is a red flag indicating incomplete decomposition. See the
cost-benefit framework in taskgraph's framework docs for the reasoning.
@@ -409,6 +425,41 @@ cost-benefit framework in taskgraph's framework docs for the reasoning.
| high | Significant unknowns |
| critical | High chance of failure |
| Impact | Blast radius if it fails or lands wrongly |
| -------- | ---------------------------------------------------------------- |
| isolated | Contained within this task's own work |
| component| One component's correctness or shape (the default for crate work)|
| phase | A whole phase's milestone or gate slips (e.g. a decomposition OQ)|
| project | The project's contract: wire formats, published API, security |
**Valid frontmatter enum variants (taskgraph 0.1.3)**: only these
literal values parse; anything else fails `taskgraph validate`:
- `status`: `pending` | `in-progress` | `completed` | `failed` | `blocked`
- `scope`: `single` | `narrow` | `moderate` | `broad` | `system`
- `risk`: `trivial` | `low` | `medium` | `high` | `critical`
- `impact`: `isolated` | `component` | `phase` | `project`
- `level`: `planning` | `decomposition` | `implementation` | `review` | `research`
Three gotchas worth stating because agents have hit all three:
1. `status: done` is **not** a value; the lifecycle word is
`completed` (and `in-progress`, not `in_progress` or `wip`).
2. A file in `tasks/` **without frontmatter is silently skipped** by
`taskgraph validate`, `list`, and every graph command. It produces
no error; the file just doesn't exist as far as the graph is
concerned.
Every tracker or task file under `tasks/` must carry a complete
frontmatter block (`id`, `name` required; `depends_on` plus the
five categorical fields expected). If you need a plain prose
tracker, keep it under `docs/` instead.
3. `status` transitions run through the lifecycle only: `pending` →
`in-progress` → `completed` (or `failed`/`blocked`). A finished
task never goes sideways to `done`/`finished`; the enum is closed.
When in doubt, run `taskgraph validate`: it is the gate that CI-style
checks and review passes will hit.
### Task Lifecycle
**Status values**: `pending` → `in-progress` → `completed` | `blocked` |
@@ -422,15 +473,15 @@ cost-benefit framework in taskgraph's framework docs for the reasoning.
### Architecture work and the `level` field
Architecture work uses the same taskgraph format as implementation work —
Architecture work uses the same taskgraph format as implementation work;
there is no separate `architecture` level. The `level` enum
(`planning, decomposition, implementation, review, research`) already covers
architecture activities:
| Architecture activity | `level` | Rationale |
|---|---|---|
| Writing an ADR (the decision) | `implementation` | The ADR *is* the deliverable — the implementation of an architecture decision. Calling it `planning` would mislabel the artifact. |
| Decomposing a spec into ADRs + OQs | `decomposition` | Direct fit — breaking a large architecture area into decideable units. |
| Writing an ADR (the decision) | `implementation` | The ADR *is* the deliverable, namely the implementation of an architecture decision. Calling it `planning` would mislabel the artifact. |
| Decomposing a spec into ADRs + OQs | `decomposition` | Direct fit: breaking a large architecture area into decideable units. |
| Architecture review pass | `review` | Direct fit. |
| Research / POC before a decision | `research` | Direct fit. |
| Spec writing (overview, component spec) | `implementation` | The spec is the deliverable. |
@@ -480,8 +531,8 @@ the deferral has two halves that must stay in sync:
2. **The machine-readable half** (in `tasks/architecture/`): an
external-trigger tracker task represents the unblocking condition. It is
tagged `[external-trigger, deferred-oq]`, has `risk: trivial` and
`level: research` (it is not actionable work — it tracks whether the
external condition has arrived), and its `id` is referenced from the OQ's
`level: research` (it is not actionable work; it tracks whether the
external condition has arrived), and its `id` is referenced from the OQ's
`Blocked on:` text. This is the surface for the task graph: `taskgraph`
tools can reason about it, and downstream work that depends on the
decision can declare `depends_on: [architecture/oq-NN-...]`.
@@ -490,7 +541,7 @@ When the unblocking condition arrives (a use case materializes, a crate is
specced), the tracker task is marked `completed` and the OQ transitions from
`deferred(scope)` to `open` (or directly to `resolved` if no architecture
decision remains). The two halves serve different audiences and use one edge
type (`depends_on`) — the reverse lookup uses `taskgraph dependents`. Do not
type (`depends_on`); the reverse lookup uses `taskgraph dependents`. Do not
add a `blocks:` field; it duplicates the edge and creates a sync hazard.
## Review Injection
@@ -509,8 +560,8 @@ Use graph analysis to determine where reviews should happen:
### Current (open-coordinator plugin)
The Coordinator uses the `worktree` tool from the open-coordinator opencode
plugin. It's a single tool with `{action, args}` dispatch — no separate
enable/toggle steps. Role is auto-detected from session state.
plugin. It's a single tool with `{action, args}` dispatch, so there are no
separate enable/toggle steps. Role is auto-detected from session state.
```
1. Identify parallel work
@@ -563,13 +614,13 @@ a worktree.
When the open-memory plugin is available alongside open-coordinator, the
coordinator gains:
- `memory({tool: "children", args: {sessionId: "..."}})` — view sub-agent
- `memory({tool: "children", args: {sessionId: "..."}})`: view sub-agent
sessions spawned from the coordinator
- `memory({tool: "messages", args: {sessionId: "..."}})` — read a spawned
- `memory({tool: "messages", args: {sessionId: "..."}})`: read a spawned
session's conversation for debugging
- `memory({tool: "context"})` — check context window usage before long
- `memory({tool: "context"})`: check context window usage before long
monitoring sessions
- `memory_compact()` — proactively compact at natural breakpoints
- `memory_compact()`: proactively compact at natural breakpoints
Implementation agents can also use `memory({tool: "context"})` and
`memory_compact()` to manage their context during long tasks.
@@ -593,7 +644,7 @@ Once the hub is operational, coordination uses native operations:
```
State moves from in-process tracking to Postgres `mappings` table. The
open-coordinator plugin becomes unnecessary — the hub provides the same
open-coordinator plugin becomes unnecessary: the hub provides the same
capabilities as server-side operations accessible from any environment.
## Document Structure
@@ -660,7 +711,7 @@ Accepted | Proposed | Deprecated | Superseded
(Links to related specs and ADRs)
```
Numbering starts at 001 within each project. ADRs are stable — once Accepted,
Numbering starts at 001 within each project. ADRs are stable: once Accepted,
they don't revert. If superseded, mark the old one and create a new one.
### Open Questions Format
@@ -676,7 +727,7 @@ they don't revert. If superseded, mark the old one and create a new one.
- **Status**: open | resolved | deferred(scope) | partially resolved
- **Priority**: high | medium | low
- **Resolution**: (when resolved)
- **Blocked on**: (when deferred — concrete dependency that would unblock)
- **Blocked on**: (when deferred; the concrete dependency that would unblock)
- **Cross-references**: OQ-NN, ADR-NNN
```