Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
]
}
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion CONTEXT-MAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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 reference or free-text ticket description>
---

# 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 <key> --plain`
- az → `az boards work-item show --id <id> --output json` (Azure DevOps)
- gh → `gh issue view <number> --repo <owner/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": "<Jira project key | ADO project | GitHub owner/repo>",
"project_name": "<human name>",
"key": "<existing reference, else empty>",
"reason": "<one line: why this target>"
}
```

- `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: <ticket summary>

## Subject & intent
## Affected code areas
- <repo>: <file/module> — <why it is relevant>

## Linked documentation
## Technical constraints & dependencies
## Open questions
- [ ] <question>

## Assumptions
- <assumption>
```

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.
Original file line number Diff line number Diff line change
@@ -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: <free-text ticket description>
---

# 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`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same bug on the create path. Azure DevOps System.Description is an HTML field; the raw Markdown from draft-description.md renders as literal #/**/| characters.

Suggest rendering MD → HTML before the write:

2. Render `draft-description.md` to HTML per *Description formatting* and save to
   `$ARTIFACTS_DIR/description.html`. Create the work item with the title, then set
   the description/repro field to that HTML — never the raw Markdown.

Mirror of the uts-apply-update.md comment. GitHub (line 67) is correct as Markdown; Jira (line 46) should be ADF/wiki markup.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, diff generated in my own project installation trying to solve this, in case this can help here too:

Image

(e.g. `az boards work-item create --type "<type>" --title "<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.
Original file line number Diff line number Diff line change
@@ -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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Azure DevOps System.Description (and Microsoft.VSTS.TCM.ReproSteps) are HTML fields. Writing the raw Markdown contents of draft-description.md here makes ##, **, | tables and - [ ] render as literal text — the ticket is unreadable to humans. This is exactly what happened on a real resumed run.

Suggest: render draft-description.md → HTML first (e.g. write to $ARTIFACTS_DIR/description.html), then set the field from that, per a shared ## Description formatting section. e.g.

1. Render `draft-description.md` to HTML per *Description formatting* and save to
   `$ARTIFACTS_DIR/description.html`. Update the work item `key`, setting the
   description/repro field to that HTML — never the raw Markdown.

GitHub (line 52) is fine as-is since the issue body is Markdown-native; Jira (line 36) should use ADF/wiki markup, also not raw Markdown.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, diff generated in my own project installation trying to solve this, in case this can help here too:

Image

`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.
Loading
Loading