Portfolio environment analysis
You are given (a) a task prompt.txt describing a business scope, (b) an
answer_template.json (usually under input/payloads/) that is the exact output
contract, and (c) a runtime-access note (environment_access.md) with the base
URL, endpoint list, and query token. Produce one JSON object that satisfies
the template. Nothing else is graded.
0. Orient before you compute
- Read the runtime-access file for the base URL, the allowed endpoints,
and the query token. The token (header
X-Env-Token) is required only
for POST /api/query; GET endpoints are open. Do not hardcode a base URL —
read it each run (<TASK_ENV_BASE_URL> in the prompt maps to that value).
- Read the task
prompt.txt and the answer_template.json together. The
template's required keys, const values, enums, patterns, minItems/
maxItems, and field descriptions are binding instructions — they often
state the ordering and rounding rules verbatim. Echo every const/fixed
scope value straight from the template.
- Identify which of the three task families you are in (see §5). Field names
in the template tell you:
category_*/mix_table → portfolio mix; *_primary_ids/
aging_*/breach_rate → SLA aging; ship_decision/milestone_completion/
readiness_score → release readiness.
1. Pull data — prefer the SQL endpoint
POST /api/query accepts SQLite-flavored read-only SQL over these tables:
work_items, mix_targets, sla_policy, releases, milestones, blockers,
dependencies. Request body {"sql": "..."}, header X-Env-Token: <token>.
Use it to filter and aggregate server-side (GROUP BY, julianday() date math,
ORDER BY) rather than paging the GET lists by hand. GET endpoints return the
same data if you prefer. work_items has ~239 rows — small enough to pull whole
and reason in code. See reference.md for the field dictionary and query recipes.
2. Authoritative vs. stale fields (the #1 recurring trap)
Every family warns about "stale mirror / legacy / export fields." Concretely:
| Use (authoritative) |
Ignore (planted distractor) |
status |
mirror_status |
work_type, labels, title |
legacy_category |
closed_at, created_at, owner, severity, team, product_area, duplicate_of, release_id, milestone_id |
(any field the prompt names as mirror/export) |
Never let mirror_status or legacy_category drive inclusion or classification.
For release truth (family 3), use milestone/status/blocker/dependency records, not
mirror fields.
3. Exclusions & the primary population
Before counting anything, split in-scope records into primary vs excluded:
- Duplicates — exclude if
status = 'Duplicate' OR duplicate_of IS NOT NULL.
These two signals only partly overlap, so test both. When the answer wants
duplicate clusters, group the duplicate records by their duplicate_of target
(that target is the primary_id); duplicates whose target is null have no cluster.
- Cancelled — exclude if
status = 'Cancelled'.
- Distractors — same-scope records that are not primary closed portfolio work
(duplicates, cancelled, wrong-status, or records the prompt calls out). Report
their ids where the template asks (
excluded_*_ids), but never count them in the
primary metrics.
primary = in-scope AND not duplicate AND not cancelled. Report excluded ids in
the template's exclusion fields; they are graded too.
4. Classify work into the four portfolio categories
Categories are exactly: NewFeature, TechDebt, Reliability, Security. Derive
each item's category from authoritative signals (work_type first, then
labels/title); never from legacy_category. Canonical work_type map:
- Feature, Enhancement → NewFeature
- Refactor, Chore, Dependency → TechDebt
- Bug, Incident, Reliability → Reliability
- Security, Compliance → Security
When signals conflict (e.g. a generic work_type but a strong label/title
keyword, or multiple categories in the labels), resolve with the fixed
risk-priority precedence Security > Reliability > TechDebt > NewFeature, using
the keyword sets in reference.md. Apply the same rule to every item consistently.
5. Family playbooks (map each to the template fields)
5a. Portfolio-mix review
Scope = quarter + teams + product area(s) + scope_id.
- Primary population = closed portfolio work in scope:
team ∈ scope teams,
product_area ∈ scope areas, closed_at inside the quarter, and a terminal
completed status (Closed, Deployed, Done, Verified — not Backlog/
In Progress/Review/Reopened/Duplicate/Cancelled). Apply §3 exclusions.
- Classify each (§4);
category_counts are item counts, not story points.
actual_pct = count / total × 100, rounded to 1 dp.
target_pct = the mix_targets row whose scope_id equals the task
scope_id — its new_feature_pct/tech_debt_pct/reliability_pct/
security_pct are fractions; ×100 and round to 1 dp.
gap_pct = actual_pct − target_pct (1 dp). Under-invested / largest-deficit =
most-negative gap(s). Follow the template's action enum and rationale-code logic
(largest negative gap → REBALANCE_CAPACITY; no negative gaps → MAINTAIN; data
conflict → INVESTIGATE_DATA_QUALITY).
included_work_item_ids ordered by closed_at asc, then id asc.
5b. SLA aging / breach review
Scope = teams + as-of date + recent-closed-window (days) + SLA categories
(Reliability, Security).
- Primary population = in-scope, primary (§3) items classified into the SLA
categories, whose
closed_at falls in [as_of − window_days, as_of].
- SLA deadline for an item =
created_at + days_to_due[severity] from
/api/sla-policy (currently S1=3, S2=10, S3=21, S4=45 days — re-fetch, don't
assume). due_at on the item deviates from policy and behaves like a mirror
field — treat the policy as authoritative unless the task explicitly names
due_at. Overdue / breach = resolution later than the deadline
(closed_at > deadline; for unresolved items, as_of > deadline).
- Aging = days overdue (
closed_at − deadline, floored at 0), bucketed per the
template's bucket labels; or by severity when the template keys by S1–S4.
- Hotspot = the (team, owner) pair with the most overdue primary records; missing
owner → the template's sentinel (e.g.
UNASSIGNED). missing_owner_ids =
primary items with null owner.
- Duplicate clusters (§3) reported but excluded from primary counts.
breach_rate = overdue_primary / included_primary, rounded to 3 dp.
Escalation queue = overdue primary in priority order (severity, then age/due).
5c. Release-readiness assessment
Scope = one release_id.
- Release work items =
work_items.release_id = <release>. Milestones =
milestones.release_id = <release>; each milestone's primary items = release
work items with that milestone_id (apply §3; use authoritative status).
- Per milestone:
complete_primary = completed primary (terminal status),
primary_total = primary denominator, completion_pct = ratio × 100 (1 dp).
Sort milestone_completion by milestone_id asc.
gating_work_item_ids = non-complete release work items gating readiness,
sorted asc, unique.
blocker_cause_counts = count unresolved (resolved_at IS NULL)
high-impact (severity ∈ {Critical, High}) blockers for the release,
keyed by the exact cause string.
critical_dependency_chains = ordered work-item-id paths from a blocked release
work item to a non-complete dependency, walking dependencies (relations like
blocks-release-readiness, security-review-required, validation-required,
audit-evidence-required). Sort paths lexicographically by the full path.
readiness_score = completed primary / primary denominator, 3 dp. ship_decision
∈ {SHIP, SHIP_WITH_WATCH, NO_SHIP} per the template's threshold logic (unresolved
high-impact blockers / low readiness → NO_SHIP; clean → SHIP; in-between → WATCH).
6. Determinism & precision (apply to every answer)
- Ordering: id lists lexicographic string sort unless told otherwise; portfolio
included_work_item_ids by closed_at asc then id asc; teams/product areas
alphabetical (or the template's stated order); duplicate clusters by primary_id
with duplicate_ids sorted; dependency paths lexicographic. Note string sort puts
digits before letters (WI-24024-050 < WI-24024-S004).
- Precision: percentages /
*_pct → 1 dp; breach_rate / readiness_score /
other rates → 3 dp. gap = actual − target. Round only at output.
- Counts are item counts, never story points.
7. Before you submit
- Output is a single JSON object, no prose, no markdown fences, nothing outside it.
- Every
required key present; with additionalProperties: false add no extra
keys; respect every const/enum/pattern/minItems/maxItems.
- Re-verify each list's sort order and each number's decimal places.
- Sanity check: category counts sum to
total_included; percentages ~100; excluded
∪ included covers the in-scope records with no overlap.
- Spot-check 2–3 records end-to-end (classification, exclusion, breach) against the
raw fields before trusting the aggregate.
See reference.md for the field dictionary, full enum vocabularies, the
classification keyword sets, and copy-paste SQL recipes.
1---2name: portfolio-env-analysis-23description: Answer engineering-portfolio review tasks that run against the shared read-only work-item HTTP environment (base URL + endpoints + query token supplied in an environment_access.md / runtime-access file). Covers the three recurring task families: (1) quarterly portfolio-mix reviews vs. a mix_targets row, (2) SLA aging / breach reviews for Reliability+Security work, and (3) release-readiness ship decisions from releases/milestones/blockers/dependencies. Use whenever a prompt references work items, mix targets, an SLA policy, releases, milestones, blockers, or dependencies and asks for a single JSON answer matching an input/payloads/answer_template.json. Encodes the shared conventions: which fields are authoritative vs. stale, how to exclude duplicates/cancelled/ distractors, how to classify work into the four portfolio categories, and the ordering + precision rules that make answers deterministic.4---56# Portfolio environment analysis78You are given (a) a task `prompt.txt` describing a business scope, (b) an9`answer_template.json` (usually under `input/payloads/`) that is the exact output10contract, and (c) a runtime-access note (`environment_access.md`) with the base11URL, endpoint list, and query token. Produce **one JSON object** that satisfies12the template. Nothing else is graded.1314## 0. Orient before you compute15161. Read the runtime-access file for the **base URL**, the **allowed endpoints**,17 and the **query token**. The token (header `X-Env-Token`) is required *only*18 for `POST /api/query`; GET endpoints are open. Do not hardcode a base URL —19 read it each run (`<TASK_ENV_BASE_URL>` in the prompt maps to that value).202. Read the task `prompt.txt` and the `answer_template.json` **together**. The21 template's required keys, `const` values, `enum`s, `pattern`s, `minItems`/22 `maxItems`, and field `description`s are binding instructions — they often23 state the ordering and rounding rules verbatim. Echo every `const`/fixed24 scope value straight from the template.253. Identify which of the three **task families** you are in (see §5). Field names26 in the template tell you: `category_*`/`mix_table` → portfolio mix; `*_primary_ids`/27 `aging_*`/`breach_rate` → SLA aging; `ship_decision`/`milestone_completion`/28 `readiness_score` → release readiness.2930## 1. Pull data — prefer the SQL endpoint3132`POST /api/query` accepts SQLite-flavored read-only SQL over these tables:33`work_items`, `mix_targets`, `sla_policy`, `releases`, `milestones`, `blockers`,34`dependencies`. Request body `{"sql": "..."}`, header `X-Env-Token: <token>`.35Use it to filter and aggregate server-side (`GROUP BY`, `julianday()` date math,36`ORDER BY`) rather than paging the GET lists by hand. GET endpoints return the37same data if you prefer. `work_items` has ~239 rows — small enough to pull whole38and reason in code. See `reference.md` for the field dictionary and query recipes.3940## 2. Authoritative vs. stale fields (the #1 recurring trap)4142Every family warns about "stale mirror / legacy / export fields." Concretely:4344| Use (authoritative) | Ignore (planted distractor) |45|-----------------------------------------|-----------------------------------------|46| `status` | `mirror_status` |47| `work_type`, `labels`, `title` | `legacy_category` |48| `closed_at`, `created_at`, `owner`, `severity`, `team`, `product_area`, `duplicate_of`, `release_id`, `milestone_id` | (any field the prompt names as mirror/export) |4950Never let `mirror_status` or `legacy_category` drive inclusion or classification.51For release truth (family 3), use milestone/status/blocker/dependency records, not52mirror fields.5354## 3. Exclusions & the primary population5556Before counting anything, split in-scope records into **primary** vs **excluded**:5758- **Duplicates** — exclude if `status = 'Duplicate'` **OR** `duplicate_of IS NOT NULL`.59 These two signals only partly overlap, so test both. When the answer wants60 *duplicate clusters*, group the duplicate records by their `duplicate_of` target61 (that target is the `primary_id`); duplicates whose target is null have no cluster.62- **Cancelled** — exclude if `status = 'Cancelled'`.63- **Distractors** — same-scope records that are not primary closed portfolio work64 (duplicates, cancelled, wrong-status, or records the prompt calls out). Report65 their ids where the template asks (`excluded_*_ids`), but never count them in the66 primary metrics.6768`primary = in-scope AND not duplicate AND not cancelled`. Report excluded ids in69the template's exclusion fields; they are graded too.7071## 4. Classify work into the four portfolio categories7273Categories are exactly: **NewFeature, TechDebt, Reliability, Security**. Derive74each item's category from authoritative signals (`work_type` first, then75`labels`/`title`); never from `legacy_category`. Canonical `work_type` map:7677- Feature, Enhancement → **NewFeature**78- Refactor, Chore, Dependency → **TechDebt**79- Bug, Incident, Reliability → **Reliability**80- Security, Compliance → **Security**8182When signals **conflict** (e.g. a generic `work_type` but a strong label/title83keyword, or multiple categories in the labels), resolve with the fixed84risk-priority precedence **Security > Reliability > TechDebt > NewFeature**, using85the keyword sets in `reference.md`. Apply the same rule to every item consistently.8687## 5. Family playbooks (map each to the template fields)8889### 5a. Portfolio-mix review90Scope = quarter + teams + product area(s) + `scope_id`.911. Primary population = closed portfolio work in scope: `team` ∈ scope teams,92 `product_area` ∈ scope areas, `closed_at` inside the quarter, and a terminal93 completed `status` (`Closed`, `Deployed`, `Done`, `Verified` — **not** Backlog/94 In Progress/Review/Reopened/Duplicate/Cancelled). Apply §3 exclusions.952. Classify each (§4); `category_counts` are **item counts, not story points**.963. `actual_pct` = count / total × 100, rounded to **1 dp**.974. `target_pct` = the `mix_targets` row **whose `scope_id` equals the task98 `scope_id`** — its `new_feature_pct`/`tech_debt_pct`/`reliability_pct`/99 `security_pct` are fractions; ×100 and round to 1 dp.1005. `gap_pct = actual_pct − target_pct` (1 dp). Under-invested / largest-deficit =101 most-negative gap(s). Follow the template's action enum and rationale-code logic102 (largest negative gap → REBALANCE_CAPACITY; no negative gaps → MAINTAIN; data103 conflict → INVESTIGATE_DATA_QUALITY).1046. `included_work_item_ids` ordered by `closed_at` asc, then `id` asc.105106### 5b. SLA aging / breach review107Scope = teams + as-of date + recent-closed-window (days) + SLA categories108(Reliability, Security).1091. Primary population = in-scope, primary (§3) items classified into the SLA110 categories, whose `closed_at` falls in `[as_of − window_days, as_of]`.1112. **SLA deadline** for an item = `created_at + days_to_due[severity]` from112 `/api/sla-policy` (currently S1=3, S2=10, S3=21, S4=45 days — re-fetch, don't113 assume). `due_at` on the item deviates from policy and behaves like a mirror114 field — treat the **policy** as authoritative unless the task explicitly names115 `due_at`. **Overdue / breach** = resolution later than the deadline116 (`closed_at > deadline`; for unresolved items, `as_of > deadline`).1173. Aging = days overdue (`closed_at − deadline`, floored at 0), bucketed per the118 template's bucket labels; or by severity when the template keys by S1–S4.1194. Hotspot = the (team, owner) pair with the most overdue primary records; missing120 owner → the template's sentinel (e.g. `UNASSIGNED`). `missing_owner_ids` =121 primary items with null `owner`.1225. Duplicate clusters (§3) reported but excluded from primary counts.1236. `breach_rate = overdue_primary / included_primary`, rounded to **3 dp**.124 Escalation queue = overdue primary in priority order (severity, then age/due).125126### 5c. Release-readiness assessment127Scope = one `release_id`.1281. Release work items = `work_items.release_id = <release>`. Milestones =129 `milestones.release_id = <release>`; each milestone's primary items = release130 work items with that `milestone_id` (apply §3; use authoritative `status`).1312. Per milestone: `complete_primary` = completed primary (terminal status),132 `primary_total` = primary denominator, `completion_pct` = ratio × 100 (1 dp).133 Sort `milestone_completion` by `milestone_id` asc.1343. `gating_work_item_ids` = non-complete release work items gating readiness,135 sorted asc, unique.1364. `blocker_cause_counts` = count **unresolved** (`resolved_at IS NULL`)137 **high-impact** (`severity ∈ {Critical, High}`) blockers for the release,138 keyed by the **exact** `cause` string.1395. `critical_dependency_chains` = ordered work-item-id paths from a blocked release140 work item to a non-complete dependency, walking `dependencies` (relations like141 `blocks-release-readiness`, `security-review-required`, `validation-required`,142 `audit-evidence-required`). Sort paths lexicographically by the full path.1436. `readiness_score` = completed primary / primary denominator, 3 dp. `ship_decision`144 ∈ {SHIP, SHIP_WITH_WATCH, NO_SHIP} per the template's threshold logic (unresolved145 high-impact blockers / low readiness → NO_SHIP; clean → SHIP; in-between → WATCH).146147## 6. Determinism & precision (apply to every answer)148149- **Ordering**: id lists lexicographic string sort unless told otherwise; portfolio150 `included_work_item_ids` by `closed_at` asc then `id` asc; teams/product areas151 alphabetical (or the template's stated order); duplicate clusters by `primary_id`152 with `duplicate_ids` sorted; dependency paths lexicographic. Note string sort puts153 digits before letters (`WI-24024-050` < `WI-24024-S004`).154- **Precision**: percentages / `*_pct` → 1 dp; `breach_rate` / `readiness_score` /155 other rates → 3 dp. `gap = actual − target`. Round only at output.156- **Counts** are item counts, never story points.157158## 7. Before you submit159160- Output is a **single JSON object**, no prose, no markdown fences, nothing outside it.161- Every `required` key present; with `additionalProperties: false` add **no** extra162 keys; respect every `const`/`enum`/`pattern`/`minItems`/`maxItems`.163- Re-verify each list's sort order and each number's decimal places.164- Sanity check: category counts sum to `total_included`; percentages ~100; excluded165 ∪ included covers the in-scope records with no overlap.166- Spot-check 2–3 records end-to-end (classification, exclusion, breach) against the167 raw fields before trusting the aggregate.168169See `reference.md` for the field dictionary, full enum vocabularies, the170classification keyword sets, and copy-paste SQL recipes.