| name | portfolio-work-item-analysis |
| description | 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. |
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 .
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).
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)?