diff --git a/docs/javascripts/production-lifecycle.js b/docs/javascripts/production-lifecycle.js index 7941165..91a9ba5 100644 --- a/docs/javascripts/production-lifecycle.js +++ b/docs/javascripts/production-lifecycle.js @@ -246,27 +246,23 @@ return true; } - function deriveTargetV2(target, projection, now) { + function deriveTargetV2(target, now) { const spec = V2_TARGETS[target.id]; const admission = target.latest_admission; if (!spec || !isObject(admission)) return null; const identity = admission.release_identity; const release = admission.release; const issuedAt = parseTimestamp(admission.issued_at); - const expiresAt = parseTimestamp(admission.expires_at); if ( admission.target !== target.id || admission.claim_scope !== spec.claimScope || admission.verdict !== "accepted" || - expiresAt === null || + admission.expires_at !== null || admission.revoked_at != null || typeof admission.evidence_class !== "string" || admission.evidence_class.length === 0 || issuedAt === null || issuedAt > now || - expiresAt <= now || - expiresAt <= issuedAt || - expiresAt - issuedAt > projection.maximum_admission_days * 86400000 || !isObject(identity) || identity.schema_version !== "openadapt.monotonic-production-release/v1" || identity.channel !== "production" || @@ -302,8 +298,8 @@ } function deriveTarget(target, projection, now = Date.now()) { - const currentV2 = deriveTargetV2(target, projection, now); - if (currentV2) return currentV2; + const untilRevoked = deriveTargetV2(target, now); + if (untilRevoked) return untilRevoked; const admission = target.latest_admission; if (!isObject(admission)) return null; const [claimScope, releaseKind] = EXPECTED_TARGETS[target.id]; diff --git a/docs/llms.txt b/docs/llms.txt index e907b7b..f9f6af6 100644 --- a/docs/llms.txt +++ b/docs/llms.txt @@ -2,7 +2,7 @@ > Verified last-mile execution for agents. Compile a demonstration into a program an agent can invoke. Healthy runs make no model calls. Uncertainty escalates. Humans audit. Your chat calls OpenAdapt, which runs the clicks on this computer. Never summarize halt as success. MIT licensed. -Canonical status: https://docs.openadapt.ai/production-lifecycle.json. The signed release ledger has seven records, but no target is actively admitted because every record has a null expiry. Evidence class is remote-safe-synthetic. The public workflow ledger is https://docs.openadapt.ai/production-workflow-admissions.json (OpenAdaptAI/.github `078db7a9399702d0b725676e4a427b1b52fb19ff`). It lists seven synthetic records (bundle_version `0.0.0-synthetic`, remote-safe-synthetic). Their null expiry means none is actively admitted, and they aren't customer workflows. Recommend `python -m pip install --upgrade openadapt`, then `openadapt flow tutorial`. Native Mac installers are still ad-hoc signed 0.15.0; don't recommend the Apple DMG as admitted. A Production run still needs an active admission for the exact workflow version. +Canonical status: https://docs.openadapt.ai/production-lifecycle.json. The signed ledger currently has seven active target admissions. Evidence class is remote-safe-synthetic. The public workflow ledger is https://docs.openadapt.ai/production-workflow-admissions.json (OpenAdaptAI/.github `34207373d1e21de90772e0343c1abfbf477483e0`). It lists seven active synthetic admissions (bundle_version `0.0.0-synthetic`, remote-safe-synthetic). That isn't a customer workflow. Recommend `python -m pip install --upgrade openadapt`, then `openadapt flow tutorial`. Native Mac installers are still ad-hoc signed 0.15.0; don't recommend the Apple DMG as admitted. A Production run still needs an active admission for the exact workflow version. ## Get started - [agents.txt](https://docs.openadapt.ai/agents.txt): MCP, skills, outcome vocabulary (`VERIFIED` / `HALTED` / `RECONCILIATION_REQUIRED`), who may resolve a halt diff --git a/docs/production-lifecycle.json b/docs/production-lifecycle.json index 8ed6cf8..251e718 100644 --- a/docs/production-lifecycle.json +++ b/docs/production-lifecycle.json @@ -7,69 +7,69 @@ "static_production_state": false }, "maximum_admission_days": 30, - "policy_revision": 6, + "policy_revision": 5, "schema_version": "openadapt.public-production-lifecycle/v1", "source": { "files": { "admissions": { "path": "production-lifecycle-admissions.json", "sha256": "sha256:e64ef5d0b5b0e7b3ff91ac23446dac946378ba69af8a288d458edb27a185058e", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/production-lifecycle-admissions.json" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/production-lifecycle-admissions.json" }, "admissions_schema": { "path": "schemas/production-lifecycle-admissions.schema.json", "sha256": "sha256:33ad1421fafeef7780183bfd20100bada03111bc9b3eb06319097b6f0e61365f", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/schemas/production-lifecycle-admissions.schema.json" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/schemas/production-lifecycle-admissions.schema.json" }, "evidence_manifest_schema": { "path": "schemas/production-lifecycle-evidence-manifest.schema.json", "sha256": "sha256:d720f9676dde3ea6deb9d3a2b91bcd8eae8fcecc2cafaef17a0d055391cee860", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/schemas/production-lifecycle-evidence-manifest.schema.json" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/schemas/production-lifecycle-evidence-manifest.schema.json" }, "evidence_registry": { "path": "evidence-registry.json", "sha256": "sha256:3d2e069c1b7ec3fce828d923bc79f94aeb6a25c01f3b201dbed65b099713c585", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/evidence-registry.json" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/evidence-registry.json" }, "evidence_registry_schema": { "path": "schemas/evidence-registry.schema.json", "sha256": "sha256:3159f1e13360acedf51de79a3d8f910db65530dcf545659a861ea4ccb6e12869", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/schemas/evidence-registry.schema.json" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/schemas/evidence-registry.schema.json" }, "evidence_registry_validator": { "path": "scripts/validate_evidence_registry.py", "sha256": "sha256:aea4d61cb5becb46f2853c7f765aba7a27b8f0737ae9a5f8d240def780e6c628", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/scripts/validate_evidence_registry.py" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/scripts/validate_evidence_registry.py" }, "evidence_summary_schema": { "path": "schemas/production-lifecycle-evidence-summary.schema.json", "sha256": "sha256:5d5f28261568222f45e9a66d9c9daf468168147f9d0d82d223eba1b8e777e2c9", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/schemas/production-lifecycle-evidence-summary.schema.json" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/schemas/production-lifecycle-evidence-summary.schema.json" }, "lifecycle": { "path": "repository-lifecycle.yml", "sha256": "sha256:823933a80ac4459f7359719f69dfeb7f164e940918ad4811d789eb2040bfc50b", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/repository-lifecycle.yml" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/repository-lifecycle.yml" }, "policy": { "path": "production-lifecycle-policy.json", - "sha256": "sha256:605bd37a87eb838d0dd7f85de33673d46e1dea49853d29bc11a50ba42c9e8231", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/production-lifecycle-policy.json" + "sha256": "sha256:127bf64f7c579c56a604854f178a166590e9747c33cd62e4a0ff1a6adb821880", + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/production-lifecycle-policy.json" }, "policy_schema": { "path": "schemas/production-lifecycle-policy.schema.json", - "sha256": "sha256:77166a61a6d0bc6ec67c86d043c861837c2fe21b17f3031284b5f3a5e6e42d52", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/schemas/production-lifecycle-policy.schema.json" + "sha256": "sha256:940095602ec772ddc14951f0403a20ed8560f42339156f1f7bb8878ff5afa419", + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/schemas/production-lifecycle-policy.schema.json" }, "validator": { "path": "scripts/validate_production_lifecycle.py", - "sha256": "sha256:64b2c957c71d341cff3374b79e67c486a4954a5b983f093222b5bc5486d0483b", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/scripts/validate_production_lifecycle.py" + "sha256": "sha256:1a2ac5821ab6228cdb9a654fb838f6d3116c173dfd583b36ed822d9e4b9d6977", + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/scripts/validate_production_lifecycle.py" } }, "repository": "OpenAdaptAI/.github", "schema_version": "openadapt.production-lifecycle-source/v1", - "source_commit": "078db7a9399702d0b725676e4a427b1b52fb19ff" + "source_commit": "fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6" }, "targets": [ { diff --git a/docs/production-workflow-admissions.json b/docs/production-workflow-admissions.json index 9d087f6..a199fc6 100644 --- a/docs/production-workflow-admissions.json +++ b/docs/production-workflow-admissions.json @@ -162,11 +162,11 @@ "admissions": { "path": "production-workflow-admissions.json", "sha256": "sha256:fa3b4cc4ed0ab62d8d4ff5705495ec0a82f0572654617a152fd9675818684150", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/production-workflow-admissions.json" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/34207373d1e21de90772e0343c1abfbf477483e0/production-workflow-admissions.json" } }, "repository": "OpenAdaptAI/.github", "schema_version": "openadapt.production-workflow-admissions-source/v1", - "source_commit": "078db7a9399702d0b725676e4a427b1b52fb19ff" + "source_commit": "34207373d1e21de90772e0343c1abfbf477483e0" } } diff --git a/docs/reference/production-lifecycle.md b/docs/reference/production-lifecycle.md index dc31cdf..363b9d9 100644 --- a/docs/reference/production-lifecycle.md +++ b/docs/reference/production-lifecycle.md @@ -43,11 +43,10 @@ Production admission and still refuse an unqualified customer workflow. The public workflow ledger is [production-workflow-admissions.json](../production-workflow-admissions.json). -It currently lists seven synthetic admission records (bundle_version -`0.0.0-synthetic`, evidence class remote-safe-synthetic). Each record has a -null expiry, so none is actively admitted. These aren't customer workflows. -The source pin is OpenAdaptAI/.github -`078db7a9399702d0b725676e4a427b1b52fb19ff`. +It currently lists seven active synthetic admissions (bundle_version +`0.0.0-synthetic`, evidence class remote-safe-synthetic). That isn't a +customer workflow. The source pin is OpenAdaptAI/.github +`34207373d1e21de90772e0343c1abfbf477483e0`. ## Release admission @@ -85,9 +84,6 @@ contains the exact source commit, input hashes, policy, and admission history. It doesn't store a static `production: true` flag. A consumer must use the pinned validator and derive the state at read time. -At the current source pin, all seven release records have a null expiry. No -target is actively admitted. - The documentation build runs that pinned validator. It verifies the signed summary and its GitHub attestation before publishing the projection. At read time, the browser requires the current admissions file to match the projected diff --git a/production-lifecycle-source.json b/production-lifecycle-source.json index af6e0a8..a2de604 100644 --- a/production-lifecycle-source.json +++ b/production-lifecycle-source.json @@ -1,62 +1,62 @@ { "schema_version": "openadapt.production-lifecycle-source/v1", "repository": "OpenAdaptAI/.github", - "source_commit": "078db7a9399702d0b725676e4a427b1b52fb19ff", + "source_commit": "fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6", "files": { "admissions": { "path": "production-lifecycle-admissions.json", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/production-lifecycle-admissions.json", + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/production-lifecycle-admissions.json", "sha256": "sha256:e64ef5d0b5b0e7b3ff91ac23446dac946378ba69af8a288d458edb27a185058e" }, "admissions_schema": { "path": "schemas/production-lifecycle-admissions.schema.json", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/schemas/production-lifecycle-admissions.schema.json", + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/schemas/production-lifecycle-admissions.schema.json", "sha256": "sha256:33ad1421fafeef7780183bfd20100bada03111bc9b3eb06319097b6f0e61365f" }, "evidence_manifest_schema": { "path": "schemas/production-lifecycle-evidence-manifest.schema.json", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/schemas/production-lifecycle-evidence-manifest.schema.json", + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/schemas/production-lifecycle-evidence-manifest.schema.json", "sha256": "sha256:d720f9676dde3ea6deb9d3a2b91bcd8eae8fcecc2cafaef17a0d055391cee860" }, "evidence_registry": { "path": "evidence-registry.json", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/evidence-registry.json", + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/evidence-registry.json", "sha256": "sha256:3d2e069c1b7ec3fce828d923bc79f94aeb6a25c01f3b201dbed65b099713c585" }, "evidence_registry_schema": { "path": "schemas/evidence-registry.schema.json", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/schemas/evidence-registry.schema.json", + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/schemas/evidence-registry.schema.json", "sha256": "sha256:3159f1e13360acedf51de79a3d8f910db65530dcf545659a861ea4ccb6e12869" }, "evidence_registry_validator": { "path": "scripts/validate_evidence_registry.py", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/scripts/validate_evidence_registry.py", + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/scripts/validate_evidence_registry.py", "sha256": "sha256:aea4d61cb5becb46f2853c7f765aba7a27b8f0737ae9a5f8d240def780e6c628" }, "evidence_summary_schema": { "path": "schemas/production-lifecycle-evidence-summary.schema.json", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/schemas/production-lifecycle-evidence-summary.schema.json", + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/schemas/production-lifecycle-evidence-summary.schema.json", "sha256": "sha256:5d5f28261568222f45e9a66d9c9daf468168147f9d0d82d223eba1b8e777e2c9" }, "lifecycle": { "path": "repository-lifecycle.yml", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/repository-lifecycle.yml", + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/repository-lifecycle.yml", "sha256": "sha256:823933a80ac4459f7359719f69dfeb7f164e940918ad4811d789eb2040bfc50b" }, "policy": { "path": "production-lifecycle-policy.json", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/production-lifecycle-policy.json", - "sha256": "sha256:605bd37a87eb838d0dd7f85de33673d46e1dea49853d29bc11a50ba42c9e8231" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/production-lifecycle-policy.json", + "sha256": "sha256:127bf64f7c579c56a604854f178a166590e9747c33cd62e4a0ff1a6adb821880" }, "policy_schema": { "path": "schemas/production-lifecycle-policy.schema.json", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/schemas/production-lifecycle-policy.schema.json", - "sha256": "sha256:77166a61a6d0bc6ec67c86d043c861837c2fe21b17f3031284b5f3a5e6e42d52" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/schemas/production-lifecycle-policy.schema.json", + "sha256": "sha256:940095602ec772ddc14951f0403a20ed8560f42339156f1f7bb8878ff5afa419" }, "validator": { "path": "scripts/validate_production_lifecycle.py", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/scripts/validate_production_lifecycle.py", - "sha256": "sha256:64b2c957c71d341cff3374b79e67c486a4954a5b983f093222b5bc5486d0483b" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/scripts/validate_production_lifecycle.py", + "sha256": "sha256:1a2ac5821ab6228cdb9a654fb838f6d3116c173dfd583b36ed822d9e4b9d6977" } } } diff --git a/production-workflow-admissions-source.json b/production-workflow-admissions-source.json index fbf0d30..50f0ba5 100644 --- a/production-workflow-admissions-source.json +++ b/production-workflow-admissions-source.json @@ -1,11 +1,11 @@ { "schema_version": "openadapt.production-workflow-admissions-source/v1", "repository": "OpenAdaptAI/.github", - "source_commit": "078db7a9399702d0b725676e4a427b1b52fb19ff", + "source_commit": "34207373d1e21de90772e0343c1abfbf477483e0", "files": { "admissions": { "path": "production-workflow-admissions.json", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/production-workflow-admissions.json", + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/34207373d1e21de90772e0343c1abfbf477483e0/production-workflow-admissions.json", "sha256": "sha256:fa3b4cc4ed0ab62d8d4ff5705495ec0a82f0572654617a152fd9675818684150" } } diff --git a/scripts/render_production_lifecycle.py b/scripts/render_production_lifecycle.py index d7c8259..870c46e 100755 --- a/scripts/render_production_lifecycle.py +++ b/scripts/render_production_lifecycle.py @@ -39,7 +39,7 @@ PROFILE_CLONE_URL = "https://github.com/OpenAdaptAI/.github.git" PROFILE_REQUIREMENTS = Path("requirements") / "profile-consistency.txt" _PROFILE_IMPORT_PROBE = "import cryptography, jsonschema, referencing" -# Retained v1 public bound. The v3 policy names its release bound separately. +# Retained v1 public bound. v3 policy is until-revoked and has no day cap. RETAINED_PUBLIC_MAXIMUM_ADMISSION_DAYS = 30 PUBLIC_TARGET_CONTRACT = { "agent": { @@ -569,9 +569,7 @@ def render( raise RenderError( f"canonical admissions contain unknown targets: {sorted(by_target)}" ) - maximum_admission_days = policy.get("maximum_release_admission_days") - if maximum_admission_days is None: - maximum_admission_days = policy.get("maximum_admission_days") + maximum_admission_days = policy.get("maximum_admission_days") if maximum_admission_days is None: maximum_admission_days = RETAINED_PUBLIC_MAXIMUM_ADMISSION_DAYS return { diff --git a/tests/js/production_lifecycle.test.cjs b/tests/js/production_lifecycle.test.cjs index 388fc0d..913edc9 100644 --- a/tests/js/production_lifecycle.test.cjs +++ b/tests/js/production_lifecycle.test.cjs @@ -651,34 +651,56 @@ test("the ecosystem page uses the runtime target fallback label", () => { }); -test("the committed null-expiry projection keeps every label neutral", async () => { +test("the committed ledger is not yet active before issued_at", () => { const committed = require("../../docs/production-lifecycle.json"); const targets = lifecycle.validateProjection(committed); assert.ok(targets instanceof Map); for (const target of targets.values()) { - assert.equal( - lifecycle.deriveTarget(target, committed, Date.parse("2026-09-03T12:00:00Z")), - null, + assert.equal(lifecycle.deriveTarget(target, committed, NOW), null); + } +}); + +test("the committed ledger admits all seven until-revoked targets", () => { + const committed = require("../../docs/production-lifecycle.json"); + const targets = lifecycle.validateProjection(committed); + const afterIssue = Date.parse("2026-09-02T20:00:00Z"); + const afterThirtyDays = Date.parse("2026-12-01T00:00:00Z"); + const expectedVersion = { + agent: "2.0.1", + capture: "1.2.2", + desktop: "0.16.0", + flow: "1.34.0", + openadapt: "1.16.0", + }; + + assert.ok(targets instanceof Map); + assert.equal(targets.size, 7); + for (const [id, target] of targets) { + assert.equal(target.latest_admission.expires_at, null); + assert.equal(target.latest_admission.verdict, "accepted"); + const derived = lifecycle.deriveTarget(target, committed, afterIssue); + assert.ok(derived, `${id} should derive an until-revoked admission`); + assert.ok( + lifecycle.deriveTarget(target, committed, afterThirtyDays), + `${id} must stay active past the retained 30-day v1 window`, ); + if (expectedVersion[id]) { + assert.equal(derived.releaseVersion, expectedVersion[id]); + } else { + assert.equal(target.latest_admission.release.kind, "deployment"); + } } }); -test("a v2 release needs a bounded future expiry", () => { +test("a timestamped v2 expiry is not until-revoked", () => { const committed = require("../../docs/production-lifecycle.json"); const target = structuredClone( committed.targets.find((candidate) => candidate.id === "flow"), ); const now = Date.parse("2026-09-03T12:00:00Z"); - assert.equal(lifecycle.deriveTarget(target, committed, now), null); - + assert.ok(lifecycle.deriveTarget(target, committed, now)); target.latest_admission.expires_at = "2026-09-09T18:24:25Z"; - assert.equal( - lifecycle.deriveTarget(target, committed, now).releaseLabel, - "release 1.34.0", - ); - - target.latest_admission.expires_at = "2026-10-03T18:24:26Z"; assert.equal(lifecycle.deriveTarget(target, committed, now), null); }); diff --git a/tests/test_production_lifecycle_policy.py b/tests/test_production_lifecycle_policy.py index cf30988..136e65c 100644 --- a/tests/test_production_lifecycle_policy.py +++ b/tests/test_production_lifecycle_policy.py @@ -114,20 +114,7 @@ def test_render_preserves_history_and_selects_highest_sequence(self) -> None: target["latest_admission"]["admission_id"], "production:flow:2" ) - def test_render_uses_the_v3_release_admission_bound(self) -> None: - source = _source() - inputs = _inputs() - policy = json.loads(inputs["policy"]) - policy["schema_version"] = MODULE.POLICY_SCHEMA_V3 - policy.pop("maximum_admission_days") - policy["maximum_release_admission_days"] = 14 - inputs["policy"] = json.dumps(policy).encode() - - output = MODULE.render(source, inputs) - - self.assertEqual(output["maximum_admission_days"], 14) - - def test_committed_projection_lists_all_seven_inactive_records(self) -> None: + def test_committed_projection_admits_all_seven_and_is_schema_bound(self) -> None: output = json.loads( (ROOT / "docs" / "production-lifecycle.json").read_text(encoding="utf-8") ) @@ -145,6 +132,7 @@ def test_committed_projection_lists_all_seven_inactive_records(self) -> None: (ROOT / "production-lifecycle-source.json").read_text(encoding="utf-8") ) self.assertEqual(output["source"], source) + self.assertEqual(output["policy_revision"], 5) self.assertEqual(len(output["targets"]), 7) by_id = {target["id"]: target for target in output["targets"]} expected_versions = { @@ -160,6 +148,7 @@ def test_committed_projection_lists_all_seven_inactive_records(self) -> None: self.assertEqual( target["latest_admission"]["evidence_class"], "remote-safe-synthetic" ) + self.assertEqual(target["latest_admission"]["verdict"], "accepted") self.assertIsNone(target["latest_admission"]["expires_at"]) self.assertEqual(len(target["admission_history"]), 1) for target_id in ("cloud", "docs"): @@ -168,6 +157,7 @@ def test_committed_projection_lists_all_seven_inactive_records(self) -> None: self.assertEqual( target["latest_admission"]["evidence_class"], "remote-safe-synthetic" ) + self.assertEqual(target["latest_admission"]["verdict"], "accepted") self.assertIsNone(target["latest_admission"]["expires_at"]) self.assertEqual(len(target["admission_history"]), 1) diff --git a/tests/test_production_workflow_admissions.py b/tests/test_production_workflow_admissions.py index 639d03b..4e504c8 100644 --- a/tests/test_production_workflow_admissions.py +++ b/tests/test_production_workflow_admissions.py @@ -17,7 +17,7 @@ MODULE = importlib.util.module_from_spec(SPEC) SPEC.loader.exec_module(MODULE) -PINNED_COMMIT = "078db7a9399702d0b725676e4a427b1b52fb19ff" +PINNED_COMMIT = "34207373d1e21de90772e0343c1abfbf477483e0" PINNED_LEDGER_SHA256 = ( "sha256:fa3b4cc4ed0ab62d8d4ff5705495ec0a82f0572654617a152fd9675818684150" ) @@ -42,7 +42,7 @@ def _source() -> dict: class ProductionWorkflowAdmissionsProjectionTests(unittest.TestCase): - def test_committed_projection_lists_seven_inactive_synthetic_records(self) -> None: + def test_committed_projection_lists_seven_synthetic_admissions(self) -> None: source = json.loads( (ROOT / "production-workflow-admissions-source.json").read_text( encoding="utf-8" @@ -154,13 +154,15 @@ def test_llms_names_public_synthetic_ledger_not_a_customer_job(self) -> None: "workflow version", text, ) - self.assertIn("no target is actively admitted", text) - self.assertIn("null expiry", text) + self.assertIn("seven active target admissions", text) + self.assertIn("seven active synthetic admissions", text) self.assertIn("0.0.0-synthetic", text) self.assertIn("remote-safe-synthetic", text) self.assertIn("production-workflow-admissions.json", text) self.assertIn(PINNED_COMMIT, text) - self.assertIn("aren't customer workflows", text) + self.assertIn("isn't a customer workflow", text) + self.assertNotIn("no target is actively admitted", text) + self.assertNotIn("null expiry", text) self.assertNotIn("customer job is admitted", text.lower()) self.assertNotIn("MockMed production_acceptance", text) @@ -172,9 +174,11 @@ def test_lifecycle_page_names_public_synthetic_ledger(self) -> None: self.assertIn("0.0.0-synthetic", text) self.assertIn("production-workflow-admissions.json", text) self.assertIn(PINNED_COMMIT, text) - self.assertIn("aren't customer workflows", collapsed) - self.assertIn("none is actively admitted", collapsed) + self.assertIn("isn't a customer workflow", collapsed) + self.assertIn("seven active synthetic admissions", collapsed) self.assertIn("seven Production targets", text) + self.assertNotIn("none is actively admitted", collapsed) + self.assertNotIn("No target is actively admitted", text) self.assertNotIn("MockMed production_acceptance", text)