tasks(arch): adopt taskgraph for architecture work — blocker tasks for deferred OQs, level mapping, status fixes

Establishes the tasks/architecture/ blocker-task half of the Safe Exit
protocol. Each deferred OQ now has an external-trigger tracker task
representing its unblocking condition, and the OQ's Blocked on field names
the tracker task ID — closing the gap where the deferral field existed but
the task-graph half was unenforced.

Changes:
- Add 6 external-trigger tracker tasks for the deferred OQs (OQ-09, 10, 32,
  41, 44, 46) under tasks/architecture/, tagged [external-trigger, deferred-oq]
- Add structured Blocked on field to OQ-09 and OQ-10 (previously used legacy
  'deferred' status with the reason in the Resolution prose); index now
  surfaces all 6 deferred OQs with concrete conditions, no placeholders
- Document the architecture-task level mapping (level: implementation for
  ADRs/specs, decomposition for spec-to-ADR breakdown, planning for backlog
  seeding, research/review direct fits) and the deferred-OQ blocker-task
  pattern in docs/sdd_process.md
- Mark architecture/safe-exit-blocker-task-mechanism and
  architecture/oq-09-10-blocking-conditions completed (implemented by this pass)
- Fix 4 pre-existing ADR-050 implementation tasks: status: done → completed
  (taskgraph enum is 'completed', not 'done') — taskgraph validate now passes
  for all 100 tasks
This commit is contained in:
glm-5.2 committed 2026-07-06 16:34:01 +00:00
1 parent e6062230f9
commit df5d04af1d
16 files changed
+440 -14

No files matched your search

+2 -2
View File
@@ -160,13 +160,13 @@ filtering the tables above.
### OQ-09: WASM Target Boundaries ### OQ-09: WASM Target Boundaries
- **Blocked on**: _(no explicit blocking condition recorded — see full file)_ - **Blocked on**: A concrete server-side WASM use case, or a deliberate confirmation that WASM stays a client-side design constraint. Tracked as `architecture/oq-09-wasm-server-use-case` in `tasks/architecture/`.
- **Priority**: low - **Priority**: low
- **Full file**: [OQ-09](questions/009-wasm-target-boundaries.md) - **Full file**: [OQ-09](questions/009-wasm-target-boundaries.md)
### OQ-10: Git Adapter Scope — Smart Protocol Only or Full Server? ### OQ-10: Git Adapter Scope — Smart Protocol Only or Full Server?
- **Blocked on**: _(no explicit blocking condition recorded — see full file)_ - **Blocked on**: Speccing the alknet-git crate — resolve this when that crate is specified, not deferred past it. Tracked as `architecture/oq-10-git-adapter-spec` in `tasks/architecture/`.
- **Priority**: low - **Priority**: low
- **Full file**: [OQ-10](questions/010-git-adapter-scope-smart-protocol-only-or-full-server.md) - **Full file**: [OQ-10](questions/010-git-adapter-scope-smart-protocol-only-or-full-server.md)
@@ -4,5 +4,6 @@
- **Status**: deferred - **Status**: deferred
- **Door type**: One-way (when applicable) - **Door type**: One-way (when applicable)
- **Priority**: low - **Priority**: low
- **Blocked on**: A concrete server-side WASM use case, or a deliberate confirmation that WASM stays a client-side design constraint. Tracked as `architecture/oq-09-wasm-server-use-case` in `tasks/architecture/`.
- **Resolution**: Not an active question — WASM compatibility is a design constraint (see ADR-009, overview.md design principles), not a deliverable. Specific WASM targeting decisions will be made when individual crates are implemented. **BiStream being a trait preserves the *client-side* stream door** — a browser can implement BiStream over WebTransport streams. **The *server-side* dispatch door is NOT preserved by ADR-007 and is a known, accepted closure**: `Connection` is a concrete quinn-bound struct (not a trait), the accept loop uses `tokio::spawn` (tokio does not run on WASM), and the call-protocol dispatch internals (`PendingRequestMap`, `CallAdapter`) use tokio `oneshot`/`mpsc` channels. A WASM server-side peer would require a `Connection` trait and a runtime-abstracted accept loop — not planned. The browser path is client-side via a JS SDK, not server-side Rust-to-WASM. This is an explicit one-way door, not an oversight. - **Resolution**: Not an active question — WASM compatibility is a design constraint (see ADR-009, overview.md design principles), not a deliverable. Specific WASM targeting decisions will be made when individual crates are implemented. **BiStream being a trait preserves the *client-side* stream door** — a browser can implement BiStream over WebTransport streams. **The *server-side* dispatch door is NOT preserved by ADR-007 and is a known, accepted closure**: `Connection` is a concrete quinn-bound struct (not a trait), the accept loop uses `tokio::spawn` (tokio does not run on WASM), and the call-protocol dispatch internals (`PendingRequestMap`, `CallAdapter`) use tokio `oneshot`/`mpsc` channels. A WASM server-side peer would require a `Connection` trait and a runtime-abstracted accept loop — not planned. The browser path is client-side via a JS SDK, not server-side Rust-to-WASM. This is an explicit one-way door, not an oversight.
- **Cross-references**: ADR-007, ADR-009 - **Cross-references**: ADR-007, ADR-009
@@ -4,5 +4,6 @@
- **Status**: deferred - **Status**: deferred
- **Door type**: Two-way - **Door type**: Two-way
- **Priority**: low - **Priority**: low
- **Blocked on**: Speccing the alknet-git crate — resolve this when that crate is specified, not deferred past it. Tracked as `architecture/oq-10-git-adapter-spec` in `tasks/architecture/`.
- **Resolution**: Deferred per the cleanup plan. Start with git smart protocol over QUIC streams. ERC721 integration and full server capabilities are additive. **Composability fork (review #002 W18)**: whether git operations are registered in the `OperationRegistry` and callable via `env.invoke()`, or only available as raw smart protocol on `alknet/git`, is a separate decision from ERC721 scope. The path of least resistance (raw smart protocol only) forecloses agent composition of git operations — an agent handler that wants to compose `git/clone` cannot, because there's no `OperationSpec`, no `Handler`, no registration. To make git composable, a call-protocol projection (a set of `HandlerRegistration` bundles wrapping git operations behind the registry) must be built alongside or instead of the raw handler. Resolve this when speccing alknet-git, not deferred past it. - **Resolution**: Deferred per the cleanup plan. Start with git smart protocol over QUIC streams. ERC721 integration and full server capabilities are additive. **Composability fork (review #002 W18)**: whether git operations are registered in the `OperationRegistry` and callable via `env.invoke()`, or only available as raw smart protocol on `alknet/git`, is a separate decision from ERC721 scope. The path of least resistance (raw smart protocol only) forecloses agent composition of git operations — an agent handler that wants to compose `git/clone` cannot, because there's no `OperationSpec`, no `Handler`, no registration. To make git composable, a call-protocol projection (a set of `HandlerRegistration` bundles wrapping git operations behind the registry) must be built alongside or instead of the raw handler. Resolve this when speccing alknet-git, not deferred past it.
- **Cross-references**: ADR-001 - **Cross-references**: ADR-001
+48
View File
@@ -420,6 +420,27 @@ cost-benefit framework in taskgraph's framework docs for the reasoning.
2. Fills in `## Summary` section 2. Fills in `## Summary` section
3. Commits changes to worktree branch 3. Commits changes to worktree branch
### Architecture work and the `level` field
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. |
| 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. |
| Seeding a backlog of OQs/ADRs for a crate | `planning` | The upfront graph-shaping pass. |
Architecture tasks live under `tasks/architecture/` and use the standard
task body sections (`## Description`, `## Work`, `## Verification`,
`## Out of scope`). The `## Summary` section is filled on completion, same as
implementation tasks.
## Safe Exit Protocol ## Safe Exit Protocol
When a task becomes untendable: When a task becomes untendable:
@@ -445,6 +466,33 @@ When a task becomes untendable:
3. Document in task notes 3. Document in task notes
4. Notify coordinator 4. Notify coordinator
### Deferred OQs and the blocker-task half
The Safe Exit protocol applies to architecture decisions too, not just
implementation tasks. When an open question (OQ) is marked `deferred(scope)`,
the deferral has two halves that must stay in sync:
1. **The visibility half** (in `docs/architecture/`): the OQ's `Blocked on:`
field names the concrete blocking condition, and the `open-questions.md`
index surfaces it in the cross-theme **Deferred / Blocked** section so
"what's currently parked and why" is answerable at a glance. This is the
human-readable surface for the architect.
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
`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-...]`.
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
add a `blocks:` field; it duplicates the edge and creates a sync hazard.
## Review Injection ## Review Injection
Use graph analysis to determine where reviews should happen: Use graph analysis to determine where reviews should happen:
@@ -1,12 +1,13 @@
--- ---
id: architecture/oq-09-10-blocking-conditions id: architecture/oq-09-10-blocking-conditions
name: Add explicit Blocked on conditions to OQ-09 (WASM) and OQ-10 (Git Adapter) name: Add explicit Blocked on conditions to OQ-09 (WASM) and OQ-10 (Git Adapter)
status: pending status: completed
depends_on: [] depends_on: []
scope: narrow scope: narrow
risk: low risk: low
impact: component impact: component
level: decomposition level: decomposition
tags: [convention]
--- ---
## Description ## Description
@@ -39,8 +40,20 @@ Either:
The decision content stays unchanged — this is a metadata-structure fix, not a The decision content stays unchanged — this is a metadata-structure fix, not a
re-resolution. re-resolution.
## Summary
Completed alongside `architecture/safe-exit-blocker-task-mechanism`. Added a
structured `Blocked on:` field to both OQ-09 and OQ-10 in their per-OQ files,
each pointing at its new external-trigger tracker task
(`architecture/oq-09-wasm-server-use-case`,
`architecture/oq-10-git-adapter-spec`). Kept the legacy `deferred` status
rather than reframing to `deferred(scope)` — the distinction is no longer
load-bearing now that both have explicit blocking conditions and tracker
tasks. The `open-questions.md` Deferred/Blocked section now surfaces all six
deferred OQs with concrete conditions inline — no placeholders remain.
## Verification ## Verification
The "Deferred / Blocked" section of `docs/architecture/open-questions.md` should The "Deferred / Blocked" section of `docs/architecture/open-questions.md`
show a concrete blocking condition for OQ-09 and OQ-10 instead of the "no shows a concrete blocking condition for OQ-09 and OQ-10 instead of the "no
explicit blocking condition recorded" placeholder. explicit blocking condition recorded" placeholder.
@@ -0,0 +1,58 @@
---
id: architecture/oq-09-wasm-server-use-case
name: External trigger — a concrete server-side WASM use case (or confirmation it stays a client-side constraint)
status: pending
depends_on: []
scope: single
risk: trivial
impact: component
level: research
tags: [external-trigger, deferred-oq]
---
## Description
External-trigger tracker for [OQ-09](../docs/architecture/questions/009-wasm-target-boundaries.md)
(WASM Target Boundaries). This is **not actionable work** — it tracks whether
a concrete server-side WASM use case has emerged, or whether the project
confirms that WASM compatibility remains a *client-side* design constraint
only (a browser can implement BiStream over WebTransport streams; the
server-side dispatch door is a known, accepted closure per ADR-007/009).
## Trigger condition
Either:
- **A concrete server-side WASM use case arrives** (a deployment that wants
to run an alknet server peer compiled to WASM, which would require a
`Connection` trait and a runtime-abstracted accept loop — currently not
planned), or
- **A deliberate confirmation** that WASM stays a client-side design
constraint, at which point OQ-09 transitions from `deferred` to `resolved`
with the accepted-closure framing already in its Resolution text.
The second path is the more likely one — the OQ exists mainly so the
server-side WASM door closure is documented rather than implicit.
## What unblocking looks like
When a decision is made (either direction):
1. Mark this task `status: completed`.
2. Move [OQ-09](../docs/architecture/questions/009-wasm-target-boundaries.md)
from `deferred` to either `resolved` (client-side-only confirmed) or `open`
(server-side use case arrives, requiring a Connection trait + WASM runtime
abstraction ADR).
## Why this is a task, not just an OQ field
OQ-09 predates the formalized `deferred(scope)` + blocking-condition pattern
and lacked a structured `Blocked on:` field (it used the legacy `deferred`
status with the deferral reason in the Resolution prose). This task
formalizes the blocking condition and gives the OQ a machine-readable
presence in the task graph.
## Verification
This task is "completed" when either a server-side WASM use case arrives
(move OQ-09 to `open`) or the client-side-only constraint is confirmed
(move OQ-09 to `resolved`).
@@ -0,0 +1,56 @@
---
id: architecture/oq-10-git-adapter-spec
name: External trigger — speccing alknet-git (resolve OQ-10 when that crate is specified, not deferred past it)
status: pending
depends_on: []
scope: single
risk: trivial
impact: component
level: research
tags: [external-trigger, deferred-oq]
---
## Description
External-trigger tracker for [OQ-10](../docs/architecture/questions/010-git-adapter-scope-smart-protocol-only-or-full-server.md)
(Git Adapter Scope — Smart Protocol Only or Full Server?). This is **not
actionable work** — it tracks when the alknet-git crate is being specified,
at which point OQ-10 must be resolved (not deferred past it).
## Trigger condition
The alknet-git crate is being specified. The OQ's Resolution text already
states: "Resolve this when speccing alknet-git, not deferred past it." The
two sub-questions:
1. **Git adapter scope** — start with git smart protocol over QUIC streams;
ERC721 integration and full server capabilities are additive.
2. **Composability fork** — whether git operations are registered in the
`OperationRegistry` and callable via `env.invoke()`, or only available as
raw smart protocol on `alknet/git`. The path of least resistance (raw
smart protocol only) forecloses agent composition of git operations; to
make git composable, a call-protocol projection (a set of
`HandlerRegistration` bundles wrapping git operations behind the
registry) must be built alongside or instead of the raw handler.
## What unblocking looks like
When alknet-git is specced:
1. Mark this task `status: completed`.
2. Move [OQ-10](../docs/architecture/questions/010-git-adapter-scope-smart-protocol-only-or-full-server.md)
from `deferred` to `open`, then resolve it as part of the alknet-git spec
pass (the Resolution text is explicit: do not defer past the spec).
## Why this is a task, not just an OQ field
OQ-10 predates the formalized `deferred(scope)` + blocking-condition pattern
and lacked a structured `Blocked on:` field (it used the legacy `deferred`
status with the deferral reason in the Resolution prose). This task
formalizes the blocking condition and gives the OQ a machine-readable
presence in the task graph.
## Verification
This task is "completed" when alknet-git is being specced and OQ-10 has been
moved to `open` (then resolved as part of that spec pass).
@@ -0,0 +1,53 @@
---
id: architecture/oq-32-multihop-use-case
name: External trigger — a concrete multi-hop federation use case
status: pending
depends_on: []
scope: single
risk: trivial
impact: component
level: research
tags: [external-trigger, deferred-oq]
---
## Description
External-trigger tracker for [OQ-32](../docs/architecture/questions/032-multi-hop-federation.md)
(Multi-Hop Federation). This is **not actionable work** — it tracks whether a
concrete use case for multi-hop federation has arrived. When it does, mark this
task `completed` and the OQ moves from `deferred(scope)` to `open`.
## Trigger condition
A concrete deployment or use case that requires transitive op discovery across
more than one hop — i.e., worker A needs to reach worker B's ops *through* the
head, where the head is not explicitly re-exporting them. The one-hop model
(head→worker, runner→hub) covers all current use cases.
## What unblocking looks like
When a use case arrives:
1. Mark this task `status: completed`.
2. Move [OQ-32](../docs/architecture/questions/032-multi-hop-federation.md)
from `deferred(scope)` to `open` (update the Status field + the
`open-questions.md` index tables + Deferred/Blocked section).
3. Create an architecture task to write the multi-hop federation ADR — the
peer-keyed overlay model extends to multi-hop without redesign (ADR-029 §3.7),
but path-finding (which peer reaches which op transitively) is where the
design work lives. A graph library (petgraph) may pay off for multi-hop; for
one-hop, a nested `HashMap<PeerId, HashMap<String, ...>>` suffices.
## Why this is a task, not just an OQ field
The OQ's `Blocked on:` field in `open-questions.md` is the human-readable
visibility surface ("what's parked and why"). This task is the machine-readable
half: it lives in the task graph so `taskgraph` tools can reason about it, and
so downstream work that depends on multi-hop being resolved can declare
`depends_on: [architecture/oq-32-multihop-use-case]`.
## Verification
This task is "completed" when a concrete multi-hop use case is documented
(e.g., in a research finding or deployment note) and OQ-32 has been moved to
`open`.
@@ -0,0 +1,66 @@
---
id: architecture/oq-41-stream-operators-use-case
name: External trigger — a handler that needs stream operators beyond existing combinators
status: pending
depends_on: []
scope: single
risk: trivial
impact: component
level: research
tags: [external-trigger, deferred-oq]
---
## Description
External-trigger tracker for [OQ-41](../docs/architecture/questions/041-stream-operators-library.md)
(Stream Operators Library). This is **not actionable work** — it tracks
whether a handler has emerged that needs stream operators (filter, map, batch,
dedupe, window, etc. on `BoxStream<T>`) and finds the existing combinators
insufficient. The operators library is a convenience, not a prerequisite for
any handler.
## Trigger condition
A handler that transforms subscription streams (`BoxStream<ResponseEnvelope>`)
and finds `Box::pin(stream::iter(...))`, `async_stream::stream!`, and
`futures::stream` combinators insufficient — i.e., the handler code is
demonstrably boilerplate-heavy for stream manipulation that the operators
library would collapse. The architectural decision (stream composition is
handler-level, not protocol-level) is already made in ADR-049; this tracks the
*implementation* of the utility library.
## What unblocking looks like
When a handler needs the operators:
1. Mark this task `status: completed`.
2. Move [OQ-41](../docs/architecture/questions/041-stream-operators-library.md)
from `deferred(scope)` to `resolved` (the architectural decision is already
made — ADR-049; what remains is the implementation, which is scheduling
work). The OQ may transition directly to `resolved` rather than `open`,
since no architecture decision remains.
3. Implement the operators library (no ADR needed — internal utility code
that doesn't cross crate boundaries as a contract; an ADR would be
warranted only if the operators become part of a public API surface, e.g.,
a handler-registration DSL that references operator names).
## Why this is a task, not just an OQ field
The OQ's `Blocked on:` field in `open-questions.md` is the human-readable
visibility surface. This task is the machine-readable half: it lives in the
task graph so `taskgraph` tools can reason about it, and so a handler that
needs the operators can declare `depends_on:
[architecture/oq-41-stream-operators-use-case]`.
## Prior art
`@alkdev/pubsub/src/operators.ts` — 13 operators (`filter`, `map`, `take`,
`batch`, `dedupe`, `window`, `chain`, `join`, `reduce`, `groupBy`, `flat`,
`pipe`, `toArray`) on `AsyncIterable<T>`, forked from graphql-yoga's
subscription implementation. The Rust analogue would provide the same set on
`BoxStream<T>` / `impl Stream<Item = T>`.
## Verification
This task is "completed" when a handler is identified that needs the operators
and OQ-41 has been moved to `resolved`.
@@ -0,0 +1,51 @@
---
id: architecture/oq-44-tty-modes-use-case
name: External trigger — a concrete TTY mode-control use case
status: pending
depends_on: []
scope: single
risk: trivial
impact: component
level: research
tags: [external-trigger, deferred-oq]
---
## Description
External-trigger tracker for [OQ-44](../docs/architecture/questions/044-terminal-modes-tty-modes.md)
(Terminal Modes / TTY modes). This is **not actionable work** — it tracks
whether a concrete deployment has emerged that needs to set TTY modes (echo,
raw, canonical, etc.) on a PTY beyond the backend's defaults.
## Trigger condition
A concrete deployment that needs to control TTY modes beyond the defaults the
backends already provide (`portable_pty`, docker `tty: true`, russh
`pty_request` all have defaults that work for the common terminal case). The
`modes` field in `TerminalParams` is `serde_json::Value` (reserved as `{}` in
v1) for when this arrives.
## What unblocking looks like
When a mode-control use case arrives:
1. Mark this task `status: completed`.
2. Move [OQ-44](../docs/architecture/questions/044-terminal-modes-tty-modes.md)
from `deferred(scope)` to `open`.
3. Specify the `modes` JSON shape (SSH's `pty_request` carries TTY modes as a
packed bitmask; the Rust analogue extends the `modes` field). Adding mode
control is additive (extend the `modes` JSON shape) and does not break
downstream — the architectural commitment is two-way-door.
## Why this is a task, not just an OQ field
The OQ's `Blocked on:` field in `open-questions.md` is the human-readable
visibility surface. This task is the machine-readable half: it lives in the
task graph so `taskgraph` tools can reason about it, and so downstream work
that depends on TTY mode control can declare `depends_on:
[architecture/oq-44-tty-modes-use-case]`.
## Verification
This task is "completed" when a concrete mode-control use case is documented
and OQ-44 has been moved to `open`.
@@ -0,0 +1,52 @@
---
id: architecture/oq-46-runner-policy-use-case
name: External trigger — a concrete runner-policy use case that forces the API surface
status: pending
depends_on: []
scope: single
risk: trivial
impact: component
level: research
tags: [external-trigger, deferred-oq]
---
## Description
External-trigger tracker for [OQ-46](../docs/architecture/questions/046-runner-api-surface.md)
(Runner API Surface). This is **not actionable work** — it tracks whether a
concrete runner-policy use case has emerged that forces the API surface (job
management, log persistence, task graph integration). The runner *mechanism*
(pipe mode) is already in alknet-tty (ADR-054); the runner *policy* is a
downstream crate's job.
## Trigger condition
A concrete deployment that needs runner *policy* — job management, log
persistence, task graph integration — on top of the pipe-mode mechanism
(`TtyParams.terminal = None` → `std::process::Command` with piped stdio →
framed byte stream + exit code) that alknet-tty already provides.
## What unblocking looks like
When a runner-policy use case arrives:
1. Mark this task `status: completed`.
2. Move [OQ-46](../docs/architecture/questions/046-runner-api-surface.md)
from `deferred(scope)` to `open`.
3. Decide whether a runner-policy crate (e.g., an `alknet-runner` crate that
builds on the pipe mode + the wire format to provide job management) is
needed, and what its API surface would be. The mechanism is preserved
regardless of the policy decision.
## Why this is a task, not just an OQ field
The OQ's `Blocked on:` field in `open-questions.md` is the human-readable
visibility surface. This task is the machine-readable half: it lives in the
task graph so `taskgraph` tools can reason about it, and so downstream work
that depends on runner policy can declare `depends_on:
[architecture/oq-46-runner-policy-use-case]`.
## Verification
This task is "completed" when a concrete runner-policy use case is documented
and OQ-46 has been moved to `open`.
@@ -1,12 +1,13 @@
--- ---
id: architecture/safe-exit-blocker-task-mechanism id: architecture/safe-exit-blocker-task-mechanism
name: Establish the tasks/architecture/ blocker-task half of the Safe Exit protocol name: Establish the tasks/architecture/ blocker-task half of the Safe Exit protocol
status: pending status: completed
depends_on: [] depends_on: []
scope: moderate scope: moderate
risk: low risk: low
impact: project impact: project
level: planning level: planning
tags: [convention]
--- ---
## Description ## Description
@@ -58,6 +59,30 @@ it").
role spec) so future deferrals create the blocker task as part of the Safe role spec) so future deferrals create the blocker task as part of the Safe
Exit step. Exit step.
## Summary
Completed in the July 2026 architecture-task pass. Decisions:
- **Format**: adopted the existing taskgraph frontmatter verbatim — no new
`blocks:` field. One edge type (`depends_on`) keeps the graph simple; the
reverse lookup uses `taskgraph dependents`. The OQ's `Blocked on:` text is
the human-readable pointer; the task `depends_on` is the machine-readable
edge. They serve different audiences (architect vs. planner/agent).
- **External-trigger tasks**: the four `deferred(scope)` OQs (32, 41, 44, 46)
plus the two legacy `deferred` OQs (09, 10) each got an external-trigger
tracker task under `tasks/architecture/` tagged `[external-trigger,
deferred-oq]`. These represent the external condition (a use case arriving,
a crate being specced) that would unblock the OQ — they are not actionable
work, so `risk: trivial` and `level: research`.
- **OQ-09/10 backfill**: added a structured `Blocked on:` field to both
(previously they used legacy `deferred` status with the reason in the
Resolution prose). The `open-questions.md` index now surfaces all six
deferred OQs with concrete blocking conditions in the Deferred/Blocked
section — no more "_(no explicit blocking condition recorded)_" placeholders.
- **Convention doc**: the `docs/sdd_process.md` Task File Format section is
updated with the architecture-task level mapping and the Safe Exit
blocker-task pattern (separate edit).
## Out of scope ## Out of scope
- The DB-backed backend (no manual links, vector/text search) — that's a future - The DB-backed backend (no manual links, vector/text search) — that's a future
@@ -67,7 +92,9 @@ it").
## Verification ## Verification
- `tasks/architecture/` contains one blocker task per `deferred(scope)` OQ - `tasks/architecture/` contains one external-trigger task per deferred OQ
- Each blocker task's `depends_on` names the concrete unblocking condition (or (six total: OQ-09, 10, 32, 41, 44, 46).
a task that represents it) - Each OQ's `Blocked on:` field names its tracker task ID, and the
- `docs/sdd_process.md` (or equivalent) references the convention `open-questions.md` Deferred/Blocked section surfaces the condition inline.
- `taskgraph validate` passes for `tasks/architecture/`.
- `docs/sdd_process.md` references the convention.
@@ -1,7 +1,7 @@
--- ---
id: call/registry/access-control-ownership-check id: call/registry/access-control-ownership-check
name: Update AccessControl::check to consult OwnershipProvider for dynamic resource ownership (ADR-050 §2) name: Update AccessControl::check to consult OwnershipProvider for dynamic resource ownership (ADR-050 §2)
status: done status: completed
depends_on: [core/ownership-store-trait] depends_on: [core/ownership-store-trait]
scope: moderate scope: moderate
risk: medium risk: medium
@@ -1,7 +1,7 @@
--- ---
id: call/registry/dispatch-resource-id-extraction id: call/registry/dispatch-resource-id-extraction
name: Wire dispatch path to extract resource_id from input and thread OwnershipProvider to AccessControl::check (ADR-050 §2a, §4a) name: Wire dispatch path to extract resource_id from input and thread OwnershipProvider to AccessControl::check (ADR-050 §2a, §4a)
status: done status: completed
depends_on: [call/registry/operation-spec-resource-id-path, call/registry/access-control-ownership-check] depends_on: [call/registry/operation-spec-resource-id-path, call/registry/access-control-ownership-check]
scope: moderate scope: moderate
risk: medium risk: medium
@@ -1,7 +1,7 @@
--- ---
id: call/registry/operation-spec-resource-id-path id: call/registry/operation-spec-resource-id-path
name: Add resource_id_path field to OperationSpec (ADR-050 §2a) name: Add resource_id_path field to OperationSpec (ADR-050 §2a)
status: done status: completed
depends_on: [] depends_on: []
scope: single scope: single
risk: low risk: low
+1 -1
View File
@@ -1,7 +1,7 @@
--- ---
id: core/ownership-store-trait id: core/ownership-store-trait
name: Add OwnershipProvider (sync read) + OwnershipStore (async write) traits and InMemoryOwnershipStore (ADR-050) name: Add OwnershipProvider (sync read) + OwnershipStore (async write) traits and InMemoryOwnershipStore (ADR-050)
status: done status: completed
depends_on: [] depends_on: []
scope: moderate scope: moderate
risk: low risk: low