Skill — Credit-Risk / Lending-Committee Answer Generation (task_group_011)
Transferable method for producing committee-ready JSON answers against the
shared credit-office public REST API. Distilled from five fully-worked train
tasks (Redwood regrade, Lakeview allocation, Civic NC segment posture, Summit
watch-list, Harbor competing-CRE) and cross-checked against live API data.
0. Environment & ground rules
- Base URL:
<remote-env-url>. Read-only GET only. No auth.
- Always start by reading
/api/policies (the single source of credit rules:
risk-rating thresholds, CDFI factor scores, CRE weighted score, stress
formulas, capacity/concentration policy) and /api/manifest (versions).
- Never call
/api/judge (no test-time judge; it is not part of the public
surface). Do not read any local env/ source.
- Pipe responses through
jq to inspect and project fields.
- Latest quarter of branch metrics is
2025Q1 (the second array element on
these branches is 2024Q4). Use the latest quarter unless a prompt names a
quarter. review_date / as-of date in prompts is 2025-03-31.
- Policy version is
credit_policy_v2025Q1. FDIC set fdic_q4_2024; NCUA
ncua_q1_2025.
1. API SOP — endpoints and how each answer section uses them
| Endpoint |
Used for |
Key query params |
GET /api/policies |
every task — rating tables, stress formulas, score weights, capacity rules |
none |
GET /api/manifest |
versions to populate benchmark_version strings |
none |
GET /api/branches |
branch roster; ?institution_type=credit_union filters CU segments |
none |
GET /api/branches/{id} |
capacity (lending_capacity_q1), sector_ceiling_pct, cre_policy_limit_pct, state_code, total_assets, fdic_benchmark_set |
none |
GET /api/branches/{id}/metrics |
total_loans_outstanding, nonperforming_loans, delinquency_30_plus_pct, allowances (latest quarter array element) |
?quarter= |
GET /api/branches/{id}/loans |
loan-level regrade inputs: dscr, ltv, debt_to_asset, fico, liquidity_months, payment_status, outstanding_balance, current_rating, sector, loan_type, borrower_name |
?loan_type=, ?payment_status=, ?min_current_rating= |
GET /api/branches/{id}/sector-exposures |
per-sector current_exposure and per-sector limit_pct (overrides sector_ceiling_pct); grandfathered flag |
none |
GET /api/branches/{id}/applications |
pending applications: requested_amount, dscr, ltv, fico, years_in_business, sba_guaranty_pct, bankruptcy_months_ago, documentation_complete, co_guarantor_strength, total_debt, total_assets, sector, loan_type |
?loan_type= |
GET /api/benchmarks/fdic/q4-2024 |
five FDIC ratios (noncurrent + 30-89, by total / real-estate / construction) |
none |
GET /api/benchmarks/ncua/q1-2025 |
all state + US rows; ?state_code= returns one row |
?state_code= |
GET /api/credit-union-segments/{segment_id} |
CU segment: state_code, peer_states, minimum_checklist, risk_tolerance, quarterly_capacity, current_outstanding, internal_context |
none |
branch_id values are uppercase (REDWOOD, LAKEVIEW, SUMMIT, HARBOR,
uppercase bank branch ids plus credit-union segment ids, which also appear in /api/branches
with institution_type=credit_union.
2. Cross-cutting business rules
2.1 Risk-rating re-derivation (dominant-factor / worst-notch rule)
Policy risk_rating.dominant_factor_rule: the final re-derived rating is the
worst (highest) numeric rating from the available DSCR, LTV/collateral, and
delinquency factors. Compute each factor's rating, then take the max.
DSCR thresholds (risk_rating.dscr_thresholds, min is inclusive lower bound):
dscr >= 1.5 → 3
dscr >= 1.25 → 4
dscr >= 1.05 → 5
dscr >= 1.0 → 6
dscr < 1.0 → 7
dscr null/missing → factor contributes nothing (skip it).
LTV thresholds (risk_rating.ltv_thresholds, max inclusive upper bound):
ltv <= 0.65 → 3
ltv <= 0.75 → 4
ltv <= 0.85 → 5
ltv <= 1.0 → 6
ltv > 1.0 → 7
Delinquency minimums (risk_rating.delinquency_minimums) — a hard floor:
Current → no floor (null)
30 Days Past Due → at least 4
60 Days Past Due → at least 5
90+ Days Past Due → at least 7
Nonaccrual → at least 8
final_rating = max(dscr_rating*, ltv_rating*, delinquency_floor*). Example
verified on REDWOOD: RED-LN-002 (dscr 1.49→4, ltv 1.0219→7, 60 DPD→5) ⇒
final 7 (current 4, downgrade 3).
Population filter: regrade population = loans with
current_rating >= target_current_rating_min (e.g. Redwood >=3). Pull with
/api/branches/{id}/loans?min_current_rating=3. This is NOT the watch-list
follow-up population (see 2.7).
Material downgrade: material_downgrade_notches = 2. A loan is a material
downgrade iff current_rating - final_rating <= -2 (i.e. worsened by ≥2
notches). Only downgrades (final > current) by ≥2 notches are listed; list is
ordered ascending by loan_id.
2.2 NPA & FDIC/NCUA variance
NPA variance (bank branches): use branch metrics latest quarter.
branch_npa_exposure = metrics.nonperforming_loans (cross-check: sum of
outstanding_balance where payment_status == "Nonaccrual"; the two agree).
branch_total_loans = metrics.total_loans_outstanding.
branch_npa_ratio = round(branch_npa_exposure / branch_total_loans, 4).
fdic_benchmark_ratio = the chosen FDIC metric value (a decimal ratio, e.g.
0.0098).
variance_ratio = round(npa_exposure/total_loans - fdic_ratio, 4) (positive
= branch worse than benchmark).
variance_bps = round((npa_exposure/total_loans - fdic_ratio) * 10000, 2),
computed from the unrounded ratio (Redwood: 1037.49, not 1037.00).
benchmark_version = "fdic_q4_2024".
FDIC metric choice (npa_benchmark.benchmark_metric enum):
total_loans_noncurrent_pct, total_real_estate_noncurrent_pct,
construction_development_noncurrent_pct. Choose by branch/portfolio focus:
general branch NPA review → total_loans_noncurrent_pct; CRE-heavy →
total_real_estate_noncurrent_pct; construction-heavy →
construction_development_noncurrent_pct. The benchmark value comes from the
matching field of /api/benchmarks/fdic/q4-2024.
CRE/FDIC delinquency variance (Harbor-style CRE decision): here the variance
pairs the branch delinquency_30_plus_pct (from metrics) against the FDIC
total_real_estate_30_89_pct benchmark (template-hardcoded for that task).
branch_delinquency_ratio = metrics.delinquency_30_plus_pct (e.g. 0.2853).
fdic_benchmark_ratio = FDIC total_real_estate_30_89_pct (e.g. 0.0051).
fdic_variance_ratio = round(branch - fdic, 4); fdic_variance_bps = round((branch - fdic) * 10000, 2) (e.g. 2802.0). Sign: positive = branch
under-performs benchmark.
NCUA peer comparison (credit-union segment tasks):
- Fetch
/api/benchmarks/ncua/q1-2025 (all rows, includes US).
state_metrics = the segment state_code row; benchmark_version = "ncua_q1_2025". Values are integers exactly as reported
(delinquency_bps, loan_to_share_pct, roaa_bps,
positive_net_income_pct).
- Peer states come from the segment JSON
peer_states (e.g. ["SC","TN","VA"]),
already ascending; do not invent peers. Compute the median of the peer
states' values per metric.
nc_vs_us and nc_vs_peer_median: direction per metric ∈
{"higher","lower","equal"} (substitute the segment's state code for "nc").
Example: NC delinquency_bps 79 vs US 58 → higher; NC roaa_bps 44 vs peer
median 59 → lower.
2.3 Capacity & concentration ceilings
Lending capacity (from branches.lending_capacity_q1):
gross_approved_amount = Σ approved_amount over all approve +
conditional_approve decisions (full approved principal, including amounts
that are later participated / SBA-guaranteed).
committed_capacity_amount = Σ bank_capacity_used over the same decisions
(the bank's at-risk retained exposure, after mitigation).
remaining_capacity = lending_capacity_q1 - committed_capacity_amount.
bank_capacity_used by decision type:
approve → approved_amount (full retention).
conditional_approve + sba_guaranty_required →
approved_amount * (1 - sba_guaranty_pct). Verified: LAK-APP-902,
approved 840,000 × (1 − 0.75) = 210,000.
conditional_approve + participation_required → bank-retained amount sized
so the affected sector sits at its limit_pct against the pro-forma book
(total_loans_outstanding + committed bank-retained capacity); the excess is
participated out. Verified: LAK-APP-901 retained 1,508,113.31 =
0.19 × (total_loans_outstanding + committed_capacity) − existing_healthcare.
Sector concentration limits: limit_pct comes from each
sector-exposures row (per-sector override); default is
branches.sector_ceiling_pct when a sector is not listed. CRE-wide ceiling =
branches.cre_policy_limit_pct. Mitigations allowed by policy:
participation_required, reduced_amount, board_exception. Grandfathering:
existing over-ceiling exposure may be grandfathered, but a new approval may not
worsen that sector without mitigation.
Existing CRE exposure (Harbor-style): existing_cre_exposure = Σ
outstanding_balance of loans with loan_type == "CRE" (equivalently the sum
of CRE sector exposures). existing_cre_concentration = round(existing_cre_exposure / total_loans_outstanding, 4).
selected_post_approval_cre_concentration = round((existing_cre_exposure + selected_requested_amount) / (total_loans_outstanding + selected_requested_amount), 4) — both numerator and denominator grow by the
new principal. selected_policy_variance_bps = round((post_unrounded - cre_policy_limit_pct) * 10000, 2) (Harbor: 0.5349 − 0.29 ⇒ 2449.15 bps).
CRITICAL denominator rule: concentration denominators use
metrics.total_loans_outstanding (e.g. 14,334,094.87 for Lakeview), never
branches.total_assets (which is ~25× larger and would make every ratio
trivially tiny).
post_approval_concentrations (reporting section):
exposure_after_approval = existing sector exposure + Σ full
approved_amount of approve/conditional apps in that sector (use approved
principal, not bank_capacity_used).
- denominator =
total_loans_outstanding + gross_approved_amount (pro-forma
book including all newly approved principal). Verified on all four Lakeview
sectors: Construction 3,934,283.26 / 18,908,348.32 = 0.2081, etc.
post_approval_pct = round(exposure_after_approval / denom, 4).
over_limit = post_approval_pct > limit_pct (boolean).
concentration_flags: for each approve/conditional app whose sector is at or
near its ceiling, report sector, application_id, limit_pct,
post_approval_pct (4dp), flag (true when the deal breaches/near-breaches
the sector limit on a committed-capacity basis), handling ∈
{approve, conditional_approve, decline, participation_required, none}. Sort
by sector then application_id.
2.4 CDFI factor scores & risk classes (watch-list)
CDFI factor score = sum of four sub-scores (lower = better); null/missing
factors score 0.
ltv: <0.40→0, 0.40–0.60→2, 0.60–0.80→4, >0.80→6
fico: >720→0, 680–720→1, 580–679→3, <580→5 (null→0)
debt_to_asset: <0.40→0, 0.40–0.60→2, 0.60–0.80→4, >0.80→6 (null→0)
liquidity_months: >12→0, 6–12→1, 3–6→3, <3→5 (null→0)
Verified: SUM-LN-003 (ltv 0.9511→6, fico null→0, dta 0.3579→0, liq 11.6→1) ⇒
factor_score 7 ⇒ Desirable. SUM-LN-902 (ltv 1.18→6, dta 0.88→6, liq 1.6→5) ⇒
17.
Risk class from factor_score (policies.cdfi_factor_scores.classes):
Prime: 0–5
Desirable: 6–9
Satisfactory: 10–13
Watch: 14–18
Doubtful: ≥19
Projected Loss: ≥19 and ltv > 1.0
Severe-delinquency override: a loan with payment_status == "Nonaccrual"
is classed Projected Loss regardless of its factor score (it overrides the
score-range mapping). Verified on SUM-LN-902 (factor 17, Nonaccrual ⇒ Projected
Loss). 90+ Days Past Due does not override — SUM-LN-011/015 stay at their
score-derived class.
2.5 +200bp DSCR stress (watch-list) and CRE dual stress
From policies.stress:
watch_list_parallel_shock = "+200bp" ⇒ shock_label = "+200bp".
- Watch-list formula:
stressed_dscr = dscr / (1 + 0.18) =
dscr / 1.18. Verified: SUM-LN-003 1.59/1.18 = 1.35; SUM-LN-004 1.01/1.18 =
0.86.
- CRE dual-stress formula (
cre_dual_stress_formula):
stressed_dscr = dscr * 0.85 / (1 + 0.18) = dscr * 0.85 / 1.18. Verified:
HAR-APP-901 1.47×0.85/1.18 = 1.06; HAR-APP-902 1.32×0.85/1.18 = 0.95.
Report formula as the compact expression, e.g. "dscr * 0.85 / 1.18".
coverage_breach_threshold = 1.0. breaches_threshold = stressed_dscr < 1.0 (i.e. stressed < 1.0). Use strict < (Harbor 901 stressed 1.06 ⇒ false;
902 stressed 0.95 ⇒ true).
- Stress results include only loans with DSCR available (skip null-dscr
loans such as SUM-LN-010). DSCR values round to 2dp.
breach_loan_ids = ascending loan_id list of loans whose stressed DSCR
breaches.
2.6 Decline reason codes & decision enums
Decision enum: approve, conditional_approve, decline, defer,
participation_required (used as a per-application decision and as the
selected path).
Conditions enum: participation_required, reduced_amount,
board_exception, sba_guaranty_required, startup_monitoring, none.
Approve ⇒ ["none"]; conditional_approve lists the mitigations in effect.
Decline reason codes (assign to declined apps; sorted ascending
alphabetically in output):
high_ltv — ltv elevated past the 0.80 boundary (borderline) on an app
declined for compounding weakness; clearly high when ltv > 0.85.
weak_dscr — base DSCR < 1.25 (when DSCR applicable). In CRE-decision
contexts, weak_dscr is instead triggered by stressed DSCR < 1.0 (the
coverage breach) — verified: HAR-APP-902 (stressed 0.95) gets weak_dscr,
HAR-APP-901 (stressed 1.06) does not.
low_fico — fico < 580.
recent_bankruptcy — bankruptcy_months_ago not null and < 24 months.
startup_risk — years_in_business < 2.
sector_breach — the app's sector post-approval concentration breaches (or
would breach) limit_pct without mitigation.
capacity_limit — the app is credit-acceptable but cannot be funded within
remaining lending capacity after higher-priority approvals (e.g. LAK-APP-006:
ltv 0.7749, fico 668 — passes credit, declined only on capacity).
fdic_adverse_variance — branch FDIC delinquency materially exceeds
benchmark (applies to all apps at an under-performing branch in a CRE
decision; both Harbor apps carry it).
ncua_peer_weakness — credit-union segment's state is weaker than national
and peer median.
underwater_collateral, policy_floor_missing, documentation_gap — use
when collateral/value, policy floor, or documentation gaps are the binding
weakness (documentation_complete == 0 ⇒ documentation_gap).
Decision logic (transferable):
approve — passes every credit test (ltv ≤ 0.85 band, dscr ≥ 1.25 where
applicable, fico ≥ 580, no recent bankruptcy, years ≥ 2, documentation
complete) AND fits capacity and sector ceilings. approved_amount =
requested_amount; bank_capacity_used = approved_amount.
conditional_approve — credit-acceptable but needs a mitigation:
participation_required (sector near ceiling), sba_guaranty_required
(startup risk partly offset by SBA guaranty), startup_monitoring,
reduced_amount, or board_exception.
decline — compounding credit weaknesses (e.g. high_ltv + weak_dscr; or
low_fico + recent_bankruptcy) or capacity_limit.
defer — for a competing-credit scenario, the weaker credit that is not
selected but not outright declined (remediation possible). unselected_disposition
∈ {decline, defer}.
2.7 Watch-list action coverage & workout queues
Watch-list action coverage (regrade tasks, e.g. Redwood): covers the subset
of the regrade population whose final (re-derived) rating ≥ 6. Loans
regraded to ≤ 5 are dropped from follow-up (they are no longer adverse).
covered_loan_count / covered_exposure = Σ over final ≥ 6. Group by_action,
each action sorted ascending alphabetically; loan_ids ascending.
Action mapping by final_rating (Redwood pattern):
- final
8 (Nonaccrual) → partial_chargeoff_review
- final
7 → special_assets
- final
6 → watchlist
Workout queue (watch-list tasks, e.g. Summit): includes all adverse
loans (current_rating >= adverse_rating_min), each assigned a
recommended_action based on a payment-status-first cascade, then risk class.
Sort the queue descending by exposure, then ascending loan_id.
Action cascade (verified on Summit):
Nonaccrual → partial_chargeoff_review (projected_loss = true)
90+ Days Past Due → special_assets (projected_loss = false)
Current + class Watch → special_assets
Current + class Desirable → watchlist
- (
Current + Prime/mild → monitor; Doubtful/Projected Loss current →
workout — extend by analogy for unseen buckets.)
watch_list_summary:
adverse_rating_min = the threshold from the prompt (e.g. 6).
adverse_loan_count = count of loans with current_rating >= adverse_rating_min.
adverse_balance = Σ outstanding_balance of those loans (verified:
Summit 7,675,179.41).
risk_classes = one row per adverse loan, sorted ascending loan_id, with
factor_score (integer) and risk_class (with Nonaccrual ⇒ Projected Loss
override).
monitoring_cadence = monthly for an adverse watch-list.
stress_results: all DSCR-bearing adverse loans, ascending loan_id, with
base_dscr, stressed_dscr = base/1.18 (2dp), breaches_threshold.
breach_loan_ids ascending.
severe_bucket_counts: group adverse loans by (current_rating, payment_status), sorted ascending current_rating then payment_status. Note
payment_status ascending is string-ascending, so "90+ Days Past Due"
sorts before "Current" (digit 9 < C), and "Current" before
"Nonaccrual". loan_count and exposure (Σ outstanding_balance) per bucket.
3. Per-task archetypes & method
A. Rating-migration review (Redwood)
GET /api/policies, /api/branches/REDWOOD, /api/branches/REDWOOD/loans?min_current_rating=3,
/api/branches/REDWOOD/metrics, /api/benchmarks/fdic/q4-2024.
- Re-derive
final_rating per loan (2.1). Build final_rating_exposure_totals
(all target loans, ascending final_rating — count + Σ exposure) and
migration_from_current_rating_3 (only loans whose current_rating == 3,
grouped by final_rating, with ascending loan_ids).
watch_list_action_coverage over final ≥ 6 (2.7).
npa_benchmark (2.2) with total_loans_noncurrent_pct.
material_downgrades = loans with final - current >= 2 notches worsening,
ascending loan_id.
top_problem_credit = the worst final_rating loan (prefer Nonaccrual / highest
final / largest exposure); include borrower_name, payment_status,
recommended_action.
B. Quarterly allocation package (Lakeview)
GET /api/branches/LAKEVIEW, /applications, /sector-exposures,
/metrics, /api/policies.
- Decide each application (2.6); compute
approved_amount and
bank_capacity_used per decision (2.3). Decisions sorted ascending
application_id.
- Aggregate
allocation (2.3); priority_ranking = approved + conditional
application_ids, highest committee funding priority first (strategic/stronger
credits lead).
concentration_flags per near-ceiling conditional app (2.3).
decline_reasons map (only declined apps) with sorted reason-code lists.
post_approval_concentrations for every sector touched by an approval
(reporting denominator = total_loans_outstanding + gross_approved_amount;
2.3), sorted ascending sector.
C. Credit-union segment posture (Civic NC)
GET /api/credit-union-segments/{segment_id}, /api/policies,
/api/benchmarks/ncua/q1-2025, /api/manifest.
state_metrics from the segment state_code NCUA row (integer values);
benchmark_version = "ncua_q1_2025".
peer_comparison: peer_states from segment (ascending); direction vs US
row and vs median of peer rows (2.2), each ∈ {higher, lower, equal}.
posture from capacity vs external risk: capacity available + external risk
weaker ⇒ continue_with_tighter_conditions; capacity constrained ⇒
temporarily_pause; external risk strong ⇒ continue_approving.
controls.required_checklist_gates = segment minimum_checklist;
added_operating_controls = the standard tighter set (pre-close insurance
binder verification, lien perfection prior to funding, senior underwriter
second review, quarterly state benchmark monitoring, monthly segment
delinquency watch); include committee_exception_for_capacity_overrun only
when capacity is constrained.
escalation_triggers: ascending trigger_id (ET001 delinquency ≥ 90 bps →
credit_risk_manager; ET002 missing insurance/lien exception →
operations_control_manager; ET003 quarterly capacity exceeded/exception →
lending_committee_chair). Condition/owner enums only.
interpretation: derive capacity_status, external_risk_status,
risk_tolerance (from segment), and the matching committee_message.
D. Watch-list stress packet (Summit)
GET /api/branches/SUMMIT, /api/branches/SUMMIT/loans?min_current_rating=6,
/api/policies.
watch_list_summary (2.7): factor scores, risk classes (Nonaccrual ⇒
Projected Loss), adverse_balance, monitoring_cadence = monthly.
stress_results (2.5): +200bp, dscr/1.18, threshold 1.0, DSCR-bearing
loans only.
workout_queue (2.7): all adverse loans, action cascade, descending exposure
then ascending loan_id.
severe_bucket_counts (2.7) with the string-ascending payment_status order.
E. Competing CRE decision (Harbor)
GET /api/branches/HARBOR, /metrics, /loans?loan_type=CRE,
/sector-exposures, /applications, /api/policies,
/api/benchmarks/fdic/q4-2024.
- For each competing CRE app compute
weighted_cdfi_score (1dp, lower better)
using cre_weighted_score weights (capacity 0.45, collateral_exposure 0.36,
conditions 0.11, character 0.05, capital 0.03) — capacity (DSCR) and
collateral (LTV) carry 81% of the weight; map to score_class
(approve_quality ≤ 2.0, conditional ≤ 3.0, weak > 3.0).
stress (2.5 CRE dual): formula = "dscr * 0.85 / 1.18", threshold 1.0.
weak_dscr reason fires when stressed < 1.0.
concentration (2.2/2.3): existing & post-approval CRE concentration vs
cre_policy_limit_pct; FDIC variance vs total_real_estate_30_89_pct.
applications_compared (ascending application_id): each with score, class,
decision, sorted reason_codes. Reasons include fdic_adverse_variance and
sector_breach (both apps at an under-performing, CRE-heavy branch) plus
weak_dscr only for the stressed-breach app.
recommended_path: select the lower (better) weighted score app; its
decision becomes path. unselected_disposition ∈ {decline, defer};
unselected reason_codes ascending.
conditions: for a participation_required selected CRE path, the full set
(all 7 enum values), sorted ascending alphabetically:
bank_retained_exposure_cap, committee_cre_exception,
minimum_dscr_covenant_1_25, no_additional_cre_without_committee_review,
quarterly_financial_reporting, tenant_roll_and_lease_review,
updated_appraisal_before_close.
4. Output field definitions & exact enums (consolidated)
payment_status: Current, 30 Days Past Due, 60 Days Past Due,
90+ Days Past Due, Nonaccrual.
recommended_action / watch-list action: monitor, watchlist,
special_assets (underscore — this is the spelling used by both the Redwood
and Summit templates and their gold answers), workout,
partial_chargeoff_review, legal_referral. Still copy the exact token from
the task's answer_template.json enum in case a future template differs.
decision: approve, conditional_approve, decline, defer,
participation_required.
conditions: participation_required, reduced_amount, board_exception,
sba_guaranty_required, startup_monitoring, none.
handling: approve, conditional_approve, decline,
participation_required, none.
risk_class: Prime, Desirable, Satisfactory, Watch, Doubtful,
Projected Loss.
score_class: approve_quality, conditional, weak.
posture: continue_approving, continue_with_tighter_conditions,
temporarily_pause.
capacity_status: capacity_available, capacity_constrained,
no_capacity.
external_risk_status: stronger_than_national_and_peers,
mixed_vs_national_and_peers, weaker_than_national_and_peers.
risk_tolerance: restrained, moderate, expansive.
committee_message (segment): capacity_available_but_external_risk_weaker,
pause_until_state_metrics_recover, routine_approval_path_supported.
unselected_disposition: decline, defer.
- Decline
reason_code set: capacity_limit, sector_breach, weak_dscr,
high_ltv, low_fico, recent_bankruptcy, startup_risk,
underwater_collateral, policy_floor_missing, documentation_gap,
fdic_adverse_variance, ncua_peer_weakness.
Ordering rules (apply unless the template says otherwise):
- lists of loans/applications/actions: ascending
loan_id / application_id;
final_rating ascending; sector ascending; action/condition/reason-code
ascending alphabetically; trigger_id ascending;
material_downgrades ascending loan_id; workout_queue descending
exposure then ascending loan_id; severe_bucket_counts ascending
current_rating then string-ascending payment_status;
priority_ranking: highest priority first (approved + conditional only);
- numeric lists inside objects follow the
ordering field in the template.
5. Numeric conventions
- Money / exposure / balance: 2 decimals USD (e.g. 1725000.00).
- Ratios (concentration, NPA, FDIC variance, post_approval_pct, limit_pct):
4 decimals as a ratio (0.1897, not 18.97%).
variance_bps / *_variance_bps / policy_variance_bps: 2 decimals,
signed (positive = branch/app worse than benchmark/policy); computed from
the unrounded ratio difference × 10000.
dscr / base_dscr / stressed_dscr: 2 decimals.
weighted_cdfi_score: 1 decimal (lower is better).
factor_score, ratings, counts, bps-in-state-metrics: integers.
- Percentages in policy/templates are ratios (0.19), never percent integers.
- Round only at output; keep unrounded intermediates for variance_bps so the bps
figure stays consistent with the rounded ratio (Redwood 0.1135 ratio but
1037.49 bps, not 1037.00).
6. Common misjudgments & exclusion rules
- Regrade population ≠ watch-list coverage. Regrade population =
current_rating >= target_current_rating_min. Watch-list action coverage =
subset with re-derived final_rating ≥ 6. Do not list final ≤ 5 loans in
coverage, and do not use current_rating to decide coverage.
- Severe-delinquency override is Nonaccrual only. Nonaccrual ⇒
Projected Loss regardless of factor_score. 90+ Days Past Due does not
override the score-derived class (SUM-LN-011/015 stay Desirable).
- Concentration denominator =
total_loans_outstanding, not total_assets.
total_assets is ~25–35× larger and yields trivially small ratios. Always
pull total_loans_outstanding from metrics (latest quarter).
- Pro-forma denominator grows with new principal. For
post_approval_concentrations the denominator = total_loans_outstanding + gross_approved_amount; for CRE selected_post_approval_cre_concentration
both numerator and denominator grow by the selected requested_amount.
Keep numerator and denominator consistent.
approved_amount is always full principal; bank_capacity_used is the
reduced, retained figure. SBA-guaranty ⇒ approved × (1 − sba_guaranty_pct); participation ⇒ sized to sector limit. Never substitute
bank_capacity_used into gross_approved_amount or sector
exposure_after_approval.
variance_bps uses the unrounded ratio. Recompute from the raw
exposure/total fraction before multiplying by 10000, then round to 2dp.
This avoids the 0.5–1 bps drift that matching judges penalize.
- Stress excludes null-DSCR loans.
stress_results lists only loans with a
DSCR; null-dscr loans (often Consumer/Equipment) still appear in
risk_classes and workout_queue.
- Material downgrade threshold is 2 notches. Include only loans worsened by
≥ 2 (
final - current >= 2). Single-notch downgrades are not "material".
benchmark_metric choice tracks portfolio focus. General NPA review ⇒
total_loans_noncurrent_pct; CRE decision task ⇒
total_real_estate_30_89_pct paired with delinquency_30_plus_pct. Do not
mix a noncurrent metric with a 30-89 branch figure.
- Payment-status ordering is string-ascending, not severity-ascending.
"90+ Days Past Due" < "Current" < "Nonaccrual" because '9' < 'C'.
In severe_bucket_counts, a rating-7 "90+ DPD" bucket sorts before a
rating-7 "Current" bucket.
top_problem_credit should be the genuinely worst credit (highest
final_rating, prefer Nonaccrual, then largest exposure), not the largest
balance regardless of rating.
- Selection in a competing-credit task = lower weighted score. The
selected app is the one with the lower
weighted_cdfi_score (better
quality); its decision becomes the path; the other is
defer/decline.
- Always honor the exact enum spelling in the target
answer_template.json.
The action enum is consistently special_assets (underscore) across the
train templates and gold answers; still verify against the target template's
allowed_values rather than assuming.
- Do not invent peer states or benchmark versions. Peer states come from
the segment JSON; benchmark versions come from
/api/manifest. Use only the
enum choices and identifiers the template allows.
7. End-to-end execution checklist
- Read the prompt; fix the
branch_id / segment_id, as-of date, and which
answer_template to mirror.
GET /api/policies and /api/manifest first; cache the rating tables,
stress formulas, score weights, and benchmark versions.
- Pull the branch, metrics (latest quarter), loans (
?min_current_rating= as
needed), sector-exposures, applications, and the relevant benchmark
(FDIC and/or NCUA all-rows).
- Re-derive ratings / scores / stresses with the rules above; never trust the
stored
current_rating as the final answer.
- Assemble the JSON exactly matching the template's
required_top_level_keys
and per-section required_keys, applying ordering and precision rules.
- Emit only valid JSON — no narrative outside the object. Money 2dp, ratios 4dp,
bps 2dp signed, scores 1dp, DSCR 2dp.
1---2name: fewshot-attempt-02-113description: Skill — Credit-Risk / Lending-Committee Answer Generation (task_group_011)4---5# Skill — Credit-Risk / Lending-Committee Answer Generation (task_group_011)67Transferable method for producing committee-ready JSON answers against the8shared credit-office public REST API. Distilled from five fully-worked train9tasks (Redwood regrade, Lakeview allocation, Civic NC segment posture, Summit10watch-list, Harbor competing-CRE) and cross-checked against live API data.1112## 0. Environment & ground rules1314- Base URL: `<remote-env-url>`. Read-only GET only. No auth.15- Always start by reading `/api/policies` (the single source of credit rules:16 risk-rating thresholds, CDFI factor scores, CRE weighted score, stress17 formulas, capacity/concentration policy) and `/api/manifest` (versions).18- Never call `/api/judge` (no test-time judge; it is not part of the public19 surface). Do not read any local `env/` source.20- Pipe responses through `jq` to inspect and project fields.21- Latest quarter of branch metrics is `2025Q1` (the second array element on22 these branches is `2024Q4`). Use the latest quarter unless a prompt names a23 quarter. `review_date` / as-of date in prompts is `2025-03-31`.24- Policy version is `credit_policy_v2025Q1`. FDIC set `fdic_q4_2024`; NCUA25 `ncua_q1_2025`.2627## 1. API SOP — endpoints and how each answer section uses them2829| Endpoint | Used for | Key query params |30| --- | --- | --- |31| `GET /api/policies` | every task — rating tables, stress formulas, score weights, capacity rules | none |32| `GET /api/manifest` | versions to populate `benchmark_version` strings | none |33| `GET /api/branches` | branch roster; `?institution_type=credit_union` filters CU segments | none |34| `GET /api/branches/{id}` | capacity (`lending_capacity_q1`), `sector_ceiling_pct`, `cre_policy_limit_pct`, `state_code`, `total_assets`, `fdic_benchmark_set` | none |35| `GET /api/branches/{id}/metrics` | `total_loans_outstanding`, `nonperforming_loans`, `delinquency_30_plus_pct`, allowances (latest quarter array element) | `?quarter=` |36| `GET /api/branches/{id}/loans` | loan-level regrade inputs: `dscr`, `ltv`, `debt_to_asset`, `fico`, `liquidity_months`, `payment_status`, `outstanding_balance`, `current_rating`, `sector`, `loan_type`, `borrower_name` | `?loan_type=`, `?payment_status=`, `?min_current_rating=` |37| `GET /api/branches/{id}/sector-exposures` | per-sector `current_exposure` and per-sector `limit_pct` (overrides `sector_ceiling_pct`); `grandfathered` flag | none |38| `GET /api/branches/{id}/applications` | pending applications: `requested_amount`, `dscr`, `ltv`, `fico`, `years_in_business`, `sba_guaranty_pct`, `bankruptcy_months_ago`, `documentation_complete`, `co_guarantor_strength`, `total_debt`, `total_assets`, `sector`, `loan_type` | `?loan_type=` |39| `GET /api/benchmarks/fdic/q4-2024` | five FDIC ratios (noncurrent + 30-89, by total / real-estate / construction) | none |40| `GET /api/benchmarks/ncua/q1-2025` | all state + `US` rows; `?state_code=` returns one row | `?state_code=` |41| `GET /api/credit-union-segments/{segment_id}` | CU segment: `state_code`, `peer_states`, `minimum_checklist`, `risk_tolerance`, `quarterly_capacity`, `current_outstanding`, `internal_context` | none |4243`branch_id` values are uppercase (`REDWOOD`, `LAKEVIEW`, `SUMMIT`, `HARBOR`,44uppercase bank branch ids plus credit-union segment ids, which also appear in `/api/branches`45with `institution_type=credit_union`.4647## 2. Cross-cutting business rules4849### 2.1 Risk-rating re-derivation (dominant-factor / worst-notch rule)5051Policy `risk_rating.dominant_factor_rule`: the final re-derived rating is the52**worst (highest) numeric rating** from the available DSCR, LTV/collateral, and53delinquency factors. Compute each factor's rating, then take the max.5455**DSCR thresholds** (`risk_rating.dscr_thresholds`, `min` is inclusive lower bound):56- `dscr >= 1.5` → 357- `dscr >= 1.25` → 458- `dscr >= 1.05` → 559- `dscr >= 1.0` → 660- `dscr < 1.0` → 761- `dscr` null/missing → factor contributes nothing (skip it).6263**LTV thresholds** (`risk_rating.ltv_thresholds`, `max` inclusive upper bound):64- `ltv <= 0.65` → 365- `ltv <= 0.75` → 466- `ltv <= 0.85` → 567- `ltv <= 1.0` → 668- `ltv > 1.0` → 76970**Delinquency minimums** (`risk_rating.delinquency_minimums`) — a hard floor:71- `Current` → no floor (null)72- `30 Days Past Due` → at least 473- `60 Days Past Due` → at least 574- `90+ Days Past Due` → at least 775- `Nonaccrual` → at least 87677`final_rating = max(dscr_rating*, ltv_rating*, delinquency_floor*)`. Example78verified on REDWOOD: `RED-LN-002` (dscr 1.49→4, ltv 1.0219→7, 60 DPD→5) ⇒79final 7 (current 4, downgrade 3).8081**Population filter:** regrade population = loans with82`current_rating >= target_current_rating_min` (e.g. Redwood `>=3`). Pull with83`/api/branches/{id}/loans?min_current_rating=3`. This is NOT the watch-list84follow-up population (see 2.7).8586**Material downgrade:** `material_downgrade_notches = 2`. A loan is a material87downgrade iff `current_rating - final_rating <= -2` (i.e. worsened by ≥288notches). Only downgrades (final > current) by ≥2 notches are listed; list is89ordered ascending by `loan_id`.9091### 2.2 NPA & FDIC/NCUA variance9293**NPA variance (bank branches):** use branch metrics latest quarter.94- `branch_npa_exposure` = `metrics.nonperforming_loans` (cross-check: sum of95 `outstanding_balance` where `payment_status == "Nonaccrual"`; the two agree).96- `branch_total_loans` = `metrics.total_loans_outstanding`.97- `branch_npa_ratio = round(branch_npa_exposure / branch_total_loans, 4)`.98- `fdic_benchmark_ratio` = the chosen FDIC metric value (a decimal ratio, e.g.99 0.0098).100- `variance_ratio = round(npa_exposure/total_loans - fdic_ratio, 4)` (positive101 = branch worse than benchmark).102- `variance_bps = round((npa_exposure/total_loans - fdic_ratio) * 10000, 2)`,103 computed from the **unrounded** ratio (Redwood: 1037.49, not 1037.00).104- `benchmark_version = "fdic_q4_2024"`.105106**FDIC metric choice** (`npa_benchmark.benchmark_metric` enum):107`total_loans_noncurrent_pct`, `total_real_estate_noncurrent_pct`,108`construction_development_noncurrent_pct`. Choose by branch/portfolio focus:109general branch NPA review → `total_loans_noncurrent_pct`; CRE-heavy →110`total_real_estate_noncurrent_pct`; construction-heavy →111`construction_development_noncurrent_pct`. The benchmark value comes from the112matching field of `/api/benchmarks/fdic/q4-2024`.113114**CRE/FDIC delinquency variance (Harbor-style CRE decision):** here the variance115pairs the branch `delinquency_30_plus_pct` (from metrics) against the FDIC116`total_real_estate_30_89_pct` benchmark (template-hardcoded for that task).117- `branch_delinquency_ratio` = `metrics.delinquency_30_plus_pct` (e.g. 0.2853).118- `fdic_benchmark_ratio` = FDIC `total_real_estate_30_89_pct` (e.g. 0.0051).119- `fdic_variance_ratio = round(branch - fdic, 4)`; `fdic_variance_bps =120 round((branch - fdic) * 10000, 2)` (e.g. 2802.0). Sign: positive = branch121 under-performs benchmark.122123**NCUA peer comparison (credit-union segment tasks):**124- Fetch `/api/benchmarks/ncua/q1-2025` (all rows, includes `US`).125- `state_metrics` = the segment `state_code` row; `benchmark_version =126 "ncua_q1_2025"`. Values are integers exactly as reported127 (`delinquency_bps`, `loan_to_share_pct`, `roaa_bps`,128 `positive_net_income_pct`).129- Peer states come from the **segment JSON** `peer_states` (e.g. `["SC","TN","VA"]`),130 already ascending; do not invent peers. Compute the **median** of the peer131 states' values per metric.132- `nc_vs_us` and `nc_vs_peer_median`: direction per metric ∈133 `{"higher","lower","equal"}` (substitute the segment's state code for "nc").134 Example: NC delinquency_bps 79 vs US 58 → `higher`; NC roaa_bps 44 vs peer135 median 59 → `lower`.136137### 2.3 Capacity & concentration ceilings138139**Lending capacity** (from `branches.lending_capacity_q1`):140- `gross_approved_amount` = Σ `approved_amount` over all `approve` +141 `conditional_approve` decisions (full approved principal, including amounts142 that are later participated / SBA-guaranteed).143- `committed_capacity_amount` = Σ `bank_capacity_used` over the same decisions144 (the bank's at-risk retained exposure, after mitigation).145- `remaining_capacity = lending_capacity_q1 - committed_capacity_amount`.146147**bank_capacity_used by decision type:**148- `approve` → `approved_amount` (full retention).149- `conditional_approve` + `sba_guaranty_required` →150 `approved_amount * (1 - sba_guaranty_pct)`. Verified: LAK-APP-902,151 approved 840,000 × (1 − 0.75) = 210,000.152- `conditional_approve` + `participation_required` → bank-retained amount sized153 so the affected sector sits at its `limit_pct` against the pro-forma book154 (total_loans_outstanding + committed bank-retained capacity); the excess is155 participated out. Verified: LAK-APP-901 retained 1,508,113.31 =156 `0.19 × (total_loans_outstanding + committed_capacity) − existing_healthcare`.157158**Sector concentration limits:** `limit_pct` comes from each159`sector-exposures` row (per-sector override); default is160`branches.sector_ceiling_pct` when a sector is not listed. CRE-wide ceiling =161`branches.cre_policy_limit_pct`. Mitigations allowed by policy:162`participation_required`, `reduced_amount`, `board_exception`. Grandfathering:163existing over-ceiling exposure may be grandfathered, but a new approval may not164*worsen* that sector without mitigation.165166**Existing CRE exposure (Harbor-style):** `existing_cre_exposure` = Σ167`outstanding_balance` of loans with `loan_type == "CRE"` (equivalently the sum168of CRE sector exposures). `existing_cre_concentration =169round(existing_cre_exposure / total_loans_outstanding, 4)`.170`selected_post_approval_cre_concentration = round((existing_cre_exposure +171selected_requested_amount) / (total_loans_outstanding +172selected_requested_amount), 4)` — **both numerator and denominator grow by the173new principal**. `selected_policy_variance_bps = round((post_unrounded -174cre_policy_limit_pct) * 10000, 2)` (Harbor: 0.5349 − 0.29 ⇒ 2449.15 bps).175176**CRITICAL denominator rule:** concentration denominators use177`metrics.total_loans_outstanding` (e.g. 14,334,094.87 for Lakeview), **never**178`branches.total_assets` (which is ~25× larger and would make every ratio179trivially tiny).180181**post_approval_concentrations (reporting section):**182- `exposure_after_approval` = existing sector exposure + Σ **full**183 `approved_amount` of approve/conditional apps in that sector (use approved184 principal, not bank_capacity_used).185- denominator = `total_loans_outstanding + gross_approved_amount` (pro-forma186 book including all newly approved principal). Verified on all four Lakeview187 sectors: Construction 3,934,283.26 / 18,908,348.32 = 0.2081, etc.188- `post_approval_pct = round(exposure_after_approval / denom, 4)`.189- `over_limit = post_approval_pct > limit_pct` (boolean).190191**concentration_flags:** for each approve/conditional app whose sector is at or192near its ceiling, report `sector`, `application_id`, `limit_pct`,193`post_approval_pct` (4dp), `flag` (true when the deal breaches/near-breaches194the sector limit on a committed-capacity basis), `handling` ∈195`{approve, conditional_approve, decline, participation_required, none}`. Sort196by sector then application_id.197198### 2.4 CDFI factor scores & risk classes (watch-list)199200CDFI factor score = sum of four sub-scores (lower = better); **null/missing201factors score 0**.202- `ltv`: `<0.40`→0, `0.40–0.60`→2, `0.60–0.80`→4, `>0.80`→6203- `fico`: `>720`→0, `680–720`→1, `580–679`→3, `<580`→5 (null→0)204- `debt_to_asset`: `<0.40`→0, `0.40–0.60`→2, `0.60–0.80`→4, `>0.80`→6 (null→0)205- `liquidity_months`: `>12`→0, `6–12`→1, `3–6`→3, `<3`→5 (null→0)206207Verified: SUM-LN-003 (ltv 0.9511→6, fico null→0, dta 0.3579→0, liq 11.6→1) ⇒208factor_score 7 ⇒ Desirable. SUM-LN-902 (ltv 1.18→6, dta 0.88→6, liq 1.6→5) ⇒20917.210211**Risk class from factor_score** (`policies.cdfi_factor_scores.classes`):212- `Prime`: 0–5213- `Desirable`: 6–9214- `Satisfactory`: 10–13215- `Watch`: 14–18216- `Doubtful`: ≥19217- `Projected Loss`: ≥19 **and** ltv > 1.0218219**Severe-delinquency override:** a loan with `payment_status == "Nonaccrual"`220is classed **`Projected Loss`** regardless of its factor score (it overrides the221score-range mapping). Verified on SUM-LN-902 (factor 17, Nonaccrual ⇒ Projected222Loss). `90+ Days Past Due` does **not** override — SUM-LN-011/015 stay at their223score-derived class.224225### 2.5 +200bp DSCR stress (watch-list) and CRE dual stress226227From `policies.stress`:228- `watch_list_parallel_shock = "+200bp"` ⇒ `shock_label = "+200bp"`.229- **Watch-list formula:** `stressed_dscr = dscr / (1 + 0.18)` =230 `dscr / 1.18`. Verified: SUM-LN-003 1.59/1.18 = 1.35; SUM-LN-004 1.01/1.18 =231 0.86.232- **CRE dual-stress formula** (`cre_dual_stress_formula`):233 `stressed_dscr = dscr * 0.85 / (1 + 0.18)` = `dscr * 0.85 / 1.18`. Verified:234 HAR-APP-901 1.47×0.85/1.18 = 1.06; HAR-APP-902 1.32×0.85/1.18 = 0.95.235 Report `formula` as the compact expression, e.g. `"dscr * 0.85 / 1.18"`.236- `coverage_breach_threshold = 1.0`. `breaches_threshold = stressed_dscr <237 1.0` (i.e. stressed < 1.0). Use strict `<` (Harbor 901 stressed 1.06 ⇒ false;238 902 stressed 0.95 ⇒ true).239- Stress results include **only loans with DSCR available** (skip null-dscr240 loans such as SUM-LN-010). DSCR values round to 2dp.241- `breach_loan_ids` = ascending loan_id list of loans whose stressed DSCR242 breaches.243244### 2.6 Decline reason codes & decision enums245246**Decision enum:** `approve`, `conditional_approve`, `decline`, `defer`,247`participation_required` (used as a per-application `decision` and as the248selected `path`).249250**Conditions enum:** `participation_required`, `reduced_amount`,251`board_exception`, `sba_guaranty_required`, `startup_monitoring`, `none`.252Approve ⇒ `["none"]`; conditional_approve lists the mitigations in effect.253254**Decline reason codes** (assign to declined apps; sorted ascending255alphabetically in output):256- `high_ltv` — ltv elevated past the 0.80 boundary (borderline) on an app257 declined for compounding weakness; clearly high when ltv > 0.85.258- `weak_dscr` — base DSCR < 1.25 (when DSCR applicable). In CRE-decision259 contexts, `weak_dscr` is instead triggered by **stressed DSCR < 1.0** (the260 coverage breach) — verified: HAR-APP-902 (stressed 0.95) gets `weak_dscr`,261 HAR-APP-901 (stressed 1.06) does not.262- `low_fico` — `fico < 580`.263- `recent_bankruptcy` — `bankruptcy_months_ago` not null and `< 24` months.264- `startup_risk` — `years_in_business < 2`.265- `sector_breach` — the app's sector post-approval concentration breaches (or266 would breach) `limit_pct` without mitigation.267- `capacity_limit` — the app is credit-acceptable but cannot be funded within268 remaining lending capacity after higher-priority approvals (e.g. LAK-APP-006:269 ltv 0.7749, fico 668 — passes credit, declined only on capacity).270- `fdic_adverse_variance` — branch FDIC delinquency materially exceeds271 benchmark (applies to all apps at an under-performing branch in a CRE272 decision; both Harbor apps carry it).273- `ncua_peer_weakness` — credit-union segment's state is weaker than national274 and peer median.275- `underwater_collateral`, `policy_floor_missing`, `documentation_gap` — use276 when collateral/value, policy floor, or documentation gaps are the binding277 weakness (`documentation_complete == 0` ⇒ `documentation_gap`).278279**Decision logic (transferable):**280- `approve` — passes every credit test (ltv ≤ 0.85 band, dscr ≥ 1.25 where281 applicable, fico ≥ 580, no recent bankruptcy, years ≥ 2, documentation282 complete) AND fits capacity and sector ceilings. `approved_amount` =283 `requested_amount`; `bank_capacity_used = approved_amount`.284- `conditional_approve` — credit-acceptable but needs a mitigation:285 `participation_required` (sector near ceiling), `sba_guaranty_required`286 (startup risk partly offset by SBA guaranty), `startup_monitoring`,287 `reduced_amount`, or `board_exception`.288- `decline` — compounding credit weaknesses (e.g. high_ltv + weak_dscr; or289 low_fico + recent_bankruptcy) or `capacity_limit`.290- `defer` — for a competing-credit scenario, the weaker credit that is not291 selected but not outright declined (remediation possible). `unselected_disposition`292 ∈ `{decline, defer}`.293294### 2.7 Watch-list action coverage & workout queues295296**Watch-list action coverage (regrade tasks, e.g. Redwood):** covers the subset297of the regrade population whose **final (re-derived) rating ≥ 6**. Loans298regraded to ≤ 5 are dropped from follow-up (they are no longer adverse).299`covered_loan_count` / `covered_exposure` = Σ over final ≥ 6. Group `by_action`,300each action sorted ascending alphabetically; `loan_ids` ascending.301302**Action mapping by final_rating** (Redwood pattern):303- final `8` (Nonaccrual) → `partial_chargeoff_review`304- final `7` → `special_assets`305- final `6` → `watchlist`306307**Workout queue (watch-list tasks, e.g. Summit):** includes **all** adverse308loans (`current_rating >= adverse_rating_min`), each assigned a309`recommended_action` based on a payment-status-first cascade, then risk class.310Sort the queue **descending by exposure, then ascending loan_id**.311312**Action cascade (verified on Summit):**3131. `Nonaccrual` → `partial_chargeoff_review` (`projected_loss = true`)3142. `90+ Days Past Due` → `special_assets` (`projected_loss = false`)3153. `Current` + class `Watch` → `special_assets`3164. `Current` + class `Desirable` → `watchlist`3175. (`Current` + `Prime`/mild → `monitor`; `Doubtful`/`Projected Loss` current →318 `workout` — extend by analogy for unseen buckets.)319320**watch_list_summary:**321- `adverse_rating_min` = the threshold from the prompt (e.g. 6).322- `adverse_loan_count` = count of loans with `current_rating >= adverse_rating_min`.323- `adverse_balance` = Σ `outstanding_balance` of those loans (verified:324 Summit 7,675,179.41).325- `risk_classes` = one row per adverse loan, sorted ascending `loan_id`, with326 `factor_score` (integer) and `risk_class` (with Nonaccrual ⇒ `Projected Loss`327 override).328- `monitoring_cadence` = `monthly` for an adverse watch-list.329330**stress_results:** all DSCR-bearing adverse loans, ascending loan_id, with331`base_dscr`, `stressed_dscr = base/1.18` (2dp), `breaches_threshold`.332`breach_loan_ids` ascending.333334**severe_bucket_counts:** group adverse loans by `(current_rating,335payment_status)`, sorted ascending `current_rating` then payment_status. Note336payment_status ascending is **string-ascending**, so `"90+ Days Past Due"`337sorts before `"Current"` (digit `9` < `C`), and `"Current"` before338`"Nonaccrual"`. `loan_count` and `exposure` (Σ outstanding_balance) per bucket.339340## 3. Per-task archetypes & method341342### A. Rating-migration review (Redwood)3431. `GET /api/policies`, `/api/branches/REDWOOD`, `/api/branches/REDWOOD/loans?min_current_rating=3`,344 `/api/branches/REDWOOD/metrics`, `/api/benchmarks/fdic/q4-2024`.3452. Re-derive `final_rating` per loan (2.1). Build `final_rating_exposure_totals`346 (all target loans, ascending final_rating — count + Σ exposure) and347 `migration_from_current_rating_3` (only loans whose current_rating == 3,348 grouped by final_rating, with ascending `loan_ids`).3493. `watch_list_action_coverage` over final ≥ 6 (2.7).3504. `npa_benchmark` (2.2) with `total_loans_noncurrent_pct`.3515. `material_downgrades` = loans with `final - current >= 2` notches worsening,352 ascending loan_id.3536. `top_problem_credit` = the worst final_rating loan (prefer Nonaccrual / highest354 final / largest exposure); include `borrower_name`, `payment_status`,355 `recommended_action`.356357### B. Quarterly allocation package (Lakeview)3581. `GET /api/branches/LAKEVIEW`, `/applications`, `/sector-exposures`,359 `/metrics`, `/api/policies`.3602. Decide each application (2.6); compute `approved_amount` and361 `bank_capacity_used` per decision (2.3). Decisions sorted ascending362 `application_id`.3633. Aggregate `allocation` (2.3); `priority_ranking` = approved + conditional364 application_ids, highest committee funding priority first (strategic/stronger365 credits lead).3664. `concentration_flags` per near-ceiling conditional app (2.3).3675. `decline_reasons` map (only declined apps) with sorted reason-code lists.3686. `post_approval_concentrations` for every sector touched by an approval369 (reporting denominator = total_loans_outstanding + gross_approved_amount;370 2.3), sorted ascending sector.371372### C. Credit-union segment posture (Civic NC)3731. `GET /api/credit-union-segments/{segment_id}`, `/api/policies`,374 `/api/benchmarks/ncua/q1-2025`, `/api/manifest`.3752. `state_metrics` from the segment `state_code` NCUA row (integer values);376 `benchmark_version = "ncua_q1_2025"`.3773. `peer_comparison`: `peer_states` from segment (ascending); direction vs `US`378 row and vs **median** of peer rows (2.2), each ∈ {higher, lower, equal}.3794. `posture` from capacity vs external risk: capacity available + external risk380 weaker ⇒ `continue_with_tighter_conditions`; capacity constrained ⇒381 `temporarily_pause`; external risk strong ⇒ `continue_approving`.3825. `controls.required_checklist_gates` = segment `minimum_checklist`;383 `added_operating_controls` = the standard tighter set (pre-close insurance384 binder verification, lien perfection prior to funding, senior underwriter385 second review, quarterly state benchmark monitoring, monthly segment386 delinquency watch); include `committee_exception_for_capacity_overrun` only387 when capacity is constrained.3886. `escalation_triggers`: ascending `trigger_id` (ET001 delinquency ≥ 90 bps →389 `credit_risk_manager`; ET002 missing insurance/lien exception →390 `operations_control_manager`; ET003 quarterly capacity exceeded/exception →391 `lending_committee_chair`). Condition/owner enums only.3927. `interpretation`: derive `capacity_status`, `external_risk_status`,393 `risk_tolerance` (from segment), and the matching `committee_message`.394395### D. Watch-list stress packet (Summit)3961. `GET /api/branches/SUMMIT`, `/api/branches/SUMMIT/loans?min_current_rating=6`,397 `/api/policies`.3982. `watch_list_summary` (2.7): factor scores, risk classes (Nonaccrual ⇒399 Projected Loss), `adverse_balance`, `monitoring_cadence = monthly`.4003. `stress_results` (2.5): `+200bp`, `dscr/1.18`, threshold 1.0, DSCR-bearing401 loans only.4024. `workout_queue` (2.7): all adverse loans, action cascade, descending exposure403 then ascending loan_id.4045. `severe_bucket_counts` (2.7) with the string-ascending payment_status order.405406### E. Competing CRE decision (Harbor)4071. `GET /api/branches/HARBOR`, `/metrics`, `/loans?loan_type=CRE`,408 `/sector-exposures`, `/applications`, `/api/policies`,409 `/api/benchmarks/fdic/q4-2024`.4102. For each competing CRE app compute `weighted_cdfi_score` (1dp, lower better)411 using `cre_weighted_score` weights (capacity 0.45, collateral_exposure 0.36,412 conditions 0.11, character 0.05, capital 0.03) — capacity (DSCR) and413 collateral (LTV) carry 81% of the weight; map to `score_class`414 (`approve_quality` ≤ 2.0, `conditional` ≤ 3.0, `weak` > 3.0).4153. `stress` (2.5 CRE dual): `formula = "dscr * 0.85 / 1.18"`, threshold 1.0.416 `weak_dscr` reason fires when stressed < 1.0.4174. `concentration` (2.2/2.3): existing & post-approval CRE concentration vs418 `cre_policy_limit_pct`; FDIC variance vs `total_real_estate_30_89_pct`.4195. `applications_compared` (ascending application_id): each with score, class,420 decision, sorted reason_codes. Reasons include `fdic_adverse_variance` and421 `sector_breach` (both apps at an under-performing, CRE-heavy branch) plus422 `weak_dscr` only for the stressed-breach app.4236. `recommended_path`: select the **lower** (better) weighted score app; its424 `decision` becomes `path`. `unselected_disposition` ∈ `{decline, defer}`;425 unselected reason_codes ascending.4267. `conditions`: for a `participation_required` selected CRE path, the full set427 (all 7 enum values), sorted ascending alphabetically:428 bank_retained_exposure_cap, committee_cre_exception,429 minimum_dscr_covenant_1_25, no_additional_cre_without_committee_review,430 quarterly_financial_reporting, tenant_roll_and_lease_review,431 updated_appraisal_before_close.432433## 4. Output field definitions & exact enums (consolidated)434435- `payment_status`: `Current`, `30 Days Past Due`, `60 Days Past Due`,436 `90+ Days Past Due`, `Nonaccrual`.437- `recommended_action` / watch-list `action`: `monitor`, `watchlist`,438 `special_assets` (underscore — this is the spelling used by both the Redwood439 and Summit templates and their gold answers), `workout`,440 `partial_chargeoff_review`, `legal_referral`. Still copy the exact token from441 the task's `answer_template.json` enum in case a future template differs.442- `decision`: `approve`, `conditional_approve`, `decline`, `defer`,443 `participation_required`.444- `conditions`: `participation_required`, `reduced_amount`, `board_exception`,445 `sba_guaranty_required`, `startup_monitoring`, `none`.446- `handling`: `approve`, `conditional_approve`, `decline`,447 `participation_required`, `none`.448- `risk_class`: `Prime`, `Desirable`, `Satisfactory`, `Watch`, `Doubtful`,449 `Projected Loss`.450- `score_class`: `approve_quality`, `conditional`, `weak`.451- `posture`: `continue_approving`, `continue_with_tighter_conditions`,452 `temporarily_pause`.453- `capacity_status`: `capacity_available`, `capacity_constrained`,454 `no_capacity`.455- `external_risk_status`: `stronger_than_national_and_peers`,456 `mixed_vs_national_and_peers`, `weaker_than_national_and_peers`.457- `risk_tolerance`: `restrained`, `moderate`, `expansive`.458- `committee_message` (segment): `capacity_available_but_external_risk_weaker`,459 `pause_until_state_metrics_recover`, `routine_approval_path_supported`.460- `unselected_disposition`: `decline`, `defer`.461- Decline `reason_code` set: `capacity_limit`, `sector_breach`, `weak_dscr`,462 `high_ltv`, `low_fico`, `recent_bankruptcy`, `startup_risk`,463 `underwater_collateral`, `policy_floor_missing`, `documentation_gap`,464 `fdic_adverse_variance`, `ncua_peer_weakness`.465466**Ordering rules (apply unless the template says otherwise):**467- lists of loans/applications/actions: ascending `loan_id` / `application_id`;468- `final_rating` ascending; `sector` ascending; `action`/condition/reason-code469 ascending alphabetically; `trigger_id` ascending;470- `material_downgrades` ascending `loan_id`; `workout_queue` **descending**471 exposure then ascending `loan_id`; `severe_bucket_counts` ascending472 `current_rating` then string-ascending `payment_status`;473- `priority_ranking`: highest priority first (approved + conditional only);474- numeric lists inside objects follow the `ordering` field in the template.475476## 5. Numeric conventions477478- Money / exposure / balance: **2 decimals** USD (e.g. 1725000.00).479- Ratios (concentration, NPA, FDIC variance, post_approval_pct, limit_pct):480 **4 decimals** as a ratio (0.1897, not 18.97%).481- `variance_bps` / `*_variance_bps` / `policy_variance_bps`: **2 decimals**,482 **signed** (positive = branch/app worse than benchmark/policy); computed from483 the **unrounded** ratio difference × 10000.484- `dscr` / `base_dscr` / `stressed_dscr`: **2 decimals**.485- `weighted_cdfi_score`: **1 decimal** (lower is better).486- `factor_score`, ratings, counts, bps-in-state-metrics: integers.487- Percentages in policy/templates are ratios (0.19), never percent integers.488- Round only at output; keep unrounded intermediates for variance_bps so the bps489 figure stays consistent with the rounded ratio (Redwood 0.1135 ratio but490 1037.49 bps, not 1037.00).491492## 6. Common misjudgments & exclusion rules4934941. **Regrade population ≠ watch-list coverage.** Regrade population =495 `current_rating >= target_current_rating_min`. Watch-list action coverage =496 subset with **re-derived final_rating ≥ 6**. Do not list final ≤ 5 loans in497 coverage, and do not use current_rating to decide coverage.4982. **Severe-delinquency override is Nonaccrual only.** Nonaccrual ⇒499 `Projected Loss` regardless of factor_score. `90+ Days Past Due` does **not**500 override the score-derived class (SUM-LN-011/015 stay Desirable).5013. **Concentration denominator = `total_loans_outstanding`, not `total_assets`.**502 `total_assets` is ~25–35× larger and yields trivially small ratios. Always503 pull `total_loans_outstanding` from `metrics` (latest quarter).5044. **Pro-forma denominator grows with new principal.** For505 `post_approval_concentrations` the denominator = `total_loans_outstanding +506 gross_approved_amount`; for CRE `selected_post_approval_cre_concentration`507 both numerator and denominator grow by the selected `requested_amount`.508 Keep numerator and denominator consistent.5095. **`approved_amount` is always full principal; `bank_capacity_used` is the510 reduced, retained figure.** SBA-guaranty ⇒ `approved × (1 −511 sba_guaranty_pct)`; participation ⇒ sized to sector limit. Never substitute512 `bank_capacity_used` into `gross_approved_amount` or sector513 `exposure_after_approval`.5146. **`variance_bps` uses the unrounded ratio.** Recompute from the raw515 `exposure/total` fraction before multiplying by 10000, then round to 2dp.516 This avoids the 0.5–1 bps drift that matching judges penalize.5177. **Stress excludes null-DSCR loans.** `stress_results` lists only loans with a518 DSCR; null-dscr loans (often Consumer/Equipment) still appear in519 `risk_classes` and `workout_queue`.5208. **Material downgrade threshold is 2 notches.** Include only loans worsened by521 ≥ 2 (`final - current >= 2`). Single-notch downgrades are not "material".5229. **`benchmark_metric` choice tracks portfolio focus.** General NPA review ⇒523 `total_loans_noncurrent_pct`; CRE decision task ⇒524 `total_real_estate_30_89_pct` paired with `delinquency_30_plus_pct`. Do not525 mix a noncurrent metric with a 30-89 branch figure.52610. **Payment-status ordering is string-ascending, not severity-ascending.**527 `"90+ Days Past Due"` < `"Current"` < `"Nonaccrual"` because `'9' < 'C'`.528 In `severe_bucket_counts`, a rating-7 "90+ DPD" bucket sorts before a529 rating-7 "Current" bucket.53011. **`top_problem_credit` should be the genuinely worst credit** (highest531 final_rating, prefer Nonaccrual, then largest exposure), not the largest532 balance regardless of rating.53312. **Selection in a competing-credit task = lower weighted score.** The534 selected app is the one with the **lower** `weighted_cdfi_score` (better535 quality); its `decision` becomes the `path`; the other is536 `defer`/`decline`.53713. **Always honor the exact enum spelling in the target `answer_template.json`.**538 The action enum is consistently `special_assets` (underscore) across the539 train templates and gold answers; still verify against the target template's540 `allowed_values` rather than assuming.54114. **Do not invent peer states or benchmark versions.** Peer states come from542 the segment JSON; benchmark versions come from `/api/manifest`. Use only the543 enum choices and identifiers the template allows.544545## 7. End-to-end execution checklist5465471. Read the prompt; fix the `branch_id` / `segment_id`, as-of date, and which548 answer_template to mirror.5492. `GET /api/policies` and `/api/manifest` first; cache the rating tables,550 stress formulas, score weights, and benchmark versions.5513. Pull the branch, metrics (latest quarter), loans (`?min_current_rating=` as552 needed), sector-exposures, applications, and the relevant benchmark553 (FDIC and/or NCUA all-rows).5544. Re-derive ratings / scores / stresses with the rules above; never trust the555 stored `current_rating` as the final answer.5565. Assemble the JSON exactly matching the template's `required_top_level_keys`557 and per-section `required_keys`, applying ordering and precision rules.5586. Emit only valid JSON — no narrative outside the object. Money 2dp, ratios 4dp,559 bps 2dp signed, scores 1dp, DSCR 2dp.