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
5 changes: 4 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ For AI agents (Goose, Cursor, etc.) working on Platypus platform tooling:
1. [access/README.md](access/README.md)
2. [access/mcps/interlink-map.md](access/mcps/interlink-map.md)
3. [docs/how-we-work.md](docs/how-we-work.md)
4. [jira/rules/epic-preservation.yaml](jira/rules/epic-preservation.yaml)
4. [jira/recipes/jira-delivery-workflow.md](jira/recipes/jira-delivery-workflow.md)
5. [jira/rules/epic-preservation.yaml](jira/rules/epic-preservation.yaml)

**Before coding a ticket:** run `jira/scripts/jira-context.sh PLAT-XXX` for scope (requires local Jira config). After merge, comment on the ticket and mark the correct issue level Done — see the recipe.

**Rules**

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ Platform tooling, **access documentation**, and Jira planning for the Platypus (
| Engineers | [access/README.md](access/README.md) → [docs/onboarding.md](docs/onboarding.md) |
| Local config | `~/.config/platform-tools/config.yaml` — see [config/config.yaml.example](config/config.yaml.example) |
| AI agents | [AGENTS.md](AGENTS.md) |
| PLAT planning | [docs/how-we-work.md](docs/how-we-work.md) → [jira/scripts/jira-plan.sh](jira/scripts/jira-plan.sh) |
| PLAT planning | [docs/how-we-work.md](docs/how-we-work.md) → [jira/recipes/jira-delivery-workflow.md](jira/recipes/jira-delivery-workflow.md) |
| Jira context CLI | `jira/scripts/jira-context.sh PLAT-XXX` |
| Merge / PR rules | [docs/how-we-work.md#deployment-and-merge-process](docs/how-we-work.md#deployment-and-merge-process) |

## Links
Expand Down
80 changes: 80 additions & 0 deletions docs/how-we-work.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

**last_updated:** 2026-08-28

> **Workflow recipe:** [jira/recipes/jira-delivery-workflow.md](../jira/recipes/jira-delivery-workflow.md) — pull context, blockers, merge checklist, marking tickets Done.

## PLAT planning loop

Platform delivery is tracked on the [PLAT board](https://catalystsoftware.atlassian.net/jira/software/c/projects/PLAT/summary). This repo owns lightweight **jira-plan** tooling (read-before-write, human ack before apply):
Expand All @@ -24,6 +26,83 @@ jira/scripts/jira-plan.sh --help

See [jira-space-overview.md](jira-space-overview.md) for Epic → Story → Task hierarchy.

## Jira context and ticket lifecycle

Delivery work should stay **traceable**: Jira describes *what* and *why*; git shows *how*. Use the steps below so tickets, PRs, and comments stay aligned.

### Pull relevant context (before you branch)

Start from the ticket in your PR title, not from memory.

```bash
# Markdown summary: status, parent, links, siblings (privacy-safe formatting)
jira/scripts/jira-context.sh PLAT-XXX

# Planning / dependency sweep
jira/scripts/jira-plan.sh pull
jira/scripts/jira-plan.sh review
```

| Source | What to extract |
|--------|-----------------|
| **Target ticket** | Summary, description, labels, current status |
| **Parent Story** | Acceptance criteria — your task should support these |
| **Epic** (`plat-ref:E0N` or parent epic) | Sequencing and dependencies ([dependency-rules.yaml](../jira/rules/dependency-rules.yaml)) |
| **Issue links** | `blocks` / `is blocked by` / `relates to` |
| **Siblings** | Other open tasks under the same story — avoid duplicate work |

**Agents and IDE tools** can load `jira-context.sh` output as read-only scope. Do not copy customer names, tenant IDs, or credentials from Jira into commits or prompts. Obfuscate in ticket comments the same way as in git ([Privacy](#privacy)).

Full step-by-step recipe: [jira/recipes/jira-delivery-workflow.md](../jira/recipes/jira-delivery-workflow.md).

### Align git work with Jira

| Step | Jira | Git |
|------|------|-----|
| Start | Ticket **In Progress** (or assign yourself) | Branch `ab/PLAT-XXX/short-topic` |
| Open PR | Ticket unchanged or comment “PR opened” | Title `PLAT-XXX: …`; body links ticket |
| Review | Respond to review questions in Jira if they affect AC | Address PR comments; push updates |
| Merge | Comment with **merged PR URL** | Merge to `main`; delete branch |

Post-merge comment (no secrets, no customer PII):

```bash
jira/scripts/jira-comment.sh PLAT-XXX "Merged: https://github.com/totango/<repo>/pull/N — <one-line summary>"
```

### Blockers

When progress stops on external access, another team, or an unresolved decision:

1. Create a new **PLAT** ticket — summary `Blocker: …`, label `blocker`.
2. Link it **`blocks`** the ticket you are working on.
3. Comment on the blocked ticket with the blocker key.
4. Do **not** mark the blocked ticket **Done** until the blocker is resolved or scope is renegotiated.

Split unrelated follow-ups into **new** tickets instead of expanding the original scope silently.

### Marking tickets Done

Match Jira status to **actual** delivery — not “PR merged” alone when AC are unfinished.

| Issue type | Move to Done when |
|------------|-------------------|
| **Subtask** | Single deliverable complete (usually one merged PR) |
| **Task** | Phase complete; no remaining subtasks for that chunk |
| **Story** | **All** acceptance criteria satisfied |
| **Epic** | Epic exit criteria met (see [jira-space-overview.md](jira-space-overview.md)) — typically not every PR |

**After merge checklist**

1. PR merged and branch removed.
2. Jira comment with PR link ([`jira-comment.sh`](../jira/scripts/jira-comment.sh)).
3. Close **Subtask/Task** if this PR fulfilled it.
4. Close **Story** only if every AC is done; otherwise file remaining work as new tasks.
5. Update or close **blocker** tickets.
6. File **follow-ups** as new PLAT tickets (link `relates to` parent story/epic).

Transitions are done in the Jira UI today; `jira_list_transitions` in [`jira-api.sh`](../jira/scripts/lib/jira-api.sh) is available for future automation.

## Local configuration

Store machine-specific settings **outside the git repo** using the XDG-style path:
Expand Down Expand Up @@ -151,6 +230,7 @@ Label doc-only PRs **`eng-information`**.
4. CI green (`validate-pr-title` required; doc checks when applicable).
5. **≥ 1 approval** from a teammate (soft rule — do not self-merge without review).
6. Squash or merge per team preference; delete the branch after merge.
7. **Jira hygiene** — comment with merged PR URL; transition Subtask/Task/Story per [Marking tickets Done](#marking-tickets-done); update blockers and file follow-ups as new tickets.

### Privacy at merge time

Expand Down
4 changes: 2 additions & 2 deletions docs/onboarding.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
2. Read [access/README.md](../access/README.md) and [security.md](security.md).
3. Copy `config/config.yaml.example` → `~/.config/platform-tools/config.yaml` (local only).
4. Run `access/scripts/check-access.sh` — expect SKIP/FAIL until configured.
5. Join PLAT planning: [how-we-work.md](how-we-work.md).
6. Merges to `main`: PR title `PLAT-XXX: …`, ≥ 1 approval, CI green — see [Deployment and merge process](how-we-work.md#deployment-and-merge-process).
5. Join PLAT planning: [how-we-work.md](how-we-work.md) and [jira delivery recipe](../jira/recipes/jira-delivery-workflow.md).
6. Merges to `main`: PR title `PLAT-XXX: …`, ≥ 1 approval, CI green — see [Deployment and merge process](how-we-work.md#deployment-and-merge-process). After merge, comment on Jira and close the right ticket level.

## Agents

Expand Down
112 changes: 112 additions & 0 deletions jira/recipes/jira-delivery-workflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Recipe: Jira delivery workflow (PLAT)

**last_updated:** 2026-08-28
**tags:** eng-information, platform, jira
**audience:** engineers, agents

Use this recipe when starting work, opening a PR, or closing out a ticket. It complements [how-we-work.md](../../docs/how-we-work.md) and is safe to load as agent context.

---

## 1. Pull context before coding

```bash
# Single ticket (markdown for humans/agents)
jira/scripts/jira-context.sh PLAT-XXX

# Raw JSON for tooling
jira/scripts/jira-context.sh PLAT-XXX --json

# Broader snapshot (planning / dependency review)
jira/scripts/jira-plan.sh pull
```

**Read in Jira (or from context output):**

| Check | Why |
|-------|-----|
| Parent **Story** acceptance criteria | Your task should roll up to story outcomes |
| **Epic** (`plat-ref:E0N` label or parent) | Avoid work that conflicts with epic sequencing |
| **Linked issues** | Blockers, duplicates, relates-to |
| **Status** of siblings | Coordinate if another task owns the same surface |

**Agents:** Use `jira-context.sh` output for scope only. Do not paste ticket bodies with customer names into commits.

---

## 2. Branch and PR alignment

| Artifact | Convention |
|----------|------------|
| Branch | `ab/PLAT-XXX/short-topic` or `platform-tools/PLAT-XXX/topic` |
| PR title | `PLAT-XXX: imperative summary` (CI validates) |
| PR body | Jira link, Summary, QA, Validation, Next steps — see [PR template](../../.github/pull_request_template.md) |
| Commits | Prefer `PLAT-XXX: …` in subject when practical |

One **Subtask** ≈ one PR where possible. If scope grows, split a new PLAT ticket rather than overloading the original.

---

## 3. During development — blockers

Create a **blocker** ticket when work cannot proceed without another team, access, or decision.

| Field | Guidance |
|-------|----------|
| **Type** | Task (or Bug if production defect) |
| **Summary** | `Blocker: <what is blocked> — needs <owner/team>` |
| **Link** | `blocks` → the ticket you cannot finish |
| **Labels** | `blocker`, area label (`platform`, `access-docs`, …) |
| **Description** | What you tried, what is missing, **no customer PII** |

Comment on the blocked ticket with the new blocker key. Do **not** mark the original ticket Done while blocked.

---

## 4. At merge — Jira + git hygiene

After the PR merges to `main`:

```bash
# 1. Comment with PR link (no secrets, no customer data)
jira/scripts/jira-comment.sh PLAT-XXX "Merged: https://github.com/totango/platform-tools/pull/N — <one-line summary>"

# 2. Transition in Jira UI (or list transitions for automation later)
# Subtask/Task → Done when PR scope is complete
# Story → Done only when ALL acceptance criteria are met
```

**Completion checklist**

- [ ] PR merged; branch deleted
- [ ] Jira comment links the PR (or documents why not, e.g. doc-only internal change)
- [ ] **Subtask/Task** moved to **Done** if this PR fulfilled it
- [ ] **Story** moved to **Done** only if every AC is satisfied (or split remaining AC to new tasks)
- [ ] **Blocker** tickets updated: close if resolved, or leave open with comment
- [ ] Follow-up work filed as new PLAT tickets (not left as PR comment only)

---

## 5. When to mark what Done

| Level | Mark Done when |
|-------|----------------|
| **Subtask** | Single deliverable shipped (usually one merged PR) |
| **Task** | Phase complete; no open subtasks for this chunk |
| **Story** | All acceptance criteria met and demo-able |
| **Epic** | Exit criteria from [jira-space-overview.md](../../docs/jira-space-overview.md) — typically quarterly review, not every PR |

If you only completed part of a Story, leave the Story **In Progress** and close the Task/Subtask you finished.

---

## 6. Agent quick reference

```text
START → jira-context.sh PLAT-XXX
WORK → branch ab/PLAT-XXX/… ; PR title PLAT-XXX: …
BLOCK → create blocker ticket ; link blocks ; comment
MERGE → jira-comment.sh ; transition Done on correct level
```

See [AGENTS.md](../../AGENTS.md) for repo read order and privacy rules.
55 changes: 55 additions & 0 deletions jira/scripts/jira-comment.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/usr/bin/env bash
# Post a comment on a PLAT issue (e.g. PR merged, blocker note).
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=/dev/null
source "${SCRIPT_DIR}/lib/load-config.sh"
# shellcheck source=/dev/null
source "${SCRIPT_DIR}/lib/jira-api.sh"

usage() {
cat <<'EOF'
jira-comment — add a comment to a PLAT issue

Usage:
jira-comment.sh PLAT-XXX "Comment text"
jira-comment.sh PLAT-XXX --file path/to/comment.md
echo "text" | jira-comment.sh PLAT-XXX --stdin

Use for PR links, merge notes, and blocker updates — not for secrets or customer data.
EOF
}

die() { echo "jira-comment: error: $*" >&2; exit 1; }

KEY="${1:-}"
[[ -n "$KEY" ]] || { usage; exit 2; }
shift

body=""
if [[ "${1:-}" == "--file" ]]; then
[[ -n "${2:-}" ]] || die "missing --file path"
body="$(cat "$2")"
elif [[ "${1:-}" == "--stdin" ]]; then
body="$(cat)"
elif [[ -n "${1:-}" ]]; then
body="$*"
else
usage
exit 2
fi

[[ -n "$body" ]] || die "empty comment"

payload="$(python3 -c '
import json, sys
text = sys.argv[1]
print(json.dumps({"body": {"type": "doc", "version": 1, "content": [
{"type": "paragraph", "content": [{"type": "text", "text": text}]}
]}}))
' "$body")"

result="$(jira_add_comment "$KEY" "$payload")"
comment_id="$(echo "$result" | jq -r '.id // empty')"
echo "Comment added to ${KEY}${comment_id:+ (id ${comment_id})}"
56 changes: 56 additions & 0 deletions jira/scripts/jira-context.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/usr/bin/env bash
# Pull Jira issue context (summary, parent, links, children) for engineers and agents.
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LIB="${SCRIPT_DIR}/lib/jira-api.sh"
FORMATTER="${SCRIPT_DIR}/lib/jira-context.py"

usage() {
cat <<'EOF'
jira-context — fetch PLAT ticket context for planning or agent prompts

Usage:
jira-context.sh PLAT-XXX Markdown summary to stdout
jira-context.sh PLAT-XXX --json Raw issue + children JSON
jira-context.sh --help

Requires jira.email and jira.api_token in ~/.config/platform-tools/config.yaml
EOF
}

die() { echo "jira-context: error: $*" >&2; exit 1; }

KEY="${1:-}"
FORMAT="markdown"
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
usage
exit 0
fi
[[ -n "$KEY" ]] || { usage; exit 2; }
if [[ "${2:-}" == "--json" ]]; then
FORMAT="json"
fi

# shellcheck source=/dev/null
source "${SCRIPT_DIR}/lib/load-config.sh"
# shellcheck source=/dev/null
source "$LIB"

issue="$(jira_get_issue_full "$KEY")"
parent_key="$(echo "$issue" | jq -r '.fields.parent.key // empty')"
children_json='{"issues":[]}'
if [[ -n "$parent_key" ]]; then
children_json="$(jira_search "parent = ${parent_key} ORDER BY rank" 50)"
elif [[ "$(echo "$issue" | jq -r '.fields.issuetype.name')" =~ ^(Story|Epic|Task)$ ]]; then
children_json="$(jira_search "parent = ${KEY} ORDER BY rank" 50)"
fi

payload="$(jq -n --argjson issue "$issue" --argjson children "$children_json" \
'{issue: $issue, children: $children.issues}')"

if [[ "$FORMAT" == "json" ]]; then
echo "$payload" | jq .
else
echo "$payload" | python3 "$FORMATTER" "$KEY"
fi
5 changes: 5 additions & 0 deletions jira/scripts/jira-plan.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ Usage:
jira-plan.sh ack [--run-id ID] Write approval.json (interactive stub)
jira-plan.sh apply [--run-id ID] Refuse without approval.json

Related:
jira-context.sh PLAT-XXX Pull ticket context (markdown)
jira-comment.sh PLAT-XXX "text" Post merge/blocker comment
See jira/recipes/jira-delivery-workflow.md

Local config (~/.config/platform-tools/config.yaml):
jira.email, jira.api_token, jira.site, jira.project
See config/config.yaml.example
Expand Down
Loading
Loading