{
  "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": "a8eb9cae-2929-4c42-90e3-0e6be4af2782",
      "key": "CSEARCH",
      "title": "A catalog search that survives interruption",
      "field": "Frontend",
      "summary": "Build navigable search and result recovery for a public directory.",
      "context": "Fictional workshop directory Cedar lists tools and spare parts using a local search adapter.",
      "stack": [
        "React",
        "TypeScript",
        "CSS Modules",
        "Testing Library"
      ],
      "prerequisites": [
        "Create synthetic items with stable identifiers.",
        "Stub cursor pages, facets, delayed responses and missing items."
      ],
      "developerValue": "Practice URL contracts and request ownership.",
      "companyValue": "Inspect whether discovery intent survives changing results.",
      "delivery": "Use local fixtures; external search services are excluded.",
      "phases": [
        {
          "id": "query",
          "title": "Specify search state",
          "goal": "Make queries shareable."
        },
        {
          "id": "results",
          "title": "Handle changing results",
          "goal": "Keep feedback attached to active queries."
        },
        {
          "id": "shipping",
          "title": "Prepare recovery",
          "goal": "Bound caching and support diagnosis."
        }
      ],
      "tickets": [
        {
          "id": "bee813b1-be0d-4057-9a08-38377e4c3309",
          "key": "CSEARCH-101",
          "title": "Normalize directory whitespace without changing part codes",
          "type": "TASK",
          "priority": "MEDIUM",
          "difficulty": "FOUNDATIONAL",
          "estimateMinutes": 60,
          "phaseId": "query",
          "dependsOn": [],
          "scenario": "Repeated spaces create separate search requests. Define normalization without altering case-sensitive part codes.",
          "acceptanceCriteria": [
            "Outer spaces trim",
            "Internal-space policy is documented",
            "Input preserves edits until submit"
          ],
          "implementationNotes": [
            "Do not silently lowercase identifiers."
          ],
          "verification": [
            "Submit padded code",
            "Submit whitespace only"
          ],
          "deliverables": [
            "Query parser and examples"
          ],
          "rollout": "Revert normalization while accepting old links.",
          "skills": [
            "URL state",
            "Parsing"
          ],
          "fieldMix": [
            {
              "field": "Frontend",
              "percentage": 100
            }
          ],
          "patterns": []
        },
        {
          "id": "4f3e73e8-da44-4de7-84c9-ee7f84b07306",
          "key": "CSEARCH-102",
          "title": "Expose selected directory facets in shared links",
          "type": "STORY",
          "priority": "MEDIUM",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 105,
          "phaseId": "query",
          "dependsOn": [
            "CSEARCH-101"
          ],
          "scenario": "Shared search links conceal which filters excluded parts. Render removable facets from validated URL values.",
          "acceptanceCriteria": [
            "Active facets have labels",
            "Unknown facets use visible fallback",
            "Removal resets pagination"
          ],
          "implementationNotes": [
            "Resolve labels from trusted metadata."
          ],
          "verification": [
            "Open two selected facets",
            "Use unknown facet ID"
          ],
          "deliverables": [
            "Facet controls and URL cases"
          ],
          "rollout": "Disable facet writes while retaining parsing.",
          "skills": [
            "Navigation",
            "Filtering"
          ],
          "fieldMix": [
            {
              "field": "Frontend",
              "percentage": 100
            }
          ],
          "patterns": []
        },
        {
          "id": "40dbb1f2-a650-4d74-a1bb-29ffbe8040e7",
          "key": "CSEARCH-103",
          "title": "Announce completed directory searches once",
          "type": "BUG",
          "priority": "MEDIUM",
          "difficulty": "FOUNDATIONAL",
          "estimateMinutes": 75,
          "phaseId": "query",
          "dependsOn": [
            "CSEARCH-101"
          ],
          "scenario": "A live region reads counts after every keystroke. Announce committed search outcomes without moving focus.",
          "acceptanceCriteria": [
            "Count announces on completion",
            "Loading has one status",
            "Errors explain retry"
          ],
          "implementationNotes": [
            "Do not focus status updates."
          ],
          "verification": [
            "Submit and inspect announcement",
            "Reject search and inspect error"
          ],
          "deliverables": [
            "Status component and manual protocol"
          ],
          "rollout": "Use submit-only announcements if updates repeat.",
          "skills": [
            "Accessibility",
            "Live regions"
          ],
          "fieldMix": [
            {
              "field": "Accessibility",
              "percentage": 70
            },
            {
              "field": "Frontend",
              "percentage": 30
            }
          ],
          "patterns": []
        },
        {
          "id": "c90114cd-15ae-4e8d-ad99-e38cb94e616a",
          "key": "CSEARCH-104",
          "title": "Bind directory page cursors to their originating query",
          "type": "BUG",
          "priority": "HIGH",
          "difficulty": "ADVANCED",
          "estimateMinutes": 165,
          "phaseId": "results",
          "dependsOn": [
            "CSEARCH-102"
          ],
          "scenario": "More results races with facet changes and appends unrelated products. Scope pages to query identity.",
          "acceptanceCriteria": [
            "Matching pages alone append",
            "Facet changes invalidate cursors",
            "IDs deduplicate repeated results"
          ],
          "implementationNotes": [
            "Cancellation alone is insufficient."
          ],
          "verification": [
            "Append matching page",
            "Resolve old page after new query"
          ],
          "deliverables": [
            "Query-scoped pager and race test"
          ],
          "rollout": "Disable incremental paging on ownership failure.",
          "skills": [
            "Race conditions",
            "Pagination"
          ],
          "fieldMix": [
            {
              "field": "Frontend",
              "percentage": 80
            },
            {
              "field": "API design",
              "percentage": 20
            }
          ],
          "patterns": []
        },
        {
          "id": "aa435d3b-18cf-4189-aac9-98dc4d554a5d",
          "key": "CSEARCH-105",
          "title": "Restore a directory result anchor after detail navigation",
          "type": "STORY",
          "priority": "MEDIUM",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 150,
          "phaseId": "results",
          "dependsOn": [
            "CSEARCH-104"
          ],
          "scenario": "Returning from a part detail resets a long search to the top. Restore the visited item.",
          "acceptanceCriteria": [
            "Back restores query and anchor",
            "Missing item has fallback",
            "Focus returns to originating link"
          ],
          "implementationNotes": [
            "Do not identify anchors only by pixels."
          ],
          "verification": [
            "Visit later result and return",
            "Remove it before Back"
          ],
          "deliverables": [
            "History restoration and browser case"
          ],
          "rollout": "Disable anchoring while retaining query history.",
          "skills": [
            "Navigation",
            "Focus"
          ],
          "fieldMix": [
            {
              "field": "Frontend",
              "percentage": 60
            },
            {
              "field": "Accessibility",
              "percentage": 40
            }
          ],
          "patterns": []
        },
        {
          "id": "18c1e082-9a83-44f9-87e1-5a78c525f9d1",
          "key": "CSEARCH-106",
          "title": "Explain unavailable parts in bookmarked directory links",
          "type": "STORY",
          "priority": "MEDIUM",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 120,
          "phaseId": "results",
          "dependsOn": [
            "CSEARCH-101"
          ],
          "scenario": "An old part bookmark renders a blank page. Distinguish removal from failed loading.",
          "acceptanceCriteria": [
            "Missing and failed reads differ",
            "Return preserves valid search",
            "External return targets are rejected"
          ],
          "implementationNotes": [
            "Allowlist internal return routes."
          ],
          "verification": [
            "Open removed part",
            "Inject external return URL"
          ],
          "deliverables": [
            "Unavailable state and route tests"
          ],
          "rollout": "Restore static unavailable page if navigation breaks.",
          "skills": [
            "Error handling",
            "URL safety"
          ],
          "fieldMix": [
            {
              "field": "Frontend",
              "percentage": 70
            },
            {
              "field": "Security",
              "percentage": 30
            }
          ],
          "patterns": []
        },
        {
          "id": "992eb921-cc0a-4182-8124-4f9fe8f11100",
          "key": "CSEARCH-107",
          "title": "Keep zero-count directory facets removable",
          "type": "BUG",
          "priority": "MEDIUM",
          "difficulty": "ADVANCED",
          "estimateMinutes": 180,
          "phaseId": "results",
          "dependsOn": [
            "CSEARCH-102",
            "CSEARCH-104"
          ],
          "scenario": "Selected filters disappear when counts reach zero, trapping the query. Separate selection from availability.",
          "acceptanceCriteria": [
            "Selected zero-count facets remain",
            "Stale counts are labeled",
            "Count failure preserves selection"
          ],
          "implementationNotes": [
            "Count refresh cannot rewrite selections."
          ],
          "verification": [
            "Select zero-result facet",
            "Reject facet-count refresh"
          ],
          "deliverables": [
            "Facet reconciliation and cases"
          ],
          "rollout": "Freeze counts while preserving removal.",
          "skills": [
            "State management",
            "Resilience"
          ],
          "fieldMix": [
            {
              "field": "Frontend",
              "percentage": 100
            }
          ],
          "patterns": []
        },
        {
          "id": "5341c4c6-5d0f-4906-8cef-a4f2ecbe0576",
          "key": "CSEARCH-108",
          "title": "Bound the directory query cache",
          "type": "TASK",
          "priority": "MEDIUM",
          "difficulty": "EXPERT",
          "estimateMinutes": 240,
          "phaseId": "shipping",
          "dependsOn": [
            "CSEARCH-104",
            "CSEARCH-107"
          ],
          "scenario": "Browsing many searches grows memory indefinitely. Introduce capacity and expiry with deterministic eviction.",
          "acceptanceCriteria": [
            "Capacity is configurable",
            "Keys contain all query dimensions",
            "Expired entries refresh visibly"
          ],
          "implementationNotes": [
            "Cache public directory records only."
          ],
          "verification": [
            "Revisit retained query",
            "Exceed capacity and inspect eviction"
          ],
          "deliverables": [
            "Cache policy and clock tests"
          ],
          "rollout": "Disable cache and fetch committed queries.",
          "skills": [
            "Caching",
            "Memory"
          ],
          "fieldMix": [
            {
              "field": "Performance engineering",
              "percentage": 60
            },
            {
              "field": "Frontend",
              "percentage": 40
            }
          ],
          "patterns": []
        },
        {
          "id": "7a19a7e9-4633-45b2-9083-2dd7c22bd52a",
          "key": "CSEARCH-109",
          "title": "Print a directory shortlist from current item identities",
          "type": "STORY",
          "priority": "MEDIUM",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 135,
          "phaseId": "shipping",
          "dependsOn": [
            "CSEARCH-105",
            "CSEARCH-106"
          ],
          "scenario": "Printed shortlists include removed entries. Use a dedicated projection resolved by current item IDs.",
          "acceptanceCriteria": [
            "Missing items are identified",
            "Removal updates print",
            "Controls are hidden in print"
          ],
          "implementationNotes": [
            "Use the local directory adapter."
          ],
          "verification": [
            "Print two entries",
            "Remove one before printing"
          ],
          "deliverables": [
            "Print view and media checks"
          ],
          "rollout": "Hide printing if projections diverge.",
          "skills": [
            "CSS",
            "Projection"
          ],
          "fieldMix": [
            {
              "field": "Frontend",
              "percentage": 100
            }
          ],
          "patterns": []
        },
        {
          "id": "c3910c4e-87a5-4314-a44e-9db99939cb90",
          "key": "CSEARCH-110",
          "title": "Instrument directory search without capturing query text",
          "type": "CHORE",
          "priority": "LOW",
          "difficulty": "ADVANCED",
          "estimateMinutes": 120,
          "phaseId": "shipping",
          "dependsOn": [
            "CSEARCH-103",
            "CSEARCH-108"
          ],
          "scenario": "Search failure metrics currently include potentially sensitive part queries. Add bounded operational events.",
          "acceptanceCriteria": [
            "Includes outcome and duration bucket",
            "Omits queries and item names",
            "Retries retain correlation token"
          ],
          "implementationNotes": [
            "Verify through a local event collector."
          ],
          "verification": [
            "Complete search and inspect event",
            "Fail secret-like query and check exclusion"
          ],
          "deliverables": [
            "Event allowlist and samples"
          ],
          "rollout": "Disable event sink if unexpected fields appear.",
          "skills": [
            "Instrumentation",
            "Privacy"
          ],
          "fieldMix": [
            {
              "field": "Privacy engineering",
              "percentage": 60
            },
            {
              "field": "Frontend",
              "percentage": 40
            }
          ],
          "patterns": []
        }
      ]
    }
  ]
}
