From 8a2d3089ad9efae0620814a2ababdfe9b9045391 Mon Sep 17 00:00:00 2001 From: abrichr Date: Thu, 3 Sep 2026 00:10:48 -0400 Subject: [PATCH] fix: keep null-expiry lifecycle records inactive --- docs/javascripts/production-lifecycle.js | 12 +++++--- docs/llms.txt | 2 +- docs/production-lifecycle.json | 32 ++++++++++---------- docs/production-workflow-admissions.json | 4 +-- docs/reference/production-lifecycle.md | 12 +++++--- production-lifecycle-source.json | 30 +++++++++--------- production-workflow-admissions-source.json | 4 +-- scripts/render_production_lifecycle.py | 6 ++-- tests/js/production_lifecycle.test.cjs | 26 ++++++++++++++-- tests/test_production_lifecycle_policy.py | 17 ++++++++++- tests/test_production_workflow_admissions.py | 12 +++++--- 11 files changed, 103 insertions(+), 54 deletions(-) diff --git a/docs/javascripts/production-lifecycle.js b/docs/javascripts/production-lifecycle.js index 91a9ba53..79411657 100644 --- a/docs/javascripts/production-lifecycle.js +++ b/docs/javascripts/production-lifecycle.js @@ -246,23 +246,27 @@ return true; } - function deriveTargetV2(target, now) { + function deriveTargetV2(target, projection, 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" || - admission.expires_at !== null || + expiresAt === 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" || @@ -298,8 +302,8 @@ } function deriveTarget(target, projection, now = Date.now()) { - const untilRevoked = deriveTargetV2(target, now); - if (untilRevoked) return untilRevoked; + const currentV2 = deriveTargetV2(target, projection, now); + if (currentV2) return currentV2; 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 f9f6af66..e907b7b2 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 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. +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. ## 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 251e718b..8ed6cf89 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": 5, + "policy_revision": 6, "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/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/production-lifecycle-admissions.json" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/production-lifecycle-admissions.json" }, "admissions_schema": { "path": "schemas/production-lifecycle-admissions.schema.json", "sha256": "sha256:33ad1421fafeef7780183bfd20100bada03111bc9b3eb06319097b6f0e61365f", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/schemas/production-lifecycle-admissions.schema.json" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/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/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/schemas/production-lifecycle-evidence-manifest.schema.json" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/schemas/production-lifecycle-evidence-manifest.schema.json" }, "evidence_registry": { "path": "evidence-registry.json", "sha256": "sha256:3d2e069c1b7ec3fce828d923bc79f94aeb6a25c01f3b201dbed65b099713c585", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/evidence-registry.json" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/evidence-registry.json" }, "evidence_registry_schema": { "path": "schemas/evidence-registry.schema.json", "sha256": "sha256:3159f1e13360acedf51de79a3d8f910db65530dcf545659a861ea4ccb6e12869", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/schemas/evidence-registry.schema.json" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/schemas/evidence-registry.schema.json" }, "evidence_registry_validator": { "path": "scripts/validate_evidence_registry.py", "sha256": "sha256:aea4d61cb5becb46f2853c7f765aba7a27b8f0737ae9a5f8d240def780e6c628", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/scripts/validate_evidence_registry.py" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/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/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/schemas/production-lifecycle-evidence-summary.schema.json" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/schemas/production-lifecycle-evidence-summary.schema.json" }, "lifecycle": { "path": "repository-lifecycle.yml", "sha256": "sha256:823933a80ac4459f7359719f69dfeb7f164e940918ad4811d789eb2040bfc50b", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/repository-lifecycle.yml" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/repository-lifecycle.yml" }, "policy": { "path": "production-lifecycle-policy.json", - "sha256": "sha256:127bf64f7c579c56a604854f178a166590e9747c33cd62e4a0ff1a6adb821880", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/production-lifecycle-policy.json" + "sha256": "sha256:605bd37a87eb838d0dd7f85de33673d46e1dea49853d29bc11a50ba42c9e8231", + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/production-lifecycle-policy.json" }, "policy_schema": { "path": "schemas/production-lifecycle-policy.schema.json", - "sha256": "sha256:940095602ec772ddc14951f0403a20ed8560f42339156f1f7bb8878ff5afa419", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/schemas/production-lifecycle-policy.schema.json" + "sha256": "sha256:77166a61a6d0bc6ec67c86d043c861837c2fe21b17f3031284b5f3a5e6e42d52", + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/schemas/production-lifecycle-policy.schema.json" }, "validator": { "path": "scripts/validate_production_lifecycle.py", - "sha256": "sha256:1a2ac5821ab6228cdb9a654fb838f6d3116c173dfd583b36ed822d9e4b9d6977", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/scripts/validate_production_lifecycle.py" + "sha256": "sha256:64b2c957c71d341cff3374b79e67c486a4954a5b983f093222b5bc5486d0483b", + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/scripts/validate_production_lifecycle.py" } }, "repository": "OpenAdaptAI/.github", "schema_version": "openadapt.production-lifecycle-source/v1", - "source_commit": "fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6" + "source_commit": "078db7a9399702d0b725676e4a427b1b52fb19ff" }, "targets": [ { diff --git a/docs/production-workflow-admissions.json b/docs/production-workflow-admissions.json index a199fc63..9d087f69 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/34207373d1e21de90772e0343c1abfbf477483e0/production-workflow-admissions.json" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/production-workflow-admissions.json" } }, "repository": "OpenAdaptAI/.github", "schema_version": "openadapt.production-workflow-admissions-source/v1", - "source_commit": "34207373d1e21de90772e0343c1abfbf477483e0" + "source_commit": "078db7a9399702d0b725676e4a427b1b52fb19ff" } } diff --git a/docs/reference/production-lifecycle.md b/docs/reference/production-lifecycle.md index 363b9d9c..dc31cdf1 100644 --- a/docs/reference/production-lifecycle.md +++ b/docs/reference/production-lifecycle.md @@ -43,10 +43,11 @@ 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 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`. +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`. ## Release admission @@ -84,6 +85,9 @@ 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 a2de6040..af6e0a8c 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": "fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6", + "source_commit": "078db7a9399702d0b725676e4a427b1b52fb19ff", "files": { "admissions": { "path": "production-lifecycle-admissions.json", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/production-lifecycle-admissions.json", + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/production-lifecycle-admissions.json", "sha256": "sha256:e64ef5d0b5b0e7b3ff91ac23446dac946378ba69af8a288d458edb27a185058e" }, "admissions_schema": { "path": "schemas/production-lifecycle-admissions.schema.json", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/schemas/production-lifecycle-admissions.schema.json", + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/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/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/schemas/production-lifecycle-evidence-manifest.schema.json", + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/schemas/production-lifecycle-evidence-manifest.schema.json", "sha256": "sha256:d720f9676dde3ea6deb9d3a2b91bcd8eae8fcecc2cafaef17a0d055391cee860" }, "evidence_registry": { "path": "evidence-registry.json", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/evidence-registry.json", + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/evidence-registry.json", "sha256": "sha256:3d2e069c1b7ec3fce828d923bc79f94aeb6a25c01f3b201dbed65b099713c585" }, "evidence_registry_schema": { "path": "schemas/evidence-registry.schema.json", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/schemas/evidence-registry.schema.json", + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/schemas/evidence-registry.schema.json", "sha256": "sha256:3159f1e13360acedf51de79a3d8f910db65530dcf545659a861ea4ccb6e12869" }, "evidence_registry_validator": { "path": "scripts/validate_evidence_registry.py", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/scripts/validate_evidence_registry.py", + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/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/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/schemas/production-lifecycle-evidence-summary.schema.json", + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/schemas/production-lifecycle-evidence-summary.schema.json", "sha256": "sha256:5d5f28261568222f45e9a66d9c9daf468168147f9d0d82d223eba1b8e777e2c9" }, "lifecycle": { "path": "repository-lifecycle.yml", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/repository-lifecycle.yml", + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/repository-lifecycle.yml", "sha256": "sha256:823933a80ac4459f7359719f69dfeb7f164e940918ad4811d789eb2040bfc50b" }, "policy": { "path": "production-lifecycle-policy.json", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/production-lifecycle-policy.json", - "sha256": "sha256:127bf64f7c579c56a604854f178a166590e9747c33cd62e4a0ff1a6adb821880" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/production-lifecycle-policy.json", + "sha256": "sha256:605bd37a87eb838d0dd7f85de33673d46e1dea49853d29bc11a50ba42c9e8231" }, "policy_schema": { "path": "schemas/production-lifecycle-policy.schema.json", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/schemas/production-lifecycle-policy.schema.json", - "sha256": "sha256:940095602ec772ddc14951f0403a20ed8560f42339156f1f7bb8878ff5afa419" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/schemas/production-lifecycle-policy.schema.json", + "sha256": "sha256:77166a61a6d0bc6ec67c86d043c861837c2fe21b17f3031284b5f3a5e6e42d52" }, "validator": { "path": "scripts/validate_production_lifecycle.py", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/fe51c2cdeb0e0de865f3cd6c10738bd581b8d1a6/scripts/validate_production_lifecycle.py", - "sha256": "sha256:1a2ac5821ab6228cdb9a654fb838f6d3116c173dfd583b36ed822d9e4b9d6977" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/scripts/validate_production_lifecycle.py", + "sha256": "sha256:64b2c957c71d341cff3374b79e67c486a4954a5b983f093222b5bc5486d0483b" } } } diff --git a/production-workflow-admissions-source.json b/production-workflow-admissions-source.json index 50f0ba5f..fbf0d307 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": "34207373d1e21de90772e0343c1abfbf477483e0", + "source_commit": "078db7a9399702d0b725676e4a427b1b52fb19ff", "files": { "admissions": { "path": "production-workflow-admissions.json", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/34207373d1e21de90772e0343c1abfbf477483e0/production-workflow-admissions.json", + "url": "https://raw.githubusercontent.com/OpenAdaptAI/.github/078db7a9399702d0b725676e4a427b1b52fb19ff/production-workflow-admissions.json", "sha256": "sha256:fa3b4cc4ed0ab62d8d4ff5705495ec0a82f0572654617a152fd9675818684150" } } diff --git a/scripts/render_production_lifecycle.py b/scripts/render_production_lifecycle.py index 870c46e8..d7c82593 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. v3 policy is until-revoked and has no day cap. +# Retained v1 public bound. The v3 policy names its release bound separately. RETAINED_PUBLIC_MAXIMUM_ADMISSION_DAYS = 30 PUBLIC_TARGET_CONTRACT = { "agent": { @@ -569,7 +569,9 @@ def render( raise RenderError( f"canonical admissions contain unknown targets: {sorted(by_target)}" ) - maximum_admission_days = policy.get("maximum_admission_days") + maximum_admission_days = policy.get("maximum_release_admission_days") + if maximum_admission_days is None: + 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 52982983..388fc0db 100644 --- a/tests/js/production_lifecycle.test.cjs +++ b/tests/js/production_lifecycle.test.cjs @@ -651,12 +651,34 @@ test("the ecosystem page uses the runtime target fallback label", () => { }); -test("the committed admission-free projection keeps every label neutral", async () => { +test("the committed null-expiry projection keeps every label neutral", async () => { 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, NOW), null); + assert.equal( + lifecycle.deriveTarget(target, committed, Date.parse("2026-09-03T12:00:00Z")), + null, + ); } }); + +test("a v2 release needs a bounded future expiry", () => { + 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); + + 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 994f5189..cf30988e 100644 --- a/tests/test_production_lifecycle_policy.py +++ b/tests/test_production_lifecycle_policy.py @@ -114,7 +114,20 @@ def test_render_preserves_history_and_selects_highest_sequence(self) -> None: target["latest_admission"]["admission_id"], "production:flow:2" ) - def test_committed_projection_admits_all_seven_and_is_schema_bound(self) -> None: + 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: output = json.loads( (ROOT / "docs" / "production-lifecycle.json").read_text(encoding="utf-8") ) @@ -147,6 +160,7 @@ def test_committed_projection_admits_all_seven_and_is_schema_bound(self) -> None self.assertEqual( target["latest_admission"]["evidence_class"], "remote-safe-synthetic" ) + self.assertIsNone(target["latest_admission"]["expires_at"]) self.assertEqual(len(target["admission_history"]), 1) for target_id in ("cloud", "docs"): target = by_id[target_id] @@ -154,6 +168,7 @@ def test_committed_projection_admits_all_seven_and_is_schema_bound(self) -> None self.assertEqual( target["latest_admission"]["evidence_class"], "remote-safe-synthetic" ) + self.assertIsNone(target["latest_admission"]["expires_at"]) self.assertEqual(len(target["admission_history"]), 1) def test_source_requires_exact_commit_bound_inventory(self) -> None: diff --git a/tests/test_production_workflow_admissions.py b/tests/test_production_workflow_admissions.py index c2196017..639d03b1 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 = "34207373d1e21de90772e0343c1abfbf477483e0" +PINNED_COMMIT = "078db7a9399702d0b725676e4a427b1b52fb19ff" PINNED_LEDGER_SHA256 = ( "sha256:fa3b4cc4ed0ab62d8d4ff5705495ec0a82f0572654617a152fd9675818684150" ) @@ -42,7 +42,7 @@ def _source() -> dict: class ProductionWorkflowAdmissionsProjectionTests(unittest.TestCase): - def test_committed_projection_lists_seven_synthetic_admissions(self) -> None: + def test_committed_projection_lists_seven_inactive_synthetic_records(self) -> None: source = json.loads( (ROOT / "production-workflow-admissions-source.json").read_text( encoding="utf-8" @@ -154,12 +154,13 @@ def test_llms_names_public_synthetic_ledger_not_a_customer_job(self) -> None: "workflow version", text, ) - self.assertIn("seven active target admissions", text) + self.assertIn("no target is actively admitted", text) + self.assertIn("null expiry", 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("isn't a customer workflow", text) + self.assertIn("aren't customer workflows", text) self.assertNotIn("customer job is admitted", text.lower()) self.assertNotIn("MockMed production_acceptance", text) @@ -171,7 +172,8 @@ 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("isn't a customer workflow", collapsed) + self.assertIn("aren't customer workflows", collapsed) + self.assertIn("none is actively admitted", collapsed) self.assertIn("seven Production targets", text) self.assertNotIn("MockMed production_acceptance", text)