This commit is contained in:
24
docs/distribution-changelog.md
Normal file
24
docs/distribution-changelog.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Distribution Contract Changelog
|
||||
|
||||
# 1.0.3
|
||||
- 2026-04-04
|
||||
- Added CI distribution gate with reproducibility and manifest validation (`scripts/release-gate.py` + `.github/workflows/release-gate.yml`).
|
||||
- Added maintenance governance fields in `docs/distribution-contract.json` and operational release ownership/retention guidance in `docs/distribution.md`.
|
||||
|
||||
# 1.0.2
|
||||
- 2026-04-04
|
||||
- Added canonical consumer resolution entrypoint at `dist/index.json`.
|
||||
- Kept a single deprecated compatibility path `dist/{distribution_contract_version}/index.json` with documented sunset date (`2026-07-01`).
|
||||
- Documented immutable tuple-based resolution and checksum-first consumption examples for external projects.
|
||||
|
||||
# 1.0.1
|
||||
- 2026-04-04
|
||||
- Added canonical release orchestrator and versioned dist layout index manifest generation (`scripts/release-orchestrator.py` + `scripts/release-orchestrator.config.json`).
|
||||
- Extended contract metadata with generator location, path template, and artifact index path (`docs/distribution-contract.json`).
|
||||
|
||||
## 1.0.0
|
||||
- 2026-04-04
|
||||
- Introduced versioned distribution contract (`distribution-contract@1.0`) for deterministic, cross-project-safe consumption.
|
||||
- Defined canonical `dist/` namespace and immutable artifact naming pattern.
|
||||
- Added machine-readable compatibility and checksum policy in `docs/distribution-contract.json`.
|
||||
- Established compatibility governance and changelog requirement for contract assumption changes.
|
||||
201
docs/distribution-contract.json
Normal file
201
docs/distribution-contract.json
Normal file
@@ -0,0 +1,201 @@
|
||||
{
|
||||
"schema_version": "distribution-contract-v1",
|
||||
"contract_version": "1.0.0",
|
||||
"artifact": {
|
||||
"name": "codex-controller-loop",
|
||||
"entrypoints": [
|
||||
"codex-controller-loop"
|
||||
],
|
||||
"distribution_index": {
|
||||
"canonical": "dist/index.json",
|
||||
"legacy": [
|
||||
{
|
||||
"path": "dist/{contract_version}/index.json",
|
||||
"status": "deprecated",
|
||||
"deprecation_sunset": "2026-07-01T00:00:00Z",
|
||||
"migration_deadline": "2026-07-01T00:00:00Z",
|
||||
"notes": "Temporary compatibility path while downstream scripts migrate to dist/index.json."
|
||||
}
|
||||
],
|
||||
"resolution_precedence": [
|
||||
"dist/index.json",
|
||||
"dist/{contract_version}/index.json"
|
||||
]
|
||||
},
|
||||
"versioning": {
|
||||
"model": "semver",
|
||||
"release_channels": [
|
||||
"stable"
|
||||
],
|
||||
"pre_release": "supported",
|
||||
"pinning_guidance": "Pin by exact tuple: version, target, profile, git_rev, toolchain, dist_revision"
|
||||
},
|
||||
"naming": {
|
||||
"namespace": "dist",
|
||||
"canonical_pattern": "codex-controller-loop-v{version}-{target}-{profile}-{rust}-{gitsha}-{dist_revision}.{ext}",
|
||||
"immutable_fields": [
|
||||
"version",
|
||||
"target",
|
||||
"profile",
|
||||
"rust",
|
||||
"gitsha",
|
||||
"dist_revision"
|
||||
]
|
||||
},
|
||||
"release_artifacts": {
|
||||
"formats": [
|
||||
"tar.gz"
|
||||
],
|
||||
"required_manifests": [
|
||||
"checksums.json",
|
||||
"manifest.json",
|
||||
"provenance.json"
|
||||
],
|
||||
"index_path_template": "dist/index.json",
|
||||
"legacy_index_path_template": "dist/{contract_version}/index.json",
|
||||
"artifact_directory_template": "dist/{contract_version}/{version}/{target}/{profile}/{toolchain}/{gitsha}/{dist_revision}",
|
||||
"orchestrator": {
|
||||
"name": "scripts/release-orchestrator.py",
|
||||
"config": "scripts/release-orchestrator.config.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"compatibility_matrix": [
|
||||
{
|
||||
"platform_family": "linux",
|
||||
"os": "Linux",
|
||||
"arch": [
|
||||
"x86_64",
|
||||
"aarch64"
|
||||
],
|
||||
"compatibility": "recommended",
|
||||
"runtime_expectations": [
|
||||
"posix-like terminal with ANSI support",
|
||||
"UTF-8 locale",
|
||||
"interactive TTY for full features"
|
||||
]
|
||||
},
|
||||
{
|
||||
"platform_family": "darwin",
|
||||
"os": "Darwin",
|
||||
"arch": [
|
||||
"x86_64",
|
||||
"aarch64"
|
||||
],
|
||||
"compatibility": "recommended",
|
||||
"runtime_expectations": [
|
||||
"POSIX terminal",
|
||||
"UTF-8 locale",
|
||||
"interactive TTY for full features"
|
||||
]
|
||||
},
|
||||
{
|
||||
"platform_family": "windows",
|
||||
"os": "Windows",
|
||||
"arch": [
|
||||
"x86_64"
|
||||
],
|
||||
"compatibility": "planned",
|
||||
"runtime_expectations": [
|
||||
"UTF-8 locale",
|
||||
"terminal support in Windows console"
|
||||
]
|
||||
}
|
||||
],
|
||||
"assumptions": {
|
||||
"rust": {
|
||||
"edition": "2021",
|
||||
"toolchain": "release-time explicit rustc version",
|
||||
"minimum_guaranteed": null,
|
||||
"minimum_for_release": "recorded per artifact in manifest"
|
||||
},
|
||||
"terminal": "ANSI-capable terminal and TTY are required for interactive TUI mode",
|
||||
"non_tty_mode": "operable only for restricted command-and-control paths"
|
||||
},
|
||||
"reproducibility_metadata": {
|
||||
"required": true,
|
||||
"fields": [
|
||||
"version",
|
||||
"target",
|
||||
"profile",
|
||||
"build_time_inputs",
|
||||
"git_rev",
|
||||
"toolchain",
|
||||
"checksums"
|
||||
]
|
||||
},
|
||||
"provenance_metadata": {
|
||||
"schema_version": "distribution-provenance-v1",
|
||||
"required_fields": [
|
||||
"artifact",
|
||||
"build_inputs",
|
||||
"build_artifact",
|
||||
"generated_at"
|
||||
]
|
||||
},
|
||||
"checksum_policy": {
|
||||
"algorithm": "sha256",
|
||||
"required": true,
|
||||
"file": "checksums.json",
|
||||
"consumer_check_required": true
|
||||
},
|
||||
"maintenance_governance": {
|
||||
"release_ownership": {
|
||||
"primary_owner": "Repository maintainers and release steward on duty",
|
||||
"ownership_handoff_required": [
|
||||
"Update docs/distribution.md, docs/distribution-changelog.md, and docs/distribution-contract.json in lockstep for any compatibility-relevant changes",
|
||||
"Attach a passing scripts/release-gate.py run log to the release PR or merge checklist",
|
||||
"Verify the release steward handoff note in the PR description before publish"
|
||||
],
|
||||
"handoff_minimum_gap": "at least one full release cycle"
|
||||
},
|
||||
"deprecation_governance": {
|
||||
"required_notice_days": 60,
|
||||
"retention_for_retirement": "one release cycle",
|
||||
"retirement_announcement_channels": [
|
||||
"docs/distribution.md",
|
||||
"docs/distribution-changelog.md"
|
||||
]
|
||||
},
|
||||
"retention_minimum_generations": 6
|
||||
},
|
||||
"compatibility_policy": {
|
||||
"contract_versioning": "distribution-contract-v1",
|
||||
"breaking_change_requires_bump": true,
|
||||
"deprecation_notice_cycles": 1,
|
||||
"assumption_changes_requires_changelog": true
|
||||
},
|
||||
"deprecation_policy": {
|
||||
"legacy_index_path": {
|
||||
"supported_until": "2026-07-01T00:00:00Z",
|
||||
"sunset_reason": "Canonical index migration to dist/index.json.",
|
||||
"required_action": "All consumers must resolve artifacts from the canonical index path and remove legacy hard-coding before sunset.",
|
||||
"notice_channels": [
|
||||
"docs/distribution-changelog.md",
|
||||
"docs/distribution.md"
|
||||
]
|
||||
},
|
||||
"migration_window": {
|
||||
"minimum_notice_days": 60,
|
||||
"default_alias_removal_after": "2026-07-01T00:00:00Z"
|
||||
}
|
||||
},
|
||||
"retention_policy": {
|
||||
"kept_release_generations": 6,
|
||||
"retention_rationale": "Keep at least six release generations to support rollback and reproducible debugging.",
|
||||
"migration_required_on_removal": true,
|
||||
"migration_minimum_notice_window": "one release cycle"
|
||||
},
|
||||
"migration_steps": {
|
||||
"breaking_change_notice": [
|
||||
"Add an entry to docs/distribution-changelog.md with impact summary and effective release",
|
||||
"Provide compatibility matrix and assumption deltas in docs/distribution.md",
|
||||
"Publish manifest/metadata updates alongside artifacts before deprecation cutoff"
|
||||
],
|
||||
"rollback_path": "Consumers can repin immutable artifact tuple in their integration config."
|
||||
},
|
||||
"changelog": {
|
||||
"file": "docs/distribution-changelog.md",
|
||||
"required_on_contract_changes": true
|
||||
}
|
||||
}
|
||||
145
docs/distribution.md
Normal file
145
docs/distribution.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# Distribution Contract for `codex-controller-loop`
|
||||
|
||||
## v1 Contract
|
||||
|
||||
This document defines the first stable, versioned distribution contract for the Rust controller binary. It is the canonical compatibility and consumption reference for external projects.
|
||||
|
||||
## 1) Contract version and release identity
|
||||
|
||||
- Contract version: `distribution-contract@1.0`
|
||||
- Release artifact identity is immutable for a given tuple:
|
||||
- `artifact_version` (semver)
|
||||
- `git_rev` (full SHA, immutable reference)
|
||||
- `toolchain` and `build_profile`
|
||||
- `target`
|
||||
- `dist_revision` (incrementing revision when rebuilds occur for the same release tuple)
|
||||
- Consumers must pin by immutable tuple, never by moving tags.
|
||||
|
||||
## 2) Canonical artifact entrypoint
|
||||
|
||||
- Primary entrypoint: `codex-controller-loop` CLI.
|
||||
- Canonical binary names:
|
||||
- `codex-controller-loop` (single binary)
|
||||
- Canonical distribution entrypoint index: `dist/index.json`.
|
||||
- Deprecated compatibility entrypoint (removed after 2026-07-01): `dist/{distribution_contract_version}/index.json`.
|
||||
|
||||
## 3) Canonical dist layout and naming
|
||||
|
||||
- `dist/` is the only published artifact namespace.
|
||||
- Directory template (contract version stable):
|
||||
- `dist/{distribution_contract_version}/{artifact_version}/{target}/{profile}/{toolchain}/{gitsha}/{dist_revision}/`
|
||||
- Example: `dist/1.0.0/0.1.0/x86_64-unknown-linux-gnu/release/1.84.0/ab12cd34/r1/`
|
||||
- Canonical artifact filename:
|
||||
- `codex-controller-loop-v{version}-{target}-{profile}-{rust}-{gitsha}-{dist_revision}.{ext}`
|
||||
- `version` = semver release (e.g. `1.4.2`)
|
||||
- `target` = Rust target triple
|
||||
- `profile` = `release` or `debug`
|
||||
- `rust` = rustc version string used in build
|
||||
- `gitsha` = short git commit hash of source revision
|
||||
- `dist_revision` = `r1`, `r2`, ... for immutable re-build iterations
|
||||
- `ext` = container format used by release pipeline (e.g. `tar.gz`)
|
||||
- Canonical generator entrypoint:
|
||||
- `scripts/release-orchestrator.py` (single orchestrator)
|
||||
- Controlled by `scripts/release-orchestrator.config.json`
|
||||
- Index manifest output: `dist/index.json`
|
||||
- Deterministic provenance snapshot in generated index:
|
||||
- Each index artifact row is keyed by `version + target + profile + toolchain + gitsha + dist_revision`
|
||||
- `artifact_file`, `manifest_file`, `checksums_file`, `artifact_sha256`, and `source_date_epoch` are emitted
|
||||
|
||||
## 8) Consumer integration examples
|
||||
|
||||
Use the canonical index first, then fail fast if no rows match the requested immutable tuple. Optional legacy fallback is accepted only during migration.
|
||||
|
||||
```bash
|
||||
VERSION=0.1.0
|
||||
TARGET=x86_64-unknown-linux-gnu
|
||||
PROFILE=release
|
||||
TOOLCHAIN=1.84.0
|
||||
GITSHA=ab12cd34
|
||||
DIST_REVISION=r1
|
||||
|
||||
INDEX=dist/index.json
|
||||
if [ ! -f "$INDEX" ]; then
|
||||
INDEX=dist/1.0.0/index.json
|
||||
echo "warning: using deprecated index path, remove by 2026-07-01"
|
||||
fi
|
||||
|
||||
ARTIFACTS=$(jq -r --arg version "$VERSION" --arg target "$TARGET" --arg profile "$PROFILE" \
|
||||
--arg toolchain "$TOOLCHAIN" --arg git "$GITSHA" --arg dist "$DIST_REVISION" \
|
||||
'.releases[] | select(.version==$version) | .targets[] | select(.target==$target) | .profiles[] | select(.profile==$profile) | .artifacts[] | select(.toolchain|startswith($toolchain)) | select(.git_rev|startswith($git)) | select(.dist_revision==$dist) | .artifact_file' "$INDEX")
|
||||
|
||||
COUNT=$(printf "%s\n" "$ARTIFACTS" | awk 'NF {count += 1} END {print count + 0}')
|
||||
if [ "$COUNT" -ne 1 ]; then
|
||||
echo "expected exactly one artifact for immutable tuple" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ARTIFACT_FILE=$(printf "%s" "$ARTIFACTS")
|
||||
echo "resolved artifact: $ARTIFACT_FILE"
|
||||
|
||||
PACKAGE_DIR="${ARTIFACT_FILE%/*}/package"
|
||||
CHECKSUMS="$PACKAGE_DIR/checksums.json"
|
||||
|
||||
python - <<PY
|
||||
import hashlib, json, pathlib
|
||||
artifact = pathlib.Path("${ARTIFACT_FILE}")
|
||||
checksums = json.loads(pathlib.Path("${CHECKSUMS}").read_text())
|
||||
actual = hashlib.sha256(artifact.read_bytes()).hexdigest()
|
||||
if actual != checksums["artifact_sha256"]:
|
||||
raise SystemExit("artifact checksum mismatch")
|
||||
print(f"artifact sha256: {actual}")
|
||||
PY
|
||||
```
|
||||
|
||||
## 4) Release compatibility matrix
|
||||
|
||||
| Platform | OS | Arch | Binary compatibility | Runtime assumptions | Notes |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| Linux | Linux | `x86_64` / `aarch64` | Recommended | UTF-8 locale and terminal (TTY) | Required for TUI rendering |
|
||||
| macOS | Darwin | `aarch64` / `x86_64` | Recommended | UTF-8 locale and terminal (TTY) | Build validation expected |
|
||||
| Windows | Windows | `x86_64` | Planned / not guaranteed | UTF-8 locale and terminal (TTY) | Future support candidate |
|
||||
|
||||
- Rust compatibility: Release pipelines are required to document exact `rust` toolchain versions per artifact.
|
||||
- Source compatibility is guaranteed only for the same `distribution_contract_version` and `artifact_version` tuple.
|
||||
|
||||
## 5) Checksums and integrity
|
||||
|
||||
- Every release artifact must include `checksums.json`, `manifest.json`, and `provenance.json`.
|
||||
- All checksums use SHA-256.
|
||||
- Consumers treat a release as valid only if:
|
||||
1. Artifact checksum matches manifest entry.
|
||||
2. Manifest has reproducibility metadata matching expected tuple (version, target, profile, toolchain, git rev).
|
||||
|
||||
## 6) Retention and migration
|
||||
|
||||
- The canonical contract file is immutable once published for a release version.
|
||||
- Backward compatibility matrix changes require a migration note in the contract changelog.
|
||||
- Deprecated/removed platform support is announced via the changelog and removed only after a deprecation cycle.
|
||||
- Contract index migration note: `dist/{distribution_contract_version}/index.json` is a temporary compatibility alias and is retired on **2026-07-01**.
|
||||
|
||||
## 7) Changelog and compatibility governance
|
||||
|
||||
- Compatibility assumption changes require a changelog entry in `docs/distribution-changelog.md`.
|
||||
- Contract fields are first-class API: any consumer-facing contract change must update:
|
||||
- `docs/distribution.md`
|
||||
- `docs/distribution-contract.json`
|
||||
- `docs/distribution-changelog.md`
|
||||
|
||||
## 8) Source-of-truth documents
|
||||
|
||||
Machine-readable contract file: `docs/distribution-contract.json`
|
||||
|
||||
The JSON contract is the machine-readable interface; this ADR should be treated as the human-readable interpretation.
|
||||
|
||||
## 9) Operational governance and release hygiene
|
||||
|
||||
- Release ownership handoff is explicit and bounded:
|
||||
- The active release steward must own the distribution contract updates for the PR.
|
||||
- Ownership is transferred only after `docs/distribution.md`, `docs/distribution-changelog.md`, and `docs/distribution-contract.json` are updated together.
|
||||
- Handoff requires at least one release cycle of overlap so downstream consumers have a recovery window.
|
||||
- Deprecation workflow is fixed:
|
||||
- Legacy index path support remains for the documented minimum notice period.
|
||||
- Any compatibility assumption change requires a changelog entry and a migration step before sunset.
|
||||
- Minimum retention window:
|
||||
- Keep at least six release generations in the contract retention policy.
|
||||
- Do not remove deprecated aliases before the contract's documented notice date and retention cleanup policy are both met.
|
||||
Reference in New Issue
Block a user