{
  "policy": {
    "version": 5,
    "patterns": {
      "version": 1,
      "method": "CURATED_PRACTICE_TOPIC",
      "notice": "Pattern topics identify design choices to practice. Read the ticket's acceptance criteria and justify the simplest suitable approach. Tags are not capability or ownership evidence; an untagged ticket has no curated pattern topic assigned."
    },
    "fieldMix": {
      "version": 1,
      "method": "CURATED_ESTIMATE",
      "notice": "Field percentages are editorial estimates of the ticket's engineering focus. They total 100%; they are not measured time, proficiency scores, or ownership evidence."
    },
    "contentStatus": "PRACTICE_BRIEF",
    "assessmentStatus": "NOT_QUALIFIED",
    "evidenceUse": "NONE",
    "aiPolicy": "AI tools are welcome during implementation. Record assumptions, review the result, and verify its behavior.",
    "notice": "Fictional engineering practice briefs. Starter repositories, fixtures, automated grading, and verified ownership are not included.",
    "outcomeEvidence": "Tests, patches, and runbooks are requested deliverables. They become Outcome Evidence only through a qualified Mission and immutable Evidence IDs.",
    "ownershipEvidence": "Independent adaptation must be observed under a declared verification policy and cite immutable Evidence IDs. Completing a planning ticket establishes no Ownership Evidence."
  },
  "patternTopics": [
    {
      "id": "factory-method",
      "label": "Factory Method",
      "group": "Creational"
    },
    {
      "id": "abstract-factory",
      "label": "Abstract Factory",
      "group": "Creational"
    },
    {
      "id": "builder",
      "label": "Builder",
      "group": "Creational"
    },
    {
      "id": "prototype",
      "label": "Prototype",
      "group": "Creational"
    },
    {
      "id": "singleton",
      "label": "Singleton",
      "group": "Creational"
    },
    {
      "id": "adapter",
      "label": "Adapter",
      "group": "Structural"
    },
    {
      "id": "bridge",
      "label": "Bridge",
      "group": "Structural"
    },
    {
      "id": "composite",
      "label": "Composite",
      "group": "Structural"
    },
    {
      "id": "decorator",
      "label": "Decorator",
      "group": "Structural"
    },
    {
      "id": "facade",
      "label": "Facade",
      "group": "Structural"
    },
    {
      "id": "flyweight",
      "label": "Flyweight",
      "group": "Structural"
    },
    {
      "id": "proxy",
      "label": "Proxy",
      "group": "Structural"
    },
    {
      "id": "chain-of-responsibility",
      "label": "Chain of Responsibility",
      "group": "Behavioral"
    },
    {
      "id": "command",
      "label": "Command",
      "group": "Behavioral"
    },
    {
      "id": "interpreter",
      "label": "Interpreter",
      "group": "Behavioral"
    },
    {
      "id": "iterator",
      "label": "Iterator",
      "group": "Behavioral"
    },
    {
      "id": "mediator",
      "label": "Mediator",
      "group": "Behavioral"
    },
    {
      "id": "memento",
      "label": "Memento",
      "group": "Behavioral"
    },
    {
      "id": "observer",
      "label": "Observer",
      "group": "Behavioral"
    },
    {
      "id": "state",
      "label": "State",
      "group": "Behavioral"
    },
    {
      "id": "strategy",
      "label": "Strategy",
      "group": "Behavioral"
    },
    {
      "id": "template-method",
      "label": "Template Method",
      "group": "Behavioral"
    },
    {
      "id": "visitor",
      "label": "Visitor",
      "group": "Behavioral"
    },
    {
      "id": "ports-and-adapters",
      "label": "Ports and Adapters",
      "group": "Architectural"
    },
    {
      "id": "cqrs",
      "label": "CQRS",
      "group": "Architectural"
    },
    {
      "id": "strangler-fig",
      "label": "Strangler Fig",
      "group": "Architectural"
    },
    {
      "id": "saga",
      "label": "Saga",
      "group": "Distributed and reliability"
    },
    {
      "id": "transactional-outbox",
      "label": "Transactional Outbox",
      "group": "Distributed and reliability"
    },
    {
      "id": "circuit-breaker",
      "label": "Circuit Breaker",
      "group": "Distributed and reliability"
    },
    {
      "id": "bulkhead",
      "label": "Bulkhead",
      "group": "Distributed and reliability"
    }
  ],
  "projects": [
    {
      "id": "a9c6bc62-e083-4c93-b2c3-bccfd759c82f",
      "key": "BILL",
      "title": "Close the billing reconciliation gap",
      "field": "Backend",
      "summary": "Reconcile payment settlements against invoice records without hiding money movement or retry failures.",
      "context": "A fictional subscription service invoices in USD and EUR. Finance currently compares a payment-provider CSV with database exports; late refunds and retried imports make the monthly close unreliable. Work on synthetic ledger entries only.",
      "stack": [
        "TypeScript",
        "NestJS",
        "PostgreSQL",
        "BullMQ"
      ],
      "prerequisites": [
        "REST APIs",
        "SQL transactions",
        "Integer money representation"
      ],
      "developerValue": "Practice monetary invariants, concurrent writes, reconciliation, and operational recovery across one service boundary.",
      "companyValue": "Review how an engineer makes discrepancies explainable and recoverable before a financial workflow is trusted.",
      "delivery": "Ten bounded tickets over three phases; choose an individual issue or deliver the complete synthetic reconciliation service with a finance handoff.",
      "phases": [
        {
          "id": "intake",
          "title": "Reliable intake",
          "goal": "Make settlement imports inspectable and repeatable."
        },
        {
          "id": "reconcile",
          "title": "Explain the balance",
          "goal": "Match settlements and preserve unresolved differences."
        },
        {
          "id": "operate",
          "title": "Close and recover",
          "goal": "Make runs reproducible and safe to operate."
        }
      ],
      "tickets": [
        {
          "id": "bc37f44d-1c85-40c8-bbeb-b5fa97093b82",
          "key": "BILL-101",
          "title": "Reject ambiguous settlement amounts before import",
          "type": "TASK",
          "priority": "HIGH",
          "difficulty": "FOUNDATIONAL",
          "estimateMinutes": 90,
          "phaseId": "intake",
          "dependsOn": [],
          "scenario": "Finance received a row containing 12,34 EUR beside rows using 12.34. The importer currently guesses the separator and silently books the wrong amount.",
          "acceptanceCriteria": [
            "Accept documented decimal-dot amounts and convert them to integer minor units.",
            "Reject mixed separators, excess precision, and unsupported currency codes with row numbers.",
            "A rejected file creates no settlement rows."
          ],
          "implementationNotes": [
            "The exercise supports USD and EUR, both with two minor digits; do not use floating-point arithmetic."
          ],
          "verification": [
            "Import 0.01, 12.34, and a negative refund amount exactly.",
            "Reject 12,34 and 1.005 without partial writes."
          ],
          "deliverables": [
            "Settlement input contract and parser regression fixtures"
          ],
          "rollout": "Run validation against saved synthetic files before enabling persistence; revert the parser behind the importer flag.",
          "skills": [
            "Input validation",
            "Money arithmetic",
            "Error contracts"
          ],
          "fieldMix": [
            {
              "field": "Backend",
              "percentage": 80
            },
            {
              "field": "API design",
              "percentage": 20
            }
          ],
          "patterns": []
        },
        {
          "id": "19e07eb7-371b-4916-806a-3f17dc950ab1",
          "key": "BILL-102",
          "title": "Make repeated settlement uploads converge on one batch",
          "type": "BUG",
          "priority": "HIGH",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 150,
          "phaseId": "intake",
          "dependsOn": [
            "BILL-101"
          ],
          "scenario": "An upload times out after committing. Finance retries the same file and sees every payout twice. Two browser tabs can also submit it together.",
          "acceptanceCriteria": [
            "Identical bytes within one merchant resolve to the same batch identity.",
            "Concurrent submissions create one committed batch and one set of rows.",
            "A different file with a reused client request key returns a conflict."
          ],
          "implementationNotes": [
            "Scope deduplication to the merchant and retain the original content hash."
          ],
          "verification": [
            "Submit the same file concurrently and compare persisted counts.",
            "Reuse a request key with a changed amount and assert conflict."
          ],
          "deliverables": [
            "Idempotent import command and concurrency reproduction"
          ],
          "rollout": "Enable for one synthetic merchant; rollback routing while preserving committed batch identities.",
          "skills": [
            "Idempotency",
            "Transactions",
            "Concurrency"
          ],
          "fieldMix": [
            {
              "field": "Backend",
              "percentage": 60
            },
            {
              "field": "Database engineering",
              "percentage": 40
            }
          ],
          "patterns": []
        },
        {
          "id": "72799d15-f9b5-4998-bc2e-77e4c17e1930",
          "key": "BILL-103",
          "title": "Add a finance-readable import rejection report",
          "type": "STORY",
          "priority": "MEDIUM",
          "difficulty": "FOUNDATIONAL",
          "estimateMinutes": 90,
          "phaseId": "intake",
          "dependsOn": [
            "BILL-101"
          ],
          "scenario": "Support currently pastes a stack trace into the finance channel when a file fails. Finance needs row-level corrections without exposure to provider tokens or infrastructure details.",
          "acceptanceCriteria": [
            "Report row number, field, stable reason code, and a plain-English correction hint.",
            "Cap the report at 100 errors and state the total omitted count.",
            "Require merchant authorization before downloading a report."
          ],
          "implementationNotes": [
            "Do not echo complete source rows or payment instrument data."
          ],
          "verification": [
            "Show distinct corrections for missing reference and invalid currency.",
            "Deny another merchant and confirm a token-like cell is absent."
          ],
          "deliverables": [
            "Bounded rejection report endpoint and example response"
          ],
          "rollout": "Expose reports for new imports first; disable downloads without deleting import audit records.",
          "skills": [
            "API design",
            "Authorization",
            "Data minimization"
          ],
          "fieldMix": [
            {
              "field": "API design",
              "percentage": 40
            },
            {
              "field": "Backend",
              "percentage": 30
            },
            {
              "field": "Security",
              "percentage": 30
            }
          ],
          "patterns": []
        },
        {
          "id": "c30621b0-bf4a-47d5-a9ab-b4129e1cacb8",
          "key": "BILL-104",
          "title": "Match settlement lines without combining currencies",
          "type": "STORY",
          "priority": "HIGH",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 180,
          "phaseId": "reconcile",
          "dependsOn": [
            "BILL-102"
          ],
          "scenario": "Provider reference pay_204 exists on a EUR invoice, but a malformed USD settlement row shares the reference. The current join marks the invoice paid.",
          "acceptanceCriteria": [
            "Match only within merchant, reference, and currency boundaries.",
            "Represent matched, unmatched, and conflicting lines separately.",
            "Calculate batch totals independently for each currency."
          ],
          "implementationNotes": [
            "A reference match with a currency mismatch is a conflict, never an exchange-rate conversion."
          ],
          "verification": [
            "Match a complete EUR batch to its invoices.",
            "Keep same-reference USD and cross-merchant rows unresolved."
          ],
          "deliverables": [
            "Reconciliation query and currency-conflict fixture"
          ],
          "rollout": "Compare dry-run classifications with the synthetic finance baseline before switching reports.",
          "skills": [
            "SQL",
            "Domain modeling",
            "Data integrity"
          ],
          "fieldMix": [
            {
              "field": "Backend",
              "percentage": 60
            },
            {
              "field": "Database engineering",
              "percentage": 40
            }
          ],
          "patterns": []
        },
        {
          "id": "7a35182a-6dc3-46b7-a660-d2e63194b9c5",
          "key": "BILL-105",
          "title": "Carry partial refunds across settlement days",
          "type": "BUG",
          "priority": "HIGH",
          "difficulty": "ADVANCED",
          "estimateMinutes": 210,
          "phaseId": "reconcile",
          "dependsOn": [
            "BILL-104"
          ],
          "scenario": "A 100.00 charge receives refunds of 20.00 on Monday and 15.00 on Thursday. The report subtracts only the latest refund, overstating net revenue by 20.00.",
          "acceptanceCriteria": [
            "Net the charge against all distinct posted refunds in the selected cutoff.",
            "Keep pending refunds visible but outside settled totals.",
            "Flag cumulative refunds above the captured amount without silently clipping them."
          ],
          "implementationNotes": [
            "Preserve each provider refund identity and posting timestamp; never overwrite the original charge."
          ],
          "verification": [
            "Assert net 65.00 after both refunds and 80.00 at Monday cutoff.",
            "Replay a refund and inject an excess refund; check deduplication and conflict."
          ],
          "deliverables": [
            "Append-only refund matching change and cutoff examples"
          ],
          "rollout": "Recompute a shadow report for one month; retain the previous report revision for comparison.",
          "skills": [
            "Temporal data",
            "Ledger invariants",
            "Regression analysis"
          ],
          "fieldMix": [
            {
              "field": "Backend",
              "percentage": 100
            }
          ],
          "patterns": []
        },
        {
          "id": "17f73b86-1e7c-43bb-a1af-c8b64c7e05ae",
          "key": "BILL-106",
          "title": "Record discrepancy decisions without editing source entries",
          "type": "STORY",
          "priority": "MEDIUM",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 150,
          "phaseId": "reconcile",
          "dependsOn": [
            "BILL-104"
          ],
          "scenario": "Finance has confirmed that a 0.03 difference is a provider fee adjustment. They need to explain it without changing the settlement CSV or marking every mismatch resolved automatically.",
          "acceptanceCriteria": [
            "An authorized reviewer can append a reason and decision to one discrepancy.",
            "Concurrent decisions against the same revision produce one success and one conflict.",
            "Reports show the original difference and complete decision history."
          ],
          "implementationNotes": [
            "A decision changes review state, not the immutable amounts or provider provenance."
          ],
          "verification": [
            "Resolve and reopen a discrepancy while preserving both decisions.",
            "Reject stale revisions and an actor lacking finance-review permission."
          ],
          "deliverables": [
            "Discrepancy decision API and history contract"
          ],
          "rollout": "Gate the command to a test finance role; disable writes while keeping history readable on rollback.",
          "skills": [
            "Optimistic concurrency",
            "Auditability",
            "RBAC"
          ],
          "fieldMix": [
            {
              "field": "Backend",
              "percentage": 60
            },
            {
              "field": "Security",
              "percentage": 20
            },
            {
              "field": "Database engineering",
              "percentage": 20
            }
          ],
          "patterns": []
        },
        {
          "id": "623302ca-54bd-4cb4-91fc-8c14e8caa875",
          "key": "BILL-107",
          "title": "Freeze a close report against a reproducible cutoff",
          "type": "TASK",
          "priority": "HIGH",
          "difficulty": "EXPERT",
          "estimateMinutes": 300,
          "phaseId": "operate",
          "dependsOn": [
            "BILL-105",
            "BILL-106"
          ],
          "scenario": "The April report changed after a late settlement arrived in May. Finance needs to explain what was known at close while still showing corrections in a new revision.",
          "acceptanceCriteria": [
            "Freeze source identities, cutoff, calculation version, and report hash for a close revision.",
            "Later imports cannot mutate the frozen report.",
            "A correction creates a new linked revision with explicit differences."
          ],
          "implementationNotes": [
            "Distinguish provider effective time from system receipt time; document which governs inclusion."
          ],
          "verification": [
            "Regenerate a frozen revision and compare canonical hashes.",
            "Import a backdated settlement after close and prove the old revision is unchanged."
          ],
          "deliverables": [
            "Versioned close report design, implementation, and correction walkthrough"
          ],
          "rollout": "Introduce revisioned closes in parallel with the current export; retain old readers until reconciliation agrees.",
          "skills": [
            "Snapshot consistency",
            "Temporal modeling",
            "Canonical hashing",
            "Migration design"
          ],
          "fieldMix": [
            {
              "field": "Database engineering",
              "percentage": 40
            },
            {
              "field": "Backend",
              "percentage": 40
            },
            {
              "field": "Data engineering",
              "percentage": 20
            }
          ],
          "patterns": []
        },
        {
          "id": "c809f0fc-1f55-4e16-a25d-8452a8c1a8ac",
          "key": "BILL-108",
          "title": "Recover an import after the queue acknowledgement is lost",
          "type": "BUG",
          "priority": "HIGH",
          "difficulty": "ADVANCED",
          "estimateMinutes": 180,
          "phaseId": "operate",
          "dependsOn": [
            "BILL-102",
            "BILL-104"
          ],
          "scenario": "The database commits an accepted batch and the API process exits before enqueueing reconciliation. The UI remains on Processing until someone runs SQL manually.",
          "acceptanceCriteria": [
            "Accepted batch and dispatch intent commit atomically.",
            "Restarted dispatch delivers pending work with a deterministic job identity.",
            "Repeated delivery produces the same reconciliation result without duplicate entries."
          ],
          "implementationNotes": [
            "Keep provider payloads out of queue metadata; use a durable batch identifier."
          ],
          "verification": [
            "Interrupt after database commit and recover through the dispatcher.",
            "Deliver the same job twice and compare the ledger and audit counts."
          ],
          "deliverables": [
            "Transactional dispatch path and crash-recovery reproduction"
          ],
          "rollout": "Start dispatch in observe mode, then enable pending batches; pause dispatch to rollback without losing intent.",
          "skills": [
            "Outbox",
            "Queue semantics",
            "Failure recovery"
          ],
          "fieldMix": [
            {
              "field": "Distributed systems",
              "percentage": 50
            },
            {
              "field": "Backend",
              "percentage": 30
            },
            {
              "field": "Database engineering",
              "percentage": 20
            }
          ],
          "patterns": []
        },
        {
          "id": "a088a39d-2bde-4f74-8b26-94f54bf591a0",
          "key": "BILL-109",
          "title": "Alert when a merchant close is blocked by stale imports",
          "type": "CHORE",
          "priority": "MEDIUM",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 120,
          "phaseId": "operate",
          "dependsOn": [
            "BILL-108"
          ],
          "scenario": "Operations sees a healthy API while three accepted imports have made no progress for 45 minutes. Finance discovers the blockage at the end of the day.",
          "acceptanceCriteria": [
            "Expose accepted-to-completed age and counts by bounded processing state.",
            "Alert after the oldest accepted batch exceeds 15 minutes for two observations.",
            "The runbook identifies the batch safely and distinguishes retryable from invalid input failures."
          ],
          "implementationNotes": [
            "Avoid merchant IDs, file names, and references as metric labels."
          ],
          "verification": [
            "Advance a controlled clock to trigger a stale-batch alert.",
            "Confirm a rejected file does not trigger queue-lag paging."
          ],
          "deliverables": [
            "Metrics, alert rule, and import recovery runbook"
          ],
          "rollout": "Observe alerts for one synthetic close cycle before enabling paging; disable the rule if it pages on rejected files.",
          "skills": [
            "Observability",
            "Alert design",
            "Incident operations"
          ],
          "fieldMix": [
            {
              "field": "Site reliability",
              "percentage": 70
            },
            {
              "field": "Backend",
              "percentage": 30
            }
          ],
          "patterns": []
        },
        {
          "id": "5b487ee4-16a4-4370-b996-73e39c62ee7c",
          "key": "BILL-110",
          "title": "Paginate the discrepancy export during a large close",
          "type": "TASK",
          "priority": "MEDIUM",
          "difficulty": "ADVANCED",
          "estimateMinutes": 180,
          "phaseId": "operate",
          "dependsOn": [
            "BILL-107"
          ],
          "scenario": "A synthetic close with 250,000 discrepancies exhausts API memory because the export loads every row. Finance also receives spreadsheet formulas when a reference begins with an equals sign.",
          "acceptanceCriteria": [
            "Stream a stable report revision in bounded pages without omissions or duplicate rows.",
            "Neutralize spreadsheet formula prefixes in text cells.",
            "Abort export promptly on client disconnect and preserve authorization on resume."
          ],
          "implementationNotes": [
            "Use a repeatable 250,000-row synthetic benchmark and report peak memory rather than assuming scalability."
          ],
          "verification": [
            "Compare streamed row identities with the frozen report count.",
            "Export malicious-looking references and cancel halfway through; check escaping and cleanup."
          ],
          "deliverables": [
            "Bounded CSV export and benchmark report"
          ],
          "rollout": "Offer the streamed export alongside the old limit; rollback the route while retaining report revisions.",
          "skills": [
            "Streaming",
            "Pagination",
            "CSV safety",
            "Performance measurement"
          ],
          "fieldMix": [
            {
              "field": "Performance engineering",
              "percentage": 40
            },
            {
              "field": "Backend",
              "percentage": 40
            },
            {
              "field": "Security",
              "percentage": 20
            }
          ],
          "patterns": []
        }
      ]
    }
  ]
}
