Skip to content

fix(task): reject malformed multi-part --id; surface over-segmented task ids#158

Merged
SUaDtL merged 1 commit into
mainfrom
fix/taskwrite-malformed-id
Jun 28, 2026
Merged

fix(task): reject malformed multi-part --id; surface over-segmented task ids#158
SUaDtL merged 1 commit into
mainfrom
fix/taskwrite-malformed-id

Conversation

@SUaDtL

@SUaDtL SUaDtL commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator

Fixes #157.

Problem

taskwrite add --id <value> accepted a value with more than two dot-separated parts and silently minted a 4-segment task id (e.g. --id mvp1.store.0002 -> mvp1.store.0002.0001) via split(".", 1). That id was then un-targetable by start/done and invisible to validate_id/lint_board_IDISH_RE matched exactly three segments, so _split_id_title absorbed the 4-segment token into the title (id=None). A single mistyped --id permanently stranded a task with no tool path to flip or even detect it (the board hard-rule forbids hand-editing).

Fix

  • taskwrite.py — validate --id is exactly GROUP.TYPE (one dot, two non-empty parts); error clearly and write nothing otherwise, replacing the lossy split(".", 1).
  • _taskboardlib.py — widen _IDISH_RE to 3+ segments so a malformed id parses as an id: rejected by validate_id, surfaced by lint_board, and targetable by set_state for repair (defense in depth).

Tests

  • test_taskwriter.py (+2): --id mvp1.store.0002 rejected (exit 1, board untouched); well-formed --id mvp1.store still mints mvp1.store.0001.
  • test_taskboardlib.py (+5): a 4-segment token parses as id, validate_id rejects it, lint_board surfaces it, set_state targets it; a 3-segment id is unaffected.
  • Suites green: taskboardlib 95, taskwriter 33.

Notes

Found while dogfooding on a downstream project: a harvested follow-up was filed with --id mvp1.store.0002 and could never be marked done. Backward-compatible — well-formed ids and all existing behavior are unchanged.

https://claude.ai/code/session_01Md7D2ufFrX254HkY1GJPZf

taskwrite `add --id <value>` accepted a value with more than two
dot-separated parts and minted a 4-segment id (e.g. `mvp1.store.0002`
-> `mvp1.store.0002.0001`) via `split(".", 1)`. That id was then
un-targetable by start/done and invisible to validate_id/lint_board,
because `_IDISH_RE` matched exactly three segments and `_split_id_title`
absorbed the 4-segment token into the title (id=None). A single mistyped
--id permanently stranded a task with no tool path to fix or detect it.

- taskwrite.py: validate --id is exactly GROUP.TYPE (one dot, two
  non-empty parts); error with a clear message and write nothing
  otherwise, instead of a lossy `split(".", 1)`.
- _taskboardlib.py: widen `_IDISH_RE` to 3+ segments so a malformed id
  parses as an id — rejected by validate_id, surfaced by lint_board, and
  targetable by set_state for repair (defense in depth).
- Tests: +2 CLI (rejection + well-formed still mints), +5 lib
  (parse/validate/lint/target a 4-segment id; 3-segment unaffected).
  Suites: 95 + 33 green.

Closes #157
Claude-Session: https://claude.ai/code/session_01Md7D2ufFrX254HkY1GJPZf
@SUaDtL
SUaDtL merged commit 4fe331c into main Jun 28, 2026
23 checks passed
@SUaDtL
SUaDtL deleted the fix/taskwrite-malformed-id branch June 28, 2026 23:14
SUaDtL added a commit that referenced this pull request Jul 1, 2026
2.6.1 is a security-hardening release: the five P1 enforcement-bypass fixes
(#159-#163) plus the #158 task-id fix and the #151 release-skill/AGPLv3 surface
alignment. Version (plugin.json) and README badges were already at 2.6.1; this
adds the user-facing CHANGELOG section and is the last surface before tagging.


Claude-Session: https://claude.ai/code/session_01JqTxpxEkbDHb6AuADohtMR

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

taskwrite add --id mints a malformed 4-segment task ID that start/done can't target and lint can't surface

1 participant