{
  "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": "6a2ae98c-4938-46b5-bcd2-64cd6cbad7c4",
      "key": "CROUTE",
      "title": "An offline field-service route notebook",
      "field": "Mobile",
      "summary": "Keep synthetic visit notes reliable through offline use and process restarts.",
      "context": "Fictional maintenance coordinator Elm dispatches non-safety-critical inspection visits. Implement a mobile application in an emulator with local API stubs; physical devices and credentials are unnecessary.",
      "stack": [
        "React Native",
        "TypeScript",
        "SQLite",
        "Emulator"
      ],
      "prerequisites": [
        "Create synthetic visits and a local sync adapter.",
        "Provide controllable connectivity and process-restart simulation."
      ],
      "developerValue": "Practice durable mobile state and explicit sync conflicts.",
      "companyValue": "Inspect how an engineer protects field work during disconnection.",
      "delivery": "Use synthetic notes and emulator runs; no location tracking or real dispatch.",
      "phases": [
        {
          "id": "visits",
          "title": "Make visits readable",
          "goal": "Define local visit state."
        },
        {
          "id": "offline",
          "title": "Protect offline work",
          "goal": "Persist notes and resolve conflicts."
        },
        {
          "id": "recovery",
          "title": "Recover interruptions",
          "goal": "Bound retries and explain sync outcomes."
        }
      ],
      "tickets": [
        {
          "id": "362ea8e1-b6bd-406e-8c94-6d653f86f996",
          "key": "CROUTE-101",
          "title": "Label route-note timestamps with their actual zone",
          "type": "BUG",
          "priority": "MEDIUM",
          "difficulty": "FOUNDATIONAL",
          "estimateMinutes": 60,
          "phaseId": "visits",
          "dependsOn": [],
          "scenario": "Visit notes appear an hour early after an emulator zone change. Distinguish stored instants from displayed local times.",
          "acceptanceCriteria": [
            "Instants remain UTC",
            "Display identifies zone",
            "Invalid timestamps show unavailable"
          ],
          "implementationNotes": [
            "Inject time and zone in tests."
          ],
          "verification": [
            "Change emulator zone",
            "Render malformed timestamp"
          ],
          "deliverables": [
            "Timestamp formatter and cases"
          ],
          "rollout": "Restore UTC display if local conversion fails.",
          "skills": [
            "Time",
            "Mobile UI"
          ],
          "fieldMix": [
            {
              "field": "Mobile",
              "percentage": 100
            }
          ],
          "patterns": []
        },
        {
          "id": "6ea7dc96-6ad7-4249-9e04-1c22f67242e8",
          "key": "CROUTE-102",
          "title": "Show downloaded visits when route refresh fails",
          "type": "STORY",
          "priority": "HIGH",
          "difficulty": "FOUNDATIONAL",
          "estimateMinutes": 75,
          "phaseId": "visits",
          "dependsOn": [],
          "scenario": "An offline refresh replaces the route with an empty screen. Retain downloaded visits and identify staleness.",
          "acceptanceCriteria": [
            "Cached visits remain visible",
            "Last sync time appears",
            "Retry does not delete cache"
          ],
          "implementationNotes": [
            "Distinguish empty success from network failure."
          ],
          "verification": [
            "Load cached route offline",
            "Return successful empty route"
          ],
          "deliverables": [
            "Route loading states"
          ],
          "rollout": "Disable refresh clearing if cache disappears.",
          "skills": [
            "Offline UX",
            "Caching"
          ],
          "fieldMix": [
            {
              "field": "Mobile",
              "percentage": 100
            }
          ],
          "patterns": []
        },
        {
          "id": "fe684644-f94a-4467-9103-b7e1221604af",
          "key": "CROUTE-103",
          "title": "Persist field-note edits before leaving a visit",
          "type": "BUG",
          "priority": "HIGH",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 120,
          "phaseId": "visits",
          "dependsOn": [
            "CROUTE-101",
            "CROUTE-102"
          ],
          "scenario": "Back navigation occasionally loses the last sentence. Commit local edits before acknowledging navigation.",
          "acceptanceCriteria": [
            "Confirmed save survives restart",
            "Failed writes retain editor",
            "Navigation explains unresolved persistence"
          ],
          "implementationNotes": [
            "Use a local transaction, not delayed component state."
          ],
          "verification": [
            "Save then restart",
            "Inject storage failure on Back"
          ],
          "deliverables": [
            "Durable draft write and restart cases"
          ],
          "rollout": "Prevent exit with unsaved changes until repaired.",
          "skills": [
            "SQLite",
            "Durability"
          ],
          "fieldMix": [
            {
              "field": "Mobile",
              "percentage": 60
            },
            {
              "field": "Database engineering",
              "percentage": 40
            }
          ],
          "patterns": []
        },
        {
          "id": "bf08e31f-69e3-4c34-8968-520e3665bbc9",
          "key": "CROUTE-104",
          "title": "Queue field-note synchronization by immutable operation ID",
          "type": "TASK",
          "priority": "HIGH",
          "difficulty": "ADVANCED",
          "estimateMinutes": 180,
          "phaseId": "offline",
          "dependsOn": [
            "CROUTE-103"
          ],
          "scenario": "The same offline note uploads twice after a timeout. Persist an operation ID alongside each queued change.",
          "acceptanceCriteria": [
            "Retries reuse ID",
            "Accepted changes leave queue atomically",
            "New edits create new operations"
          ],
          "implementationNotes": [
            "Server stub must deduplicate operations."
          ],
          "verification": [
            "Replay accepted upload",
            "Crash after acceptance before dequeue"
          ],
          "deliverables": [
            "Sync queue and replay tests"
          ],
          "rollout": "Pause uploading without deleting queued work.",
          "skills": [
            "Idempotency",
            "Transactions"
          ],
          "fieldMix": [
            {
              "field": "Mobile",
              "percentage": 40
            },
            {
              "field": "Database engineering",
              "percentage": 30
            },
            {
              "field": "Distributed systems",
              "percentage": 30
            }
          ],
          "patterns": []
        },
        {
          "id": "57ccf3cb-881a-413d-9f51-90ef48281d37",
          "key": "CROUTE-105",
          "title": "Preserve conflicting offline visit notes for explicit resolution",
          "type": "STORY",
          "priority": "HIGH",
          "difficulty": "ADVANCED",
          "estimateMinutes": 180,
          "phaseId": "offline",
          "dependsOn": [
            "CROUTE-104"
          ],
          "scenario": "Two emulators edit one visit offline and last-write-wins removes a paragraph. Surface both revisions.",
          "acceptanceCriteria": [
            "Conflict preserves both texts",
            "No silent merge occurs",
            "Resolution creates a new revision"
          ],
          "implementationNotes": [
            "Synthetic notes only; omit content from logs."
          ],
          "verification": [
            "Resolve two independent drafts",
            "Reject a stale resolution revision"
          ],
          "deliverables": [
            "Conflict screen and revision cases"
          ],
          "rollout": "Keep conflicts read-only while resolver is repaired.",
          "skills": [
            "Conflict resolution",
            "Revision control"
          ],
          "fieldMix": [
            {
              "field": "Mobile",
              "percentage": 50
            },
            {
              "field": "Distributed systems",
              "percentage": 50
            }
          ],
          "patterns": []
        },
        {
          "id": "759e2468-713a-424e-af11-728ed87f1f65",
          "key": "CROUTE-106",
          "title": "Resume route-note upload after application termination",
          "type": "BUG",
          "priority": "HIGH",
          "difficulty": "EXPERT",
          "estimateMinutes": 240,
          "phaseId": "offline",
          "dependsOn": [
            "CROUTE-104"
          ],
          "scenario": "Killing the process mid-sync leaves operations marked Sending forever. Add a recoverable lease state.",
          "acceptanceCriteria": [
            "Expired sends return to retryable",
            "Active leases avoid duplicate work",
            "Attempt count is bounded"
          ],
          "implementationNotes": [
            "Use an injected monotonic lease clock."
          ],
          "verification": [
            "Restart after expired lease",
            "Restart before lease expiry"
          ],
          "deliverables": [
            "Lease recovery and crash schedule"
          ],
          "rollout": "Pause runner and retain durable queue.",
          "skills": [
            "Recovery",
            "Leases"
          ],
          "fieldMix": [
            {
              "field": "Mobile",
              "percentage": 50
            },
            {
              "field": "Distributed systems",
              "percentage": 30
            },
            {
              "field": "Site reliability",
              "percentage": 20
            }
          ],
          "patterns": []
        },
        {
          "id": "f553e98e-0a04-4cc5-b9be-a7ccff4cfa20",
          "key": "CROUTE-107",
          "title": "Remove downloaded visit attachments without deleting notes",
          "type": "TASK",
          "priority": "MEDIUM",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 120,
          "phaseId": "offline",
          "dependsOn": [
            "CROUTE-102",
            "CROUTE-103"
          ],
          "scenario": "Attachment cache consumes emulator storage. Add per-visit cleanup that preserves authored drafts.",
          "acceptanceCriteria": [
            "Cleanup targets downloaded blobs",
            "Notes and queue remain intact",
            "Failed deletion reports reclaim uncertainty"
          ],
          "implementationNotes": [
            "Separate cache paths from draft storage."
          ],
          "verification": [
            "Clean one visit",
            "Fail blob removal and inspect notes"
          ],
          "deliverables": [
            "Cache cleanup and isolation cases"
          ],
          "rollout": "Disable cleanup if protected paths overlap.",
          "skills": [
            "Storage",
            "Resource management"
          ],
          "fieldMix": [
            {
              "field": "Storage systems",
              "percentage": 60
            },
            {
              "field": "Mobile",
              "percentage": 40
            }
          ],
          "patterns": []
        },
        {
          "id": "3df04cfc-c032-498e-bccd-8e1145439ac2",
          "key": "CROUTE-108",
          "title": "Bound offline route-note retry backoff",
          "type": "CHORE",
          "priority": "MEDIUM",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 105,
          "phaseId": "recovery",
          "dependsOn": [
            "CROUTE-106"
          ],
          "scenario": "A disconnected emulator retries uploads continuously and drains resources. Add capped backoff with manual retry semantics.",
          "acceptanceCriteria": [
            "Delays grow to cap",
            "Connectivity recovery can wake once",
            "Manual retry cannot create parallel senders"
          ],
          "implementationNotes": [
            "Inject timers; avoid wall-clock sleeps in tests."
          ],
          "verification": [
            "Observe scheduled delays",
            "Toggle connectivity repeatedly"
          ],
          "deliverables": [
            "Retry scheduler and fake-clock tests"
          ],
          "rollout": "Stop automatic retries and expose manual sync.",
          "skills": [
            "Scheduling",
            "Backoff"
          ],
          "fieldMix": [
            {
              "field": "Mobile",
              "percentage": 60
            },
            {
              "field": "Site reliability",
              "percentage": 40
            }
          ],
          "patterns": []
        },
        {
          "id": "d690c6fd-e6f2-471e-8925-3ea1ca90aee2",
          "key": "CROUTE-109",
          "title": "Explain partial route synchronization per visit",
          "type": "STORY",
          "priority": "MEDIUM",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 120,
          "phaseId": "recovery",
          "dependsOn": [
            "CROUTE-105",
            "CROUTE-108"
          ],
          "scenario": "A global Sync complete banner hides a failed visit. Show per-visit pending, conflicted, and acknowledged states.",
          "acceptanceCriteria": [
            "Global summary counts unresolved visits",
            "Each failure has action",
            "Success follows acknowledgement"
          ],
          "implementationNotes": [
            "Do not infer sync from an empty in-memory queue."
          ],
          "verification": [
            "Sync mixed outcomes",
            "Restart with persisted pending work"
          ],
          "deliverables": [
            "Sync summary and recovery checks"
          ],
          "rollout": "Hide global success if summaries disagree.",
          "skills": [
            "State projection",
            "UX"
          ],
          "fieldMix": [
            {
              "field": "Mobile",
              "percentage": 80
            },
            {
              "field": "API design",
              "percentage": 20
            }
          ],
          "patterns": []
        },
        {
          "id": "b3243c62-4d30-496c-8e9c-2c22084aad36",
          "key": "CROUTE-110",
          "title": "Export a synthetic field-note recovery bundle deliberately",
          "type": "CHORE",
          "priority": "LOW",
          "difficulty": "ADVANCED",
          "estimateMinutes": 150,
          "phaseId": "recovery",
          "dependsOn": [
            "CROUTE-106",
            "CROUTE-109"
          ],
          "scenario": "Debugging a stuck route requires durable state without sharing account tokens. Provide a previewed local recovery bundle.",
          "acceptanceCriteria": [
            "Includes operation metadata",
            "Excludes tokens and attachment bytes",
            "User explicitly includes note text"
          ],
          "implementationNotes": [
            "Default export omits note bodies."
          ],
          "verification": [
            "Export default bundle",
            "Verify secret-like fixture text is absent"
          ],
          "deliverables": [
            "Bundle schema and redaction cases"
          ],
          "rollout": "Remove export if exclusions fail.",
          "skills": [
            "Diagnostics",
            "Privacy"
          ],
          "fieldMix": [
            {
              "field": "Privacy engineering",
              "percentage": 60
            },
            {
              "field": "Mobile",
              "percentage": 40
            }
          ],
          "patterns": []
        }
      ]
    }
  ]
}
