diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-24 07:09:21 -0400 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-24 07:09:21 -0400 |
| commit | 9709638f1d50193bd4636205a142a2277f92e4f4 (patch) | |
| tree | 8b9adc4fee6a853c4f629dd85210de91807db157 | |
| parent | 06b6cbcf086729414ff9a533b1f031fb41c4088b (diff) | |
| download | rulesets-9709638f1d50193bd4636205a142a2277f92e4f4.tar.gz rulesets-9709638f1d50193bd4636205a142a2277f92e4f4.zip | |
refactor(tasks): use a :blocker: tag, not a :BLOCKS: property
:BLOCKS: rulesets: was a malformed org tag, and the property form (:BLOCKED_BY: / :BLOCKS: carrying <project>: <what>) was more structure than the dependency needs. The blocking side now carries a plain :blocker: tag, mirroring :blocked: on the waiting side, with the which-project detail in the task body rather than a property. open-tasks.org reads the body for the blocking/requesting project; the scheme, the todo-format convention, and the inbox blocking-dependency handoff all move to the two-tag form. No property anywhere.
| -rw-r--r-- | .ai/workflows/inbox.org | 2 | ||||
| -rw-r--r-- | .ai/workflows/open-tasks.org | 16 | ||||
| -rw-r--r-- | claude-rules/todo-format.md | 42 | ||||
| -rw-r--r-- | claude-templates/.ai/workflows/inbox.org | 2 | ||||
| -rw-r--r-- | claude-templates/.ai/workflows/open-tasks.org | 16 | ||||
| -rw-r--r-- | todo.org | 5 |
6 files changed, 40 insertions, 43 deletions
diff --git a/.ai/workflows/inbox.org b/.ai/workflows/inbox.org index f3d400a..5fc855f 100644 --- a/.ai/workflows/inbox.org +++ b/.ai/workflows/inbox.org @@ -114,7 +114,7 @@ The item extends a task already filed. Update the parent TODO's body with a date ** File as TODO Substantive but waits, or needs design/triage before implementation. Add the TODO under =* <Project> Open Work= with priority + tags per the priority-scheme check (core §6). Body summarizes the proposal and links the inbox content if it's been moved to =docs/design/=. Delete the inbox file (or move it to =docs/design/= first if the content survives). -*Blocking-dependency handoff.* A special shape: another project sends a note that *this* project's work is blocking one of theirs ("your task X is blocked on us — we need Y"). File or link the owning task and add a =:BLOCKS: <their-project>: <what>= property to it (see the cross-project dependency convention in =todo-format.md=). The =:BLOCKS:= marker makes =open-tasks.org= surface that task *first*, since clearing it unblocks the other project. Dedup against an existing task rather than filing a duplicate. When the work later lands, drop =:BLOCKS:= and notify the waiting project (=inbox-send <their-project> --text "Delivered: <what> — you're unblocked."=) so it can lift its own =:blocked:=. +*Blocking-dependency handoff.* A special shape: another project sends a note that *this* project's work is blocking one of theirs ("your task X is blocked on us — we need Y"). File or link the owning task, tag it =:blocker:=, and name the requesting project in the body (see the cross-project dependency convention in =todo-format.md=). The =:blocker:= tag makes =open-tasks.org= surface that task *first*, since clearing it unblocks the other project. Dedup against an existing task rather than filing a duplicate. When the work later lands, drop =:blocker:= and notify the waiting project (=inbox-send <their-project> --text "Delivered: <what> — you're unblocked."=) so it can lift its own =:blocked:=. ** Defer Rename in place to =inbox/PROCESSED-<original-filename>= and add a brief comment line at the top: =# Deferred YYYY-MM-DD: <condition>=. Don't accumulate deferred items indefinitely — sweep them on a future process pass when the condition is met or the deferral has aged out. diff --git a/.ai/workflows/open-tasks.org b/.ai/workflows/open-tasks.org index 873fc0e..4ba29dd 100644 --- a/.ai/workflows/open-tasks.org +++ b/.ai/workflows/open-tasks.org @@ -176,9 +176,9 @@ Next Mode answers two questions in one output: "what matters most right now?" (t Apply the prioritization cascade in order. Stop at the first matching step. This is the importance/urgency answer. -*Exclude blocked tasks.* A task tagged =:blocked:= has an unmet cross-project dependency (its =:BLOCKED_BY:= property names the project and the work owed, per =todo-format.md=). It can't be worked until that other project delivers, so it is *never* the cascade recommendation — skip it at every cascade step below. Blocked tasks are surfaced on their own in Step 3 so the stalled dependency stays visible instead of silently dropping out of view. +*Exclude blocked tasks.* A task tagged =:blocked:= has an unmet cross-project dependency (its body names the project and the work owed, per =todo-format.md=). It can't be worked until that other project delivers, so it is *never* the cascade recommendation — skip it at every cascade step below. Blocked tasks are surfaced on their own in Step 3 so the stalled dependency stays visible instead of silently dropping out of view. -*Surface blocking tasks first.* The mirror of the above: a task carrying a =:BLOCKS:= property is holding up work in *another* project (the property names which project and what's owed, per =todo-format.md=). Clearing it unblocks that project, so it carries borrowed urgency — surface it at the *top* of the cascade recommendation regardless of its own priority cookie, ahead of the normal In-Progress / deadline / priority order. When several =:BLOCKS:= tasks exist, lead with the one blocking the most, or the longest. This is the "do the thing that unblocks someone else first" rule; a =:BLOCKS:= task left at its own low priority is exactly how a cross-project dependency stalls. +*Surface blocking tasks first.* The mirror of the above: a task tagged =:blocker:= is holding up work in *another* project (its body names which project and what's owed, per =todo-format.md=). Clearing it unblocks that project, so it carries borrowed urgency — surface it at the *top* of the cascade recommendation regardless of its own priority cookie, ahead of the normal In-Progress / deadline / priority order. When several =:blocker:= tasks exist, lead with the one blocking the most, or the longest. This is the "do the thing that unblocks someone else first" rule; a =:blocker:= task left at its own low priority is exactly how a cross-project dependency stalls. **** 1. In-Progress Tasks - Look for tasks marked =DOING= or partially complete. @@ -236,7 +236,7 @@ The friction filter is the override path. When the cascade winner is partially b Independently of the cascade and the friction filter, collect every open task tagged =:blocked:=. These are tasks this project can't advance until another project delivers; surfacing them keeps a cross-project dependency from rotting at low priority on the other side — the exact failure the tag exists to prevent (a blocked task whose blocker is a =[#D]= in another project sits forever otherwise). -For each blocked task, read its =:BLOCKED_BY:= property (=<project>: <what>=) and present one line: the task, the blocking project, and what that project owes. Then offer — per blocked task — to nudge the blocker: an =inbox-send <project> --text= note naming what's needed and why it's blocking, so the dependency gets attention in the project that owns it. Don't send without the user's go. +For each blocked task, read its body for the blocking project and what's owed, and present one line: the task, the blocking project, and what that project owes. Then offer — per blocked task — to nudge the blocker: an =inbox-send <project> --text= note naming what's needed and why it's blocking, so the dependency gets attention in the project that owns it. Don't send without the user's go. If no =:blocked:= tasks exist, omit this surface entirely (the common case). @@ -246,7 +246,7 @@ Pair the cascade recommendation with the friction block beneath it, and the bloc #+begin_example Unblocks other projects (do these first): -- ai-term wrap-teardown companion — :BLOCKS: rulesets (the three ai-term functions) +- ai-term wrap-teardown companion — :blocker:, unblocks rulesets (the three ai-term functions) Cascade recommendation (importance/urgency): - Fix org-noter reliability — [#A], Method 1, 8/18 complete, blocks daily reading/annotation @@ -260,20 +260,20 @@ Blocked on other projects (can't advance until the blocker delivers): - Wrap-teardown feature — blocked by emacsd: ai-term companion functions — nudge? #+end_example -The =:BLOCKS:= surface sits at the very top — clearing one of those is the highest-leverage thing on the list, since it frees work in another project. Omit it when no =:BLOCKS:= task exists (the common case). +The =:blocker:= surface sits at the very top — clearing one of those is the highest-leverage thing on the list, since it frees work in another project. Omit it when no =:blocker:= task exists (the common case). Include for each row: - Task name / description. - Priority + tag cluster. - One-line reasoning. For the cascade row, name which cascade step matched. For friction rows, an effort hint when one is obvious. - Progress indicator (for V2MOM-structured todos) on the cascade row only. -- For a =:BLOCKS:= row: the project it unblocks and what's owed (from the =:BLOCKS:= property). -- For a blocked row: the blocking project and what it owes (from =:BLOCKED_BY:=), plus the nudge offer. +- For a =:blocker:= row: the project it unblocks and what's owed (from the task body). +- For a blocked row: the blocking project and what it owes (from the task body), plus the nudge offer. **** Edge cases - *Empty friction block.* If no =:quick:= or =:solo:= tagged tasks exist in the open set, omit the friction block entirely. Present only the cascade recommendation. -- *No =:BLOCKS:= tasks.* Omit the "Unblocks other projects" surface entirely (the common case) — show it only when a task carries a =:BLOCKS:= property. +- *No =:blocker:= tasks.* Omit the "Unblocks other projects" surface entirely (the common case) — show it only when a task carries the =:blocker:= tag. - *Dedupe.* If the cascade recommendation IS the same task as one of the friction rows (e.g. it's =:quick:solo:= and also won the cascade), show it once at the top with both labels. Don't list it twice. - *Decline behavior.* If the user declines the cascade recommendation, drop straight to the friction block as the natural next prompt. Do not fall through to lower-cascade-tier tasks; the friction filter IS the override. diff --git a/claude-rules/todo-format.md b/claude-rules/todo-format.md index 7bc7299..5c34966 100644 --- a/claude-rules/todo-format.md +++ b/claude-rules/todo-format.md @@ -264,57 +264,53 @@ are noise that pollute his `cj:` greps. *** 2026-05-15 Fri @ 14:00:00 -0500 Kostya basis — part-time, 20 hr/week Nerses confirmed 5/15 13:30 CDT: Kostya runs at 20 hr/week part-time, mirroring Vrezh's structure. Plugged into Exhibit A § 2 of the contract draft. -## Cross-Project Dependency Tag +## Cross-Project Dependency Tags A task can be blocked by work that has to happen in a *different project* — a rulesets task that can't finish until `.emacs.d` ships a companion function, say. Left unmarked, two things go wrong: the what's-next workflow keeps recommending the blocked task even though it can't move, and the blocker sits at low priority in the other project, so the dependency stalls silently. -The dependency is tracked on *both* sides so neither the waiter nor the blocker loses sight of it: the waiting task carries `:blocked:` + `:BLOCKED_BY:`, and the blocking project gets a reciprocal handoff that becomes a `:BLOCKS:`-marked task on its side. This applies to *any* project pair — the convention here and the surfacing in `open-tasks.org` live in the shared rule + workflow layer, not in one project. +Two plain org tags track it, one on each side, so neither the waiter nor the blocker loses sight of the dependency: `:blocked:` on the task that's waiting, `:blocker:` on the task that owes the work. The cross-project detail — which project, what work — goes in the task *body*, not a property. This applies to *any* project pair; the convention here and the surfacing in `open-tasks.org` live in the shared rule + workflow layer, not in one project. -### The waiting side — `:blocked:` + `:BLOCKED_BY:` +### `:blocked:` — the waiting side -Mark the dependent task with the `:blocked:` tag plus a `:BLOCKED_BY:` property: +The task that can't proceed carries `:blocked:`. Its body names the project it's waiting on and what that project owes: ``` ** DOING [#B] Wrap-teardown feature :feature:blocked: -:PROPERTIES: -:BLOCKED_BY: emacsd: ai-term companion functions (cj/ai-term-quit, -live-count) -:END: -Body... +Blocked on emacsd: needs the ai-term companion functions +(cj/ai-term-quit, -live-count) before the manual validation can run. ``` -- The `:blocked:` tag on the heading is the filterable marker — `open-tasks.org` reads it to pull the task out of the "do this next" recommendation. -- The `:BLOCKED_BY:` property names *which* project blocks the task and *what* it owes, as `<project>: <what>`. The project token is the short basename (`emacsd`, `home`, `work`), matching the inbox/handoff naming. +`open-tasks.org` reads the `:blocked:` tag to pull the task out of the "do this next" cascade (it can't be worked) and surface it in a dedicated "Blocked on other projects" section, reading the body for which project to name and nudge. ### Registering with the blocker — the reciprocal handoff (required) -Setting `:blocked:` is not complete until the blocking project knows it's blocking. The moment you add `:blocked:` + `:BLOCKED_BY: <project>: ...`, send `<project>` a dependency handoff: +Setting `:blocked:` is not complete until the blocking project knows it's blocking. The moment you mark a task `:blocked:` on another project's work, send that project a dependency handoff: ``` -inbox-send <project> --text "Blocking dependency: <this-project>'s task \"<task>\" is blocked on you — it needs <what>. It stays :blocked: until this lands. File it on your side as a task with :BLOCKS: <this-project>: <what> so it surfaces as priority work." +inbox-send <project> --text "Blocking dependency: <this-project>'s task \"<task>\" is blocked on you — it needs <what>. It stays blocked until this lands. Tag the owning task :blocker: on your side so it surfaces as priority work." ``` -This is what closes the gap: without it, the blocker only learns it's blocking by accident. The handoff lands in `<project>`'s `inbox/` and its normal inbox processing files it (below). A `:blocked:` task with no matching reciprocal handoff is half-done — the dependency is invisible to the one project that can clear it. (Skip the send only when the blocker demonstrably already tracks the work, e.g. it's the same handoff that spawned the dependency; the blocker dedups against an existing task either way.) +This is what closes the gap: without it, the blocker only learns it's blocking by accident. The handoff lands in `<project>`'s `inbox/` and its normal inbox processing tags the work (below). A `:blocked:` task with no matching reciprocal handoff is half-done — the dependency is invisible to the one project that can clear it. Skip the send only when the blocker demonstrably already tracks the work (e.g. it's the same handoff that spawned the dependency); it dedups against an existing task either way. -### The blocking side — `:BLOCKS:` +### `:blocker:` — the blocking side -When a project processes a blocking-dependency handoff (inbox process mode), it files or links the relevant task and marks it with the inverse property — a `:BLOCKS:` property naming the project + work it's holding up: +When a project processes a blocking-dependency handoff (inbox process mode), it tags the owning task `:blocker:` and names the requesting project in the body: ``` -** TODO [#B] ai-term wrap-teardown companion :feature: -:PROPERTIES: -:BLOCKS: rulesets: wrap-teardown feature (the three ai-term functions) -:END: +** TODO [#B] ai-term wrap-teardown companion :feature:blocker: +Rulesets' wrap-teardown feature is blocked on this — it needs the three +ai-term functions. Surface first so rulesets unblocks. ``` -The blocking task does *not* carry `:blocked:` — it isn't blocked, it's the blocker. `:BLOCKS:` is a priority signal: `open-tasks.org` surfaces a `:BLOCKS:` task *first*, since clearing it unblocks work in another project, so a dependency that would otherwise stall at low priority gets pulled forward. This is the "surface dependencies first" half of the design. +The blocking task does *not* carry `:blocked:` — it isn't blocked, it's the blocker. `:blocker:` is a priority signal: `open-tasks.org` surfaces a `:blocker:` task *first*, since clearing it unblocks work in another project, so a dependency that would otherwise stall at low priority gets pulled forward. This is the "surface dependencies first" half of the design. ### Resolving the dependency When the blocker delivers: -1. The blocking project completes its `:BLOCKS:` task, drops the `:BLOCKS:` property, and notifies the waiter (`inbox-send <waiter> --text "Delivered: <what> — you're unblocked."`). -2. The waiting project drops the `:blocked:` tag and `:BLOCKED_BY:` property in the same edit; the task is workable again. Either side noticing the delivery can lift its own marker — the notification just makes it prompt. +1. The blocking project completes its `:blocker:` task, drops the `:blocker:` tag, and notifies the waiter (`inbox-send <waiter> --text "Delivered: <what> — you're unblocked."`). +2. The waiting project drops the `:blocked:` tag; the task is workable again. Either side noticing the delivery can lift its own tag — the notification just makes it prompt. ### Not the same as VERIFY -`:blocked:` marks "waiting on another *project's* work"; `VERIFY` marks "waiting on Craig's input." If Craig's input is what's needed, it's a VERIFY, not `:blocked:`. And `:BLOCKS:` only ever sits on the project that *owes* the work, never the one waiting. +`:blocked:` marks "waiting on another *project's* work"; `VERIFY` marks "waiting on Craig's input." If Craig's input is what's needed, it's a VERIFY, not `:blocked:`. And `:blocker:` only ever sits on the project that *owes* the work, never the one waiting. diff --git a/claude-templates/.ai/workflows/inbox.org b/claude-templates/.ai/workflows/inbox.org index f3d400a..5fc855f 100644 --- a/claude-templates/.ai/workflows/inbox.org +++ b/claude-templates/.ai/workflows/inbox.org @@ -114,7 +114,7 @@ The item extends a task already filed. Update the parent TODO's body with a date ** File as TODO Substantive but waits, or needs design/triage before implementation. Add the TODO under =* <Project> Open Work= with priority + tags per the priority-scheme check (core §6). Body summarizes the proposal and links the inbox content if it's been moved to =docs/design/=. Delete the inbox file (or move it to =docs/design/= first if the content survives). -*Blocking-dependency handoff.* A special shape: another project sends a note that *this* project's work is blocking one of theirs ("your task X is blocked on us — we need Y"). File or link the owning task and add a =:BLOCKS: <their-project>: <what>= property to it (see the cross-project dependency convention in =todo-format.md=). The =:BLOCKS:= marker makes =open-tasks.org= surface that task *first*, since clearing it unblocks the other project. Dedup against an existing task rather than filing a duplicate. When the work later lands, drop =:BLOCKS:= and notify the waiting project (=inbox-send <their-project> --text "Delivered: <what> — you're unblocked."=) so it can lift its own =:blocked:=. +*Blocking-dependency handoff.* A special shape: another project sends a note that *this* project's work is blocking one of theirs ("your task X is blocked on us — we need Y"). File or link the owning task, tag it =:blocker:=, and name the requesting project in the body (see the cross-project dependency convention in =todo-format.md=). The =:blocker:= tag makes =open-tasks.org= surface that task *first*, since clearing it unblocks the other project. Dedup against an existing task rather than filing a duplicate. When the work later lands, drop =:blocker:= and notify the waiting project (=inbox-send <their-project> --text "Delivered: <what> — you're unblocked."=) so it can lift its own =:blocked:=. ** Defer Rename in place to =inbox/PROCESSED-<original-filename>= and add a brief comment line at the top: =# Deferred YYYY-MM-DD: <condition>=. Don't accumulate deferred items indefinitely — sweep them on a future process pass when the condition is met or the deferral has aged out. diff --git a/claude-templates/.ai/workflows/open-tasks.org b/claude-templates/.ai/workflows/open-tasks.org index 873fc0e..4ba29dd 100644 --- a/claude-templates/.ai/workflows/open-tasks.org +++ b/claude-templates/.ai/workflows/open-tasks.org @@ -176,9 +176,9 @@ Next Mode answers two questions in one output: "what matters most right now?" (t Apply the prioritization cascade in order. Stop at the first matching step. This is the importance/urgency answer. -*Exclude blocked tasks.* A task tagged =:blocked:= has an unmet cross-project dependency (its =:BLOCKED_BY:= property names the project and the work owed, per =todo-format.md=). It can't be worked until that other project delivers, so it is *never* the cascade recommendation — skip it at every cascade step below. Blocked tasks are surfaced on their own in Step 3 so the stalled dependency stays visible instead of silently dropping out of view. +*Exclude blocked tasks.* A task tagged =:blocked:= has an unmet cross-project dependency (its body names the project and the work owed, per =todo-format.md=). It can't be worked until that other project delivers, so it is *never* the cascade recommendation — skip it at every cascade step below. Blocked tasks are surfaced on their own in Step 3 so the stalled dependency stays visible instead of silently dropping out of view. -*Surface blocking tasks first.* The mirror of the above: a task carrying a =:BLOCKS:= property is holding up work in *another* project (the property names which project and what's owed, per =todo-format.md=). Clearing it unblocks that project, so it carries borrowed urgency — surface it at the *top* of the cascade recommendation regardless of its own priority cookie, ahead of the normal In-Progress / deadline / priority order. When several =:BLOCKS:= tasks exist, lead with the one blocking the most, or the longest. This is the "do the thing that unblocks someone else first" rule; a =:BLOCKS:= task left at its own low priority is exactly how a cross-project dependency stalls. +*Surface blocking tasks first.* The mirror of the above: a task tagged =:blocker:= is holding up work in *another* project (its body names which project and what's owed, per =todo-format.md=). Clearing it unblocks that project, so it carries borrowed urgency — surface it at the *top* of the cascade recommendation regardless of its own priority cookie, ahead of the normal In-Progress / deadline / priority order. When several =:blocker:= tasks exist, lead with the one blocking the most, or the longest. This is the "do the thing that unblocks someone else first" rule; a =:blocker:= task left at its own low priority is exactly how a cross-project dependency stalls. **** 1. In-Progress Tasks - Look for tasks marked =DOING= or partially complete. @@ -236,7 +236,7 @@ The friction filter is the override path. When the cascade winner is partially b Independently of the cascade and the friction filter, collect every open task tagged =:blocked:=. These are tasks this project can't advance until another project delivers; surfacing them keeps a cross-project dependency from rotting at low priority on the other side — the exact failure the tag exists to prevent (a blocked task whose blocker is a =[#D]= in another project sits forever otherwise). -For each blocked task, read its =:BLOCKED_BY:= property (=<project>: <what>=) and present one line: the task, the blocking project, and what that project owes. Then offer — per blocked task — to nudge the blocker: an =inbox-send <project> --text= note naming what's needed and why it's blocking, so the dependency gets attention in the project that owns it. Don't send without the user's go. +For each blocked task, read its body for the blocking project and what's owed, and present one line: the task, the blocking project, and what that project owes. Then offer — per blocked task — to nudge the blocker: an =inbox-send <project> --text= note naming what's needed and why it's blocking, so the dependency gets attention in the project that owns it. Don't send without the user's go. If no =:blocked:= tasks exist, omit this surface entirely (the common case). @@ -246,7 +246,7 @@ Pair the cascade recommendation with the friction block beneath it, and the bloc #+begin_example Unblocks other projects (do these first): -- ai-term wrap-teardown companion — :BLOCKS: rulesets (the three ai-term functions) +- ai-term wrap-teardown companion — :blocker:, unblocks rulesets (the three ai-term functions) Cascade recommendation (importance/urgency): - Fix org-noter reliability — [#A], Method 1, 8/18 complete, blocks daily reading/annotation @@ -260,20 +260,20 @@ Blocked on other projects (can't advance until the blocker delivers): - Wrap-teardown feature — blocked by emacsd: ai-term companion functions — nudge? #+end_example -The =:BLOCKS:= surface sits at the very top — clearing one of those is the highest-leverage thing on the list, since it frees work in another project. Omit it when no =:BLOCKS:= task exists (the common case). +The =:blocker:= surface sits at the very top — clearing one of those is the highest-leverage thing on the list, since it frees work in another project. Omit it when no =:blocker:= task exists (the common case). Include for each row: - Task name / description. - Priority + tag cluster. - One-line reasoning. For the cascade row, name which cascade step matched. For friction rows, an effort hint when one is obvious. - Progress indicator (for V2MOM-structured todos) on the cascade row only. -- For a =:BLOCKS:= row: the project it unblocks and what's owed (from the =:BLOCKS:= property). -- For a blocked row: the blocking project and what it owes (from =:BLOCKED_BY:=), plus the nudge offer. +- For a =:blocker:= row: the project it unblocks and what's owed (from the task body). +- For a blocked row: the blocking project and what it owes (from the task body), plus the nudge offer. **** Edge cases - *Empty friction block.* If no =:quick:= or =:solo:= tagged tasks exist in the open set, omit the friction block entirely. Present only the cascade recommendation. -- *No =:BLOCKS:= tasks.* Omit the "Unblocks other projects" surface entirely (the common case) — show it only when a task carries a =:BLOCKS:= property. +- *No =:blocker:= tasks.* Omit the "Unblocks other projects" surface entirely (the common case) — show it only when a task carries the =:blocker:= tag. - *Dedupe.* If the cascade recommendation IS the same task as one of the friction rows (e.g. it's =:quick:solo:= and also won the cascade), show it once at the top with both labels. Don't list it twice. - *Decline behavior.* If the user declines the cascade recommendation, drop straight to the friction block as the natural next prompt. Do not fall through to lower-cascade-tier tasks; the friction filter IS the override. @@ -30,9 +30,10 @@ Optional *effort and autonomy tags* — orthogonal to type, both can apply on th - =:quick:= — likely to take ≤30 minutes from start through verification. - =:solo:= — Claude can complete the work end to end, including verification, without input from Craig. -Optional *dependency tag*: +Optional *dependency tags* — cross-project, both plain tags with the which-project detail in the task body (per =todo-format.md=): -- =:blocked:= — the task can't advance until another project delivers; a =:BLOCKED_BY: <project>: <what>= property names the blocker. =open-tasks.org= pulls =:blocked:= tasks out of the cascade and surfaces them on their own. Distinct from =VERIFY= (which waits on Craig). See =todo-format.md=. +- =:blocked:= — the task can't advance until another project delivers the work named in its body. =open-tasks.org= pulls =:blocked:= tasks out of the cascade and surfaces them on their own. Distinct from =VERIFY= (which waits on Craig). +- =:blocker:= — this task owes work that's blocking another project (named in its body). =open-tasks.org= surfaces =:blocker:= tasks first, since clearing one unblocks the other project. Tags are assigned and refreshed by =task-audit=; =task-review= keeps them honest in passing. |
