{
  "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": "9d1ded75-447e-4375-9830-750cc6b77246",
      "key": "FORM",
      "title": "Purchase requests without spreadsheet follow-ups",
      "field": "Frontend",
      "summary": "Make purchase requests accurate, recoverable, and reviewable across revisions.",
      "context": "The fictional Beacon operations team buys equipment through a browser form. Finance checks totals, departments, and quotes. Scope is one currency per request and an approval API contract; payments and accounting integration are excluded.",
      "stack": [
        "Vue",
        "TypeScript",
        "CSS Modules",
        "Vitest",
        "Playwright"
      ],
      "prerequisites": [
        "Synthetic department and cost-center directory",
        "Draft, submission, and approval API fixtures"
      ],
      "developerValue": "Practice form modeling, exact totals, revision handling, and collaboration under failure.",
      "companyValue": "Inspect whether a developer protects business workflows from duplicate, lost, or misleading submissions.",
      "delivery": "Each issue is a bounded pull request against one shared form. The complete project is optional; document mocked APIs.",
      "phases": [
        {
          "id": "request",
          "title": "Capture the request",
          "goal": "Make entry accurate and accessible."
        },
        {
          "id": "draft",
          "title": "Keep work recoverable",
          "goal": "Preserve drafts and supporting documents."
        },
        {
          "id": "review",
          "title": "Make approval trustworthy",
          "goal": "Expose changes and protect submission boundaries."
        },
        {
          "id": "release",
          "title": "Validate the handoff",
          "goal": "Keep the workflow usable under team conditions."
        }
      ],
      "tickets": [
        {
          "id": "2e7a58ce-8173-4942-9746-3bc2d4d9136b",
          "key": "FORM-101",
          "title": "Point requesters to the field that needs fixing",
          "type": "STORY",
          "priority": "MEDIUM",
          "difficulty": "FOUNDATIONAL",
          "estimateMinutes": 75,
          "phaseId": "request",
          "dependsOn": [],
          "scenario": "An incomplete request produces only Invalid request. Add field feedback and an error summary for vendor, business reason, and delivery date.",
          "acceptanceCriteria": [
            "Invalid submission focuses a summary with links to fields.",
            "Each error is associated with its field and stays visible until resolved.",
            "Entered values survive client and server validation failures."
          ],
          "implementationNotes": [
            "Map known server field errors and use a safe general fallback for unknown errors."
          ],
          "verification": [
            "Submit an empty form by keyboard and follow each summary link.",
            "Return an unknown server validation code; preserve values and display an actionable general error."
          ],
          "deliverables": [
            "Accessible validation summary and server-error mapping cases"
          ],
          "rollout": "Ship behind the form flag; restore the earlier route if requesters cannot correct errors.",
          "skills": [
            "Form validation",
            "Accessibility",
            "API errors"
          ],
          "fieldMix": [
            {
              "field": "Frontend",
              "percentage": 50
            },
            {
              "field": "Accessibility",
              "percentage": 50
            }
          ],
          "patterns": []
        },
        {
          "id": "3f727678-cefb-4555-9bac-12dbda8ddcae",
          "key": "FORM-102",
          "title": "Make the total match the submitted line items",
          "type": "BUG",
          "priority": "HIGH",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 120,
          "phaseId": "request",
          "dependsOn": [],
          "scenario": "Three items at 19.99 sometimes display extra decimal digits. Finance also found a preview total that differed from the payload after a quantity edit.",
          "acceptanceCriteria": [
            "One calculation path derives display and payload totals from integer minor units.",
            "Fractional quantities, negative prices, overflow, and mixed currencies are rejected.",
            "Editing or removing a line immediately updates the same total."
          ],
          "implementationNotes": [
            "Document the fixed two-decimal practice currency and rounding rule; conversion is excluded."
          ],
          "verification": [
            "Enter three units at 19.99 and compare preview and payload at 59.97.",
            "Try overflow and negative amounts; submission is blocked with field errors."
          ],
          "deliverables": [
            "Pure total calculator and boundary fixtures"
          ],
          "rollout": "Compare old and new totals on synthetic requests; block submission rather than use inaccurate arithmetic on mismatch.",
          "skills": [
            "Money arithmetic",
            "Data modeling",
            "Boundary testing"
          ],
          "fieldMix": [
            {
              "field": "Frontend",
              "percentage": 100
            }
          ],
          "patterns": []
        },
        {
          "id": "13ef76d4-3d33-427e-bf8c-82a5b33f32b1",
          "key": "FORM-103",
          "title": "Clear an orphaned cost center when department changes",
          "type": "BUG",
          "priority": "MEDIUM",
          "difficulty": "FOUNDATIONAL",
          "estimateMinutes": 60,
          "phaseId": "request",
          "dependsOn": [
            "FORM-101"
          ],
          "scenario": "Selecting Marketing / Events and then changing to Engineering leaves Events hidden in the payload. The reviewer has to send the request back.",
          "acceptanceCriteria": [
            "Department changes clear any incompatible cost center.",
            "Loading and unavailable directory states differ from an empty directory.",
            "A previous department response cannot replace active options."
          ],
          "implementationNotes": [
            "Store stable directory IDs and explain why a selection was cleared."
          ],
          "verification": [
            "Switch Marketing / Events to Engineering; the field and payload both lose Events.",
            "Reverse response order and reject the active request; stale options remain unavailable."
          ],
          "deliverables": [
            "Dependent-select fix and delayed-directory tests"
          ],
          "rollout": "Release after synthetic department-switch checks; prevent submission if required directory data is unavailable.",
          "skills": [
            "Dependent forms",
            "Async state",
            "Data integrity"
          ],
          "fieldMix": [
            {
              "field": "Frontend",
              "percentage": 100
            }
          ],
          "patterns": []
        },
        {
          "id": "f0df6a5d-1dfb-453a-ad6d-1ce8f10ea892",
          "key": "FORM-104",
          "title": "Save drafts without overwriting a second browser tab",
          "type": "STORY",
          "priority": "HIGH",
          "difficulty": "ADVANCED",
          "estimateMinutes": 180,
          "phaseId": "draft",
          "dependsOn": [
            "FORM-102",
            "FORM-103"
          ],
          "scenario": "A requester edits quantities in one tab and the explanation in another. Background autosave overwrites whichever tab saved first.",
          "acceptanceCriteria": [
            "Autosave includes the loaded revision and serializes local writes.",
            "Conflicts pause autosave and offer reload or a reviewable copy of local edits.",
            "Navigating away with unsaved changes triggers a meaningful warning where supported."
          ],
          "implementationNotes": [
            "Never merge money fields automatically; preserve local edits for explicit conflict resolution."
          ],
          "verification": [
            "Save normally and verify the saved indicator follows the acknowledged revision.",
            "Race two tabs, then fail recovery fetch; local edits remain and autosave stays paused."
          ],
          "deliverables": [
            "Revision-aware draft coordinator and two-tab conflict reproduction"
          ],
          "rollout": "Pilot manual saves before autosave; disable autosave without changing stored drafts if conflicts rise.",
          "skills": [
            "Optimistic concurrency",
            "Draft recovery",
            "State machines"
          ],
          "fieldMix": [
            {
              "field": "Frontend",
              "percentage": 80
            },
            {
              "field": "API design",
              "percentage": 20
            }
          ],
          "patterns": []
        },
        {
          "id": "b63e748c-3b01-4b08-b737-711a15587a77",
          "key": "FORM-105",
          "title": "Show attachment quarantine and upload recovery clearly",
          "type": "STORY",
          "priority": "MEDIUM",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 120,
          "phaseId": "draft",
          "dependsOn": [
            "FORM-101"
          ],
          "scenario": "Quotes look attached immediately even though the upload service can reject or quarantine them. Requesters submit before learning the reviewer cannot open the PDF.",
          "acceptanceCriteria": [
            "Uploading, scanning, accepted, rejected, and failed have distinct labels.",
            "Submission stays unavailable while a required quote is pending or rejected.",
            "Retry uses the upload operation contract; removal cancels pending UI work."
          ],
          "implementationNotes": [
            "Treat names and service messages as untrusted text; scanning uses API fixtures here."
          ],
          "verification": [
            "Advance an upload from scanning to accepted and verify submission becomes available.",
            "Reject a file and resolve a removed upload late; neither appears accepted."
          ],
          "deliverables": [
            "Attachment state component and upload fixture scenarios"
          ],
          "rollout": "Pilot quote-required requests; disable new attachment submissions if accepted state cannot be reconciled.",
          "skills": [
            "Upload UX",
            "State modeling",
            "Untrusted input"
          ],
          "fieldMix": [
            {
              "field": "Frontend",
              "percentage": 80
            },
            {
              "field": "Security",
              "percentage": 20
            }
          ],
          "patterns": []
        },
        {
          "id": "6de07eb8-9aaa-404d-ac1b-47c83a584eaf",
          "key": "FORM-106",
          "title": "Show finance what changed since their last review",
          "type": "STORY",
          "priority": "HIGH",
          "difficulty": "ADVANCED",
          "estimateMinutes": 150,
          "phaseId": "review",
          "dependsOn": [
            "FORM-104",
            "FORM-105"
          ],
          "scenario": "After clarification, a requester changes both the explanation and amount. The review page says Updated without identifying which lines changed.",
          "acceptanceCriteria": [
            "The view compares two explicit immutable revisions.",
            "Stable line IDs identify additions, removals, and old/new values.",
            "Collapsing unchanged fields keeps total difference and revision identifiers visible."
          ],
          "implementationNotes": [
            "Use the shared money calculator; array position is not a line identity."
          ],
          "verification": [
            "Reorder unchanged lines and confirm no false replacements appear.",
            "Remove one line and increase another; verify both changes, total difference, and a missing-baseline error."
          ],
          "deliverables": [
            "Revision comparison view and change-set fixtures"
          ],
          "rollout": "Add a comparison tab; hide it on regression while preserving both original revision pages.",
          "skills": [
            "Diff presentation",
            "Immutable data",
            "Review workflows"
          ],
          "fieldMix": [
            {
              "field": "Frontend",
              "percentage": 100
            }
          ],
          "patterns": []
        },
        {
          "id": "d4bd4b1b-b325-4127-ba46-1a6654c5a282",
          "key": "FORM-107",
          "title": "Close the duplicate-submit gap after a slow response",
          "type": "BUG",
          "priority": "HIGH",
          "difficulty": "EXPERT",
          "estimateMinutes": 195,
          "phaseId": "review",
          "dependsOn": [
            "FORM-104",
            "FORM-105"
          ],
          "scenario": "A 15-second response prompts a refresh and second submission. Finance receives two approval requests. Coordinate submission identity with saved revisions and uncertain responses.",
          "acceptanceCriteria": [
            "Submission waits for acknowledgement of the exact displayed draft revision.",
            "One revision reuses a durable operation key across refresh and retry.",
            "Uncertain results use status lookup; edits cannot silently reuse the old key for a different revision."
          ],
          "implementationNotes": [
            "Use the idempotency/status contract; disabling the button alone is insufficient."
          ],
          "verification": [
            "Drop the accepted response, refresh, and retry; one logical submission exists under the same operation key.",
            "Edit during failed autosave and submit; no unsaved or mismatched revision is sent."
          ],
          "deliverables": [
            "Submission coordinator and refresh-after-timeout test"
          ],
          "rollout": "Pilot with one queue and inspect operation conflicts; disable new submissions on reconciliation failure while preserving drafts.",
          "skills": [
            "Idempotency",
            "Concurrency",
            "Workflow integrity"
          ],
          "fieldMix": [
            {
              "field": "Frontend",
              "percentage": 60
            },
            {
              "field": "Distributed systems",
              "percentage": 40
            }
          ],
          "patterns": []
        },
        {
          "id": "caf839bf-5797-46e5-ac1c-26f251afb64d",
          "key": "FORM-108",
          "title": "Retire approval controls when authority changes",
          "type": "BUG",
          "priority": "HIGH",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 90,
          "phaseId": "review",
          "dependsOn": [
            "FORM-106"
          ],
          "scenario": "An approver leaves the page open past delegation expiry. Approve fails, but controls remain active and the page incorrectly labels the request handled.",
          "acceptanceCriteria": [
            "Denial preserves request state and removes stale controls after authority refresh.",
            "Organization switching clears previous request and permissions before rendering new context.",
            "Access-loss messages omit private authorization diagnostics."
          ],
          "implementationNotes": [
            "UI checks improve interaction; the API must authorize every action independently."
          ],
          "verification": [
            "Approve with current authority and display only the confirmed result.",
            "Expire delegation and switch organizations mid-request; no success state or old request content appears."
          ],
          "deliverables": [
            "Authority-refresh handling and denied-approval browser case"
          ],
          "rollout": "Release denial handling independently; disable approvals separately while preserving permitted reading.",
          "skills": [
            "Authorization UX",
            "Tenant isolation",
            "Error handling"
          ],
          "fieldMix": [
            {
              "field": "Frontend",
              "percentage": 60
            },
            {
              "field": "Security",
              "percentage": 40
            }
          ],
          "patterns": []
        },
        {
          "id": "10a7b798-b71b-42f2-845f-51da7e375880",
          "key": "FORM-109",
          "title": "Make a 40-line request usable on a narrow screen",
          "type": "TASK",
          "priority": "MEDIUM",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 120,
          "phaseId": "release",
          "dependsOn": [
            "FORM-102",
            "FORM-106"
          ],
          "scenario": "At 200% zoom, a sticky total covers the final row and horizontal scrolling separates quantity inputs from labels. Fix the equipment-request layout.",
          "acceptanceCriteria": [
            "Labels, values, errors, and remove actions remain associated at the agreed narrow viewport and zoom.",
            "The total and footer never cover focusable content.",
            "Long vendor names and 40 lines remain readable without losing review information."
          ],
          "implementationNotes": [
            "Use semantic CSS; do not replace rows with unlabelled visual cards."
          ],
          "verification": [
            "Review the 40-line fixture by keyboard at 200% zoom and capture its final row.",
            "Use a long vendor and invalid quantity; the error remains visible without horizontal page overflow."
          ],
          "deliverables": [
            "Responsive line-item layout and zoom walkthrough"
          ],
          "rollout": "Preview on agreed finance viewports; revert the layout if associations or actions become inaccessible.",
          "skills": [
            "Responsive design",
            "CSS",
            "Accessibility"
          ],
          "fieldMix": [
            {
              "field": "Accessibility",
              "percentage": 60
            },
            {
              "field": "Frontend",
              "percentage": 40
            }
          ],
          "patterns": []
        },
        {
          "id": "f9983b25-288f-4976-8007-4cc1ec25eabb",
          "key": "FORM-110",
          "title": "Check the request-return-resubmit journey before release",
          "type": "CHORE",
          "priority": "MEDIUM",
          "difficulty": "ADVANCED",
          "estimateMinutes": 135,
          "phaseId": "release",
          "dependsOn": [
            "FORM-107",
            "FORM-108",
            "FORM-109"
          ],
          "scenario": "Form tests passed, but the last release lost a quote when finance returned a request. Add a deterministic journey through return, edit, and resubmission.",
          "acceptanceCriteria": [
            "The journey covers submission, finance return, changed amount, and review of the new revision.",
            "Attachment and request identities stay stable across revision creation.",
            "Resubmission failure leaves the earlier revision readable and the new draft recoverable."
          ],
          "implementationNotes": [
            "Synthetic users and API fixtures establish workflow behavior, not live-service readiness."
          ],
          "verification": [
            "Run twice from clean fixtures and compare final revision relationships.",
            "Fail after draft save before submission acknowledgement; attachments survive and requests do not duplicate."
          ],
          "deliverables": [
            "Workflow browser check and release/rollback checklist"
          ],
          "rollout": "Gate form releases on this journey; stop rollout on identity loss and restore the prior form build.",
          "skills": [
            "End-to-end testing",
            "Release engineering",
            "Recovery"
          ],
          "fieldMix": [
            {
              "field": "Quality engineering",
              "percentage": 70
            },
            {
              "field": "Frontend",
              "percentage": 30
            }
          ],
          "patterns": []
        }
      ]
    }
  ]
}
