{
  "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": "f6dc6904-7fbe-4c66-bb1a-d6e1540bebba",
      "key": "DESK",
      "title": "A support console that survives a busy shift",
      "field": "Frontend",
      "summary": "Repair queue navigation, reply composition, and agent handoffs in a customer support workspace.",
      "context": "The fictional Alder support team handles billing and delivery conversations in a browser console. Agents keep several tickets open, share filtered queues, and work through unreliable office Wi-Fi. The API contract returns ticket revisions and cursor-based pages; work stays within the console and its test adapter.",
      "stack": [
        "React",
        "TypeScript",
        "CSS Modules",
        "Testing Library",
        "Playwright"
      ],
      "prerequisites": [
        "A ticket-list and reply API contract to implement or stub",
        "Synthetic conversations across two organizations with revision conflicts"
      ],
      "developerValue": "Practice state ownership, asynchronous races, accessible interaction, and recovery in bounded changes.",
      "companyValue": "Inspect how an engineer protects agent work and handles failures that interrupt support operations.",
      "delivery": "Choose one ticket or deliver the phases as separate pull requests. Use synthetic customer content; live email delivery is outside scope.",
      "phases": [
        {
          "id": "orientation",
          "title": "Make the queue dependable",
          "goal": "Keep navigation and list state understandable."
        },
        {
          "id": "workspace",
          "title": "Protect the conversation",
          "goal": "Keep drafts, revisions, and focus tied to the right ticket."
        },
        {
          "id": "resilience",
          "title": "Handle interruptions",
          "goal": "Recover from partial failures and concurrent work."
        },
        {
          "id": "handoff",
          "title": "Prepare the next shift",
          "goal": "Make the release diagnosable and supportable."
        }
      ],
      "tickets": [
        {
          "id": "b140e640-2ac6-440b-93f4-917506f105fb",
          "key": "DESK-101",
          "title": "Keep shared queue links useful after refresh",
          "type": "STORY",
          "priority": "MEDIUM",
          "difficulty": "FOUNDATIONAL",
          "estimateMinutes": 75,
          "phaseId": "orientation",
          "dependsOn": [],
          "scenario": "A shift lead shares the unassigned billing queue, but recipients land on All tickets. Put filters in the URL so opening the link, refreshing, and using Back retain the intended view.",
          "acceptanceCriteria": [
            "Status, team, and reference filters round-trip through the URL.",
            "Unknown filter values use documented defaults without crashing.",
            "Back restores the preceding filters and clears any cursor invalidated by them."
          ],
          "implementationNotes": [
            "Reference search targets synthetic ticket IDs; customer message text must not enter query parameters."
          ],
          "verification": [
            "Open team=billing and status=unassigned in a fresh tab and inspect the request.",
            "Use an unknown status, then navigate Back after two filter changes; verify fallback and history order."
          ],
          "deliverables": [
            "Queue URL parser and browser navigation regression case"
          ],
          "rollout": "Enable URL filters for the internal queue; a flag restores the default queue without invalidating ticket links.",
          "skills": [
            "URL state",
            "React",
            "Navigation"
          ],
          "fieldMix": [
            {
              "field": "Frontend",
              "percentage": 100
            }
          ],
          "patterns": []
        },
        {
          "id": "3973e7cf-fee7-4139-8a0f-afcbe009a5f1",
          "key": "DESK-102",
          "title": "Separate an empty queue from a failed request",
          "type": "BUG",
          "priority": "HIGH",
          "difficulty": "FOUNDATIONAL",
          "estimateMinutes": 60,
          "phaseId": "orientation",
          "dependsOn": [],
          "scenario": "During an API outage the console said No tickets, and an agent assumed the queue was clear. Give loading, empty, stale, and failed responses distinct presentations.",
          "acceptanceCriteria": [
            "An empty successful response shows active filters and a clear-filters action.",
            "A failed refresh retains previously loaded rows and labels them stale.",
            "Retry fetches the active query once and exposes an accessible error if it fails again."
          ],
          "implementationNotes": [
            "Keep the last successful response while refreshing."
          ],
          "verification": [
            "Return an empty successful page and verify no outage message appears.",
            "Load three rows, reject refresh, then recover on retry; rows persist until replacement."
          ],
          "deliverables": [
            "Explicit queue request states and failure screenshots"
          ],
          "rollout": "Release state rendering independently; revert the presentation component if the queue becomes unusable.",
          "skills": [
            "Error handling",
            "UI state",
            "Accessibility"
          ],
          "fieldMix": [
            {
              "field": "Frontend",
              "percentage": 70
            },
            {
              "field": "Accessibility",
              "percentage": 30
            }
          ],
          "patterns": []
        },
        {
          "id": "5d2dd4c9-dd25-4437-b25f-53e916a95cf9",
          "key": "DESK-103",
          "title": "Stop a late search response replacing the current queue",
          "type": "BUG",
          "priority": "HIGH",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 105,
          "phaseId": "orientation",
          "dependsOn": [
            "DESK-101",
            "DESK-102"
          ],
          "scenario": "On a slow connection, searching DL-42 and immediately clearing the field sometimes repopulates the screen with DL-42 results. Make response ownership follow the active query.",
          "acceptanceCriteria": [
            "Only a response for the active query updates rows, counts, or errors.",
            "Changing filters abandons the previous page cursor.",
            "Unmounting the queue prevents late responses from changing visible state."
          ],
          "implementationNotes": [
            "Guard against adapters that resolve after cancellation."
          ],
          "verification": [
            "Resolve two requests in reverse order and confirm the last selection wins.",
            "Reject a superseded request after the current one succeeds; no stale error appears."
          ],
          "deliverables": [
            "Query ownership fix and deterministic out-of-order response test"
          ],
          "rollout": "Canary with the support pilot; revert the coordinator if current-query results stop loading.",
          "skills": [
            "Async state",
            "Race conditions",
            "Testing"
          ],
          "fieldMix": [
            {
              "field": "Frontend",
              "percentage": 100
            }
          ],
          "patterns": []
        },
        {
          "id": "b38547ea-48c5-4130-ae20-45924e80c709",
          "key": "DESK-104",
          "title": "Restore the right reply draft when switching tickets",
          "type": "STORY",
          "priority": "HIGH",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 120,
          "phaseId": "workspace",
          "dependsOn": [
            "DESK-103"
          ],
          "scenario": "Agents alternate between conversations while checking orders. The editor resets on every switch, and one experimental fix restored a draft into the wrong customer thread.",
          "acceptanceCriteria": [
            "Draft keys include organization, signed-in agent, and ticket.",
            "Returning to a ticket restores its text without copying it to another conversation.",
            "Sign-out clears local drafts; storage refusal leaves editing usable with a persistence notice."
          ],
          "implementationNotes": [
            "Use synthetic text and never record draft content in analytics."
          ],
          "verification": [
            "Write different drafts on two tickets, switch repeatedly, and reload in the same account.",
            "Switch organizations and simulate quota failure; no prior-account draft appears and typing still works."
          ],
          "deliverables": [
            "Scoped draft store and account-switch regression coverage"
          ],
          "rollout": "Start with session-scoped storage; disable persistence and retain the in-memory editor if corruption is reported.",
          "skills": [
            "State persistence",
            "Privacy",
            "Storage"
          ],
          "fieldMix": [
            {
              "field": "Frontend",
              "percentage": 60
            },
            {
              "field": "Privacy engineering",
              "percentage": 40
            }
          ],
          "patterns": []
        },
        {
          "id": "4aecd497-906e-41bf-8f01-cee00ca9815a",
          "key": "DESK-105",
          "title": "Make ticket switching predictable from the keyboard",
          "type": "TASK",
          "priority": "MEDIUM",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 90,
          "phaseId": "workspace",
          "dependsOn": [
            "DESK-102"
          ],
          "scenario": "An agent navigating by keyboard loses their place after closing ticket details. Define focus behavior for opening, closing, and removing the selected row.",
          "acceptanceCriteria": [
            "Opening details moves focus to the detail heading or first intentional control.",
            "Closing returns focus to the originating row or a documented adjacent fallback.",
            "Shortcuts do not fire in text inputs or during input method composition."
          ],
          "implementationNotes": [
            "Shortcuts supplement semantic controls and the ordinary tab sequence."
          ],
          "verification": [
            "Open and close details using only the keyboard and inspect focus.",
            "Remove the originating row while details are open, then close; focus remains visible and useful."
          ],
          "deliverables": [
            "Focus restoration and keyboard walkthrough notes"
          ],
          "rollout": "Ship focus restoration before shortcuts; disable shortcuts separately if they conflict with editing.",
          "skills": [
            "Keyboard access",
            "Focus management",
            "React"
          ],
          "fieldMix": [
            {
              "field": "Accessibility",
              "percentage": 60
            },
            {
              "field": "Frontend",
              "percentage": 40
            }
          ],
          "patterns": []
        },
        {
          "id": "392d0ee6-6827-4558-849a-60ab9536e0c2",
          "key": "DESK-106",
          "title": "Show assignment conflicts without pretending a save worked",
          "type": "BUG",
          "priority": "HIGH",
          "difficulty": "ADVANCED",
          "estimateMinutes": 150,
          "phaseId": "workspace",
          "dependsOn": [
            "DESK-103"
          ],
          "scenario": "Two leads assign the same ticket within a second. One console displays its optimistic assignment forever even though the API rejected the stale revision.",
          "acceptanceCriteria": [
            "Writes include the last observed ticket revision.",
            "A conflict restores authoritative assignment and explains the competing update.",
            "An older rollback cannot overwrite a newer confirmed assignment."
          ],
          "implementationNotes": [
            "The API remains authoritative; never automatically retry a stale assignment."
          ],
          "verification": [
            "Accept an assignment and verify the returned revision replaces the local one.",
            "Interleave two changes with a conflict arriving last; the newest authoritative owner remains visible."
          ],
          "deliverables": [
            "Revision-aware assignment flow and interleaving tests"
          ],
          "rollout": "Enable for one team and inspect conflicts; fall back to confirmed saves if optimistic rollback proves unreliable.",
          "skills": [
            "Concurrency",
            "Optimistic UI",
            "API contracts"
          ],
          "fieldMix": [
            {
              "field": "Frontend",
              "percentage": 70
            },
            {
              "field": "API design",
              "percentage": 30
            }
          ],
          "patterns": []
        },
        {
          "id": "86f7d66d-8ea5-467b-863b-4cc2cf86c8a4",
          "key": "DESK-107",
          "title": "Report partial bulk-close results per ticket",
          "type": "STORY",
          "priority": "MEDIUM",
          "difficulty": "ADVANCED",
          "estimateMinutes": 165,
          "phaseId": "resilience",
          "dependsOn": [
            "DESK-106"
          ],
          "scenario": "Closing 25 conversations returns 22 successes, two permission denials, and one revision conflict. Today a green toast appears and every selected row disappears.",
          "acceptanceCriteria": [
            "Successful IDs leave the queue only if its filter excludes closed work.",
            "Denied and conflicting tickets remain selected with individual reasons.",
            "Retry targets retryable failures without replaying confirmed closes."
          ],
          "implementationNotes": [
            "Interpret the result per ID; a successful HTTP status does not mean every item succeeded."
          ],
          "verification": [
            "Run mixed outcomes and compare row visibility and selection with each result.",
            "Inspect retry IDs; denied and already closed items are excluded."
          ],
          "deliverables": [
            "Bulk result summary and mixed-outcome integration test"
          ],
          "rollout": "Limit the first release to 25 tickets; disable bulk close while retaining individual close on regression.",
          "skills": [
            "Batch operations",
            "Authorization UX",
            "Error recovery"
          ],
          "fieldMix": [
            {
              "field": "Frontend",
              "percentage": 80
            },
            {
              "field": "API design",
              "percentage": 20
            }
          ],
          "patterns": []
        },
        {
          "id": "7e16f42d-c88f-41a9-8a4c-45532b1de624",
          "key": "DESK-108",
          "title": "Reconcile an uncertain reply send after a connection drop",
          "type": "BUG",
          "priority": "URGENT",
          "difficulty": "EXPERT",
          "estimateMinutes": 210,
          "phaseId": "resilience",
          "dependsOn": [
            "DESK-104",
            "DESK-106"
          ],
          "scenario": "The server accepts a reply, but Wi-Fi drops before acknowledgement. Clicking Send again duplicates the email. Model an uncertain result and recover through operation lookup.",
          "acceptanceCriteria": [
            "One operation key survives timeout and explicit retry.",
            "Uncertain sending preserves the draft and checks status before clearing it.",
            "Changed text receives a new operation only after reconciliation; account changes stop status polling."
          ],
          "implementationNotes": [
            "Use a stubbed idempotent send/status contract; actual email delivery is excluded."
          ],
          "verification": [
            "Accept sending but drop the response, then report accepted from lookup; display one reply.",
            "Return unknown and then unavailable from lookup; preserve the draft and prevent an unkeyed duplicate."
          ],
          "deliverables": [
            "Reply send state machine and lost-acknowledgement test"
          ],
          "rollout": "Pilot with synthetic mailboxes; disable the new sending flow if operation reconciliation cannot be trusted.",
          "skills": [
            "Idempotency",
            "State machines",
            "Network recovery"
          ],
          "fieldMix": [
            {
              "field": "Frontend",
              "percentage": 60
            },
            {
              "field": "Distributed systems",
              "percentage": 40
            }
          ],
          "patterns": []
        },
        {
          "id": "a2db3a36-c354-4ea1-97ad-653e344c8a43",
          "key": "DESK-109",
          "title": "Keep a long conversation responsive without hiding context",
          "type": "TASK",
          "priority": "MEDIUM",
          "difficulty": "ADVANCED",
          "estimateMinutes": 150,
          "phaseId": "resilience",
          "dependsOn": [
            "DESK-105"
          ],
          "scenario": "An 800-message synthetic conversation makes expanding attachments slow. Reduce initial rendering while preserving chronological reading and position when older messages load.",
          "acceptanceCriteria": [
            "Initial rendering uses a bounded window and explicit load-older control.",
            "Prepending older messages preserves the visible anchor and keyboard focus.",
            "A before/after profile on the same fixture records the performance budget and measurement environment."
          ],
          "implementationNotes": [
            "Prefer explicit pagination if virtualization would break assistive-technology reading order."
          ],
          "verification": [
            "Profile the 800-message fixture and load two older pages without a scroll jump.",
            "Fail and retry an older-page request; no messages duplicate and the current position remains."
          ],
          "deliverables": [
            "Bounded message rendering and reproducible profile note"
          ],
          "rollout": "Enable above an agreed conversation size; restore full rendering if reading order regresses.",
          "skills": [
            "Performance profiling",
            "Pagination",
            "Accessibility"
          ],
          "fieldMix": [
            {
              "field": "Performance engineering",
              "percentage": 50
            },
            {
              "field": "Frontend",
              "percentage": 30
            },
            {
              "field": "Accessibility",
              "percentage": 20
            }
          ],
          "patterns": []
        },
        {
          "id": "377936ee-afd8-4102-942c-88e2f77c4bc5",
          "key": "DESK-110",
          "title": "Give support a useful report when the console fails",
          "type": "CHORE",
          "priority": "MEDIUM",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 90,
          "phaseId": "handoff",
          "dependsOn": [
            "DESK-107",
            "DESK-108",
            "DESK-109"
          ],
          "scenario": "On-call receives screenshots saying It stopped working with no clue whether loading, assignment, or sending failed. Add redacted diagnostics and a release handoff.",
          "acceptanceCriteria": [
            "Diagnostics include build version, operation category, timestamp, and safe correlation ID.",
            "Customer text, drafts, tokens, and raw responses are excluded.",
            "Copying diagnostics after failure preserves current draft and navigation."
          ],
          "implementationNotes": [
            "Use a field allowlist and document the owner of each operation category."
          ],
          "verification": [
            "Trigger sending failure and match its correlation ID to the synthetic trace.",
            "Put secret-like strings in messages and errors; copied diagnostics contain none."
          ],
          "deliverables": [
            "Redacted diagnostics panel and one-page console runbook"
          ],
          "rollout": "Enable diagnostic copying for the pilot; hide it immediately if excluded content appears.",
          "skills": [
            "Observability",
            "Privacy",
            "Operational handoff"
          ],
          "fieldMix": [
            {
              "field": "Frontend",
              "percentage": 40
            },
            {
              "field": "Privacy engineering",
              "percentage": 30
            },
            {
              "field": "Site reliability",
              "percentage": 30
            }
          ],
          "patterns": []
        }
      ]
    }
  ]
}
