diff --git a/CITATION.cff b/CITATION.cff index 94b0b35..a0e9e0d 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -1,10 +1,10 @@ cff-version: 1.2.0 -message: "If you use F7-LAS, cite the archived whitepaper below." +message: "If you use F7-LAS, cite the current whitepaper below." authors: - family-names: Fuller given-names: Anthony L. -title: "Securing Agentic AI: The AFuller F7-LAS™ (7-Layer) Model" -version: "3.0" -doi: 10.5281/zenodo.18292122 +title: "Securing Agentic AI with F7-LAS" +version: "4.0" +doi: 10.5281/zenodo.22867553 url: https://github.com/anthfuller/F7-LAS license: CC-BY-4.0 diff --git a/README.md b/README.md index e3ada83..8e8c557 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,15 @@ # F7-LAS™ — Fuller 7-Layer Agentic Security [![CI](https://github.com/anthfuller/F7-LAS/actions/workflows/f7las-ci.yml/badge.svg)](https://github.com/anthfuller/F7-LAS/actions/workflows/f7las-ci.yml) -[![DOI](https://img.shields.io/badge/whitepaper-10.5281%2Fzenodo.18292122-blue.svg)](https://doi.org/10.5281/zenodo.18292122) +[![DOI](https://img.shields.io/badge/whitepaper-10.5281%2Fzenodo.22867553-blue.svg)](https://doi.org/10.5281/zenodo.22867553) ![maturity](https://img.shields.io/badge/implementation-prototype-yellow) > **Current status:** Established reference model with a **prototype reference implementation**. The repository is not a production agent platform, control plane, SOC product, SIEM/SOAR replacement, or production-ready implementation. -Repository version **4.0.0** is prepared as an unpublished release candidate. -No tag, GitHub release, or Zenodo update is implied by the version file. +Whitepaper version **4.0** is the current publication and is distinct from +repository release **v4.0.0**. Zenodo is the authoritative publication record +for the whitepaper; the Git tag identifies the independently versioned +repository release. F7-LAS™ is Anthony Fuller's vendor-neutral, protocol-agnostic security model for designing, reviewing, and governing agentic AI systems. It separates security responsibilities across seven layers so that generative output is not treated as authority to act. @@ -29,7 +31,10 @@ Software supply-chain security is a cross-cutting supplemental domain, **Layer S ## What is in this repository now -- The immutable [F7-LAS whitepaper v3.0](docs/F7-LAS-model-whitepaper_v3.0.pdf) +- The current [F7-LAS Whitepaper v4.0](docs/whitepaper/F7-LAS-Whitepaper-v4.0.pdf), + published at [10.5281/zenodo.22867553](https://doi.org/10.5281/zenodo.22867553), + with a repository [SHA-256 checksum](docs/whitepaper/F7-LAS-Whitepaper-v4.0.sha256) +- The immutable historical [F7-LAS whitepaper v3.0](docs/F7-LAS-model-whitepaper_v3.0.pdf) - A draft [implementation guide](docs/f7-las-implementation-guide/README.md) - A draft catalog of [46 core Layers 1–7 controls plus five supplemental Layer S controls](docs/F7-LAS-Control-Catalog-v0.1.md) - Current [architecture diagrams](docs/architecture-diagrams.md), with explicit implementation boundaries, and engineering review material @@ -84,7 +89,10 @@ These commands validate the **current prototype and repository structure**. They ## Start here - [Documentation index](docs/README.md) -- [Whitepaper v3.0 artifact record](docs/whitepaper-v3.0-artifact.md) +- [Current Whitepaper v4.0](docs/whitepaper/F7-LAS-Whitepaper-v4.0.pdf) +- [Whitepaper v4.0 permanent DOI](https://doi.org/10.5281/zenodo.22867553) +- [Whitepaper v4.0 SHA-256 checksum](docs/whitepaper/F7-LAS-Whitepaper-v4.0.sha256) +- [Historical Whitepaper v3.0 artifact record](docs/whitepaper-v3.0-artifact.md) - [Implementation guide](docs/f7-las-implementation-guide/README.md) - [Control catalog v0.1](docs/F7-LAS-Control-Catalog-v0.1.md) - [Engineering review checklist](docs/Engineering-Review-Checklist.md) @@ -95,7 +103,7 @@ These commands validate the **current prototype and repository structure**. They - [Clean-user acceptance](docs/clean-user-acceptance.md) - [Architecture diagrams](docs/architecture-diagrams.md) - [Control-to-evidence traceability](config/control-traceability.json) -- [4.0.0 release-candidate notes](RELEASE_NOTES.md) +- [v4.0.0 release notes](RELEASE_NOTES.md) - [Release and exact-tag SBOM procedure](docs/release-process.md) - [Roadmap](ROADMAP.md) - [Security policy](SECURITY.md) @@ -107,11 +115,11 @@ Versions belong to individual artifacts: | Artifact | Current version/status | |---|---| | Seven-layer model | Established design baseline | -| Whitepaper | v3.0, immutable historical artifact | +| Whitepaper | v4.0, current publication; v3.0 preserved as an immutable historical artifact | | Implementation guide | Draft; bundled with 4.0.0 but not independently versioned | | Control catalog | v0.1 draft | -| Executable reference implementation | Prototype included in the 4.0.0 candidate | -| Repository release candidate | 4.0.0; not tagged or published | +| Executable reference implementation | Prototype included in repository release v4.0.0 | +| Repository release | v4.0.0, independently versioned from Whitepaper v4.0 | Repository version numbers do not silently change the whitepaper, control catalog, or schema versions. @@ -122,7 +130,10 @@ Repository version numbers do not silently change the whitepaper, control catalo - [LICENSE.md](LICENSE.md) defines the file-level boundary for mixed directories. - F7-LAS™ is a trademark of Anthony L. Fuller. Copyright licenses do not grant trademark rights or imply endorsement. -Preferred citation metadata is in [CITATION.cff](CITATION.cff). The archived whitepaper DOI is [10.5281/zenodo.18292122](https://doi.org/10.5281/zenodo.18292122). +Preferred citation metadata is in [CITATION.cff](CITATION.cff). The current +Whitepaper v4.0 DOI is +[10.5281/zenodo.22867553](https://doi.org/10.5281/zenodo.22867553). Historical +Whitepaper v3.0 remains preserved unchanged in the repository. ## Contributing diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 2113762..fda86c0 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,14 +1,20 @@ -# F7-LAS 4.0.0 Release Candidate +# F7-LAS v4.0.0 Repository Release -**Status:** Prepared but not tagged or published. This document does not -authorize a GitHub release, Zenodo update, or change to the historical -whitepaper. +**Status:** Published as the +[v4.0.0 GitHub repository release](https://github.com/anthfuller/F7-LAS/releases/tag/v4.0.0). +The annotated tag targets commit +`d6a413b878393d98cc2fb2581678f05801767134`. -F7-LAS 4.0.0 is the intended repository release for the evidence-driven +F7-LAS v4.0.0 is the published repository release for the evidence-driven overhaul. It packages the established seven-layer reference model with one bounded, deterministic, synthetic Python + OPA reference workflow. It remains a prototype reference implementation, not a production agent platform. +Whitepaper v4.0 was published separately at +[10.5281/zenodo.22867553](https://doi.org/10.5281/zenodo.22867553) and added to +the repository after the `v4.0.0` release. It is not part of the immutable +`v4.0.0` tag. The tag retains Whitepaper v3.0 as a historical artifact. + ## Release contents - Canonical v1.0.0 contracts for request-through-audit records. @@ -36,22 +42,20 @@ a prototype reference implementation, not a production agent platform. - Evidence is digest-bound but not digitally signed, externally timestamped, or backed by a provenance service. - The control catalog and implementation guide remain draft authored content; - repository version 4.0.0 does not silently revise their independent versions - or the immutable whitepaper v3.0. - -## Required publication gate - -Publication requires all of the following after the release-candidate branch -is independently reviewed: - -1. merge the exact approved tree through a protected pull request; -2. verify the push-triggered `main` workflow against the resulting merge SHA; -3. create `v4.0.0` at that exact approved `main` SHA without additional files; -4. verify the tag-triggered workflow and its complete validation job; -5. download the CycloneDX artifact produced by that tag run, verify its run, - commit, name, and digest, and attach it unchanged to the GitHub release; -6. publish these notes only after the release assets and target SHA are - independently verified. + repository release v4.0.0 does not silently revise their independent + versions or either whitepaper publication. + +## Publication verification + +The repository release completed the reviewed publication process: + +1. the exact approved tree was merged through a protected pull request; +2. the push-triggered `main` workflow passed against the merge commit; +3. the annotated `v4.0.0` tag was created at the exact approved commit; +4. the tag-triggered workflow passed all validation steps; +5. the exact-tag CycloneDX SBOM and checksum manifest were verified and + attached unchanged to the GitHub release; +6. the GitHub release was published from the existing immutable tag. The detailed procedure is in [`docs/release-process.md`](docs/release-process.md). diff --git a/ROADMAP.md b/ROADMAP.md index fd78e82..cf6c8f7 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -4,9 +4,9 @@ F7-LAS is an established seven-layer reference model with a **prototype reference implementation**. The repository is not Beta and is not production-ready. The canonical Python + OPA workflow demonstrates one bounded, synthetic runtime control path; the broader repository remains illustrative and incomplete. -## Approved target +## Delivered target -The target is an **Executable Reference Implementation**: one offline, deterministic Python + OPA workflow that demonstrates bounded behavior and correlated evidence across Layers 1–7. It will remain a reference implementation, not a production agent platform. +The delivered **Executable Reference Implementation** provides one offline, deterministic Python + OPA workflow that demonstrates bounded behavior and correlated evidence across Layers 1–7. It remains a reference implementation, not a production agent platform. ## Milestones @@ -23,7 +23,14 @@ The target is an **Executable Reference Implementation**: one offline, determini ## Release boundary -Repository version **4.0.0** is prepared as an unpublished release candidate. The whitepaper remains **v3.0** until it is separately reviewed and revised. No milestone authorizes a merge to `main`, a tag, a release, or a Zenodo update without explicit approval. See [RELEASE_NOTES.md](RELEASE_NOTES.md) and the [release process](docs/release-process.md). +Repository release **v4.0.0** is tagged and published from immutable commit +`d6a413b878393d98cc2fb2581678f05801767134`. Whitepaper **v4.0** was reviewed +and published separately at +[10.5281/zenodo.22867553](https://doi.org/10.5281/zenodo.22867553), then added +to the repository after the `v4.0.0` tag. It is not part of that immutable +repository release. Whitepaper v3.0 remains preserved as a historical +artifact. See [RELEASE_NOTES.md](RELEASE_NOTES.md) and the +[release process](docs/release-process.md). ## Completion standard diff --git a/docs/F7-LAS-QA.md b/docs/F7-LAS-QA.md index 3379b08..a5120de 100644 --- a/docs/F7-LAS-QA.md +++ b/docs/F7-LAS-QA.md @@ -31,7 +31,8 @@ No. Current planners, tools, sandboxing, telemetry, and policy adapters are inco ## What is canonical? -The seven-layer model and whitepaper v3.0 are the design baseline. The canonical +The seven-layer model and current Whitepaper v4.0 are the design baseline. +Whitepaper v3.0 remains an immutable historical artifact. The canonical executable path is the offline, deterministic Python + OPA workflow under [`examples/canonical-workflow/`](../examples/canonical-workflow/README.md). Other policy-engine examples remain non-canonical illustrative patterns unless @@ -118,13 +119,18 @@ Use it as a design-review lens, threat-modeling aid, draft control catalog, and ## How are artifacts versioned? -- Whitepaper: v3.0, immutable historical artifact. +- Whitepaper: v4.0, current publication; v3.0 is preserved as an immutable + historical artifact. - Control catalog: v0.1 draft. -- Implementation guide: draft; bundled with repository 4.0.0 but not independently versioned. -- Reference code: prototype prepared as repository release candidate 4.0.0; not yet tagged or published. +- Implementation guide: draft; bundled with repository release v4.0.0 but not + independently versioned. +- Reference code: prototype published in tagged repository release v4.0.0. - Individual schemas: independently versioned. -Repository release numbers do not silently revise the whitepaper or other artifacts. +Repository release v4.0.0 and Whitepaper v4.0 are independently versioned. +Whitepaper v4.0 was published separately and is not part of the immutable +v4.0.0 tag. Repository release numbers do not silently revise a whitepaper or +other artifact. ## Is this affiliated with Microsoft? diff --git a/docs/README.md b/docs/README.md index 3660e2d..1a203de 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,10 +4,15 @@ This directory contains the governed documentation for the F7-LAS seven-layer re ## Authoritative artifacts +- [Whitepaper v4.0](whitepaper/F7-LAS-Whitepaper-v4.0.pdf): current publication; + the authoritative record is the permanent + [Zenodo DOI 10.5281/zenodo.22867553](https://doi.org/10.5281/zenodo.22867553). +- [Whitepaper v4.0 SHA-256 checksum](whitepaper/F7-LAS-Whitepaper-v4.0.sha256): + repository integrity record for the published PDF. - [Whitepaper v3.0](F7-LAS-model-whitepaper_v3.0.pdf) — immutable historical publication. - [Whitepaper artifact record](whitepaper-v3.0-artifact.md) — integrity identifiers and handling rule. -- [Whitepaper v3.0 errata](corrections/whitepaper-v3.0-errata.md) — corrections proposed for a future reviewed edition; does not alter the PDF. -- [Implementation guide](f7-las-implementation-guide/README.md) — draft engineering guidance bundled with the repository release candidate. +- [Historical Whitepaper v3.0 correction record](corrections/whitepaper-v3.0-errata.md): areas addressed or superseded by Whitepaper v4.0; does not alter the historical PDF. +- [Implementation guide](f7-las-implementation-guide/README.md): draft engineering guidance bundled with repository release v4.0.0. - [Control catalog v0.1](F7-LAS-Control-Catalog-v0.1.md) — 46 core Layers 1–7 controls plus five supplemental Layer S controls; all remain draft controls in this repository. - [Engineering review checklist](Engineering-Review-Checklist.md) — design-review aid. - [QA and maturity](F7-LAS-QA.md) — current repository truth and limitations. @@ -19,6 +24,9 @@ This directory contains the governed documentation for the F7-LAS seven-layer re - [Control-to-evidence traceability](../config/control-traceability.json) — machine-readable status, limitations, and evidence locators for all 51 controls. - [Release process](release-process.md) — independent approval, exact-tag validation, and release-SBOM attachment procedure. +Whitepaper version **4.0** is distinct from repository release **v4.0.0**. +Publishing or revising either artifact does not silently revise the other. + ## Architecture graphics The [current architecture diagrams](architecture-diagrams.md) describe the diff --git a/docs/architecture-diagrams.md b/docs/architecture-diagrams.md index 826d1ee..e78cb3d 100644 --- a/docs/architecture-diagrams.md +++ b/docs/architecture-diagrams.md @@ -77,5 +77,5 @@ F7-LAS trademark rights or imply endorsement. Legacy draft graphics were removed from the current documentation set because they contained ambiguous execution routing, private-reasoning terminology, or -unsupported active-remediation claims. The immutable whitepaper PDF was not -modified. +unsupported active-remediation claims. The historical Whitepaper v3.0 PDF +remains unchanged. diff --git a/docs/corrections/whitepaper-v3.0-errata.md b/docs/corrections/whitepaper-v3.0-errata.md index adcb08e..118e740 100644 --- a/docs/corrections/whitepaper-v3.0-errata.md +++ b/docs/corrections/whitepaper-v3.0-errata.md @@ -1,8 +1,11 @@ -# Whitepaper v3.0 Errata and Future-Revision Notes +# Historical Whitepaper v3.0 Correction Record -This file records corrections or proposed clarifications for a future reviewed whitepaper edition. It does not modify the historical v3.0 PDF and is not itself a revised whitepaper. +This file preserves the correction areas identified against Whitepaper v3.0. +They were addressed or superseded through the reviewed Whitepaper v4.0 +publication. This historical record does not modify the v3.0 PDF and is not a +current whitepaper or an amendment to that immutable artifact. -## Open review areas +## Historical correction areas - Align execution semantics with the approved canonical workflow. - Formalize threat-model assets, trust boundaries, attacker capabilities, and failure modes. @@ -14,4 +17,7 @@ This file records corrections or proposed clarifications for a future reviewed w - Clarify that Layer 4 proposed tool requests are not authorized invocations. - Clarify that monitoring feedback enters governed change control and cannot cause uncontrolled self-modification. -Specific page-level corrections will be added only after the executable reference semantics and replacement diagrams are reviewed. +Whitepaper v4.0 is the current publication. Its reviewed text and diagrams, +rather than this historical list, define the current whitepaper content. Any +future correction must use a new governed revision and must not overwrite the +v3.0 or v4.0 publications. diff --git a/docs/f7-las-implementation-guide/README.md b/docs/f7-las-implementation-guide/README.md index 7dd1c1d..e7e69c0 100644 --- a/docs/f7-las-implementation-guide/README.md +++ b/docs/f7-las-implementation-guide/README.md @@ -2,7 +2,7 @@ This guide translates the seven-layer model into engineering guidance. It describes desired controls and patterns; it does **not** claim that the current repository implements or automatically verifies every requirement. -The guide remains draft authored content bundled with repository release candidate 4.0.0. It is reconciled with the canonical offline Python + OPA workflow and machine-readable control traceability, but it is not independently versioned or represented as fully implemented guidance. +The guide remains draft authored content bundled with repository release v4.0.0. It is reconciled with the canonical offline Python + OPA workflow and machine-readable control traceability, but it is not independently versioned or represented as fully implemented guidance. ## Contents diff --git a/docs/whitepaper/F7-LAS-Whitepaper-v4.0.pdf b/docs/whitepaper/F7-LAS-Whitepaper-v4.0.pdf new file mode 100644 index 0000000..842e269 Binary files /dev/null and b/docs/whitepaper/F7-LAS-Whitepaper-v4.0.pdf differ diff --git a/docs/whitepaper/F7-LAS-Whitepaper-v4.0.sha256 b/docs/whitepaper/F7-LAS-Whitepaper-v4.0.sha256 new file mode 100644 index 0000000..c39a552 --- /dev/null +++ b/docs/whitepaper/F7-LAS-Whitepaper-v4.0.sha256 @@ -0,0 +1 @@ +67bfbff70f60309608921a58b28ee472d7aca876146988600916af093c992fe7 F7-LAS-Whitepaper-v4.0.pdf diff --git a/scripts/validate-documentation.py b/scripts/validate-documentation.py index 1bd80d4..8dac16d 100755 --- a/scripts/validate-documentation.py +++ b/scripts/validate-documentation.py @@ -53,11 +53,48 @@ "Agent Planning", } EXPECTED_RELEASE_VERSION = "4.0.0" +CURRENT_WHITEPAPER_PATH = Path("docs/whitepaper/F7-LAS-Whitepaper-v4.0.pdf") +CURRENT_WHITEPAPER_CHECKSUM_PATH = Path( + "docs/whitepaper/F7-LAS-Whitepaper-v4.0.sha256" +) +CURRENT_WHITEPAPER_SHA256 = ( + "67bfbff70f60309608921a58b28ee472d7aca876146988600916af093c992fe7" +) +CURRENT_WHITEPAPER_DOI = "https://doi.org/10.5281/zenodo.22867553" +HISTORICAL_WHITEPAPER_PATH = Path("docs/F7-LAS-model-whitepaper_v3.0.pdf") +HISTORICAL_WHITEPAPER_SHA256 = ( + "24f6e855fc8816edb200280c8cdf26fe41e3736a2544f87906bf2b2d273989fa" +) RETIRED_PLACEHOLDERS = { Path("src/agents/placeholder"), Path("src/core/placeholder"), Path("src/tools/placeholder"), } +CURRENT_STATUS_DOCUMENTS = { + Path("README.md"), + Path("RELEASE_NOTES.md"), + Path("ROADMAP.md"), + Path("docs/README.md"), + Path("docs/F7-LAS-QA.md"), + Path("docs/architecture-diagrams.md"), + Path("docs/corrections/whitepaper-v3.0-errata.md"), + Path("docs/f7-las-implementation-guide/README.md"), +} +STALE_STATUS_PATTERNS = { + "unpublished release candidate": re.compile( + r"unpublished\s+release[- ]candidate", re.IGNORECASE + ), + "not tagged or published": re.compile( + r"not\s+tagged\s+or\s+published", re.IGNORECASE + ), + "whitepaper remains v3.0": re.compile( + r"whitepaper\s+remains\s+(?:\*\*)?v3\.0", re.IGNORECASE + ), + "repository release candidate 4.0.0": re.compile( + r"repository\s+release[- ]candidate\s+(?:\*\*)?4\.0\.0", + re.IGNORECASE, + ), +} class DocumentationError(ValueError): @@ -178,16 +215,109 @@ def validate_diagrams(root: Path) -> None: ) -def validate_release_candidate(root: Path) -> None: +def validate_pdf_digest(path: Path, expected_digest: str, label: str) -> None: + try: + content = path.read_bytes() + except OSError as exc: + raise DocumentationError(f"missing {label}: {path}") from exc + if not content.startswith(b"%PDF-"): + raise DocumentationError(f"{label} is not a PDF: {path}") + if hashlib.sha256(content).hexdigest() != expected_digest: + raise DocumentationError(f"{label} digest mismatch: {path}") + + +def validate_whitepapers(root: Path) -> None: + current = root / CURRENT_WHITEPAPER_PATH + validate_pdf_digest(current, CURRENT_WHITEPAPER_SHA256, "current whitepaper") + validate_pdf_digest( + root / HISTORICAL_WHITEPAPER_PATH, + HISTORICAL_WHITEPAPER_SHA256, + "historical whitepaper", + ) + + expected_manifest = ( + f"{CURRENT_WHITEPAPER_SHA256} {CURRENT_WHITEPAPER_PATH.name}\n" + ) + try: + manifest = (root / CURRENT_WHITEPAPER_CHECKSUM_PATH).read_text( + encoding="ascii" + ) + except OSError as exc: + raise DocumentationError( + f"missing current whitepaper checksum: {CURRENT_WHITEPAPER_CHECKSUM_PATH}" + ) from exc + if manifest != expected_manifest: + raise DocumentationError("current whitepaper checksum manifest mismatch") + + whitepaper_directory = current.parent + if whitepaper_directory.exists() and any( + path.suffix.lower() == ".docx" for path in whitepaper_directory.iterdir() + ): + raise DocumentationError("DOCX whitepaper artifacts must not be published here") + + required_references = { + Path("README.md"): { + f"(docs/whitepaper/{CURRENT_WHITEPAPER_PATH.name})", + f"(docs/whitepaper/{CURRENT_WHITEPAPER_CHECKSUM_PATH.name})", + CURRENT_WHITEPAPER_DOI, + "Whitepaper version **4.0**", + "repository release **v4.0.0**", + }, + Path("docs/README.md"): { + f"(whitepaper/{CURRENT_WHITEPAPER_PATH.name})", + f"(whitepaper/{CURRENT_WHITEPAPER_CHECKSUM_PATH.name})", + CURRENT_WHITEPAPER_DOI, + "Whitepaper version **4.0**", + "repository release **v4.0.0**", + }, + } + for relative, required in required_references.items(): + text = (root / relative).read_text(encoding="utf-8") + missing = sorted(item for item in required if item not in text) + if missing: + raise DocumentationError( + f"{relative} has drifted from the current whitepaper metadata: {missing}" + ) + + citation = (root / "CITATION.cff").read_text(encoding="utf-8") + required_citation = { + 'title: "Securing Agentic AI with F7-LAS"', + 'version: "4.0"', + "doi: 10.5281/zenodo.22867553", + } + missing_citation = sorted( + item for item in required_citation if item not in citation + ) + if missing_citation: + raise DocumentationError( + f"CITATION.cff has drifted from Whitepaper v4.0: {missing_citation}" + ) + + +def validate_no_stale_status(relative: Path, text: str) -> None: + for label, pattern in STALE_STATUS_PATTERNS.items(): + if pattern.search(text): + raise DocumentationError( + f"{relative} contains stale current-status assertion: {label}" + ) + + +def validate_release_status(root: Path) -> None: version = (root / "VERSION").read_text(encoding="utf-8").strip() if version != EXPECTED_RELEASE_VERSION: raise DocumentationError( f"VERSION must be {EXPECTED_RELEASE_VERSION}; found {version!r}" ) required_version_documents = { - Path("README.md"): "not tagged or published", - Path("RELEASE_NOTES.md"): "Prepared but not tagged or published", - Path("ROADMAP.md"): "unpublished release candidate", + Path("README.md"): "repository release **v4.0.0**", + Path("RELEASE_NOTES.md"): "Published as the", + Path("ROADMAP.md"): "tagged and published", + Path("docs/F7-LAS-QA.md"): ( + "prototype published in tagged repository release v4.0.0" + ), + Path("docs/f7-las-implementation-guide/README.md"): ( + "bundled with repository release v4.0.0" + ), Path("docs/release-process.md"): "v4.0.0", } for relative, boundary in required_version_documents.items(): @@ -196,6 +326,23 @@ def validate_release_candidate(root: Path) -> None: raise DocumentationError( f"{relative} does not state the {EXPECTED_RELEASE_VERSION} release boundary" ) + + release_notes = (root / "RELEASE_NOTES.md").read_text(encoding="utf-8") + for boundary in ( + "Whitepaper v4.0 was published separately", + "not part of the immutable", + "The tag retains Whitepaper v3.0 as a historical artifact", + ): + if boundary not in release_notes: + raise DocumentationError( + f"RELEASE_NOTES.md is missing whitepaper/release separation: {boundary}" + ) + + for relative in CURRENT_STATUS_DOCUMENTS: + validate_no_stale_status( + relative, (root / relative).read_text(encoding="utf-8") + ) + for relative in RETIRED_PLACEHOLDERS: if (root / relative).exists(): raise DocumentationError(f"retired placeholder returned: {relative}") @@ -213,7 +360,8 @@ def validate_repository(root: Path = ROOT) -> None: validate_command_boundaries(path, root) validate_diagrams(root) - validate_release_candidate(root) + validate_whitepapers(root) + validate_release_status(root) illustrative_opa = ( root / "examples" / "layer5-policy-engines" / "opa-rego" / "README.md" diff --git a/tests/test_documentation.py b/tests/test_documentation.py index fd12a08..45cdfc2 100644 --- a/tests/test_documentation.py +++ b/tests/test_documentation.py @@ -70,6 +70,84 @@ def test_substituted_canonical_diagram_is_rejected(tmp_path: Path) -> None: ) +def test_substituted_current_whitepaper_is_rejected(tmp_path: Path) -> None: + substituted = tmp_path / MODULE.CURRENT_WHITEPAPER_PATH.name + source = ROOT / MODULE.CURRENT_WHITEPAPER_PATH + substituted.write_bytes(source.read_bytes() + b"substituted") + with pytest.raises(MODULE.DocumentationError, match="whitepaper digest mismatch"): + MODULE.validate_pdf_digest( + substituted, + MODULE.CURRENT_WHITEPAPER_SHA256, + "current whitepaper", + ) + + +def test_whitepaper_checksum_manifest_is_exact() -> None: + manifest = (ROOT / MODULE.CURRENT_WHITEPAPER_CHECKSUM_PATH).read_text( + encoding="ascii" + ) + assert manifest == ( + f"{MODULE.CURRENT_WHITEPAPER_SHA256} " + f"{MODULE.CURRENT_WHITEPAPER_PATH.name}\n" + ) + + +def test_whitepaper_publication_metadata_is_locked() -> None: + assert MODULE.CURRENT_WHITEPAPER_PATH == Path( + "docs/whitepaper/F7-LAS-Whitepaper-v4.0.pdf" + ) + assert MODULE.CURRENT_WHITEPAPER_DOI == ( + "https://doi.org/10.5281/zenodo.22867553" + ) + assert MODULE.CURRENT_WHITEPAPER_SHA256 == ( + "67bfbff70f60309608921a58b28ee472d7aca876146988600916af093c992fe7" + ) + + +@pytest.mark.parametrize( + "stale_assertion", + [ + "Repository version 4.0.0 is an unpublished release candidate.", + "Version 4.0.0 is not tagged or published.", + "The whitepaper remains v3.0.", + ], +) +def test_stale_current_status_assertions_are_rejected( + stale_assertion: str, +) -> None: + with pytest.raises( + MODULE.DocumentationError, match="stale current-status assertion" + ): + MODULE.validate_no_stale_status(Path("README.md"), stale_assertion) + + +def test_docx_whitepaper_is_rejected(tmp_path: Path) -> None: + docs = tmp_path / "docs" + whitepaper = docs / "whitepaper" + whitepaper.mkdir(parents=True) + (whitepaper / MODULE.CURRENT_WHITEPAPER_PATH.name).write_bytes( + (ROOT / MODULE.CURRENT_WHITEPAPER_PATH).read_bytes() + ) + (docs / MODULE.HISTORICAL_WHITEPAPER_PATH.name).write_bytes( + (ROOT / MODULE.HISTORICAL_WHITEPAPER_PATH).read_bytes() + ) + (whitepaper / MODULE.CURRENT_WHITEPAPER_CHECKSUM_PATH.name).write_text( + f"{MODULE.CURRENT_WHITEPAPER_SHA256} " + f"{MODULE.CURRENT_WHITEPAPER_PATH.name}\n", + encoding="ascii", + ) + (whitepaper / "F7-LAS-Whitepaper-v4.0.docx").write_bytes(b"not public") + (tmp_path / "README.md").write_text( + (ROOT / "README.md").read_text(encoding="utf-8"), encoding="utf-8" + ) + (docs / "README.md").write_text( + (ROOT / "docs" / "README.md").read_text(encoding="utf-8"), + encoding="utf-8", + ) + with pytest.raises(MODULE.DocumentationError, match="DOCX whitepaper"): + MODULE.validate_whitepapers(tmp_path) + + def test_control_loop_semantics_are_required() -> None: expected = { "returns to the PDP for reevaluation",