From ef1496725bdc89589caf47dc929f98177c695ea7 Mon Sep 17 00:00:00 2001 From: DTALEX66 <151195992+DTALEX66@users.noreply.github.com> Date: Sat, 1 Aug 2026 00:00:33 +0800 Subject: [PATCH 1/3] fix(release): prepare v0.4.1 verified remediation --- .github/workflows/release.yml | 2 +- README.md | 2 +- app/main.py | 6 +-- app/release-manifest.json | 4 +- config/defaults.yaml | 2 +- config/settings.yaml | 2 +- desktop/package-lock.json | 4 +- desktop/package.json | 2 +- desktop/scripts/verify_nsis_install.ps1 | 4 +- desktop/src-tauri/Cargo.lock | 2 +- desktop/src-tauri/Cargo.toml | 2 +- desktop/src-tauri/tauri.conf.json | 2 +- knowledge_base/api.py | 4 +- pyproject.toml | 2 +- shared/config.py | 2 +- tests/test_ci_a0_gates.py | 51 +++++++++++++++++++++++++ tests/test_diagnostics_api.py | 6 +-- tests/test_release_manifest.py | 16 ++++---- uv.lock | 2 +- 19 files changed, 84 insertions(+), 33 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 05367b2..12fb6b7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -46,7 +46,7 @@ jobs: --commit $env:GITHUB_SHA ` --tree $tree ` --tag $env:GITHUB_REF_NAME ` - --version 0.4.0 ` + --version 0.4.1 ` --url "$env:GITHUB_SERVER_URL/$env:GITHUB_REPOSITORY/releases/tag/$env:GITHUB_REF_NAME" ` --ci-url "$env:GITHUB_SERVER_URL/$env:GITHUB_REPOSITORY/actions/runs/$env:GITHUB_RUN_ID" ` --output .hermes/desktop-runtime-v1/runtime/release-identity.json diff --git a/README.md b/README.md index 6543029..48f749c 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ ArcheAxis OS 以受治理的统一知识底座连接个人学习链和 AI 使用 - **个人学习与 AI 使用的双向反馈**:学习笔记、纠错、练习和人工审核不会自动提升为事实;AI 的来源、Claim、解释、任务结果和 Lesson 同样必须先经 Candidate 治理。 - **可治理的本地运行时**:SQLite 持久化、Outbox/Receipt、失败不改状态、重试与回读,以及不暴露内部审计 ID 的公开投影。 - **桌面 Workspace A1**:默认 Apple-light,Violet Core 保留为暗色主题;一级 Rail、动态二级导航、上下文与证据检查器和真实活动坞均已接入。真实 Chromium 验证壳层交互后方可发布;Tauri WebView 点击级证据和公开发布资产仍未闭环,不能用 Chromium 结果替代。 -- **当前版本**:`0.4.0`;Release Manifest 仍为 `unreleased / public=false`,公开 Alpha/Beta/Stable 发布尚未宣告。 +- **当前版本**:`0.4.1`;Release Manifest 仍为 `unreleased / public=false`,公开 Alpha/Beta/Stable 发布尚未宣告。 Research candidate 仍必须经过人工审查和来源独立性验证,不能自动当作 verified truth。产品定位见 [`docs/PRODUCT_POSITIONING.md`](docs/PRODUCT_POSITIONING.md);当前事实、限制和验证证据见 [`docs/PROJECT_STATUS.md`](docs/PROJECT_STATUS.md) 与 [`docs/VERIFICATION_POLICY.md`](docs/VERIFICATION_POLICY.md)。 diff --git a/app/main.py b/app/main.py index 5d08b92..a92a7ae 100644 --- a/app/main.py +++ b/app/main.py @@ -42,7 +42,7 @@ async def core_runtime_lifespan(_: FastAPI): app = FastAPI( lifespan=core_runtime_lifespan, title="Cognitive-Loop-OS", - version=str(config.get("app.version", "0.4.0")), + version=str(config.get("app.version", "0.4.1")), description="AI cognitive runtime with an integrated Knowledge-Base. " "Absorbs Obsidian, Tana, Notion, Logseq, Roam, Heptabase, GraphRAG, Zettelkasten.", docs_url="/docs", @@ -362,7 +362,7 @@ def health(): return { "status": "ok", "system": "cognitive-loop-os", - "version": str(config.get("app.version", "0.4.0")), + "version": str(config.get("app.version", "0.4.1")), "endpoints": _http_route_counts(), "stats": { "documents": _c("kb_documents"), @@ -649,7 +649,7 @@ def architecture(): Auth-->Router; Router-->Pipeline; Pipeline-->Search; Pipeline-->Garden Search-->SQLite; Search-->VecDB; Garden-->GraphDB; Review-->SQLite; SQLite-->Backup ```""", - "version": str(config.get("app.version", "0.4.0")), + "version": str(config.get("app.version", "0.4.1")), "measurement_note": "Module and test counts are intentionally not embedded.", } diff --git a/app/release-manifest.json b/app/release-manifest.json index 6f82c03..c3b3b1d 100644 --- a/app/release-manifest.json +++ b/app/release-manifest.json @@ -6,7 +6,7 @@ "english_name": "ArcheAxis OS", "workspace_name": "元枢·观心", "workspace_english_name": "ArcheAxis Cognitive Workspace", - "version": "0.4.0", + "version": "0.4.1", "requires_python": ">=3.11" }, "release": { @@ -23,7 +23,7 @@ "dependency_lock": { "path": "uv.lock", "algorithm": "sha256", - "digest": "59051acb00c9824e4c76d5ea1a69eb701d029565687b68dd683d50d2788a589c", + "digest": "10304d747de08eaefd2a6972ad0191bdc24c1ead3d42c3ca644194daeb4421c3", "format_version": 1, "revision": 3 }, diff --git a/config/defaults.yaml b/config/defaults.yaml index bbf6dd1..e1f7fac 100644 --- a/config/defaults.yaml +++ b/config/defaults.yaml @@ -1,7 +1,7 @@ # Public, non-secret baseline for every runtime profile. app: name: "Cognitive-Loop-OS" - version: "0.4.0" + version: "0.4.1" environment: "development" port: 8000 host: "127.0.0.1" diff --git a/config/settings.yaml b/config/settings.yaml index c87d9bd..480a4a1 100644 --- a/config/settings.yaml +++ b/config/settings.yaml @@ -4,7 +4,7 @@ app: name: "Cognitive-Loop-OS" - version: "0.4.0" + version: "0.4.1" release_version: "" environment: "development" port: 8000 diff --git a/desktop/package-lock.json b/desktop/package-lock.json index 2e6ada1..ee832ab 100644 --- a/desktop/package-lock.json +++ b/desktop/package-lock.json @@ -1,12 +1,12 @@ { "name": "archeaxis-desktop-shell", - "version": "0.4.0", + "version": "0.4.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "archeaxis-desktop-shell", - "version": "0.4.0", + "version": "0.4.1", "devDependencies": { "@tauri-apps/cli": "2.11.4" } diff --git a/desktop/package.json b/desktop/package.json index 0f5a4e7..877b9f2 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -1,7 +1,7 @@ { "name": "archeaxis-desktop-shell", "private": true, - "version": "0.4.0", + "version": "0.4.1", "scripts": { "tauri": "tauri" }, diff --git a/desktop/scripts/verify_nsis_install.ps1 b/desktop/scripts/verify_nsis_install.ps1 index da8c9ee..a373a6c 100644 --- a/desktop/scripts/verify_nsis_install.ps1 +++ b/desktop/scripts/verify_nsis_install.ps1 @@ -93,14 +93,14 @@ try { $base = "http://127.0.0.1:$($normal.Listener.LocalPort)" $workspaceStatus = (Invoke-WebRequest "$base/workspace" -UseBasicParsing).StatusCode $status = Invoke-RestMethod "$base/workspace/api/status" - if ($workspaceStatus -ne 200 -or $status.release.version -ne '0.4.0') { + if ($workspaceStatus -ne 200 -or $status.release.version -ne '0.4.1') { throw 'installed Workspace returned an invalid product response' } if ($RequireReleaseIdentity) { $version = Invoke-RestMethod "$base/version" if ( $version.release.status -ne 'released' -or - $version.release.tag -ne 'v0.4.0' -or + $version.release.tag -ne 'v0.4.1' -or $version.capabilities.public_installer -ne 'available' ) { throw 'installed runtime did not expose the verified public release identity' diff --git a/desktop/src-tauri/Cargo.lock b/desktop/src-tauri/Cargo.lock index b125db4..bd85bc5 100644 --- a/desktop/src-tauri/Cargo.lock +++ b/desktop/src-tauri/Cargo.lock @@ -49,7 +49,7 @@ checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "archeaxis-desktop-shell" -version = "0.4.0" +version = "0.4.1" dependencies = [ "getrandom 0.3.4", "serde", diff --git a/desktop/src-tauri/Cargo.toml b/desktop/src-tauri/Cargo.toml index 82fb7cf..d9b51a2 100644 --- a/desktop/src-tauri/Cargo.toml +++ b/desktop/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "archeaxis-desktop-shell" -version = "0.4.0" +version = "0.4.1" edition = "2024" rust-version = "1.88" diff --git a/desktop/src-tauri/tauri.conf.json b/desktop/src-tauri/tauri.conf.json index 22f5e8d..8d38f79 100644 --- a/desktop/src-tauri/tauri.conf.json +++ b/desktop/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "ArcheAxis OS", - "version": "0.4.0", + "version": "0.4.1", "identifier": "com.archeaxis.cognitive-workspace", "build": { "frontendDist": "../bootstrap" diff --git a/knowledge_base/api.py b/knowledge_base/api.py index eedb7d7..6c18a68 100644 --- a/knowledge_base/api.py +++ b/knowledge_base/api.py @@ -1,4 +1,4 @@ -"""Knowledge-Base API — SQLite-backed, v0.4.0. +"""Knowledge-Base API — SQLite-backed, v0.4.1. Route counts are reported live by the mounted Core ``/health`` endpoint. Full OpenAPI documentation at /docs. @@ -34,7 +34,7 @@ app = FastAPI( title="Cognitive-Loop-OS Knowledge-Base", - version="0.4.0", + version="0.4.1", description="Knowledge management runtime. Absorbs capabilities from Obsidian, Tana, Notion, Logseq, Roam, Heptabase, Capacities, Anytype, GraphRAG, and Zettelkasten.", docs_url="/docs", redoc_url="/redoc", diff --git a/pyproject.toml b/pyproject.toml index bcc8bb8..f3b3250 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "cognitive-loop-os" -version = "0.4.0" +version = "0.4.1" description = "Local-first cognitive and knowledge runtime with evidence-constrained workflows" readme = "README.md" requires-python = ">=3.11" diff --git a/shared/config.py b/shared/config.py index 23cd743..11574d8 100644 --- a/shared/config.py +++ b/shared/config.py @@ -25,7 +25,7 @@ _DEFAULTS: dict[str, Any] = { "app": { "name": "Cognitive-Loop-OS", - "version": "0.4.0", + "version": "0.4.1", "environment": "development", "port": 8000, "host": "0.0.0.0", diff --git a/tests/test_ci_a0_gates.py b/tests/test_ci_a0_gates.py index 1ca8aaa..c7244d2 100644 --- a/tests/test_ci_a0_gates.py +++ b/tests/test_ci_a0_gates.py @@ -94,6 +94,57 @@ def test_desktop_shell_uses_the_product_version_everywhere() -> None: } == {product_version} +def test_v0_4_1_release_candidate_uses_one_version_everywhere() -> None: + expected_version = "0.4.1" + project = tomllib.loads((ROOT / "pyproject.toml").read_text(encoding="utf-8")) + manifest = json.loads( + (ROOT / "app/release-manifest.json").read_text(encoding="utf-8") + ) + package = json.loads((ROOT / "desktop/package.json").read_text(encoding="utf-8")) + package_lock = json.loads( + (ROOT / "desktop/package-lock.json").read_text(encoding="utf-8") + ) + tauri = json.loads( + (ROOT / "desktop/src-tauri/tauri.conf.json").read_text(encoding="utf-8") + ) + cargo = tomllib.loads( + (ROOT / "desktop/src-tauri/Cargo.toml").read_text(encoding="utf-8") + ) + release_workflow = ( + ROOT / ".github/workflows/release.yml" + ).read_text(encoding="utf-8") + + assert { + project["project"]["version"], + manifest["product"]["version"], + package["version"], + package_lock["version"], + package_lock["packages"][""]["version"], + tauri["version"], + cargo["package"]["version"], + } == {expected_version} + assert f"--version {expected_version}" in release_workflow + assert ( + 'name = "cognitive-loop-os"\nversion = "0.4.1"\nsource = { editable = "." }' + in (ROOT / "uv.lock").read_text(encoding="utf-8") + ) + for path in ( + "config/defaults.yaml", + "config/settings.yaml", + "shared/config.py", + "app/main.py", + "knowledge_base/api.py", + "desktop/scripts/verify_nsis_install.ps1", + ): + text = (ROOT / path).read_text(encoding="utf-8") + assert expected_version in text + assert "0.4.0" not in text + lifecycle = (ROOT / "desktop/scripts/verify_nsis_install.ps1").read_text( + encoding="utf-8" + ) + assert "v0.4.1" in lifecycle + + def test_wheel_gate_requires_release_and_workspace_assets() -> None: workflow = WORKFLOW.read_text(encoding="utf-8") wheel_job = _job_section(workflow, "wheel-smoke", "browser-smoke") diff --git a/tests/test_diagnostics_api.py b/tests/test_diagnostics_api.py index 10ae72c..c82a4f1 100644 --- a/tests/test_diagnostics_api.py +++ b/tests/test_diagnostics_api.py @@ -18,7 +18,7 @@ def test_diagnostics_returns_safe_versioned_status() -> None: ) assert payload["release"] == { "status": "unreleased", - "version": "0.4.0", + "version": "0.4.1", "channel": "development", "source_commit": "unavailable", } @@ -37,7 +37,7 @@ def test_diagnostics_does_not_promote_unverified_release_override(monkeypatch) - assert response.status_code == 200 assert response.json()["release"] == { "status": "unreleased", - "version": "0.4.0", + "version": "0.4.1", "channel": "development", "source_commit": "unavailable", } @@ -54,7 +54,7 @@ def test_diagnostics_rejects_unsafe_release_version(monkeypatch) -> None: assert response.status_code == 200 assert response.json()["release"] == { "status": "unreleased", - "version": "0.4.0", + "version": "0.4.1", "channel": "development", "source_commit": "unavailable", } diff --git a/tests/test_release_manifest.py b/tests/test_release_manifest.py index 2f917bf..24702a0 100644 --- a/tests/test_release_manifest.py +++ b/tests/test_release_manifest.py @@ -30,7 +30,7 @@ def test_release_manifest_is_packaged_truth_and_matches_dependency_lock() -> Non "channel": "development", "public": False, } - assert manifest["product"]["version"] == "0.4.0" + assert manifest["product"]["version"] == "0.4.1" assert manifest["source"]["commit"] == "unavailable" assert manifest["verification"]["embedded_test_counts"] is False lock_digest = hashlib.sha256((root / "uv.lock").read_bytes()).hexdigest() @@ -63,7 +63,7 @@ def test_release_manifest_is_packaged_truth_and_matches_dependency_lock() -> Non assert manifest["product"]["version"] == config.get("app.version") assert safe_release_summary() == { "status": "unreleased", - "version": "0.4.0", + "version": "0.4.1", "channel": "development", "source_commit": "unavailable", } @@ -145,11 +145,11 @@ def test_bundled_release_identity_exposes_a_verified_public_release_summary( { "schema_version": "1.0.0", "release": { - "tag": "v0.4.0", - "version": "0.4.0", + "tag": "v0.4.1", + "version": "0.4.1", "channel": "stable", "public": True, - "url": "https://github.com/DTALEX66/Cognitive-Loop-OS/releases/tag/v0.4.0", + "url": "https://github.com/DTALEX66/Cognitive-Loop-OS/releases/tag/v0.4.1", }, "source": { "commit": "34ca0fbd5ae636314a3403c473bde9247ef95907", @@ -166,12 +166,12 @@ def test_bundled_release_identity_exposes_a_verified_public_release_summary( assert release.safe_release_summary() == { "status": "released", - "version": "0.4.0", + "version": "0.4.1", "channel": "stable", "source_commit": "34ca0fbd5ae636314a3403c473bde9247ef95907", - "tag": "v0.4.0", + "tag": "v0.4.1", "ci_run": 30548553629, - "url": "https://github.com/DTALEX66/Cognitive-Loop-OS/releases/tag/v0.4.0", + "url": "https://github.com/DTALEX66/Cognitive-Loop-OS/releases/tag/v0.4.1", } assert release.effective_capabilities()["public_installer"] == "available" diff --git a/uv.lock b/uv.lock index 5f0598f..9c4b4d5 100644 --- a/uv.lock +++ b/uv.lock @@ -618,7 +618,7 @@ wheels = [ [[package]] name = "cognitive-loop-os" -version = "0.4.0" +version = "0.4.1" source = { editable = "." } dependencies = [ { name = "apscheduler" }, From 643160512090b4c72ae9638ac40095f74377112d Mon Sep 17 00:00:00 2001 From: DTALEX66 Date: Sat, 1 Aug 2026 07:45:35 +0800 Subject: [PATCH 2/3] fix(release): gate v0.4.1 draft publication --- .github/workflows/release.yml | 96 +++++++++++++++++++++++++++++++++- CHANGELOG.md | 20 +++++++ LICENSE | 21 ++++++++ README.md | 8 +++ SECURITY.md | 29 ++++++++++ THIRD_PARTY_NOTICES.md | 35 +++++++++++++ docs/PROJECT_STATUS.md | 8 ++- tests/test_release_manifest.py | 79 ++++++++++++++++++++++++++++ 8 files changed, 293 insertions(+), 3 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 LICENSE create mode 100644 SECURITY.md create mode 100644 THIRD_PARTY_NOTICES.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 12fb6b7..0e90aac 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,6 +13,7 @@ jobs: runs-on: windows-latest timeout-minutes: 45 permissions: + actions: read contents: write steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 @@ -28,6 +29,24 @@ jobs: if ((git rev-parse origin/main).Trim() -ne $env:GITHUB_SHA) { throw "release tag must point to the current main commit" } + - name: Require successful exact-SHA CI + env: + GH_TOKEN: ${{ github.token }} + shell: pwsh + run: | + $ciRuns = @( + gh run list --commit $env:GITHUB_SHA --workflow CI --limit 100 ` + --json databaseId,headSha,conclusion,status,url,workflowName,createdAt | ConvertFrom-Json + ) + $successfulRuns = @($ciRuns | Where-Object { + $_.workflowName -eq "CI" -and + $_.headSha -eq $env:GITHUB_SHA -and + $_.status -eq "completed" -and + $_.conclusion -eq "success" + }) + if ($successfulRuns.Count -lt 1) { + throw "exact-SHA CI did not complete successfully" + } - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e with: enable-cache: true @@ -84,7 +103,7 @@ jobs: if ($manifestNames.Count -ne 3 -or ($manifestNames | Select-Object -Unique).Count -ne 3) { throw "expected exactly three unique checksum-bound payloads" } - - name: Publish verified assets + - name: Upload verified assets to draft release env: GH_TOKEN: ${{ github.token }} shell: pwsh @@ -97,4 +116,77 @@ jobs: (Resolve-Path "release-assets/release-identity.json").Path, (Resolve-Path "release-assets/SHA256SUMS.txt").Path ) - gh release create $env:GITHUB_REF_NAME $releaseAssets --verify-tag --title "ArcheAxis OS $env:GITHUB_REF_NAME" --generate-notes + gh release create $env:GITHUB_REF_NAME $releaseAssets --draft --verify-tag --title "ArcheAxis OS $env:GITHUB_REF_NAME" --generate-notes + - name: Read back and verify draft release assets + env: + GH_TOKEN: ${{ github.token }} + shell: pwsh + run: | + $expectedAssets = @( + "ArcheAxis.OS-Windows-x64-setup.exe", + "cognitive_loop_os-0.4.1-py3-none-any.whl", + "release-identity.json", + "SHA256SUMS.txt" + ) | Sort-Object + $release = gh release view $env:GITHUB_REF_NAME --json tagName,targetCommitish,isDraft,assets,url | ConvertFrom-Json + if ($release.tagName -ne $env:GITHUB_REF_NAME) { throw "release tag readback mismatch" } + if ($release.isDraft -ne $true) { throw "release draft state mismatch" } + $acceptedReleaseTargets = @("main", $env:GITHUB_SHA) + if ($release.targetCommitish -notin $acceptedReleaseTargets) { + throw "release target commit is neither main nor exact workflow SHA" + } + if ((git rev-list -n 1 "$($release.tagName)^{}").Trim() -ne $env:GITHUB_SHA) { + throw "release tag target does not equal the exact workflow SHA" + } + $publicAssets = @($release.assets | ForEach-Object name | Sort-Object) + if (Compare-Object $expectedAssets $publicAssets) { + throw "public asset set differs from expected release asset set" + } + $downloadRoot = ".hermes/task-runtime/release-download-readback" + New-Item -ItemType Directory -Force $downloadRoot | Out-Null + gh release download $env:GITHUB_REF_NAME --dir $downloadRoot + foreach ($asset in $release.assets) { + if (-not $asset.digest -or -not $asset.digest.StartsWith("sha256:")) { + throw "provider digest is missing for $($asset.name)" + } + $downloadedDigest = (Get-FileHash "$downloadRoot/$($asset.name)" -Algorithm SHA256).Hash.ToLowerInvariant() + if ($asset.digest.ToLowerInvariant() -ne "sha256:$downloadedDigest") { + throw "provider digest differs from downloaded asset for $($asset.name)" + } + } + $manifest = Get-Content "$downloadRoot/SHA256SUMS.txt" | ForEach-Object { + if ($_ -notmatch '^([0-9a-fA-F]{64}) (.+)$') { throw "invalid downloaded SHA256SUMS line: $_" } + [pscustomobject]@{ digest = $Matches[1].ToLowerInvariant(); name = $Matches[2] } + } + $expectedPayloads = @($expectedAssets | Where-Object { $_ -ne "SHA256SUMS.txt" } | Sort-Object) + $manifestPayloads = @($manifest | ForEach-Object name | Sort-Object) + if (Compare-Object $expectedPayloads $manifestPayloads) { + throw "downloaded checksum payload set differs from expected payload set" + } + foreach ($entry in $manifest) { + $actual = (Get-FileHash "$downloadRoot/$($entry.name)" -Algorithm SHA256).Hash.ToLowerInvariant() + if ($actual -ne $entry.digest) { throw "downloaded SHA-256 mismatch for $($entry.name)" } + $asset = @($release.assets | Where-Object name -eq $entry.name) + if ($asset.Count -ne 1 -or -not $asset[0].digest) { throw "missing provider digest for $($entry.name)" } + if ($asset[0].digest.ToLowerInvariant() -ne "sha256:$actual") { throw "provider digest mismatch for $($entry.name)" } + } + $identity = Get-Content "$downloadRoot/release-identity.json" -Raw | ConvertFrom-Json + $tree = (git rev-parse "$env:GITHUB_SHA^{tree}").Trim() + if ($identity.release.tag -ne $env:GITHUB_REF_NAME -or $identity.source.commit -ne $env:GITHUB_SHA) { + throw "downloaded release identity does not bind tag and exact commit" + } + if ($identity.source.tree -ne $tree) { + throw "downloaded release identity tree mismatch" + } + if ($identity.source.ci_url -ne "$env:GITHUB_SERVER_URL/$env:GITHUB_REPOSITORY/actions/runs/$env:GITHUB_RUN_ID") { + throw "downloaded release identity CI URL mismatch" + } + if ($identity.source.ci_run -ne [int64]$env:GITHUB_RUN_ID -or $identity.release.url -ne $release.url) { + throw "downloaded release identity provenance readback mismatch" + } + - name: Publish verified draft release + env: + GH_TOKEN: ${{ github.token }} + shell: pwsh + run: | + gh release edit $env:GITHUB_REF_NAME --draft=false diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..b4f18b1 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,20 @@ +# Changelog + +All notable release changes are recorded here. Dates and publication status +must be read from Git/GitHub; a source entry does not itself prove publication. + +## [Unreleased] + +- The source Release Manifest remains `unreleased / public=false` until a + release artifact receives an injected and verified public identity. +- Added release-truth, licensing, third-party, and security documentation. +- Added a post-publication gate that reads back the exact public asset set, + provider digests, release identity, and downloaded SHA-256 payloads. + +## [0.4.0] - historical release + +`v0.4.0` is retained as historical publication evidence. Readback found +incomplete checksum payload coverage: the public installer name did not match +its checksum-manifest name and an extra public payload was absent from the +manifest. The tag, Release, and assets are intentionally preserved; remediation +must use a new version. This entry makes no signature claim. \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bd1f473 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 DTALEX66 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 48f749c..47133cf 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ ArcheAxis OS 以受治理的统一知识底座连接个人学习链和 AI 使用 - **可治理的本地运行时**:SQLite 持久化、Outbox/Receipt、失败不改状态、重试与回读,以及不暴露内部审计 ID 的公开投影。 - **桌面 Workspace A1**:默认 Apple-light,Violet Core 保留为暗色主题;一级 Rail、动态二级导航、上下文与证据检查器和真实活动坞均已接入。真实 Chromium 验证壳层交互后方可发布;Tauri WebView 点击级证据和公开发布资产仍未闭环,不能用 Chromium 结果替代。 - **当前版本**:`0.4.1`;Release Manifest 仍为 `unreleased / public=false`,公开 Alpha/Beta/Stable 发布尚未宣告。 +- **发布真相**:`v0.4.0` 是保留且不可原地改写的 historical release,但具有 **incomplete checksum payload coverage**;它不证明完整发布完整性。`v0.4.1` 只有在 merge-SHA CI、tag、公开资产集合、下载后 SHA-256、release identity 与 installer lifecycle 全部回读通过后才可宣称发布。 Research candidate 仍必须经过人工审查和来源独立性验证,不能自动当作 verified truth。产品定位见 [`docs/PRODUCT_POSITIONING.md`](docs/PRODUCT_POSITIONING.md);当前事实、限制和验证证据见 [`docs/PROJECT_STATUS.md`](docs/PROJECT_STATUS.md) 与 [`docs/VERIFICATION_POLICY.md`](docs/VERIFICATION_POLICY.md)。 @@ -190,3 +191,10 @@ pre-commit run --all-files 开发中只运行受影响的定向测试;diff 冻结后运行一次必要完整门禁,推送后以一次 GitHub CI 为准。详细触发规则见 [`docs/VERIFICATION_POLICY.md`](docs/VERIFICATION_POLICY.md)。 CI 使用 `pyproject.toml` 作为依赖与工具配置单一事实源;`requirements.txt` 仅作为兼容安装清单并与核心依赖保持同步。 + +## 许可、安全与变更记录 + +- 项目许可:[`LICENSE`](LICENSE) +- 第三方组件说明:[`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md) +- 漏洞报告:[`SECURITY.md`](SECURITY.md) +- 发布历史与已知完整性限制:[`CHANGELOG.md`](CHANGELOG.md) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..dc27b05 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,29 @@ +# Security Policy + +## Supported versions + +Security fixes are developed against the current `main` branch and the newest +published release, when one exists. The historical `v0.4.0` release is retained +for provenance but does not have complete checksum payload coverage. It must +not be treated as a fully integrity-verified distribution. + +## Reporting a vulnerability + +Do not open a public issue containing exploit details, credentials, personal +data, or private paths. Use GitHub's private security-advisory reporting for +this repository. If that facility is unavailable, open a minimal public issue +asking the maintainer to enable a private reporting channel, without including +the vulnerability details. + +Include the affected version or commit, impact, reproduction preconditions, +and the smallest non-sensitive proof available. Never attach real credentials, +database contents, personal Vault data, or executable payloads. + +## Release-security boundary + +A successful build or CI run alone does not prove a public release. Release +claims require an exact main-commit tag, the required exact-SHA CI, an explicit +asset allowlist, checksum-manifest coverage, provider asset inventory readback, +downloaded SHA-256 verification, release-identity provenance, and installer +lifecycle verification. No release artifact is claimed to be cryptographically +signed unless a separate, verifiable signature and trust chain are published. \ No newline at end of file diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md new file mode 100644 index 0000000..2c45643 --- /dev/null +++ b/THIRD_PARTY_NOTICES.md @@ -0,0 +1,35 @@ +# Third-Party Notices + +ArcheAxis OS / Cognitive-Loop-OS is licensed under the MIT License; see +[`LICENSE`](LICENSE). That license does not replace the licenses of third-party +packages, bundled binaries, fonts, or other components. + +## Declared direct Python dependencies + +The release dependency contract is `pyproject.toml` plus the exact resolved +`uv.lock`. At version 0.4.1 the direct runtime declarations are: + +`fastapi`, `python-multipart`, `uvicorn`, `pydantic`, `numpy`, `requests`, +`pyyaml`, `beautifulsoup4`, `defusedxml`, `apscheduler`, `sqlite-vec`, `loguru`, +`structlog`, `markitdown`, `trafilatura`, `networkx`, `litellm`, `pillow`, and +`pytesseract`. + +Optional or development groups additionally declare `setuptools`, +`playwright`, `httpx2`, `jinja2`, `jsonschema`, `pytest`, `ruff`, `tomli`, +`newspaper4k`, `readabilipy`, `youtube-transcript-api`, `mypy`, `pre-commit`, +`crawl4ai`, `langfuse`, and `promptfoo`. The desktop dependency contract is +`desktop/package.json`, `desktop/package-lock.json`, `desktop/src-tauri/Cargo.toml`, +and `desktop/src-tauri/Cargo.lock`. + +The lockfiles, not this summary, are authoritative for exact names, versions, +and transitive packages. Each component remains subject to its own upstream +license and notices. A redistributor must preserve those terms and audit the +exact built artifact; this document does not claim that every optional package +is bundled into every distribution. + +## External tools + +Some development or verification paths can invoke separately installed tools +such as Git, GitHub CLI, Tesseract OCR, Node.js/npm, Rust/Cargo, and NSIS. They +are not relicensed by this repository. Their presence in a build log is not +proof that they are included in a published asset. \ No newline at end of file diff --git a/docs/PROJECT_STATUS.md b/docs/PROJECT_STATUS.md index f21f66f..a7ce110 100644 --- a/docs/PROJECT_STATUS.md +++ b/docs/PROJECT_STATUS.md @@ -1,6 +1,12 @@ # 项目当前状态 -> 更新:2026-07-27。本页是能力状态入口;旧审计文件是历史快照。机器无关的恢复检查见 [`HANDOFF_2026-07-21.md`](HANDOFF_2026-07-21.md),实时分支、SHA、dirty 状态与 CI 必须从 Git/GitHub 读取。 +> 更新:2026-08-01。本页是能力状态入口;旧审计文件是历史快照。机器无关的恢复检查见 [`HANDOFF_2026-07-21.md`](HANDOFF_2026-07-21.md),实时分支、SHA、dirty 状态与 CI 必须从 Git/GitHub 读取。 + +## 发布真相 + +- 当前源码版本为 `0.4.1`,但 packaged source manifest 仍为 `unreleased / public=false`;源码字段不会预先宣告公开发布。 +- `v0.4.0` 是保留的 historical release,但 readback 已证明 incomplete checksum payload coverage:公开 installer 名称与 manifest 名称不一致,且有一个额外公开 payload 未被 manifest 覆盖。历史 tag、Release 和资产不原地替换。 +- 新版本只有在 tag 精确绑定受保护 `main`、exact-SHA CI 全绿、公开资产集合与 checksum payload allowlist 双向一致、provider digest 可核验、下载后 SHA-256 复算通过、release identity 读回一致、installer lifecycle 通过后,才可报告发布完成。当前没有签名发布声明。 ## 当前阶段 diff --git a/tests/test_release_manifest.py b/tests/test_release_manifest.py index 24702a0..4e1e6dd 100644 --- a/tests/test_release_manifest.py +++ b/tests/test_release_manifest.py @@ -294,6 +294,85 @@ def test_release_workflow_publishes_only_checksum_bound_allowlist() -> None: assert 'gh release create $env:GITHUB_REF_NAME $releaseAssets' in workflow +def test_release_truth_documents_preserve_historical_and_source_manifest_truth() -> None: + root = Path(__file__).resolve().parents[1] + required = ["LICENSE", "THIRD_PARTY_NOTICES.md", "SECURITY.md", "CHANGELOG.md"] + for relative in required: + assert (root / relative).is_file(), f"missing release truth document: {relative}" + + readme = (root / "README.md").read_text(encoding="utf-8") + status = (root / "docs" / "PROJECT_STATUS.md").read_text(encoding="utf-8") + changelog = (root / "CHANGELOG.md").read_text(encoding="utf-8") + combined = "\n".join((readme, status, changelog)) + assert "v0.4.0" in combined + assert "historical" in combined.lower() + assert "incomplete checksum payload coverage" in combined + assert "unreleased / public=false" in combined + assert "artifacts are signed" not in changelog.lower() + + +def test_release_workflow_downloads_and_rehashes_exact_public_asset_set() -> None: + root = Path(__file__).resolve().parents[1] + workflow = (root / ".github" / "workflows" / "release.yml").read_text( + encoding="utf-8" + ) + + assert "Read back and verify draft release assets" in workflow + assert "gh release view $env:GITHUB_REF_NAME --json tagName,targetCommitish,isDraft,assets,url" in workflow + assert "gh release download $env:GITHUB_REF_NAME" in workflow + assert "Get-FileHash" in workflow + assert "provider digest" in workflow.lower() + assert "public asset set differs from expected release asset set" in workflow + + +def test_release_workflow_requires_exact_sha_ci_before_building_release_assets() -> None: + """A tag bound to main cannot bypass the successful CI run for that exact SHA.""" + root = Path(__file__).resolve().parents[1] + workflow = (root / ".github" / "workflows" / "release.yml").read_text( + encoding="utf-8" + ) + + assert "Require successful exact-SHA CI" in workflow + assert 'gh run list --commit $env:GITHUB_SHA --workflow CI' in workflow + assert "exact-SHA CI did not complete successfully" in workflow + assert workflow.index("Require successful exact-SHA CI") < workflow.index( + "Prepare bundled runtime and inject exact release identity" + ) + + +def test_release_workflow_keeps_assets_draft_until_readback_closes() -> None: + """No asset becomes public before its GitHub inventory and downloaded bytes verify.""" + root = Path(__file__).resolve().parents[1] + workflow = (root / ".github" / "workflows" / "release.yml").read_text( + encoding="utf-8" + ) + + draft_create = 'gh release create $env:GITHUB_REF_NAME $releaseAssets --draft' + download = "gh release download $env:GITHUB_REF_NAME" + publish = 'gh release edit $env:GITHUB_REF_NAME --draft=false' + assert draft_create in workflow + assert "Read back and verify draft release assets" in workflow + assert "$release.isDraft -ne $true" in workflow + assert "release draft state mismatch" in workflow + assert publish in workflow + assert workflow.index(draft_create) < workflow.index(download) < workflow.index(publish) + + +def test_release_workflow_readback_binds_target_and_identity_tree() -> None: + """The downloaded identity must bind all Git provenance, not only its commit.""" + root = Path(__file__).resolve().parents[1] + workflow = (root / ".github" / "workflows" / "release.yml").read_text( + encoding="utf-8" + ) + + assert '$acceptedReleaseTargets = @("main", $env:GITHUB_SHA)' in workflow + assert '$release.targetCommitish -notin $acceptedReleaseTargets' in workflow + assert "release target commit is neither main nor exact workflow SHA" in workflow + assert '$identity.source.tree -ne $tree' in workflow + assert "downloaded release identity tree mismatch" in workflow + assert "downloaded release identity CI URL mismatch" in workflow + + def test_release_identity_injection_manifests_exact_commit_and_tree(tmp_path) -> None: """Verify scripts/release_inject_identity.py writes valid identity manifest.""" import json From 058fcf6fb9210b5ee381e76675f62d230977df97 Mon Sep 17 00:00:00 2001 From: DTALEX66 Date: Sat, 1 Aug 2026 07:52:47 +0800 Subject: [PATCH 3/3] fix(ci): satisfy repository text conventions --- CHANGELOG.md | 2 +- LICENSE | 2 +- SECURITY.md | 2 +- THIRD_PARTY_NOTICES.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4f18b1..adde69a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,4 +17,4 @@ must be read from Git/GitHub; a source entry does not itself prove publication. incomplete checksum payload coverage: the public installer name did not match its checksum-manifest name and an extra public payload was absent from the manifest. The tag, Release, and assets are intentionally preserved; remediation -must use a new version. This entry makes no signature claim. \ No newline at end of file +must use a new version. This entry makes no signature claim. diff --git a/LICENSE b/LICENSE index bd1f473..a0931e1 100644 --- a/LICENSE +++ b/LICENSE @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/SECURITY.md b/SECURITY.md index dc27b05..2e7ee66 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -26,4 +26,4 @@ claims require an exact main-commit tag, the required exact-SHA CI, an explicit asset allowlist, checksum-manifest coverage, provider asset inventory readback, downloaded SHA-256 verification, release-identity provenance, and installer lifecycle verification. No release artifact is claimed to be cryptographically -signed unless a separate, verifiable signature and trust chain are published. \ No newline at end of file +signed unless a separate, verifiable signature and trust chain are published. diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 2c45643..058997a 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -32,4 +32,4 @@ is bundled into every distribution. Some development or verification paths can invoke separately installed tools such as Git, GitHub CLI, Tesseract OCR, Node.js/npm, Rust/Cargo, and NSIS. They are not relicensed by this repository. Their presence in a build log is not -proof that they are included in a published asset. \ No newline at end of file +proof that they are included in a published asset.