Portfolio Work-Item Analysis
This skill handles a family of analysis tasks against one shared environment of
work items. Every task gives you a prompt.txt and an answer_template.json
(the JSON Schema contract) and asks for a single JSON object, no prose.
Three archetypes recur:
- Portfolio mix review — closed-work category mix vs. a target mix.
- SLA aging audit — overdue / aging analysis of reliability & security work.
- Release-readiness assessment — ship decision for one release.
The hard parts are never the math. They are (a) selecting the right records and
excluding duplicates / cancelled / distractors, and (b) using authoritative
fields while ignoring stale mirror/export fields. Get those right and the rest
follows.
Always-on workflow
Run these steps for every task, in order.
- Read the contract. Open
prompt.txt and input/payloads/answer_template.json.
The template is the spec: its required list, enum values,
additionalProperties: false, const fields, ordering notes, and precision
notes are mandatory. Match it exactly — no extra keys, no missing keys.
- Extract scope. Pull teams, quarter or as-of date, product area(s),
categories, window length, release id, and
scope_id from the prompt. The
scope_id selects the target-mix row; do not invent one.
- Get access. Read
environment_access.md for the base URL, the
X-Env-Token value, and the allowed endpoint list. Use only that file for
network access — do not hardcode URLs or tokens. See
references/access_and_query.md for HTTP and SQL-query mechanics.
- Fetch what you need. Pull the endpoints / tables the archetype requires
(see
references/data_model.md for the schema). Prefer the REST endpoints for
whole-collection reads; use POST /api/query only for filtered/aggregated SQL.
- Apply universal data hygiene (below) to separate primary work from
duplicates, cancelled, and distractors.
- Compute per the archetype rules below.
- Emit one JSON object matching the template. No prose. Respect ordering and
precision. Validate mentally against the schema before finishing.
Universal data-hygiene rules (apply to every archetype)
These rules are the substance of the skill. They are derived from the actual
environment schema — see references/data_model.md for field vocabularies.
Use authoritative fields; ignore stale ones
- Authoritative (use these):
status, work_type, owner, team,
product_area, created_at, due_at, closed_at, severity, priority,
labels, release_id, milestone_id, duplicate_of.
- Stale — never use as truth:
mirror_status — a stale export of status. Ignore for any status/closed
decision. (The prompt calls this out as "stale mirror fields"; some templates
require ignored_mirror_status_... = true.)
legacy_category — a stale category tag. Ignore for portfolio category;
classify from work_type / labels / title using the conventions below.
Status → class
Map status (authoritative) as follows. closed_at is populated exactly for the
resolved states, so it is a reliable secondary signal.
| status |
closed_at |
class |
Closed, Done, Deployed, Verified |
set |
completed / terminal (closed work) |
Cancelled |
set |
excluded — cancelled |
Duplicate |
set |
excluded — duplicate |
Backlog, In Progress, Review, Reopened |
null |
open / active |
Primary vs duplicate
- A record is a duplicate (non-primary) when
duplicate_of IS NOT NULL
(it points at its canonical primary) — primary signal — or status = "Duplicate"
(corroborating signal). Treat either as a duplicate.
- Primary records (the ones you count) are: not a duplicate and not
cancelled (
status not in Duplicate/Cancelled and duplicate_of is null).
Note the orphan edge case: status = "Duplicate" with duplicate_of null has
no primary to cluster under — exclude it from primary counts; it cannot appear
in a cluster.
- Duplicate clusters: for every record that has
duplicate_of set,
primary_id = duplicate_of (the canonical work item it points at) and the
record's own id goes into that cluster's duplicate_ids. Duplicates are
reported in duplicate_clusters but never counted as primary work.
Distractors
Some records match the scope superficially (same team / area / quarter) but are
not primary closed portfolio work — they are open, duplicates, or cancelled.
Exclude them from the primary set and, where the template asks
(excluded_distractor_ids, exclusion flags), list them.
Id discipline
- Use each included id exactly once across the answer.
- Sort id lists as the template specifies (usually lexicographically / ascending).
- Sort team / product-area lists alphabetically unless the template fixes an order.
Precision
- Percentages / percentage points: 1 decimal place.
- Rates (breach rate, readiness score): 3 decimal places.
gap_pct = actual_pct − target_pct (percentage points).
Portfolio category classification
Four categories: NewFeature, TechDebt, Reliability, Security. Each
included item maps to exactly one. Resolve conflicting work_type / labels
/ title signals with this priority (a higher-priority signal wins):
- Security —
work_type = Security or Compliance; labels/title contain
security, cve, auth, encryption.
- Reliability —
work_type = Reliability or Incident; labels/title contain
reliability, incident, outage, latency, flaky.
- TechDebt —
work_type = Refactor, Bug, or Chore; labels/title contain
cleanup, refactor, tech-debt, deprecate, migrate.
- NewFeature —
work_type = Feature or Enhancement; labels/title contain
feature, rollout.
When signals conflict, prefer an explicit portfolio-category label over
work_type, and work_type over title keywords. Apply the rule consistently
across all items. (Never use legacy_category.)
Archetype A — Portfolio mix review
Examples: closed-work mix readout for a quarter, scope, and set of teams.
- Select in-scope closed primary work:
closed_at within the stated quarter,
team in the scope teams, product_area in the scope areas, status is a
completed terminal (Closed/Done/Deployed/Verified), and the record is
primary (not duplicate, not cancelled).
- Exclude & report: duplicates and cancelled in-scope records go to the
exclusion flags / distractor list the template defines.
- Classify each included item into one portfolio category.
- Counts are item counts, not story points.
total_included = sum(counts).
- actual_pct =
count / total_included × 100, 1 decimal.
- Target: read the
mix_targets row whose scope_id matches the prompt's
scope_id. Target fractions are 0–1; multiply by 100 for percentage points.
- gap_pct = actual − target, 1 decimal. Build the gap/mix table in the fixed
order
NewFeature, TechDebt, Reliability, Security.
- Under-invested / largest deficit = category with the most negative
gap_pct. List under-invested categories ordered most-negative → least-negative.
- Follow-up action:
REBALANCE_CAPACITY when there is a negative gap (point at the largest
deficit category; owner_team = the scope team that owns that category's
work).
MAINTAIN_CURRENT_MIX when no gap is negative (rationale_code = NO_NEGATIVE_GAPS).
INVESTIGATE_DATA_QUALITY when the data conflicts (rationale_code = DATA_CONFLICT).
- Otherwise
rationale_code = LARGEST_NEGATIVE_GAP.
- Ordering:
included_work_item_ids by closed_at ascending, then id
ascending. Teams / product areas alphabetical (or the order the template fixes).
Archetype B — SLA aging audit
Examples: reliability & security SLA aging for given teams, as-of date, window.
- Primary SLA population (
included_primary_ids): primary work (not
duplicate, not cancelled) for the scope teams whose portfolio category is in
the SLA categories (typically Security and Reliability). Sort ascending.
- SLA due date =
created_at + sla_policy.days_to_due for the item's
severity (S1=3, S2=10, S3=21, S4=45 days — read sla_policy to confirm).
- Overdue (
overdue_primary_ids): an item is overdue when its SLA due date
has passed relative to the reference date — as_of for still-open items
(closed_at null), closed_at for items that have closed. Reference date >
SLA due date ⇒ overdue. Subset of included primary ids; sort ascending.
- Aging buckets
0-3, 4-7, 8-14, 15-30, 31+ (days). Age = elapsed
days from created_at to the reference date (as_of for open, closed_at for
closed). Bucket each primary item; confirm the exact population (included
primary vs. overdue) from the prompt + template.
- breach_rate / sla_breach_rate =
overdue_primary_count / included_primary_count, 3 decimals.
- By-severity (
overdue_counts_by_severity): count overdue primary items per
S1/S2/S3/S4.
- Team / owner hotspot: count overdue primary items per team, and per
(team, owner) pair.
top_hotspot = the pair with the most overdue; owner =
"UNASSIGNED" when owner is null. Teams listed alphabetically.
- Escalation queue (
escalation_queue_ids): overdue primary ids in priority
order for follow-up — sort by priority ascending (1 = highest), then
severity (S1 > S2 > S3 > S4), then due_at ascending, then id ascending.
- missing_owner_ids: included primary ids with
owner null, sorted ascending.
- duplicate_clusters: grouped by
primary_id = duplicate_of,
duplicate_ids sorted ascending, clusters sorted by primary_id.
Archetype C — Release-readiness assessment
Example: ship decision for a single release id.
- Release work items =
work_items with release_id = the release under
review. Restrict to primary (non-duplicate). Use authoritative status
only — never mirror_status — as release truth.
- Milestone completion: for each milestone in the release (from
milestones where release_id matches), primary_total = primary release
work items assigned to that milestone_id; complete_primary = those whose
status is a completed terminal (Closed/Done/Deployed/Verified);
completion_pct = complete/total × 100, 1 decimal. Sort by milestone_id
ascending.
- Gating work item ids = non-complete primary release work items (status not
a completed terminal), sorted ascending, de-duplicated.
- Blocker cause counts: from
blockers for this release, count only
unresolved (resolved_at null) high-impact (severity High or
Critical) blockers, keyed by the exact cause string.
- Critical dependency chains: from
dependencies, follow depends_on_id
edges starting from blocked release work items. A chain is an ordered path of
work-item ids from a blocked release work item to a dependency that is
non-complete (status not a completed terminal). relation values such as
blocks-release-readiness, validation-required, security-review-required,
audit-evidence-required, implementation-dependency, depends-on describe
the edge; include edges that gate readiness. Sort chains lexicographically by
the full id path.
- readiness_score =
completed_primary_release_work / primary_release_denominator,
3 decimals.
- ship_decision:
SHIP — readiness is complete, no gating items, no unresolved high-impact
blockers, no non-complete critical dependencies.
SHIP_WITH_WATCH — broadly ready but with watchable risk (e.g., minor
blockers or a small number of non-critical non-complete items).
NO_SHIP — open gating work, unresolved high-impact blockers, or broken
critical dependency chains.
Output discipline (all archetypes)
- Return one JSON object matching
answer_template.json exactly: every
required field present, values within enum, no keys beyond the schema
(additionalProperties: false), const fields equal to their fixed value.
- No prose outside the JSON.
- Apply the ordering the template specifies; where it does not, sort ids
ascending and names alphabetically.
- Round as specified (percentages 1 decimal, rates 3 decimals).
- Use each included id exactly once; de-duplicate id lists unless told otherwise.
Final checks before submitting
- Did I use
status (not mirror_status) for every closed/overdue decision?
- Did I classify from
work_type/labels/title (not legacy_category)?
- Are duplicates excluded from counts and only reported in clusters?
- Are cancelled and distractor records excluded and listed where required?
- Does every list match the template's required ordering?
- Are all percentages 1 decimal and all rates 3 decimals?
- Does the object validate against the schema (required, enums, no extra keys)?
1---2name: portfolio-work-item-analysis3description: Analyze the shared portfolio work-item environment to produce portfolio-mix reviews, SLA-aging audits, and release-readiness assessments. Use whenever a task points at the portfolio environment (work items, mix targets, SLA policy, releases, milestones, dependencies, blockers) and asks for a single JSON answer. Read this before fetching any data.4---56# Portfolio Work-Item Analysis78This skill handles a family of analysis tasks against one shared environment of9work items. Every task gives you a `prompt.txt` and an `answer_template.json`10(the JSON Schema contract) and asks for **a single JSON object, no prose**.1112Three archetypes recur:1314- **Portfolio mix review** — closed-work category mix vs. a target mix.15- **SLA aging audit** — overdue / aging analysis of reliability & security work.16- **Release-readiness assessment** — ship decision for one release.1718The hard parts are never the math. They are (a) selecting the *right* records and19excluding duplicates / cancelled / distractors, and (b) using **authoritative**20fields while ignoring stale mirror/export fields. Get those right and the rest21follows.2223## Always-on workflow2425Run these steps for every task, in order.26271. **Read the contract.** Open `prompt.txt` and `input/payloads/answer_template.json`.28 The template is the spec: its `required` list, `enum` values,29 `additionalProperties: false`, `const` fields, ordering notes, and precision30 notes are mandatory. Match it exactly — no extra keys, no missing keys.312. **Extract scope.** Pull teams, quarter or as-of date, product area(s),32 categories, window length, release id, and `scope_id` from the prompt. The33 `scope_id` selects the target-mix row; do not invent one.343. **Get access.** Read `environment_access.md` for the base URL, the35 `X-Env-Token` value, and the allowed endpoint list. Use **only** that file for36 network access — do not hardcode URLs or tokens. See37 `references/access_and_query.md` for HTTP and SQL-query mechanics.384. **Fetch what you need.** Pull the endpoints / tables the archetype requires39 (see `references/data_model.md` for the schema). Prefer the REST endpoints for40 whole-collection reads; use `POST /api/query` only for filtered/aggregated SQL.415. **Apply universal data hygiene** (below) to separate primary work from42 duplicates, cancelled, and distractors.436. **Compute** per the archetype rules below.447. **Emit one JSON object** matching the template. No prose. Respect ordering and45 precision. Validate mentally against the schema before finishing.4647## Universal data-hygiene rules (apply to every archetype)4849These rules are the substance of the skill. They are derived from the actual50environment schema — see `references/data_model.md` for field vocabularies.5152### Use authoritative fields; ignore stale ones53- **Authoritative** (use these): `status`, `work_type`, `owner`, `team`,54 `product_area`, `created_at`, `due_at`, `closed_at`, `severity`, `priority`,55 `labels`, `release_id`, `milestone_id`, `duplicate_of`.56- **Stale — never use as truth**:57 - `mirror_status` — a stale export of status. Ignore for any status/closed58 decision. (The prompt calls this out as "stale mirror fields"; some templates59 require `ignored_mirror_status_... = true`.)60 - `legacy_category` — a stale category tag. Ignore for portfolio category;61 classify from `work_type` / `labels` / `title` using the conventions below.6263### Status → class64Map `status` (authoritative) as follows. `closed_at` is populated exactly for the65resolved states, so it is a reliable secondary signal.6667| status | closed_at | class |68|--------------------------------|-----------|------------------------------------|69| `Closed`, `Done`, `Deployed`, `Verified` | set | **completed / terminal** (closed work) |70| `Cancelled` | set | **excluded** — cancelled |71| `Duplicate` | set | **excluded** — duplicate |72| `Backlog`, `In Progress`, `Review`, `Reopened` | null | **open / active** |7374### Primary vs duplicate75- A record is a **duplicate (non-primary)** when `duplicate_of IS NOT NULL`76 (it points at its canonical primary) — primary signal — **or** `status = "Duplicate"`77 (corroborating signal). Treat either as a duplicate.78- **Primary records** (the ones you count) are: not a duplicate **and** not79 cancelled (`status` not in `Duplicate`/`Cancelled` and `duplicate_of` is null).80 Note the orphan edge case: `status = "Duplicate"` with `duplicate_of` null has81 no primary to cluster under — exclude it from primary counts; it cannot appear82 in a cluster.83- **Duplicate clusters**: for every record that has `duplicate_of` set,84 `primary_id = duplicate_of` (the canonical work item it points at) and the85 record's own `id` goes into that cluster's `duplicate_ids`. Duplicates are86 **reported** in `duplicate_clusters` but **never counted** as primary work.8788### Distractors89Some records match the scope superficially (same team / area / quarter) but are90not primary closed portfolio work — they are open, duplicates, or cancelled.91Exclude them from the primary set and, where the template asks92(`excluded_distractor_ids`, exclusion flags), list them.9394### Id discipline95- Use each included id **exactly once** across the answer.96- Sort id lists as the template specifies (usually lexicographically / ascending).97- Sort team / product-area lists alphabetically unless the template fixes an order.9899### Precision100- Percentages / percentage points: **1 decimal place**.101- Rates (breach rate, readiness score): **3 decimal places**.102- `gap_pct = actual_pct − target_pct` (percentage points).103104## Portfolio category classification105106Four categories: `NewFeature`, `TechDebt`, `Reliability`, `Security`. Each107included item maps to **exactly one**. Resolve conflicting `work_type` / `labels`108/ `title` signals with this priority (a higher-priority signal wins):1091101. **Security** — `work_type = Security` or `Compliance`; labels/title contain111 `security`, `cve`, `auth`, `encryption`.1122. **Reliability** — `work_type = Reliability` or `Incident`; labels/title contain113 `reliability`, `incident`, `outage`, `latency`, `flaky`.1143. **TechDebt** — `work_type = Refactor`, `Bug`, or `Chore`; labels/title contain115 `cleanup`, `refactor`, `tech-debt`, `deprecate`, `migrate`.1164. **NewFeature** — `work_type = Feature` or `Enhancement`; labels/title contain117 `feature`, `rollout`.118119When signals conflict, prefer an explicit portfolio-category **label** over120`work_type`, and `work_type` over title keywords. Apply the rule consistently121across all items. (Never use `legacy_category`.)122123## Archetype A — Portfolio mix review124125*Examples: closed-work mix readout for a quarter, scope, and set of teams.*1261271. **Select in-scope closed primary work**: `closed_at` within the stated quarter,128 `team` in the scope teams, `product_area` in the scope areas, status is a129 completed terminal (`Closed`/`Done`/`Deployed`/`Verified`), and the record is130 primary (not duplicate, not cancelled).1312. **Exclude & report**: duplicates and cancelled in-scope records go to the132 exclusion flags / distractor list the template defines.1333. **Classify** each included item into one portfolio category.1344. **Counts are item counts, not story points.** `total_included = sum(counts)`.1355. **actual_pct** = `count / total_included × 100`, 1 decimal.1366. **Target**: read the `mix_targets` row whose `scope_id` matches the prompt's137 `scope_id`. Target fractions are 0–1; multiply by 100 for percentage points.1387. **gap_pct** = actual − target, 1 decimal. Build the gap/mix table in the fixed139 order `NewFeature, TechDebt, Reliability, Security`.1408. **Under-invested / largest deficit** = category with the most negative141 `gap_pct`. List under-invested categories ordered most-negative → least-negative.1429. **Follow-up action**:143 - `REBALANCE_CAPACITY` when there is a negative gap (point at the largest144 deficit category; `owner_team` = the scope team that owns that category's145 work).146 - `MAINTAIN_CURRENT_MIX` when no gap is negative (`rationale_code = NO_NEGATIVE_GAPS`).147 - `INVESTIGATE_DATA_QUALITY` when the data conflicts (`rationale_code = DATA_CONFLICT`).148 - Otherwise `rationale_code = LARGEST_NEGATIVE_GAP`.14910. **Ordering**: `included_work_item_ids` by `closed_at` ascending, then `id`150 ascending. Teams / product areas alphabetical (or the order the template fixes).151152## Archetype B — SLA aging audit153154*Examples: reliability & security SLA aging for given teams, as-of date, window.*1551561. **Primary SLA population** (`included_primary_ids`): primary work (not157 duplicate, not cancelled) for the scope teams whose portfolio category is in158 the SLA categories (typically `Security` and `Reliability`). Sort ascending.1592. **SLA due date** = `created_at + sla_policy.days_to_due` for the item's160 `severity` (S1=3, S2=10, S3=21, S4=45 days — read `sla_policy` to confirm).1613. **Overdue** (`overdue_primary_ids`): an item is overdue when its SLA due date162 has passed relative to the reference date — `as_of` for still-open items163 (`closed_at` null), `closed_at` for items that have closed. Reference date >164 SLA due date ⇒ overdue. Subset of included primary ids; sort ascending.1654. **Aging buckets** `0-3`, `4-7`, `8-14`, `15-30`, `31+` (days). Age = elapsed166 days from `created_at` to the reference date (as_of for open, closed_at for167 closed). Bucket each primary item; confirm the exact population (included168 primary vs. overdue) from the prompt + template.1695. **breach_rate / sla_breach_rate** = `overdue_primary_count /170 included_primary_count`, 3 decimals.1716. **By-severity** (`overdue_counts_by_severity`): count overdue primary items per172 `S1/S2/S3/S4`.1737. **Team / owner hotspot**: count overdue primary items per team, and per174 (team, owner) pair. `top_hotspot` = the pair with the most overdue; `owner` =175 `"UNASSIGNED"` when `owner` is null. Teams listed alphabetically.1768. **Escalation queue** (`escalation_queue_ids`): overdue primary ids in priority177 order for follow-up — sort by `priority` ascending (1 = highest), then178 `severity` (S1 > S2 > S3 > S4), then `due_at` ascending, then `id` ascending.1799. **missing_owner_ids**: included primary ids with `owner` null, sorted ascending.18010. **duplicate_clusters**: grouped by `primary_id = duplicate_of`,181 `duplicate_ids` sorted ascending, clusters sorted by `primary_id`.182183## Archetype C — Release-readiness assessment184185*Example: ship decision for a single release id.*1861871. **Release work items** = `work_items` with `release_id` = the release under188 review. Restrict to **primary** (non-duplicate). Use authoritative `status`189 only — never `mirror_status` — as release truth.1902. **Milestone completion**: for each milestone in the release (from191 `milestones` where `release_id` matches), `primary_total` = primary release192 work items assigned to that `milestone_id`; `complete_primary` = those whose193 status is a completed terminal (`Closed`/`Done`/`Deployed`/`Verified`);194 `completion_pct` = complete/total × 100, 1 decimal. Sort by `milestone_id`195 ascending.1963. **Gating work item ids** = non-complete primary release work items (status not197 a completed terminal), sorted ascending, de-duplicated.1984. **Blocker cause counts**: from `blockers` for this release, count only199 **unresolved** (`resolved_at` null) **high-impact** (`severity` High or200 Critical) blockers, keyed by the **exact** `cause` string.2015. **Critical dependency chains**: from `dependencies`, follow `depends_on_id`202 edges starting from blocked release work items. A chain is an ordered path of203 work-item ids from a blocked release work item to a dependency that is204 non-complete (status not a completed terminal). `relation` values such as205 `blocks-release-readiness`, `validation-required`, `security-review-required`,206 `audit-evidence-required`, `implementation-dependency`, `depends-on` describe207 the edge; include edges that gate readiness. Sort chains lexicographically by208 the full id path.2096. **readiness_score** = `completed_primary_release_work / primary_release_denominator`,210 3 decimals.2117. **ship_decision**:212 - `SHIP` — readiness is complete, no gating items, no unresolved high-impact213 blockers, no non-complete critical dependencies.214 - `SHIP_WITH_WATCH` — broadly ready but with watchable risk (e.g., minor215 blockers or a small number of non-critical non-complete items).216 - `NO_SHIP` — open gating work, unresolved high-impact blockers, or broken217 critical dependency chains.218219## Output discipline (all archetypes)220221- Return **one JSON object** matching `answer_template.json` exactly: every222 `required` field present, values within `enum`, no keys beyond the schema223 (`additionalProperties: false`), `const` fields equal to their fixed value.224- **No prose outside the JSON.**225- Apply the ordering the template specifies; where it does not, sort ids226 ascending and names alphabetically.227- Round as specified (percentages 1 decimal, rates 3 decimals).228- Use each included id exactly once; de-duplicate id lists unless told otherwise.229230## Final checks before submitting231232- Did I use `status` (not `mirror_status`) for every closed/overdue decision?233- Did I classify from `work_type`/`labels`/`title` (not `legacy_category`)?234- Are duplicates excluded from counts and only reported in clusters?235- Are cancelled and distractor records excluded and listed where required?236- Does every list match the template's required ordering?237- Are all percentages 1 decimal and all rates 3 decimals?238- Does the object validate against the schema (required, enums, no extra keys)?