Skip to content

Commit 55eb72a

Browse files
author
TNFR AI Agent
committed
Automate lightweight development bundles for every release
Build exact committed source snapshots with versioned onboarding and verified evidence recovery. Require the bundle in contributor instructions and publish its ZIP and SHA256 sidecar independently of PyPI. Validate with focused packaging controls, a full source rehearsal and strict documentation checks.
1 parent acfaeb3 commit 55eb72a

10 files changed

Lines changed: 2029 additions & 1 deletion

‎.github/WORKFLOWS.md‎

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ not establish current GitHub run health, branch protection or enabled secrets.
1313
| [docs.yml](workflows/docs.yml) | Filtered push/PR to main and manual invocation. References, documentation integrity, staging and strict MkDocs build; publishes Pages only on pushes to main. |
1414
| [verify-references.yml](workflows/verify-references.yml) | Filtered Markdown/notebook changes on PRs and pushes to main/develop. Independent local Markdown target and fragment check. |
1515
| [pip-audit.yml](workflows/pip-audit.yml) | Push/PR to main/master, Monday 05:00 UTC and manual invocation. Audits the installed core, test-all and serialization environment; captures available reports and fails when the JSON audit fails. |
16-
| [pypi-zenodo.yml](workflows/pypi-zenodo.yml) | Published GitHub release or manual invocation. Builds and checks distributions; publishes on release or when the existing manual force-publish input is true. |
16+
| [pypi-zenodo.yml](workflows/pypi-zenodo.yml) | Published GitHub release or manual invocation. Builds/checks PyPI distributions and an independent lightweight development bundle. Publishes to PyPI on release or when the existing manual force-publish input is true; attaches the bundle only on release events. |
1717
| [lint-workflows.yml](workflows/lint-workflows.yml) | Filtered workflow/script changes on PRs and pushes to main. Searches for unsupported Bandit SARIF invocation; this is not a general Actions schema linter. |
1818
| [copilot-setup-steps.yml](workflows/copilot-setup-steps.yml) | Manual invocation or edits to its own workflow. Installs a Python 3.11 development/test environment and verifies importability. |
1919

@@ -90,6 +90,19 @@ Release preparation must consider relevant validation evidence; the workflow
9090
does not itself enforce a dependency on successful CI runs. A normal manual
9191
invocation builds/checks only; its existing force-publish input enables publishing.
9292

93+
An independent `development-bundle` job uses the standard-library builder
94+
`scripts/build_development_bundle.py` to create the mandatory
95+
`TNFR-<version>-light-development.zip` and `.zip.sha256` from the checked-out
96+
commit. It validates the release tag/version and verifies the extracted bundle
97+
with its included evidence helper. It requires neither PyPI credentials nor
98+
engine dependencies, and its output is outside `dist/`. Both manual and release
99+
runs retain an Actions artifact. Only release events attach the ZIP and sidecar
100+
to the existing release; only this job has `contents: write`. Existing matching
101+
assets are accepted by size and SHA256; conflicting assets fail without
102+
overwriting. This job's checksum/extraction checks do not replace the
103+
[onboarding and selected execution checks](../TESTING.md#lightweight-development-bundle)
104+
required during release preparation.
105+
93106
Publishing a GitHub release therefore also requests PyPI publication. Before
94107
that trigger, review validation runs for the exact commit being released:
95108
blocking CI formatting/static checks and its test matrix (including SDK tests),
@@ -109,7 +122,9 @@ verify a DOI. No GitHub/PyPI/Zenodo settings are inferred from this file.
109122
| --- | --- |
110123
| PyPI wheel | Installable `tnfr` package, typing/schema data and package metadata. The publication job uploads built distributions; it does not upload the repository or site. |
111124
| PyPI source distribution | Maintained build inputs selected by [MANIFEST.in](https://github.com/fermga/TNFR-Python-Engine/blob/main/MANIFEST.in), including package source and small root metadata. Research evidence, tests, generated sites and optional application trees are outside this package payload. |
125+
| Lightweight development ZIP | Exact source snapshot for agent/developer handoff: code, theory, documentation, instructions, examples and tests, with generated onboarding, a manifest and verified retrieval of omitted `docs/assets/` evidence. The publication workflow attaches it and its SHA256 sidecar to each GitHub release. It contains neither Git history nor installed dependencies. |
112126
| GitHub source ZIP/TAR | The release tag's repository tree, including committed research evidence. GitHub's source downloads are distinct from wheel/sdist assets and do not contain Git history. |
127+
| Zenodo archive | The complete source archive retained through the separately configured GitHub integration. The lightweight development ZIP does not replace this evidence archive or require another deposit. Verify the actual version DOI and archive after publication. |
113128
| Documentation site | Rendered documentation plus retained downloadable source/evidence, deployed separately to `gh-pages`. Its size is not the installed package size. |
114129
| Actions reports | JUnit, coverage and dependency-audit reports retained by their respective jobs; they are not package contents or release archives. |
115130

‎.github/agents/my-agent.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,12 @@ inputs, not `build/docs-source/` or `site/`.
281281
site build. Report checks and material limitations.
282282
- For distribution changes, check fresh wheel and source archives with
283283
`scripts/check_distribution.py` and the release checks in [Testing].
284+
- Every release must include a verified lightweight development bundle and its
285+
SHA256 sidecar as GitHub release assets. Build from the exact release commit
286+
with `scripts/build_development_bundle.py`; retain code, theory, instructions,
287+
examples and tests, plus onboarding and verified retrieval of omitted evidence.
288+
Follow [Testing] for source/version, extraction and helper checks. Preserve
289+
the complete repository/Zenodo archive separately.
284290

285291
Distinguish grammar rejection, live-state rejection, solver defects, unavailable
286292
observations and unsupported theorem domains. Coherence decrease violates only

‎.github/workflows/pypi-zenodo.yml‎

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,131 @@ on:
1212
type: boolean
1313

1414
jobs:
15+
development-bundle:
16+
runs-on: ubuntu-latest
17+
permissions:
18+
contents: write
19+
20+
steps:
21+
- uses: actions/checkout@v5
22+
with:
23+
fetch-depth: 0
24+
25+
- name: Set up Python for the standard-library bundle tools
26+
uses: actions/setup-python@v6
27+
with:
28+
python-version: '3.11'
29+
30+
- name: Build the exact source development bundle
31+
env:
32+
RELEASE_TAG: ${{ github.event.release.tag_name }}
33+
run: |
34+
python - <<'PY'
35+
import os
36+
import subprocess
37+
import sys
38+
from pathlib import Path
39+
40+
output = Path(os.environ["RUNNER_TEMP"]) / "tnfr-development-bundle"
41+
output.mkdir()
42+
command = [
43+
sys.executable, "scripts/build_development_bundle.py",
44+
"--revision", "HEAD", "--output-dir", str(output),
45+
]
46+
tag = os.environ.get("RELEASE_TAG")
47+
if tag:
48+
command.extend(["--expected-tag", tag])
49+
result = subprocess.run(command, check=True, capture_output=True, text=True)
50+
(output / "build-receipt.json").write_text(result.stdout, encoding="utf-8")
51+
print(result.stdout)
52+
PY
53+
54+
- name: Verify the extracted development bundle
55+
run: |
56+
python - <<'PY'
57+
import os
58+
import subprocess
59+
import sys
60+
import tempfile
61+
import zipfile
62+
from pathlib import Path
63+
64+
output = Path(os.environ["RUNNER_TEMP"]) / "tnfr-development-bundle"
65+
archives = list(output.glob("TNFR-*-light-development.zip"))
66+
if len(archives) != 1:
67+
raise SystemExit("Expected exactly one development bundle")
68+
archive = archives[0]
69+
with tempfile.TemporaryDirectory(prefix="tnfr-extracted-") as directory:
70+
with zipfile.ZipFile(archive) as bundle:
71+
bundle.extractall(directory)
72+
root = Path(directory) / archive.stem
73+
subprocess.run([
74+
sys.executable, "scripts/development_bundle_evidence.py", "verify",
75+
], cwd=root, check=True)
76+
PY
77+
78+
- name: Retain the development bundle and build receipt
79+
uses: actions/upload-artifact@v4
80+
with:
81+
name: tnfr-development-bundle
82+
path: ${{ runner.temp }}/tnfr-development-bundle/*
83+
if-no-files-found: error
84+
retention-days: 14
85+
86+
- name: Attach the development bundle to the published release
87+
if: github.event_name == 'release'
88+
env:
89+
GH_TOKEN: ${{ github.token }}
90+
GH_REPO: ${{ github.repository }}
91+
RELEASE_TAG: ${{ github.event.release.tag_name }}
92+
run: |
93+
python - <<'PY'
94+
import hashlib
95+
import json
96+
import os
97+
import subprocess
98+
import tempfile
99+
from pathlib import Path
100+
from urllib.parse import quote
101+
102+
tag = os.environ["RELEASE_TAG"]
103+
repository = os.environ["GH_REPO"]
104+
output = Path(os.environ["RUNNER_TEMP"]) / "tnfr-development-bundle"
105+
archives = list(output.glob("TNFR-*-light-development.zip"))
106+
if len(archives) != 1:
107+
raise SystemExit("Expected exactly one development bundle")
108+
archive = archives[0]
109+
payloads = [archive, archive.with_name(archive.name + ".sha256")]
110+
result = subprocess.run([
111+
"gh", "api", f"repos/{repository}/releases/tags/{quote(tag, safe='')}",
112+
], check=True, capture_output=True, text=True)
113+
release = json.loads(result.stdout)
114+
existing = {asset["name"]: asset for asset in release["assets"]}
115+
missing = []
116+
for path in payloads:
117+
expected = hashlib.sha256(path.read_bytes()).hexdigest()
118+
asset = existing.get(path.name)
119+
if asset is None:
120+
missing.append(str(path))
121+
continue
122+
actual = asset.get("digest")
123+
if actual is None:
124+
with tempfile.TemporaryDirectory(prefix="tnfr-release-asset-") as directory:
125+
subprocess.run([
126+
"gh", "release", "download", tag, "--repo", repository,
127+
"--pattern", path.name, "--dir", directory,
128+
], check=True)
129+
downloaded = Path(directory) / path.name
130+
actual = "sha256:" + hashlib.sha256(downloaded.read_bytes()).hexdigest()
131+
if asset["size"] != path.stat().st_size or actual != "sha256:" + expected:
132+
raise SystemExit(f"Existing release asset differs: {path.name}")
133+
print(f"Verified existing release asset: {path.name}")
134+
if missing:
135+
subprocess.run([
136+
"gh", "release", "upload", tag, *missing, "--repo", repository,
137+
], check=True)
138+
PY
139+
15140
pypi-publish:
16141
runs-on: ubuntu-latest
17142
permissions:

‎AGENTS.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,12 @@ inputs, not `build/docs-source/` or `site/`.
281281
site build. Report checks and material limitations.
282282
- For distribution changes, check fresh wheel and source archives with
283283
`scripts/check_distribution.py` and the release checks in [Testing].
284+
- Every release must include a verified lightweight development bundle and its
285+
SHA256 sidecar as GitHub release assets. Build from the exact release commit
286+
with `scripts/build_development_bundle.py`; retain code, theory, instructions,
287+
examples and tests, plus onboarding and verified retrieval of omitted evidence.
288+
Follow [Testing] for source/version, extraction and helper checks. Preserve
289+
the complete repository/Zenodo archive separately.
284290

285291
Distinguish grammar rejection, live-state rejection, solver defects, unavailable
286292
observations and unsupported theorem domains. Coherence decrease violates only

‎TESTING.md‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,51 @@ requirements; see [publication payloads](.github/WORKFLOWS.md#published-payloads
487487
Frozen scientific evidence must retain its original bytes and dependencies.
488488
Removing it merely to meet a package budget would not validate the package.
489489

490+
### Lightweight development bundle
491+
492+
Every release also delivers `TNFR-<version>-light-development.zip` and its
493+
`.zip.sha256` sidecar as GitHub release assets. This is the minimum development
494+
handoff for another agent: code, theory, ontology, documentation, instructions,
495+
examples and tests from the selected commit, without Git history or installed
496+
dependencies. The builder derives the version from that commit, not the live
497+
working tree, and excludes bulky `docs/assets/` evidence while retaining the
498+
site's `mathjax.js`. Omitted evidence remains in the full repository/Zenodo
499+
archive and is individually recorded for retrieval; omission does not authorize
500+
deleting or regenerating frozen results.
501+
502+
Build an initial candidate from a committed tree with Python 3.11+ and Git:
503+
504+
```sh
505+
python scripts/build_development_bundle.py --revision HEAD --output-dir artifacts/development-bundle
506+
```
507+
508+
For publication, set `--revision` to the exact release commit/tag and pass
509+
`--expected-tag` with the actual release tag. The tag must be `v` followed by
510+
the version declared by that tree. Keep this output separate from `dist/`;
511+
the bundle is not a PyPI distribution.
512+
513+
Before accepting each release bundle:
514+
515+
1. Verify the archive and sidecar, selected commit/version, byte-exact retained
516+
files, mirrored agent instructions and the declared omitted-evidence manifest.
517+
2. Extract into a fresh directory. Read generated `START_HERE.md` and `AGENTS.md`
518+
and run `python scripts/development_bundle_evidence.py verify` there.
519+
The standard-library helper checks retained files and reports missing
520+
evidence explicitly; its `list` and `fetch` commands support retrieval of
521+
original evidence by pinned revision and SHA256.
522+
3. Follow onboarding to install dependencies and run the README example and
523+
affected contract checks. Report the actual environment and selection;
524+
checksum verification does not execute the engine or validate research.
525+
Research checks needing omitted files require those files to be retrieved
526+
first. Do not substitute missing evidence with regenerated responses.
527+
528+
The [publication workflow](.github/WORKFLOWS.md#package-publication) independently
529+
builds the bundle and verifies its extracted contents without installing engine
530+
dependencies. A manual run retains it as an Actions artifact; a release run also
531+
attaches the ZIP and sidecar to the release. The source archive and existing
532+
Zenodo integration remain the full evidence distribution; no separate Zenodo
533+
deposit is required for this development handoff.
534+
490535
## Security checks
491536

492537
Follow [SECURITY.md](SECURITY.md) for reporting and trust boundaries:

0 commit comments

Comments
 (0)