target-revenue/workplans/TREV-WP-0013-remission-credit-automation.md
tegwick 3064c0fe0c Go-live T05 + WP-0013/0014: first Phase and Control Plane completion
Accept WP-0008-T05 for trsl:phase:info-tech-canon-service-surface
(history/260805-T05-GoLive-info-tech-canon.md). Finish WP-0013 remission
automation and WP-0014 extension/breach/attestation Control Plane UI.
Update SCOPE, README, and pilot-candidate notes for pilot Stage 1.
2026-08-05 16:00:06 +02:00

119 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
id: TREV-WP-0013
type: workplan
title: "Remission Credit automation (degeneration policy execution)"
domain: infotech
repo: target-revenue
status: finished
owner: claude
topic_slug: infotech
created: "2026-07-30"
updated: "2026-08-05"
state_hub_workstream_id: "4a423219-2a3e-413c-b0dc-f8542c2afba1"
---
# Remission Credit automation (degeneration policy execution)
Spun out of `workplans/TREV-WP-0012-phase-provenance-and-policy-modeling.md`
(use case 4). Every Phase declares a `degeneration_policy`
(`trsl:policy:linear-longstop-v0` is the accepted v1 norm,
`specs/OpenQuestions-WorkingDefaults.md` Q7), which is supposed to
generate `remission-credit` Target Ledger entries over time when
Development Credit progress is insufficient (FR-6,
`specs/ProductRequirementsDocument.md`). **Nothing in this codebase
previously computed or wrote these entries.** `fold.py` can consume them
if they exist; this workplan produces them. This gap predated the Control
Plane UI work — it was never in scope for WP-0006 (Trust Service) or
WP-0009 (Control Plane), and surfaced when reviewing the UI's
Phase-registration flow prompted a fuller look at what a Phase's lifecycle
actually requires end to end.
Blocked on `workplans/TREV-WP-0012-phase-provenance-and-policy-modeling.md`
T03's decision (how a policy id maps to its spec file) — **unblocked**
when WP-0012 finished and WP-0015 landed `specs/policies/linear-longstop-v0.md`.
```task
id: TREV-WP-0013-T01
status: done
priority: high
state_hub_task_id: "d6ab7448-c009-4398-b616-560e8085cf1a"
```
**Design the remission calculation and scheduling model.** `R(t) = T0 ×
clamp((t - t0)/(tL - t0), 0, 1)` (Q7) needs: a chosen recognition cadence
(daily/monthly UTC per Q7's own text), an idempotent
"has this period's remission already been recorded" check (ledger entries
are append-only — a re-run must not double-remit), and a decision on
where `t0` (Phase activation) comes from if it isn't already a manifest
field (registration time? an explicit `activated_at`?). Cross-check
against whatever `trsl:policy:linear-longstop-v0`'s spec file (once
WP-0012-T03 lands) says, rather than re-deriving the formula from Q7
prose alone.
**Result (2026-08-05):** Design recorded in
`src/target_revenue/remission.py` module docstring and
`specs/policies/linear-longstop-v0.md` Status section:
1. **t0** = Trust Service `phase_manifests.registered_at`. No new
manifest `activated_at` field for Stage 0 — a Phase is not active
until registered. Pure callers pass `t0` explicitly.
2. **Cadence / idempotency**: cumulative delta model, not period-keyed
rows. Each run remits `max(0, R(as_of) − Σ policy remission-credit)`.
Re-run at the same `as_of` is a no-op; missed schedules catch up
without double-counting. Dust floor `MIN_REMISSION_AMOUNT = 0.01`.
Scheduled convention: monthly UTC (1st 00:00) or longstop if sooner.
3. **Actor**: dedicated Licensor credential labeled
`system:policy-engine` (rights operator), auto-issued on first use.
Never a null `submitted_by_token`; never a human credential for
policy-driven rows. Control Plane audit still records which human
*triggered* an on-demand run.
4. Formula checked against `specs/policies/linear-longstop-v0.md`, not
re-derived from Q7 prose alone.
```task
id: TREV-WP-0013-T02
status: done
priority: high
state_hub_task_id: "078da2e7-594b-4bd3-85b6-881a808f1ca2"
```
**Implement and test** the calculation as a pure function (mirroring
`fold.py`'s determinism discipline) plus whatever writes the resulting
`remission-credit` entries into the hosted Target Ledger — a scheduled
job, an on-demand Control Plane action, or both. Decide which actor
"submits" these entries for attribution purposes (no human credential
naturally owns a policy-driven entry) and record that decision explicitly
rather than leaving `submitted_by_token` implicitly null.
**Result:** `src/target_revenue/remission.py` — pure
`cumulative_remission` / `plan_remission` / `build_remission_entry_input`
plus hosted `apply_remission_for_phase` /
`apply_remission_for_all_phases`. Trust Service routes:
`POST /phases/{id}/remission` (on-demand, Operator+),
`POST /remission/run` (batch for cron). Control Plane:
`control_plane.apply_policy_remission` + form on phase detail.
Tests: `tests/test_remission.py` (pure), `tests/test_remission_hosting.py`
(Docker Postgres — append, idempotency, `system:policy-engine`
attribution, metrics forecasts).
```task
id: TREV-WP-0013-T03
status: done
priority: medium
state_hub_task_id: "fe12ee80-2868-4857-83ca-04bd7223e270"
```
**Surface it in the Control Plane UI**: `phase_detail.html`'s Ledger
table already renders `remission-credit` rows generically once they
exist; verify that holds, and add a metrics-level explanation (e.g. next
scheduled remission date/amount) if `metrics.py` doesn't already forecast
one.
**Result:** `metrics.compute_metrics` gains optional `activated_at` and
forecasts `remission_if_applied_now`, `next_scheduled_remission_at`,
`next_scheduled_remission_amount` (labeled **forecast**, never facts).
Hosted metrics and Control Plane phase detail pass
`registered_at` as t0. `phase_detail.html` shows the forecast table,
activation/longstop facts, and an Operator+ "Apply policy remission now"
button. Existing ledger table continues to render `remission-credit`
rows generically.