- fixed:
tq show/info/path/depsno longer fail withticket '<id>' not foundon archived tickets; ID resolution and file lookup for these commands now cover.tickets/archive/as well as.tickets/(tiqt-c4bb)
- added:
tq start/close/cancel/reopenaccept--note TEXT(repeatable). Each note is appended to the## Notessection of every ticket whose status the invocation actually changed, auto-prefixed with a verb tag ([started],[closed],[canceled],[reopened]). All notes in one invocation share a timestamp. Cascades via-f/--forcepropagate the same note(s) to every affected descendant. Failed transitions write no notes. - changed:
tq start/close/cancel/reopenreject idempotent transitions; an explicitly-named target whose current status already equals the requested target status is rejected witherror: <id> is already <status>. In a batch, a single already-at-status ticket aborts the whole invocation before any write.--forcedoes not bypass this check for the named target (still rejected even if descendants are open). - changed: ticket timestamps (frontmatter
createdand Notes entries) are now written in minute-precision Zulu formYYYY-MM-DDTHH:MMZ. Legacy microsecond+offset timestamps remain readable; runtq autofixto normalize them on disk (active and archived). - improved:
tq --helpnow shows[--note]in each transition command signature with a one-line description; the shorttqhelp stays minimal. - fixed:
tq autofixprefix-rename no longer drops## Notesor other body content for tickets without an explicit## Descriptionheading (tiqt-0896).
- added:
tq prunepermanently deletes archived tickets by filter (-s/--status,-t/--type,--before YYYY-MM-DD); filters AND-combine, at least one is required, and deletion is a dry run unless-y/--yesis passed
- added:
tq start/close/cancel/reopenaccept multiple ticket IDs; IDs are validated up front, so an unknown ID or a descendant-blocked target aborts the whole batch before any write (atomic, fail-fast)
- fixed: legacy
status: completedno longer crashestq validate/tq ls/tq showwith a rawValueErrortraceback; the error is now a clean diagnostic pointing attq autofix(tiqt-280e) - fixed:
tq archivenow propagates the "not archivable" constraint throughlinksas well asdepsandparent; previously a link-only chain (open A → link → closed B → link → closed C) could archive C while keeping B (tiqt-6e82) - fixed: malformed ticket files now raise a clear
TicketParseError(unknown frontmatter keys, missing:separator, id mismatch, unknown types, missing fields) instead of crashing with a rawTypeError/ValueError(tiqt-de75, tiqt-4e3d, tiqt-8d9f) - fixed:
read_tickettitle now comes from the first non-empty line after---(strips leading#); the## Descriptionmarker is matched as a whole line, not a substring (tiqt-f586) - fixed:
read_ticketnow filters self-links and self-deps on read, emitting a warning to stderr (tiqt-6deb) - fixed:
_is_blockednow treats dangling (unknown) dep IDs as blocking, sotq ls --readyandtq validateagree on what is actionable (tiqt-5781) - fixed:
tq ls --dep ""and--parent ""now exit with a clear error instead of silently producing all tickets (tiqt-0fd3) - fixed:
tq start/close/cancel/reopenno longer silently swallow a missing-ticket error; it now prints a diagnostic and exits 1 (tiqt-cac3) - improved:
tq showdependency-tree view memoizes subtree depths, preventing exponential blowup on wide/shared dependency subtrees (tiqt-6478) - improved:
tq archivebuilds a reverse-reference index once (O(n)) and propagates the "not archivable" constraint in a single pass, replacing the O(n²) iterative convergence loop (tiqt-ea43)
- changed:
_handle_archivesplit into_build_referrer_index,_compute_archivable, and_move_to_archive(tiqt-6e82) - removed: unused
list_ticket_idsfromstore.py(tiqt-757b) - changed:
TERMINAL_STATUSESis now a module-levelfrozenset[Status]; removedStatus.TERMINALmonkey-patch and three# type: ignore[attr-defined]comments (tiqt-dc74) - changed:
UNSET_TARGETSmoved tostore.py;FieldChanges.conflicting_set_and_unsetand_apply_validated's unset block now iterate the tuple instead of hard-coding the three field names (tiqt-e9d6) - changed:
_abbreviaterenamed toabbreviate(public) since it's imported bycommands/autofix.py(tiqt-855c) - changed:
tq linksbuilds sorted link pairs withouttuple(sorted(...))and dropped the# type: ignore[arg-type](tiqt-8e45) - changed:
validateno longer re-globsarchive/afterload_all_tickets; it loads active and archived separately and unions them (tiqt-3c26) - changed:
_ticket_to_dictreturn-type annotation is honest; status is coerced tostrat the boundary (tiqt-0d9b) - changed:
start/close/cancel/reopencarry their targetStatusviaset_defaults(target_status=...);_handle_statusdispatches on the enum andis_terminal()instead of a four-way string compare onargs.command(tiqt-d266) - changed: added a test asserting the
_handle_statuscascade keeps the parent open whenwrite_ticketraises between the last descendant and the parent write (parent-last invariant) (tiqt-3914) - changed: extracted
_required_str,_optional_str,_required_listhelpers instore.py;read_ticketno longer repeats the sameisinstancecheck eleven times (tiqt-6893) - changed: extracted
_merge_uniquehelper instore.pyand reused it for the tags/deps/links sections of_apply_validated(tiqt-5d5e) - changed:
_apply_scoperaises_LsScopeErrorinstead ofsys.exit-ing mid-pipeline; the top-level_handle_lshandler prints and exits (tiqt-7cf8) - changed:
_TreePrinter._has_filtered_descendantsrenamed tohas_filtered_descendantssince_handle_lscalls it from outside the class (tiqt-1069) - changed:
_handle_depsno longer defines_subtree_depthand_print_treeas closures; they are now module-level (_subtree_depth) and a dataclass (_DepTreePrinter), mirroring the_TreePrinterpattern used byls(tiqt-6ee4) - changed: hoisted function-local
datetime/timezone/Pathimports incommands/lifecycle.pyto module level (tiqt-4597) - changed:
--priorityargparse argument now usestype=int, choices=range(5)instead of string choices; removedVALID_PRIORITIESconstant and manualint()conversion innamespace_to_field_changes(tiqt-6695) - changed: replaced private
T._GenericAliasannotation on all fiveregister()signatures withargparse._SubParsersAction[argparse.ArgumentParser]; removedtype: ignorecomments and unusedimport typing as T(tiqt-d348) - changed:
Statusconverted from plain string-constant class toStrEnum;Ticket.statusis now typedStatus;_checkboxmatch is now exhaustive over enum members (tiqt-43f8) - changed:
is_terminalnow acceptsstatus: Statusinstead of a fullTicket; callers updated to passt.status(tiqt-77ba) - changed: replaced
T.Anyannotations instore.pyandcli.pywith concrete types (_FrontmatterValueunion,str | Sequence[str] | None) (tiqt-66f7) - changed:
has_dep_cycleis now a pure predicate; signature changed to(graph, extra_edges)-- no longer mutates the graph (tiqt-963a) - changed:
apply_field_changessplit into_validate_changes+_apply_validated; validation and mutation are now structurally separate (tiqt-bbd8) - changed: added
iter_ticketsandload_all_ticketsinstore.py; all glob-and-parse loops in lifecycle, autofix, validate, and query now route through them (tiqt-c629) - changed:
_find_open_descendantsreturnsdict[str, Ticket];_handle_statusreuses those loaded tickets for the mutation pass instead of re-reading from disk (tiqt-d5f1) - changed:
resolve_idnow acceptsIterable[str]candidates;resolve_id_in_dir(partial, tickets_dir)is the directory-based wrapper;_resolve_in_setin query.py removed (tiqt-a713) - changed: added
_resolve_or_exithelper in query.py; replaces four identical try/except blocks around resolve_id_in_dir (tiqt-86e8) - changed: added
_ticket_to_dicthelper in query.py; replaces three near-identical JSON serialization literals in show, info, and ls --jsonl (tiqt-07e1) - changed:
_handle_lsdecomposed into_select_source,_apply_scope,_apply_filter,_render_flat,_render_jsonl, and_render_tree; duplicate sort logic eliminated;_TreePrinterdataclass replacesnonlocalclosure (tiqt-8288, tiqt-ed8c)
- changed (BREAKING): per-field mutation verbs removed:
tag,untag,dep,undep,nest,unnest,link,unlink,assign,change-prio,change-type,describe,add-note,xref. No aliases. - added:
tq edit <id> [field-options]is the single post-creation mutation surface. Accepts everycreatefield-option plus--title,--untag,--undep,--unlink, and--unset {parent,xref,assignee}. Setting and unsetting the same field in one call is rejected. - changed (BREAKING): terminal status renamed
completed→closed.tq closenow writesstatus: closed. The verb and stored value match. - added:
tq autofixunconditionally rewrites legacystatus: completed→status: closed. Idempotent. - changed (BREAKING): the legacy
closed → completed/canceledautofix migrator (from v0.1.4) is removed; under v1.2closedis again the terminal status. Users on pre-v0.1.4 data must runautofixon a v0.1.4/v0.1.5 release before upgrading. - changed (BREAKING):
tq create <title>— title is now a required positional. The implicit "Untitled" default is gone. - added:
tq create --link <id>(symmetric, repeatable) andtq create --note <text>(timestamped at creation, repeatable). Both also valid ontq edit. - changed (BREAKING):
tq ls --tagshort-Tremoved. Use--tag. - changed:
tq ls --status completednow exits non-zero with a message pointing atautofixand theclosedspelling.
- added:
tq ls --parent <id>scopes the listing to a ticket and its transitive descendants, rendered as a tree rooted at<id>. The named root is shown as a context heading when it doesn't itself satisfy stacked filters. Stacks with all other filters. - added:
tq ls --dep <id>lists tickets whosedepsdirectly contain<id>, rendered as a flat list. Direct dependents only -- transitive chains are excluded. Stacks with all other filters. - added:
--parentand--depare mutually exclusive with each other.
- changed (BREAKING):
closedstatus no longer exists. The two terminal statuses are nowcompleted(viatq close) andcanceled(viatq cancel). - changed (BREAKING):
tq closesetsstatus: completeddirectly;tq cancelsetsstatus: canceled. Theresolutionfield is removed entirely. - changed (BREAKING):
tq ls --completedandtq ls --canceledflags removed. Usetq ls -s completedortq ls -s canceled(-sis short for--status). - added:
tq autofixmigrates legacyclosedtickets:closed+resolution:canceled→canceled, all otherclosed→completed. Strips strayresolutionfields from non-closed tickets.
- changed (BREAKING):
tq cancelnow rejects a ticket with open descendants (parity withtq close). Pass-f/--forceto cascade-cancel the whole subtree. - added:
tq close -f/--forceto cascade-close a parent and all open descendants ascompleted. - changed (BREAKING):
tq lsrenders canceled tickets as[~]instead of[x].[x]now means "closed, completed" only. - added:
tq ls --archivedlists only archived tickets. - added:
tq ls --all(short:-a) lists active and archived tickets together. Mirrorsls -a. - changed (BREAKING):
tq lsshort flag for--assigneeis now-A(was-a). Use-A "Alice"or--assignee "Alice". - changed (BREAKING):
tq createshort flag for--assigneeis now-A(was-a), matchingtq ls.
- changed: ticket ID prefix is now an abbreviation (max 4 chars) of the project directory name. Multi-token names use first letters of each token; single-token names use the first 4 characters; short multi-token names fill from the trailing characters of the last token.
- changed: prefix prefers a consonant in the 4th position. If the candidate 4th char is a vowel, scan further chars for a consonant; if none, fall back to a 3-char prefix (or accept the vowel if char 3 is also a vowel).
- added:
tq autofixmaintenance command that reconciles tickets with current behavior. Renames tickets with stale ID prefixes, propagating the new IDs into everyparent,deps, andlinksreference (including archived tickets) so nothing is orphaned. - fixed:
tq lsnow includes closed tickets by default. Previously the default filter hidclosedstatus.
Initial release of tiquette (tq), a Python reimplementation of the ticket (tk) bash CLI.
tq create <title>creates a ticket file in.tickets/, printing the generated ID to stdout. The ID uses the current directory name as prefix with a 4-hex suffix (e.g.myproject-a9f9).- Supports all creation flags:
-p(priority 0–4),-t(type),-a(assignee),-d(description),--tag,--dep,--parent,--xref. tq start,close,cancel,reopentransition ticket status. Note: these commands currently produce no output on success (ticket ID is not echoed).tq closerejects a parent with open descendants, printing the blocking child IDs to stderr.- Closing the last open child of a parent prints a notification:
note: <parent-id> has no remaining open children. - After
tq reopen, theresolutionfield is set tonullin the file rather than being removed.
tq lsrenders tickets as an indented tree using box-drawing characters for parent-child relationships. Children appear under their parent with└──connectors.- Line format:
<id> [tags] - [checkbox] <title> <- [deps]. Priority and type tags are hidden when they are the defaults (priority 2, type "task"). - Checkboxes:
[ ]open,[/]in_progress,[x]closed. --readyshows only tickets with no open deps and no open children. A parent with open children is implicitly blocked. Closed deps count as satisfied.--blockedshows tickets with at least one open dep or open child. Parents appear as context headings with their blocked children indented.--status <value>filters by status. In filtered tree views, non-matching parents appear as unlabelled context headings.--assigneeand--tagfilters work (long form only;-aand-Tshort flags are not available).--type,--limit,--sort,--jsonlflags supported.
tq dep/tq undep: add/remove blocking dependencies with cycle detection.tq nest/tq unnest: set/clear parent-child relationships.tq link/tq unlink: bidirectional symmetric links.- Cycle detection rejects both direct and transitive cycles.
tq show <id>: full ticket content with computed sections (Blockers, Blocking, Children, Linked).tq info <id>: frontmatter and relationships only, no body.tq deps <id>: transitive dependency tree with box-drawing characters.tq path <id>: prints the ticket file path.tq tags: tag frequency list for open/in-progress tickets.tq links: all linked pairs across the store.tq archive: moves closed tickets to.tickets/archive/, skipping any referenced by open tickets (with stderr diagnostics). Cascading block detection prevents archiving closed tickets that are deps of blocked-from-archiving tickets.tq validate: ticket store validation.- Partial ID resolution:
tq show 9a50resolves to the matching full ID.
- Tickets are markdown files with YAML frontmatter in
.tickets/. - The
.tickets/directory is created automatically on firsttq create. - The tickets directory can be overridden with the
TICKETS_DIRenvironment variable.