Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions docs/release/alpha-evidence-contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# MuxTV 0.1.0-alpha evidence contract

Status: release-gate contract, not release evidence
Issue: #31

## Purpose

Issue #31 requires a reproducible `0.1.0-alpha` whose compatibility and quality claims are bounded by evidence collected on the exact release commit. This contract and `alpha-gates-v1.json` define the canonical gate inventory; `schemas/alpha-evidence-manifest.schema.json` defines the structural manifest format; the executable finalizer enforces cross-field provenance, gate ownership, claim eligibility and conservative redaction rules.

Authoring or validating this contract does not mark any release gate `PASSED`, enable R8, change the app version, create signing material, generate an SBOM, or claim device compatibility.

## Core rule

An alpha manifest is claim-eligible only when every gate marked `required=true` is `PASSED` on the exact manifest commit and every passed-gate/artifact provenance record belongs to that same commit.

`DEFERRED` is an explicit product decision, not success. A deferred item requires an issue number, rationale and scope effect. A required gate cannot be deferred while `claimEligible=true`.

## Canonical virtual-device policy

Repository-owned persistent Android TV AVD identities are exactly:

- `MuxTV_TV_OLD_API26`;
- `MuxTV_TV_CURRENT_API36`.

The release contract must not introduce `virtual.mainstream` or any third persistent AVD. `virtual.low_ram` is a constrained runtime/device configuration applied to one of the canonical devices where representable; it is not a separate AVD identity. API37 and hardware-specific behavior are ephemeral/physical release evidence under #31.

## Manifest identity

Every manifest records the schema version, repository, exact lowercase 40-character source commit, optional source ref, requested release version, UTC generation timestamp, claim-eligibility flag, canonical gate map, artifact provenance and known limitations.

Paths, workflow IDs/URLs and artifact names are references only. They are never interpreted as proof unless the owning gate is `PASSED` and its `evidenceCommit` equals the manifest commit.

## Gate states

- `PENDING` — not executed or evidence not reviewed;
- `PASSED` — exact-head evidence exists and acceptance checks are satisfied;
- `FAILED` — executed and did not satisfy the gate;
- `BLOCKED` — cannot execute because of an external dependency/environment;
- `DEFERRED` — explicitly removed from current scope with issue+rationale+scope effect.

Never use `PASSED` for static review when a gate requires execution.

## Gate groups

The executable inventory is `alpha-gates-v1.json`. Its groups are:

- scope/truth: dependency scope, version identity, clean source tree;
- release build: release assembly, R8/resource shrinking, Baseline Profile packaging, signing, SBOM and dependency report;
- virtual correctness: API26 old edge, API36 current, and constrained low-RAM mode on a canonical device;
- data/security: Room schema/upgrade, Keystore persistence/reset and redaction;
- recovery: TV-operable recovery, previous-good preservation and user recovery docs;
- player/diagnostics: core TV journey, transport behavior and diagnostic export;
- physical qualification: current Android/Google TV, constrained TV and availability-conditional Fire TV;
- performance: Baseline Profile effect, startup/frame/memory evidence and measurement provenance.

Physical observations remain device-scoped. Emulator evidence cannot certify vendor codec, HDR/Dolby Vision, passthrough, weak-ARM, Fire OS, thermal or absolute performance behavior.

## Canonical gate ownership

Every gate from `alpha-gates-v1.json`, including optional/availability-conditional gates, must be present in the manifest so unavailable work cannot disappear silently. The finalizer rejects unknown gates and missing canonical gates. A manifest cannot weaken a gate whose catalog entry has `requiredByDefault=true` by setting `required=false`.

Optional gates may remain non-required with an explicit non-passed status while `claimEligible=true`; their state and limitations must still be represented truthfully.

## Exact-commit provenance

For every `PASSED` gate:

- `evidenceCommit` is required by schema;
- the finalizer requires `evidenceCommit == manifest.commit`.

For every artifact:

- `sourceCommit` must equal `manifest.commit`.

A claim-eligible manifest must include at least one APK or AAB with a non-null SHA-256 digest and exact source-commit provenance.

## Claim eligibility

`claimEligible=true` is valid only when:

1. schema validation succeeds;
2. manifest gate names exactly match the canonical catalog;
3. no required-by-default gate is weakened;
4. every `required=true` gate is `PASSED`;
5. every passed gate belongs to the manifest commit;
6. every artifact belongs to the manifest commit;
7. at least one APK or AAB has SHA-256 provenance;
8. `security.redaction` is passed as part of the required gate set;
9. known limitations remain explicit rather than hidden through omitted gates.

The finalizer validates eligibility; it does not generate evidence or turn pending gates into passed ones.

## Redaction boundary

Evidence references and artifact names are public-safe metadata only. The finalizer conservatively rejects values shaped like:

- Authorization/Cookie headers;
- credential/signature query parameters such as token/password/signature values;
- URI user-info credentials;
- private-machine absolute filesystem paths.

Diagnostics for rejected metadata must not echo the rejected value. The manifest must never contain playlist/provider credentials, signing secrets, temporary signed download URLs, private keystore paths, Authorization/Cookie material or raw secret-bearing locators.

## Release artifact and performance policy

Release hardening must operate on the minified release-shaped artifact. R8 keep-rule changes need analyzer/runtime evidence rather than broad defensive keep rules. Baseline Profile packaging is a separate gate from measured effect; emulator-generated profiles or correctness runs are not public performance claims. Physical-device before/after evidence remains required for release-facing startup/frame performance claims.

## Qualification boundary

The canonical API26/API36 matrix proves bounded platform correctness. Android 17/API37 Local Network Protection, route-dependent IPv6, vendor codecs/HDR/passthrough and Fire TV behavior require appropriate ephemeral or physical evidence. Lack of such evidence must remain visible in the manifest/known limitations rather than being inferred from emulator success.
42 changes: 42 additions & 0 deletions docs/release/alpha-gates-v1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"schemaVersion": 1,
"releaseTrack": "0.1.0-alpha",
"gates": [
{ "id": "scope.alpha_dependencies", "requiredByDefault": true, "availabilityConditional": false },
{ "id": "truth.version", "requiredByDefault": true, "availabilityConditional": false },
{ "id": "truth.clean_tree", "requiredByDefault": true, "availabilityConditional": false },

{ "id": "build.release_assemble", "requiredByDefault": true, "availabilityConditional": false },
{ "id": "build.r8_shrinking", "requiredByDefault": true, "availabilityConditional": false },
{ "id": "build.baseline_profile_packaged", "requiredByDefault": true, "availabilityConditional": false },
{ "id": "build.signing", "requiredByDefault": true, "availabilityConditional": false },
{ "id": "build.sbom", "requiredByDefault": true, "availabilityConditional": false },
{ "id": "build.dependency_report", "requiredByDefault": true, "availabilityConditional": false },

{ "id": "virtual.old_edge", "requiredByDefault": true, "availabilityConditional": false },
{ "id": "virtual.current", "requiredByDefault": true, "availabilityConditional": false },
{ "id": "virtual.low_ram", "requiredByDefault": true, "availabilityConditional": false },

{ "id": "data.room_current_schema", "requiredByDefault": true, "availabilityConditional": false },
{ "id": "data.upgrade_previous_supported", "requiredByDefault": true, "availabilityConditional": false },
{ "id": "security.keystore_persistence", "requiredByDefault": true, "availabilityConditional": false },
{ "id": "security.keystore_reset", "requiredByDefault": true, "availabilityConditional": false },
{ "id": "security.redaction", "requiredByDefault": true, "availabilityConditional": false },

{ "id": "recovery.tv_path", "requiredByDefault": true, "availabilityConditional": false },
{ "id": "recovery.failure_previous_good", "requiredByDefault": true, "availabilityConditional": false },
{ "id": "recovery.user_docs", "requiredByDefault": true, "availabilityConditional": false },

{ "id": "player.core_journey", "requiredByDefault": true, "availabilityConditional": false },
{ "id": "player.transport", "requiredByDefault": true, "availabilityConditional": false },
{ "id": "diagnostics.export", "requiredByDefault": true, "availabilityConditional": false },

{ "id": "physical.current_android_tv", "requiredByDefault": true, "availabilityConditional": false },
{ "id": "physical.constrained_tv", "requiredByDefault": true, "availabilityConditional": false },
{ "id": "physical.fire_tv", "requiredByDefault": false, "availabilityConditional": true },

{ "id": "perf.baseline_profile_effect", "requiredByDefault": true, "availabilityConditional": false },
{ "id": "perf.startup_frames_memory", "requiredByDefault": true, "availabilityConditional": false },
{ "id": "perf.measurement_provenance", "requiredByDefault": true, "availabilityConditional": false }
]
}
169 changes: 169 additions & 0 deletions docs/release/schemas/alpha-evidence-manifest.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://muxtv.app/schemas/alpha-evidence-manifest.schema.json",
"title": "MuxTV alpha release evidence manifest",
"type": "object",
"additionalProperties": false,
"required": [
"schemaVersion",
"repository",
"commit",
"releaseVersion",
"generatedAtUtc",
"claimEligible",
"gates",
"artifacts",
"knownLimitations"
],
"properties": {
"schemaVersion": { "const": 1 },
"repository": { "const": "MuxTV/Muxtv" },
"commit": { "type": "string", "pattern": "^[0-9a-f]{40}$" },
"sourceRef": { "type": ["string", "null"], "maxLength": 200 },
"releaseVersion": {
"type": "string",
"pattern": "^0\\.1\\.0-alpha(?:[.+-][0-9A-Za-z.-]+)?$"
},
"generatedAtUtc": { "type": "string", "format": "date-time" },
"claimEligible": { "type": "boolean" },
"gates": {
"type": "object",
"minProperties": 1,
"additionalProperties": { "$ref": "#/$defs/gate" },
"propertyNames": {
"pattern": "^[a-z][a-z0-9_]*(?:\\.[a-z][a-z0-9_]*)+$"
}
},
"artifacts": {
"type": "array",
"items": { "$ref": "#/$defs/artifact" }
},
"knownLimitations": {
"type": "array",
"items": { "type": "string", "minLength": 1, "maxLength": 1000 }
}
},
"$defs": {
"status": {
"type": "string",
"enum": ["PENDING", "PASSED", "FAILED", "BLOCKED", "DEFERRED"]
},
"evidenceReference": {
"type": "object",
"additionalProperties": false,
"required": ["kind", "value"],
"properties": {
"kind": {
"type": "string",
"enum": [
"WORKFLOW_RUN_ID",
"WORKFLOW_JOB_ID",
"CI_ARTIFACT_NAME",
"REPOSITORY_PATH",
"DEVICE_REPORT",
"MEASUREMENT_REPORT",
"OTHER"
]
},
"value": {
"type": "string",
"minLength": 1,
"maxLength": 500,
"description": "Safe evidence reference only. Secret-bearing URLs and credentials are rejected by the executable finalizer."
}
}
},
"defer": {
"type": "object",
"additionalProperties": false,
"required": ["issueNumber", "rationale", "scopeEffect"],
"properties": {
"issueNumber": { "type": "integer", "minimum": 1 },
"rationale": { "type": "string", "minLength": 1, "maxLength": 2000 },
"scopeEffect": { "type": "string", "minLength": 1, "maxLength": 2000 }
}
},
"gate": {
"type": "object",
"additionalProperties": false,
"required": ["required", "status", "evidence"],
"properties": {
"required": { "type": "boolean" },
"status": { "$ref": "#/$defs/status" },
"evidenceCommit": {
"type": ["string", "null"],
"pattern": "^[0-9a-f]{40}$"
},
"evidence": {
"type": "array",
"items": { "$ref": "#/$defs/evidenceReference" }
},
"facts": {
"type": "object",
"additionalProperties": {
"type": ["string", "number", "integer", "boolean", "null"]
}
},
"defer": { "$ref": "#/$defs/defer" },
"note": { "type": ["string", "null"], "maxLength": 2000 }
},
"allOf": [
{
"if": {
"properties": { "status": { "const": "PASSED" } },
"required": ["status"]
},
"then": {
"required": ["evidenceCommit"],
"properties": {
"evidenceCommit": {
"type": "string",
"pattern": "^[0-9a-f]{40}$"
},
"evidence": { "minItems": 1 }
}
}
},
{
"if": {
"properties": { "status": { "const": "DEFERRED" } },
"required": ["status"]
},
"then": { "required": ["defer"] }
}
]
},
"artifact": {
"type": "object",
"additionalProperties": false,
"required": ["kind", "name", "sourceCommit"],
"properties": {
"kind": {
"type": "string",
"enum": [
"APK",
"AAB",
"SBOM",
"DEPENDENCY_REPORT",
"BASELINE_PROFILE_EVIDENCE",
"TEST_EVIDENCE",
"MEASUREMENT_EVIDENCE",
"OTHER"
]
},
"name": {
"type": "string",
"minLength": 1,
"maxLength": 300,
"description": "Safe repository-relative or CI artifact name only."
},
"sha256": {
"type": ["string", "null"],
"pattern": "^[0-9a-f]{64}$"
},
"byteCount": { "type": ["integer", "null"], "minimum": 0 },
"sourceCommit": { "type": "string", "pattern": "^[0-9a-f]{40}$" }
}
}
}
}
Loading