diff --git a/.claude/skills/backlog/SKILL.md b/.claude/skills/backlog/SKILL.md new file mode 100644 index 0000000..e70ef7b --- /dev/null +++ b/.claude/skills/backlog/SKILL.md @@ -0,0 +1,103 @@ +--- +name: backlog +description: Keep accepted work that has not started yet in backlog.md — what belongs there, the entry format, priority order, and how an entry leaves the backlog when the work starts or is dropped. Use when work is accepted for later, or when asked to record, note, or make a task for something that will not start now. +argument-hint: "[work to record | entry to start or drop]" +--- + +This skill maintains `backlog.md`, a single document that lists work accepted for +implementation but not started yet, and defines how entries are added, ordered, and +taken out. It assumes nothing about the project beyond the presence of a repository. + +## Project version first + +If the current repository contains `.claude/skills/backlog/SKILL.md` and that file is +not the one you are reading, read it and follow it instead of this file. + +Where the project's own documentation (for example `CONTRIBUTING.md` or `CLAUDE.md`) +sets a different convention, the project's convention wins over this skill. + +## backlog.md + +`backlog.md` records decisions to do a piece of work later, together with what has +already been decided about it. Most of the effort behind an entry is the discussion +that led to it; the entry keeps the outcome of that discussion so that it is not +repeated. An entry is not broken down into tasks: the breakdown is done when the work +starts, when it can draw on the state of the project at that time. + +**Location.** If `backlog.md` already exists in the repository, use that file and never +create a second one. If it does not exist when the first entry is recorded, create it +in the directory where the project keeps its documentation, or in `docs/` (creating it) +if the project has no such directory. + +**What goes here:** +- Work that has been accepted for implementation and is not starting now +- Work accepted on a condition that has not occurred yet, with that condition recorded +- Work postponed before it was finished: once it stops, it counts as not started again + +**What does not go here:** +- Work that starts now — break it down and start it the way the project handles any + change +- An idea that has not been accepted — discuss it first; record it only once accepted +- A known problem deliberately left unfixed — that is tech debt, recorded where the + project keeps it + +**Practical test:** if the answer to "are we going to do this?" is yes, and to "are we +starting now?" is no, it goes in `backlog.md`. + +## Format + +```markdown +# Backlog + +Accepted work that has not started yet, in priority order. + +### `short-name` +**Goal:** what the work achieves for the users of the project +**Context:** decisions, constraints, and open questions already established +**Blocked until:** the event that must occur before the work can start +**Added:** YYYY-MM-DD, in + +### `short-name` +**Goal:** ... +**Context:** ... +**Added:** ... +``` + +**Blocked until** is optional: an entry without it can start at any time. It names a +precondition, not a deadline — the work does not have to start when it occurs. +**Context** may run to several paragraphs or a list; it is the input to planning the +work when it starts. +The **Added** line names where the work was accepted — a review, a plan of work, or a +conversation. Entries have no numbers; the short name identifies an entry and is not +reused while the entry is in the file. + +## Order + +The order of entries is their priority: the entry nearest the top that is not blocked +is the next to start. A new entry goes where its priority puts it, not at the end by +default; ask the user when the position is not evident. + +## Adding and changing an entry + +Add an entry when work is accepted for later — in a discussion, a review, or while +planning or doing other work — or when work already started is postponed; then the +entry carries the decisions made so far. Before writing it, show the entry and its +position to the user. The entry goes into the same commit as the change that led to the +decision, or into a commit of its own when there is no such change. + +An entry is edited when a later discussion changes what was decided: the goal, the +context, the precondition, or the position. It records the current decision, not the +history of how it was reached. + +## Taking an entry out + +An entry leaves the backlog in one of two ways: + +- **Started.** The work is broken down the way the project breaks down any change, with + the entry's goal and context as the input. The entry is removed in the same commit + that creates that breakdown. +- **Dropped.** When the work is no longer wanted, the entry is removed, and the commit + message states why. + +Either way, the entry is deleted rather than marked; its text remains in the repository +history. `backlog.md` stays in place when it becomes empty. diff --git a/.claude/skills/changelog/SKILL.md b/.claude/skills/changelog/SKILL.md new file mode 100644 index 0000000..42cf111 --- /dev/null +++ b/.claude/skills/changelog/SKILL.md @@ -0,0 +1,136 @@ +--- +name: changelog +description: Maintain CHANGELOG.md for users, not developers — Keep a Changelog format, user-visible changes only, the six standard sections, strict entry format, and the release procedure. +argument-hint: "[change to record | release X.Y.Z]" +--- + +Format and rules for maintaining `CHANGELOG.md`. Use this skill when adding an entry, +reviewing whether a change belongs in the changelog, or preparing a release section. + +## Project version first + +If the current repository contains `.claude/skills/changelog/SKILL.md` and that file is +not the one you are reading, read it and follow it instead of this file. + +Where the project's own documentation (for example `CONTRIBUTING.md` or `CLAUDE.md`) +sets a different convention, the project's convention wins over this skill. + +## Existing changelogs + +If `CHANGELOG.md` already exists in a format other than the one described here, do not +convert it unless the user asks for that explicitly. Add new entries in the format the +file already uses, and offer the conversion as a separate change. + +## Audience + +Every entry is written for the **user**, not the developer. The changelog describes +changes in observable behaviour — not implementation details, refactoring, or test +coverage. If a change is invisible to the user, it does not belong here. + +## Structure + +The file follows [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/), with one +deliberate omission: no version comparison links are kept at the end of the file. They +are addressed to developers rather than users, and every release would have to rewrite +them. + +The file opens with a short preamble naming the format, followed by one section per +release, ordered from newest to oldest. The top section is always `[Unreleased]` and +collects changes that have not yet been released. Sections are separated by their +headings alone — no horizontal rules between them. + +```markdown +# Changelog + +All notable changes to this project are documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). + +## [Unreleased] + +### Added +- ... + +## [0.2.0] - 2026-03-15 + +### Changed +- ... + +### Fixed +- ... + +## [0.1.0] - 2026-02-28 + +### Added +- ... +``` + +If the project declares a versioning scheme — in its contributing guide or similar +documentation — the preamble names it as well, for example: "and this project adheres +to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." Do not name a scheme the +project has not declared. + +A version heading is `## [X.Y.Z] - YYYY-MM-DD`: the version in brackets, a hyphen +surrounded by spaces, and the release date in ISO 8601 format. + +## Sections + +Use exactly these six section names, in this order. A section heading appears only +when it has at least one entry — in `[Unreleased]` as well as in released versions. +When `[Unreleased]` has no entries, it consists of its heading alone. The first entry +for a section adds that section's heading at its position in the order below. + +| Section | Use for | +|---------|---------| +| `Added` | New features and capabilities visible to the user. | +| `Changed` | Existing behaviour that has been intentionally altered. | +| `Deprecated` | Features or options that still work but will be removed in a future release. | +| `Removed` | Features or options that no longer exist. | +| `Fixed` | Bugs that were observable by the user and are now resolved. | +| `Security` | Fixed vulnerabilities, so that users know to upgrade. | + +## Entry format + +One entry per line, starting with `-`. One sentence, ending with a period. +No issue numbers, no commit hashes, no author names. + +**Write what changed in behaviour, not what changed in code.** + +Start each entry with the subject of the change, not a verb: + +``` +✓ Export button now produces a file named after the current date. +✓ Filters are no longer reset after a manual data refresh. + +✗ Added date-based filename generation to the export button. +✗ Fixed filter state preservation on refresh. +``` + +## What to include and what to skip + +| Include | Skip | +|---------|------| +| New feature visible to the user | Refactoring without behaviour change | +| Bug fix the user could observe | Dependency updates with no visible effect | +| Config field or CLI flag change | New or updated tests | +| Change in existing feature behaviour | Documentation edits | +| Removed or deprecated feature or option | Internal code cleanup | + +## Releasing + +If the project automates the release, the automation performs these steps; do not +perform them by hand. Otherwise, before tagging a release: + +1. Insert a version heading `## [X.Y.Z] - YYYY-MM-DD` directly below `## [Unreleased]`, + so that the entries collected so far belong to the new version and `[Unreleased]` + is left empty. +2. Commit the result before tagging. + +```markdown +## [Unreleased] + +## [0.2.0] - 2026-03-15 + +### Fixed +- ... +``` diff --git a/.claude/skills/tasks/SKILL.md b/.claude/skills/tasks/SKILL.md new file mode 100644 index 0000000..56ae574 --- /dev/null +++ b/.claude/skills/tasks/SKILL.md @@ -0,0 +1,170 @@ +--- +name: tasks +description: Universal task-file workflow — create a structured tasks file for work that starts now and execute it step by step, tracking what is done vs. pending in any project. No project-specific assumptions. +argument-hint: "[feature description to break down | path to a tasks file]" +--- + +A portable, file-based way to record and track work in any repository. It keeps +"what is done vs. what is left" unambiguous: a single markdown file holds an ordered +list of atomic tasks, each with a plain-text status. The skill requires no particular +tooling or project files and imposes no fixed task categories. + +## Project version first + +If the current repository contains `.claude/skills/tasks/SKILL.md` and that file is +not the one you are reading, read it and follow it instead of this file. + +Where the project's own documentation (for example `CONTRIBUTING.md` or `CLAUDE.md`) +sets a different convention, the project's convention wins over this skill. + +## Mode detection + +Decide what to do from the user's request and the current context: + +- A path to an existing `Active` tasks file is given, or the request names no new work + and exactly one `Active` file exists in the tasks directory (see "The tasks file") → + **Execute** mode. +- A feature or request description is given and no matching file exists → **Create** + mode (breakdown), then offer to start executing. Work that is not to start now does + not get a tasks file (see "Create mode"). +- The request names no new work and no `Active` file exists → propose starting the next + item from where the project keeps accepted work that has not started, if it has such + a place, as **Create** mode; otherwise ask the user what to work on. +- Ambiguous (e.g. several active files, or unclear whether to create or execute) → ask + the user one short question before proceeding. + +## The tasks file + +Use the following format verbatim. + +- **Name:** `YYYY-MM-DD-tasks-{slug}.md` — the date prefix orders files and avoids + name collisions; `{slug}` is a short kebab-case name for the feature/work. +- **Location:** active files live in the *tasks directory*: the directory that already + holds the project's active tasks files, or the parent of the `archive/` directory + that holds its archived ones; otherwise the directory where the project keeps its + documentation; otherwise `docs/`, created if it does not exist. Completed files move + to the `archive/` subdirectory of the tasks directory. +- **Header status:** `**Status:** Active` while in progress; `**Status:** Archived` + once every task is resolved — `Done` or `Skipped`. + +### Layout + +```markdown +# YYYY-MM-DD-tasks-{slug}.md + +**Status:** Active + +## Context + +[Optional, free-form. Shared decisions, constraints, or notes that span several +tasks. Omit the whole section if there is nothing to record.] + +--- + +### TASK-01 `short-name` +**Description:** what to do +**Definition of done:** an observable, checkable outcome +**Status:** Pending + +### TASK-02 `short-name` +**Description:** ... +**Definition of done:** ... +**Status:** Pending +``` + +There are no task types and no cross-references between tasks. Every task has the same +four fields. + +### Status values + +| Status | Meaning | +|--------|---------| +| `Pending` | Not started. | +| `In progress` | Currently being worked on. | +| `Done` | Definition of done is met. | +| `Skipped — ` | Intentionally not done; reason recorded inline. | + +A task is *resolved* when it is `Done` or `Skipped`. + +Write the status text verbatim. The only status that carries extra text is +`Skipped`, which appends its reason. Never annotate a `Pending` task — do not mark one +as "next", do not rank candidates, do not add any note to it. The next task to do is +the one already `In progress`, if any, and otherwise the topmost `Pending` one; that is +self-evident and needs no marker. + +## Create mode (breakdown) + +A tasks file is created when the work starts, not in advance: most of the effort before +that is discussion and decisions, and a breakdown made ahead of time goes stale. + +1. Confirm that the work starts now. If it does not — the user wants it recorded, + noted, or kept for later — do not create a tasks file. Record it where the project + keeps accepted work that has not started, if it has such a place; otherwise ask the + user where it should go. If the work was recorded there earlier, use that record as + the input to the breakdown and take it out as that place's rules require. +2. Turn the feature/request into a complete, ordered list of **atomic** tasks — each + one achievable in a single focused session, with a definition of done that can be + checked. +3. Propose the task structure to the user and let them adjust before writing anything. +4. Write the file to the tasks directory with header `Active` and every task `Pending`, + numbered `TASK-01`, `TASK-02`, … in order. +5. Offer to start executing the first task. + +## Execute mode (tracking discipline) + +1. The next task is the one already `In progress`, if any — for example, one left + unfinished by an interrupted session; otherwise it is the topmost `Pending` one, and + when you start working on it, set its status to `In progress`. No decision is + required either way. Do not start or mark the other `Pending` tasks. +2. Do the work for that one task only — do not run ahead into later tasks. +3. When its definition of done is met, set its status to `Done`. +4. **Same-commit rule:** when the task's work is committed, the updated tasks file goes + into that **same commit** — never a separate status-only commit. This skill follows + the project's normal commit cadence and does not force commits the user did not ask + for; it only governs how the file participates when a commit happens. +5. When every task in the file is resolved — the last one is done or skipped, or the + last `Pending` ones are removed — change the header to `Archived` and move the file + to the `archive/` subdirectory, in the commit that brings the file to that state. +6. When the work shows that the plan is wrong, change it as described in "Changing + the plan". + +## Changing the plan + +The `Pending` tasks are a plan, not a record, and the plan changes when the work shows +it is wrong: a task turns out to be needed, a description contains a mistake, two tasks +contradict each other, or a task is redundant. A task whose outcome is a decision often +determines the tasks that follow it; those are added once the decision is made. + +- `Pending` tasks may be added, edited, reordered, or removed, and the Context section + may be updated. Propose every such change to the user before writing it, as when + creating the file. +- `Done` tasks are not rewritten: they record what was done, and commits refer to them. +- A task that was a mistake in the plan — a duplicate, a contradiction, a task not + needed — is removed. A task that is deliberately not done, where the reason is worth + knowing later, is marked `Skipped — ` instead. +- The order of execution is the order in the file. When tasks are added, removed, or + reordered, the `Pending` tasks may be renumbered so that the numbers follow that + order. `Done`, `Skipped`, and `In progress` tasks keep their numbers, since commits + may refer to them, and a renumbered task never takes a number that one of them holds. + +### Postponing the work + +When work in an `Active` file stops before every task is resolved and is to be resumed +later, record its goal and the decisions already made where the project keeps accepted +work that has not started, if it has such a place; otherwise ask the user. Then, in the +same commit: + +- if everything in the file is carried over — it has no `Done` task and no record + worth keeping on its own — delete it; +- otherwise mark the unresolved tasks `Skipped — postponed` and archive the file, so + that the work already done stays on record. + +## Invariants + +- Tasks are atomic; work on one at a time. +- Never leave a finished task unmarked — the file is the source of truth for progress. +- `Done` tasks are never rewritten; `Pending` tasks change when the plan proves wrong. +- The tasks file travels with the work it describes in a single commit. +- Archive the file (header + move to `archive/`) once every task is resolved. +- Require no particular project files or tooling; follow the project's documented + conventions where they exist. diff --git a/.claude/skills/tech-debt/SKILL.md b/.claude/skills/tech-debt/SKILL.md new file mode 100644 index 0000000..ae9f9bf --- /dev/null +++ b/.claude/skills/tech-debt/SKILL.md @@ -0,0 +1,108 @@ +--- +name: tech-debt +description: Record and track consciously deferred problems in tech-debt.md — what qualifies as tech debt, the TD entry format, and closing entries as fixed or dropped. Use when deferring a known issue or documenting a deliberate compromise. +argument-hint: "[problem to record | TD-NN to close]" +--- + +This skill maintains `tech-debt.md`, a single document that records known problems +deliberately deferred, and defines how entries are added, changed, and closed. It +assumes nothing about the project beyond the presence of a repository. + +## Project version first + +If the current repository contains `.claude/skills/tech-debt/SKILL.md` and that file is +not the one you are reading, read it and follow it instead of this file. + +Where the project's own documentation (for example `CONTRIBUTING.md` or `CLAUDE.md`) +sets a different convention, the project's convention wins over this skill. + +## tech-debt.md + +`tech-debt.md` exists for the entire life of the project. It records known problems +that have been consciously deferred — not forgotten, but deliberately not fixed yet. + +**Location.** If `tech-debt.md` already exists in the repository, use that file and +never create a second one. If it does not exist when the first entry is recorded, +create it in the directory where the project keeps its documentation, or in `docs/` +(creating it) if the project has no such directory. + +**What goes here:** +- A known issue with an explicit decision to defer it, with reasoning +- A compromise made consciously during a code review +- A known problem whose fix requires design or architectural thinking first + +**What does not go here:** +- A bug with no decision to defer it — fix it now or plan it as regular work +- A small cosmetic fix — either fix it immediately or ignore it +- New work accepted for later that does not fix a problem in what exists — record it + where the project keeps accepted work that has not started + +**Practical test:** if during a code review you would write "this is an intentional +tradeoff, document it" — it goes in `tech-debt.md`. If you would write "fix this" — +fix it. + +## Format + +```markdown +# Tech debt + +## Active + +### TD-03 `short-name` +**Problem:** what is wrong +**Why deferred:** reasoning +**Trigger:** the event that makes the fix necessary (a new feature, a performance issue) +**Fix notes:** what is already known about the fix (optional) +**Added:** YYYY-MM-DD, in + +--- + +## Closed + +### TD-01 `short-name` +**Closed:** YYYY-MM-DD — fixed in + +### TD-02 `short-name` +**Closed:** YYYY-MM-DD — dropped: +``` + +Numbers (`TD-NN`) are assigned sequentially and never reused. The **Added** line +names where the decision was made — a review, a plan of work, or a conversation. + +**Trigger** names only the event that makes the fix necessary, not what to do when it +occurs. What is already known about the fix — findings, constraints, and the steps +worked out when the decision to defer was made — goes into **Fix notes**, so that it is +not rediscovered later. The field may run to several paragraphs or a list, and is +omitted when nothing is known yet. + +## Adding and changing an entry + +When a problem is deliberately deferred — during a review, while planning or doing +work, or in a discussion — add an entry under Active with the next free number. Before +writing it, show the entry to the user. The entry goes into the same commit as the +change that led to the decision, such as the review fixes, or into a commit of its own +when there is no such change. + +An entry is edited when a later discussion changes what is known: the problem, the +reasoning, the trigger, or the fix notes. It records the current understanding, not +the history of how it was reached. + +## Closing an entry + +An entry is closed in one of two ways: + +- **Fixed.** When the entry is ready to be fixed, it becomes a regular piece of work, + planned and carried out the way the project plans any change, with the entry's + **Fix notes** as an input to the planning. The entry is closed in the commit that + completes the fix, with `fixed in` and a reference to the plan of the work or the + pull request; a commit is named only when the entry is closed after the fix has been + committed. +- **Dropped.** When a review of the entry shows that no change is needed — the problem + does not occur in practice, or it turns out to be a decision rather than a deferred + problem — the entry is closed with `dropped:` and the reason. + +Either way, the entry moves to the Closed section, keeping its number and short name, +and its body is replaced with the **Closed** line. The original reasoning remains in +the repository history. + +`tech-debt.md` is never moved or split; closed entries stay in its Closed section. diff --git a/CLAUDE.md b/CLAUDE.md index 1b6a804..37b9dcb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,18 +4,16 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Process -The project follows a deliberate, strict workflow defined by process skills: `project-start` (brief, tech stack, architecture, repo skeleton), `tasks` (task breakdown and execution), `tech-debt` (deferred compromises), and `changelog` (CHANGELOG.md entries and releases). If these skills are available, they are authoritative — invoke the matching one before doing the work; this CLAUDE.md intentionally does not duplicate their content. If they are not available in the current environment, ask the user how to proceed instead of improvising the process from memory. - -Note: tasks files created before the move to skills (e.g. `docs/2026-05-20-tasks-html-quirks.md`) follow an older format with task types and emoji statuses; keep their existing format when updating them. +The workflow is defined by four process skills kept in the repository under `.claude/skills/`: `tasks` (task breakdown and execution), `backlog` (accepted work not started yet), `changelog` (`CHANGELOG.md` entries and releases), and `tech-debt` (deferred compromises). They are authoritative — invoke the matching one before doing the work; this CLAUDE.md intentionally does not duplicate their content. `CONTRIBUTING.md` records the choices they leave to the project ("Process") and the purpose of each project document ("Project documents"). ## Project state -The MVP is implemented: `tfscli wit work-items get` works end to end, `tfscli wit work-items list` and `tfscli wit work-items get-batch` read several work items by id, `tfscli wit wiql query-by-wiql` finds work item ids with a WIQL query, and the module has one package per architecture module (see the "Project Structure" section of `docs/architecture.md`). Release 0.0.5, which predates the current command names, has been run against a live TFS instance: `auth login` and reading one work item (`get`) work there; `list`, `get-batch`, and `query-by-wiql` have not been run against a live server yet; the error paths and rich-text rendering on real data have not been checked yet — see the "Status" and "Known limitations" sections of `README.md`. Further work goes through tasks files per the `tasks` skill. +The MVP is implemented: `tfscli wit work-items get` works end to end, `tfscli wit work-items list` and `tfscli wit work-items get-batch` read several work items by id, `tfscli wit wiql query-by-wiql` finds work item ids with a WIQL query, and the module has one package per architecture module (see the "Project Structure" section of `docs/architecture.md`). Release 0.0.5, which predates the current command names, has been run against a live TFS instance: `auth login` and reading one work item (`get`) work there; `list`, `get-batch`, and `query-by-wiql` have not been run against a live server yet; the error paths and rich-text rendering on real data have not been checked yet — see the "Status" and "Known limitations" sections of `README.md`. Accepted work that has not started is listed in `docs/backlog.md`; work in progress goes through tasks files per the `tasks` skill. -Three documents drive everything: +Read these before the corresponding kind of change (the full list of project documents is in "Project documents" in `CONTRIBUTING.md`): -- `project-brief.md` — product scope, constraints, non-goals, error/config/command contracts. **Read this before proposing any user-facing change.** -- `architecture.md` — tech stack with rationale and rejected alternatives. **Read this before adding a dependency or swapping a library.** +- `project-brief.md` — purpose, audience, scope, constraints, non-goals, and design principles of the product. **Read this before proposing any user-facing change.** It changes only when a product decision changes; feature behaviour belongs in `README.md`, not here. +- `architecture.md` — technology stack, architecture, and project structure, with rationale and rejected alternatives. **Read this before adding a dependency or swapping a library.** - Process skills — the development workflow (see "Process" above). ## Non-negotiable product constraints diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cb514c2..6ce57cc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -85,30 +85,54 @@ if a change would break one, raise it before implementing. parsing are `encoding/json`. A fourth dependency is a decision recorded in `docs/architecture.md`, not a `go get`. +## Project documents + +Each document has one job, and new information goes to the document whose job it is. +`project-brief.md` and `architecture.md` also state their purpose at the top. + +| Document | What it records | When it changes | +|---|---|---| +| `docs/project-brief.md` | Purpose, audience, scope, constraints, non-goals, and design principles | A product decision changes — not when a feature is added | +| `docs/architecture.md` | Technology stack, architecture, and project structure, with rationale | The stack, the modules, their interactions, or the top-level structure change | +| `README.md` | What tfscli does and how to use it | Any user-visible behaviour changes | +| `skills/tfscli/SKILL.md` | How an AI agent uses tfscli; ships in every release archive | A command, a flag, an error category, or an environment variable changes | +| `CHANGELOG.md` | User-visible changes, per release | Any user-visible change | +| `CONTRIBUTING.md` | How the project is built, checked, released, and worked on | The build, the rules, or the process change | +| `CLAUDE.md` | Guidance for AI agents working in the repository | Anything it summarises changes | +| `docs/backlog.md` | Accepted work not started yet, in priority order | Work is accepted, started, or dropped | +| `docs/tech-debt.md` | Consciously deferred problems | A problem is deferred or closed | +| `docs/YYYY-MM-DD-tasks-.md` | The plan and progress of one unit of work | The work progresses; archived to `docs/archive/` when done | +| `.claude/skills/` | The process rules below | The process changes | + ## Process -Work is planned and tracked through the process skills `tasks`, `changelog`, and -`tech-debt`, which are authoritative wherever they are available. Where they are not, ask -how to proceed rather than reconstructing the process from the summary below. - -- **Tasks files.** A unit of work gets `docs/YYYY-MM-DD-tasks-.md`, with - `**Status:** Active` in the header and an ordered list of atomic tasks, each carrying a - description, a definition of done, and a status of `Pending`, `In progress`, `Done`, or - `Skipped — `. The file travels in the same commit as the work it describes; a - status-only commit is never correct. When the last task is resolved, the header becomes - `Archived` and the file moves to `docs/archive/` in that same commit. - `docs/2026-05-20-tasks-html-quirks.md` predates this format and keeps its own — task - types and emoji statuses — when updated. -- **Changelog.** `CHANGELOG.md` is written for the user of the tool: features, observable - bug fixes, changed configuration keys and flags, behaviour changes, and removals, but not - refactoring, tests, documentation, or invisible dependency updates. The format is - [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/) without version - comparison links. Sections are exactly `Added`, `Changed`, `Removed`, and `Fixed`, in - that order, and a section heading appears only when it has entries; an entry is one - sentence starting with the subject of the change rather than a verb. Unreleased work - accumulates under `[Unreleased]`. -- **Tech debt.** A consciously deferred compromise is recorded in `docs/tech-debt.md` - rather than left in a comment, and is closed through the normal tasks-file workflow. +Work is planned and tracked with four process skills kept in the repository. Each is a +plain Markdown file and states the full rules, for people as much as for AI agents, +completed by the project choices listed below: + +- `.claude/skills/tasks/SKILL.md` — tasks files; +- `.claude/skills/backlog/SKILL.md` — `docs/backlog.md`; +- `.claude/skills/changelog/SKILL.md` — `CHANGELOG.md`; +- `.claude/skills/tech-debt/SKILL.md` — `docs/tech-debt.md`. + +The plans, the backlog, and the tech debt are kept as files in the repository rather +than in an issue tracker, so that each decision is versioned together with the change it +concerns, is reviewed in the same pull request, and can be read by an AI agent without +access to an external service. + +The skills are written to be portable: the same files are also used outside this +repository, which is why they make no assumptions about the project. A copy installed +as a personal skill takes precedence over the project one in Claude Code, so each skill +opens with a "Project version first" section that sends the reader to the repository +copy when one exists; in the repository copy that section has no effect. + +The skills leave a few choices to the project. In tfscli: + +- Tasks files live in `docs/`, and archived ones in `docs/archive/`. Work taken from the + backlog and the fix of a tech debt entry are planned as a tasks file. +- The changelog preamble names Semantic Versioning, as declared in "Choosing the + version", and the version heading is inserted by the release workflow (see + "Releasing"). ## Commits and branches @@ -152,8 +176,10 @@ major version is 0, the usual convention for `0.y.z` applies: version — `0.1.0` after `0.0.6`. Breaking usage means removing or renaming a command, a flag, an environment variable, or a configuration key, or refusing input that used to be accepted. An entry under `Removed` in `[Unreleased]`, or an entry under `Changed` - that does one of these, makes the release breaking; -- any other release — new features and fixes only — raises the patch version. + that does one of these, makes the release breaking. An entry under `Deprecated` does + not: what it names still works; +- any other release — new features, deprecations, and fixes only — raises the patch + version. Version `1.0.0` is a separate, explicit decision. From then on, a breaking release raises the major version. diff --git a/README.md b/README.md index 19fe022..b676e61 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Not present in v1: write operations (out of scope) and JSON output (`--json`, pl ### Known limitations - **HTML tables collapse.** The markdown converter runs on library defaults, which have no table support: a `` becomes its cell text run together, without separators. A work item whose Description holds a table renders unreadably. -- **Rich-text noise is unhandled.** @-mentions, attachment links, Word- and Outlook-pasted markup, and work-item references are converted literally, with whatever wrapper markup TFS stored. Fixing this needs samples from a real instance and is tracked in `docs/2026-05-20-tasks-html-quirks.md`. +- **Rich-text noise is unhandled.** @-mentions, attachment links, Word- and Outlook-pasted markup, and work-item references are converted literally, with whatever wrapper markup TFS stored. Fixing this needs samples from a real instance and is recorded in `docs/backlog.md`. - **Servers with anonymous access are not supported.** `auth login` has not been tested against a server that admits anonymous requests, such as Azure DevOps Server with public projects enabled. On such a server an invalid PAT may pass the login check and be stored, and the other commands would then read as the anonymous user, seeing less, without an error. ## Installation diff --git a/docs/2026-05-20-tasks-html-quirks.md b/docs/2026-05-20-tasks-html-quirks.md deleted file mode 100644 index 7e1ccd9..0000000 --- a/docs/2026-05-20-tasks-html-quirks.md +++ /dev/null @@ -1,42 +0,0 @@ -## 2026-05-20-tasks-html-quirks.md - -**Status:** Active -**Trigger:** A decision to collect rich-text samples from a live TFS instance. The work in this file is deliberately deferred until then — see the "HTML quirks" decision in the conversation that produced the Architecture section of `architecture.md`. A live connection is no longer the blocker: release 0.0.5 ran `auth login` and the single work item read (now `wit work-items get`) successfully against a live server. - -### Contracts - -No new API contracts. Scope is limited to the `htmlmd` module and golden tests around it. - -### Note on task types - -This file predates the move of the process into the global `tasks` skill and keeps the older format: task types (Regular, Smoke, Review, Docs) and emoji statuses. Under that format a tasks file normally contains all four task types. This file deliberately omits the Smoke type: there is no running code to smoke-test — the feature is fixtures (`testdata/`) plus golden tests plus library-plugin rules. TASK-01 already requires a live-TFS round-trip to harvest the fixtures, which is the only end-to-end interaction the feature can produce; the golden tests in TASK-02 are the verification mechanism instead of a smoke check. - ---- - -### TASK-01 `collect-html-samples` -**Type:** Regular -**Description:** Connect to a live TFS instance and dump the raw HTML of 10–20 work item rich-text fields, with coverage across `System.Description`, `Microsoft.VSTS.TCM.ReproSteps`, `Microsoft.VSTS.TCM.SystemInfo`, and `Microsoft.VSTS.Common.AcceptanceCriteria`. Aim for variety: items with @-mentions, items with attachments, items with Word- or Outlook-pasted content, items linking to other work items. Store the samples as fixtures under `internal/htmlmd/testdata/` (one file per sample, `.html` extension). Add a sibling `README.md` enumerating each file with one-line provenance — work item type and which quirks it exhibits. -**Definition of done:** ≥10 sample HTML files committed under `internal/htmlmd/testdata/`, with a `README.md` enumerating them. -**Status:** ⬜ Pending - -### TASK-02 `golden-tests-baseline` -**Type:** Regular -**Description:** For each sample collected in TASK-01, add a golden test that runs the current `htmlmd.Convert` over it and records the output as `.expected.md` next to the input. Goal here is to capture today's behaviour, not to improve it — golden tests will signal when changes in TASK-03 take effect. Standard `-update` flag pattern to regenerate expectations. -**Definition of done:** all golden tests pass; running with `-update` regenerates expected files. -**Status:** ⬜ Pending - -### TASK-03 `identify-and-fix-noise` -**Type:** Regular -**Description:** Inspect the golden outputs from TASK-02. Classify noise: mentions rendering as `[@name](#)`, attachment links pointing to authenticated URLs, Word-paste leftovers (``, `MsoNormal`, mso-* attributes), work-item references rendering as long URLs. Known before the samples arrive: the library's default rule set has no table support, so `
` markup collapses to its concatenated cell text — enabling `plugin/table` belongs to this task. For each chosen class, add a rule via the library's plugin API. Update golden expectations in the same commit. Out of scope: anything that requires additional TFS API calls (e.g. fetching attachment filenames from the attachments endpoint) — that becomes a separate feature. -**Definition of done:** the list of noise classes addressed is recorded both in the commit message and in this tasks file (append a short bullet list under this DoD when the task completes); each rule has a corresponding golden diff demonstrating its effect. -**Status:** ⬜ Pending - -### TASK-04 `review` -**Type:** Review -**Covers:** TASK-01, TASK-02, TASK-03 -**Status:** ⬜ Pending - -### TASK-05 `docs` -**Type:** Docs -**Description:** Update `README.md`, `CHANGELOG.md`, and the "Known limitations" section of `skills/tfscli/SKILL.md` with a one-line user-facing note that HTML rendering now handles the relevant TFS patterns (mentions, attachments, Word paste, work-item refs — whichever were addressed). `README.md` and `SKILL.md` both carry the limitation and must stop claiming it together. No technical detail in `CHANGELOG.md`; see the `changelog` skill. -**Status:** ⬜ Pending diff --git a/docs/2026-10-04-tasks-domains-3-git-pull-requests.md b/docs/2026-10-04-tasks-domains-3-git-pull-requests.md deleted file mode 100644 index 86a90a2..0000000 --- a/docs/2026-10-04-tasks-domains-3-git-pull-requests.md +++ /dev/null @@ -1,22 +0,0 @@ -# 2026-10-04-tasks-domains-3-git-pull-requests.md - -**Status:** Active - -## Context - -Third of five planned API domains, numbered in the order they are to be taken. -An agent sees the code through the local `git`, but pull requests, review -comments, and linked work items exist only on the server; the "address the -review comments" scenario is impossible without them. The domain is of no use -on TFVC projects. Pull request threads appeared only in TFS 2017; under -"Server compatibility is preserved" in `docs/project-brief.md` they should be -optional, failing with a clear error on an older server while everything else -keeps working. - ---- - -### TASK-01 `git-pull-requests` -**Description:** Read pull requests, their comment threads, and linked work -items. To be broken down into atomic tasks before work starts. -**Definition of done:** The task is broken down into atomic tasks in this file. -**Status:** Pending diff --git a/docs/2026-10-04-tasks-domains-4-workitem-comments.md b/docs/2026-10-04-tasks-domains-4-workitem-comments.md deleted file mode 100644 index 16785f5..0000000 --- a/docs/2026-10-04-tasks-domains-4-workitem-comments.md +++ /dev/null @@ -1,18 +0,0 @@ -# 2026-10-04-tasks-domains-4-workitem-comments.md - -**Status:** Active - -## Context - -Fourth of five planned API domains, numbered in the order they are to be taken. -This extends the existing work item domain rather than adding a new one. The -Comments API exists only on recent servers; on older ones the discussion is -kept in the revisions of the `System.History` field. - ---- - -### TASK-01 `workitem-comments` -**Description:** Read the discussion of a work item. To be broken down into -atomic tasks before work starts. -**Definition of done:** The task is broken down into atomic tasks in this file. -**Status:** Pending diff --git a/docs/2026-10-04-tasks-domains-5-build.md b/docs/2026-10-04-tasks-domains-5-build.md deleted file mode 100644 index 917dc3c..0000000 --- a/docs/2026-10-04-tasks-domains-5-build.md +++ /dev/null @@ -1,19 +0,0 @@ -# 2026-10-04-tasks-domains-5-build.md - -**Status:** Active - -## Context - -Fifth of five planned API domains, numbered in the order they are to be taken. -Scenario: a CI build failed and the agent is to find out why, locating the -failed step through the timeline and reading its log. Less frequent than the -domains before it, since many on-prem teams run CI outside TFS Build. The -volume of logs needs a decision: the whole log or only the failed step. - ---- - -### TASK-01 `build-status-and-logs` -**Description:** Read builds, their timeline, and their logs. To be broken down -into atomic tasks before work starts. -**Definition of done:** The task is broken down into atomic tasks in this file. -**Status:** Pending diff --git a/docs/architecture.md b/docs/architecture.md index fa11231..37cbb21 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,5 +1,7 @@ # Architecture — tfscli +This document records the technology stack, the architecture, and the project structure of tfscli, with their rationale and the alternatives rejected. It changes when the stack, the modules, their interactions, or the top-level structure change. The behaviour of individual features is documented in `README.md`, not here. + ## Tech Stack ### Language: Go @@ -175,7 +177,7 @@ This section describes the conceptual module structure, their responsibilities, - **apiclient** — wraps `net/http`. Builds URLs from `Config.URL + Collection + path`, adding `api-version` only when `APIVersion` is set; without it the server answers at the version it chooses. Sets `Authorization: Basic base64(":")` on every request. Configures TLS using `CABundle` (appended to system root pool) and `InsecureSkipVerify`. Hooks the logger via a `RoundTripper` — that transport is the single call site of `LogRequest`, so every outgoing request is logged exactly once and no other module logs HTTP traffic. Classifies HTTP outcomes into the stable error categories defined in the brief (`auth`, `not_found`, `forbidden`, `server`, `config`, `network`). - **workitem** — domain logic for Work Items: Get Work Item, Work Items - List, and Get Work Items Batch. Owns the `WorkItem`/`Field` types, the `BatchRequest`/`Batch` types for reading several work items, and the allowlist of fields known to contain HTML (`System.Description`, `Microsoft.VSTS.TCM.ReproSteps`, `Microsoft.VSTS.TCM.SystemInfo`, `Microsoft.VSTS.Common.AcceptanceCriteria`, …). After unmarshalling, tags each field with its `FieldKind`. For several work items, keeps the server's order and reports the requested ids the server did not return. Returns raw values — does not perform markdown conversion. - **wiql** — domain logic for Wiql: Query By Wiql. Owns the `Request` and `Result` types. Builds the path at collection level, under a project, or under a project and a team; passes the query text through unchanged and sends `$top` and `timePrecision` only when they are given. Reads the query type, `asOf`, the reference names of the columns, and either the work item ids of a flat query or the relations of a link query, keeping the server's order. Does not read the work items themselves: that is a separate call through `workitem`, made by the caller. -- **htmlmd** — thin wrapper over `github.com/JohannesKaufmann/html-to-markdown/v2`. One method: `Convert(html string) (string, error)`. v1 uses library defaults only; the wrapper is the extension point where TFS-specific rules will be registered once real HTML samples have been collected (see `2026-05-20-tasks-html-quirks.md`). +- **htmlmd** — thin wrapper over `github.com/JohannesKaufmann/html-to-markdown/v2`. One method: `Convert(html string) (string, error)`. v1 uses library defaults only; the wrapper is the extension point where TFS-specific rules will be registered once real HTML samples have been collected (see the `html-quirks` entry in `docs/backlog.md`). - **tfserr** — typed error `Error{Category, Message, HTTPStatus, Cause}` with the stable category set from the brief. Provides `Print(err, w)` writing `Error [category]: message (HTTP status)` and `ExitCode(err)` mapping to a non-zero process exit code (v1: 1 for every category; the API leaves room for per-category exit codes later without breaking the contract). - **log** — `Logger` interface with two implementations: `noop` (default) and `stderr` (selected by `--verbose` / `TFSCLI_VERBOSE=1`). Methods: `LogRequest(method, url, status, dur)` and `Warn(msg)`. The logger interface accepts only these four request fields — PAT and the `Authorization` header are never passed in, so they cannot leak through the logger by construction. @@ -318,7 +320,7 @@ type Result struct { - **New API domain** (repos, builds): add a new domain module and register its resource command under the area command, adding the area command (e.g. `git`) if it does not exist yet. No edits to `apiclient`, `config`, or `tfserr`. - **`--json` output**: introduce a `Renderer` interface in the cli layer; add a `JSONRenderer` that consumes `*WorkItem` directly. The markdown printer becomes the other implementation. Local refactor confined to the cli layer. -- **TFS-specific HTML quirks** (mentions, attachment links, Word paste leftovers): add rules in `htmlmd` plus golden tests on real samples. Tracked in `2026-05-20-tasks-html-quirks.md`. +- **TFS-specific HTML quirks** (mentions, attachment links, Word paste leftovers): add rules in `htmlmd` plus golden tests on real samples. Recorded as the `html-quirks` entry in `docs/backlog.md`. --- @@ -336,7 +338,8 @@ Standard Go CLI layout: a single binary entry point under `cmd/`, all implementa |---|---| | `cmd/` | Binary entry points, one subdirectory per binary. v1 has a single binary, `tfscli`; its `main` only wires modules together and delegates to the cli package. | | `internal/` | All implementation packages, one per architecture module. Tests sit next to the code (`_test.go`); fixtures live in per-package `testdata/` directories. | -| `docs/` | Design documents (`project-brief.md`, `architecture.md`), tasks files, tech-debt register. Not part of the shipped binary. | +| `docs/` | Design documents (`project-brief.md`, `architecture.md`), tasks files, backlog, tech-debt register. Not part of the shipped binary. | | `skills/` | The agent skill shipped beside the binary, `skills/tfscli/SKILL.md`, in the Agent Skills format. Copied into the archive by goreleaser and installed by copying the directory into an agent's skills path. | +| `.claude/` | Claude Code project settings and the process skills in `.claude/skills/` (tasks, backlog, changelog, tech-debt). Not shipped, and unrelated to `skills/`, which holds the skill for users of tfscli. | Deliberately absent: `pkg/` (nothing is exported), `vendor/` (dependencies resolve through the module proxy), a separate `test/` tree (Go convention keeps tests beside the code they test). diff --git a/docs/backlog.md b/docs/backlog.md new file mode 100644 index 0000000..b1c4c30 --- /dev/null +++ b/docs/backlog.md @@ -0,0 +1,61 @@ +# Backlog + +Accepted work that has not started yet, in priority order. + +### `git-pull-requests` +**Goal:** An agent can read pull requests, their comment threads, and the work items +linked to them. +**Context:** The API domains are ordered by how much each lets an agent do without being +handed an id by a person. An agent sees the code through the local `git`, but pull +requests, review comments, and linked work items exist only on the server; the "address +the review comments" scenario is impossible without them. The domain is of no use on +TFVC projects. Pull request threads appeared only in TFS 2017; under "Server +compatibility is preserved" in `docs/project-brief.md` they should be optional, failing +with a clear error on an older server while everything else keeps working. +**Added:** 2026-10-04, in conversation on the next API domains + +### `workitem-comments` +**Goal:** An agent can read the discussion of a work item. +**Context:** The API domains are ordered by how much each lets an agent do without being +handed an id by a person. This extends the existing work item domain rather than adding +a new one. The Comments API exists only on recent servers; on older ones the discussion +is kept in the revisions of the `System.History` field. +**Added:** 2026-10-04, in conversation on the next API domains + +### `build-status-and-logs` +**Goal:** An agent can read builds, their timeline, and their logs. +**Context:** The API domains are ordered by how much each lets an agent do without being +handed an id by a person. Scenario: a CI build failed and the agent is to find out why, +locating the failed step through the timeline and reading its log. Less frequent than +the domains before it, since many on-prem teams run CI outside TFS Build. The volume of +logs needs a decision: the whole log or only the failed step. +**Added:** 2026-10-04, in conversation on the next API domains + +### `html-quirks` +**Goal:** Rich-text fields such as Description and Repro Steps are printed without the +TFS-specific markup noise they are stored with. +**Context:** The scope is the `htmlmd` package and golden tests around it; no new API +contracts. Decisions already made: +- The rules are derived from real data: 10–20 raw HTML samples of + `System.Description`, `Microsoft.VSTS.TCM.ReproSteps`, + `Microsoft.VSTS.TCM.SystemInfo`, and `Microsoft.VSTS.Common.AcceptanceCriteria`, + covering @-mentions, attachments, Word- and Outlook-pasted content, and links to + other work items, stored as fixtures in `internal/htmlmd/testdata/` with a + `README.md` giving the provenance of each. +- Golden tests first record the current output of `htmlmd.Convert` on the samples, + with the usual `-update` flag, so that each rule added afterwards shows as a golden + diff. +- The expected noise classes: mentions rendered as `[@name](#)`, attachment links to + authenticated URLs, Word-paste leftovers (``, `MsoNormal`, `mso-*` attributes), + and work item references rendered as long URLs. Known before the samples arrive: the + library's default rule set has no table support, so `
` collapses to its + concatenated cell text; enabling `plugin/table` belongs to this work. +- Out of scope: anything that needs additional TFS API calls, such as fetching + attachment file names; that is a separate feature. +- The limitation is stated in "Known limitations" in both `README.md` and + `skills/tfscli/SKILL.md`, which must stop claiming it together. +**Blocked until:** A decision to collect rich-text samples from a live TFS instance. A +live connection is no longer the blocker: release 0.0.5 ran `auth login` and reading one +work item against a live server. +**Added:** 2026-05-20, in the conversation that produced the Architecture section of +`docs/architecture.md` diff --git a/docs/project-brief.md b/docs/project-brief.md index 4a58cff..2286228 100644 --- a/docs/project-brief.md +++ b/docs/project-brief.md @@ -1,5 +1,7 @@ # Project Brief — tfscli +This document records the purpose, audience, scope, constraints, non-goals, and design principles of tfscli. It changes only when one of these decisions changes. The behaviour of individual features is documented in `README.md`, not here. + ## Problem AI coding agents (Claude Code, opencode, Cursor) have no lightweight way to access on-premises TFS / Azure DevOps Server data. Existing solutions are MCP servers that require Node.js runtime, long startup times, a persistent process, and explicit credentials (PAT or login/password). They don't fit the stateless CLI model that agents already use for OS interaction (`git`, `grep`, `find`). diff --git a/docs/tech-debt.md b/docs/tech-debt.md index ac68da3..66b4ea4 100644 --- a/docs/tech-debt.md +++ b/docs/tech-debt.md @@ -1,16 +1,16 @@ -## tech-debt.md +# Tech debt -### Active +## Active -#### TD-07 `main-not-protected` +### TD-07 `main-not-protected` **Problem:** `main` has no server-side protection. The rule that changes reach it only through pull requests with a green `check` is a convention that GitHub does not enforce. **Why deferred:** The repository is private on the free plan, where GitHub does not enforce rulesets. Protection becomes possible only when the repository is made public or the account moves to GitHub Pro, and neither has been decided. -**Trigger:** The repository becomes public, or the account moves to GitHub Pro. The -work found necessary at that point: +**Trigger:** The repository becomes public, or the account moves to GitHub Pro. +**Fix notes:** - A branch ruleset on the default branch: pull request required with 0 approvals, the `check` status check from GitHub Actions required with branches up to date, linear history, deletions and force pushes blocked; only rebase merging allowed @@ -31,28 +31,46 @@ work found necessary at that point: **Added:** 2026-10-05, in conversation; replaces TASK-01 of `docs/archive/2026-10-04-tasks-release-bypass.md` +### TD-08 `brief-describes-feature-behaviour` +**Problem:** The preamble of `docs/project-brief.md` states that it records the purpose, +scope, constraints, non-goals, and design principles of tfscli, and that feature +behaviour is documented in `README.md`. Several of its sections predate that statement +and describe current behaviour instead: the login sequence under "Authentication", +the details under "Configuration", the list of commands under "Command Format", the +sample messages under "Error Format", and "Key Dependencies". The same content is kept in +`README.md`, `CLAUDE.md`, `skills/tfscli/SKILL.md`, and `docs/architecture.md`, so +every feature change also has to update the brief, which duplicates content kept +elsewhere. +**Why deferred:** Moving the content is a separate review of each section — what is a +principle and stays, what is behaviour and is already covered elsewhere — and is not +needed to state the purpose of the document. +**Trigger:** the next change that would otherwise add feature behaviour to the brief. +**Fix notes:** The normative parts stay as principles: the credential model, the +configuration precedence, and the error format and its stability. +**Added:** 2026-10-05, in conversation on the purpose of the project documents + --- -### Closed +## Closed -#### TD-01 `login-anonymous-detection` -**Fixed:** 2026-10-05 — dropped without a change: no server with anonymous -access is available to verify against; recorded as a known limitation in -`README.md` until a user reports such a server +### TD-01 `login-anonymous-detection` +**Closed:** 2026-10-05 — dropped: no server with anonymous access is available to +verify against; recorded as a known limitation in `README.md` until a user reports +such a server -#### TD-02 `tls-1.2-minimum` -**Fixed:** 2026-10-05 — not a deferred problem but a decision; recorded under -"Defaults are safe" in `docs/architecture.md` +### TD-02 `tls-1.2-minimum` +**Closed:** 2026-10-05 — dropped: not a deferred problem but a decision; recorded +under "Defaults are safe" in `docs/architecture.md` -#### TD-03 `request-duration-test` -**Fixed:** 2026-10-05 +### TD-03 `request-duration-test` +**Closed:** 2026-10-05 — fixed in c70658c -#### TD-04 `url-userinfo-in-output` -**Fixed:** 2026-10-05 +### TD-04 `url-userinfo-in-output` +**Closed:** 2026-10-05 — fixed in d46745f -#### TD-05 `collection-named-like-virtual-directory` -**Fixed:** 2026-10-05 — dropped without a change: the naming is not known to -occur, and a report of it would surface as a `not_found` at login +### TD-05 `collection-named-like-virtual-directory` +**Closed:** 2026-10-05 — dropped: the naming is not known to occur, and a +report of it would surface as a `not_found` at login -#### TD-06 `path-segments-unescaped` -**Fixed:** 2026-10-05 +### TD-06 `path-segments-unescaped` +**Closed:** 2026-10-05 — fixed in c70658c diff --git a/internal/htmlmd/doc.go b/internal/htmlmd/doc.go index 2a2128e..586fba7 100644 --- a/internal/htmlmd/doc.go +++ b/internal/htmlmd/doc.go @@ -1,4 +1,4 @@ // Package htmlmd is a thin wrapper over html-to-markdown/v2 exposing // Convert(html) (string, error). It is the extension point for -// TFS-specific conversion rules (see docs/2026-05-20-tasks-html-quirks.md). +// TFS-specific conversion rules (see the html-quirks entry in docs/backlog.md). package htmlmd diff --git a/internal/htmlmd/htmlmd_test.go b/internal/htmlmd/htmlmd_test.go index 596bd55..1ac4df3 100644 --- a/internal/htmlmd/htmlmd_test.go +++ b/internal/htmlmd/htmlmd_test.go @@ -5,7 +5,7 @@ import "testing" // The expectations below record the library defaults, which is all v1 uses. // They are a smoke check that the wrapper is wired to the converter — not an // endorsement of any particular rendering. TFS-specific rules and golden tests -// on real samples arrive with docs/2026-05-20-tasks-html-quirks.md. +// on real samples arrive with the html-quirks entry in docs/backlog.md. func TestConvert(t *testing.T) { tests := []struct { name string