{
  "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": "5e342e68-06ac-450f-97dc-2882570fedea",
      "key": "CLINK",
      "title": "Mobile invitation and session navigation",
      "field": "Mobile",
      "summary": "Handle deep links, expired sessions, and interrupted navigation using local identity stubs.",
      "context": "Fictional team-planning app Finch opens invitations and shared work items. All users and sessions are synthetic; implement the app in an emulator without external identity credentials.",
      "stack": [
        "React Native",
        "TypeScript",
        "Emulator",
        "Testing Library"
      ],
      "prerequisites": [
        "Create a local session adapter with expiry and revocation.",
        "Define synthetic invitation and work-item links."
      ],
      "developerValue": "Practice navigation state, capability checks and session boundaries.",
      "companyValue": "Inspect whether links remain useful without bypassing authorization.",
      "delivery": "Deliver local navigation behaviors; real identity integrations remain separate.",
      "phases": [
        {
          "id": "links",
          "title": "Parse entry points",
          "goal": "Establish safe route contracts."
        },
        {
          "id": "sessions",
          "title": "Handle identity changes",
          "goal": "Preserve only authorized navigation intent."
        },
        {
          "id": "recovery",
          "title": "Test interruption",
          "goal": "Recover process and session transitions."
        }
      ],
      "tickets": [
        {
          "id": "94ab1322-3e8e-4767-8a36-0fa4f6ce1394",
          "key": "CLINK-101",
          "title": "Reject malformed mobile invitation routes before navigation",
          "type": "TASK",
          "priority": "HIGH",
          "difficulty": "FOUNDATIONAL",
          "estimateMinutes": 75,
          "phaseId": "links",
          "dependsOn": [],
          "scenario": "Pasted links with missing invitation IDs crash route construction. Add strict parsing before mounting screens.",
          "acceptanceCriteria": [
            "Valid IDs produce typed routes",
            "Malformed links show a recoverable state",
            "Unsupported schemes are rejected"
          ],
          "implementationNotes": [
            "Do not render arbitrary link text as markup."
          ],
          "verification": [
            "Open valid fixture link",
            "Open empty and oversized IDs"
          ],
          "deliverables": [
            "Link parser and invalid-input cases"
          ],
          "rollout": "Disable invitation entry while keeping home navigation.",
          "skills": [
            "Parsing",
            "Navigation"
          ],
          "fieldMix": [
            {
              "field": "Mobile",
              "percentage": 70
            },
            {
              "field": "Security",
              "percentage": 30
            }
          ],
          "patterns": []
        },
        {
          "id": "bd963918-9199-4570-a221-49277c472b71",
          "key": "CLINK-102",
          "title": "Explain expired mobile invitations without automatic retries",
          "type": "STORY",
          "priority": "MEDIUM",
          "difficulty": "FOUNDATIONAL",
          "estimateMinutes": 75,
          "phaseId": "links",
          "dependsOn": [
            "CLINK-101"
          ],
          "scenario": "Expired invitations spin indefinitely because the client treats every response as transient. Render terminal expiry distinctly.",
          "acceptanceCriteria": [
            "Expiry stops retry",
            "User can return home",
            "Temporary failures retain manual retry"
          ],
          "implementationNotes": [
            "Follow typed adapter error codes."
          ],
          "verification": [
            "Open expired fixture",
            "Simulate temporary outage"
          ],
          "deliverables": [
            "Invitation error states"
          ],
          "rollout": "Restore static invitation failure page if classification breaks.",
          "skills": [
            "Error handling",
            "Mobile UI"
          ],
          "fieldMix": [
            {
              "field": "Mobile",
              "percentage": 100
            }
          ],
          "patterns": []
        },
        {
          "id": "723af4e7-3069-4d2c-9157-829e93dbec20",
          "key": "CLINK-103",
          "title": "Retain a mobile link destination through local sign-in",
          "type": "STORY",
          "priority": "MEDIUM",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 120,
          "phaseId": "links",
          "dependsOn": [
            "CLINK-101",
            "CLINK-102"
          ],
          "scenario": "Signing in from an invitation lands on the dashboard. Preserve a validated destination until identity is available.",
          "acceptanceCriteria": [
            "Valid destination resumes once",
            "External destinations are rejected",
            "Cancel sign-in clears pending intent"
          ],
          "implementationNotes": [
            "Store route identity, not raw credentials or URL tokens."
          ],
          "verification": [
            "Sign in and resume",
            "Cancel then sign in normally"
          ],
          "deliverables": [
            "Pending-route coordinator"
          ],
          "rollout": "Disable continuation if validation fails.",
          "skills": [
            "Navigation",
            "State management"
          ],
          "fieldMix": [
            {
              "field": "Mobile",
              "percentage": 80
            },
            {
              "field": "Security",
              "percentage": 20
            }
          ],
          "patterns": []
        },
        {
          "id": "b1bcdf64-7b6a-45d1-9913-1485c742edce",
          "key": "CLINK-104",
          "title": "Recheck mobile work-item access after account switching",
          "type": "BUG",
          "priority": "HIGH",
          "difficulty": "ADVANCED",
          "estimateMinutes": 180,
          "phaseId": "sessions",
          "dependsOn": [
            "CLINK-103"
          ],
          "scenario": "Switching accounts leaves the previous account's work screen visible. Clear account-scoped cache before refetching.",
          "acceptanceCriteria": [
            "Old content clears on switch",
            "New identity requires authorized read",
            "Denied reads show no prior item data"
          ],
          "implementationNotes": [
            "Account identity belongs in cache boundaries."
          ],
          "verification": [
            "Switch to authorized account",
            "Switch to denied account"
          ],
          "deliverables": [
            "Account-switch isolation and checks"
          ],
          "rollout": "Force full local session reset while repaired.",
          "skills": [
            "Authorization",
            "Caching"
          ],
          "fieldMix": [
            {
              "field": "Security",
              "percentage": 70
            },
            {
              "field": "Mobile",
              "percentage": 30
            }
          ],
          "patterns": []
        },
        {
          "id": "930c4ab4-8742-4877-8b91-f73b40bb2af9",
          "key": "CLINK-105",
          "title": "Serialize mobile session refresh for simultaneous requests",
          "type": "TASK",
          "priority": "HIGH",
          "difficulty": "ADVANCED",
          "estimateMinutes": 180,
          "phaseId": "sessions",
          "dependsOn": [
            "CLINK-103"
          ],
          "scenario": "Opening a linked screen triggers three refresh calls and conflicting session updates. Share one in-flight refresh.",
          "acceptanceCriteria": [
            "Concurrent reads share refresh",
            "Failure releases waiting requests consistently",
            "Later retry can start anew"
          ],
          "implementationNotes": [
            "Session tokens stay outside generic logs."
          ],
          "verification": [
            "Trigger three expired reads",
            "Reject refresh then retry"
          ],
          "deliverables": [
            "Refresh coordinator and concurrency tests"
          ],
          "rollout": "Stop automatic refresh and require explicit sign-in.",
          "skills": [
            "Concurrency",
            "Authentication"
          ],
          "fieldMix": [
            {
              "field": "Mobile",
              "percentage": 50
            },
            {
              "field": "Security",
              "percentage": 30
            },
            {
              "field": "Performance engineering",
              "percentage": 20
            }
          ],
          "patterns": []
        },
        {
          "id": "ee8175ab-7137-4d1c-8a5c-e99dd084592e",
          "key": "CLINK-106",
          "title": "Cancel pending mobile navigation when invitation ownership changes",
          "type": "BUG",
          "priority": "HIGH",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 135,
          "phaseId": "sessions",
          "dependsOn": [
            "CLINK-104",
            "CLINK-105"
          ],
          "scenario": "An invitation opened before account switching completes under the wrong identity. Bind continuation to the initiating session generation.",
          "acceptanceCriteria": [
            "Stale continuation is discarded",
            "Current session can reopen link",
            "No prior invitation details remain visible"
          ],
          "implementationNotes": [
            "Cancellation must survive late adapter responses."
          ],
          "verification": [
            "Resolve current invitation",
            "Resolve old invitation after switch"
          ],
          "deliverables": [
            "Session-scoped continuation guard"
          ],
          "rollout": "Clear all pending routes on account change.",
          "skills": [
            "Race conditions",
            "Isolation"
          ],
          "fieldMix": [
            {
              "field": "Mobile",
              "percentage": 50
            },
            {
              "field": "Security",
              "percentage": 50
            }
          ],
          "patterns": []
        },
        {
          "id": "fa337840-8ea6-48d4-b6d1-ddf4e6bb13dc",
          "key": "CLINK-107",
          "title": "Recover mobile sign-in continuation after process restart",
          "type": "STORY",
          "priority": "MEDIUM",
          "difficulty": "EXPERT",
          "estimateMinutes": 240,
          "phaseId": "sessions",
          "dependsOn": [
            "CLINK-103",
            "CLINK-105"
          ],
          "scenario": "An emulator restart during local sign-in loses the requested destination. Persist minimal, expiring continuation state.",
          "acceptanceCriteria": [
            "Valid continuation resumes once",
            "Expired continuation is discarded",
            "Unknown schema versions fail closed"
          ],
          "implementationNotes": [
            "Persist no session secrets in navigation storage."
          ],
          "verification": [
            "Restart before continuation",
            "Tamper with saved destination"
          ],
          "deliverables": [
            "Versioned continuation store and restart checks"
          ],
          "rollout": "Delete continuation metadata and return home.",
          "skills": [
            "Persistence",
            "Security"
          ],
          "fieldMix": [
            {
              "field": "Mobile",
              "percentage": 60
            },
            {
              "field": "Security",
              "percentage": 40
            }
          ],
          "patterns": []
        },
        {
          "id": "04218435-cda2-4bb2-a14d-88dd3c397f9b",
          "key": "CLINK-108",
          "title": "Keep the mobile Back stack free of completed sign-in screens",
          "type": "BUG",
          "priority": "MEDIUM",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 105,
          "phaseId": "recovery",
          "dependsOn": [
            "CLINK-106",
            "CLINK-107"
          ],
          "scenario": "After joining a team, Back returns to a completed sign-in screen that resubmits. Replace transient routes on success.",
          "acceptanceCriteria": [
            "Back reaches prior meaningful screen",
            "Completed forms cannot resubmit",
            "Failed sign-in remains editable"
          ],
          "implementationNotes": [
            "Specify stack behavior for cold and warm launches."
          ],
          "verification": [
            "Complete cold-launch sign-in",
            "Fail then retry warm launch"
          ],
          "deliverables": [
            "Stack transition and navigation tests"
          ],
          "rollout": "Reset to home after sign-in if replacement fails.",
          "skills": [
            "Navigation",
            "State transitions"
          ],
          "fieldMix": [
            {
              "field": "Mobile",
              "percentage": 100
            }
          ],
          "patterns": []
        },
        {
          "id": "cadaefab-0a72-4a87-94bf-49411f775609",
          "key": "CLINK-109",
          "title": "Expose revoked mobile sessions before showing cached team content",
          "type": "BUG",
          "priority": "HIGH",
          "difficulty": "ADVANCED",
          "estimateMinutes": 180,
          "phaseId": "recovery",
          "dependsOn": [
            "CLINK-104",
            "CLINK-108"
          ],
          "scenario": "A resumed app flashes old team details before learning the session was revoked. Gate sensitive cached rendering.",
          "acceptanceCriteria": [
            "Resume enters verification state",
            "Revocation clears scoped cache",
            "Offline uncertainty has an explicit locked view"
          ],
          "implementationNotes": [
            "Public shell may render without private records."
          ],
          "verification": [
            "Resume valid session",
            "Revoke during background pause"
          ],
          "deliverables": [
            "Resume gate and revocation checks"
          ],
          "rollout": "Clear private cache on every resume until fixed.",
          "skills": [
            "Authorization",
            "Lifecycle"
          ],
          "fieldMix": [
            {
              "field": "Security",
              "percentage": 70
            },
            {
              "field": "Mobile",
              "percentage": 30
            }
          ],
          "patterns": []
        },
        {
          "id": "35869e79-68b9-444b-be15-3300a46da8e6",
          "key": "CLINK-110",
          "title": "Record mobile deep-link failure categories without invitation tokens",
          "type": "CHORE",
          "priority": "LOW",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 90,
          "phaseId": "recovery",
          "dependsOn": [
            "CLINK-109"
          ],
          "scenario": "Support logs raw invitation URLs while diagnosing failures. Emit categories and route types only.",
          "acceptanceCriteria": [
            "Logs exclude IDs and tokens",
            "Known failures have stable categories",
            "Unexpected errors use bounded safe summaries"
          ],
          "implementationNotes": [
            "Verify with synthetic secret-like links."
          ],
          "verification": [
            "Log valid route outcome",
            "Reject token-bearing malformed URL"
          ],
          "deliverables": [
            "Diagnostic event contract"
          ],
          "rollout": "Disable link diagnostics on redaction failure.",
          "skills": [
            "Logging",
            "Data minimization"
          ],
          "fieldMix": [
            {
              "field": "Privacy engineering",
              "percentage": 60
            },
            {
              "field": "Mobile",
              "percentage": 40
            }
          ],
          "patterns": []
        }
      ]
    }
  ]
}
