task-agent 1.0.3__tar.gz → 1.0.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. task_agent-1.0.9/.gemini/settings.json +22 -0
  2. {task_agent-1.0.3 → task_agent-1.0.9}/PKG-INFO +1 -1
  3. {task_agent-1.0.3 → task_agent-1.0.9}/docs/README.md +2 -0
  4. task_agent-1.0.9/docs/STATION-MAP.md +154 -0
  5. {task_agent-1.0.3 → task_agent-1.0.9}/docs/architecture/README.md +1 -1
  6. task_agent-1.0.9/opencode.json +16 -0
  7. {task_agent-1.0.3 → task_agent-1.0.9}/pyproject.toml +2 -2
  8. {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/cli.py +27 -123
  9. {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/manager.py +252 -73
  10. {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/mcp.py +19 -14
  11. {task_agent-1.0.3 → task_agent-1.0.9}/tests/test_cli.py +11 -4
  12. {task_agent-1.0.3 → task_agent-1.0.9}/tests/test_manager.py +110 -5
  13. {task_agent-1.0.3 → task_agent-1.0.9}/tests/test_mcp.py +8 -3
  14. {task_agent-1.0.3 → task_agent-1.0.9}/uv.lock +1 -1
  15. task_agent-1.0.3/.gemini/settings.json +0 -11
  16. {task_agent-1.0.3 → task_agent-1.0.9}/.env +0 -0
  17. {task_agent-1.0.3 → task_agent-1.0.9}/.github/workflows/ci.yml +0 -0
  18. {task_agent-1.0.3 → task_agent-1.0.9}/.github/workflows/publish.yml +0 -0
  19. {task_agent-1.0.3 → task_agent-1.0.9}/.gitignore +0 -0
  20. {task_agent-1.0.3 → task_agent-1.0.9}/.mise.toml +0 -0
  21. {task_agent-1.0.3 → task_agent-1.0.9}/.pre-commit-config.yaml +0 -0
  22. {task_agent-1.0.3 → task_agent-1.0.9}/.python-version +0 -0
  23. {task_agent-1.0.3 → task_agent-1.0.9}/.ta/agents/agy-cli/meta.toml +0 -0
  24. {task_agent-1.0.3 → task_agent-1.0.9}/.ta/agents/gh/meta.toml +0 -0
  25. {task_agent-1.0.3 → task_agent-1.0.9}/.ta/agents/minimal/meta.toml +0 -0
  26. {task_agent-1.0.3 → task_agent-1.0.9}/.ta/agents/uat-aws/meta.toml +0 -0
  27. {task_agent-1.0.3 → task_agent-1.0.9}/.ta/worker +0 -0
  28. {task_agent-1.0.3 → task_agent-1.0.9}/CLAUDE.md +0 -0
  29. {task_agent-1.0.3 → task_agent-1.0.9}/GEMINI.md +0 -0
  30. {task_agent-1.0.3 → task_agent-1.0.9}/Makefile +0 -0
  31. {task_agent-1.0.3 → task_agent-1.0.9}/README.md +0 -0
  32. {task_agent-1.0.3 → task_agent-1.0.9}/agent.env +0 -0
  33. {task_agent-1.0.3 → task_agent-1.0.9}/docs/chats/gemini-conversation-1773283034591.json +0 -0
  34. {task_agent-1.0.3 → task_agent-1.0.9}/docs/chats/project-init-to-v0.1.46.md +0 -0
  35. {task_agent-1.0.3 → task_agent-1.0.9}/docs/cli/README.md +0 -0
  36. {task_agent-1.0.3 → task_agent-1.0.9}/docs/development/README.md +0 -0
  37. {task_agent-1.0.3 → task_agent-1.0.9}/docs/task-agents.md +0 -0
  38. {task_agent-1.0.3 → task_agent-1.0.9}/docs/workflow/README.md +0 -0
  39. {task_agent-1.0.3 → task_agent-1.0.9}/scripts/clean_duplicates.py +0 -0
  40. {task_agent-1.0.3 → task_agent-1.0.9}/scripts/migrate_tasks.py +0 -0
  41. {task_agent-1.0.3 → task_agent-1.0.9}/scripts/recover_dates.py +0 -0
  42. {task_agent-1.0.3 → task_agent-1.0.9}/sidecars/adk-worker/.env.example +0 -0
  43. {task_agent-1.0.3 → task_agent-1.0.9}/sidecars/adk-worker/README.md +0 -0
  44. {task_agent-1.0.3 → task_agent-1.0.9}/sidecars/adk-worker/instructions/manager.md +0 -0
  45. {task_agent-1.0.3 → task_agent-1.0.9}/sidecars/adk-worker/instructions/validator.md +0 -0
  46. {task_agent-1.0.3 → task_agent-1.0.9}/sidecars/adk-worker/instructions/worker.md +0 -0
  47. {task_agent-1.0.3 → task_agent-1.0.9}/sidecars/adk-worker/profiles/default.json +0 -0
  48. {task_agent-1.0.3 → task_agent-1.0.9}/sidecars/adk-worker/pyproject.toml +0 -0
  49. {task_agent-1.0.3 → task_agent-1.0.9}/sidecars/adk-worker/uv.lock +0 -0
  50. {task_agent-1.0.3 → task_agent-1.0.9}/sidecars/adk-worker/worker.py +0 -0
  51. {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/__init__.py +0 -0
  52. {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/__main__.py +0 -0
  53. {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/agent.py +0 -0
  54. {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/audit.py +0 -0
  55. {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/config.py +0 -0
  56. {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/discovery.py +0 -0
  57. {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/models/__init__.py +0 -0
  58. {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/models/issue.py +0 -0
  59. {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/models/metric.py +0 -0
  60. {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/plugins/__init__.py +0 -0
  61. {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/plugins/github.py +0 -0
  62. {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/templates.py +0 -0
  63. {task_agent-1.0.3 → task_agent-1.0.9}/test-dir/opencode.json +0 -0
  64. {task_agent-1.0.3 → task_agent-1.0.9}/tests/conftest.py +0 -0
  65. {task_agent-1.0.3 → task_agent-1.0.9}/tests/e2e/test_agent_sandboxing.sh +0 -0
  66. {task_agent-1.0.3 → task_agent-1.0.9}/tests/test_agent.py +0 -0
  67. {task_agent-1.0.3 → task_agent-1.0.9}/tests/test_agy_cli_worker.py +0 -0
  68. {task_agent-1.0.3 → task_agent-1.0.9}/tests/test_cli_windows.py +0 -0
  69. {task_agent-1.0.3 → task_agent-1.0.9}/tests/test_discovery.py +0 -0
  70. {task_agent-1.0.3 → task_agent-1.0.9}/tests/test_manager_git.py +0 -0
  71. {task_agent-1.0.3 → task_agent-1.0.9}/tests/test_metrics.py +0 -0
  72. {task_agent-1.0.3 → task_agent-1.0.9}/tests/test_migration.py +0 -0
  73. {task_agent-1.0.3 → task_agent-1.0.9}/tests/test_templates.py +0 -0
@@ -0,0 +1,22 @@
1
+ {
2
+ "mcpServers": {
3
+ "task-agent": {
4
+ "command": "ta",
5
+ "args": [
6
+ "mcp"
7
+ ],
8
+ "trust": true
9
+ },
10
+ "task_agent": {
11
+ "command": "uv",
12
+ "args": [
13
+ "run",
14
+ "--project",
15
+ "/home/mstouffer/repos/task-agent",
16
+ "ta",
17
+ "mcp"
18
+ ],
19
+ "trust": true
20
+ }
21
+ }
22
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: task-agent
3
- Version: 1.0.3
3
+ Version: 1.0.9
4
4
  Summary: A prioritized, file-based task queue for autonomous agentic workers
5
5
  Author-email: Mark Stouffer <1802850+InTEGr8or@users.noreply.github.com>
6
6
  License: MIT
@@ -12,6 +12,8 @@ Welcome to the comprehensive documentation for **Task Agent**, a prioritized, fi
12
12
  - Best practices for managing tasks from draft to completion.
13
13
  - [**Development**](./development/README.md)
14
14
  - Information on project setup, testing, and release processes.
15
+ - [**Station Map**](./STATION-MAP.md)
16
+ - Formal declaration of task-agent as a typed file-path station system: stations, transitions, edge roles, and conformance gaps.
15
17
 
16
18
  ## 🚀 Quick Start
17
19
 
@@ -0,0 +1,154 @@
1
+ # task-agent Station Map
2
+
3
+ > **Status: RATIFIED — installed at `docs/STATION-MAP.md` on 2026-07-15.**
4
+ >
5
+ > This document declares task-agent as an instance of the typed file-path-queue substrate
6
+ > ("a typed state machine where the states are portable file paths") whose vocabulary is
7
+ > owned by the library design spec in the cocli repo (task:
8
+ > `design-spec-for-reusable-typed-file-path-queue-transformer-library-extracted-from-cocli`;
9
+ > thesis: cocli `docs/DESCRIPTION.md`). Do not fork terminology here — if a term is missing,
10
+ > add it to the spec, then use it.
11
+ >
12
+ > Sections marked **observed** describe the implementation as found on 2026-07-12.
13
+ > Sections marked **declared (v1)** are proposals ratified on 2026-07-15.
14
+
15
+ ## 1. Record type (observed)
16
+
17
+ One task = one directory, named by **slug**, containing `README.md`
18
+ (Markdown body + YAML frontmatter, currently `created_at` only). The runtime model is
19
+ `Issue` (`src/taskagent/models/issue.py`): `name`, `slug`, `blocked_by: List[str]`,
20
+ `subtask_of: Optional[str]`, with `priority` and `status` derived at runtime.
21
+ Serialization to the index is USV (`\x1f`-delimited).
22
+
23
+ Edge fields are persisted as **body prose**, regex-extracted (`manager.py` ~582):
24
+ `**Blocked by:**` for ordering dependencies, `**Subtask of:**` for hierarchy.
25
+
26
+ **Declared (v1):** edge fields move to structured frontmatter (`blocked_by:`,
27
+ `subtask_of:` YAML lists); prose-line reader extracts values at runtime.
28
+ Frontmatter is the schema; prose is presentation.
29
+
30
+ ## 2. Stations (observed)
31
+
32
+ Root: `.task-agent/tasks/` (symlinked at `docs/tasks/`).
33
+
34
+ | Station | Record state | Sharding | Notes |
35
+ | :--- | :--- | :--- | :--- |
36
+ | `draft/` | intention, not yet actionable | flat | |
37
+ | `pending/` | actionable, unclaimed | flat | |
38
+ | `active/` | claimed by a worker | flat | |
39
+ | `completed/` | terminal fact | by year (`2026/`) | month-sharding is a pending task |
40
+ | `deleted/` | terminal, rejected | flat | tombstone station |
41
+ | `mr/` | merge request datagram | by year | **Declared (v1):** task-machine station (see §2a) |
42
+ | `strategy/`, `plan.md` | mission-level records | — | not task records; out of scope of the task state machine |
43
+
44
+ **Declared (v1):** every station binds `(path template, record type, serialization)`.
45
+ `strategy/` and `plan.md` are explicitly **out of scope of the task state machine** —
46
+ they are mission-level documents with their own lifecycle, not task records.
47
+
48
+ ### §2a. `mr/` station (ratified)
49
+
50
+ **Record type:** Solution datagram — a text file (`<slug>.md`) containing the solution
51
+ explanation written by a worker agent.
52
+
53
+ **Inbound transition:** A worker that cannot directly commit writes its solution to
54
+ `mr/<slug>.md` instead of calling `ta done`. This is the handoff from the worker's
55
+ isolated environment.
56
+
57
+ **Outbound transition:** `ta merge <slug>` reads the datagram, calls `complete_issue`,
58
+ moves the task to `completed/`, and unlinks the MR file. This is a full task-machine
59
+ transition (`active/` → `completed/`) mediated by the datagram.
60
+
61
+ **Conclusion:** `mr/` is a **station of the task machine**, not a separate machine.
62
+ Its records are ephemeral (deleted on merge); they do not need their own durability
63
+ contract beyond the filesystem.
64
+
65
+ ## 3. Transitions (observed)
66
+
67
+ Typed from-station→to-station moves, all implemented as directory renames:
68
+
69
+ | Transition | From → To | Notes |
70
+ | :--- | :--- | :--- |
71
+ | `create` | ∅ → `pending/` (or `draft/` with flag) | assigns slug from title |
72
+ | `promote` | `draft/` → `pending/` | cascades along edges |
73
+ | `demote` | reverse of promote | |
74
+ | `start` / mark-active | `pending/` → `active/` | **this is the claim** |
75
+ | `complete` | `active/` → `completed/YYYY/` | terminal |
76
+ | `restore` | `completed/` → active tree | |
77
+ | `delete` | → `deleted/` | tombstone, not erasure |
78
+ | `merge` (via `mr/`) | `mr/<slug>.md` → `complete` | worker handoff path |
79
+
80
+ **Declared (v1):** each transition is a pure typed transform on the record
81
+ (from-model-to-model); a transition never partially rewrites a record it isn't moving.
82
+
83
+ ## 4. Edge roles: queue / WAL / index (observed + declared)
84
+
85
+ Roles attach to *consumer edges*, not to directories. Current reading:
86
+
87
+ - **Queue (future):** `pending/` → worker edge. `start` is the claim; `active/` holds the
88
+ lease; `completed/`, `deleted/` are terminal states. Single-worker today, so the claim
89
+ protocol is trivially safe; multi-agent use makes atomic-rename claiming load-bearing.
90
+ - **WAL (past):** `completed/YYYY/` is the fact log of the task machine — append-only,
91
+ year-partitioned segments, source of truth for history. The pending
92
+ month-sharding+WAL+compaction task extends exactly this role.
93
+ - **Index (present):** the USV issues index (with `datapackage` field declarations in
94
+ `manager.py`) is a fold over the station tree; blocked-state computation is a derived
95
+ view over `blocked_by` edges. Both must be rebuildable by rescanning stations —
96
+ **stations are the source of truth; the index is disposable.**
97
+ - **Event log:** `AuditLog` (`src/taskagent/audit.py`) writes JSONL lifecycle events to
98
+ `.task-agent/logs/YYYY-MM-DD.jsonl`, pruned after 30 days.
99
+ **Ratified:** `AuditLog` is **telemetry**, not a WAL. The station directories are the
100
+ single source of truth for task state; the JSONL log is observability data only. Pruning
101
+ after 30 days is correct and intentional. No compaction policy is needed.
102
+
103
+ ## 5. Identity (ratified)
104
+
105
+ The **directory name (slug)** is the immutable unique identifier of a task, assigned at
106
+ creation from the title and never subsequently derived from mutable fields.
107
+
108
+ - Editing the `# Title` heading in `README.md` changes the task's *display name* only;
109
+ the slug (and therefore its station path) is unchanged.
110
+ - Direct directory renaming is **forbidden** as an end-user operation — it breaks the
111
+ index, `blocked_by`/`subtask_of` references in other tasks, and `completed/` history.
112
+ - A future `ta rename <slug> <new-title>` command is the only supported path for slug
113
+ migration. It must: rename the directory, update `mission.usv`, and rewrite all
114
+ `blocked_by`/`subtask_of` references that point to the old slug.
115
+
116
+ ## 6. Conformance gaps (the migration backlog)
117
+
118
+ Each accepted gap becomes one follow-up task. Written as typed defects against the
119
+ substrate's invariants:
120
+
121
+ 1. **Identity derived from mutable content.** Slug comes from the *original* title;
122
+ retitling a task changes its display name but not its identity, and lookups by new
123
+ title fail. Invariant: identity is assigned at creation and never derived from
124
+ mutable fields. Fix: add a `ta rename` command (see §5).
125
+ 2. **Schema data outside the schema.** `blocked_by`/`subtask_of` live as regex-matched
126
+ prose lines. Move to frontmatter (§1 declared); keep the alias reader through migration.
127
+ 3. **Non-atomic record updates.** Content updates (`update_task`) rewrite the body and
128
+ silently drop edges — a read-modify-write that loses fields the writer didn't know
129
+ about. Invariant: a record update is a whole-record typed transform; unknown fields
130
+ round-trip. (This is the bug that repeatedly wiped dependencies on the cocli epic.)
131
+ 4. **Station store excluded from its own durability.** `.task-agent` is gitignored in
132
+ consumer repos, so `commit_tasks` fails and task state stays uncommitted. Declare the
133
+ durability contract: either the station tree is committed (un-ignore) or durability is
134
+ explicitly delegated elsewhere.
135
+ 5. **Edge-type conflation cleanup.** `blocked_by` (ordering) vs `subtask_of` (hierarchy)
136
+ are now separate in the `Issue` model with a legacy alias — finish the migration:
137
+ stored prose lines, docstrings (`create_task` still describes deps as parent-of
138
+ promotion cascade), and historical records under `completed/`.
139
+ 6. **Undeclared station resolved.** `mr/` declared in §2a.
140
+ 7. **Log role declared.** `AuditLog` is telemetry (§4).
141
+ 8. **Index rebuildability unproven.** No command exists to rebuild the USV index from a
142
+ station rescan and verify it matches. This is also the seed of the conformance tool:
143
+ `taskagent audit --conformance` = validate station tree against this map + rebuild
144
+ index and diff. That tool, once built, becomes the executable `done_check` for every
145
+ subsequent conformance transform (the portable-task acceptance predicate).
146
+
147
+ ## 7. Relationship to the library spec
148
+
149
+ task-agent is **dogfood consumer #2** (after cocli). The library spec owns: station/
150
+ transition/edge-role vocabulary, the claim protocol, WAL/compaction semantics,
151
+ fold/watermark semantics, and schema versioning. This map owns only: which stations
152
+ exist here, their record types, and this repo's conformance state. When the library's
153
+ Protocol interfaces land, task-agent's `manager.py` should be refactorable to implement
154
+ them with no on-disk change — that refactor being cheap is the test that this map is true.
@@ -35,7 +35,7 @@ Task Agent supports two ways to store task content:
35
35
 
36
36
  ## 🔗 Dependencies
37
37
 
38
- Tasks can depend on one or more other tasks using the `**Depends on:** slug1, slug2` syntax within their Markdown descriptions. Task Agent uses this to provide a hierarchical view in `ta list`.
38
+ Tasks can specify ordering dependencies using the `**Blocked by:** slug1, slug2` syntax within their Markdown descriptions. Task Agent uses this to provide a hierarchical view in `ta list`.
39
39
 
40
40
  ## 🔒 File Protection
41
41
 
@@ -0,0 +1,16 @@
1
+ {
2
+ "$schema": "https://opencode.ai/config.json",
3
+ "mcp": {
4
+ "task_agent": {
5
+ "type": "local",
6
+ "command": [
7
+ "uv",
8
+ "run",
9
+ "--project",
10
+ "/home/mstouffer/repos/task-agent",
11
+ "ta",
12
+ "mcp"
13
+ ]
14
+ }
15
+ }
16
+ }
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "task-agent"
3
- version = "1.0.3"
3
+ version = "1.0.9"
4
4
  description = "A prioritized, file-based task queue for autonomous agentic workers"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -39,7 +39,7 @@ dev = [
39
39
  ]
40
40
 
41
41
  [tool.bumpversion]
42
- current_version = "1.0.3"
42
+ current_version = "1.0.9"
43
43
  parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
44
44
  serialize = ["{major}.{minor}.{patch}"]
45
45
  search = "version = \"{current_version}\""
@@ -586,31 +586,10 @@ def cmd_search(console: Console, manager: TaskAgent, pattern: str):
586
586
  matches.append(i)
587
587
 
588
588
  # Always search completed tasks too
589
- completed_root = manager.issues_root / "completed"
590
- if completed_root.exists():
591
- for year_dir in sorted(completed_root.iterdir(), reverse=True):
592
- if year_dir.is_dir():
593
- for f in year_dir.glob("*.md"):
594
- if fuzzy_match(f.stem, pat_norm):
595
- name = manager.extract_title(f)
596
- matches.append(
597
- Issue(
598
- name=name, slug=f.stem, status="completed", priority=0
599
- )
600
- )
601
- for d in year_dir.iterdir():
602
- if d.is_dir():
603
- readme = d / "README.md"
604
- if readme.exists() and fuzzy_match(d.name, pat_norm):
605
- name = manager.extract_title(readme)
606
- matches.append(
607
- Issue(
608
- name=name,
609
- slug=d.name,
610
- status="completed",
611
- priority=0,
612
- )
613
- )
589
+ for f, slug in manager.walk_completed():
590
+ if fuzzy_match(slug, pat_norm):
591
+ name = manager.extract_title(f)
592
+ matches.append(Issue(name=name, slug=slug, status="completed", priority=0))
614
593
 
615
594
  if not matches:
616
595
  console.print(f"[yellow]No issues match pattern '{pattern}'.[/yellow]")
@@ -703,46 +682,20 @@ def cmd_search(console: Console, manager: TaskAgent, pattern: str):
703
682
  issues = manager.load_mission()
704
683
  matches = [i for i in issues if fuzzy_match(i.slug, pat_norm)]
705
684
  # Also re-search completed
706
- completed_root = manager.issues_root / "completed"
707
685
  new_matches: List[Issue] = list(matches)
708
- if completed_root.exists():
709
- for year_dir in sorted(
710
- completed_root.iterdir(), reverse=True
686
+ for f, slug in manager.walk_completed():
687
+ if fuzzy_match(slug, pat_norm) and not any(
688
+ m.slug == slug for m in new_matches
711
689
  ):
712
- if year_dir.is_dir():
713
- for f in year_dir.glob("*.md"):
714
- if fuzzy_match(f.stem, pat_norm) and not any(
715
- m.slug == f.stem for m in new_matches
716
- ):
717
- name = manager.extract_title(f)
718
- new_matches.append(
719
- Issue(
720
- name=name,
721
- slug=f.stem,
722
- status="completed",
723
- priority=0,
724
- )
725
- )
726
- for d in year_dir.iterdir():
727
- if d.is_dir():
728
- readme = d / "README.md"
729
- if (
730
- readme.exists()
731
- and fuzzy_match(d.name, pat_norm)
732
- and not any(
733
- m.slug == d.name
734
- for m in new_matches
735
- )
736
- ):
737
- name = manager.extract_title(readme)
738
- new_matches.append(
739
- Issue(
740
- name=name,
741
- slug=d.name,
742
- status="completed",
743
- priority=0,
744
- )
745
- )
690
+ name = manager.extract_title(f)
691
+ new_matches.append(
692
+ Issue(
693
+ name=name,
694
+ slug=slug,
695
+ status="completed",
696
+ priority=0,
697
+ )
698
+ )
746
699
  matches = new_matches
747
700
  if cursor >= len(matches):
748
701
  cursor = max(0, len(matches) - 1)
@@ -766,23 +719,7 @@ def cmd_search(console: Console, manager: TaskAgent, pattern: str):
766
719
 
767
720
  def cmd_history(console: Console, manager: TaskAgent, limit: int = 20):
768
721
  """Show completed tasks in reverse chronological order."""
769
- completed_root = manager.issues_root / "completed"
770
- if not completed_root.exists():
771
- console.print("[yellow]No completed tasks found.[/yellow]")
772
- return
773
-
774
- all_completed: List[Tuple[Path, str]] = []
775
-
776
- for year_dir in completed_root.iterdir():
777
- if not year_dir.is_dir():
778
- continue
779
- for f in year_dir.glob("*.md"):
780
- all_completed.append((f, f.stem))
781
- for d in year_dir.iterdir():
782
- if d.is_dir():
783
- readme = d / "README.md"
784
- if readme.exists():
785
- all_completed.append((readme, d.name))
722
+ all_completed = manager.walk_completed()
786
723
 
787
724
  if not all_completed:
788
725
  console.print("[yellow]No completed tasks found.[/yellow]")
@@ -1739,7 +1676,6 @@ def cmd_new(
1739
1676
  title: Optional[str],
1740
1677
  body: str,
1741
1678
  draft: bool,
1742
- depends_on: Optional[str] = None,
1743
1679
  as_dir: bool = True,
1744
1680
  completion_criteria: Optional[str] = None,
1745
1681
  interactive: bool = False,
@@ -1775,7 +1711,7 @@ def cmd_new(
1775
1711
 
1776
1712
  t_body = t.get("body", "")
1777
1713
  t_draft = t.get("draft", draft)
1778
- t_blocked_by = t.get("blocked_by") or t.get("depends_on")
1714
+ t_blocked_by = t.get("blocked_by")
1779
1715
  t_subtask_of = t.get("subtask_of")
1780
1716
  t_as_dir = t.get("as_dir", as_dir)
1781
1717
 
@@ -1783,7 +1719,6 @@ def cmd_new(
1783
1719
  title=t_title,
1784
1720
  body=t_body,
1785
1721
  draft=t_draft,
1786
- depends_on=t_blocked_by,
1787
1722
  as_dir=t_as_dir,
1788
1723
  completion_criteria=t_criteria,
1789
1724
  blocked_by=t_blocked_by,
@@ -1809,7 +1744,7 @@ def cmd_new(
1809
1744
  temp_file = temp_dir / "README.md"
1810
1745
 
1811
1746
  # Resolve relations for the template
1812
- final_blocked_by = blocked_by if blocked_by is not None else (depends_on or "")
1747
+ final_blocked_by = blocked_by or ""
1813
1748
  final_subtask_of = subtask_of or ""
1814
1749
 
1815
1750
  created_at = datetime.now().astimezone().isoformat()
@@ -1856,7 +1791,6 @@ created_at: {created_at}
1856
1791
  title=title,
1857
1792
  body=body,
1858
1793
  draft=draft,
1859
- depends_on=depends_on,
1860
1794
  as_dir=as_dir,
1861
1795
  completion_criteria=completion_criteria,
1862
1796
  blocked_by=blocked_by,
@@ -2142,7 +2076,6 @@ def cmd_update(
2142
2076
  console: Console,
2143
2077
  manager: TaskAgent,
2144
2078
  slug_part: str,
2145
- depends_on: Optional[str] = None,
2146
2079
  blocked_by: Optional[str] = None,
2147
2080
  subtask_of: Optional[str] = None,
2148
2081
  ):
@@ -2159,10 +2092,9 @@ def cmd_update(
2159
2092
 
2160
2093
  updated = False
2161
2094
  try:
2162
- # Handle blocked_by / depends_on
2163
- final_blocked_by = blocked_by if blocked_by is not None else depends_on
2164
- if final_blocked_by is not None:
2165
- manager.update_dependencies(target.slug, final_blocked_by)
2095
+ # Handle blocked_by
2096
+ if blocked_by is not None:
2097
+ manager.update_dependencies(target.slug, blocked_by)
2166
2098
  console.print(
2167
2099
  f"[bold green]Successfully updated prerequisites for task '{target.slug}'.[/bold green]"
2168
2100
  )
@@ -3258,28 +3190,11 @@ def cmd_triage(
3258
3190
  if completed:
3259
3191
  # Load completed issues from disk since they aren't in mission.usv
3260
3192
  all_issues = []
3261
- completed_root = manager.issues_root / "completed"
3262
- if completed_root.exists():
3263
- for year_dir in sorted(completed_root.iterdir(), reverse=True):
3264
- if year_dir.is_dir():
3265
- # File-based
3266
- for f in sorted(year_dir.glob("*.md")):
3267
- slug = f.stem
3268
- name = manager.extract_title(f)
3269
- all_issues.append(
3270
- Issue(
3271
- name=name, slug=slug, status="completed", priority=0
3272
- )
3273
- )
3274
- # Directory-based
3275
- for d in sorted(year_dir.glob("*/README.md")):
3276
- slug = d.parent.name
3277
- name = manager.extract_title(d)
3278
- all_issues.append(
3279
- Issue(
3280
- name=name, slug=slug, status="completed", priority=0
3281
- )
3282
- )
3193
+ for f, slug in manager.walk_completed():
3194
+ name = manager.extract_title(f)
3195
+ all_issues.append(
3196
+ Issue(name=name, slug=slug, status="completed", priority=0)
3197
+ )
3283
3198
  issues = all_issues
3284
3199
  else:
3285
3200
  issues = manager.sync_mission()
@@ -3864,11 +3779,6 @@ def main():
3864
3779
  "update", help="Update task properties (e.g. relationships)"
3865
3780
  )
3866
3781
  update_parser.add_argument("slug", help="Slug of the task to update")
3867
- update_parser.add_argument(
3868
- "-d",
3869
- "--depends-on",
3870
- help="Comma-separated list of task slugs this task depends on (use empty string to clear). Deprecated: use --blocked-by instead.",
3871
- )
3872
3782
  update_parser.add_argument(
3873
3783
  "--blocked-by",
3874
3784
  help="Comma-separated list of prerequisite task slugs that block this task (use empty string to clear)",
@@ -4087,10 +3997,6 @@ Usage:
4087
3997
  new_parser.add_argument(
4088
3998
  "-i", "--interactive", action="store_true", help="Open editor to fill in task"
4089
3999
  )
4090
- new_parser.add_argument(
4091
- "--depends-on",
4092
- help="Comma-separated slugs this task depends on. Deprecated: use --blocked-by instead.",
4093
- )
4094
4000
  new_parser.add_argument(
4095
4001
  "--blocked-by",
4096
4002
  help="Comma-separated slugs of prerequisite tasks that block this task, e.g. 'setup-ci,build-artifacts'.",
@@ -4183,7 +4089,6 @@ Usage:
4183
4089
  console,
4184
4090
  manager,
4185
4091
  args.slug,
4186
- depends_on=args.depends_on,
4187
4092
  blocked_by=args.blocked_by,
4188
4093
  subtask_of=args.subtask_of,
4189
4094
  )
@@ -4254,7 +4159,6 @@ Usage:
4254
4159
  title=args.title,
4255
4160
  body=args.body,
4256
4161
  draft=args.draft,
4257
- depends_on=args.depends_on,
4258
4162
  as_dir=not args.file,
4259
4163
  completion_criteria=args.criteria,
4260
4164
  interactive=args.interactive,