trekoon 0.3.1 → 0.3.2

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.
@@ -0,0 +1,244 @@
1
+ # Planning Reference
2
+
3
+ Write implementation plans directly into Trekoon as epics with task/subtask
4
+ DAGs. Plans must be directly executable without re-interpretation.
5
+
6
+ **Clarify ambiguity upfront.** If the plan has unclear requirements or meaningful
7
+ tradeoffs, ask the user before writing. Present options with clear tradeoffs.
8
+ Use multi-select for independent features that can be combined; use single-select
9
+ for mutually exclusive choices.
10
+
11
+ ## Planning data model
12
+
13
+ - **Epic** = full feature outcome and constraints.
14
+ - **Task** = one complete subsystem/domain work unit that can be owned by one
15
+ agent.
16
+ - **Subtask** = concrete implementation/test/verification step under a task.
17
+ - **Dependency edge** = strict prerequisite only (do not add "nice to have"
18
+ dependencies).
19
+
20
+ All entities start in `todo`. See the status machine in the main SKILL.md.
21
+
22
+ Plan implications:
23
+ - Never set initial status to anything other than `todo` in create commands.
24
+ - Task descriptions should reference valid transitions when documenting
25
+ completion flow (e.g., "todo -> in_progress -> done", not "todo -> done").
26
+ - `task done` auto-transitions through `in_progress`, but `task update` does
27
+ not — plan descriptions should note this for agents.
28
+
29
+ ## Information-dense writing standard
30
+
31
+ ### Epic title
32
+
33
+ Use a functional, outcome-oriented format:
34
+
35
+ `<Product/Area>: <deliverable> to <user/system outcome> (<key constraint>)`
36
+
37
+ Example: `Checkout: add idempotent payment capture to prevent duplicate charges
38
+ (Stripe + retries)`
39
+
40
+ ### Epic description
41
+
42
+ Include:
43
+
44
+ 1. **Goal & why now**
45
+ 2. **In scope** (specific capabilities)
46
+ 3. **Out of scope** (explicit exclusions)
47
+ 4. **Success criteria** (testable outcomes)
48
+ 5. **Risks/constraints** (data migration, latency budgets, auth boundaries)
49
+ 6. **Verification gates** (tests, manual checks, perf/security checks)
50
+
51
+ ### Task title
52
+
53
+ Use a structure that encodes subsystem + action + outcome:
54
+
55
+ `[<Subsystem>] <verb> <artifact/interface> to <observable outcome>`
56
+
57
+ Example: `[API/Auth] issue refresh-token rotation endpoint to invalidate
58
+ replayed sessions`
59
+
60
+ ### Task description
61
+
62
+ Must include:
63
+
64
+ - concrete scope and affected paths/symbols
65
+ - acceptance criteria (observable behavior)
66
+ - required tests/verification commands
67
+ - integration constraints (contracts, backward compatibility)
68
+ - explicit "can run in parallel with ..." or "blocked by ..." note
69
+
70
+ **File scope** — declare explicitly so the agent doesn't waste tokens exploring:
71
+
72
+ - **Target files**: files to create or modify
73
+ - **Read-first files**: files the agent should read for context
74
+ - **Do-not-touch**: paths that parallel agents own — prevents merge conflicts
75
+
76
+ **Context loading hints** — point the agent to existing patterns:
77
+
78
+ - Reference a concrete file as the pattern to mirror
79
+ - Name the specific function/class/export to extend or integrate with
80
+ - State project conventions rather than assuming the agent will discover them
81
+
82
+ **Owner assignment** — when the plan has clear subsystem lanes, assign owners in
83
+ task descriptions so the executor knows which agent/person owns each task:
84
+
85
+ ```
86
+ Owner: auth-lane
87
+ Can run in parallel with: billing-lane tasks
88
+ ```
89
+
90
+ Example task description:
91
+
92
+ ```
93
+ Implement refresh-token rotation endpoint.
94
+
95
+ Target files: src/auth/refresh.ts (new), src/auth/refresh.test.ts (new)
96
+ Read first: src/auth/login.ts (follow same handler pattern)
97
+ Do not touch: src/billing/* (owned by billing-lane)
98
+
99
+ Follow the handler pattern in login.ts: schema validation -> service call ->
100
+ response mapping.
101
+
102
+ Acceptance: POST /auth/refresh returns new token pair, invalidates old token.
103
+ Verify: bun test src/auth/refresh.test.ts
104
+ Owner: auth-lane
105
+ Can run in parallel with: billing-lane. Blocked by: task-types (needs AuthToken).
106
+ ```
107
+
108
+ ### Subtask title/description
109
+
110
+ - Titles are imperative and specific, not generic.
111
+ - Description states exact artifact and completion signal.
112
+ - Use subtasks for real units, not filler checklist noise.
113
+ - Inherit file scope from parent task — only override if different files.
114
+
115
+ ## Parallelism & dependencies
116
+
117
+ Model execution lanes intentionally:
118
+
119
+ - Tasks with no dependency edge between them are parallel candidates.
120
+ - Tasks in different subsystems should usually run in parallel.
121
+ - Tasks in the same subsystem should be combined or sequenced.
122
+ - Keep task groups to ~3-4 tasks per active subsystem lane.
123
+
124
+ Dependency policy:
125
+
126
+ 1. Add an edge only for hard prerequisites.
127
+ 2. Prefer task-to-task dependencies; use subtask dependencies only when required.
128
+ 3. Validate acyclic graph assumptions before finalizing.
129
+
130
+ ## Assign owners after creation
131
+
132
+ After creating tasks, assign ownership for multi-agent execution:
133
+
134
+ ```bash
135
+ trekoon --toon task update <task-id> --owner auth-lane
136
+ trekoon --toon task update <task-id> --owner billing-lane
137
+ ```
138
+
139
+ ## Validate the plan
140
+
141
+ After creating the epic, validate before handing off to execution.
142
+
143
+ ### Check progress structure
144
+
145
+ ```bash
146
+ trekoon --toon epic progress <epic-id>
147
+ ```
148
+
149
+ Verify: total count matches expectations, all tasks are in `todo`, ready count
150
+ equals the number of tasks with no dependencies.
151
+
152
+ ### Run suggest to confirm sanity
153
+
154
+ ```bash
155
+ trekoon --toon suggest --epic <epic-id>
156
+ ```
157
+
158
+ `suggest` will surface issues: sync gaps, recovery needs, or unexpected blocker
159
+ states. If it suggests claiming a task, the plan's dependency graph is valid and
160
+ execution-ready.
161
+
162
+ ### Verify dependency graph
163
+
164
+ ```bash
165
+ trekoon --toon task ready --epic <epic-id> --limit 50
166
+ ```
167
+
168
+ Confirm that the expected first-wave tasks appear as ready candidates and
169
+ second-wave tasks appear as blocked with the right dependencies.
170
+
171
+ ## Plan output and handoff
172
+
173
+ After creating the epic and validating, present a summary to the user. This
174
+ summary is the primary handoff artifact — it must be self-contained and
175
+ actionable.
176
+
177
+ ### ID rules
178
+
179
+ - **Always use full UUIDs** for epic and task IDs. Never use temp-keys
180
+ (`task-truthy`, `@task-api`, etc.) in the summary — those are ephemeral
181
+ creation-time references that do not exist in the database.
182
+ - IDs must be copy-friendly: render them in monospace/code formatting so the
183
+ user can select and copy a UUID directly.
184
+
185
+ ### Summary structure
186
+
187
+ 1. **Epic ID + title** — displayed prominently at the top, e.g.:
188
+ ```
189
+ Epic: <full-uuid>
190
+ Title: <epic title>
191
+ ```
192
+ 2. Tasks grouped by wave/batch with columns: full UUID, title, owner/lane
193
+ 3. Dependencies shown per task (using full UUIDs or task titles, not temp-keys)
194
+ 4. Verification gate (commands to run after all tasks complete)
195
+
196
+ ### Example format
197
+
198
+ ```
199
+ Epic: 904b3129-be2d-4b20-8030-537dc327491a
200
+ Title: Checkout: add idempotent payment capture
201
+
202
+ Wave 1 (parallel)
203
+ | ID | Task | Owner |
204
+ |--------------------------------------|-------------------------------|--------------|
205
+ | c12c9746-dbae-4660-bcbb-ebe660cb7054 | [API] Payment capture endpoint| api-lane |
206
+ | 4f0848f3-538a-44d3-8415-5bb16cf3f39e | [UI] Checkout button states | ui-lane |
207
+
208
+ Wave 2 (depends on wave 1)
209
+ | ID | Task | Depends on |
210
+ |--------------------------------------|-------------------------------|--------------------------------------|
211
+ | 8a76afac-155d-45b3-b205-df2e4ef8988b | [API] Retry logic | c12c9746-dbae-4660-bcbb-ebe660cb7054 |
212
+
213
+ Verification: bun run build && bun run test
214
+ ```
215
+
216
+ ### Execution handoff contract
217
+
218
+ Every plan must be directly executable without re-interpretation. Include these
219
+ in task descriptions:
220
+
221
+ 1. **Lane/subsystem ownership** (`[Subsystem] ...` in title, `--owner` set).
222
+ 2. **Dependency intent** (explicit `blocked by ...` / `can run in parallel
223
+ with ...`).
224
+ 3. **Verification evidence** (exact commands and expected outcome).
225
+ 4. **Completion semantics** (`done` means verified and handoff-ready; use
226
+ `blocked` with reason when not).
227
+ 5. **Stable contract** (execution appends progress notes rather than rewriting
228
+ original plan text unless the plan itself is wrong).
229
+
230
+ ## Quality rules
231
+
232
+ 1. **No markdown plan files** as source of truth.
233
+ 2. **No vague titles** ("Refactor stuff", "Fix bugs").
234
+ 3. **Descriptions must be implementation-usable** by another agent without
235
+ guessing.
236
+ 4. **Every task must define completion evidence** (tests/manual checks).
237
+ 5. **Parallelism must be explicit** (not implied).
238
+ 6. **Status transitions must be valid** — never describe a `todo -> done` flow.
239
+ 7. **Owners should be assigned** when multiple execution lanes exist.
240
+
241
+ ## Large initiatives
242
+
243
+ For large scope, create multiple epics with explicit cross-epic boundaries. Use
244
+ dependencies within each epic DAG and keep each epic executable in bounded time.
package/README.md CHANGED
@@ -129,18 +129,29 @@ For the full lifecycle and examples, read [Quickstart](docs/quickstart.md) and
129
129
 
130
130
  ## AI skill
131
131
 
132
- Trekoon ships with a bundled `trekoon` skill for AI agents. It teaches the
133
- agent to:
132
+ Trekoon ships with a self-contained `trekoon` skill for AI agents. One skill
133
+ covers the full plan-to-completion workflow:
134
134
 
135
- - use `--toon` by default
136
- - prefer the smallest sufficient read
137
- - use transactional bulk planning commands when possible
138
- - append progress and blocker notes instead of rewriting full descriptions
139
- - preview scoped replace before `--apply`
140
- - treat `.trekoon` as shared repo-scoped operational state
135
+ - **Command reference** — `--toon` defaults, status machine, bulk planning,
136
+ append-based progress logging
137
+ - **Planning** decomposition into epic/task/subtask DAGs, writing standard,
138
+ file scopes, owner assignment, dependency modeling
139
+ - **Execution** graph building, lane grouping, sub-agent dispatch, task done
140
+ orchestration, verification
141
+ - **Agent Teams** — TeamCreate/SendMessage pattern for parallel Claude Code
142
+ instances (requires `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=true`)
143
+
144
+ The skill accepts arguments for quick entity-scoped actions:
145
+
146
+ ```
147
+ /trekoon → load the skill
148
+ /trekoon <id> → show status and next steps for an entity
149
+ /trekoon <id> execute → start executing the entity's epic
150
+ /trekoon <id> plan → decompose into tasks/subtasks/deps
151
+ ```
141
152
 
142
153
  Read [AI agents and the Trekoon skill](docs/ai-agents.md) for installation,
143
- editor linking, recommended skill combinations, and example prompts.
154
+ editor linking, and example prompts.
144
155
 
145
156
  ## Read next
146
157
 
package/docs/ai-agents.md CHANGED
@@ -15,9 +15,21 @@ agent to:
15
15
  - preview scoped replace before `--apply`
16
16
  - treat `.trekoon` as shared repo-scoped operational state
17
17
 
18
- The canonical installed file lives at:
18
+ The skill ships with bundled reference guides for planning and execution so the
19
+ agent can handle the full plan-to-completion workflow from a single skill:
19
20
 
20
- - `.agents/skills/trekoon/SKILL.md`
21
+ ```
22
+ .agents/skills/trekoon/
23
+ SKILL.md ← command reference, status machine, agent loop
24
+ reference/
25
+ planning.md ← decomposition, writing standard, validation
26
+ execution.md ← graph building, lane dispatch, verification
27
+ execution-with-team.md ← Agent Teams pattern (Claude Code only)
28
+ ```
29
+
30
+ The agent reads the relevant reference file on demand — `planning.md` when asked
31
+ to plan, `execution.md` when asked to execute, `execution-with-team.md` when Agent
32
+ Teams are available.
21
33
 
22
34
  ## Install the skill
23
35
 
@@ -46,25 +58,41 @@ Path behavior:
46
58
  - `--to <path>` changes only the editor link root
47
59
  - `--allow-outside-repo` is for intentional external links
48
60
 
49
- ## Recommended skill stack
61
+ ## Using the skill with arguments
62
+
63
+ The skill accepts an optional entity ID and action text:
64
+
65
+ ```
66
+ /trekoon → loads the skill normally
67
+ /trekoon <id> → resolves the entity, shows status and next steps
68
+ /trekoon <id> analyze → runs epic progress + suggest, reports findings
69
+ /trekoon <id> execute → starts the execution loop for the entity's epic
70
+ /trekoon <id> plan the implementation → decomposes into tasks/subtasks/deps
71
+ ```
72
+
73
+ The skill resolves the ID as an epic, task, or subtask. For tasks and subtasks,
74
+ it scopes session/suggest/progress calls to the parent epic automatically.
75
+
76
+ ## Skill stack
50
77
 
51
- If your agent environment supports skills, this is the cleanest split of
52
- responsibilities:
78
+ The `trekoon` skill is self-contained for the full plan-to-completion workflow.
79
+ It bundles planning methodology, execution orchestration, and the command
80
+ reference in one install.
53
81
 
54
- | Job | Skill | Why |
82
+ For specialized needs, these optional companion skills add value:
83
+
84
+ | Job | Skill | When to use |
55
85
  | --- | --- | --- |
56
- | Turn a request into a concrete backlog | `writing-plans` | Breaks work into epics, tasks, subtasks, and dependencies |
57
- | Create and update tracker state | `trekoon` | Uses Trekoon safely and efficiently |
58
- | Execute the plan in dependency order | `executing-plans` | Helps the agent work through the backlog without losing the sequence |
59
- | Clarify architecture before planning | `architecting-systems` | Useful when boundaries or ownership are still fuzzy |
86
+ | Clarify architecture before planning | `architecting-systems` | Boundaries or ownership are still fuzzy |
87
+ | Specialized code review | `code-review-expert` | Want structured review before closing an epic |
60
88
 
61
- In practice, the flow is usually:
89
+ In practice, the flow is:
62
90
 
63
- 1. plan the work
64
- 2. load `trekoon`
65
- 3. create or update the Trekoon graph
66
- 4. execute the next ready task
67
- 5. update progress, blockers, and completion state as work moves forward
91
+ 1. `/trekoon` — load the skill
92
+ 2. Plan the work (skill reads `reference/planning.md` internally)
93
+ 3. Create or update the Trekoon graph
94
+ 4. Execute the plan (skill reads `reference/execution.md` internally)
95
+ 5. Update progress, blockers, and completion state as work moves forward
68
96
 
69
97
  ## Default execution loop for agents
70
98
 
@@ -161,29 +189,34 @@ Notes:
161
189
 
162
190
  ## Tell the agent exactly what to do
163
191
 
164
- These prompts work well because they are explicit about the skill order and the
165
- expected loop.
192
+ These prompts work well because they are explicit about the expected workflow.
166
193
 
167
194
  ### Plan first, then create the backlog
168
195
 
169
196
  ```text
170
- Load writing-plans, break this feature into one epic with tasks and subtasks,
171
- then load trekoon and create that graph in Trekoon.
197
+ /trekoon plan this feature as one epic with tasks, subtasks, and dependencies,
198
+ then create the graph in Trekoon.
172
199
  ```
173
200
 
174
201
  ### Execute an existing backlog
175
202
 
176
203
  ```text
177
- Load trekoon, run `trekoon --toon session`, take the next ready task, do the
178
- work, append progress notes, mark it done, and repeat until there are no ready
179
- tasks or you hit a blocker.
204
+ /trekoon <epic-id> execute
205
+ ```
206
+
207
+ Or more explicitly:
208
+
209
+ ```text
210
+ /trekoon — run session, take the next ready task, do the work, append progress
211
+ notes, mark it done, and repeat until there are no ready tasks or you hit a
212
+ blocker.
180
213
  ```
181
214
 
182
- ### Use planning, tracking, and execution together
215
+ ### Plan and execute end to end
183
216
 
184
217
  ```text
185
- Load writing-plans to shape the backlog, load trekoon to create and update the
186
- tasks, then load executing-plans and complete the work in dependency order.
218
+ /trekoon plan this feature, create the backlog, then execute the tasks in
219
+ dependency order until the epic is complete.
187
220
  ```
188
221
 
189
222
  ## Keep reads small and mutations safe
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trekoon",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "AI-first local issue tracker CLI.",
5
5
  "keywords": [
6
6
  "ai",
@@ -34,7 +34,7 @@
34
34
  "docs",
35
35
  "src",
36
36
  "src/board/assets",
37
- ".agents/skills/trekoon/SKILL.md",
37
+ ".agents/skills/trekoon",
38
38
  "README.md",
39
39
  "LICENSE"
40
40
  ],
@@ -555,8 +555,13 @@ export async function bootLegacyBoard(options = {}) {
555
555
  submitCreateSubtask: (id, data) => actions.submitCreateSubtask(id, data),
556
556
  addDependency: (src, data) => actions.addDependency(src, data),
557
557
  dropTaskStatus: (id, status) => actions.dropTaskStatus(id, status),
558
+ getTaskStatus: (id) => actions.getTaskStatus(id),
558
559
  changeEpicStatus: (epicId, status) => actions.changeEpicStatus(epicId, status),
559
560
  bulkSetStatus: (epicId, status) => actions.bulkSetStatus(epicId, status),
561
+ toggleEpicStatusFilter: (status) => actions.toggleEpicStatusFilter(status),
562
+ toggleTaskStatusFilter: (status) => actions.toggleTaskStatusFilter(status),
563
+ resetEpicFilter: () => actions.resetEpicFilter(),
564
+ resetTaskFilter: () => actions.resetTaskFilter(),
560
565
  handleKeydown: (event) => actions.handleKeydown(event),
561
566
  });
562
567
 
@@ -3,7 +3,10 @@ import {
3
3
  panelClasses,
4
4
  renderEmptyState,
5
5
  sectionLabelClasses,
6
+ STATUS_LABELS,
7
+ STATUS_ORDER,
6
8
  } from "./helpers.js";
9
+ import { DEFAULT_STATUS_FILTER } from "../state/store.js";
7
10
 
8
11
  /**
9
12
  * Render the epics overview HTML.
@@ -17,6 +20,9 @@ import {
17
20
  function render(props) {
18
21
  const { visibleEpics, selectedEpicId, store } = props;
19
22
 
23
+ const epicStatusFilter = store.epicStatusFilter || { ...DEFAULT_STATUS_FILTER };
24
+ const isNonDefault = STATUS_ORDER.some(s => epicStatusFilter[s] !== DEFAULT_STATUS_FILTER[s]);
25
+
20
26
  return `
21
27
  <div class="board-root board-root--epics">
22
28
  <section class="board-overview ${panelClasses("board-overview--dense p-4 sm:p-5")}" aria-label="Epics overview">
@@ -31,6 +37,13 @@ function render(props) {
31
37
  <span class="board-chip board-chip--neutral">${store.snapshot.tasks.length} total tasks</span>
32
38
  ${store.isMutating ? '<span class="board-chip board-chip--neutral">Saving\u2026</span>' : ""}
33
39
  </div>
40
+ <div class="board-filter-bar">
41
+ ${STATUS_ORDER.map(status => {
42
+ const active = epicStatusFilter[status] !== false;
43
+ return `<button type="button" class="board-filter-pill ${active ? 'board-filter-pill--active' : 'board-filter-pill--inactive'} board-filter-pill--${status}" data-toggle-epic-status-filter="${status}" aria-pressed="${active}" title="${active ? 'Hide' : 'Show'} ${STATUS_LABELS[status]} epics">${STATUS_LABELS[status]}</button>`;
44
+ }).join('')}
45
+ ${isNonDefault ? `<button type="button" class="board-filter-pill board-filter-pill--reset" data-reset-epic-filter title="Reset filters to defaults">Reset</button>` : ''}
46
+ </div>
34
47
  </header>
35
48
  <div class="board-table board-table--epics">
36
49
  <div class="board-table__header board-table__header--epics hidden md:grid">
@@ -20,8 +20,8 @@ import {
20
20
  STATUS_LABELS,
21
21
  STATUS_ORDER,
22
22
  } from "./helpers.js";
23
- import { orderEpicsNewestFirst } from "../state/store.js";
24
- import { VIEW_MODES } from "../state/utils.js";
23
+ import { DEFAULT_STATUS_FILTER, orderEpicsNewestFirst } from "../state/store.js";
24
+ import { getSelectableStatuses, VIEW_MODES } from "../state/utils.js";
25
25
 
26
26
  // ---------------------------------------------------------------------------
27
27
  // Workspace header
@@ -58,6 +58,9 @@ function renderWorkspaceHeader(props) {
58
58
  visibleTasks,
59
59
  } = props;
60
60
 
61
+ const taskStatusFilter = store.taskStatusFilter || { ...DEFAULT_STATUS_FILTER };
62
+ const isTaskFilterNonDefault = STATUS_ORDER.some(s => taskStatusFilter[s] !== DEFAULT_STATUS_FILTER[s]);
63
+
61
64
  const description = selectedEpic.description?.trim() || "";
62
65
  const inlineSelect = `${fieldClasses()} !py-1 !px-2 !text-xs !min-h-0 !rounded-xl`;
63
66
  const epicStatusTooltip = "Change this epic's overall status.";
@@ -79,7 +82,7 @@ function renderWorkspaceHeader(props) {
79
82
  <label class="board-wh__control-label">
80
83
  <span class="board-wh__control-text">Epic status</span>
81
84
  <select class="${inlineSelect}" name="status" aria-label="Epic status" title="${escapeHtml(epicStatusTooltip)}" ${store.isMutating ? "disabled" : ""}>
82
- ${STATUS_ORDER.map(s => `<option value="${escapeHtml(s)}" ${selectedEpic.status === s ? 'selected' : ''}>${escapeHtml(STATUS_LABELS[s] ?? s)}</option>`).join('')}
85
+ ${getSelectableStatuses(selectedEpic.status).map(s => `<option value="${escapeHtml(s)}" ${selectedEpic.status === s ? 'selected' : ''}>${escapeHtml(STATUS_LABELS[s] ?? s)}</option>`).join('')}
83
86
  </select>
84
87
  </label>
85
88
  </form>
@@ -114,6 +117,14 @@ function renderWorkspaceHeader(props) {
114
117
  <span class="${neutralChipClasses()}">${visibleTasks.length} visible</span>
115
118
  ${store.isMutating ? `<span class="${neutralChipClasses()}">Saving\u2026</span>` : ""}
116
119
  </div>
120
+ <div class="board-filter-bar">
121
+ ${STATUS_ORDER.map(status => {
122
+ const active = taskStatusFilter[status] !== false;
123
+ const count = selectedEpic.counts?.[status] ?? 0;
124
+ return `<button type="button" class="board-filter-pill ${active ? 'board-filter-pill--active' : 'board-filter-pill--inactive'} board-filter-pill--${status}" data-toggle-task-status-filter="${status}" aria-pressed="${active}" title="${active ? 'Hide' : 'Show'} ${STATUS_LABELS[status]} tasks">${STATUS_LABELS[status]} (${count})</button>`;
125
+ }).join('')}
126
+ ${isTaskFilterNonDefault ? `<button type="button" class="board-filter-pill board-filter-pill--reset" data-reset-task-filter title="Reset filters to defaults">Reset</button>` : ''}
127
+ </div>
117
128
  <div class="board-wh__actions">
118
129
  <div class="board-wh__action-group">
119
130
  <button type="button" class="board-copy-btn ${isCopied ? "board-copy-btn--active" : ""}" data-copy-epic-id="${escapeHtml(selectedEpic.id)}" aria-label="${escapeHtml(isCopied ? `Copied epic UUID for ${selectedEpic.title}` : copyLabel)}" title="${escapeHtml(isCopied ? "Epic UUID copied." : copyTooltip)}">
@@ -155,9 +166,12 @@ function renderWorkspaceHeader(props) {
155
166
  // ---------------------------------------------------------------------------
156
167
 
157
168
  function renderKanbanColumns(props) {
158
- const { visibleTasks, selectedTaskId, isMutating } = props;
169
+ const { visibleTasks, selectedTaskId, isMutating, taskStatusFilter } = props;
170
+ const filter = taskStatusFilter || { ...DEFAULT_STATUS_FILTER };
159
171
 
160
- const columnsMarkup = STATUS_ORDER.map((status) => {
172
+ const columnsMarkup = STATUS_ORDER
173
+ .filter((status) => filter[status] !== false)
174
+ .map((status) => {
161
175
  const columnTasks = visibleTasks.filter((t) => t.status === status);
162
176
  const columnTitle = readStatusLabel(status);
163
177
  const content = columnTasks.length === 0
@@ -265,19 +279,20 @@ function render(props) {
265
279
  const headerMarkup = renderWorkspaceHeader({
266
280
  selectedEpic,
267
281
  snapshotEpics,
268
- store: {
269
- copyFeedback: store.copyFeedback,
270
- notesPanelOpen: store.notesPanelOpen,
271
- isMutating: store.isMutating,
272
- selectedEpicId: selectedEpic.id,
273
- view: store.view,
282
+ store: {
283
+ copyFeedback: store.copyFeedback,
284
+ notesPanelOpen: store.notesPanelOpen,
285
+ isMutating: store.isMutating,
286
+ selectedEpicId: selectedEpic.id,
287
+ view: store.view,
274
288
  viewModes,
289
+ taskStatusFilter: store.taskStatusFilter,
275
290
  },
276
291
  visibleTasks,
277
292
  });
278
293
 
279
294
  const contentMarkup = store.view === "kanban"
280
- ? renderKanbanColumns({ visibleTasks, selectedTaskId, isMutating: store.isMutating })
295
+ ? renderKanbanColumns({ visibleTasks, selectedTaskId, isMutating: store.isMutating, taskStatusFilter: store.taskStatusFilter })
281
296
  : renderListView({ visibleTasks, selectedTaskId });
282
297
 
283
298
  return `
@@ -1,4 +1,4 @@
1
- import { escapeHtml, formatDate, normalizeStatus, STATUS_ORDER } from "../state/utils.js";
1
+ import { escapeHtml, formatDate, getSelectableStatuses, normalizeStatus, STATUS_ORDER } from "../state/utils.js";
2
2
 
3
3
  export { escapeHtml, formatDate, normalizeStatus, STATUS_ORDER };
4
4
 
@@ -123,9 +123,10 @@ export function renderStatusBadge(rawStatus, label = readStatusLabel(rawStatus))
123
123
  }
124
124
 
125
125
  export function renderStatusSelect(name, selectedStatus, disabled = false) {
126
+ const statuses = getSelectableStatuses(selectedStatus);
126
127
  return `
127
128
  <select class="${fieldClasses()}" name="${escapeHtml(name)}" ${disabled ? "disabled" : ""}>
128
- ${STATUS_ORDER.map((status) => `
129
+ ${statuses.map((status) => `
129
130
  <option value="${escapeHtml(status)}" ${selectedStatus === status ? "selected" : ""}>${escapeHtml(STATUS_LABELS[status] ?? status)}</option>
130
131
  `).join("")}
131
132
  </select>