diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-28 08:08:20 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-28 08:08:20 -0500 |
| commit | ee7049aaa62d0c38e83f20c0b3796e5eded4ca23 (patch) | |
| tree | e0d9fc2e44df4bda3e36a06b6c0c4f40076a0eec | |
| parent | 4f650f79fb3c426fb4795bb12741b481d8270005 (diff) | |
| download | rulesets-ee7049aaa62d0c38e83f20c0b3796e5eded4ca23.tar.gz rulesets-ee7049aaa62d0c38e83f20c0b3796e5eded4ca23.zip | |
feat(workflows): audit-warranted pre-step + priority and tag enforcement
Two changes land together because each is broken without the other.
open-tasks.org gains a new Phase A.1, evaluated only in Next Mode. The
phase reads :LAST_AUDIT: from notes.org and walks five state signals
(reminder/task mismatch, passed scheduled date, "waiting on X" matches a
shipped X, dead file: link, sub-task >75% DONE coverage). If the temporal
threshold of 14 days trips, or any signal fires, Next Mode offers a
task-audit run before producing the recommendation. Item 1 in the offer
is "run task-audit first" per the recommendation-at-item-1 convention.
task-audit.org gains two pieces. Phase C now enforces priority and
type-tag presence per the project's legend, applies the [#A] dating rule
from that legend, and re-assesses :quick: and :solo: from reconciled
facts. Unambiguous calls land autonomously. Ambiguous ones flag
NEEDS-USER instead of being guessed. A new Phase E stamps :LAST_AUDIT:
on completion.
| -rw-r--r-- | .ai/workflows/open-tasks.org | 104 | ||||
| -rw-r--r-- | .ai/workflows/task-audit.org | 16 | ||||
| -rw-r--r-- | claude-templates/.ai/workflows/open-tasks.org | 42 | ||||
| -rw-r--r-- | claude-templates/.ai/workflows/task-audit.org | 16 |
4 files changed, 158 insertions, 20 deletions
diff --git a/.ai/workflows/open-tasks.org b/.ai/workflows/open-tasks.org index b07f352..e90bc12 100644 --- a/.ai/workflows/open-tasks.org +++ b/.ai/workflows/open-tasks.org @@ -25,13 +25,51 @@ Don't route "task review" / "review tasks" here — those trigger the hygiene ha Issue all source reads as a single batch of parallel tool calls. They're independent: -1. Read =notes.org= — sections *Active Reminders* and *Pending Decisions* only. +1. Read =notes.org= — sections *Active Reminders*, *Pending Decisions*, and *Workflow State* (note the =:LAST_AUDIT:= date if present; "unset" or absent is treated as "never run"). 2. Read each of the 2-3 most recent files in =.ai/sessions/= — only the *Next Steps* subsection under =* Summary= (those capture recent "next time, do X" items that may still be open). 3. Read =todo.org= — entries under the open-work header (=* $Project Open Work=, e.g. =* Homelab Open Work=). Skip the resolved header (=* $Project Resolved=). 4. Run =date= for an accurate timestamp (used to evaluate deadlines + flag suspected completions). The phases below work entirely from this in-memory snapshot. The original split-workflow versions issued these reads sequentially in different files; consolidating saves round-trips and prevents the two implementations drifting apart. +* Phase A.1: Evaluate audit-warranted (Next Mode only) + +Before Next Mode produces a recommendation, decide whether a =task-audit= run looks warranted. List Mode skips this phase — it displays the plate as-is and should not trigger a write-side-effect workflow first. + +Two triggers; either one trips the warranted flag. + +** Temporal threshold + +If =:LAST_AUDIT:= is absent / unset, or its date is older than 14 days from today, the temporal trigger trips. (The 14-day default is per-project; tune by editing this workflow if a project churns faster or slower.) + +** State-signal scan + +Walk the Phase A snapshot for any of these cheap signals — one is enough: + +1. *Reminder/task mismatch* — an Active Reminder names a task or completion target that doesn't appear in =todo.org=, or =todo.org= names a reminder not present in *Active Reminders*. +2. *Passed scheduled date* — a task with =SCHEDULED:= or =DEADLINE:= earlier than today is still =TODO= or =DOING= with no completion log under it. +3. *"Waiting on X" matches a shipped X* — a task body contains "waiting on", "after X ships", "blocked by", and the recent session *Next Steps* (or *Files Modified*) summaries log the named X as shipped. +4. *Dead =file:= link* — a task body's =file:= link points to a path that no longer exists (one existence check per linked path). +5. *Sub-task DONE coverage >75%* — a parent task is still =TODO= or =DOING= while >75% of its sub-tasks are =DONE= or =CANCELLED= (a likely promotion-to-DONE candidate). + +** Surface and decide + +If neither trigger trips, proceed silently to Phase B. + +If one or both trip, surface them inline and ask: + +#+begin_example +Task-audit looks worth running before recommending what's next: +- Temporal: 23 days since last audit (threshold 14). +- Signal: "blocked by review of spec X" — sessions show spec X merged. + +1. Run task-audit first, then come back to "what's next" (recommended) +2. Skip the audit — proceed straight to the recommendation +3. Defer — proceed now, file a TODO to run task-audit later +#+end_example + +If the user picks option 1, hand off to =task-audit.org= and resume Next Mode at Phase B after it stamps =:LAST_AUDIT:=. If option 2, continue. If option 3, file a =** TODO [#B] Run task-audit= entry under =* $Project Open Work= with today's date and continue. + * Phase B: Reconcile (both modes) Compare the open-task signal across sources. For each task in =notes.org= Active Reminders or in a recent session's Next Steps that does NOT have a corresponding =todo.org= entry: @@ -118,61 +156,89 @@ For tasks Craig says are NOT done, leave them as-is. ** Next Mode -Apply the prioritization cascade in order. Stop at the first matching step: +Next Mode answers two questions in one output: "what matters most right now?" (the cascade, importance/urgency) and "what shape of task can I actually finish?" (the friction filter, effort + autonomy). The user picks the row that matches the state of their day. + +*** Step 1 — Cascade recommendation (importance/urgency) -*** 1. In-Progress Tasks +Apply the prioritization cascade in order. Stop at the first matching step. This is the importance/urgency answer. + +**** 1. In-Progress Tasks - Look for tasks marked =DOING= or partially complete. - *If found:* Recommend that task (always finish what's started). - *If user declines:* Continue to next step. -*** 2. Active Reminders +**** 2. Active Reminders - Review notes.org Active Reminders (already in the Phase A snapshot). - *If found:* Recommend reminder task. - *If user declines:* Add to =todo.org= per Phase B (if not already there), then continue. -*** 3. Deadline-Driven Tasks +**** 3. Deadline-Driven Tasks - Scan =todo.org= for tasks with explicit deadlines. - *If found:* Recommend the task with the closest deadline. - *If none:* Continue to next step. -*** 4. V2MOM Method Order (if applicable) +**** 4. V2MOM Method Order (if applicable) If =todo.org= is structured with V2MOM methods: - Method 1 priority A tasks first. - Then Method 2 priority A, Method 3 priority A, etc. - Then Method 1 priority B, Method 2 priority B, etc. - Continue pattern through priorities C and D. -*** 5. Simple Priority Order +**** 5. Simple Priority Order If =todo.org= is a flat list: - Evaluate all priority A tasks, pick most important. - If no priority A, evaluate priority B tasks. - Continue through priorities C and D. -*** 6. All Tasks Complete +**** 6. All Tasks Complete If no tasks remain: report "All done — no open tasks in =todo.org=." -*** Handling Multiple Tasks at Same Level +**** Handling Multiple Tasks at Same Level When multiple tasks share priority/method position, pick one based on: 1. *Blocks other work* — dependencies matter. 2. *Recently discussed* — mentioned in recent conversation. 3. *Most foundational* — enables other tasks. -4. *If truly uncertain* — show 2-3 options and let Craig choose. +4. *If truly uncertain* — show 2-3 options and let the user choose. + +*** Step 2 — Friction filter (effort + autonomy) + +Independently of the cascade, scan the open-task set for tasks tagged =:quick:= and =:solo:=. Build three ranked picks: + +- *Quick + solo* — the top task carrying BOTH tags. Smallest effort × highest autonomy = lowest friction. Ideal for a 20-minute window or a flagging-energy moment. +- *Quick only* — the top task carrying =:quick:= but not =:solo:=. Small but needs the user's preference / hardware / sign-off. +- *Solo only* — the top task carrying =:solo:= but not =:quick:=. Bounded scope that Claude can finish end-to-end, but not a short job. + +Within each row, pick a single task per the same-level tie-breakers above (blocks-other-work, recently-discussed, most-foundational). If a row has no tasks, omit it rather than padding. + +The friction filter is the override path. When the cascade winner is partially blocked, hardware-dependent, or simply too large for the user's current state, one of the friction rows is what they pick instead. *** Output Format -Keep the recommendation concise but informative: +Pair the cascade recommendation with the friction block beneath it. Recommendation-at-item-1 convention applies to the friction rows — quick+solo first, since it's the strongest low-friction pick. #+begin_example -Next: Fix org-noter reliability (Method 1, Priority A, 8/18 complete) -Reason: Blocks daily reading/annotation workflow +Cascade recommendation (importance/urgency): +- Fix org-noter reliability — [#A], Method 1, 8/18 complete, blocks daily reading/annotation + +If you want lower friction instead: +1. Quick + solo: Bump linter config — [#C] :quick:solo:, ~15 min +2. Quick: Confirm new dirvish setup — [#B] :quick:, needs your eye +3. Solo: Refactor config-utilities — [#B] :solo:, bounded but multi-hour #+end_example -Include: +Include for each row: - Task name / description. -- One-line reasoning (which cascade step matched and why). -- Progress indicator (for V2MOM-structured todos). +- 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. + +**** 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. +- *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. * Resolving a Task — Format Reference @@ -204,8 +270,12 @@ Key elements: 4. *Creating =todo.org= entries for pure informational reminders* — use judgment in Phase B. 5. *Using a table for the task list* — Craig prefers flat bulleted lists in list mode. 6. *Skipping the cascade order in next mode* — the cascade exists to override subjective choice with objective criteria; respect it. -7. *Recommending more than one task in next mode* — be decisive. Only show 2-3 if truly uncertain after applying the same-level tie-breakers. +7. *Recommending more than one task in the cascade row* — be decisive on the cascade. The friction block is a 3-row choice by design, but the cascade row is single-pick. 8. *Re-querying =todo.org= during Phase C* — the snapshot from Phase A is canonical; don't re-read. +9. *Skipping the friction block when the cascade winner isn't actionable.* The friction filter is the override path; don't fall through to lower-cascade-tier tasks if a =:quick:= or =:solo:= task is what's actually needed. +10. *Padding the friction block when a row is empty.* If no task carries both =:quick:= and =:solo:=, omit that row entirely. Don't promote a =:quick:=-only task into the =:quick:solo:= slot. +11. *Recommending over stale facts in Next Mode.* When =:LAST_AUDIT:= is old or a state signal trips in Phase A.1, offer =task-audit= first. Let the user override — but don't skip the offer. +12. *Running Phase A.1 in List Mode.* The audit-warranted check is a Next Mode pre-step. List Mode displays the plate as-is and shouldn't trigger a write workflow first. * Living Document diff --git a/.ai/workflows/task-audit.org b/.ai/workflows/task-audit.org index edbaa8d..c999dab 100644 --- a/.ai/workflows/task-audit.org +++ b/.ai/workflows/task-audit.org @@ -73,7 +73,9 @@ For every STALE task, edit it in the main thread: - Mark statuses that moved; rewrite "waiting on X" lines whose X resolved. - Fix dead/renamed =file:= links. - *Consolidate duplicates* — when several tasks track the same thing, fold them into one home and delete the duplicates (per the user's call on which is canonical). -- *Re-assess the =:quick:= and =:solo:= tags* — reconciliation can change a task's effort or autonomy: a resolved dependency may make a stuck task =:solo:=, a scope cut may make it =:quick:=, and new complexity surfaced by the sources can invalidate either. Add or remove the tags per the definitions in [[file:task-review.org][task-review.org]] when the reconciled facts make the call clear. When they don't — an effort estimate you can't pin down, a =:solo:= gate you can't confirm — it's a NEEDS-USER flag, not a guess. +- *Ensure priority is set per the project scheme.* The top of the project's =todo.org= should carry the priority legend (=[#A]= through =[#D]=). Every task should carry an explicit priority cookie. If a cookie is missing, or no longer matches the reconciled facts, assign the right level per the legend. If the level is unambiguous from the body, do it autonomously; if it's a judgment call (especially the [#A] / [#B] line for important-but-not-urgent work), flag NEEDS-USER. Also enforce the [#A]-discipline rule from the legend — an [#A] task without a =SCHEDULED:= or =DEADLINE:= line is mis-graded and is either down-graded to [#B] (when reconciled facts say "important but not urgent") or surfaced as NEEDS-USER for the user to date. +- *Ensure a type tag is set.* Every task carries one type tag from the project's tag legend (typically =:feature:= / =:chore:= / =:spec:= / =:bug:=). If missing or wrong, assign or correct it from the body when the type is unambiguous. If two tags fit (a refactor that also fixes a bug; a spec that's also a chore), flag NEEDS-USER rather than picking one silently. +- *Re-assess the =:quick:= and =:solo:= tags* — reconciliation can change a task's effort or autonomy: a resolved dependency may make a stuck task =:solo:=, a scope cut may make it =:quick:=, and new complexity surfaced by the sources can invalidate either. Add or remove the tags per the definitions in the project's tag legend (and [[file:task-review.org][task-review.org]]) when the reconciled facts make the call clear. When they don't — an effort estimate you can't pin down, a =:solo:= gate you can't confirm — it's a NEEDS-USER flag, not a guess. - Bump =:LAST_REVIEWED:= on each edited task. Follow =todo-format.md= for completion mechanics (depth-based DONE vs dated-rewrite) and the working-files / link-hygiene rules when moving artifacts. @@ -84,6 +86,18 @@ Present the NEEDS-USER bucket as a short, scannable list — one line per task, When the trail on a task has gone cold and only the user knows the outcome, *say so and ask* — do not invent a status. ("The screen was set for 5/15; nothing in sessions, email, or chat records what came of it. Did it happen, and what's your read?") +** Phase E — Close out + +After Phase D's judgment calls are adjudicated and applied, stamp the audit run by updating =.ai/notes.org='s *Workflow State* section: + +#+begin_example +:LAST_AUDIT: YYYY-MM-DD +#+end_example + +Use today's =date= output (=date "+%Y-%m-%d"=). Overwrite the previous value — the marker reflects the most recent run, not a history. + +If the *Workflow State* section doesn't exist yet, create it (placement: after *Active Reminders*). Other workflows (notably =open-tasks.org= Next Mode's audit-warranted check) read this marker to decide whether to offer running another audit. + * Common Mistakes 1. *Guessing a judgment call instead of flagging it.* If the answer isn't in the sources, it's bucket 3. Ask. diff --git a/claude-templates/.ai/workflows/open-tasks.org b/claude-templates/.ai/workflows/open-tasks.org index 7143aea..e90bc12 100644 --- a/claude-templates/.ai/workflows/open-tasks.org +++ b/claude-templates/.ai/workflows/open-tasks.org @@ -25,13 +25,51 @@ Don't route "task review" / "review tasks" here — those trigger the hygiene ha Issue all source reads as a single batch of parallel tool calls. They're independent: -1. Read =notes.org= — sections *Active Reminders* and *Pending Decisions* only. +1. Read =notes.org= — sections *Active Reminders*, *Pending Decisions*, and *Workflow State* (note the =:LAST_AUDIT:= date if present; "unset" or absent is treated as "never run"). 2. Read each of the 2-3 most recent files in =.ai/sessions/= — only the *Next Steps* subsection under =* Summary= (those capture recent "next time, do X" items that may still be open). 3. Read =todo.org= — entries under the open-work header (=* $Project Open Work=, e.g. =* Homelab Open Work=). Skip the resolved header (=* $Project Resolved=). 4. Run =date= for an accurate timestamp (used to evaluate deadlines + flag suspected completions). The phases below work entirely from this in-memory snapshot. The original split-workflow versions issued these reads sequentially in different files; consolidating saves round-trips and prevents the two implementations drifting apart. +* Phase A.1: Evaluate audit-warranted (Next Mode only) + +Before Next Mode produces a recommendation, decide whether a =task-audit= run looks warranted. List Mode skips this phase — it displays the plate as-is and should not trigger a write-side-effect workflow first. + +Two triggers; either one trips the warranted flag. + +** Temporal threshold + +If =:LAST_AUDIT:= is absent / unset, or its date is older than 14 days from today, the temporal trigger trips. (The 14-day default is per-project; tune by editing this workflow if a project churns faster or slower.) + +** State-signal scan + +Walk the Phase A snapshot for any of these cheap signals — one is enough: + +1. *Reminder/task mismatch* — an Active Reminder names a task or completion target that doesn't appear in =todo.org=, or =todo.org= names a reminder not present in *Active Reminders*. +2. *Passed scheduled date* — a task with =SCHEDULED:= or =DEADLINE:= earlier than today is still =TODO= or =DOING= with no completion log under it. +3. *"Waiting on X" matches a shipped X* — a task body contains "waiting on", "after X ships", "blocked by", and the recent session *Next Steps* (or *Files Modified*) summaries log the named X as shipped. +4. *Dead =file:= link* — a task body's =file:= link points to a path that no longer exists (one existence check per linked path). +5. *Sub-task DONE coverage >75%* — a parent task is still =TODO= or =DOING= while >75% of its sub-tasks are =DONE= or =CANCELLED= (a likely promotion-to-DONE candidate). + +** Surface and decide + +If neither trigger trips, proceed silently to Phase B. + +If one or both trip, surface them inline and ask: + +#+begin_example +Task-audit looks worth running before recommending what's next: +- Temporal: 23 days since last audit (threshold 14). +- Signal: "blocked by review of spec X" — sessions show spec X merged. + +1. Run task-audit first, then come back to "what's next" (recommended) +2. Skip the audit — proceed straight to the recommendation +3. Defer — proceed now, file a TODO to run task-audit later +#+end_example + +If the user picks option 1, hand off to =task-audit.org= and resume Next Mode at Phase B after it stamps =:LAST_AUDIT:=. If option 2, continue. If option 3, file a =** TODO [#B] Run task-audit= entry under =* $Project Open Work= with today's date and continue. + * Phase B: Reconcile (both modes) Compare the open-task signal across sources. For each task in =notes.org= Active Reminders or in a recent session's Next Steps that does NOT have a corresponding =todo.org= entry: @@ -236,6 +274,8 @@ Key elements: 8. *Re-querying =todo.org= during Phase C* — the snapshot from Phase A is canonical; don't re-read. 9. *Skipping the friction block when the cascade winner isn't actionable.* The friction filter is the override path; don't fall through to lower-cascade-tier tasks if a =:quick:= or =:solo:= task is what's actually needed. 10. *Padding the friction block when a row is empty.* If no task carries both =:quick:= and =:solo:=, omit that row entirely. Don't promote a =:quick:=-only task into the =:quick:solo:= slot. +11. *Recommending over stale facts in Next Mode.* When =:LAST_AUDIT:= is old or a state signal trips in Phase A.1, offer =task-audit= first. Let the user override — but don't skip the offer. +12. *Running Phase A.1 in List Mode.* The audit-warranted check is a Next Mode pre-step. List Mode displays the plate as-is and shouldn't trigger a write workflow first. * Living Document diff --git a/claude-templates/.ai/workflows/task-audit.org b/claude-templates/.ai/workflows/task-audit.org index edbaa8d..c999dab 100644 --- a/claude-templates/.ai/workflows/task-audit.org +++ b/claude-templates/.ai/workflows/task-audit.org @@ -73,7 +73,9 @@ For every STALE task, edit it in the main thread: - Mark statuses that moved; rewrite "waiting on X" lines whose X resolved. - Fix dead/renamed =file:= links. - *Consolidate duplicates* — when several tasks track the same thing, fold them into one home and delete the duplicates (per the user's call on which is canonical). -- *Re-assess the =:quick:= and =:solo:= tags* — reconciliation can change a task's effort or autonomy: a resolved dependency may make a stuck task =:solo:=, a scope cut may make it =:quick:=, and new complexity surfaced by the sources can invalidate either. Add or remove the tags per the definitions in [[file:task-review.org][task-review.org]] when the reconciled facts make the call clear. When they don't — an effort estimate you can't pin down, a =:solo:= gate you can't confirm — it's a NEEDS-USER flag, not a guess. +- *Ensure priority is set per the project scheme.* The top of the project's =todo.org= should carry the priority legend (=[#A]= through =[#D]=). Every task should carry an explicit priority cookie. If a cookie is missing, or no longer matches the reconciled facts, assign the right level per the legend. If the level is unambiguous from the body, do it autonomously; if it's a judgment call (especially the [#A] / [#B] line for important-but-not-urgent work), flag NEEDS-USER. Also enforce the [#A]-discipline rule from the legend — an [#A] task without a =SCHEDULED:= or =DEADLINE:= line is mis-graded and is either down-graded to [#B] (when reconciled facts say "important but not urgent") or surfaced as NEEDS-USER for the user to date. +- *Ensure a type tag is set.* Every task carries one type tag from the project's tag legend (typically =:feature:= / =:chore:= / =:spec:= / =:bug:=). If missing or wrong, assign or correct it from the body when the type is unambiguous. If two tags fit (a refactor that also fixes a bug; a spec that's also a chore), flag NEEDS-USER rather than picking one silently. +- *Re-assess the =:quick:= and =:solo:= tags* — reconciliation can change a task's effort or autonomy: a resolved dependency may make a stuck task =:solo:=, a scope cut may make it =:quick:=, and new complexity surfaced by the sources can invalidate either. Add or remove the tags per the definitions in the project's tag legend (and [[file:task-review.org][task-review.org]]) when the reconciled facts make the call clear. When they don't — an effort estimate you can't pin down, a =:solo:= gate you can't confirm — it's a NEEDS-USER flag, not a guess. - Bump =:LAST_REVIEWED:= on each edited task. Follow =todo-format.md= for completion mechanics (depth-based DONE vs dated-rewrite) and the working-files / link-hygiene rules when moving artifacts. @@ -84,6 +86,18 @@ Present the NEEDS-USER bucket as a short, scannable list — one line per task, When the trail on a task has gone cold and only the user knows the outcome, *say so and ask* — do not invent a status. ("The screen was set for 5/15; nothing in sessions, email, or chat records what came of it. Did it happen, and what's your read?") +** Phase E — Close out + +After Phase D's judgment calls are adjudicated and applied, stamp the audit run by updating =.ai/notes.org='s *Workflow State* section: + +#+begin_example +:LAST_AUDIT: YYYY-MM-DD +#+end_example + +Use today's =date= output (=date "+%Y-%m-%d"=). Overwrite the previous value — the marker reflects the most recent run, not a history. + +If the *Workflow State* section doesn't exist yet, create it (placement: after *Active Reminders*). Other workflows (notably =open-tasks.org= Next Mode's audit-warranted check) read this marker to decide whether to offer running another audit. + * Common Mistakes 1. *Guessing a judgment call instead of flagging it.* If the answer isn't in the sources, it's bucket 3. Ask. |
