{
  "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": "89f008a8-4896-4778-85be-be486e5ad541",
      "key": "RDELETE",
      "title": "Delete a workspace member profile across owned stores",
      "summary": "Coordinate scoped profile removal, derived-store cleanup and truthful completion reporting.",
      "context": "A fictional collaboration product lets a member request removal of their personal profile. Search, notifications and cached displays retain copies after the primary row disappears. The exercise uses a documented product deletion policy and synthetic identities.",
      "stack": [
        "TypeScript",
        "PostgreSQL",
        "Queue adapter",
        "Search adapter"
      ],
      "prerequisites": [
        "Create local profile, search-index and notification fixtures with controlled provider failures.",
        "Define synthetic members, organizations and a current-session authorization fixture."
      ],
      "developerValue": "Practice distributed cleanup, identity scope, idempotency and accurate user-facing lifecycle states.",
      "companyValue": "Produce a reviewable deletion workflow and copy inventory for adaptation to an approved company policy.",
      "delivery": "Ten tickets across request, cleanup and reconciliation phases. No live accounts are deleted; the brief does not certify legal erasure or removal from undeclared third parties.",
      "phases": [
        {
          "id": "request",
          "title": "Accept a scoped removal request",
          "goal": "Define identity, authority and the product deletion contract."
        },
        {
          "id": "cleanup",
          "title": "Remove declared copies",
          "goal": "Coordinate idempotent cleanup and prevent data from returning."
        },
        {
          "id": "reconcile",
          "title": "Reconcile exceptions and completion",
          "goal": "Make partial progress and recovery inspectable."
        }
      ],
      "field": "Privacy engineering",
      "tickets": [
        {
          "id": "0b8e4c17-e8d0-472b-968c-fd9eb0c117c3",
          "key": "RDELETE-101",
          "title": "Separate profile removal from leaving one organization",
          "type": "TASK",
          "priority": "MEDIUM",
          "difficulty": "FOUNDATIONAL",
          "estimateMinutes": 75,
          "phaseId": "request",
          "dependsOn": [],
          "scenario": "A member belongs to two organizations. The current button says delete account but removes only the active membership.",
          "acceptanceCriteria": [
            "Define distinct commands for leaving one organization and removing the global personal profile.",
            "List owned stores affected by each command and any retained records under the fictional policy.",
            "Show the command scope and expected access change before submission."
          ],
          "implementationNotes": [
            "Do not infer global identity from an organization-local display name or email field."
          ],
          "verification": [
            "Trace a two-organization member through both commands and compare affected records.",
            "Verify a membership-only request leaves the other organization and global profile unchanged."
          ],
          "deliverables": [
            "Deletion scope contract and multi-organization fixtures"
          ],
          "rollout": "Introduce distinct command names before enabling cleanup adapters.",
          "skills": [
            "Domain boundaries",
            "Identity scope"
          ],
          "fieldMix": [
            {
              "field": "Privacy engineering",
              "percentage": 50
            },
            {
              "field": "System design",
              "percentage": 30
            },
            {
              "field": "Security",
              "percentage": 20
            }
          ],
          "patterns": []
        },
        {
          "id": "43fb0b51-6fd4-4537-a26c-603e8256a141",
          "key": "RDELETE-102",
          "title": "Verify the current member before accepting profile removal",
          "type": "STORY",
          "priority": "MEDIUM",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 150,
          "phaseId": "request",
          "dependsOn": [
            "RDELETE-101"
          ],
          "scenario": "A request body supplies a profile ID and the API trusts it even when it differs from the authenticated member.",
          "acceptanceCriteria": [
            "Resolve the subject from the current authorized session and declared command scope.",
            "Reject substituted profile IDs and stale sessions before creating a request.",
            "Return a safe request reference without exposing another profile’s existence."
          ],
          "implementationNotes": [
            "Reuse an explicit authentication provider fixture; this ticket does not invent a new identity-assurance claim."
          ],
          "verification": [
            "Submit as the matching synthetic member and verify the request subject.",
            "Substitute a second member’s ID and revoke the session; both cases must create no request."
          ],
          "deliverables": [
            "Authorized request endpoint and subject-substitution tests"
          ],
          "rollout": "Gate request creation before enabling background work; rollback disables new requests while preserving accepted ones.",
          "skills": [
            "Authorization",
            "Session validation"
          ],
          "fieldMix": [
            {
              "field": "Security",
              "percentage": 70
            },
            {
              "field": "Privacy engineering",
              "percentage": 30
            }
          ],
          "patterns": []
        },
        {
          "id": "855b626f-4d06-44f8-b19e-6cc945781c9c",
          "key": "RDELETE-103",
          "title": "Make repeated removal requests return the same active operation",
          "type": "BUG",
          "priority": "HIGH",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 135,
          "phaseId": "request",
          "dependsOn": [
            "RDELETE-102"
          ],
          "scenario": "A double-click starts two cleanup chains that race and send contradictory completion messages.",
          "acceptanceCriteria": [
            "Use a subject-scoped active-operation uniqueness rule and a stable request identity.",
            "Return the existing operation for a duplicate accepted request.",
            "Preserve previous terminal history when a genuinely new eligible request is allowed by the policy."
          ],
          "implementationNotes": [
            "Create the request and dispatch intent transactionally; a retry must not depend on process memory."
          ],
          "verification": [
            "Submit concurrent duplicates and verify one operation plus one dispatch intent.",
            "Lose the first response and retry, confirming the same safe operation reference."
          ],
          "deliverables": [
            "Idempotent request creation and concurrency regressions"
          ],
          "rollout": "Apply the uniqueness constraint before deploying the accepting endpoint.",
          "skills": [
            "Idempotency",
            "Transactions"
          ],
          "fieldMix": [
            {
              "field": "Database engineering",
              "percentage": 40
            },
            {
              "field": "Privacy engineering",
              "percentage": 40
            },
            {
              "field": "Backend",
              "percentage": 20
            }
          ],
          "patterns": []
        },
        {
          "id": "244362bc-1d2c-4460-99aa-5180b5d9c0e0",
          "key": "RDELETE-104",
          "title": "Stop new profile-derived writes after removal begins",
          "type": "BUG",
          "priority": "HIGH",
          "difficulty": "ADVANCED",
          "estimateMinutes": 210,
          "phaseId": "cleanup",
          "dependsOn": [
            "RDELETE-101",
            "RDELETE-103"
          ],
          "scenario": "The notification worker recreates a cached profile card while cleanup is deleting it.",
          "acceptanceCriteria": [
            "Record a lifecycle boundary that profile-derived writers check before producing new copies.",
            "Define how already queued work is cancelled or rejected for the removal generation.",
            "Preserve required nonpersonal operational records without copying the removed profile into them."
          ],
          "implementationNotes": [
            "Use a generation or tombstone contract with explicit retention; do not keep the entire deleted profile inside a tombstone."
          ],
          "verification": [
            "Queue a notification before removal and release it afterward; no new profile copy may appear.",
            "Race an edit with removal and verify one declared outcome with no profile resurrection."
          ],
          "deliverables": [
            "Write barrier and profile-resurrection race tests"
          ],
          "rollout": "Deploy the writer check before activating cleanup; stop new operations if a writer cannot honor it.",
          "skills": [
            "Lifecycle coordination",
            "Data minimization"
          ],
          "fieldMix": [
            {
              "field": "Privacy engineering",
              "percentage": 50
            },
            {
              "field": "Distributed systems",
              "percentage": 30
            },
            {
              "field": "Backend",
              "percentage": 20
            }
          ],
          "patterns": []
        },
        {
          "id": "7acc04de-a5aa-471d-b1cf-83a53477830e",
          "key": "RDELETE-105",
          "title": "Remove profile documents from search using the removal generation",
          "type": "STORY",
          "priority": "MEDIUM",
          "difficulty": "ADVANCED",
          "estimateMinutes": 180,
          "phaseId": "cleanup",
          "dependsOn": [
            "RDELETE-104"
          ],
          "scenario": "A delayed indexing event arrives after a search document was deleted and makes the profile searchable again.",
          "acceptanceCriteria": [
            "Bind indexing and removal to a comparable subject generation or explicit suppression rule.",
            "Make repeated search deletion safe and keep stale indexing events from restoring the profile.",
            "Keep cleanup tenant/subject-scoped so similarly named profiles remain searchable."
          ],
          "implementationNotes": [
            "Treat the search adapter as eventually consistent and define the observation needed before declaring that location complete."
          ],
          "verification": [
            "Delete a profile, replay its older indexing event and verify it stays absent after the adapter’s declared convergence condition.",
            "Search for a same-name profile in another organization and verify it remains unaffected."
          ],
          "deliverables": [
            "Search cleanup adapter and stale-event regressions"
          ],
          "rollout": "Canary on synthetic subjects; retain unresolved search state if the adapter cannot establish the expected convergence.",
          "skills": [
            "Search indexing",
            "Event ordering"
          ],
          "fieldMix": [
            {
              "field": "Privacy engineering",
              "percentage": 40
            },
            {
              "field": "Data engineering",
              "percentage": 30
            },
            {
              "field": "Distributed systems",
              "percentage": 30
            }
          ],
          "patterns": []
        },
        {
          "id": "1a37295e-ff69-4306-b063-cf48728f494e",
          "key": "RDELETE-106",
          "title": "Track notification and cache cleanup separately from the primary profile row",
          "type": "BUG",
          "priority": "HIGH",
          "difficulty": "ADVANCED",
          "estimateMinutes": 210,
          "phaseId": "cleanup",
          "dependsOn": [
            "RDELETE-104",
            "RDELETE-105"
          ],
          "scenario": "The operation reports success after deleting the database row even though a notification snapshot and cache entry still show the member’s details.",
          "acceptanceCriteria": [
            "Track required locations with independent pending, confirmed and failed states.",
            "Differentiate already absent from unavailable or unauthorized provider responses.",
            "Advance overall completion only when the declared required locations satisfy the policy."
          ],
          "implementationNotes": [
            "The location registry is versioned for each operation so a later implementation change cannot silently rewrite its promised scope."
          ],
          "verification": [
            "Fail cache cleanup after database removal and verify a partial state with a retryable location.",
            "Return an authorization error from notifications and confirm it cannot be counted as confirmed absence."
          ],
          "deliverables": [
            "Location progress model and partial-cleanup fixtures"
          ],
          "rollout": "Enable adapters one at a time in the local exercise; unsupported locations remain explicitly unresolved.",
          "skills": [
            "Workflow modeling",
            "Partial failure"
          ],
          "fieldMix": [
            {
              "field": "Privacy engineering",
              "percentage": 40
            },
            {
              "field": "Distributed systems",
              "percentage": 30
            },
            {
              "field": "Backend",
              "percentage": 30
            }
          ],
          "patterns": []
        },
        {
          "id": "044cc393-ee27-47a8-880e-1b495aa505f1",
          "key": "RDELETE-107",
          "title": "Keep the removal status page useful after the profile is gone",
          "type": "STORY",
          "priority": "MEDIUM",
          "difficulty": "INTERMEDIATE",
          "estimateMinutes": 150,
          "phaseId": "reconcile",
          "dependsOn": [
            "RDELETE-102",
            "RDELETE-106"
          ],
          "scenario": "Deleting the profile also removes the data needed to render progress, leaving the requester with a broken page before cleanup is finished.",
          "acceptanceCriteria": [
            "Provide a narrowly scoped status mechanism independent of the removed profile display fields.",
            "Expose progress categories and unresolved exceptions without returning deleted content or internal storage identifiers.",
            "Define status access expiry and deny access to unrelated operations."
          ],
          "implementationNotes": [
            "Use the exercise authentication contract or an explicitly scoped expiring receipt; do not place a reusable access secret in generic analytics."
          ],
          "verification": [
            "Read progress before and after primary-profile removal.",
            "Attempt another subject’s operation and an expired receipt/session; verify denial without detail leakage."
          ],
          "deliverables": [
            "Minimal status response and post-removal access tests"
          ],
          "rollout": "Publish the status contract before enabling destructive cleanup; retain safe operation metadata for its declared lifetime.",
          "skills": [
            "Minimal disclosure",
            "Access control"
          ],
          "fieldMix": [
            {
              "field": "Privacy engineering",
              "percentage": 40
            },
            {
              "field": "Security",
              "percentage": 40
            },
            {
              "field": "Frontend",
              "percentage": 20
            }
          ],
          "patterns": []
        },
        {
          "id": "8abfc949-7b4c-4dd4-aee2-17f3b3013e79",
          "key": "RDELETE-108",
          "title": "Reapply profile suppression before a restored backup becomes readable",
          "type": "TASK",
          "priority": "MEDIUM",
          "difficulty": "EXPERT",
          "estimateMinutes": 330,
          "phaseId": "reconcile",
          "dependsOn": [
            "RDELETE-104",
            "RDELETE-106"
          ],
          "scenario": "A restore rehearsal brings back a profile that was removed after the backup was taken.",
          "acceptanceCriteria": [
            "Define a minimal removal ledger and a restore gate that reconciles post-backup removals before serving reads.",
            "State the retention and access boundaries of the ledger without storing full profile snapshots.",
            "Keep the restored environment unavailable if reconciliation is incomplete or the ledger cannot be trusted."
          ],
          "implementationNotes": [
            "Use local database snapshots and synthetic subjects; a backup exception must be visible in the policy rather than called immediate erasure."
          ],
          "verification": [
            "Restore a snapshot containing a later-removed profile and verify suppression before any simulated read endpoint is enabled.",
            "Make the ledger unavailable and verify the restore gate fails closed."
          ],
          "deliverables": [
            "Restore reconciliation protocol and backup resurrection rehearsal"
          ],
          "rollout": "Add the gate to the local restore runbook before accepting the deletion workflow as complete for its declared stores.",
          "skills": [
            "Backup recovery",
            "Deletion semantics"
          ],
          "fieldMix": [
            {
              "field": "Privacy engineering",
              "percentage": 40
            },
            {
              "field": "Site reliability",
              "percentage": 30
            },
            {
              "field": "Storage systems",
              "percentage": 30
            }
          ],
          "patterns": []
        },
        {
          "id": "53909b87-4bb2-4788-b966-dd267f78573f",
          "key": "RDELETE-109",
          "title": "Retry a removal operation after losing a provider acknowledgement",
          "type": "CHORE",
          "priority": "MEDIUM",
          "difficulty": "ADVANCED",
          "estimateMinutes": 210,
          "phaseId": "reconcile",
          "dependsOn": [
            "RDELETE-103",
            "RDELETE-105",
            "RDELETE-106"
          ],
          "scenario": "A provider removes a copy but the cleanup process crashes before recording the result. Retrying currently converts the missing object into a fatal error.",
          "acceptanceCriteria": [
            "Reconcile uncertain acknowledgement states using the provider’s declared lookup/removal contract.",
            "Make repeated cleanup converge without recreating data or duplicating completion notifications.",
            "Retain a distinct unresolved state when the provider cannot confirm the outcome."
          ],
          "implementationNotes": [
            "Use deterministic crash points around dispatch, provider completion and local persistence."
          ],
          "verification": [
            "Crash after external removal and before local update, then retry and verify truthful convergence.",
            "Inject repeated provider timeout and verify bounded retries plus visible unresolved status."
          ],
          "deliverables": [
            "Acknowledgement-loss regression and retry procedure"
          ],
          "rollout": "Retry only through the recorded operation identity; unresolved high-impact states require authorized review in the exercise.",
          "skills": [
            "Distributed recovery",
            "Idempotency"
          ],
          "fieldMix": [
            {
              "field": "Distributed systems",
              "percentage": 50
            },
            {
              "field": "Privacy engineering",
              "percentage": 30
            },
            {
              "field": "Site reliability",
              "percentage": 20
            }
          ],
          "patterns": []
        },
        {
          "id": "4ca2f27a-e7d1-47ad-9927-9b1138f9a5aa",
          "key": "RDELETE-110",
          "title": "Produce a removal report that names remaining exceptions",
          "type": "TASK",
          "priority": "MEDIUM",
          "difficulty": "EXPERT",
          "estimateMinutes": 270,
          "phaseId": "reconcile",
          "dependsOn": [
            "RDELETE-107",
            "RDELETE-108",
            "RDELETE-109"
          ],
          "scenario": "The proposed confirmation says all your data is deleted even when a declared backup copy or unavailable provider remains.",
          "acceptanceCriteria": [
            "Generate a bounded report of completed locations, retained policy exceptions and unresolved outcomes.",
            "Bind the report to the operation and policy versions and distinguish requested time from observed completion time.",
            "Avoid claiming global erasure or legal compliance beyond the declared local scope."
          ],
          "implementationNotes": [
            "The report is workflow status, not noCV Outcome Evidence or Ownership Evidence; practice completion grants neither."
          ],
          "verification": [
            "Generate reports for complete, partial and backup-exception fixtures and compare their language with actual store state.",
            "Attempt report access as another subject and verify no operation details leak."
          ],
          "deliverables": [
            "Truthful completion report and scope/authorization tests"
          ],
          "rollout": "Use the report only after reconciliation; corrections append a new status record rather than rewriting earlier confirmations.",
          "skills": [
            "Privacy communication",
            "Auditability"
          ],
          "fieldMix": [
            {
              "field": "Privacy engineering",
              "percentage": 80
            },
            {
              "field": "Site reliability",
              "percentage": 20
            }
          ],
          "patterns": []
        }
      ]
    }
  ]
}
