diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 293cc891..4f59331d 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -28,6 +28,10 @@ { "name": "unic-spec-review", "source": "./apps/claude-code/unic-spec-review" + }, + { + "name": "unic-ticket-specification", + "source": "./apps/claude-code/unic-ticket-specification" } ] } diff --git a/AGENTS.md b/AGENTS.md index c887a735..025ff791 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,7 +16,8 @@ apps/ │ ├── unic-confluence/ │ ├── unic-archon-dlc/ │ ├── unic-pr-review/ -│ └── unic-spec-review/ +│ ├── unic-spec-review/ +│ └── unic-ticket-specification/ └── copilot/ # GitHub Copilot plugins (future) packages/ ├── biome-config/ # @unic/biome-config diff --git a/CONTEXT-MAP.md b/CONTEXT-MAP.md index 605a2cbd..750cc87f 100644 --- a/CONTEXT-MAP.md +++ b/CONTEXT-MAP.md @@ -14,6 +14,7 @@ Each context maps to an `app:` area label on the issue tracker (see [Area labels - [unic-archon-dlc](./apps/claude-code/unic-archon-dlc/CONTEXT.md): Archon-powered AI development lifecycle DLC (`app:unic-archon-dlc`) - [unic-pr-review](./apps/claude-code/unic-pr-review/CONTEXT.md): AI-powered PR review with intent checking and Confidence-scored Findings (`app:unic-pr-review`) - [unic-spec-review](./apps/claude-code/unic-spec-review/CONTEXT.md): adversarial review of web specifications across Confluence, Figma, and the live system (`app:unic-spec-review`) +- [unic-ticket-specification](./apps/claude-code/unic-ticket-specification/CONTEXT.md): portable Archon workflow that grooms a tracker ticket (Jira/Azure DevOps/GitHub) to ready-for-implementation (`app:unic-ticket-specification`) ## Workspace packages @@ -36,7 +37,8 @@ This tier is a repo convention: hand-applied and maintained here, not generated ## Relationships - All Plugin contexts share the vocabulary defined in the monorepo context -- **auto-format**, **pr-review**, **unic-archon-dlc**, **unic-pr-review**, and **unic-spec-review** are Claude Code Plugins with no runtime dependencies on each other (`unic-spec-review` vendors shared code from `unic-pr-review` rather than importing it) +- **auto-format**, **pr-review**, **unic-archon-dlc**, **unic-pr-review**, **unic-spec-review**, and **unic-ticket-specification** are Claude Code Plugins with no runtime dependencies on each other (`unic-spec-review` vendors shared code from `unic-pr-review` rather than importing it) +- **unic-ticket-specification** ships a portable Archon workflow bundle (like **unic-archon-dlc**) rather than runtime code; it requires the Archon workflow engine in the target project and a tracker (Jira/Azure DevOps/GitHub) reachable via MCP or CLI - **unic-confluence** can be installed as a git dependency for use outside Claude Code. - **pr-review** has a soft dependency on the `pr-review-toolkit` plugin from `anthropics/claude-plugins-official` - **unic-archon-dlc** requires the Archon workflow engine (version ≥ 0.10) in the target project; it has no runtime dependencies on any other plugin in this repo diff --git a/apps/claude-code/unic-ticket-specification/.archon/commands/uts-analyze.md b/apps/claude-code/unic-ticket-specification/.archon/commands/uts-analyze.md new file mode 100644 index 00000000..302830c6 --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/.archon/commands/uts-analyze.md @@ -0,0 +1,156 @@ +--- +description: Fetch (if existing) and analyze a ticket against the configured code repos and linked docs; surface gaps, ambiguities, and open questions. Tracker-agnostic (Jira / Azure DevOps / GitHub). +argument-hint: +--- + +# Ticket fetch + analysis + +You are preparing a ticket for implementation readiness. This step gathers all +context. Do **not** write anything back to the tracker. + +## Inputs + +- Original user input: `$ARGUMENTS` +- Detected input (JSON: `mode` = existing|create, `key`, `project`): `$detect-input.output` +- **Project config: read `.archon/ticket-spec.config.yaml` FIRST** — it defines + the tracker type, access method, repos, docs source, and tenant/org/project. + +Everything tracker- and project-specific comes from that config. Do not assume a +particular tracker, tenant, or repo layout. + +## Tooling: MCP-first, CLI fallback (OS-independent) + +- If `tracker.access.mcp` is `true`, a tracker MCP server is loaded for this + node. Prefer its tools. The server may need a moment to start (npx cold + start) — if its tools are not visible on your first attempt, wait briefly and + retry before giving up. +- Otherwise (or if MCP tools never appear), fall back to the CLI named in + `tracker.access.cli` via Bash. Use only portable, non-interactive commands + that work the same on Windows and macOS (no shell-specific syntax, no + OS-specific paths). Examples per tracker: + - jira → Atlassian MCP, else `jira issue view --plain` + - az → `az boards work-item show --id --output json` (Azure DevOps) + - gh → `gh issue view --repo --json ...` (GitHub) + +## Jira cloudId resolution (jira tracker only) + +If `tracker.type` is `jira` and `tracker.jira.cloud_id` is **empty**, resolve it +ONCE before any Jira/Confluence call: invoke `getAccessibleAtlassianResources` +and pick the resource whose `url` matches `tracker.jira.site_url`; use that `id` +as the cloudId everywhere below. (If only one resource is returned, use it.) If a +non-empty `cloud_id` is configured, use it directly. The same resolved cloudId +applies to Confluence when `docs.type` is `confluence`. + +## Step 0 — Fetch the ticket (existing path only) + +If `$detect-input.output.mode` is `existing`, fetch the ticket FIRST, then write +it to `$ARTIFACTS_DIR/ticket.md`. Use the method for `tracker.type`: + +- **jira**: `getJiraIssue` (cloudId from `tracker.jira.cloud_id`, issue key from + `$detect-input.output.key`) for summary, description, issue type, status, + comments; `getJiraIssueRemoteIssueLinks` to discover linked pages. +- **azure-devops**: fetch the work item by id (`tracker.azure_devops.org_url` + + `project`) for title, description/repro steps, work-item type, state, + comments, and linked artifacts. +- **github**: fetch the issue from `tracker.github.issues_repo` by number for + title, body, labels, state, and comments. + +Write the fetched content (summary/title, type, status, full description, +relevant comments, links) to `$ARTIFACTS_DIR/ticket.md`. Record the exact +**issue type name** at the top of that file (e.g. `Issue type: Bug`). + +If `mode` is `create`, there is no ticket to fetch — skip this step. + +## Step 0b — Resolve the target + +Decide where the ticket belongs and write `$ARTIFACTS_DIR/target.json`: + +``` +{ + "mode": "create" | "existing", + "tracker": "jira" | "azure-devops" | "github", + "project_key": "", + "project_name": "", + "key": "", + "reason": "" +} +``` + +- `tracker` = `tracker.type` from config. +- **Existing**: `key` = `$detect-input.output.key`. `project_key` = + - jira: the key prefix of that reference; + - azure-devops: `tracker.azure_devops.project`; + - github: `tracker.github.issues_repo`. +- **Create**: + - jira: if `$detect-input.output.project` is non-empty use it; else if the + input names a project use that; else use `tracker.jira.default_project`. + (If the tracker exposes a project list and the right one is ambiguous, you + may call `getVisibleJiraProjects` to pick the best match; fall back to the + default and say so in `reason`.) + - azure-devops: `tracker.azure_devops.project`. + - github: `tracker.github.issues_repo`. + - `key` = "". + +This `target.json` is authoritative for the apply step and is shown to the human +at the approval gate, who can override it. + +## Step 1 — Analyze + +1. **Establish the subject.** + + - Existing path: use `$ARTIFACTS_DIR/ticket.md` as the source of truth. + - Create path: use `$ARGUMENTS` as the raw requirement. + +2. **Explore the code — ACROSS ALL configured repos.** Iterate over every entry + in `repos` from the config. For each, resolve its `path` (relative to the + working directory or absolute; treat paths as forward-slash, OS-neutral) and + use Read / Grep / Glob within it to locate the components, modules, services + or pages this ticket touches. Identify: + + - The concrete files / areas that would change (note **which repo** each is in). + - Existing patterns or prior art for the requested behaviour. + - Technical constraints, dependencies, and cross-repo integration points. + + If a configured repo path does not exist on this machine, note it as a gap and + continue with the repos that are present — never fail the analysis over a + missing checkout. + +3. **Pull linked documentation** according to `docs.type`: + + - `confluence`: fetch linked/related pages with `getConfluencePage` / + `searchConfluenceUsingCql` (cloudId from `docs.confluence.cloud_id`). + - `azure-wiki`: fetch the relevant wiki pages from the Azure DevOps project. + - `github-wiki`: read the repo wiki / docs. + - `none`: skip. + Summarise anything that affects scope or acceptance criteria. + +4. **Identify gaps.** Explicitly list missing information, + ambiguities/contradictions, assumptions you would have to make, and open + questions for the requester / PO. + +## Output + +Write a structured analysis to **`$ARTIFACTS_DIR/analysis.md`** with these sections: + +``` +# Analysis: + +## Subject & intent +## Affected code areas +- : + +## Linked documentation +## Technical constraints & dependencies +## Open questions +- [ ] + +## Assumptions +- +``` + +Cite `repo:file_path:line` references where you found relevant code. This +analysis (plus `ticket.md`) is the foundation for classification, the rewritten +description, and the estimate — capture everything that affects scope. + +After writing the file(s), print a one-paragraph summary of the key findings and +the number of open questions to your output. diff --git a/apps/claude-code/unic-ticket-specification/.archon/commands/uts-apply-create.md b/apps/claude-code/unic-ticket-specification/.archon/commands/uts-apply-create.md new file mode 100644 index 00000000..3a92ddf4 --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/.archon/commands/uts-apply-create.md @@ -0,0 +1,80 @@ +--- +description: Create a new ticket from the approved draft and record the PERT estimate. Tracker-agnostic (Jira / Azure DevOps / GitHub). +argument-hint: +--- + +# Apply — CREATE new ticket + +The draft and estimate have been **approved by a human**. Create the ticket now. + +## Inputs + +- Config: `.archon/ticket-spec.config.yaml` (tracker type, access, ids/types/labels) +- Original user input: `$ARGUMENTS` +- Target (JSON: `tracker`, `project_key`, `project_name`, `reason`): read `$ARTIFACTS_DIR/target.json` +- Approved description: read `$ARTIFACTS_DIR/draft-description.md` +- Approved estimate: read `$ARTIFACTS_DIR/estimate.md` +- Classification (JSON: `kind`, `issue_type_name`): `$classify.output` + +The draft, estimate, and `target.json` already reflect any reviewer feedback, so +apply exactly what is in those files. + +## Tooling + +MCP-first (the tracker MCP server is loaded for this node), CLI fallback via the +`tracker.access.cli` tool through Bash. Use only portable, non-interactive +commands (identical on Windows and macOS). + +## Steps — branch on `target.tracker` + +Derive a concise, specific **summary/title** from the analysis and input first. + +### jira + +0. If `tracker.jira.cloud_id` is empty, resolve it via + `getAccessibleAtlassianResources` (pick the resource whose `url` matches + `tracker.jira.site_url`); otherwise use the configured value. Use this cloudId + for all Jira calls below. +1. projectKey = `target.project_key`. +2. Resolve the issue-type id for the classified `issue_type_name`: + - If `projectKey` equals `tracker.jira.default_project` and the name is in + `tracker.jira.issue_type_ids`, use that id. + - Otherwise call `getJiraProjectIssueTypesMetadata` (cloudId + `tracker.jira.cloud_id`, projectKey) and pick the id whose name matches. + If that exact type does not exist, choose the closest available and note it. +3. `createJiraIssue` (cloudId, projectKey, issueTypeId, summary, description = + full contents of `draft-description.md`). +4. Record the estimate with `addCommentToJiraIssue` — post `estimate.md` prefixed + with `*PERT effort estimate (generated by unic-ticket-specification):*`. +5. If a time-tracking original estimate field is editable, optionally set it to + the weighted E (skip silently otherwise). + +### azure-devops + +1. Work-item type = `tracker.azure_devops.work_item_types.bug` when kind=BUG, + else `...work_item_types.cr_story`. +2. Create the work item in `tracker.azure_devops.org_url` / `project` with the + title and the description = full contents of `draft-description.md` + (e.g. `az boards work-item create --type "" --title "" ...`, + then set the description/repro field). +3. Record the estimate as a comment on the work item (post `estimate.md`, + same prefix as above). Optionally set the Effort/Original Estimate field to E. + +### github + +1. repo = `target.project_key` (the `issues_repo`). +2. labels = `tracker.github.labels.bug` when kind=BUG, else `...labels.cr_story`. +3. Create the issue (title + body = full contents of `draft-description.md`) + with those labels (e.g. `gh issue create --repo <repo> --title ... --body-file ... --label ...`). +4. Record the estimate as an issue comment (post `estimate.md`, same prefix). + +## After writing (all trackers) + +Update the persisted local file (path in `$ARTIFACTS_DIR/local-output-path.txt`): +change its `Status:` line to `WRITTEN TO TRACKER` and append a `## Result` section +with the created reference, its URL, issue type, and recorded PERT E value. + +## Output + +Print the created ticket reference, its URL, the chosen issue type, and the +recorded PERT E value. diff --git a/apps/claude-code/unic-ticket-specification/.archon/commands/uts-apply-update.md b/apps/claude-code/unic-ticket-specification/.archon/commands/uts-apply-update.md new file mode 100644 index 00000000..2489e5b1 --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/.archon/commands/uts-apply-update.md @@ -0,0 +1,65 @@ +--- +description: Update an existing ticket with the approved description and record the PERT estimate. Tracker-agnostic (Jira / Azure DevOps / GitHub). +argument-hint: <ticket reference> +--- + +# Apply — UPDATE existing ticket + +The draft and estimate have been **approved by a human**. Write the changes to +the existing ticket now. + +## Inputs + +- Config: `.archon/ticket-spec.config.yaml` (tracker type, access) +- Target (JSON: `tracker`, `project_key`, `key`): read `$ARTIFACTS_DIR/target.json` + (falls back to `$detect-input.output` for `key` if absent) +- Approved description: read `$ARTIFACTS_DIR/draft-description.md` +- Approved estimate: read `$ARTIFACTS_DIR/estimate.md` + +The draft and estimate already reflect any reviewer feedback, so write exactly +what is in those files. Do not change the title/summary or issue type. + +## Tooling + +MCP-first (the tracker MCP server is loaded for this node), CLI fallback via the +`tracker.access.cli` tool through Bash. Use only portable, non-interactive +commands (identical on Windows and macOS). + +## Steps — branch on `target.tracker` + +### jira + +0. If `tracker.jira.cloud_id` is empty, resolve it via + `getAccessibleAtlassianResources` (pick the resource whose `url` matches + `tracker.jira.site_url`); otherwise use the configured value. +1. `editJiraIssue` — cloudId (resolved above), issueIdOrKey = `key`, + set the description field to the full contents of `draft-description.md`. +2. Record the estimate with `addCommentToJiraIssue` — post `estimate.md` prefixed + with `*PERT effort estimate (generated by unic-ticket-specification):*`. +3. If the time-tracking original estimate is editable, optionally set it to E. + +### azure-devops + +1. Update the work item `key` in `tracker.azure_devops.org_url` / `project`, + setting the description/repro field to the full contents of + `draft-description.md` (e.g. `az boards work-item update --id <key> ...`). +2. Record the estimate as a comment on the work item (post `estimate.md`, same + prefix). Optionally set the Effort/Original Estimate field to E. + +### github + +1. repo = `target.project_key` (the `issues_repo`); issue number from `key`. +2. Update the issue body to the full contents of `draft-description.md` + (e.g. `gh issue edit <number> --repo <repo> --body-file ...`). +3. Record the estimate as an issue comment (post `estimate.md`, same prefix). + +## After writing (all trackers) + +Update the persisted local file (path in `$ARTIFACTS_DIR/local-output-path.txt`): +change its `Status:` line to `WRITTEN TO TRACKER` and append a `## Result` section +with the reference, its URL, and recorded PERT E value. + +## Output + +Print the updated ticket reference, its URL, confirmation that the description +was updated, and the recorded PERT E value. diff --git a/apps/claude-code/unic-ticket-specification/.archon/commands/uts-estimate.md b/apps/claude-code/unic-ticket-specification/.archon/commands/uts-estimate.md new file mode 100644 index 00000000..c26d08ed --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/.archon/commands/uts-estimate.md @@ -0,0 +1,60 @@ +--- +description: Produce a 3-point PERT effort estimate for the ticket, with caveats when requirements are incomplete. +argument-hint: <ticket reference or free-text ticket description> +--- + +# PERT effort estimate + +Produce a three-point **PERT** effort estimate for this ticket. Do NOT write +anything to the tracker — only produce the draft file. + +(The configured `estimation.method` in `.archon/ticket-spec.config.yaml` is +`pert`; this command implements PERT.) + +## Inputs + +- Analysis: read `$ARTIFACTS_DIR/analysis.md` +- Rewritten description: read `$ARTIFACTS_DIR/draft-description.md` +- Completeness assessment (JSON): `$assess-completeness.output` +- Completeness detail: read `$ARTIFACTS_DIR/completeness.md` if present. + +## What to do + +1. Scope the work from the affected code areas in the analysis (across all repos) + and the acceptance criteria in the draft description. +2. Estimate effort in **person-days** at three points: + - **O** = Optimistic (everything goes smoothly) + - **M** = Most likely (realistic, expected case) + - **P** = Pessimistic (significant complications) +3. Compute the weighted estimate **E = (O + 4M + P) / 6** and the standard + deviation **SD = (P − O) / 6**. Round E and SD to one decimal place. +4. Write a short rationale referencing the concrete code areas and risks that + drive each number. +5. **Caveats are required when completeness is not high.** If the completeness + assessment is `low` or `medium`, list the open questions/assumptions the + estimate depends on and state clearly that it may change once they are + resolved. The estimate is produced regardless — never refuse to estimate + because requirements are incomplete. + +## Output + +Write the estimate to **`$ARTIFACTS_DIR/estimate.md`** in this shape: + +``` +# PERT estimate + +| Point | Person-days | +|-------|-------------| +| Optimistic (O) | <n> | +| Most likely (M) | <n> | +| Pessimistic (P) | <n> | +| **Weighted (E = (O+4M+P)/6)** | **<n>** | +| Std deviation (SD = (P−O)/6) | <n> | + +## Rationale + +## Caveats & assumptions +- <caveat — only if completeness < high; otherwise "None — requirements assessed complete."> +``` + +Then print the single line: `PERT E = <n> person-days (O=<o>, M=<m>, P=<p>)`. diff --git a/apps/claude-code/unic-ticket-specification/.archon/commands/uts-persist-local.md b/apps/claude-code/unic-ticket-specification/.archon/commands/uts-persist-local.md new file mode 100644 index 00000000..460bf657 --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/.archon/commands/uts-persist-local.md @@ -0,0 +1,63 @@ +--- +description: Persist the proposed ticket content (description + PERT estimate + open questions) to a stable local .md file before the approval gate. +argument-hint: <ticket reference or free-text ticket description> +--- + +# Persist ticket content locally + +Write the full proposed ticket content to a **stable local file** so the +workflow's result is never lost — even if the approval gate is rejected or a +later step fails. This runs BEFORE the gate. Do not write anything to the tracker +here. + +## Inputs (read these) + +- Config: `.archon/ticket-spec.config.yaml` (for `output.dir`) +- Target (JSON: `mode`, `tracker`, `project_key`, `project_name`, `key`): `$ARTIFACTS_DIR/target.json` +- Classification (JSON: `kind`, `issue_type_name`): `$classify.output` +- Proposed description: `$ARTIFACTS_DIR/draft-description.md` +- PERT estimate: `$ARTIFACTS_DIR/estimate.md` +- Completeness + open questions: `$ARTIFACTS_DIR/completeness.md` + +## Where to write + +Output directory = `output.dir` from the config (default `ticket-spec-output`), +resolved **relative to the working directory** (the project root). Use +forward-slash paths so this is identical on Windows and macOS; the Write tool +creates parent folders. Do NOT hardcode an absolute or OS-specific path. + +Filename: + +- Existing ticket (`mode == existing`): a filesystem-safe form of the reference, + e.g. `<KEY>.md` (`ACME-1234.md`) or `issue-<number>.md` for numeric refs. +- New ticket (`mode == create`): `NEW-<short-kebab-slug-of-summary>-$WORKFLOW_ID.md`. + +After writing, also write the **path of the file you created** (the same +forward-slash relative path) to `$ARTIFACTS_DIR/local-output-path.txt` (a single +line, no trailing text) so downstream nodes update the same file. + +## File contents + +``` +# Ticket specification — <summary / title> + +- Run: $WORKFLOW_ID +- Status: DRAFT — pending approval (not yet written to the tracker) +- Tracker: <tracker> +- Action: <create a new {issue_type_name} in {project_key} ({project_name}) | update {key}> +- Issue type: <issue_type_name> (kind <kind>) + +## Proposed description + +<full verbatim contents of draft-description.md> + +## PERT estimate + +<full verbatim contents of estimate.md> + +## Completeness & open questions + +<full verbatim contents of completeness.md> +``` + +After writing both files, print the path of the persisted .md to your output. diff --git a/apps/claude-code/unic-ticket-specification/.archon/commands/uts-rewrite-bug.md b/apps/claude-code/unic-ticket-specification/.archon/commands/uts-rewrite-bug.md new file mode 100644 index 00000000..4aa820ac --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/.archon/commands/uts-rewrite-bug.md @@ -0,0 +1,32 @@ +--- +description: Rewrite a Bug ticket description using the project's configured Bug template. +argument-hint: <ticket reference or free-text ticket description> +--- + +# Rewrite description — BUG template + +Produce a clean, implementation-ready **Bug** description for this ticket. Do NOT +write anything to the tracker — only produce the draft file. + +## Inputs + +- Original user input: `$ARGUMENTS` +- **Template: read `templates.bug` from `.archon/ticket-spec.config.yaml`.** This + is the exact structure to fill in — use it verbatim, do not add or rename + sections, and keep its heading levels. +- Analysis (gaps, affected code, open questions): read `$ARTIFACTS_DIR/analysis.md` +- Existing ticket (existing path only): read `$ARTIFACTS_DIR/ticket.md` if present. + +## Rules + +- Preserve any existing useful content from `ticket.md` — do not discard known facts. +- Fill each template section from the analysis. Where information is genuinely + missing, write `_Open question:_ <what is unknown>` inside the relevant section + rather than inventing details. +- Keep the exact heading structure and levels from the configured template. + +## Output + +Write the completed description to **`$ARTIFACTS_DIR/draft-description.md`** (the +ticket body only — no surrounding commentary). Then print a short note listing +which sections still contain open questions. diff --git a/apps/claude-code/unic-ticket-specification/.archon/commands/uts-rewrite-crstory.md b/apps/claude-code/unic-ticket-specification/.archon/commands/uts-rewrite-crstory.md new file mode 100644 index 00000000..46e2978f --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/.archon/commands/uts-rewrite-crstory.md @@ -0,0 +1,68 @@ +--- +description: Rewrite a Change Request / Story ticket description using the project's configured CR/Story template. +argument-hint: <ticket reference or free-text ticket description> +--- + +# Rewrite description — CHANGE REQUEST / STORY template + +Produce a clean, implementation-ready **Change Request / Story** description for +this ticket. Do NOT write anything to the tracker — only produce the draft file. + +## Inputs + +- Original user input: `$ARGUMENTS` +- **Template: read `templates.cr_story` from `.archon/ticket-spec.config.yaml`.** + This is the exact structure to fill in — use it verbatim, do not add or rename + sections, and keep its heading levels. +- Analysis (gaps, affected code, open questions): read `$ARTIFACTS_DIR/analysis.md` +- Existing ticket (existing path only): read `$ARTIFACTS_DIR/ticket.md` if present. + +## Rules + +- Preserve any existing useful content from `ticket.md` — do not discard known facts. +- Fill each template section from the analysis. Where information is genuinely + missing, write `_Open question:_ <what is unknown>` inside the relevant section + rather than inventing details. +- If the template contains a user-story line (`As a <role> I can <capability>, so +that <benefit>`), replace the placeholders with concrete values from the + analysis. +- Derive testable `Acceptance Criteria` from the analysis and affected code areas. +- Keep the exact heading structure and levels from the configured template. + +## ToDo vs. Suggested Technical Tasks — TWO DISTINCT AUDIENCES + +If the template has both a `ToDo` and a `Suggested Technical Tasks` section, they +serve different readers and MUST NOT be the same list at different indentation. + +**`ToDo` — the scope list (for POs / non-developers).** + +- Plain-language summary of _what needs to be done_ and _which parts of the + solution are touched_, so a reader familiar with the project (but not the code) + can gauge the ticket's scope and size. +- Group by area / component (e.g. **Middleware**, **StoreFront**, + **Verification**) with a short checkbox item per piece of work. +- **No file paths, no line numbers, no class/method names, no code identifiers.** + Describe the change in functional terms ("Map the external customer ID onto + the outgoing order messages"), not how to implement it. +- A handful of items per area — enough to understand scope, not a task tracker. + +**`Suggested Technical Tasks` — the implementation guide (for developers).** + +- The detailed, concrete engineering steps: the specific files, line references, + classes/methods, and the exact change in each. +- Group by repository / component (name the repo, e.g. + `web-frontend` (TS), `cms-backend` (C#)). +- Cite `repo:file_path:line` from the analysis. Include test additions and any + explicit "verify, no code change expected" checks. +- This is the level of detail the workflow produced previously — keep it here, do + NOT water it down. It simply moves out of `ToDo`. + +Every technical task should roll up to one of the higher-level `ToDo` items, so +the two sections stay consistent. Where implementation detail is genuinely +unknown, use `_Open question:_ …` rather than inventing file paths. + +## Output + +Write the completed description to **`$ARTIFACTS_DIR/draft-description.md`** (the +ticket body only — no surrounding commentary). Then print a short note listing +which sections still contain open questions. diff --git a/apps/claude-code/unic-ticket-specification/.archon/mcp/ticket-spec-tracker.json b/apps/claude-code/unic-ticket-specification/.archon/mcp/ticket-spec-tracker.json new file mode 100644 index 00000000..0c4b9a3c --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/.archon/mcp/ticket-spec-tracker.json @@ -0,0 +1,6 @@ +{ + "atlassian": { + "command": "npx", + "args": ["-y", "mcp-remote", "https://mcp.atlassian.com/v1/mcp/authv2"] + } +} diff --git a/apps/claude-code/unic-ticket-specification/.archon/ticket-spec.config.example.yaml b/apps/claude-code/unic-ticket-specification/.archon/ticket-spec.config.example.yaml new file mode 100644 index 00000000..fba252ee --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/.archon/ticket-spec.config.example.yaml @@ -0,0 +1,136 @@ +# ============================================================================= +# unic-ticket-specification — per-project configuration (TEMPLATE) +# +# Copy this file to `.archon/ticket-spec.config.yaml` in each project and fill +# it in. The workflow and its commands read THIS file at runtime — there is no +# tracker/OS/repo detail hardcoded in the workflow itself. +# +# OS note: all paths are either relative to the workflow working directory +# (the project root) or absolute. Use forward slashes "/" even on Windows. +# ============================================================================= + +project: + name: "<human-readable project name>" + # Issue-key prefixes that mark an EXISTING ticket. + # Jira -> the project keys, e.g. ["ACME", "ACMEWEB"] + # Azure DevOps -> usually leave empty (work items are bare numbers) + # GitHub -> usually leave empty (issues are #123) + key_prefixes: [] + +# ----------------------------------------------------------------------------- +# Issue tracker. Exactly one `type`. Access is MCP-first with a CLI fallback. +# ----------------------------------------------------------------------------- +tracker: + type: "jira" # jira | azure-devops | github + + access: + # An MCP server is preferred. If `mcp: true`, the workflow loads the MCP + # config at the FIXED path `.archon/mcp/ticket-spec-tracker.json`. + # Put the correct server for THIS project's tracker there. + mcp: true + # CLI used when no MCP is available (must be installed + authenticated on + # each machine). "jira" | "az" | "gh" | "" (none). + cli: "" + + # --- Jira ------------------------------------------------------------------- + jira: + # Leave cloud_id EMPTY to auto-resolve it from site_url at runtime (via + # getAccessibleAtlassianResources) — handy for client tenants whose GUID you + # don't have. Set it explicitly only to skip the lookup. + cloud_id: "" + site_url: "https://<your-or-client-tenant>.atlassian.net" + default_project: "<KEY>" # used when creating and no project named + # Optional explicit issue-type id map for the DEFAULT project (speeds apply). + # Other projects resolve their ids by name at apply time. + issue_type_ids: {} # e.g. { "Bug": "10004", "Story": "10001" } + + # --- Azure DevOps ----------------------------------------------------------- + azure_devops: + org_url: "https://dev.azure.com/<org>" + project: "<project>" + work_item_types: + bug: "Bug" + cr_story: "User Story" + + # --- GitHub ----------------------------------------------------------------- + github: + issues_repo: "<owner>/<repo>" # repo that hosts the ISSUES + labels: + bug: ["bug"] + cr_story: ["enhancement"] + +# ----------------------------------------------------------------------------- +# Classification: map tracker issue-type names to a template kind. +# Anything not listed defaults to CR_STORY. +# ----------------------------------------------------------------------------- +classification: + bug_types: ["Bug", "Defect"] + cr_story_types: ["Story", "User Story", "Change request", "Feature", "Task"] + +# ----------------------------------------------------------------------------- +# Linked documentation source pulled for extra context during analysis. +# ----------------------------------------------------------------------------- +docs: + type: "none" # confluence | azure-wiki | github-wiki | none + confluence: + cloud_id: "<atlassian cloudId>" + +# ----------------------------------------------------------------------------- +# Code repositories to analyze. One OR many. Paths relative to the working +# directory or absolute (forward slashes). The analysis greps across ALL repos. +# ----------------------------------------------------------------------------- +repos: + - name: "main" + path: "." + +estimation: + method: "pert" # pert (the implemented method) + +output: + dir: "ticket-spec-output" # where the local proposal copy is written + +# ----------------------------------------------------------------------------- +# Description templates. Defaults below match Unic's standard shapes; override +# per client. Keep heading levels intact so the tracker renders them correctly. +# ----------------------------------------------------------------------------- +templates: + bug: | + ## 1. System / Environment information + **Environment:** + **Operating System:** + **Browser version:** + **Device & Version:** + + ## 2. Steps to reproduce + + ## 3. Actual Result + + ## 4. Expected Result + + ## 5. Additional information & attachments + cr_story: | + ### Business requirements / description + + As a <role> I can <capability>, so that <receive benefit> + + ### ToDo + + - [ ] + + ### Suggested Technical Tasks + + - [ ] + + ### Acceptance Criteria + + ### Documentation + + * Needs documentation: no + + ### QA + + * Can be tested by QA: yes + + ### Deployment + + * Needs manual deployment steps: no diff --git a/apps/claude-code/unic-ticket-specification/.archon/unic-ticket-specification.README.md b/apps/claude-code/unic-ticket-specification/.archon/unic-ticket-specification.README.md new file mode 100644 index 00000000..8b6a5e67 --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/.archon/unic-ticket-specification.README.md @@ -0,0 +1,79 @@ +# unic-ticket-specification — portable Archon workflow bundle + +A generic version of `ticket-readiness`, usable by **any Unic project** +regardless of issue tracker (**Jira / Azure DevOps / GitHub**), source-control +host, number of code repos, or operating system (**Windows / macOS**). + +It takes a ticket from intake to "ready for implementation": detect input → +fetch + analyse (across all configured repos + linked docs) → classify Bug vs +Change-Request/Story → rewrite to the configured template → non-blocking +completeness check → **PERT** estimate → persist locally → present draft → +**human approval gate** → write back to the tracker → report. + +## How it stays generic + +- **No tracker/tenant/repo/OS detail is hardcoded in the workflow.** It all lives + in a per-project config: `.archon/ticket-spec.config.yaml`. +- **MCP-first, CLI fallback.** Tracker nodes load an MCP server from the fixed + path `.archon/mcp/ticket-spec-tracker.json`. If no MCP is configured, commands + fall back to the tracker CLI named in the config (`jira` / `az` / `gh`). +- **Multi-repo.** `repos:` lists one or many checkouts; analysis greps across all. +- **OS-independent.** Paths are relative + forward-slash; no shell-specific or + OS-specific commands. Works the same on Windows and macOS. + +## Files in this bundle + +Copy these into a target project's `.archon/` to install: + +``` +.archon/ +├─ workflows/ +│ └─ unic-ticket-specification.yaml # the DAG +├─ commands/ +│ ├─ uts-analyze.md # fetch + analyse (3 trackers, N repos, docs) +│ ├─ uts-rewrite-bug.md # Bug template fill +│ ├─ uts-rewrite-crstory.md # CR/Story template fill +│ ├─ uts-estimate.md # PERT estimate +│ ├─ uts-persist-local.md # stable local copy of the proposal +│ ├─ uts-apply-create.md # create (post-approval, 3 trackers) +│ └─ uts-apply-update.md # update (post-approval, 3 trackers) +├─ mcp/ +│ └─ ticket-spec-tracker.json # tracker MCP server (per project) +├─ ticket-spec.config.example.yaml # documented config template (copy → ticket-spec.config.yaml) +└─ unic-ticket-specification.README.md # this file +``` + +> `ticket-spec.config.yaml` (the ACTIVE per-project config) is **not** part of the +> bundle — you create it per project in step 1 below by copying the `.example`. + +## Per-project setup + +**Recommended:** run `/unic-ticket-specification:setup` in Claude Code. It auto-detects the tracker +from the git remote, asks a few questions, and writes `ticket-spec.config.yaml` and the MCP server +for you — no hand-edited YAML. It is idempotent (re-run to fill gaps; pass `reconfigure` to start +over). + +**Manual (3 steps)** if you prefer not to use `/setup`: + +1. **Config.** Copy `ticket-spec.config.example.yaml` → `ticket-spec.config.yaml` + and fill in: `tracker.type`, the matching tracker block (tenant/org/project), + `repos`, `docs`, and (optionally) override `templates`. +2. **Access.** Either: + - set `tracker.access.mcp: true` and put the right MCP server in + `mcp/ticket-spec-tracker.json` (Atlassian / Azure DevOps / GitHub MCP), **or** + - set `tracker.access.mcp: false` and `tracker.access.cli` to `jira`/`az`/`gh` + (that CLI must be installed + authenticated on each machine). +3. **Run** it against the project, passing either an existing ticket reference or + a free-text description of a new ticket. + +## Notes + +- The human approval gate is mandatory — nothing is written to the tracker until + you approve. Rejecting with feedback revises the draft/estimate/target and + re-presents (up to 3 attempts). +- A full local copy of every proposal is written under `output.dir` + (default `ticket-spec-output/`) so results survive rejection or failure. +- This bundle lives in the shared `unic-agents-plugins` repo and ships only the + config **template** (`ticket-spec.config.example.yaml`). Each project copies it to + `ticket-spec.config.yaml` and fills in its own tracker/repo detail; the active + config is never committed to the shared bundle. diff --git a/apps/claude-code/unic-ticket-specification/.archon/workflows/unic-ticket-specification.yaml b/apps/claude-code/unic-ticket-specification/.archon/workflows/unic-ticket-specification.yaml new file mode 100644 index 00000000..7c41cebd --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/.archon/workflows/unic-ticket-specification.yaml @@ -0,0 +1,270 @@ +name: unic-ticket-specification +description: | + Take a tracker ticket from intake to "ready for implementation" — usable by + any Unic project regardless of issue tracker (Jira, Azure DevOps or GitHub), + source-control host, repo count, or operating system (Windows / macOS). + + Use when the user wants to prepare/groom a ticket: either an existing + reference (a Jira key like ACME-1234, an Azure DevOps work-item id, or a + GitHub issue number / #123) or a free-text description of a ticket that must + be created. The workflow reads the per-project config at + `.archon/ticket-spec.config.yaml`, detects the input type, fetches + analyses + the ticket against the configured code repos and linked docs, classifies Bug + vs Change-Request/Story, rewrites the description to the matching template, + produces a 3-point PERT effort estimate, then REQUIRES human approval before + writing anything to the tracker (create or update + record the estimate). + + Completeness is assessed but never blocks — incomplete tickets are still + estimated with explicit caveats. + NOT for: implementing the ticket, code review, or general Q&A. + +provider: claude +model: sonnet +interactive: true # required — this workflow has a human approval gate + +worktree: + enabled: false # read-only against the repos; writes only to the tracker (post-approval) + +nodes: + # ── 1. Detect input type: existing ticket reference vs. free-text new ticket ── + - id: detect-input + prompt: | + Read `.archon/ticket-spec.config.yaml` to learn this project's tracker + type and `key_prefixes`. Then decide whether $ARGUMENTS refers to an + EXISTING ticket or describes a NEW ticket to be created. + + An EXISTING reference is one of: + - Jira: a key like `ACME-1234` (any `PREFIX-<digits>` where PREFIX is a + known/plausible project key), or a bare number when the project uses + Jira and a default project is configured. + - Azure DevOps: a bare work-item id (all digits), optionally `#123` or + `AB#123`. + - GitHub: `#123`, a bare number, or `owner/repo#123`. + Anything that reads as a description / request is a NEW ticket. + + Return: + mode = "existing" or "create" + key = the normalized reference for existing (e.g. "ACME-1234", + "1234", "#123"); "" for create. + project = for Jira existing: the key prefix; for Jira create when the + input names a project, that key; otherwise "". + For Azure DevOps / GitHub leave "" (resolved from config). + + Input: $ARGUMENTS + model: haiku + allowed_tools: [Read] + output_format: + type: object + properties: + mode: + type: string + enum: [existing, create] + key: + type: string + project: + type: string + required: [mode, key, project] + + # ── 2. Fetch (if existing) + analyse: code repos + linked docs, surface gaps ── + # A tracker MCP server (mcp-remote) can cold-start slowly on the first call; + # retry + a generous idle_timeout let it self-heal. If no MCP is configured, + # the command falls back to the tracker CLI. + - id: analyze + command: uts-analyze + depends_on: [detect-input] + mcp: .archon/mcp/ticket-spec-tracker.json + allowed_tools: [Read, Grep, Glob, Write, Bash] + idle_timeout: 900000 + retry: + max_attempts: 3 + delay_ms: 8000 + on_error: all + + # ── 3. Classify Bug vs. Change-Request/Story (after analysis → always has real data) ── + - id: classify + prompt: | + Determine the ticket KIND (for template selection) and the tracker issue + type name. + + Read these: + - Config: `.archon/ticket-spec.config.yaml` (the `classification` map). + - Mode: $detect-input.output.mode + - Existing ticket (existing path): `$ARTIFACTS_DIR/ticket.md` — use its + stated issue type. + - Analysis: `$ARTIFACTS_DIR/analysis.md` + - New-ticket description (create path): $ARGUMENTS + + Rules: + - If the ticket's issue-type name is listed in `classification.bug_types` + → kind=BUG. If listed in `classification.cr_story_types` → kind=CR_STORY. + - For a NEW ticket, infer the most fitting kind from the description: + a defect/regression → BUG; a new capability or change to existing + behaviour → CR_STORY. + - `issue_type_name` is the canonical tracker type name to create/keep + (e.g. "Bug", "Story", "User Story", "Change request"). For an existing + ticket keep its actual type name. For a new ticket pick a sensible name + from the matching list in config. + - If the kind genuinely cannot be determined, DEFAULT to kind=CR_STORY. + Always return a valid decision, never ask a question. + + The concrete tracker issue-type ID (if any) is resolved later at apply time + from config / tracker metadata — do not resolve ids here. + depends_on: [analyze] + model: haiku + allowed_tools: [Read] + output_format: + type: object + properties: + kind: + type: string + enum: [BUG, CR_STORY] + issue_type_name: + type: string + required: [kind, issue_type_name] + + # ── 4. Rewrite description — template-selection branch (BUG vs. everything else) ── + - id: rewrite-bug + command: uts-rewrite-bug + depends_on: [classify] + when: "$classify.output.kind == 'BUG'" + allowed_tools: [Read, Write] + + - id: rewrite-crstory + command: uts-rewrite-crstory + depends_on: [classify] + when: "$classify.output.kind != 'BUG'" + allowed_tools: [Read, Write] + + # ── 5. Completeness assessment — NON-BLOCKING annotation (never cancels) ── + - id: assess-completeness + prompt: | + Assess whether the rewritten ticket has complete and clear requirements. + This is an annotation only — it MUST NOT stop the workflow. + + Read `$ARTIFACTS_DIR/draft-description.md` and the Open questions / + Assumptions in `$ARTIFACTS_DIR/analysis.md`. + + Rate completeness as low / medium / high and write + `$ARTIFACTS_DIR/completeness.md` containing the rating, the unresolved open + questions, and the assumptions the team is relying on. The workflow always + proceeds to estimation regardless of the rating. + depends_on: [rewrite-bug, rewrite-crstory] + trigger_rule: none_failed_min_one_success + allowed_tools: [Read, Write] + output_format: + type: object + properties: + completeness: + type: string + enum: [low, medium, high] + required: [completeness] + + # ── 6. PERT effort estimate (with caveats when completeness < high) ── + - id: estimate + command: uts-estimate + depends_on: [assess-completeness] + allowed_tools: [Read, Write] + + # ── 7. Persist the proposed content to a stable local .md (survives rejection/failure) ── + - id: persist-local + command: uts-persist-local + depends_on: [estimate] + allowed_tools: [Read, Write] + + # ── 8. Present the consolidated draft + estimate for human review ── + - id: present-draft + prompt: | + Assemble the final proposal for human review and OUTPUT IT IN FULL as your + response — the reviewer reads your output before the approval gate. + + Read and consolidate: + - target: `$ARTIFACTS_DIR/target.json` (resolved tracker + project + + action: create a new ticket, or update an existing reference). Falls + back to $detect-input.output if the file is absent. + - proposed description: `$ARTIFACTS_DIR/draft-description.md` + - PERT estimate: `$ARTIFACTS_DIR/estimate.md` + - completeness + open questions: `$ARTIFACTS_DIR/completeness.md` + + Also write the consolidated proposal to `$ARTIFACTS_DIR/proposal.md`. + + Present clearly with headings: (1) what will be written and where (tracker, + project/repo, create-vs-update), (2) the proposed description, (3) the PERT + estimate, (4) completeness rating and any open questions / caveats. Mention + that the full content has been saved locally to the path in + `$ARTIFACTS_DIR/local-output-path.txt`. + depends_on: [persist-local] + allowed_tools: [Read, Write] + + # ── 9. Human approval gate — REQUIRED before any tracker write ── + - id: approval-gate + approval: + message: | + Review the proposed ticket description and PERT estimate shown above (the + full proposal is also saved to the run artifacts as proposal.md). Nothing + is written to the tracker until you approve. + + HOW TO RESPOND: + • Approve → click the **Approve** button on the workflow card, or reply + "approve". The ticket is created (or the existing one updated) and the + estimate recorded. + • Need changes → click **Reject** (or reply "reject") and give your + feedback as the reason. The draft/estimate/target are revised and + re-presented for approval. + + Plain comments typed here do NOT approve — use Approve/Reject explicitly. + on_reject: + prompt: | + The reviewer rejected the proposal with this feedback: + + $REJECTION_REASON + + Revise to address it: update `$ARTIFACTS_DIR/draft-description.md` (keep + the correct template structure), `$ARTIFACTS_DIR/estimate.md` if the + effort changed, and `$ARTIFACTS_DIR/target.json` if the reviewer named a + different target project/repo. Refresh `$ARTIFACTS_DIR/proposal.md`, AND + rewrite the persisted local file (its path is in + `$ARTIFACTS_DIR/local-output-path.txt`) so it reflects the revised + content. Then output the revised proposal in full. + max_attempts: 3 + depends_on: [present-draft] + + # ── 10. Apply to the tracker — branch on input type (post-approval only) ── + - id: apply-create + command: uts-apply-create + depends_on: [approval-gate, detect-input] + when: "$detect-input.output.mode == 'create'" + mcp: .archon/mcp/ticket-spec-tracker.json + allowed_tools: [Read, Write, Bash] + idle_timeout: 600000 + retry: + max_attempts: 3 + delay_ms: 8000 + on_error: all + + - id: apply-update + command: uts-apply-update + depends_on: [approval-gate, detect-input] + when: "$detect-input.output.mode == 'existing'" + mcp: .archon/mcp/ticket-spec-tracker.json + allowed_tools: [Read, Write, Bash] + idle_timeout: 600000 + retry: + max_attempts: 3 + delay_ms: 8000 + on_error: all + + # ── 11. Report outcome ── + - id: report + prompt: | + Summarize the outcome for the user. The write step produced: + + Create result: $apply-create.output + Update result: $apply-update.output + + Report the final ticket reference and URL, the issue type, the recorded + PERT E estimate, and list any remaining open questions from + `$ARTIFACTS_DIR/analysis.md` so the team can follow up. Keep it concise. + depends_on: [apply-create, apply-update] + trigger_rule: none_failed_min_one_success + model: haiku + allowed_tools: [Read] diff --git a/apps/claude-code/unic-ticket-specification/.claude-plugin/marketplace.json b/apps/claude-code/unic-ticket-specification/.claude-plugin/marketplace.json new file mode 100644 index 00000000..66fdccd4 --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/.claude-plugin/marketplace.json @@ -0,0 +1,27 @@ +{ + "metadata": { + "description": "A portable Archon workflow that grooms a tracker ticket to ready-for-implementation", + "homepage": "https://github.com/unic/unic-agents-plugins" + }, + "name": "unic-ticket-specification", + "owner": { + "name": "Unic AG" + }, + "plugins": [ + { + "author": { + "name": "Unic AG" + }, + "category": "productivity", + "description": "Take a tracker ticket from intake to ready-for-implementation — detect input, analyse across all configured repos and linked docs, classify Bug vs Change-Request/Story, rewrite to the configured template, PERT-estimate, and write back after a human approval gate. Tracker-agnostic (Jira, Azure DevOps, GitHub), multi-repo, and OS-independent.", + "displayName": "Unic Ticket Specification", + "homepage": "https://github.com/unic/unic-agents-plugins", + "keywords": ["archon", "workflow", "ticket", "grooming", "jira", "azure-devops", "github", "unic"], + "license": "LGPL-3.0-or-later", + "name": "unic-ticket-specification", + "source": "./", + "tags": ["productivity", "workflow", "ticketing"], + "version": "0.1.0" + } + ] +} diff --git a/apps/claude-code/unic-ticket-specification/.claude-plugin/plugin.json b/apps/claude-code/unic-ticket-specification/.claude-plugin/plugin.json new file mode 100644 index 00000000..a232a84d --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/.claude-plugin/plugin.json @@ -0,0 +1,12 @@ +{ + "name": "unic-ticket-specification", + "version": "0.1.0", + "description": "A portable Archon workflow that takes a tracker ticket from intake to ready-for-implementation — tracker-agnostic (Jira, Azure DevOps, GitHub), multi-repo, and OS-independent.", + "author": { + "name": "Unic AG", + "url": "https://www.unic.com" + }, + "homepage": "https://github.com/unic/unic-agents-plugins", + "license": "LGPL-3.0-or-later", + "keywords": ["archon", "workflow", "ticket", "grooming", "jira", "azure-devops", "github", "unic"] +} diff --git a/apps/claude-code/unic-ticket-specification/AGENTS.md b/apps/claude-code/unic-ticket-specification/AGENTS.md new file mode 100644 index 00000000..300e92f9 --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/AGENTS.md @@ -0,0 +1,85 @@ +# AGENTS.md — unic-ticket-specification + +Guidance for any AI agent working inside this Plugin directory. `CLAUDE.md` in this directory is a symlink to this file. + +## What this Plugin is + +`unic-ticket-specification` is a Claude Code Plugin in the [`unic-agents-plugins`](../../../AGENTS.md) monorepo. It packages a **portable Archon workflow** that takes a tracker ticket from intake to "ready for implementation": detect input → analyse (across all configured repos + linked docs) → classify Bug vs Change-Request/Story → rewrite to the configured template → non-blocking completeness check → PERT estimate → persist locally → human approval gate → write back to the tracker → report. It is tracker-agnostic (Jira, Azure DevOps, GitHub), multi-repo, and OS-independent (Windows / macOS). See [`CONTEXT.md`](CONTEXT.md) for the domain vocabulary (Ticket reference, Mode, Kind, Per-project config, Analysis, Proposal, Approval gate). + +## Where to start + +Root docs (monorepo-wide conventions live here — pnpm scripts, Gitflow, SemVer, Conventional Commits, code conventions, LICENSE policy, cross-platform requirement): + +- [Root AGENTS.md](../../../AGENTS.md) — source of truth for cross-cutting rules +- [Root CONTEXT.md](../../../CONTEXT.md) — monorepo-wide vocabulary (Plugin, Workspace Package, Release, Feature, Consumer) +- [Root CONTEXT-MAP.md](../../../CONTEXT-MAP.md) — index of all bounded contexts in the repo +- [Root docs/adr/](../../../docs/adr/) — monorepo-wide architecture decisions +- [Root docs/process/](../../../docs/process/) — process and workflow guides + +This Plugin's own surfaces: + +- [`.archon/`](.archon/) — the installable bundle: the workflow DAG, seven command templates, the tracker MCP config, the config template, and the bundle README +- [`.archon/unic-ticket-specification.README.md`](.archon/unic-ticket-specification.README.md) — per-project install + setup instructions +- [`commands/setup.md`](commands/setup.md) — the `/unic-ticket-specification:setup` slash command (zero-config install + configuration) +- [`docs/adr/`](docs/adr/) — Plugin-specific architecture decisions + +## Commands + +Plugin-specific pnpm scripts (run from this directory or with `pnpm --filter unic-ticket-specification <script>` from the repo root): + +```sh +pnpm bump <patch|minor|major> # bump plugin.json version + promote CHANGELOG +pnpm sync-version # mirror plugin.json version into marketplace.json + package.json +pnpm tag # create the unic-ticket-specification@<version> git tag locally +pnpm verify:changelog # check CHANGELOG entry for the current version +``` + +This Plugin ships no JavaScript — it is pure Archon workflow YAML, command markdown, and config templates — so it has no `test` or `typecheck` script. Monorepo-wide commands (`pnpm install`, `pnpm check`, `pnpm format`, `pnpm ci:check`) are documented in the [root AGENTS.md](../../../AGENTS.md). + +## Layout + +```tree +.archon/ # The installable bundle — copy into a Consumer's .archon/ to install + workflows/ + unic-ticket-specification.yaml # the DAG + commands/ + uts-*.md # seven Archon command templates rendered inside workflow nodes + mcp/ + ticket-spec-tracker.json # tracker MCP server (per project) + ticket-spec.config.example.yaml # documented per-project config template (Consumer copies → ticket-spec.config.yaml) + unic-ticket-specification.README.md # bundle install + setup instructions +.claude-plugin/ # Plugin manifest (plugin.json) and marketplace listing +commands/ # Claude Code slash command definitions — only setup.md today +docs/ # Plugin-specific documentation + adr/ # Plugin Architecture Decision Records +CONTEXT.md # Domain vocabulary for ticket specification +``` + +## Plugin doctrines + +Load-bearing invariants. These either originate in a Plugin ADR or are policy decisions that are not obvious from the files. + +- **No tracker/tenant/repo/OS detail is hardcoded in the workflow.** Everything specific lives in the per-project config `.archon/ticket-spec.config.yaml`. The workflow YAML and the seven command templates must stay generic across Jira, Azure DevOps, and GitHub. When editing, never leak a tracker name, tenant, cloud id, repo path, or OS-specific command into the workflow or commands. See [ADR-0001](docs/adr/0001-tool-agnostic-config-driven.md). +- **MCP-first, CLI fallback.** Tracker nodes load the MCP server from the fixed path `.archon/mcp/ticket-spec-tracker.json`; when no MCP is configured they fall back to the CLI named in config (`jira` / `az` / `gh`). Keep both paths working. See [ADR-0002](docs/adr/0002-mcp-first-cli-fallback.md). +- **OS-independent.** Paths are relative and forward-slash; no shell-specific or OS-specific commands. The bundle must behave identically on Windows and macOS. +- **Markdown-only descriptions.** Ticket descriptions are plain Markdown so they render across all three trackers. Do not introduce tracker-specific markup (e.g. Jira ADF) into the templates or commands. See [ADR-0003](docs/adr/0003-markdown-only-descriptions.md). +- **Setup is the zero-config entry point.** `/unic-ticket-specification:setup` (`commands/setup.md`) configures the plugin conversationally — it writes `.archon/ticket-spec.config.yaml` and the MCP server so users never hand-edit YAML. It is idempotent (fresh / partial / full / reconfigure / targeted-tweak) and ships **no JavaScript**. See [ADR-0004](docs/adr/0004-setup-conversational-no-lib.md). +- **The human approval gate is mandatory.** Nothing is written to the tracker before `approval-gate`. Rejection revises the draft / estimate / target and re-presents, up to 3 attempts. +- **Completeness is non-blocking.** The completeness assessment annotates but never cancels — incomplete tickets are still estimated, with explicit caveats. +- **`.archon/` is the bundle.** The Plugin's `.archon/` directory is exactly what a Consumer copies into their own `.archon/` to install. Ship the config **template** (`ticket-spec.config.example.yaml`) only — never a project-specific `ticket-spec.config.yaml`. + +## External dependencies + +- **Archon workflow engine** in the Consumer project (the workflow uses `interactive` approval gates, `when` branches, `output_format`, `mcp`, and `retry`). +- **Tracker access** in the Consumer: either an MCP server placed in `.archon/mcp/ticket-spec-tracker.json`, or the configured tracker CLI (`jira` / `az` / `gh`) installed and authenticated on each machine. + +## Do not add + +- **Tracker-specific markup or hardcoded tenant/repo/OS detail** in the workflow or commands — see doctrines above. +- **A shipped `ticket-spec.config.yaml`.** Only the `.example` template is part of the bundle; the active config is created per Consumer. +- **Estimation methods beyond PERT** until a real Consumer asks for one with a concrete use case. +- **A `lib/` + `test/` + `tsconfig.json` toolchain** unless `commands/setup.md` grows genuinely complex, drift-prone config logic. ADR-0004 deliberately keeps this a pure-content plugin with no JavaScript; that ADR names the trigger for revisiting. + +## Plugin ADRs + +Plugin-specific architecture decisions live in [docs/adr/](docs/adr/). diff --git a/apps/claude-code/unic-ticket-specification/CHANGELOG.md b/apps/claude-code/unic-ticket-specification/CHANGELOG.md new file mode 100644 index 00000000..943f8a89 --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/CHANGELOG.md @@ -0,0 +1,44 @@ +# Changelog + +## [Unreleased] + +### Breaking +- (none) + +### Added +- (none) + +### Fixed +- (none) + +## [0.1.0] — 2026-06-18 + +Initial release of the `unic-ticket-specification` plugin. Ships a portable Archon workflow that +takes a tracker ticket from intake to "ready for implementation" with a mandatory human approval +gate before anything is written back to the tracker. + +### Added + +- **`unic-ticket-specification` workflow** (`.archon/workflows/unic-ticket-specification.yaml`): an + 11-node DAG — detect-input → analyze → classify → rewrite (Bug / CR-Story branch) → + assess-completeness (non-blocking) → estimate (PERT) → persist-local → present-draft → + approval-gate (interactive, max 3 reject/revise attempts) → apply (create / update branch) → + report. +- **Seven Archon command templates** (`.archon/commands/uts-*.md`): `uts-analyze`, + `uts-rewrite-bug`, `uts-rewrite-crstory`, `uts-estimate`, `uts-persist-local`, + `uts-apply-create`, `uts-apply-update`. +- **Tracker MCP server config** (`.archon/mcp/ticket-spec-tracker.json`): MCP-first access with a + CLI fallback (`jira` / `az` / `gh`) selected per project. +- **Config template** (`.archon/ticket-spec.config.example.yaml`): documented per-project + configuration covering tracker type, access, classification map, linked docs, repos, estimation, + output, and description templates. Nothing tracker/tenant/repo/OS-specific is hardcoded in the + workflow. +- **Bundle README** (`.archon/unic-ticket-specification.README.md`): install and per-project setup + instructions for the copy-into-`.archon/` bundle. +- **`/unic-ticket-specification:setup` slash command** (`commands/setup.md`): zero-config, + conversational install + configuration — auto-detects the tracker, asks a few questions, and writes + `.archon/ticket-spec.config.yaml` and the MCP server so users never hand-edit YAML. Idempotent + (fresh / partial / full / reconfigure / targeted-tweak). Ships no JavaScript. +- **Plugin ADRs** (`docs/adr/`): 0001 tool-agnostic config-driven workflow, 0002 MCP-first with CLI + fallback, 0003 Markdown-only descriptions, 0004 setup as a conversational slash command with no JS + lib. diff --git a/apps/claude-code/unic-ticket-specification/CLAUDE.md b/apps/claude-code/unic-ticket-specification/CLAUDE.md new file mode 120000 index 00000000..47dc3e3d --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/apps/claude-code/unic-ticket-specification/CONTEXT.md b/apps/claude-code/unic-ticket-specification/CONTEXT.md new file mode 100644 index 00000000..45d74e91 --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/CONTEXT.md @@ -0,0 +1,92 @@ +# unic-ticket-specification + +A portable Archon workflow that takes a tracker ticket from intake to "ready for implementation". +It is tracker-agnostic (Jira, Azure DevOps, GitHub), multi-repo, and OS-independent (Windows / +macOS): every tracker/tenant/repo/OS detail lives in a per-project config, never in the workflow. + +## Language + +### Inputs and routing + +**Ticket reference**: +An existing-ticket pointer the workflow detects from its argument — a Jira key (`ACME-1234`), an +Azure DevOps work-item id, or a GitHub issue number (`#123`). Distinguished from a free-text +description by the `detect-input` node, using `project.key_prefixes` from config. +_Avoid_: issue id, ticket number (ambiguous across trackers) + +**Mode**: +The detected input class: `existing` (groom and update a known ticket) or `create` (a new ticket +from a free-text description). Drives the `apply-create` / `apply-update` branch. +_Avoid_: action, operation + +**Kind**: +The template-selection classification of a ticket: `BUG` or `CR_STORY`. Derived by the `classify` +node from the tracker issue-type name via `classification.bug_types` / `cr_story_types`, defaulting +to `CR_STORY` when undeterminable. +_Avoid_: type (collides with the tracker's own issue-type name), category + +### Configuration + +**Per-project config**: +`.archon/ticket-spec.config.yaml` — the single source of all tracker/tenant/repo/OS detail. The +workflow and every command read it at runtime. Shipped as `ticket-spec.config.example.yaml`; each +project copies it and fills it in. +_Avoid_: settings, options file + +**Tracker access**: +How the workflow reaches the tracker: MCP-first (loaded from the fixed path +`.archon/mcp/ticket-spec-tracker.json`) with a CLI fallback (`jira` / `az` / `gh`) named in config. +_Avoid_: connection, integration + +**Repos**: +The one-or-many code checkouts listed under `repos:` in config. Analysis greps across **all** of +them. Paths are relative or absolute, always forward-slash. +_Avoid_: workspace, codebase (singular implies one repo) + +### Artifacts + +**Analysis**: +`$ARTIFACTS_DIR/analysis.md` — the cross-repo + linked-docs investigation produced by `uts-analyze`, +including open questions and assumptions. Feeds classification, rewrite, completeness, and estimate. +_Avoid_: research, report + +**Draft description**: +`$ARTIFACTS_DIR/draft-description.md` — the rewritten ticket body in the configured Bug or CR-Story +template. The unit the human reviews and that gets written to the tracker on approval. +_Avoid_: body, content + +**PERT estimate**: +`$ARTIFACTS_DIR/estimate.md` — a three-point (optimistic / most-likely / pessimistic) effort +estimate with a computed expected value (E). Carries explicit caveats when completeness < high. +_Avoid_: estimate (unqualified), story points + +**Completeness rating**: +A non-blocking `low` / `medium` / `high` annotation written to `$ARTIFACTS_DIR/completeness.md`. It +never stops the workflow — incomplete tickets are still estimated, with caveats. +_Avoid_: readiness score, quality gate + +**Proposal**: +`$ARTIFACTS_DIR/proposal.md` — the consolidated draft + estimate + completeness shown at the +approval gate. A full local copy is also persisted under `output.dir` so it survives rejection or +failure. +_Avoid_: summary, output + +### Control flow + +**Approval gate**: +The mandatory interactive node (`approval-gate`) that pauses the workflow until a human approves or +rejects. Nothing is written to the tracker before it. Rejection feeds `$REJECTION_REASON` back to +revise the draft / estimate / target and re-present, up to 3 attempts. +_Avoid_: review step, confirmation + +## Relationships + +- `detect-input` sets **Mode**, which later selects `apply-create` vs `apply-update` +- `analyze` produces the **Analysis**, which every downstream node reads +- `classify` sets **Kind**, which selects `rewrite-bug` vs `rewrite-crstory` +- The **Per-project config** is read by the workflow and all seven `uts-*` commands; nothing + tracker/tenant/repo/OS-specific is hardcoded in the workflow YAML +- **Tracker access** is resolved the same way in `analyze`, `apply-create`, and `apply-update`: + MCP from `.archon/mcp/ticket-spec-tracker.json`, else the configured CLI +- The **Approval gate** is the only boundary at which the workflow writes to the tracker; the + **Proposal** is what it presents there diff --git a/apps/claude-code/unic-ticket-specification/README.md b/apps/claude-code/unic-ticket-specification/README.md new file mode 100644 index 00000000..11ee216b --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/README.md @@ -0,0 +1,110 @@ +# unic-ticket-specification + +A portable Archon workflow that takes a tracker ticket from intake to **"ready for implementation"** +— usable by any Unic project regardless of issue tracker (**Jira / Azure DevOps / GitHub**), +source-control host, number of code repos, or operating system (**Windows / macOS**). + +Archon has no marketplace; this plugin rides the Claude Code plugin marketplace. The installable +bundle is the plugin's [`.archon/`](.archon/) directory — copy it into a target project's `.archon/` +to install. See the [bundle README](.archon/unic-ticket-specification.README.md) for per-project +setup. + +--- + +## Workflow + +```mermaid +flowchart TD + A[detect-input] --> B[analyze] + B --> C[classify] + C -->|kind == BUG| D1[rewrite-bug] + C -->|kind != BUG| D2[rewrite-crstory] + D1 & D2 --> E[assess-completeness] + E --> F[estimate · PERT] + F --> G[persist-local] + G --> H[present-draft] + H --> I["approval-gate ✓"] + I -->|mode == create| J1[apply-create] + I -->|mode == existing| J2[apply-update] + J1 & J2 --> K[report] +``` + +> **✓** = interactive human gate (workflow pauses until human approves or rejects; rejection revises +> and re-presents, up to 3 attempts). Nothing is written to the tracker before this gate. + +--- + +## Node reference + +| Node | Type | Tracker write | Human gate | Notes | +| ------------------- | ----------- | ------------- | ---------- | ------------------------------------------------------ | +| detect-input | prompt | — | — | existing reference vs. free-text new ticket | +| analyze | command | read-only | — | fetch + grep across all configured repos + linked docs | +| classify | prompt | — | — | Bug vs Change-Request/Story (defaults to CR_STORY) | +| rewrite-bug | command | — | — | `when` kind == BUG | +| rewrite-crstory | command | — | — | `when` kind != BUG | +| assess-completeness | prompt | — | — | non-blocking low/medium/high annotation | +| estimate | command | — | — | three-point PERT with caveats when completeness < high | +| persist-local | command | — | — | stable local copy that survives rejection/failure | +| present-draft | prompt | — | — | consolidates the proposal for review | +| approval-gate | interactive | — | ✓ | required before any tracker write; max 3 attempts | +| apply-create | command | **create** | — | `when` mode == create | +| apply-update | command | **update** | — | `when` mode == existing | +| report | prompt | — | — | final reference, URL, issue type, PERT E, open items | + +--- + +## Quick start + +**Step 1 — Configure (recommended: zero-config).** In Claude Code, run: + +``` +/unic-ticket-specification:setup +``` + +It auto-detects your tracker from the git remote, asks a few questions, and writes +`.archon/ticket-spec.config.yaml` and `.archon/mcp/ticket-spec-tracker.json` for you — no hand-edited +YAML. It is idempotent: re-run it to fill gaps, pass `reconfigure` to start over, or pass free-form +intent (e.g. "switch tracker to azure-devops") for a targeted tweak. + +**Step 2 — Run** it, passing either an existing ticket reference or a free-text description of a new +ticket: + +```sh +archon workflow run unic-ticket-specification --input "ACME-1234" +archon workflow run unic-ticket-specification --input "Add a CSV export button to the orders list" +``` + +### Manual install (alternative) + +If you prefer not to use `/setup`: copy this plugin's `.archon/` contents into your project's +`.archon/`, copy `ticket-spec.config.example.yaml` → `.archon/ticket-spec.config.yaml` and fill in +`tracker.type`, the matching tracker block, `repos`, `docs`, and (optionally) `templates`, then +either set `tracker.access.mcp: true` with the right MCP server in +`.archon/mcp/ticket-spec-tracker.json`, or set `tracker.access.mcp: false` and name a CLI +(`jira` / `az` / `gh`) that is installed and authenticated. + +Full instructions live in the [bundle README](.archon/unic-ticket-specification.README.md). + +--- + +## How it stays generic + +- **No tracker/tenant/repo/OS detail is hardcoded.** It all lives in the per-project config + `.archon/ticket-spec.config.yaml`. +- **MCP-first, CLI fallback.** Tracker nodes load `.archon/mcp/ticket-spec-tracker.json`; otherwise + they use the configured CLI. +- **Multi-repo.** `repos:` lists one or many checkouts; analysis greps across all. +- **OS-independent.** Relative, forward-slash paths; no shell- or OS-specific commands. +- **Markdown-only descriptions** so they render across Jira, Azure DevOps, and GitHub alike. + +--- + +## Dependencies + +- **Archon workflow engine** in the target project (uses interactive approval gates, `when` + branches, `output_format`, `mcp`, and `retry`). +- **Tracker access** — one of: + - an MCP server in `.archon/mcp/ticket-spec-tracker.json` (Atlassian / Azure DevOps / GitHub MCP), or + - the matching tracker CLI installed + authenticated: `jira` (Jira), `az` with the + `azure-devops` extension (Azure DevOps), or `gh` (GitHub). diff --git a/apps/claude-code/unic-ticket-specification/commands/setup.md b/apps/claude-code/unic-ticket-specification/commands/setup.md new file mode 100644 index 00000000..fa57aa05 --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/commands/setup.md @@ -0,0 +1,108 @@ +--- +allowed-tools: ['Read', 'Write', 'Glob', 'Bash'] +argument-hint: '[reconfigure | <free-form intent>]' +description: 'Configure unic-ticket-specification for this project: tracker, repos, linked docs, and tracker access (MCP or CLI)' +--- + +# unic-ticket-specification:setup + +> Design rationale: [ADR-0004 — Setup is a conversational slash command with no JS lib](docs/adr/0004-setup-conversational-no-lib.md) + +**Arguments:** "$ARGUMENTS" + +This command makes the plugin **zero-config from the user's perspective**: instead of hand-editing +YAML, you answer a few questions and it writes `.archon/ticket-spec.config.yaml` and +`.archon/mcp/ticket-spec-tracker.json` for you. It is idempotent — safe to re-run. + +Follow these steps in order. Do not write any files except in Step 5. Ask questions **one at a +time**, with a short explainer, and skip anything that is already configured. + +## Step 0 — Archon preflight + +The workflow runs on the Archon engine. Check it is available: + +```sh +archon --version +``` + +If `archon` is not found, tell the user the plugin requires the Archon workflow engine in this +project and stop. Otherwise continue. + +## Step 1 — Locate the bundle and the target `.archon/` + +The installable bundle lives in this plugin under `${CLAUDE_PLUGIN_ROOT}/.archon/`. The target is +the current project's `.archon/` directory (create it if missing). Read the documented template +`${CLAUDE_PLUGIN_ROOT}/.archon/ticket-spec.config.example.yaml` — it is the source of truth for +every config key and its allowed values. Use it verbatim as the shape you fill in. + +## Step 2 — Determine the current state (idempotency) + +Read `.archon/ticket-spec.config.yaml` in the target project if it exists, and parse `$ARGUMENTS`: + +- **fresh** — no config file: prompt for everything in Step 3. +- **partial** — config exists but is missing one or more of `tracker.type`, the matching tracker + block, or at least one entry under `repos`: confirm what is set and ask only for the gaps. +- **full** — config is complete and `$ARGUMENTS` is empty: print the current configuration as a + summary and stop (no writes). +- **reconfigure** — `$ARGUMENTS` is `reconfigure`: re-prompt for everything, pre-filling current + values as defaults. +- **targeted tweak** — `$ARGUMENTS` is free-form intent (e.g. "switch tracker to azure-devops", + "add the platform repo"): change only what the intent names; leave everything else intact. + +## Step 3 — Gather answers (one question at a time) + +Auto-detect what you can before asking. Run `git remote -v` and `git config --get remote.origin.url` +to guess the tracker (`github.com` → `github`; `dev.azure.com` / `visualstudio.com` → `azure-devops`; +otherwise ask, defaulting to `jira`). Then collect, skipping anything already set: + +1. **`project.name`** and **`project.key_prefixes`** (Jira project keys that mark an existing + ticket, e.g. `["ACME"]`; usually empty for Azure DevOps / GitHub). +2. **`tracker.type`** — `jira` | `azure-devops` | `github` (pre-filled from auto-detection). +3. The matching **tracker block**: + - jira: `site_url`, `default_project`; leave `cloud_id` empty to auto-resolve at runtime. + - azure-devops: `org_url`, `project`, and the `work_item_types` bug / cr_story names. + - github: `issues_repo` (`owner/repo`) and the bug / cr_story `labels`. +4. **`tracker.access`** — prefer MCP: `mcp: true` (Step 5 writes the right MCP server). If the user + has no MCP, set `mcp: false` and `cli` to `jira` / `az` / `gh` (it must be installed + + authenticated on each machine). +5. **`docs`** — linked documentation source: `confluence` | `azure-wiki` | `github-wiki` | `none` + (with `confluence.cloud_id` when applicable). +6. **`repos`** — one or many code checkouts, each `name` + `path` (relative, forward-slash). Default + to a single `{ name: "main", path: "." }`. +7. Offer to keep the default `classification`, `estimation` (`pert`), `output.dir`, and `templates` + from the example; only ask to override `templates` if the client uses non-standard ticket shapes. + +## Step 4 — Confirm + +Show the user the resolved configuration as YAML and the MCP server you will write, and confirm +before writing. If they reject, return to Step 3 for the parts they want changed. + +## Step 5 — Write the files + +Into the **target project**: + +1. Write `.archon/ticket-spec.config.yaml` — start from the example template, preserve its comments, + and fill in the answers. For a partial/tweak run, merge with `defaults < existing < answers` + (answers win), and never drop fields the user did not touch. +2. Write `.archon/mcp/ticket-spec-tracker.json` only when `tracker.access.mcp` is `true`. Choose the + server that matches `tracker.type`: + - jira → the Atlassian MCP (`npx -y mcp-remote https://mcp.atlassian.com/v1/mcp/authv2`), exactly + as shipped in `${CLAUDE_PLUGIN_ROOT}/.archon/mcp/ticket-spec-tracker.json`. + - azure-devops → the Azure DevOps MCP server the team uses. + - github → the GitHub MCP server the team uses. + If `mcp` is `false`, do not write this file; the workflow uses the configured CLI instead. +3. Copy the workflow and command bundle into the target `.archon/` if not already present: the + `workflows/unic-ticket-specification.yaml` and the seven `commands/uts-*.md` files from + `${CLAUDE_PLUGIN_ROOT}/.archon/`. Never overwrite an existing customised copy without confirming. + +Use forward-slash, relative paths in everything you write — the bundle must work identically on +Windows and macOS. + +## Step 6 — Report + +Summarise what was written (config path, whether an MCP server was written or a CLI was selected, +and which repos are configured), then tell the user how to run it: + +```sh +archon workflow run unic-ticket-specification --input "<ticket reference or new-ticket description>" +``` diff --git a/apps/claude-code/unic-ticket-specification/docs/adr/0001-tool-agnostic-config-driven.md b/apps/claude-code/unic-ticket-specification/docs/adr/0001-tool-agnostic-config-driven.md new file mode 100644 index 00000000..a8997666 --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/docs/adr/0001-tool-agnostic-config-driven.md @@ -0,0 +1,40 @@ +# Tool-agnostic, config-driven workflow + +**Status:** Accepted (2026-06) + +`unic-ticket-specification` is a generalisation of a project-specific `ticket-readiness` workflow into +a single bundle usable by **any** Unic project regardless of issue tracker (Jira, Azure DevOps, +GitHub), source-control host, number of code repos, or operating system (Windows / macOS). All +project-specific variability lives in a per-project config file, `.archon/ticket-spec.config.yaml`. +The workflow YAML and the seven `uts-*` command templates contain **no** tracker name, tenant, +cloud id, repo path, or OS-specific command. + +## Considered options + +- **One workflow per tracker (jira-ticket-spec, ado-ticket-spec, …)** — rejected. Three near-identical + DAGs would drift, triple the maintenance, and still not cover multi-repo or per-client template + differences. The branching that genuinely differs between trackers is small (fetch / create / + update) and is better isolated inside command templates that read the config than duplicated across + whole workflows. + +- **Bake the active project's config into the workflow** (the original `ticket-readiness` shape) + — rejected. It is exactly what blocks reuse: tenant URLs, project keys, and repo paths hardcoded in + the DAG mean every new project forks the file. Extracting them into config is the whole point of the + generalisation. + +- **Tool-agnostic config file as the single source of variability** — chosen. `tracker.type` selects + the tracker block; `repos[]` lists one or many checkouts; `docs` names the linked-documentation + source; `classification` maps tracker issue-type names to template kinds; `templates` lets a client + override the Bug / CR-Story shapes. The workflow reads this file at runtime and stays generic. + +## Consequences + +- The workflow and commands must never reference a concrete tracker/tenant/repo/OS. New tracker + behaviour is added by reading a new config key, not by editing the DAG topology. +- A project installs the plugin by copying the bundle and filling in the config — see + [ADR-0004](0004-setup-conversational-no-lib.md) for the `/setup` command that does this without + hand-editing YAML. +- Only the config **template** (`ticket-spec.config.example.yaml`) ships in the bundle; a populated + `ticket-spec.config.yaml` is created per project and never committed to the shared plugin. +- The Jira `cloud_id` may be left empty and auto-resolved at runtime from `site_url`, so client + tenants whose GUID is unknown still work with no extra config. diff --git a/apps/claude-code/unic-ticket-specification/docs/adr/0002-mcp-first-cli-fallback.md b/apps/claude-code/unic-ticket-specification/docs/adr/0002-mcp-first-cli-fallback.md new file mode 100644 index 00000000..b1d62f43 --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/docs/adr/0002-mcp-first-cli-fallback.md @@ -0,0 +1,35 @@ +# MCP-first tracker access with a CLI fallback + +**Status:** Accepted (2026-06) + +The workflow reads from and writes to an issue tracker in three nodes (`analyze`, `apply-create`, +`apply-update`). Tracker access is **MCP-first with a CLI fallback**: tracker nodes load an MCP +server from the fixed path `.archon/mcp/ticket-spec-tracker.json`; when no MCP is configured +(`tracker.access.mcp: false`), the commands fall back to the tracker CLI named in config +(`jira` / `az` / `gh`). + +## Considered options + +- **MCP only** — rejected. Not every machine or CI runner has an MCP server configured or + authenticated, and some Unic client tenants expose a CLI more readily than an MCP endpoint. + MCP-only would make the bundle unusable in those environments. + +- **CLI only** — rejected. The richer MCP integrations (e.g. the Atlassian MCP) give structured + access to issues, fields, and metadata without per-tracker CLI quirks, and align with how the rest + of the Unic agent tooling reaches Atlassian. Forcing CLI everywhere throws that away. + +- **MCP-first, CLI fallback** — chosen. The common, richer path is MCP via a fixed, predictable file + path so workflow nodes can declare `mcp: .archon/mcp/ticket-spec-tracker.json` statically; the CLI + is the portable escape hatch. Which one is active is a single config switch, not a workflow edit. + +## Consequences + +- The MCP config path is **fixed** (`.archon/mcp/ticket-spec-tracker.json`) so the workflow YAML can + reference it without templating. `/setup` writes the server matching `tracker.type`. +- Tracker nodes carry a generous `idle_timeout` and `retry` because a remote MCP server + (`mcp-remote`) can cold-start slowly on the first call; the retry lets it self-heal rather than + failing the run. +- The fallback CLI must be installed and authenticated on each machine that runs the workflow; this + is documented in the bundle README and surfaced by `/setup`. +- Adding a new tracker means supplying its MCP server (or CLI) and the read/create/update calls in + the `uts-*` commands — the access mechanism itself does not change. diff --git a/apps/claude-code/unic-ticket-specification/docs/adr/0003-markdown-only-descriptions.md b/apps/claude-code/unic-ticket-specification/docs/adr/0003-markdown-only-descriptions.md new file mode 100644 index 00000000..3b64b991 --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/docs/adr/0003-markdown-only-descriptions.md @@ -0,0 +1,32 @@ +# Markdown-only ticket descriptions + +**Status:** Accepted (2026-06) + +The rewritten ticket descriptions the workflow produces (Bug and CR-Story templates, and the body +written back to the tracker) are **plain Markdown**. No tracker-specific markup — notably Jira's +Atlassian Document Format (ADF) and its `Expand` macro — is used. + +## Considered options + +- **Tracker-native rich markup (e.g. Jira ADF with collapsible `Expand` sections)** — rejected. ADF + renders nicely in Jira but is meaningless in Azure DevOps and GitHub, and authoring it couples the + command templates to one tracker, which directly contradicts the tool-agnostic design in + [ADR-0001](0001-tool-agnostic-config-driven.md). The user explicitly rejected a Jira-only `Expand` + element for this reason. + +- **Per-tracker description renderers** — rejected. Maintaining one body format per tracker multiplies + the template surface for marginal visual gain and reintroduces the drift the bundle exists to avoid. + +- **Markdown everywhere** — chosen. Markdown renders acceptably across Jira, Azure DevOps, and GitHub, + keeps the templates in config (`templates.bug`, `templates.cr_story`) tracker-neutral, and lets a + client override the shapes without touching tracker-specific code. + +## Consequences + +- The CR-Story template deliberately splits the old single "ToDo" into two audiences: `### ToDo` is a + plain-language scope list for product owners (no file paths or code), and `### Suggested Technical +Tasks` is the detailed, repo-grouped developer guide. Both are Markdown headings, not tracker macros. +- If a tracker mangles a given Markdown construct, the fix is to adjust the Markdown in the config + template — never to emit tracker-specific markup from a command. +- Trackers that ingest Markdown differently (e.g. Jira wiki vs. ADF on create) are handled at the + `apply-*` boundary by the tracker's own MCP/CLI conversion, keeping the authored content uniform. diff --git a/apps/claude-code/unic-ticket-specification/docs/adr/0004-setup-conversational-no-lib.md b/apps/claude-code/unic-ticket-specification/docs/adr/0004-setup-conversational-no-lib.md new file mode 100644 index 00000000..1e3ddbfe --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/docs/adr/0004-setup-conversational-no-lib.md @@ -0,0 +1,45 @@ +# Setup is a conversational slash command with no JS lib + +**Status:** Accepted (2026-06) + +The user-facing entry point for configuring the plugin in a target project is the +`/unic-ticket-specification:setup` slash command (`commands/setup.md`). Unlike +`unic-archon-dlc`, whose setup delegates all filesystem writes to a tested +`lib/install-runner.mjs` ([its ADR-0001](../../../unic-archon-dlc/docs/adr/0001-setup-as-slash-command.md)), +this plugin's setup command does the work **conversationally** — Claude reads the documented config +template, asks the questions, and writes `.archon/ticket-spec.config.yaml` plus +`.archon/mcp/ticket-spec-tracker.json` directly. The plugin ships **no JavaScript**. + +This decision exists to close a guideline gap: `CONTRIBUTING.md` requires plugins to be "zero-config +from the user's perspective — no configuration files users must create beyond credentials." Before +`/setup`, installing meant hand-copying `ticket-spec.config.example.yaml` and editing YAML, which +violated that rule. + +## Considered options + +- **Mirror `unic-archon-dlc`: a thin slash command delegating to `lib/install-runner.mjs`** — + rejected for this plugin. That pattern earns its keep when there is non-trivial, drift-prone logic + worth extracting and unit-testing (additive `defaults < existing < answers` merge, marker-delimited + `CLAUDE.md` rewrites, multi-file `docs/agents/` generation). This bundle has none of that: it writes + one config file and one MCP file from a documented template. Introducing a `lib/`, `test/`, + `tsconfig.json`, and the typescript/`@types/node` toolchain to support ~30 lines of file-writing + would add a build/test surface disproportionate to the value, and turn a pure-content plugin into a + code plugin. + +- **No setup command; keep the manual copy-and-edit instructions** — rejected. It is the status quo + that breaks the zero-config rule and makes onboarding a YAML chore. + +- **A conversational slash command, no JS** — chosen. It satisfies zero-config, keeps the plugin a + pure Archon-workflow bundle (YAML + Markdown + JSON only), and stays idempotent (fresh / partial / + full / reconfigure / targeted-tweak) by reading existing config and branching on `$ARGUMENTS`. + +## Consequences + +- The plugin's `package.json` has no `test` / `typecheck` script and no `tsconfig.json` — there is no + JavaScript to check. This is intentional and documented in `AGENTS.md`. +- The merge precedence (`defaults < existing < answers`) and idempotency states live as prose + instructions in `commands/setup.md` rather than as tested code. If this logic ever grows complex or + bug-prone, revisit by extracting a `lib/` module and adopting the `unic-archon-dlc` delegation + pattern — this ADR would then be superseded. +- `/setup` performs an Archon preflight (`archon --version`) and copies the workflow + command bundle + into the target `.archon/` so a Consumer never hand-edits YAML to get running. diff --git a/apps/claude-code/unic-ticket-specification/docs/adr/README.md b/apps/claude-code/unic-ticket-specification/docs/adr/README.md new file mode 100644 index 00000000..230f2691 --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/docs/adr/README.md @@ -0,0 +1,12 @@ +# ADRs — unic-ticket-specification plugin + +Plugin-scoped architectural decisions. Repo-wide decisions live in `docs/adr/` at the monorepo root. See the root `docs/adr/README.md` for format and numbering conventions. + +## Index + +| ID | Title | Status | +| ---- | ------------------------------------------------------ | -------- | +| 0001 | Tool-agnostic, config-driven workflow | Accepted | +| 0002 | MCP-first tracker access with a CLI fallback | Accepted | +| 0003 | Markdown-only ticket descriptions | Accepted | +| 0004 | Setup is a conversational slash command with no JS lib | Accepted | diff --git a/apps/claude-code/unic-ticket-specification/package.json b/apps/claude-code/unic-ticket-specification/package.json new file mode 100644 index 00000000..912d8aa5 --- /dev/null +++ b/apps/claude-code/unic-ticket-specification/package.json @@ -0,0 +1,21 @@ +{ + "name": "unic-ticket-specification", + "version": "0.1.0", + "private": true, + "license": "LGPL-3.0-or-later", + "type": "module", + "packageManager": "pnpm@10.33.0", + "engines": { + "node": ">=22", + "pnpm": ">=10" + }, + "scripts": { + "bump": "unic-bump", + "sync-version": "unic-sync-version", + "tag": "unic-tag", + "verify:changelog": "unic-verify-changelog" + }, + "devDependencies": { + "@unic/release-tools": "workspace:*" + } +} diff --git a/docs/issues/unic-ticket-specification/PRD.md b/docs/issues/unic-ticket-specification/PRD.md new file mode 100644 index 00000000..007689aa --- /dev/null +++ b/docs/issues/unic-ticket-specification/PRD.md @@ -0,0 +1,117 @@ +# PRD: unic-ticket-specification + +**Status:** resolved +**Plugin:** `apps/claude-code/unic-ticket-specification` +**Category:** plugin +**Tracking issue:** [unic/unic-agents-plugins#256](https://github.com/unic/unic-agents-plugins/issues/256) + +A Claude Code plugin that packages a portable Archon workflow taking a tracker ticket from intake to +"ready for implementation" — tracker-agnostic (Jira, Azure DevOps, GitHub), multi-repo, and +OS-independent (Windows / macOS). + +> Tracked retroactively to satisfy the Feature-first convention in `CONTRIBUTING.md`: the workflow +> was authored and tested in client projects and is now being +> brought into the shared `unic-agents-plugins` monorepo as a distributable plugin on branch +> `feature/add-ticket-specification-workflow`. + +--- + +## Problem Statement + +Unic teams repeatedly need to take a raw tracker ticket — an existing reference or a free-text +request — and groom it into something a developer or an AFK agent can implement: a clear, +template-shaped description, a classification (Bug vs Change-Request/Story), an effort estimate, and +a record of open questions. Doing this by hand is slow and inconsistent across clients, and each +client uses a different issue tracker (Jira, Azure DevOps, GitHub), a different number of code repos, +and a different OS. A project-specific `ticket-readiness` workflow proved the value but hardcoded one +tenant, project, and repo layout, so it could not be reused. There was no portable, installable +artifact a team could drop into any project. + +--- + +## Solution + +`unic-ticket-specification` ships the grooming workflow as a Claude Code plugin whose installable +bundle is its `.archon/` directory. The workflow is an 11-node Archon DAG: detect-input → analyze +(across all configured repos + linked docs) → classify → rewrite to the Bug or CR-Story template → +non-blocking completeness assessment → PERT estimate → persist locally → present draft → **human +approval gate** → apply (create or update) → report. All tracker/tenant/repo/OS variability lives in +a per-project config `.archon/ticket-spec.config.yaml`; nothing tracker-specific is in the workflow +or the seven `uts-*` command templates. Tracker access is MCP-first with a CLI fallback. A +`/unic-ticket-specification:setup` slash command makes installation zero-config: it asks a few +questions and writes the config and MCP server, so users never hand-edit YAML. + +--- + +## User Stories + +1. As a solution architect, I want to pass either an existing ticket reference (Jira key, ADO work-item id, + GitHub issue) or a free-text description, and have the workflow detect which it is, so that one + entry point covers both grooming and creation. +2. As a solution architect, I want the ticket analysed against all my configured code repos and linked docs, + so that the rewritten description and estimate are grounded in the real codebase. +3. As a product owner, I want the description rewritten into our standard Bug or Change-Request/Story + template, with a plain-language `### ToDo` separate from `### Suggested Technical Tasks`, so that + business and technical audiences each get the right level of detail. +4. As a solution architect, I want a non-blocking completeness assessment that annotates gaps but never stops + the run, so that incomplete tickets are still estimated, with caveats. +5. As a team lead, I want a three-point PERT effort estimate recorded on the ticket, so that planning + has a defensible number. +6. As a reviewer, I want a mandatory human approval gate before anything is written to the tracker, + with reject-and-revise up to three attempts, so that no automated content reaches the tracker + without sign-off. +7. As a solution architect on any Unic project, I want the same workflow to work whether we use Jira, Azure + DevOps, or GitHub, on Windows or macOS, across one or many repos, so that I learn it once. +8. As a solution architect installing the plugin, I want a `/setup` command that configures the project for + me, so that I do not hand-edit YAML to get started. +9. As a solution architect, I want a full local copy of every proposal written under `output.dir`, so that the + result survives a rejection or a failed tracker write. + +--- + +## Implementation Decisions + +- **Plugin location & packaging.** Lives at `apps/claude-code/unic-ticket-specification/`, mirroring + `unic-archon-dlc`: the installable Archon assets sit under the plugin's `.archon/`, with plugin + scaffolding (`.claude-plugin/`, `package.json`, `CHANGELOG.md`, `CONTEXT.md`, `AGENTS.md`, + `CLAUDE.md` symlink, `README.md`) around them. +- **Tool-agnostic, config-driven.** See [ADR-0001](../../../apps/claude-code/unic-ticket-specification/docs/adr/0001-tool-agnostic-config-driven.md). +- **MCP-first, CLI fallback.** See [ADR-0002](../../../apps/claude-code/unic-ticket-specification/docs/adr/0002-mcp-first-cli-fallback.md). +- **Markdown-only descriptions.** See [ADR-0003](../../../apps/claude-code/unic-ticket-specification/docs/adr/0003-markdown-only-descriptions.md). +- **Setup is a conversational slash command, no JS lib.** See [ADR-0004](../../../apps/claude-code/unic-ticket-specification/docs/adr/0004-setup-conversational-no-lib.md). +- **Ship the config template only.** The bundle ships `ticket-spec.config.example.yaml`; the active + `ticket-spec.config.yaml` is created per project and never committed to the shared plugin. +- **No LICENSE file.** Per the monorepo convention, the maintainer adds `LICENSE` files by hand. + +--- + +## Testing Decisions + +- This plugin ships **no JavaScript** — it is Archon workflow YAML, command Markdown, MCP JSON, and a + config template — so it has no `node:test` suite. Quality gates are the repo's `pnpm ci:check` + (Biome for JSON, Prettier for Markdown) and `pnpm --filter unic-ticket-specification +verify:changelog`. +- The workflow bundle itself was validated by real runs in multiple client projects + before extraction. +- If `commands/setup.md` ever grows non-trivial config-merge logic, extract it to a tested `lib/` + module and adopt the `unic-archon-dlc` delegation pattern (ADR-0004 names this trigger). + +--- + +## Out of Scope + +- Implementing the ticket, code review, or general Q&A — the workflow stops at "ready for + implementation". +- Estimation methods other than PERT. +- Tracker backends beyond Jira, Azure DevOps, and GitHub. +- Tracker-specific rich markup (e.g. Jira ADF) — descriptions are Markdown only (ADR-0003). +- A non-interactive / headless variant of the approval gate — the human gate is mandatory. + +--- + +## Further Notes + +- The workflow is a generalisation of the project-specific `ticket-readiness` workflow; the + generalisation (config extraction, multi-repo, multi-tracker) is the core of ADR-0001. +- The Jira `cloud_id` may be left empty and auto-resolved at runtime from `site_url`, so client + tenants whose GUID is unknown work with no extra config. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d2fe9a1c..13825c13 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -130,6 +130,12 @@ importers: specifier: 'catalog:' version: 5.8.3 + apps/claude-code/unic-ticket-specification: + devDependencies: + '@unic/release-tools': + specifier: workspace:* + version: link:../../../packages/release-tools + packages/biome-config: {} packages/release-tools: