(?:\d+\.\d+\s+)?[^.]*rate of\s+\$([\d,]+(?:\.\d+)?)\s*per[^.]*\.)",
+ re.IGNORECASE,
+)
+
+
+class ClauseNotFoundError(Exception):
+ """Raised when a required governing clause can't be confidently located - this is exactly one of the
+ two conditions (app/exceptions.py) that makes a renewal a genuine exception rather than a guess."""
+
+ def __init__(self, field_name: str):
+ super().__init__(f"could not locate the '{field_name}' clause in the contract text")
+ self.field_name = field_name
+
+
+def _extract_number(pattern: re.Pattern, text: str, field_name: str) -> tuple[float, str]:
+ match = pattern.search(text)
+ if not match:
+ raise ClauseNotFoundError(field_name)
+ number_str = match.group(2).replace(",", "")
+ return float(number_str), match.group("sentence").strip()
+
+
+def extract_contract_terms(contract_text: str) -> ContractTerms:
+ """Never partially succeeds silently: if any of the three governing facts can't be found, this raises
+ ClauseNotFoundError rather than returning a ContractTerms with a guessed or zero value - a missing
+ clause must surface as an exception, never as a quiet default (FR-equivalent of Task 1's 'never
+ bluffs' principle, carried over as an engineering discipline, not shared code)."""
+ entitlement, entitlement_quote = _extract_number(_ENTITLEMENT_RE, contract_text, "entitlement_units")
+ fee, fee_quote = _extract_number(_FEE_RE, contract_text, "monthly_fee")
+ rate, rate_quote = _extract_number(_OVERAGE_RATE_RE, contract_text, "overage_rate")
+ return ContractTerms(
+ entitlement_units=entitlement,
+ entitlement_quote=entitlement_quote,
+ monthly_fee=fee,
+ fee_quote=fee_quote,
+ overage_rate=rate,
+ overage_rate_quote=rate_quote,
+ )
diff --git a/use-cases/iashutoshyadav/renewal-true-up-engine/app/models.py b/use-cases/iashutoshyadav/renewal-true-up-engine/app/models.py
new file mode 100644
index 00000000..b4660edf
--- /dev/null
+++ b/use-cases/iashutoshyadav/renewal-true-up-engine/app/models.py
@@ -0,0 +1,72 @@
+"""Data model for one renewal cycle. Deliberately small - this is a single-cohort renewal tool, not a
+general contract database (that's Task 1's job, and this project stays separate from it by design)."""
+
+from __future__ import annotations
+
+from dataclasses import dataclass, field
+from datetime import date
+
+
+@dataclass
+class Customer:
+ customer_id: str
+ name: str
+
+
+@dataclass
+class ContractTerms:
+ """Every field carries the exact quote it was read from - the whole point of this build (per the task
+ card's own 'what strong looks like' bar) is that the governing clause is real, not assumed."""
+
+ entitlement_units: float
+ entitlement_quote: str
+ monthly_fee: float
+ fee_quote: str
+ overage_rate: float
+ overage_rate_quote: str
+
+
+@dataclass
+class UsagePeriod:
+ customer_id: str
+ period_label: str # e.g. "2026-07"
+ units_used: float
+
+
+@dataclass
+class TrueUpResult:
+ customer_id: str
+ entitlement_units: float
+ units_used: float
+ overage_units: float
+ overage_rate: float
+ true_up_amount: float
+ governing_clause_quote: str
+
+
+@dataclass
+class RenewalException:
+ customer_id: str
+ reason: str # "overage_true_up_required" | "clause_not_found" | ...
+
+
+@dataclass
+class RenewalPackage:
+ customer: Customer
+ terms: ContractTerms | None
+ usage: UsagePeriod
+ true_up: TrueUpResult | None
+ exception: RenewalException | None
+ session_id: str | None = None
+ amendment_session_id: str | None = None
+ exported_summary_markdown: str | None = None
+ decisions: list[dict] = field(default_factory=list)
+ renewal_quote: str | None = None
+ talk_track: str | None = None
+
+
+def is_clean(package: RenewalPackage) -> bool:
+ """A renewal is clean (moves through without a human) only if it has no exception at all - per the
+ task card: 'the cohort batch surfaces real exceptions instead of dumping everything into review',
+ which only means something if 'clean' is a real, checkable state, not just 'nobody flagged it yet'."""
+ return package.exception is None
diff --git a/use-cases/iashutoshyadav/renewal-true-up-engine/app/renewal_engine.py b/use-cases/iashutoshyadav/renewal-true-up-engine/app/renewal_engine.py
new file mode 100644
index 00000000..6b49cb2b
--- /dev/null
+++ b/use-cases/iashutoshyadav/renewal-true-up-engine/app/renewal_engine.py
@@ -0,0 +1,168 @@
+"""One customer, start to finish: extract terms -> compute true-up -> decide exception -> generate the
+amendment through SuperDocs -> (human gate for exceptions only) -> export.
+
+Clean vs. exception is where SuperDocs's two approval modes map directly onto the business rule
+(app/true_up.py::determine_exception): a clean renewal's amendment auto-applies (approval_mode=approve_all,
+verified in Phase 4 of the design), because there's nothing that needs a second pair of eyes - the entire
+point of 'the cohort batch surfaces real exceptions instead of dumping everything into review.' An
+exception's amendment pauses for a real human decision (approval_mode=ask_every_time, also verified live),
+same mechanism SuperDocs itself uses for HITL.
+"""
+
+from __future__ import annotations
+
+import uuid
+
+from app.clause_extraction import ClauseNotFoundError, extract_contract_terms
+from app.models import (
+ Customer,
+ RenewalException,
+ RenewalPackage,
+ UsagePeriod,
+)
+from app.superdocs_client import SuperDocsClient
+from app.true_up import compute_true_up, determine_exception
+
+
+class PendingHumanDecision(Exception):
+ """Raised when an exception's amendment is awaiting approval and no decision has been supplied yet.
+ The batch runner (app/batch.py) catches this, surfaces the pending change to a reviewer, and calls
+ apply_human_decision() once a decision exists - this function never blocks waiting for a human."""
+
+ def __init__(self, package: RenewalPackage, job_id: str, change_id: str):
+ super().__init__(f"renewal for {package.customer.customer_id} needs human approval")
+ self.package = package
+ self.job_id = job_id
+ self.change_id = change_id
+
+
+def _renewal_quote_text(package: RenewalPackage) -> str:
+ tu = package.true_up
+ if tu is None or tu.true_up_amount == 0:
+ return (
+ f"{package.customer.name}: renewal at current terms, "
+ f"{package.terms.entitlement_units:,.0f} units/month, no true-up owed this period."
+ )
+ return (
+ f"{package.customer.name}: renewal quote includes a true-up of ${tu.true_up_amount:,.2f} "
+ f"for {tu.overage_units:,.0f} units of overage at ${tu.overage_rate:.2f}/unit."
+ )
+
+
+def _talk_track(package: RenewalPackage) -> str:
+ tu = package.true_up
+ if tu is None or tu.true_up_amount == 0:
+ return (
+ f"{package.customer.name} is renewing cleanly at their current entitlement "
+ f"({package.terms.entitlement_units:,.0f} units/month) - no overage, no true-up conversation needed."
+ )
+ return (
+ f"{package.customer.name} exceeded their {tu.entitlement_units:,.0f}-unit entitlement by "
+ f"{tu.overage_units:,.0f} units this period. Per the signed agreement (\"{tu.governing_clause_quote}\"), "
+ f"that's a true-up of ${tu.true_up_amount:,.2f} at ${tu.overage_rate:.2f}/unit - walk them through the "
+ f"exact math before presenting the renewal quote, the number should never surprise them."
+ )
+
+
+def start_renewal(
+ client: SuperDocsClient,
+ customer: Customer,
+ contract_text: str,
+ usage: UsagePeriod,
+ new_entitlement_units: float,
+ template_name: str | None = None,
+) -> RenewalPackage:
+ """Loads the contract into a SuperDocs session and proposes the entitlement amendment for the new
+ period. For a clean renewal this returns a fully completed package. For an exception, this raises
+ PendingHumanDecision - the amendment is proposed but not yet applied, exactly mirroring 'a person
+ reviews what the system intends to do... before it commits' from the task brief's own Task 1 language,
+ which the same discipline applies to here even though the document doesn't literally require it for
+ Task 2."""
+ # Real bug, found live on 2026-08-20: this used to be a bare f"renewal-{customer.customer_id}" - stable
+ # across every run of the batch. Re-running the same sample cohort against the real API (exactly what
+ # the "Run cohort batch" button invites) eventually left a prior run's async approval job still active
+ # in that session, and the next run's chat_with_approval call failed with a real 409: error_code
+ # 'session_busy' - "The AI is still working on a previous request in this conversation... use a
+ # different session_id" (that suggestion is straight from SuperDocs's own error body). session_id only
+ # needs to be consistent WITHIN one call - it flows through PendingHumanDecision.package.session_id to
+ # the later approve step - not stable across separate runs, so a per-call suffix is safe.
+ session_id = f"renewal-{customer.customer_id}-{uuid.uuid4()}"
+
+ try:
+ terms = extract_contract_terms(contract_text)
+ except ClauseNotFoundError:
+ package = RenewalPackage(
+ customer=customer, terms=None, usage=usage, true_up=None,
+ exception=RenewalException(customer.customer_id, reason="clause_not_found"),
+ session_id=session_id,
+ )
+ return package
+
+ true_up = compute_true_up(customer, terms, usage)
+ exception = determine_exception(customer, true_up)
+
+ client.upload_document(f"{customer.customer_id}-contract.txt", contract_text.encode(), session_id=session_id)
+
+ plain_amendment_message = (
+ f"This is a renewal amendment. Update the entitlement clause so the Monthly Entitlement changes "
+ f"from {terms.entitlement_units:,.0f} to {new_entitlement_units:,.0f} compute units. "
+ f"Leave everything else in the contract unchanged."
+ )
+
+ package = RenewalPackage(
+ customer=customer, terms=terms, usage=usage, true_up=true_up, exception=exception,
+ session_id=session_id, amendment_session_id=session_id,
+ )
+ package.renewal_quote = _renewal_quote_text(package)
+ package.talk_track = _talk_track(package)
+ package.decisions.append({"true_up_amount": true_up.true_up_amount, "exception": exception is not None})
+
+ if exception is None:
+ # Clean: nothing for a human to weigh in on, apply immediately (approve_all). Deliberately never
+ # references the template here - found live on 2026-08-20 that referencing it caused SuperDocs to
+ # generate duplicated amendment sections and an unwanted "Please fill: Client Legal Name"
+ # placeholder instead of the clean single-clause edit this path needs, and a clean renewal applies
+ # with no human catching that before it lands. The plain instruction is what was already verified
+ # to produce a correct, surgical edit.
+ client.chat(plain_amendment_message, session_id=session_id, cross_session_search=True)
+ package.exported_summary_markdown = client.export_document(session_id, format="markdown")
+ return package
+
+ # Exception: a human reviews the proposed amendment before it applies, so it's safe to try the
+ # template-styled version here even though it's a less surgical edit - a bloated or oddly-placeholder'd
+ # proposal gets caught at the review gate instead of landing silently.
+ template_amendment_message = (
+ f"{plain_amendment_message} Use the formatting and signature-block conventions from my uploaded "
+ f"template '{template_name}' for this amendment's presentation only - do not add new sections, "
+ f"do not repeat the entitlement update more than once, and do not treat the customer's name as "
+ f"unknown when it is already present in the document you are editing."
+ if template_name else plain_amendment_message
+ )
+ job = client.chat_with_approval(template_amendment_message, session_id=session_id, cross_session_search=True)
+ resolved = client.wait_for_job(job.job_id) if hasattr(client, "wait_for_job") else client.get_job(job.job_id)
+ if resolved.status != "awaiting_approval" or not resolved.pending_changes:
+ # Nothing actually proposed (e.g. the AI declined) - still an exception, still needs a human,
+ # just with nothing to approve yet. Surfaced as-is rather than silently treated as clean.
+ return package
+ change = resolved.pending_changes[0]
+ raise PendingHumanDecision(package, job_id=job.job_id, change_id=change.change_id)
+
+
+def apply_human_decision(client: SuperDocsClient, package: RenewalPackage, job_id: str, change_id: str, approved: bool, reason: str | None = None) -> RenewalPackage:
+ """Resumes a renewal that PendingHumanDecision paused. Rejecting an amendment doesn't discard the
+ true-up record - the customer's true-up amount and governing clause stay in the package either way,
+ only the document edit itself is applied or not (mirrors FR-18's 'rejecting one finding does not
+ discard the rest', an engineering discipline carried over, not shared code).
+
+ Real bug found and fixed during live verification: approve_change() returning success does not mean
+ the edit has actually landed yet - the job needs to reach status='completed' before an export reflects
+ it. The first version of this function exported immediately after approve_change and got back the
+ UNCHANGED original document every time; this was verified working correctly by hand during design
+ (Phase 4), but that manual polling step never made it into this function's code."""
+ client.approve_change(package.session_id, job_id, change_id, approved=approved, feedback=reason)
+ package.decisions.append({"approved": approved, "reason": reason})
+ if approved:
+ if hasattr(client, "wait_for_job"):
+ client.wait_for_job(job_id)
+ package.exported_summary_markdown = client.export_document(package.session_id, format="markdown")
+ return package
diff --git a/use-cases/iashutoshyadav/renewal-true-up-engine/app/superdocs_client.py b/use-cases/iashutoshyadav/renewal-true-up-engine/app/superdocs_client.py
new file mode 100644
index 00000000..85a3dc9c
--- /dev/null
+++ b/use-cases/iashutoshyadav/renewal-true-up-engine/app/superdocs_client.py
@@ -0,0 +1,326 @@
+"""SuperDocs REST client - real HTTP calls against the confirmed endpoints
+(https://docs.superdocs.app/api-reference), not the MCP transport. Every path/schema here was verified
+against the live API during design (Phase 3/4 of the build plan): a real upload, a real sync edit, a real
+async edit with human-in-the-loop approval, and a real export were all run and inspected before this file
+was written - nothing here is guessed from documentation alone.
+
+Two implementations behind one interface (`SuperDocsClient` protocol): `RealSuperDocsClient` for actual use,
+`FakeSuperDocsClient` for tests that must run without a live key (round-wide requirement - see CONTRIBUTING.md
+and the task brief's "real tests... run without a live key").
+"""
+
+from __future__ import annotations
+
+import base64
+import re
+import time
+from dataclasses import dataclass, field
+from typing import Literal, Protocol
+
+import httpx
+
+BASE_URL = "https://api.superdocs.app"
+
+
+class SuperDocsError(Exception):
+ """Raised on a non-2xx response or a job that ends in status='failed'."""
+
+ def __init__(self, message: str, status_code: int | None = None, detail: object = None):
+ super().__init__(message)
+ self.status_code = status_code
+ self.detail = detail
+
+
+@dataclass
+class ProposedChange:
+ change_id: str
+ operation: str
+ chunk_id: str | None
+ old_html: str | None
+ new_html: str | None
+ ai_explanation: str | None
+
+
+@dataclass
+class ChatResult:
+ """Unified shape for both the sync and completed-async chat paths - callers don't need to care which
+ transport produced it."""
+
+ response_text: str
+ session_id: str
+ updated_html: str | None
+ changes: list[ProposedChange]
+ ops_charged: int
+
+
+@dataclass
+class JobStatus:
+ job_id: str
+ session_id: str
+ status: Literal["pending", "in_progress", "awaiting_approval", "completed", "failed", "cancelled"]
+ pending_changes: list[ProposedChange] = field(default_factory=list)
+ result: dict | None = None
+ error: str | None = None
+
+
+class SuperDocsClient(Protocol):
+ """The four-call contract (upload/chat/approve/export) plus the async+approval path, per
+ docs.superdocs.app - this is the entire surface our business logic depends on."""
+
+ def upload_document(self, filename: str, content: bytes, session_id: str) -> dict: ...
+ def upload_template(self, name: str, content: bytes) -> str: ...
+ def chat(self, message: str, session_id: str, cross_session_search: bool = False) -> ChatResult: ...
+ def chat_with_approval(self, message: str, session_id: str, cross_session_search: bool = False) -> JobStatus: ...
+ def get_job(self, job_id: str) -> JobStatus: ...
+ def approve_change(self, session_id: str, job_id: str, change_id: str, approved: bool, feedback: str | None = None) -> None: ...
+ def export_document(self, session_id: str, format: str = "markdown") -> str | bytes: ...
+
+
+def _parse_changes(raw_changes: list[dict] | None) -> list[ProposedChange]:
+ if not raw_changes:
+ return []
+ return [
+ ProposedChange(
+ change_id=c["change_id"],
+ operation=c.get("operation", "edit"),
+ chunk_id=c.get("chunk_id"),
+ old_html=c.get("old_html"),
+ new_html=c.get("new_html"),
+ ai_explanation=c.get("ai_explanation"),
+ )
+ for c in raw_changes
+ ]
+
+
+class RealSuperDocsClient:
+ """Every method here maps to exactly one verified REST call - no retry/backoff logic added silently;
+ long-running ops (the task brief's own warning: 30s to several minutes) are the caller's job to poll
+ for, not something this client hides behind a blocking sleep loop.
+
+ Real timeout found live on 2026-08-20: the default here was 60s despite the docstring above already
+ quoting "up to several minutes" - a real batch with cross_session_search=True enabled genuinely hit
+ that ceiling ("The read operation timed out") once this account had accumulated enough session history
+ from a day of live testing for cross-session search to take real time. 240s actually matches the
+ documented range instead of contradicting it."""
+
+ def __init__(self, api_key: str, timeout_seconds: float = 240.0):
+ if not api_key:
+ raise ValueError("SUPERDOCS_API_KEY is required - see .env.example")
+ self._client = httpx.Client(
+ base_url=BASE_URL,
+ headers={"Authorization": f"Bearer {api_key}"},
+ timeout=timeout_seconds,
+ )
+
+ def close(self) -> None:
+ self._client.close()
+
+ def upload_document(self, filename: str, content: bytes, session_id: str) -> dict:
+ resp = self._client.post(
+ "/v1/documents/upload",
+ files={"file": (filename, content)},
+ data={"session_id": session_id},
+ )
+ self._raise_for_status(resp)
+ return resp.json()
+
+ def upload_template(self, name: str, content: bytes) -> str:
+ resp = self._client.post(
+ "/v1/templates/upload-base64",
+ json={"filename": name, "file_base64": base64.b64encode(content).decode("ascii")},
+ )
+ self._raise_for_status(resp)
+ return resp.json()["id"]
+
+ def chat(self, message: str, session_id: str, cross_session_search: bool = False) -> ChatResult:
+ resp = self._client.post(
+ "/v1/chat",
+ json={
+ "message": message,
+ "session_id": session_id,
+ "approval_mode": "approve_all",
+ "cross_session_search": cross_session_search,
+ },
+ )
+ self._raise_for_status(resp)
+ body = resp.json()
+ dc = body.get("document_changes") or {}
+ return ChatResult(
+ response_text=body.get("response", ""),
+ session_id=body["session_id"],
+ updated_html=dc.get("updated_html"),
+ changes=_parse_changes(dc.get("changes")),
+ ops_charged=(body.get("usage") or {}).get("ops_charged", 0),
+ )
+
+ def chat_with_approval(self, message: str, session_id: str, cross_session_search: bool = False) -> JobStatus:
+ resp = self._client.post(
+ "/v1/chat/async",
+ json={
+ "message": message,
+ "session_id": session_id,
+ "approval_mode": "ask_every_time",
+ "cross_session_search": cross_session_search,
+ },
+ )
+ self._raise_for_status(resp)
+ body = resp.json()
+ return JobStatus(job_id=body["job_id"], session_id=body["session_id"], status=body["status"])
+
+ def get_job(self, job_id: str) -> JobStatus:
+ resp = self._client.get(f"/v1/jobs/{job_id}")
+ self._raise_for_status(resp)
+ body = resp.json()
+ metadata = body.get("metadata") or {}
+ return JobStatus(
+ job_id=body["job_id"],
+ session_id=body["session_id"],
+ status=body["status"],
+ pending_changes=_parse_changes(metadata.get("pending_changes")),
+ result=body.get("result"),
+ error=body.get("error"),
+ )
+
+ def wait_for_job(self, job_id: str, poll_seconds: float = 2.0, timeout_seconds: float = 300.0) -> JobStatus:
+ """Polls until the job leaves pending/in_progress. Stops at awaiting_approval too - the caller
+ (business logic, not this client) decides what to approve; this method never auto-approves."""
+ deadline = time.monotonic() + timeout_seconds
+ while True:
+ job = self.get_job(job_id)
+ if job.status not in ("pending", "in_progress"):
+ return job
+ if time.monotonic() > deadline:
+ raise SuperDocsError(f"job {job_id} did not resolve within {timeout_seconds}s")
+ time.sleep(poll_seconds)
+
+ def approve_change(self, session_id: str, job_id: str, change_id: str, approved: bool, feedback: str | None = None) -> None:
+ resp = self._client.post(
+ f"/v1/chat/{session_id}/approve",
+ json={"job_id": job_id, "change_id": change_id, "approved": approved, "feedback": feedback},
+ )
+ self._raise_for_status(resp)
+
+ def export_document(self, session_id: str, format: str = "markdown") -> str | bytes:
+ """Real bug found and fixed during live verification, not assumed from docs: text formats
+ (markdown/html/txt) come back as the response BODY directly (Content-Type: text/markdown etc.),
+ not wrapped in a JSON envelope - the first version of this method called resp.json() unconditionally
+ and crashed with a JSONDecodeError on every text export. Only binary formats (docx/pdf) are wrapped
+ in a JSON envelope carrying a signed download_url, per the tool docs - verified separately."""
+ resp = self._client.post("/v1/documents/export", json={"session_id": session_id, "format": format})
+ self._raise_for_status(resp)
+ if format in ("markdown", "html", "txt"):
+ return resp.text
+ # Binary formats (docx/pdf) come back as a signed download_url envelope, per the tool docs -
+ # fetching the actual bytes is the caller's job (usually not needed until final delivery).
+ return resp.json()
+
+ def _raise_for_status(self, resp: httpx.Response) -> None:
+ if resp.status_code >= 400:
+ try:
+ detail = resp.json()
+ except ValueError:
+ detail = resp.text
+ raise SuperDocsError(f"SuperDocs API {resp.status_code}", status_code=resp.status_code, detail=detail)
+
+
+class FakeSuperDocsClient:
+ """Deterministic, offline, no network call - what every test in this project runs against by default,
+ per the round-wide 'real tests, no live key needed' standard. Simulates the same edit-application
+ semantics as the real product closely enough to exercise our business logic (true-up math, clause
+ lookup, exception flagging) without depending on the network or a quota."""
+
+ def __init__(self) -> None:
+ self._documents: dict[str, str] = {} # session_id -> plain text content
+ self._jobs: dict[str, JobStatus] = {}
+ self._pending: dict[str, dict] = {} # change_id -> {session_id, job_id, old, new}
+ self._op_counter = 0
+
+ def upload_document(self, filename: str, content: bytes, session_id: str) -> dict:
+ self._documents[session_id] = content.decode("utf-8", errors="replace")
+ return {"session_id": session_id, "filename": filename, "chunks_count": self._documents[session_id].count("\n") + 1}
+
+ def upload_template(self, name: str, content: bytes) -> str:
+ self._op_counter += 1
+ return f"fake-template-{self._op_counter}"
+
+ def _parse_entitlement_change(self, message: str) -> tuple[str, str] | None:
+ """The fake client doesn't run an LLM, so it can't interpret arbitrary natural language - but this
+ app only ever sends one message shape for an amendment (renewal_engine.py's amendment_message), so
+ matching that specific pattern is enough to make our OWN business logic deterministically testable
+ end to end. This is a simulation of THIS app's usage, not a general chat simulator - documented
+ limitation, not an oversight."""
+ match = re.search(r"from\s+([\d,]+(?:\.\d+)?)\s+to\s+([\d,]+(?:\.\d+)?)\s+compute units", message)
+ if not match:
+ return None
+ return match.group(1), match.group(2)
+
+ def chat(self, message: str, session_id: str, cross_session_search: bool = False) -> ChatResult:
+ self._op_counter += 1
+ parsed = self._parse_entitlement_change(message)
+ if parsed:
+ old_num, new_num = parsed
+ doc = self._documents.get(session_id, "")
+ self._documents[session_id] = doc.replace(old_num, new_num)
+ return ChatResult(
+ response_text=f"[fake] acknowledged: {message}",
+ session_id=session_id,
+ updated_html=self._documents.get(session_id),
+ changes=[],
+ ops_charged=1,
+ )
+
+ def seed_pending_change(self, session_id: str, old_text: str, new_text: str) -> JobStatus:
+ """Test helper: fabricate a pending change the way a real ask_every_time job would produce one,
+ so approval-flow tests don't need a real network call to exercise real HITL logic."""
+ self._op_counter += 1
+ job_id = f"fake-job-{self._op_counter}"
+ change_id = f"fake-change-{self._op_counter}"
+ self._pending[change_id] = {"session_id": session_id, "job_id": job_id, "old": old_text, "new": new_text}
+ job = JobStatus(
+ job_id=job_id,
+ session_id=session_id,
+ status="awaiting_approval",
+ pending_changes=[ProposedChange(change_id, "edit", None, old_text, new_text, "fake explanation")],
+ )
+ self._jobs[job_id] = job
+ return job
+
+ def chat_with_approval(self, message: str, session_id: str, cross_session_search: bool = False) -> JobStatus:
+ """Real implementation (not a stub) for this app's one known message shape - see
+ _parse_entitlement_change. Falls back to a completed no-op job if the message doesn't match
+ anything this fake understands, rather than raising, so a caller that sends an unexpected message
+ gets an honest 'nothing proposed' rather than a crash."""
+ parsed = self._parse_entitlement_change(message)
+ if not parsed:
+ self._op_counter += 1
+ job_id = f"fake-job-{self._op_counter}"
+ job = JobStatus(job_id=job_id, session_id=session_id, status="completed")
+ self._jobs[job_id] = job
+ return job
+ old_num, new_num = parsed
+ return self.seed_pending_change(session_id, old_text=old_num, new_text=new_num)
+
+ def get_job(self, job_id: str) -> JobStatus:
+ return self._jobs[job_id]
+
+ def approve_change(self, session_id: str, job_id: str, change_id: str, approved: bool, feedback: str | None = None) -> None:
+ pending = self._pending.pop(change_id)
+ if approved:
+ doc = self._documents.get(session_id, "")
+ self._documents[session_id] = doc.replace(pending["old"], pending["new"])
+ self._jobs[job_id] = JobStatus(job_id=job_id, session_id=session_id, status="completed")
+
+ def export_document(self, session_id: str, format: str = "markdown") -> str:
+ return self._documents.get(session_id, "")
+
+
+def make_client(api_key: str | None) -> SuperDocsClient:
+ """Factory mirroring Task 1's LLM_PROVIDER=fake pattern: no key -> fake client, real key -> real client.
+ Callers never branch on which one they got."""
+ if api_key:
+ return RealSuperDocsClient(api_key)
+ return FakeSuperDocsClient()
+
+
+def to_base64(content: bytes) -> str:
+ return base64.b64encode(content).decode("ascii")
diff --git a/use-cases/iashutoshyadav/renewal-true-up-engine/app/templates/dashboard.html b/use-cases/iashutoshyadav/renewal-true-up-engine/app/templates/dashboard.html
new file mode 100644
index 00000000..af5be2e3
--- /dev/null
+++ b/use-cases/iashutoshyadav/renewal-true-up-engine/app/templates/dashboard.html
@@ -0,0 +1,214 @@
+
+
+
+
+ Renewal & True-Up Documentation Engine
+
+
+
+ Renewal & True-Up Documentation Engine
+ Assemble a customer cohort's renewal pack, auto-apply clean renewals, and hold genuine exceptions for review.
+ {% if using_fake %}⚡ Running against the offline fake client (no SUPERDOCS_API_KEY set)
{% endif %}
+
+ {% if result and result.skipped_over_limit %}
+
+ ⚠ {{ result.skipped_over_limit|length }} customer(s) were skipped by the operation-limit stopping rule
+ and never processed this run: {{ result.skipped_over_limit|join(", ") }}.
+
+ {% endif %}
+
+
+
+
+
+ {% if result %}
+
+ Clean renewals {{ result.clean|length }}
+ {% if result.clean %}
+
+ Customer Entitlement Usage True-up
+ {% for p in result.clean %}
+
+ {{ p.customer.name }}
+ {{ "%.0f"|format(p.true_up.entitlement_units) if p.true_up else "-" }}
+ {{ "%.0f"|format(p.usage.units_used) }}
+ {{ "$%.2f"|format(p.true_up.true_up_amount) if p.true_up else "-" }}
+ View pack →
+
+ {% endfor %}
+
+ {% else %}
+ No clean renewals this run.
+ {% endif %}
+
+
+
+ Needs human review {{ result.pending_review|length }}
+ {% if result.pending_review %}
+
+ Customer Reason True-up owed
+ {% for item in result.pending_review %}
+
+ {{ item.package.customer.name }}
+ {{ item.package.exception.reason }}
+ {% if item.package.true_up %}${{ "%.2f"|format(item.package.true_up.true_up_amount) }} {% else %}- {% endif %}
+ Review →
+
+ {% endfor %}
+
+ {% else %}
+ Nothing needs review this run.
+ {% endif %}
+
+
+ {% if result.failed %}
+
+ Failed {{ result.failed|length }}
+
+ Customer Error
+ {% for f in result.failed %}
+ {{ f.customer_id }} {{ f.error }}
+ {% endfor %}
+
+
+ {% endif %}
+ {% endif %}
+
+
diff --git a/use-cases/iashutoshyadav/renewal-true-up-engine/app/templates/pack.html b/use-cases/iashutoshyadav/renewal-true-up-engine/app/templates/pack.html
new file mode 100644
index 00000000..b842609d
--- /dev/null
+++ b/use-cases/iashutoshyadav/renewal-true-up-engine/app/templates/pack.html
@@ -0,0 +1,97 @@
+
+
+
+
+ Renewal pack - {{ package.customer.name }}
+
+
+
+ ← back
+ {{ package.customer.name }} clean renewal
+
+
+ Usage & value summary
+
+ entitlement = {{ "%.0f"|format(package.true_up.entitlement_units) }} units/month
+ usage = {{ "%.0f"|format(package.true_up.units_used) }} units this period
+ overage = {{ "%.0f"|format(package.true_up.overage_units) }} units (none - within entitlement)
+
+
+
+ {% if package.renewal_quote %}
+
+ Renewal quote
+ {{ package.renewal_quote }}
+
+ {% endif %}
+
+ {% if package.talk_track %}
+
+ Talk track
+ {{ package.talk_track }}
+
+ {% endif %}
+
+ {% if package.exported_summary_markdown %}
+
+ Amendment document (applied)
+ {{ package.exported_summary_markdown }}
+
+ {% endif %}
+
+
diff --git a/use-cases/iashutoshyadav/renewal-true-up-engine/app/templates/review.html b/use-cases/iashutoshyadav/renewal-true-up-engine/app/templates/review.html
new file mode 100644
index 00000000..7c3e80ac
--- /dev/null
+++ b/use-cases/iashutoshyadav/renewal-true-up-engine/app/templates/review.html
@@ -0,0 +1,206 @@
+
+
+
+
+ Review renewal - {{ item.package.customer.name }}
+
+
+
+ ← back
+ {{ item.package.customer.name }}
+
+ {% if item.package.renewal_quote %}
+
+ Renewal quote
+ {{ item.package.renewal_quote }}
+
+ {% endif %}
+
+ {% if item.package.true_up %}
+
+ True-up calculation
+
+ entitlement = {{ "%.0f"|format(item.package.true_up.entitlement_units) }}
+ usage = {{ "%.0f"|format(item.package.true_up.units_used) }}
+ overage = {{ "%.0f"|format(item.package.true_up.overage_units) }}
+ rate = ${{ item.package.true_up.overage_rate }}
+ true-up = {{ "%.0f"|format(item.package.true_up.overage_units) }} × ${{ item.package.true_up.overage_rate }}
+ = ${{ "%.2f"|format(item.package.true_up.true_up_amount) }}
+
+
+
+
+ Governing clause (from the signed agreement)
+
+
Copy
+
“{{ item.package.true_up.governing_clause_quote }}”
+
+
+
+
+ Proposed amendment
+ Updates the entitlement clause for the new renewal term. This edit has not been applied yet.
+
+
+ {% if item.package.talk_track %}
+
+ Talk track
+ {{ item.package.talk_track }}
+
+ {% endif %}
+
+
+ {% else %}
+
+
+ No amendment could be proposed.
+ Reason: {{ item.package.exception.reason }} - the governing entitlement/rate clause
+ could not be located in this customer's contract text. Nothing was sent to SuperDocs; there is no
+ true-up figure to trust until the contract itself is checked by a human.
+
+
+
+ {% endif %}
+
+
+
+
diff --git a/use-cases/iashutoshyadav/renewal-true-up-engine/app/true_up.py b/use-cases/iashutoshyadav/renewal-true-up-engine/app/true_up.py
new file mode 100644
index 00000000..1dda08a8
--- /dev/null
+++ b/use-cases/iashutoshyadav/renewal-true-up-engine/app/true_up.py
@@ -0,0 +1,37 @@
+"""True-up arithmetic and the exception rule. Both are deliberately simple and fully deterministic -
+transparent and reproducible by the customer is the literal bar the task card sets ('true-up arithmetic
+is transparent and reproducible by the customer'), and a formula a human can re-derive by hand beats a
+black box every time for that specific requirement.
+"""
+
+from __future__ import annotations
+
+from app.models import ContractTerms, Customer, RenewalException, TrueUpResult, UsagePeriod
+
+
+def compute_true_up(customer: Customer, terms: ContractTerms, usage: UsagePeriod) -> TrueUpResult:
+ """overage = max(0, usage - entitlement); true_up = overage * rate. That's the whole formula - no
+ proration, no tiering, no minimums. Matches the worked example this build was designed against:
+ entitlement 100, usage 125, overage 25, rate $20/unit -> true_up $500."""
+ overage_units = max(0.0, usage.units_used - terms.entitlement_units)
+ true_up_amount = overage_units * terms.overage_rate
+ return TrueUpResult(
+ customer_id=customer.customer_id,
+ entitlement_units=terms.entitlement_units,
+ units_used=usage.units_used,
+ overage_units=overage_units,
+ overage_rate=terms.overage_rate,
+ true_up_amount=true_up_amount,
+ governing_clause_quote=terms.overage_rate_quote,
+ )
+
+
+def determine_exception(customer: Customer, true_up: TrueUpResult) -> RenewalException | None:
+ """The exception rule this build committed to (Phase 6 of the design - the task card leaves 'genuine
+ exception' undefined, so this is an engineering decision, not a document requirement): a renewal needs
+ a human whenever real money changes hands (true_up_amount > 0). A clean renewal (no overage) never
+ needed the reviewer's attention in the first place - that's the entire point of 'surfaces real
+ exceptions instead of dumping everything into review.'"""
+ if true_up.true_up_amount > 0:
+ return RenewalException(customer_id=customer.customer_id, reason="overage_true_up_required")
+ return None
diff --git a/use-cases/iashutoshyadav/renewal-true-up-engine/app/web.py b/use-cases/iashutoshyadav/renewal-true-up-engine/app/web.py
new file mode 100644
index 00000000..06f609b7
--- /dev/null
+++ b/use-cases/iashutoshyadav/renewal-true-up-engine/app/web.py
@@ -0,0 +1,120 @@
+"""Minimal review UI: run the cohort batch, see clean renewals vs. genuine exceptions, decide each
+exception one at a time. In-memory state on purpose - this is a single-operator batch review tool for one
+cohort run at a time, not a multi-user persistent system; the task card's actual scope (one renewals
+manager reviewing one cohort) doesn't need a database, and adding one here would be exactly the kind of
+unnecessary feature the brief asks us to skip.
+"""
+
+from __future__ import annotations
+
+import os
+from pathlib import Path
+
+from dotenv import load_dotenv
+from fastapi import FastAPI, Form, Request
+from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
+from fastapi.templating import Jinja2Templates
+
+from app.batch import BatchResult, run_cohort_batch
+from app.renewal_engine import apply_human_decision
+from app.superdocs_client import make_client
+from data.sample_cohort.cohort import load_sample_cohort
+
+# python-dotenv doesn't load .env into os.environ on its own - found live on 2026-08-19 when a real
+# SUPERDOCS_API_KEY set in .env was silently ignored and the app kept running against the fake client
+# with no error, only the (correctly rendered, but unhelpfully unnoticed) "offline fake client" banner
+# as any signal. Must run before _get_client()/dashboard() ever read os.environ.
+load_dotenv()
+
+app = FastAPI(title="Renewal & True-Up Documentation Engine")
+templates = Jinja2Templates(directory="app/templates")
+
+_TEMPLATE_NAME = "renewal_amendment_template.txt"
+_TEMPLATE_PATH = Path(__file__).resolve().parent.parent / "data" / _TEMPLATE_NAME
+
+_state: dict[str, object] = {"result": None, "client": None, "progress": None, "template_uploaded": False}
+
+
+def _get_client():
+ if _state["client"] is None:
+ _state["client"] = make_client(os.environ.get("SUPERDOCS_API_KEY"))
+ return _state["client"]
+
+
+def _ensure_template_uploaded(client) -> str:
+ """Templates persist at the account level across sessions (per docs.superdocs.app), so this only
+ needs to happen once per process, not once per customer - re-uploading it for every renewal would be
+ wasteful and would pile up duplicate template listings over repeated batch runs."""
+ if not _state["template_uploaded"]:
+ client.upload_template(_TEMPLATE_NAME, _TEMPLATE_PATH.read_bytes())
+ _state["template_uploaded"] = True
+ return _TEMPLATE_NAME
+
+
+@app.get("/", response_class=HTMLResponse)
+def dashboard(request: Request):
+ result: BatchResult | None = _state["result"] # type: ignore[assignment]
+ using_fake = os.environ.get("SUPERDOCS_API_KEY", "") == ""
+ return templates.TemplateResponse(
+ request, "dashboard.html", {"result": result, "using_fake": using_fake}
+ )
+
+
+@app.post("/batch/run")
+def run_batch():
+ # Real-time progress wasn't visible anywhere before this - a real batch against the live API takes
+ # 20-90+ seconds (verified live, 2026-08-20), and the button just sat there with zero feedback the
+ # whole time. run_cohort_batch already accepted an on_progress callback; it was just never passed.
+ # Starlette runs this sync route in its thread pool, so a concurrent GET to /batch/progress (below)
+ # is served by a different thread while this is still running - no background-task machinery needed.
+ def on_progress(current: int, total: int, customer_id: str, status: str) -> None:
+ _state["progress"] = {"current": current, "total": total, "customer_id": customer_id, "status": status}
+
+ _state["progress"] = {"current": 0, "total": None, "customer_id": None, "status": "starting"}
+ client = _get_client()
+ template_name = _ensure_template_uploaded(client)
+ items = load_sample_cohort()
+ try:
+ _state["result"] = run_cohort_batch(
+ client, items, max_customers=50, on_progress=on_progress, template_name=template_name
+ )
+ finally:
+ _state["progress"] = None
+ return RedirectResponse(url="/", status_code=303)
+
+
+@app.get("/batch/progress")
+def batch_progress():
+ return JSONResponse(_state["progress"])
+
+
+@app.get("/review/{customer_id}", response_class=HTMLResponse)
+def review_item(request: Request, customer_id: str):
+ # Keyed by customer_id, not change_id: a clause_not_found exception has no change_id at all (nothing
+ # was ever proposed to SuperDocs), so it needs a stable identifier every pending item actually has.
+ result: BatchResult | None = _state["result"] # type: ignore[assignment]
+ item = next((p for p in (result.pending_review if result else []) if p.package.customer.customer_id == customer_id), None)
+ if item is None:
+ return RedirectResponse(url="/", status_code=303)
+ return templates.TemplateResponse(request, "review.html", {"item": item})
+
+
+@app.get("/pack/{customer_id}", response_class=HTMLResponse)
+def pack_item(request: Request, customer_id: str):
+ result: BatchResult | None = _state["result"] # type: ignore[assignment]
+ package = next((p for p in (result.clean if result else []) if p.customer.customer_id == customer_id), None)
+ if package is None:
+ return RedirectResponse(url="/", status_code=303)
+ return templates.TemplateResponse(request, "pack.html", {"package": package})
+
+
+@app.post("/review/{customer_id}/decide")
+def decide_item(customer_id: str, approved: bool = Form(...), reason: str = Form(default="")):
+ result: BatchResult = _state["result"] # type: ignore[assignment]
+ item = next(p for p in result.pending_review if p.package.customer.customer_id == customer_id)
+ if item.change_id is not None: # nothing to approve/reject for a clause_not_found item
+ client = _get_client()
+ apply_human_decision(client, item.package, item.job_id, item.change_id, approved=approved, reason=reason or None)
+ result.pending_review = [p for p in result.pending_review if p.package.customer.customer_id != customer_id]
+ result.clean.append(item.package)
+ return RedirectResponse(url="/", status_code=303)
diff --git a/use-cases/iashutoshyadav/renewal-true-up-engine/data/__init__.py b/use-cases/iashutoshyadav/renewal-true-up-engine/data/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/use-cases/iashutoshyadav/renewal-true-up-engine/data/renewal_amendment_template.txt b/use-cases/iashutoshyadav/renewal-true-up-engine/data/renewal_amendment_template.txt
new file mode 100644
index 00000000..213cfd25
--- /dev/null
+++ b/use-cases/iashutoshyadav/renewal-true-up-engine/data/renewal_amendment_template.txt
@@ -0,0 +1,20 @@
+RENEWAL AMENDMENT TEMPLATE
+
+Vertex Cloud Partners LLC - Standard Renewal Amendment Letterhead
+
+This Amendment updates the entitlement clause of the referenced Master Services Agreement for
+the upcoming renewal term. Amendments drafted from this template must:
+
+- State the new Monthly Entitlement in the same numbered-clause format as the original agreement
+ (e.g. "2.1 Customer is entitled to use up to N compute units per month").
+- Leave every other clause of the original agreement unchanged.
+- Use plain, formal contract language consistent with the rest of the agreement - no informal phrasing.
+
+Signature block:
+
+Vertex Cloud Partners LLC Customer
+
+_______________________ _______________________
+Authorized Signatory Authorized Signatory
+
+Date: _______________ Date: _______________
diff --git a/use-cases/iashutoshyadav/renewal-true-up-engine/data/sample_cohort/__init__.py b/use-cases/iashutoshyadav/renewal-true-up-engine/data/sample_cohort/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/use-cases/iashutoshyadav/renewal-true-up-engine/data/sample_cohort/cohort.py b/use-cases/iashutoshyadav/renewal-true-up-engine/data/sample_cohort/cohort.py
new file mode 100644
index 00000000..c1d924f9
--- /dev/null
+++ b/use-cases/iashutoshyadav/renewal-true-up-engine/data/sample_cohort/cohort.py
@@ -0,0 +1,91 @@
+"""Synthetic renewal cohort - invented clients and data, exactly as the task brief expects ('Where your
+build needs a client, a company, or data, invent them'). Four customers chosen deliberately to exercise
+every path the batch runner needs to prove: two clean renewals, one genuine overage exception, and one
+contract with a missing governing clause (the other exception trigger)."""
+
+from app.models import Customer, UsagePeriod
+from app.batch import CohortItem
+
+_ACME = """MASTER SERVICES AGREEMENT
+
+This Agreement is entered into as of January 1, 2026, between Acme Robotics Inc. ("Customer") and
+Vertex Cloud Partners LLC ("Vendor").
+
+2.1 Customer is entitled to use up to 10,000 (ten thousand) compute units per month ("Monthly Entitlement")
+under this Agreement.
+
+2.2 The fee for the Monthly Entitlement is $8,000.00 per month, payable in advance.
+
+3.1 If Customer usage exceeds the Monthly Entitlement in any given month, Vendor shall invoice Customer for
+the excess usage at a rate of $1.50 per compute unit above the Monthly Entitlement.
+"""
+
+_BRIGHTLEAF = """MASTER SERVICES AGREEMENT
+
+This Agreement is entered into as of February 1, 2026, between Brightleaf Analytics LLC ("Customer") and
+Vertex Cloud Partners LLC ("Vendor").
+
+2.1 Customer is entitled to use up to 25,000 (twenty-five thousand) compute units per month
+("Monthly Entitlement") under this Agreement.
+
+2.2 The fee for the Monthly Entitlement is $18,000.00 per month, payable in advance.
+
+3.1 If Customer usage exceeds the Monthly Entitlement in any given month, Vendor shall invoice Customer for
+the excess usage at a rate of $1.20 per compute unit above the Monthly Entitlement.
+"""
+
+_CASCADE = """MASTER SERVICES AGREEMENT
+
+This Agreement is entered into as of March 1, 2026, between Cascade Logistics Corp. ("Customer") and
+Vertex Cloud Partners LLC ("Vendor").
+
+2.1 Customer is entitled to use up to 5,000 (five thousand) compute units per month ("Monthly Entitlement")
+under this Agreement.
+
+2.2 The fee for the Monthly Entitlement is $4,500.00 per month, payable in advance.
+
+3.1 If Customer usage exceeds the Monthly Entitlement in any given month, Vendor shall invoice Customer for
+the excess usage at a rate of $2.00 per compute unit above the Monthly Entitlement.
+"""
+
+# Deliberately missing a 3.1 overage-rate clause - exercises the clause_not_found exception path with real
+# (if synthetic) messy data, not a hand-picked always-clean corpus.
+_DRIFTWOOD = """MASTER SERVICES AGREEMENT
+
+This Agreement is entered into as of April 1, 2026, between Driftwood Media Group ("Customer") and
+Vertex Cloud Partners LLC ("Vendor").
+
+2.1 Customer is entitled to use up to 8,000 (eight thousand) compute units per month ("Monthly Entitlement")
+under this Agreement.
+
+2.2 The fee for the Monthly Entitlement is $6,000.00 per month, payable in advance.
+"""
+
+
+def load_sample_cohort() -> list[CohortItem]:
+ return [
+ CohortItem(
+ customer=Customer("acme", "Acme Robotics Inc."),
+ contract_text=_ACME,
+ usage=UsagePeriod("acme", "2026-07", units_used=9_200), # clean: under 10,000
+ new_entitlement_units=12_000,
+ ),
+ CohortItem(
+ customer=Customer("brightleaf", "Brightleaf Analytics LLC"),
+ contract_text=_BRIGHTLEAF,
+ usage=UsagePeriod("brightleaf", "2026-07", units_used=31_500), # overage: exception
+ new_entitlement_units=30_000,
+ ),
+ CohortItem(
+ customer=Customer("cascade", "Cascade Logistics Corp."),
+ contract_text=_CASCADE,
+ usage=UsagePeriod("cascade", "2026-07", units_used=4_100), # clean: under 5,000
+ new_entitlement_units=6_000,
+ ),
+ CohortItem(
+ customer=Customer("driftwood", "Driftwood Media Group"),
+ contract_text=_DRIFTWOOD,
+ usage=UsagePeriod("driftwood", "2026-07", units_used=7_500), # exception: no overage clause found
+ new_entitlement_units=10_000,
+ ),
+ ]
diff --git a/use-cases/iashutoshyadav/renewal-true-up-engine/requirements.txt b/use-cases/iashutoshyadav/renewal-true-up-engine/requirements.txt
new file mode 100644
index 00000000..de28bc05
--- /dev/null
+++ b/use-cases/iashutoshyadav/renewal-true-up-engine/requirements.txt
@@ -0,0 +1,8 @@
+fastapi==0.141.1
+uvicorn[standard]==0.52.3
+httpx==0.28.1
+pydantic==2.13.4
+jinja2==3.1.6
+python-dotenv==1.2.2
+python-multipart==0.0.32
+pytest==9.1.1
diff --git a/use-cases/iashutoshyadav/renewal-true-up-engine/tests/__init__.py b/use-cases/iashutoshyadav/renewal-true-up-engine/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/use-cases/iashutoshyadav/renewal-true-up-engine/tests/test_batch.py b/use-cases/iashutoshyadav/renewal-true-up-engine/tests/test_batch.py
new file mode 100644
index 00000000..9a209cd2
--- /dev/null
+++ b/use-cases/iashutoshyadav/renewal-true-up-engine/tests/test_batch.py
@@ -0,0 +1,131 @@
+from app.batch import CohortItem, run_cohort_batch
+from app.models import Customer, UsagePeriod
+from app.superdocs_client import FakeSuperDocsClient
+
+CONTRACT_TEXT = """MASTER SERVICES AGREEMENT (SAMPLE)
+
+2.1 Customer is entitled to use up to 10,000 (ten thousand) compute units per month ("Monthly Entitlement")
+under this Agreement.
+
+2.2 The fee for the Monthly Entitlement is $8,000.00 per month, payable in advance.
+
+3.1 If Customer usage exceeds the Monthly Entitlement in any given month, Vendor shall invoice Customer for
+the excess usage at a rate of $1.50 per compute unit above the Monthly Entitlement.
+"""
+
+
+def _item(customer_id: str, units_used: float) -> CohortItem:
+ return CohortItem(
+ customer=Customer(customer_id=customer_id, name=f"Customer {customer_id}"),
+ contract_text=CONTRACT_TEXT,
+ usage=UsagePeriod(customer_id=customer_id, period_label="2026-07", units_used=units_used),
+ new_entitlement_units=12_000,
+ )
+
+
+def test_a_cohort_of_clean_renewals_needs_no_human_review():
+ client = FakeSuperDocsClient()
+ items = [_item("c1", 5_000), _item("c2", 8_000), _item("c3", 9_999)]
+
+ result = run_cohort_batch(client, items)
+
+ assert len(result.clean) == 3
+ assert result.pending_review == []
+ assert result.failed == []
+
+
+def test_only_genuine_overage_exceptions_reach_the_review_queue():
+ client = FakeSuperDocsClient()
+ items = [_item("clean-1", 5_000), _item("over-1", 15_000), _item("clean-2", 3_000), _item("over-2", 20_000)]
+
+ result = run_cohort_batch(client, items)
+
+ assert len(result.clean) == 2
+ assert {p.package.customer.customer_id for p in result.pending_review} == {"over-1", "over-2"}
+ assert result.total_processed == 4
+
+
+BROKEN_CONTRACT_NO_OVERAGE_CLAUSE = """MASTER SERVICES AGREEMENT (SAMPLE)
+
+2.1 Customer is entitled to use up to 8,000 (eight thousand) compute units per month ("Monthly Entitlement")
+under this Agreement.
+
+2.2 The fee for the Monthly Entitlement is $6,000.00 per month, payable in advance.
+"""
+
+
+def test_a_clause_not_found_exception_never_lands_in_clean_even_though_start_renewal_does_not_raise():
+ """Regression test for a real bug: start_renewal() returns NORMALLY (doesn't raise) for both a truly
+ clean renewal and a clause_not_found exception - only PendingHumanDecision is raised, and only for the
+ overage case. The first version of run_cohort_batch treated 'didn't raise' as 'clean', which silently
+ routed a clause-not-found customer into the clean bucket. Caught by actually running the web app
+ against a cohort containing this exact mix, not by any test that existed before this one - none of
+ them exercised both exception types together in the same batch."""
+ client = FakeSuperDocsClient()
+ items = [
+ _item("clean-1", 5_000),
+ CohortItem(
+ customer=Customer("no-clause", "No Clause Inc."),
+ contract_text=BROKEN_CONTRACT_NO_OVERAGE_CLAUSE,
+ usage=UsagePeriod("no-clause", "2026-07", units_used=100),
+ new_entitlement_units=10_000,
+ ),
+ _item("over-1", 15_000),
+ ]
+
+ result = run_cohort_batch(client, items)
+
+ clean_ids = {p.customer.customer_id for p in result.clean}
+ review_ids = {p.package.customer.customer_id for p in result.pending_review}
+
+ assert clean_ids == {"clean-1"}
+ assert review_ids == {"no-clause", "over-1"}
+ assert result.failed == []
+
+ no_clause_item = next(p for p in result.pending_review if p.package.customer.customer_id == "no-clause")
+ assert no_clause_item.package.exception.reason == "clause_not_found"
+ assert no_clause_item.job_id is None
+ assert no_clause_item.change_id is None
+
+
+def test_one_customers_failure_never_aborts_the_rest_of_the_batch():
+ """Simulates a real crash (not a business exception) for one customer mid-batch - the other customers
+ must still be processed, not silently dropped."""
+
+ class FlakyClient(FakeSuperDocsClient):
+ def upload_document(self, filename, content, session_id):
+ if "boom" in session_id:
+ raise RuntimeError("simulated SuperDocs outage for this customer")
+ return super().upload_document(filename, content, session_id)
+
+ client = FlakyClient()
+ items = [_item("ok-1", 5_000), _item("boom-1", 5_000), _item("ok-2", 5_000)]
+
+ result = run_cohort_batch(client, items)
+
+ assert len(result.clean) == 2
+ assert [p.customer.customer_id for p in result.clean] == ["ok-1", "ok-2"]
+ assert len(result.failed) == 1
+ assert result.failed[0].customer_id == "boom-1"
+ assert "simulated SuperDocs outage" in result.failed[0].error
+
+
+def test_max_customers_is_a_real_stopping_rule_not_a_suggestion():
+ client = FakeSuperDocsClient()
+ items = [_item(f"c{i}", 1_000) for i in range(10)]
+
+ result = run_cohort_batch(client, items, max_customers=3)
+
+ assert result.total_processed == 3
+ assert len(result.skipped_over_limit) == 7
+ assert result.skipped_over_limit == [f"c{i}" for i in range(3, 10)]
+
+
+def test_progress_callback_fires_once_per_processed_customer_in_order():
+ client = FakeSuperDocsClient()
+ items = [_item("c1", 1_000), _item("c2", 1_000)]
+ calls: list[tuple[int, int, str, str]] = []
+
+ run_cohort_batch(client, items, on_progress=lambda *args: calls.append(args))
+
+ assert calls == [(1, 2, "c1", "clean"), (2, 2, "c2", "clean")]
diff --git a/use-cases/iashutoshyadav/renewal-true-up-engine/tests/test_clause_extraction.py b/use-cases/iashutoshyadav/renewal-true-up-engine/tests/test_clause_extraction.py
new file mode 100644
index 00000000..fdc81c89
--- /dev/null
+++ b/use-cases/iashutoshyadav/renewal-true-up-engine/tests/test_clause_extraction.py
@@ -0,0 +1,64 @@
+import pytest
+
+from app.clause_extraction import ClauseNotFoundError, extract_contract_terms
+
+SAMPLE_CONTRACT = """MASTER SERVICES AGREEMENT (SAMPLE)
+
+This Agreement is entered into as of January 1, 2026, between Acme Renewals Inc. ("Customer") and
+Vertex Cloud Partners LLC ("Vendor").
+
+2.1 Customer is entitled to use up to 10,000 (ten thousand) compute units per month ("Monthly Entitlement")
+under this Agreement.
+
+2.2 The fee for the Monthly Entitlement is $8,000.00 per month, payable in advance.
+
+3.1 If Customer usage exceeds the Monthly Entitlement in any given month, Vendor shall invoice Customer for
+the excess usage at a rate of $1.50 per compute unit above the Monthly Entitlement.
+"""
+
+
+def test_extracts_all_three_governing_facts_with_exact_quotes():
+ terms = extract_contract_terms(SAMPLE_CONTRACT)
+
+ assert terms.entitlement_units == 10_000
+ assert "entitled to use up to 10,000" in terms.entitlement_quote
+
+ assert terms.monthly_fee == 8_000
+ assert "$8,000.00" in terms.fee_quote
+
+ assert terms.overage_rate == 1.5
+ assert "$1.50 per compute unit" in terms.overage_rate_quote
+
+ # Every quote must be an exact substring of the source contract, never a paraphrase - that's the
+ # entire point of this build. Verified directly, not assumed.
+ assert terms.entitlement_quote in SAMPLE_CONTRACT
+ assert terms.fee_quote in SAMPLE_CONTRACT
+ assert terms.overage_rate_quote in SAMPLE_CONTRACT
+
+ # Regression guard for a real bug found during live verification against the real SuperDocs API: each
+ # clause's own section number ("2.1", "2.2", "3.1") contains a period, which the extraction regex was
+ # treating as a sentence-ending period - silently dropping the leading section number from every
+ # quote ("3.1 If Customer..." came back as "1 If Customer..."). The substring checks above never
+ # caught this since they don't check where the quote STARTS.
+ assert terms.entitlement_quote.startswith("2.1 Customer is entitled")
+ assert terms.fee_quote.startswith("2.2 The fee")
+ assert terms.overage_rate_quote.startswith("3.1 If Customer usage exceeds")
+
+
+def test_missing_entitlement_clause_raises_rather_than_guessing():
+ contract_without_entitlement = "This contract has no entitlement clause at all, just prose."
+ with pytest.raises(ClauseNotFoundError) as exc_info:
+ extract_contract_terms(contract_without_entitlement)
+ assert exc_info.value.field_name == "entitlement_units"
+
+
+def test_missing_overage_rate_raises_the_specific_field_name():
+ contract_missing_rate = """
+ 2.1 Customer is entitled to use up to 5,000 (five thousand) compute units per month
+ ("Monthly Entitlement") under this Agreement.
+
+ 2.2 The fee for the Monthly Entitlement is $3,000.00 per month, payable in advance.
+ """
+ with pytest.raises(ClauseNotFoundError) as exc_info:
+ extract_contract_terms(contract_missing_rate)
+ assert exc_info.value.field_name == "overage_rate"
diff --git a/use-cases/iashutoshyadav/renewal-true-up-engine/tests/test_renewal_engine.py b/use-cases/iashutoshyadav/renewal-true-up-engine/tests/test_renewal_engine.py
new file mode 100644
index 00000000..e754b17c
--- /dev/null
+++ b/use-cases/iashutoshyadav/renewal-true-up-engine/tests/test_renewal_engine.py
@@ -0,0 +1,131 @@
+import pytest
+
+from app.models import Customer, UsagePeriod
+from app.renewal_engine import PendingHumanDecision, apply_human_decision, start_renewal
+from app.superdocs_client import FakeSuperDocsClient
+
+CONTRACT_TEXT = """MASTER SERVICES AGREEMENT (SAMPLE)
+
+2.1 Customer is entitled to use up to 10,000 (ten thousand) compute units per month ("Monthly Entitlement")
+under this Agreement.
+
+2.2 The fee for the Monthly Entitlement is $8,000.00 per month, payable in advance.
+
+3.1 If Customer usage exceeds the Monthly Entitlement in any given month, Vendor shall invoice Customer for
+the excess usage at a rate of $1.50 per compute unit above the Monthly Entitlement.
+"""
+
+CUSTOMER = Customer(customer_id="cust-1", name="Acme Renewals Inc.")
+
+
+def test_a_clean_renewal_applies_the_amendment_automatically_no_human_needed():
+ client = FakeSuperDocsClient()
+ usage = UsagePeriod(customer_id="cust-1", period_label="2026-07", units_used=9_000) # under entitlement
+
+ package = start_renewal(client, CUSTOMER, CONTRACT_TEXT, usage, new_entitlement_units=12_000)
+
+ assert package.exception is None
+ assert package.true_up.true_up_amount == 0
+ assert package.exported_summary_markdown is not None
+ assert "12,000" in package.exported_summary_markdown
+ assert "10,000" not in package.exported_summary_markdown
+
+
+def test_an_overage_renewal_pauses_for_human_approval_instead_of_auto_applying():
+ client = FakeSuperDocsClient()
+ usage = UsagePeriod(customer_id="cust-1", period_label="2026-07", units_used=15_000) # over entitlement
+
+ with pytest.raises(PendingHumanDecision) as exc_info:
+ start_renewal(client, CUSTOMER, CONTRACT_TEXT, usage, new_entitlement_units=12_000)
+
+ pending = exc_info.value
+ assert pending.package.exception is not None
+ assert pending.package.exception.reason == "overage_true_up_required"
+ assert pending.package.true_up.true_up_amount == pytest.approx(5_000 * 1.5) # 15000-10000=5000 overage
+ # Nothing applied yet - the document must still read the ORIGINAL entitlement.
+ assert pending.package.exported_summary_markdown is None
+
+
+def test_approving_the_pending_decision_applies_the_amendment():
+ client = FakeSuperDocsClient()
+ usage = UsagePeriod(customer_id="cust-1", period_label="2026-07", units_used=15_000)
+
+ with pytest.raises(PendingHumanDecision) as exc_info:
+ start_renewal(client, CUSTOMER, CONTRACT_TEXT, usage, new_entitlement_units=12_000)
+ pending = exc_info.value
+
+ result = apply_human_decision(
+ client, pending.package, pending.job_id, pending.change_id, approved=True,
+ )
+
+ assert "12,000" in result.exported_summary_markdown
+ # The true-up record survives regardless of the document-edit outcome - it's a fact about what
+ # happened this billing period, not something rejection should erase.
+ assert result.true_up.true_up_amount > 0
+
+
+def test_rejecting_the_pending_decision_leaves_the_original_entitlement_untouched():
+ client = FakeSuperDocsClient()
+ usage = UsagePeriod(customer_id="cust-1", period_label="2026-07", units_used=15_000)
+
+ with pytest.raises(PendingHumanDecision) as exc_info:
+ start_renewal(client, CUSTOMER, CONTRACT_TEXT, usage, new_entitlement_units=12_000)
+ pending = exc_info.value
+
+ result = apply_human_decision(
+ client, pending.package, pending.job_id, pending.change_id, approved=False, reason="needs legal review",
+ )
+
+ # Rejected: no export was even generated, since nothing was approved to export.
+ assert result.exported_summary_markdown is None
+ # But the true-up fact itself - what actually happened - is still on the record.
+ assert result.true_up.true_up_amount > 0
+
+
+def test_renewal_quote_and_talk_track_are_populated_for_every_package_with_terms():
+ """The task card names 'the renewal quote... and a talk track for the customer-success manager' as
+ real pack components, not optional extras. Found live on 2026-08-20: both were computed by helper
+ functions that existed but were never called from anywhere - dead code satisfying 'write the
+ function', not 'include it in the pack'."""
+ client = FakeSuperDocsClient()
+
+ clean_usage = UsagePeriod(customer_id="cust-1", period_label="2026-07", units_used=9_000)
+ clean = start_renewal(client, CUSTOMER, CONTRACT_TEXT, clean_usage, new_entitlement_units=12_000)
+ assert clean.renewal_quote and CUSTOMER.name in clean.renewal_quote
+ assert clean.talk_track and CUSTOMER.name in clean.talk_track
+ assert "no overage" in clean.talk_track
+
+ overage_usage = UsagePeriod(customer_id="cust-1", period_label="2026-07", units_used=15_000)
+ with pytest.raises(PendingHumanDecision) as exc_info:
+ start_renewal(client, CUSTOMER, CONTRACT_TEXT, overage_usage, new_entitlement_units=12_000)
+ pending = exc_info.value.package
+ assert pending.renewal_quote and "true-up" in pending.renewal_quote
+ # The talk track must carry the exact governing-clause quote, not a paraphrase - a CS manager
+ # repeating this to a customer needs to be citing the real contract, same discipline as the UI.
+ assert pending.true_up.governing_clause_quote in pending.talk_track
+
+
+def test_a_contract_missing_a_governing_clause_is_an_exception_not_a_crash():
+ client = FakeSuperDocsClient()
+ broken_contract = "This document has no entitlement clause in it at all."
+ usage = UsagePeriod(customer_id="cust-1", period_label="2026-07", units_used=100)
+
+ package = start_renewal(client, CUSTOMER, broken_contract, usage, new_entitlement_units=12_000)
+
+ assert package.exception is not None
+ assert package.exception.reason == "clause_not_found"
+
+
+def test_a_template_reference_is_included_in_the_amendment_instruction_when_given():
+ client = FakeSuperDocsClient()
+ template_id = client.upload_template("renewal_amendment_template.txt", b"RENEWAL AMENDMENT TEMPLATE\n")
+ assert template_id
+
+ usage = UsagePeriod(customer_id="cust-1", period_label="2026-07", units_used=9_000)
+ package = start_renewal(
+ client, CUSTOMER, CONTRACT_TEXT, usage, new_entitlement_units=12_000,
+ template_name="renewal_amendment_template.txt",
+ )
+
+ assert package.exception is None
+ assert package.exported_summary_markdown is not None
diff --git a/use-cases/iashutoshyadav/renewal-true-up-engine/tests/test_superdocs_client.py b/use-cases/iashutoshyadav/renewal-true-up-engine/tests/test_superdocs_client.py
new file mode 100644
index 00000000..f8c4575d
--- /dev/null
+++ b/use-cases/iashutoshyadav/renewal-true-up-engine/tests/test_superdocs_client.py
@@ -0,0 +1,65 @@
+"""Runs entirely offline against FakeSuperDocsClient - no network call, no SuperDocs quota spent. This is
+what makes the whole app's test suite runnable without a live key (round-wide requirement)."""
+
+import pytest
+
+from app.superdocs_client import FakeSuperDocsClient, RealSuperDocsClient, make_client
+
+
+def test_make_client_without_key_returns_fake():
+ assert isinstance(make_client(None), FakeSuperDocsClient)
+ assert isinstance(make_client(""), FakeSuperDocsClient)
+
+
+def test_make_client_with_key_returns_real_without_a_network_call():
+ client = make_client("sk_test_dummy")
+ assert isinstance(client, RealSuperDocsClient)
+ client.close()
+
+
+def test_real_client_rejects_empty_key():
+ with pytest.raises(ValueError):
+ RealSuperDocsClient("")
+
+
+def test_upload_then_export_round_trips_content():
+ client = FakeSuperDocsClient()
+ content = b"2.1 Monthly Entitlement is 10,000 units.\n2.2 Fee is $8,000.00 per month.\n"
+ client.upload_document("contract.txt", content, session_id="s1")
+ assert client.export_document("s1") == content.decode()
+
+
+def test_hitl_approval_flow_applies_only_the_approved_change():
+ """Mirrors the real product's verified behavior (Phase 4 of the design): approving one change edits
+ only the targeted text, everything else in the document stays byte-identical."""
+ client = FakeSuperDocsClient()
+ original = "2.1 Monthly Entitlement is 10,000 units.\n2.2 Fee is $8,000.00 per month.\n"
+ client.upload_document("contract.txt", original.encode(), session_id="s1")
+
+ job = client.seed_pending_change(
+ session_id="s1",
+ old_text="Monthly Entitlement is 10,000 units",
+ new_text="Monthly Entitlement is 12,000 units",
+ )
+ assert job.status == "awaiting_approval"
+ change = job.pending_changes[0]
+
+ client.approve_change("s1", job.job_id, change.change_id, approved=True)
+
+ result = client.export_document("s1")
+ assert "12,000 units" in result
+ assert "10,000 units" not in result
+ assert "Fee is $8,000.00 per month." in result # untouched section, unchanged
+
+
+def test_hitl_rejection_leaves_document_unchanged():
+ client = FakeSuperDocsClient()
+ original = "2.1 Monthly Entitlement is 10,000 units.\n"
+ client.upload_document("contract.txt", original.encode(), session_id="s1")
+
+ job = client.seed_pending_change(session_id="s1", old_text="10,000 units", new_text="12,000 units")
+ change = job.pending_changes[0]
+
+ client.approve_change("s1", job.job_id, change.change_id, approved=False)
+
+ assert client.export_document("s1") == original
diff --git a/use-cases/iashutoshyadav/renewal-true-up-engine/tests/test_true_up.py b/use-cases/iashutoshyadav/renewal-true-up-engine/tests/test_true_up.py
new file mode 100644
index 00000000..475316cc
--- /dev/null
+++ b/use-cases/iashutoshyadav/renewal-true-up-engine/tests/test_true_up.py
@@ -0,0 +1,69 @@
+from app.models import ContractTerms, Customer, UsagePeriod
+from app.true_up import compute_true_up, determine_exception
+
+CUSTOMER = Customer(customer_id="cust-1", name="Acme Renewals Inc.")
+
+
+def _terms(entitlement: float, rate: float) -> ContractTerms:
+ return ContractTerms(
+ entitlement_units=entitlement,
+ entitlement_quote="entitled to use up to ... (test)",
+ monthly_fee=1000.0,
+ fee_quote="fee is $1,000.00 (test)",
+ overage_rate=rate,
+ overage_rate_quote=f"rate of ${rate} per unit (test)",
+ )
+
+
+def test_the_exact_worked_example_from_the_design_doc():
+ """entitlement 100, usage 125, overage 25, rate $20/unit -> true_up = 25 * 20 = $500."""
+ terms = _terms(entitlement=100, rate=20)
+ usage = UsagePeriod(customer_id="cust-1", period_label="2026-07", units_used=125)
+
+ result = compute_true_up(CUSTOMER, terms, usage)
+
+ assert result.overage_units == 25
+ assert result.true_up_amount == 500
+ assert result.governing_clause_quote == terms.overage_rate_quote
+
+
+def test_usage_within_entitlement_produces_zero_true_up_and_no_negative_overage():
+ terms = _terms(entitlement=100, rate=20)
+ usage = UsagePeriod(customer_id="cust-1", period_label="2026-07", units_used=80)
+
+ result = compute_true_up(CUSTOMER, terms, usage)
+
+ assert result.overage_units == 0
+ assert result.true_up_amount == 0
+
+
+def test_usage_exactly_at_entitlement_is_the_zero_boundary_not_a_negative():
+ terms = _terms(entitlement=100, rate=20)
+ usage = UsagePeriod(customer_id="cust-1", period_label="2026-07", units_used=100)
+
+ result = compute_true_up(CUSTOMER, terms, usage)
+
+ assert result.overage_units == 0
+ assert result.true_up_amount == 0
+
+
+def test_a_true_up_above_zero_is_flagged_as_a_genuine_exception():
+ terms = _terms(entitlement=100, rate=20)
+ usage = UsagePeriod(customer_id="cust-1", period_label="2026-07", units_used=125)
+ result = compute_true_up(CUSTOMER, terms, usage)
+
+ exception = determine_exception(CUSTOMER, result)
+
+ assert exception is not None
+ assert exception.reason == "overage_true_up_required"
+
+
+def test_a_clean_renewal_with_zero_true_up_is_not_an_exception():
+ """This is the whole point of the batch design: a clean renewal never reaches a human."""
+ terms = _terms(entitlement=100, rate=20)
+ usage = UsagePeriod(customer_id="cust-1", period_label="2026-07", units_used=100)
+ result = compute_true_up(CUSTOMER, terms, usage)
+
+ exception = determine_exception(CUSTOMER, result)
+
+ assert exception is None
diff --git a/use-cases/iashutoshyadav/renewal-true-up-engine/verify_real_api.py b/use-cases/iashutoshyadav/renewal-true-up-engine/verify_real_api.py
new file mode 100644
index 00000000..120c341c
--- /dev/null
+++ b/use-cases/iashutoshyadav/renewal-true-up-engine/verify_real_api.py
@@ -0,0 +1,54 @@
+"""One-off, real-API verification script - not part of the test suite (which stays offline/fake per the
+round's own standard). Run manually, once, with a real SUPERDOCS_API_KEY, to prove the actual integration
+works before committing/pushing. Deliberately touches only ONE customer, not the full cohort - budgeting
+real operations against a metered free-tier account, per the task brief's own explicit warning.
+"""
+
+import os
+
+from app.models import Customer, UsagePeriod
+from app.renewal_engine import PendingHumanDecision, apply_human_decision, start_renewal
+from app.superdocs_client import RealSuperDocsClient
+
+CONTRACT_TEXT = """MASTER SERVICES AGREEMENT (SAMPLE)
+
+This Agreement is entered into as of January 1, 2026, between Acme Renewals Inc. ("Customer") and
+Vertex Cloud Partners LLC ("Vendor").
+
+2.1 Customer is entitled to use up to 10,000 (ten thousand) compute units per month ("Monthly Entitlement")
+under this Agreement.
+
+2.2 The fee for the Monthly Entitlement is $8,000.00 per month, payable in advance.
+
+3.1 If Customer usage exceeds the Monthly Entitlement in any given month, Vendor shall invoice Customer for
+the excess usage at a rate of $1.50 per compute unit above the Monthly Entitlement.
+"""
+
+api_key = os.environ.get("SUPERDOCS_API_KEY")
+if not api_key:
+ raise SystemExit("SUPERDOCS_API_KEY not set")
+
+client = RealSuperDocsClient(api_key)
+
+# Test 1 (clean renewal, approve_all path) already verified in a prior run - skipped here to conserve
+# real operations against the metered free-tier account, per the task brief's own warning.
+
+print("=== Test 2: overage renewal (real HITL approval), re-run with the wait-for-completion fix ===")
+customer2 = Customer("verify-3", "Real API Verification Customer 2")
+usage_overage = UsagePeriod("verify-3", "2026-07", units_used=15_000)
+try:
+ start_renewal(client, customer2, CONTRACT_TEXT, usage_overage, new_entitlement_units=12_000)
+ print("ERROR: expected PendingHumanDecision to be raised")
+except PendingHumanDecision as pending:
+ print("PendingHumanDecision raised correctly")
+ print("true_up_amount:", pending.package.true_up.true_up_amount)
+ print("governing_clause_quote:", pending.package.true_up.governing_clause_quote)
+ print("job_id:", pending.job_id, "change_id:", pending.change_id)
+
+ result = apply_human_decision(client, pending.package, pending.job_id, pending.change_id, approved=True)
+ print("after approval, exported summary contains '12,000':", "12,000" in (result.exported_summary_markdown or ""))
+ print()
+ print("--- exported markdown after approval ---")
+ print(result.exported_summary_markdown)
+
+client.close()