Files
gems/docs/design/cli-ux-improvements.md
T
joakim f57baee6bc fix: IMP-4/5/6 — parser allowlist, delete ID resolution, consistent errors
IMP-5: Replace strings.Contains(arg, ":") heuristic with an allowlist
of recognized attribute keys (ValidAttributeKeys). Colons in task
descriptions (URLs, "Meeting: topic") are no longer misinterpreted as
modifiers. Canonical key sets live in engine/keys.go and are shared
across parseAddArgs, ParseFilter, and ParseModifier. ParseModifier now
errors on unknown keys.

IMP-4: delete command now loads the working set and resolves display IDs
via GetTaskByDisplayID, matching the pattern used by done/modify.

IMP-6: All action commands (done, delete, modify, start, stop) now
return an error on no-match (stderr, exit 1). Previously done/delete
printed to stdout and exited 0; start/stop had no check at all.

Also adds requirements and design docs for the CLI UX improvements.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 13:37:33 +01:00

15 KiB

Opal CLI: User Experience Improvements

Status: Draft — awaiting feedback Date: 2026-02-18


Problem Statement

Opal's CLI is functional and expressive, but several gaps in feedback, safety, and discoverability make daily use rougher than it needs to be. These improvements target the person who uses opal 10+ times a day — reducing friction, preventing mistakes, and surfacing information at the right moment.


Proposed Improvements

IMP-1: Undo / Uncomplete

Priority: MUST Noted in: opal-web/BUGS.md (missing uncomplete feat)

Problem

Accidentally completing or deleting the wrong task has no quick recovery path. The only workaround is opal edit <uuid> and manually setting status back to pending. This is slow, error-prone, and requires knowing the UUID.

User Stories

US-1.1 As a user, I want to uncomplete a task so that I can recover from accidental completions.

  • Given task 3 was just completed
  • When I run opal 3 uncomplete (or opal undo)
  • Then task 3 returns to pending status, its end timestamp is cleared, and it reappears in my default report

US-1.2 As a user, I want a generic undo that reverts my last action so that I don't need to know the exact reverse command.

  • Given I just ran opal 5 delete
  • When I run opal undo
  • Then task 5 is restored to its previous status

Functional Requirements

  1. FR-1.1: opal undo MUST revert the last mutating CLI action (done, delete, modify, add, start, stop).
  2. FR-1.2: opal <id> uncomplete MUST set a completed task back to pending and clear the end timestamp.
  3. FR-1.3: Undo history SHOULD persist across CLI invocations (stored in a local undo log file or DB table).
  4. FR-1.4: Undo SHOULD support at least the last 10 operations.
  5. FR-1.5: opal undo MUST display what was reverted (e.g., Undone: task 3 "Buy milk" restored to pending).

Design Decisions

  • Scope: Local only — undo does NOT propagate across sync boundaries.
  • Undo add: Deletes the created task entirely (hard delete, not soft).
  • Multi-level: opal undo can be called repeatedly to walk back through the last 10 operations (stack-based, LIFO).

IMP-2: Better Feedback After add

Priority: MUST

Problem

opal add Buy groceries due:tomorrow +errand prints:

Created task 8f3a1b2c-4d5e-6f7a-8b9c-0d1e2f3a4b5c

The UUID is meaningless for subsequent commands. The user can't confirm their modifiers were parsed correctly without running a separate list or info.

User Stories

US-2.1 As a user, I want to see the display ID and parsed attributes after adding a task so that I can confirm it was created correctly and reference it immediately.

  • Given I run opal add Buy groceries due:tomorrow +errand
  • When the task is created
  • Then I see output like:
    Created task 3 — "Buy groceries"
      Due:      tomorrow (2026-02-19)
      Tags:     errand
      Priority: default
    

Functional Requirements

  1. FR-2.1: add MUST display the new task's display ID (not just UUID).
  2. FR-2.2: add MUST echo back all parsed modifiers so the user can verify.
  3. FR-2.3: For recurring tasks, add MUST show recurrence interval and the first instance's due date.
  4. FR-2.4: The display ID shown MUST be valid for immediate use (e.g., opal 3 done).

IMP-3: Show Matched Tasks in Confirmations

Priority: MUST

Problem

Destructive commands (done, delete, modify) prompt with only a count:

About to complete 3 tasks. Proceed? (y/N):

The user can't verify which 3 tasks will be affected without cancelling and running a separate list command with the same filter.

User Stories

US-3.1 As a user, I want to see the list of affected tasks before confirming a bulk action so that I can verify I'm not making a mistake.

  • Given I run opal +errand done
  • When 3 tasks match
  • Then I see:
    About to complete 3 tasks:
      1  Buy groceries          due:tomorrow  +errand
      4  Return library books   due:fri       +errand
      7  Pick up dry cleaning                 +errand
    Proceed? (y/N):
    

Functional Requirements

  1. FR-3.1: done, delete, and modify MUST list matched tasks (ID, description, key attributes) before the confirmation prompt.
  2. FR-3.2: If more than 10 tasks match, show the first 10 and note "...and N more".
  3. FR-3.3: Single-task operations (e.g., opal 3 done) SHOULD still show the task description for verification but skip the y/N prompt.

IMP-4: Fix delete Not Resolving Display IDs

Priority: MUST (bug fix)

Problem

delete calls engine.GetTasks(filter) directly without loading the working set to resolve display IDs. This means opal 3 delete may not resolve ID 3 correctly, unlike done and modify which both load the working set.

Functional Requirements

  1. FR-4.1: delete MUST load the working set and resolve display IDs the same way done and modify do.
  2. FR-4.2: All action commands (done, delete, modify, start, stop, info, edit) MUST use the same ID resolution path.

IMP-5: Handle Colons in Descriptions

Priority: MUST

Problem

parseAddArgs treats any argument containing : as a modifier. This silently drops or misparses descriptions containing colons:

opal add "Meeting: discuss Q3 goals"     # "Meeting:" parsed as modifier
opal add Fix bug in http://example.com   # "http:" parsed as modifier

There's no escaping mechanism or error message — the description is silently truncated.

User Stories

US-5.1 As a user, I want to include colons in task descriptions so that I can write natural language without worrying about parser conflicts.

  • Given I run opal add "Meeting: discuss Q3 goals"
  • When the task is created
  • Then the description is Meeting: discuss Q3 goals with no modifiers

Functional Requirements

  1. FR-5.1: Quoted strings MUST be treated as description text, not parsed for modifiers.
  2. FR-5.2: Only tokens matching a known modifier pattern (key:value where key is a recognized attribute like due, priority, project, recur, status, wait, scheduled, until) SHOULD be treated as modifiers.
  3. FR-5.3: Unknown key:value patterns SHOULD be treated as description text, not silently dropped.
  4. FR-5.4: If a token is ambiguous, prefer treating it as description text.

Design Decisions

  • Allowlist approach: Only recognized attribute keys (due, priority, project, recur, status, wait, scheduled, until) are treated as modifiers. All other key:value tokens are treated as description text.

IMP-6: Consistent Error on No-Match

Priority: SHOULD

Problem

Action commands behave inconsistently when no tasks match a filter:

Command No-match behavior Exit code
done Prints "No tasks matched." 0
delete Prints "No tasks matched." 0
modify Returns error "no tasks matched" 1
start (unknown — needs verification) ?
stop (unknown — needs verification) ?

Functional Requirements

  1. FR-6.1: All action commands MUST return exit code 1 when no tasks match an explicit filter.
  2. FR-6.2: All action commands MUST print to stderr (not stdout) when no tasks match, to support scripting.
  3. FR-6.3: The message SHOULD be consistent: Error: no tasks matched filter "<filter>".

IMP-7: Recurring Task Feedback

Priority: SHOULD

Problem

Completing a recurring task instance gives no indication about recurrence:

$ opal 3 done
Completed 1 task(s).

The user doesn't know if a next instance was spawned, when it's due, or whether the recurrence is still active.

User Stories

US-7.1 As a user, I want to see recurrence information when completing a recurring task so that I know the schedule is continuing.

  • Given task 3 is a recurring weekly task
  • When I run opal 3 done
  • Then I see:
    Completed task 3 — "Weekly review"
    Next instance created — due: 2026-02-25 (in 7 days)
    

Functional Requirements

  1. FR-7.1: Completing a recurring task instance MUST display whether a new instance was created and its due date.
  2. FR-7.2: If no new instance was created (e.g., recurrence was cleared), the output MUST say so.
  3. FR-7.3: info on a recurring instance SHOULD show the recurrence pattern and parent template UUID.

IMP-8: Shell Completions

Priority: SHOULD

Problem

No tab completion exists for commands, report names, project names, or tag names. For a CLI with 14 commands, 13 report names, and user-defined projects and tags, discoverability is poor.

Functional Requirements

  1. FR-8.1: opal completion bash|zsh|fish MUST generate shell completion scripts (cobra has built-in support for this).
  2. FR-8.2: Completions SHOULD cover: commands, report names, +tag names, project: values, priority: values, and status: values.
  3. FR-8.3: Dynamic completions for tags and projects SHOULD query the database.
  4. FR-8.4: Setup instructions SHOULD be printed after opal completion <shell>.

IMP-9: Relative Dates in CLI Reports

Priority: SHOULD

Problem

CLI report tables likely show absolute dates (2026-02-20). When scanning a task list, relative dates ("in 2d", "yesterday", "3w ago") are faster to parse at a glance. The web UI already uses relative dates.

Functional Requirements

  1. FR-9.1: Due dates in reports MUST be shown as relative when within 14 days (e.g., "tomorrow", "in 3d", "2d ago").
  2. FR-9.2: Dates beyond 14 days SHOULD fall back to short absolute format (e.g., "Feb 28", "Mar 15").
  3. FR-9.3: info SHOULD show both absolute and relative (e.g., Due: 2026-02-20 (in 2 days)).
  4. FR-9.4: Relative display COULD be togglable via config (date_display: relative|absolute).

IMP-10: Dry-Run / Preview for Action Commands

Priority: SHOULD

Problem

Before running opal +errand done, the user often runs opal +errand list first to preview. This is a two-step workflow that could be one step.

Functional Requirements

  1. FR-10.1: done, delete, modify, start, and stop SHOULD support a --dry-run flag that lists matched tasks without acting.
  2. FR-10.2: Dry-run output MUST match the same format as the confirmation listing (IMP-3), followed by "Dry run — no changes made."
  3. FR-10.3: Dry-run MUST exit with code 0 if tasks matched, 1 if none matched.

IMP-11: Task Annotations

Priority: SHOULD

Problem

There's no way to attach notes to a task after creation. For long-running tasks like "Debug auth issue" or "Research hosting options", users want to record progress without cluttering the description.

User Stories

US-11.1 As a user, I want to annotate tasks with timestamped notes so that I can track progress and findings over time.

  • Given task 3 exists
  • When I run opal 3 annotate "Traced to token expiry in middleware"
  • Then the annotation is saved with a timestamp
  • And opal 3 info shows the annotation under the task details

US-11.2 As a user, I want to link a task to a jade-depo note so that I can associate detailed research or write-ups with a task.

  • Given task 3 exists and a jade-depo note "debug-auth-issue.md" exists
  • When I run opal 3 annotate --note debug-auth-issue (or similar)
  • Then the task stores a reference to the jade-depo note
  • And opal 3 info shows the linked note path

Functional Requirements

  1. FR-11.1: opal <id> annotate "<text>" SHOULD add a timestamped note.
  2. FR-11.2: Annotations MUST be visible in info and edit.
  3. FR-11.3: Annotations MUST sync via the existing change log / sync system.
  4. FR-11.4: opal <id> denotate COULD remove the most recent annotation.
  5. FR-11.5: Annotations SHOULD support linking to jade-depo notes (exact mechanism TBD — flag, URI scheme, or convention like note:slug).

Design Decisions

  • Storage: JSON text column (annotations) on the tasks table. Each annotation is a JSON object with timestamp and text fields. Stored as a JSON array, e.g.:
    [
      {"timestamp": 1708300000, "text": "Traced to token expiry in middleware"},
      {"timestamp": 1708310000, "text": "note:debug-auth-issue"}
    ]
    
    This keeps annotations co-located with the task, avoids schema complexity, and syncs naturally via the existing change_log triggers.

Open Questions

  • Should annotations be searchable via filters (e.g., opal annotation:token list)?
  • Jade-depo integration: should opal 3 annotate --note <title> verify the note exists in jade-depo, or just store the reference loosely? Loose coupling is simpler but can lead to stale links.

IMP-12: Task History

Priority: COULD

Problem

The change_log table records every mutation for sync, but there's no user-facing way to view a task's history. Useful for understanding what changed, when, and debugging unexpected state.

Functional Requirements

  1. FR-12.1: opal <id> log COULD display the change history for a task.
  2. FR-12.2: Output SHOULD show timestamp, change type, and what changed:
    2026-02-18 09:15  created    "Buy groceries" priority:D
    2026-02-18 10:30  modified   priority: D → H
    2026-02-18 14:00  completed
    
  3. FR-12.3: History MUST respect the existing change_log retention policy.
  4. FR-12.4: History SHOULD be surfaced in info output (recent changes section) and available in edit as read-only comment lines.

IMP-13: Version Command

Priority: COULD

Functional Requirements

  1. FR-13.1: opal version (or opal --version) MUST print the build version.
  2. FR-13.2: Version SHOULD be set at build time via ldflags, reading from a VERSION file in the repo root.
  3. FR-13.3: Output SHOULD include version, commit hash, and build date.

Out of Scope

  • GUI/TUI redesign — this document covers CLI UX only.
  • New task attributes (e.g., estimated effort, dependencies between tasks).
  • Multi-user features — opal is a personal/household tool.
  • Plugin/hook system — not needed at this stage.
  • Web UI changes — covered separately in opal-web/REQUIREMENTS.md.

Priority Summary

ID Improvement Priority Effort
IMP-1 Undo / uncomplete MUST Medium
IMP-2 Better add feedback MUST Low
IMP-3 Show matched tasks in confirmations MUST Low
IMP-4 Fix delete display ID resolution MUST Low
IMP-5 Handle colons in descriptions MUST Medium
IMP-6 Consistent no-match error behavior SHOULD Low
IMP-7 Recurring task feedback SHOULD Low
IMP-8 Shell completions SHOULD Medium
IMP-9 Relative dates in CLI reports SHOULD Low
IMP-10 Dry-run flag for actions SHOULD Low
IMP-11 Task annotations SHOULD Medium
IMP-12 Task history COULD Medium
IMP-13 Version command COULD Trivial