{
  "Schema": "syrf.project-statistics.phase2c-staging-proof/v1",
  "_comment": "Template for one FEAT-024 Phase 2C staging-proof run. Copy to phase2c-staging_<YYYY-MM-DD>_<short-run-label>.json and replace every TODO. Store raw response bodies, not summaries of them: a verdict without its watermarks and scopeStates cannot be re-interpreted later. Never record reviewer identities, study titles or other project content - ids, counts and response envelopes are enough. Runbook: ../../phase2c-staging-proof-runbook.md",

  "RunLabel": "TODO",
  "DateUtc": "TODO 2026-00-00",
  "Environment": "staging",
  "Operator": "TODO who ran it",
  "ProgrammeOwnerApproval": {
    "Granted": false,
    "By": "TODO",
    "AtUtc": "TODO",
    "Reference": "TODO link to the approval"
  },

  "Gates": {
    "Syrf3196Merged": false,
    "Syrf3232Merged": false,
    "Syrf3185Closed": false,
    "PilotProjectIdRecorded": false,
    "Notes": "TODO anything that changed since the runbook was written"
  },

  "Deployment": {
    "ClusterGitOpsPullRequest": "TODO camaradesuk/cluster-gitops#807",
    "ClusterGitOpsMergeSha": "TODO",
    "ApiImageDigest": "TODO from kubectl, not from chartTag",
    "ProjectManagementImageDigest": "TODO",
    "ObservedFlags": {
      "_comment": "Read from the running pods, not from the values file. Both hosts must agree.",
      "api": {
        "materializedProjectStatisticsWrites": "TODO",
        "materializedProjectStatisticsServing": "TODO",
        "materializedProjectStatisticsScreening": "TODO",
        "materializedProjectStatisticsMembershipScreening": "TODO",
        "materializedProjectStatisticsAnnotation": "TODO",
        "materializedProjectStatisticsMembershipAnnotation": "TODO",
        "materializedProjectStatisticsQuestionAnswers": "TODO",
        "materializedProjectStatisticsSearchPopulation": "TODO",
        "materializedProjectStatisticsDerivedSummaries": "TODO",
        "materializedProjectStatisticsPages": "TODO",
        "materializedProjectStatisticsSignalR": "TODO",
        "materializedProjectStatisticsExports": "TODO"
      },
      "projectManagement": {
        "_comment": "Same twelve keys; record them, do not assume they match the API."
      }
    },
    "ObservedAllowlist": {
      "api": "TODO value of SYRF__ProjectStatistics__ProjectAllowlist as rendered",
      "projectManagement": "TODO"
    },
    "EffectiveSerilogMinimumLevel": "TODO confirm Debug lines actually appear in kubectl logs"
  },

  "PilotProject": {
    "ProjectId": "TODO GUID",
    "SelectionRationale": "TODO real screening activity; small enough to backfill in minutes; not production-critical and not somebody's live work",
    "ConfirmedSafeToUseBy": "TODO",
    "StudyCount": null,
    "ReviewerCount": null,
    "ScreeningDecisionCount": null,
    "AgreementThresholdStable": null
  },

  "LegacyBaseline": {
    "_comment": "Step 2. The authoritative screening statistics captured through the ordinary surface BEFORE anything was enabled. This is what 'prove legacy response equivalence' is measured against.",
    "CapturedAtUtc": "TODO",
    "Response": {}
  },

  "Backfill": {
    "_comment": "Step 3. POST api/admin/project-statistics/{projectId}/backfill. Synchronous despite the 202.",
    "RequestedAtUtc": "TODO",
    "DurationSeconds": null,
    "ClientTimeoutSeconds": null,
    "HttpStatus": null,
    "ResponseBody": {},
    "Interpretation": "TODO 202 with scopesStale 0 and checkpointStatus Succeeded, or the failureReason and what was done about it"
  },

  "RebuildsPerformed": [
    {
      "_comment": "Step 3/5. POST .../rebuild (forced). Record each one, including any run to clear a ControlVersionMismatch.",
      "Reason": "TODO",
      "RequestedAtUtc": "TODO",
      "HttpStatus": null,
      "ResponseBody": {}
    }
  ],

  "ParityAudits": [
    {
      "_comment": "Step 4 and step 5. Record EVERY attempt, including inconclusive ones. Store the whole report: inParity, isInconclusive, sourceAdvancedDuringAudit, materializedAvailable, readSource, fallbackReason, capacityFailure, checkpoint, watermarks, scopeStates, metrics, configurationMatchesCurrentSettings.",
      "Label": "TODO e.g. post-backfill",
      "RequestedAtUtc": "TODO",
      "HttpStatus": null,
      "ResponseBody": {},
      "Verdict": "TODO in parity / out of parity / inconclusive / unaudited",
      "Interpretation": "TODO. Remember: a bundle that refuses to serve is not in parity, it is unaudited - read fallbackReason. configurationMatchesCurrentSettings null never means it passed."
    }
  ],

  "ScreeningDecisionsExercised": [
    {
      "_comment": "Step 5. A real decision through the ordinary UI, not a database write.",
      "AtUtc": "TODO",
      "StudyId": "TODO",
      "ReviewerId": "TODO",
      "Decision": "TODO include | exclude",
      "CommittedProjectionRevisionBefore": null,
      "CommittedProjectionRevisionAfter": null,
      "ExpectedCounterMovement": "TODO which metric keys should move and by how much",
      "ObservedCounterMovement": "TODO",
      "ParityAuditLabelAfter": "TODO cross-reference into ParityAudits"
    }
  ],

  "PathsExercised": {
    "_comment": "The Phase 2 staging proof names these explicitly: force Stale/Rebuilding/Incompatible and the kill-switch paths.",
    "KillSwitchOffAndBack": { "Exercised": false, "Observation": "TODO reads fell back; parity returned 409 writes-disabled; projection intact and in parity after re-enable + rebuild" },
    "StaleObserved": { "Exercised": false, "Observation": "TODO" },
    "RebuildingObserved": { "Exercised": false, "Observation": "TODO" },
    "IncompatibleObserved": { "Exercised": false, "Observation": "TODO" },
    "ForcedRebuild": { "Exercised": false, "Observation": "TODO new publicationGeneration, still in parity" }
  },

  "FallbackReasonsObserved": {
    "_comment": "Record the SET, not only the counts. A reason nobody expected is a finding whether or not parity held. Adapter reasons (ProjectScreeningStatisticsFallbackReason) and reader reasons (ProjectStatisticsFallbackReason) appear in one log line separated by '/'.",
    "Source": "TODO pod stdout substring query, and/or the parity report's fallbackReason field",
    "Observed": [],
    "UnexpectedReasons": [],
    "ParityDivergenceWarnings": {
      "_comment": "The Warning line 'FEAT-024 declined to substitute the materialized screening section'. Any occurrence is a finding.",
      "Count": 0,
      "Details": []
    }
  },

  "Soak": {
    "_comment": "Phase 6 acceptance: at least seven ACTUAL days, 10,000 reads, 1,000 relevant mutations, 100% exact counter parity, zero stale materialized serves, 100% injected fallback success, no unresolved rebuild failures, bounded delta-ledger and storage growth. There is no read or mutation counter in the code; see the runbook.",
    "StartedAtUtc": "TODO",
    "EndedAtUtc": "TODO",
    "ElapsedDays": null,
    "ReadCount": null,
    "ReadCountMethod": "TODO log line count, or a driver counting its own calls. State WHICH reads are being counted: with every consumer flag off, ordinary page reads never touch the projection.",
    "MutationCount": null,
    "MutationCountMethod": "TODO difference between two observations of watermarks.committedProjectionRevision",
    "ParitySamples": [],
    "StaleMaterializedServes": null,
    "UnresolvedRebuildFailures": null,
    "DeltaLedgerGrowth": "TODO",
    "StorageGrowth": "TODO"
  },

  "Rollback": {
    "_comment": "Step 8. Flags false on both hosts, allowlist back to the placeholder, through cluster-gitops only.",
    "Performed": false,
    "AtUtc": "TODO",
    "ClusterGitOpsRevertRef": "TODO",
    "VerifiedReadsAuthoritative": false,
    "StateLeftBehind": "pmProjectStatistics* documents, control row, delta ledger and the backfill-observed checkpoint remain and are never read. Re-enabling requires a fresh backfill or forced rebuild: the rows are intact but no longer current, and the reader correctly refuses them as Stale."
  },

  "Outcome": {
    "ProofPassed": null,
    "Findings": [],
    "FollowUpIssues": [],
    "Notes": "TODO"
  }
}
