Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
d4b9054
Pin paired CI and make schema type checks fail closed
Aram-2003 Oct 27, 2026
c93a3ee
Bound private report retention and append recovery corrections
Aram-2003 Oct 27, 2026
912d8bb
fix: stabilize projection observation and bound read epochs
Aram-2003 Oct 27, 2026
1d01ae4
Protect report expansion from concurrent retention
Aram-2003 Oct 27, 2026
39e427b
fix: preserve types across immutable publication observations
Aram-2003 Oct 27, 2026
4dbc881
Simplify historical evidence and retain benchmark comparisons
Aram-2003 Oct 27, 2026
b1c44e9
Simplify runner admission and serial execution
Aram-2003 Oct 27, 2026
a0981d4
Reconcile checkpoint evidence after sync failures and refreeze the en…
Aram-2003 Oct 27, 2026
db15c9f
Record the human-review policy for protected merges (section 15)
Aram-2003 Oct 27, 2026
b852c8e
Pin the paired UI revision to the final v16 commit
Aram-2003 Oct 27, 2026
1ad4993
Refresh the anchored-copy transcript pins for the current Makefile an…
Aram-2003 Oct 27, 2026
061e2a8
Record the item 8 OpContext surface additions
Aram-2003 Oct 27, 2026
676c385
Re-record the scenario effects explained by the v16 contract pair
Aram-2003 Oct 27, 2026
90745ea
Preserve unfinished report evidence and verify readiness against curr…
Aram-2003 Oct 27, 2026
4ea8c5b
Promote presentation helpers and record the item 8 surface in arch ba…
Aram-2003 Oct 27, 2026
dc7a6b9
Prove shard test identities and scope the release receipt claim
Aram-2003 Oct 27, 2026
6a6c271
Share the shard completeness proof between both CI workflows
Aram-2003 Oct 27, 2026
57c9d3b
Expose build_parser in the shard completeness tool
Aram-2003 Oct 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/shard-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@
],
"rest-2": [
"tests/test_abilities.py",
"tests/test_agent_reporting.py",
"tests/test_ai_actions.py",
"tests/test_arch_caches.py",
"tests/test_arch_cli_parity.py",
Expand Down
1 change: 1 addition & 0 deletions .github/ui-revision.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3c9a5f664c147790c490fae0a256e7764b4b9157
124 changes: 49 additions & 75 deletions .github/workflows/release-pair.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ on:
permissions:
contents: read

concurrency:
group: release-pair-${{ github.event.inputs.core_sha }}-${{ github.event.inputs.ui_sha }}
cancel-in-progress: true

run-name: "release pair core ${{ github.event.inputs.core_sha }} ui ${{ github.event.inputs.ui_sha }}"


Expand Down Expand Up @@ -78,14 +82,14 @@ jobs:
echo "ui_sha=$ui_sha" >> "$GITHUB_OUTPUT"

- name: Check out Core at the exact SHA
uses: actions/checkout@v4
uses: actions/checkout@v7
with:
path: repository
ref: ${{ steps.inputs.outputs.core_sha }}
fetch-depth: 0

- name: Check out UI at the exact SHA
uses: actions/checkout@v4
uses: actions/checkout@v7
with:
repository: Aram164/learningos-obsidian-ui
path: obsidian-ui
Expand Down Expand Up @@ -114,10 +118,10 @@ jobs:
test -z "$(git -C obsidian-ui status --porcelain)" \
|| { echo "UI checkout is not clean" >&2; exit 1; }

- uses: actions/setup-python@v5
- uses: actions/setup-python@v7
with:
python-version: "3.14.4"
- uses: actions/setup-node@v4
- uses: actions/setup-node@v7
with:
node-version: 22
# The documented project environment is the repository venv: parser
Expand Down Expand Up @@ -204,7 +208,7 @@ jobs:

- name: Upload this shard's JUnit results
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: junit-${{ matrix.shard }}
path: junit-out/
Expand All @@ -220,7 +224,7 @@ jobs:
# their per-test tmp dirs must never enter the population.
- name: Upload this leg's witness population
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: population-${{ matrix.shard }}
path: |
Expand Down Expand Up @@ -269,12 +273,12 @@ jobs:
echo "ui_sha=$ui_sha" >> "$GITHUB_OUTPUT"

- name: Check out Core at the exact SHA
uses: actions/checkout@v4
uses: actions/checkout@v7
with:
path: repository
ref: ${{ steps.inputs.outputs.core_sha }}
fetch-depth: 0
- uses: actions/setup-python@v5
- uses: actions/setup-python@v7
with:
python-version: "3.14.4"
- name: Install Core development dependencies
Expand Down Expand Up @@ -302,7 +306,7 @@ jobs:
echo "LOS_SCRUT_BIN=$bin" >> "$GITHUB_ENV"

- name: Download the witness population
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
pattern: population-*
path: population-in
Expand Down Expand Up @@ -392,7 +396,11 @@ jobs:
cases = []
for leg in sorted(parts):
for log in sorted(parts[leg][2].glob("*.jsonl")):
shutil.copy(log, audit / log.name)
target = audit / log.name
assert not target.exists(), (
f"audit log {log.name!r} from leg {leg!r} collides "
f"with an earlier leg's log of the same name")
shutil.copy(log, target)
index = parts[leg][2] / "cases.txt"
if index.is_file():
cases.extend(index.read_text(encoding="utf-8").split())
Expand Down Expand Up @@ -473,7 +481,7 @@ jobs:

- name: Upload this shard's JUnit results
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: junit-${{ matrix.shard }}
path: junit-out/
Expand Down Expand Up @@ -505,14 +513,14 @@ jobs:
echo "ui_sha=$ui_sha" >> "$GITHUB_OUTPUT"

- name: Check out Core at the exact SHA
uses: actions/checkout@v4
uses: actions/checkout@v7
with:
path: repository
ref: ${{ steps.inputs.outputs.core_sha }}
fetch-depth: 0

- name: Check out UI at the exact SHA
uses: actions/checkout@v4
uses: actions/checkout@v7
with:
repository: Aram164/learningos-obsidian-ui
path: obsidian-ui
Expand Down Expand Up @@ -540,14 +548,14 @@ jobs:
fi
test -z "$(git -C obsidian-ui status --porcelain)" \
|| { echo "UI checkout is not clean" >&2; exit 1; }
- uses: actions/setup-node@v4
- uses: actions/setup-node@v7
with:
node-version: 22
# The UI check's contract:check-production step imports the real
# learning_os package from the sibling Core checkout (PYTHON, .venv,
# python3, python are probed, in that order) — the same project venv
# the paired gate builds before its own npm run check.
- uses: actions/setup-python@v5
- uses: actions/setup-python@v7
with:
python-version: "3.14.4"
- name: Install Core development dependencies
Expand Down Expand Up @@ -593,24 +601,37 @@ jobs:
echo "ui_sha_short=${ui_sha:0:12}" >> "$GITHUB_OUTPUT"

- name: Check out Core at the exact SHA
uses: actions/checkout@v4
uses: actions/checkout@v7
with:
path: repository
ref: ${{ steps.inputs.outputs.core_sha }}
fetch-depth: 0

- name: Check out UI at the exact SHA
uses: actions/checkout@v4
uses: actions/checkout@v7
with:
repository: Aram164/learningos-obsidian-ui
path: obsidian-ui
ref: ${{ steps.inputs.outputs.ui_sha }}
fetch-depth: 0

- uses: actions/setup-python@v5
# This workflow certifies the pair Core declares, not an arbitrary
# combination: the dispatched UI SHA must equal Core's own pin.
- name: Require the dispatched UI SHA to equal Core's declared pin
working-directory: .
env:
UI_SHA_INPUT: ${{ steps.inputs.outputs.ui_sha }}
run: |
pinned="$(tr -d '[:space:]' < repository/.github/ui-revision.txt)"
if [ "$pinned" != "$UI_SHA_INPUT" ]; then
echo "ui_sha $UI_SHA_INPUT does not equal Core's .github/ui-revision.txt pin $pinned" >&2
exit 1
fi

- uses: actions/setup-python@v7
with:
python-version: "3.14.4"
- uses: actions/setup-node@v4
- uses: actions/setup-node@v7
with:
node-version: 22
- name: Install Core development dependencies
Expand All @@ -622,70 +643,22 @@ jobs:
run: npm ci --ignore-scripts

- name: Download every shard's JUnit results
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
pattern: junit-*
merge-multiple: true
path: junit-in

# The completeness proof, run on every trigger: collect the whole
# suite fresh, count <testcase> elements across all shard results,
# and require set equality — no test lost, none duplicated, none
# invented. Then the static gates the local paired gate runs before
# pytest: lint, typecheck, validate, warning baseline.
# suite fresh, map every shard <testcase> back to its canonical
# node ID, and require multiset equality — no test lost, none
# duplicated, none invented. Then the static gates the local
# paired gate runs before pytest: lint, typecheck, validate,
# warning baseline.
- name: Prove the shards are exactly the suite, then run static gates
run: |
set -eu
.venv/bin/python - <<'PY'
import json
import subprocess
import sys
import xml.etree.ElementTree as ET
from pathlib import Path

manifest = json.loads(
Path(".github/shard-manifest.json").read_text(encoding="utf-8"))
disk = sorted(f"tests/{p.name}"
for p in Path("tests").glob("test_*.py"))
from collections import Counter
covered = Counter(f for leg in manifest["legs"].values()
for f in leg)
for name, count in sorted(covered.items()):
expected = (manifest.get("node_shards", {}).get(name, {})
.get("count", 1))
assert count == expected, (
f"{name} appears in {count} legs, expected {expected}")
assert sorted(covered) == disk, (
f".github/shard-manifest.json does not partition tests/: "
f"missing={sorted(set(disk) - set(covered))} "
f"extra={sorted(set(covered) - set(disk))}")

out = subprocess.run(
[".venv/bin/python", "-m", "pytest", "--collect-only", "-q",
"-m", "not live_install"],
capture_output=True, text=True, check=True)
tail = out.stdout.strip().splitlines()[-1]
# "8520/8521 tests collected (1 deselected) in 7.26s"
collected = int(tail.split()[0].split("/")[0])
ran = 0
skipped = 0
for path in sorted(Path("../junit-in").glob("junit-*.xml")):
root = ET.parse(path).getroot()
for case in root.iter("testcase"):
ran += 1
if case.find("skipped") is not None:
skipped += 1
if (case.find("failure") is not None
or case.find("error") is not None):
sys.exit(f"{path.name}: failing testcase "
f"{case.get('classname')}::{case.get('name')}")
assert ran == collected, (
f"shards ran {ran} tests; the full collection has {collected} "
"(deselecting live_install) — a test was lost, duplicated "
"or added")
print(f"gate: {ran} tests across shards == full collection "
f"({skipped} skipped, visible, never blocking)")
PY
.venv/bin/python tools/check_shard_completeness.py --junit-dir ../junit-in
.venv/bin/python -m ruff check tools tests
.venv/bin/python tools/code_reachability.py
.venv/bin/python -m mypy
Expand Down Expand Up @@ -715,6 +688,7 @@ jobs:
--ui-sha "$UI_SHA" \
--workflow-run-id "$WORKFLOW_RUN_ID" \
--workflow-run-attempt "$WORKFLOW_RUN_ATTEMPT" \
--system-check-passed \
--live-install-scope not-run-live-install-deselected \
--out ../release-pair-artifact/pair-receipt.json

Expand All @@ -734,7 +708,7 @@ jobs:
--artifact-dir ../release-pair-artifact

- name: Upload learningos-pair-<core-12>-<ui-12>
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: "learningos-pair-${{ steps.inputs.outputs.core_sha_short }}-${{ steps.inputs.outputs.ui_sha_short }}"
path: release-pair-artifact
Expand Down
Loading
Loading