diff --git a/docs/javascripts/production-lifecycle.js b/docs/javascripts/production-lifecycle.js index 91a9ba5..7941165 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 f9f6af6..e907b7b 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 251e718..8ed6cf8 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 a199fc6..9d087f6 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 363b9d9..dc31cdf 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 a2de604..af6e0a8 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 50f0ba5..fbf0d30 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 870c46e..d7c8259 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 5298298..388fc0d 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 994f518..cf30988 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 c219601..639d03b 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)