Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -160,4 +160,5 @@ jobs:
test -f .streamlit/config.toml
test -f .github/dependabot.yml
test -f docs/L6_DEPLOYMENT_HYGIENE.md
test -f docs/RELEASING.md
echo "HelixAgent release-readiness contract passed."
205 changes: 196 additions & 9 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,216 @@ on:
push:
tags:
- "v*.*.*"
workflow_dispatch:
pull_request:
paths:
- ".github/workflows/release.yml"
- "CHANGELOG.md"
- "docs/RELEASING.md"

permissions:
contents: write
packages: write
contents: read

concurrency:
group: helixagent-release-${{ github.ref }}
cancel-in-progress: false

jobs:
validate:
name: Validate release candidate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
ref: ${{ github.sha }}
fetch-depth: 0
- name: Verify tag, package version, and changelog entry
if: startsWith(github.ref, 'refs/tags/')
run: |
tag_name="${{ github.ref_name }}"
version="${{ github.ref_name }}"
version="${version#v}"
test "${{ github.event.created }}" = "true"
git cat-file -e "${tag_name}^{tag}"
if [[ ! "$tag_name" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Ref is not a semantic-version tag: $tag_name"
exit 1
fi
grep -Fq "version = \"$version\"" pyproject.toml
awk -v version="$version" '
$0 ~ "^## \\[" version "\\]" { found = 1 }
END { exit !found }
' CHANGELOG.md
- uses: actions/setup-python@v6
with:
python-version: "3.11"
cache: pip
- name: Install declared development dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements-dev.txt
- name: Run Ruff
run: ruff check api agent src tests streamlit_app.py --select E9,F63,F7,F82
- name: Run tests
env:
PYTHONPATH: .
run: pytest
- name: Build container and verify optional C++ interop
run: |
docker build --pull -t helixagent-release:${{ github.sha }} .
docker run --rm helixagent-release:${{ github.sha }} python - <<'PY'
from agent.agent_core import (
cosine_similarity_cpp,
cpp_backend_available,
)

assert cpp_backend_available()
assert cosine_similarity_cpp([1.0, 0.0], [1.0, 0.0]) == 1.0
print("Optional C++ ctypes interop verified in the release image.")
PY

security:
name: Security and SBOM
needs: validate
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@v7
with:
ref: ${{ github.sha }}
fetch-depth: 0
- uses: github/codeql-action/init@v4
with:
languages: python
- uses: github/codeql-action/autobuild@v4
- uses: github/codeql-action/analyze@v4
- name: Scan current source tree for secrets
run: |
docker run --rm \
-v "$PWD:/repo" \
-w /repo \
ghcr.io/gitleaks/gitleaks:latest \
detect --source . --no-git --redact --no-banner --exit-code 1
- uses: anchore/sbom-action@v0
with:
path: .
format: cyclonedx-json
output-file: sbom.cdx.json
- uses: actions/upload-artifact@v4
with:
name: helixagent-release-sbom
path: sbom.cdx.json
if-no-files-found: error

release:
name: Publish Release
name: Create immutable evidence release
if: startsWith(github.ref, 'refs/tags/')
needs: [validate, security]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v7
- name: Build source archive
with:
ref: ${{ github.sha }}
fetch-depth: 0
- uses: actions/download-artifact@v4
with:
name: helixagent-release-sbom
path: dist
- name: Refuse to overwrite an existing release
env:
GH_TOKEN: ${{ github.token }}
run: |
if gh release view "${{ github.ref_name }}" --repo "${{ github.repository }}" >/dev/null 2>&1; then
echo "Release ${{ github.ref_name }} already exists; refusing to overwrite it."
exit 1
fi
- name: Build source archive and checksum
run: |
mkdir -p dist
tar --exclude=.git --exclude=dist -czf dist/helixagent-${GITHUB_REF_NAME}.tar.gz .
git archive --format=tar --prefix="helixagent-${{ github.ref_name }}/" "${{ github.sha }}" | gzip -n > "dist/helixagent-${{ github.ref_name }}.tar.gz"
sha256sum "dist/helixagent-${{ github.ref_name }}.tar.gz" > "dist/helixagent-${{ github.ref_name }}.tar.gz.sha256"
- name: Prepare evidence-bound release notes
run: |
version="${{ github.ref_name }}"
version="${version#v}"
awk -v version="$version" '
$0 ~ "^## \\[" version "\\]" { in_section = 1; next }
in_section && /^## / { exit }
in_section { print }
' CHANGELOG.md > dist/changelog-section.md
test -s dist/changelog-section.md
cat > dist/reproduction.md <<'EOF'
## Reproduction

The tagged commit was validated by the release workflow with:

~~~bash
pip install -r requirements-dev.txt
ruff check api agent src tests streamlit_app.py --select E9,F63,F7,F82
pytest
docker build -t helixagent-release .
~~~

Vector backend measurements are intentionally not generated on shared GitHub-hosted
runners. Run them on the target host and retain the JSON artifact with its environment
metadata:

~~~bash
python -m benchmarks.vector_ops --output vector-ops-results.json
~~~
EOF
{
echo "# HelixAgent ${{ github.ref_name }}"
echo
echo "## What changed"
cat dist/changelog-section.md
echo
echo "## Validation"
echo "- Ruff correctness gate: passed"
echo "- Tests: passed"
echo "- Release-image build and optional C++ ctypes interop: passed"
echo "- CodeQL and secret scan: passed"
echo "- CycloneDX SBOM: generated and attached"
echo
echo "## Vector backend architecture"
echo "NumPy/BLAS default"
echo " |"
echo " +--> optional C++ ctypes backend (interop demonstration)"
echo " |"
echo " +--> pure-Python fallback"
echo
echo "The C++ backend demonstrates safe native interoperability and is not claimed to outperform NumPy/BLAS."
echo
cat dist/reproduction.md
} > dist/release-notes.md
- name: Publish GitHub Release
uses: softprops/action-gh-release@v2
with:
files: dist/*.tar.gz
generate_release_notes: true
tag_name: ${{ github.ref_name }}
target_commitish: ${{ github.sha }}
body_path: dist/release-notes.md
generate_release_notes: false
files: |
dist/helixagent-${{ github.ref_name }}.tar.gz
dist/helixagent-${{ github.ref_name }}.tar.gz.sha256
dist/sbom.cdx.json
dist/reproduction.md

publish-container:
name: Publish GHCR Image
name: Publish validated GHCR image
if: startsWith(github.ref, 'refs/tags/')
needs: release
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v7
with:
ref: ${{ github.sha }}
- uses: docker/login-action@v4
with:
registry: ghcr.io
Expand All @@ -40,6 +223,10 @@ jobs:
id: meta
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=${{ github.ref_name }}
- uses: docker/build-push-action@v6
with:
context: .
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,16 @@ The project follows Semantic Versioning and the Keep a Changelog format.
- CodeQL, Gitleaks, Trivy, pip-audit, Dependabot, and CycloneDX SBOM automation.
- GitHub Release artifacts and GHCR image publishing.
- Security, contribution, release-readiness, and nine-tier deployment-hygiene documentation.
- Evidence-driven semantic-tag release validation with source checksums, CycloneDX SBOM attachment, and reproducibility instructions.
- Three-way vector-backend benchmark infrastructure that writes measurements only when executed.

### Changed

- Hardened `DataIngestor` with file validation, split-parameter validation, deterministic partitioning, duplicate-column detection, and explicit types.
- Reworked the production image into isolated Java, C++, Python build stages and a non-root runtime stage.
- Made NumPy/BLAS the default cosine-similarity backend; the C++ ctypes backend is explicit opt-in interoperability and pure Python remains the degradation path.
- Hardened the ctypes boundary by coercing vectors to contiguous float64 buffers before pointer passing.
- Corrected vector-backend documentation to remove unsupported C++ performance claims.

## [1.0.0] - 2025-06-20

Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,19 @@ docker build -t helixagent .
docker run --rm -p 8000:8000 helixagent
```

## Releases and reproducibility

Maintainers create releases by pushing a validated semantic-version tag; the tag workflow
validates the exact commit before it can create a GitHub Release.

~~~bash
git tag -a vX.Y.Z -m "vX.Y.Z"
git push origin vX.Y.Z
~~~

See [release procedure and evidence artifacts](docs/RELEASING.md) for the required
changelog/version update, validation, and reproducibility guidance.

## Project map

```text
Expand Down
71 changes: 71 additions & 0 deletions docs/RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Releasing HelixAgent

## Release policy

HelixAgent uses semantic versions and immutable, tag-driven evidence releases.

- **PATCH**: bug fixes, documentation corrections, and small internal improvements.
- **MINOR**: backward-compatible capabilities or meaningful engineering milestones.
- **MAJOR**: intentional breaking public API changes.

A commit message does not determine a version. The maintainer selects it after reviewing
the public API, the changelog, and the validation evidence.

## Preflight

Before creating a release tag:

1. Merge the intended change set to `main`.
2. Set `[project].version` in `pyproject.toml` to the release version without the
`v` prefix.
3. Move the corresponding items from `[Unreleased]` into a dated
`## [X.Y.Z]` section in `CHANGELOG.md`.
4. Run the repository validation commands:

~~~bash
pip install -r requirements-dev.txt
ruff check api agent src tests streamlit_app.py --select E9,F63,F7,F82
pytest
docker build -t helixagent-release .
~~~

5. Review the changelog for evidence-bound statements only. Do not add benchmark,
coverage, or performance values unless they came from a documented run.

The release workflow verifies the version/tag and changelog-section consistency. A
mismatch fails before publication.

## Create a release

From the validated `main` commit:

~~~bash
git tag -a vX.Y.Z -m "vX.Y.Z"
git push origin vX.Y.Z
~~~

Only newly-created annotated semantic-version tags matching `v*.*.*` start the
release workflow. The workflow rejects tag-update events, checks out the exact tag
commit, runs validation and security/SBOM work, then creates a GitHub Release only
if those required jobs succeed. It refuses to overwrite an existing GitHub Release.

## Evidence artifacts

A successful release attaches:

- a source archive made from the tagged commit;
- a SHA-256 checksum for that archive;
- a CycloneDX SBOM; and
- release reproduction instructions.

The workflow does not run vector timing on GitHub-hosted hardware because shared
runner measurements are not a useful performance claim. Reproduce the vector
experiment on the target host instead:

~~~bash
python -m benchmarks.vector_ops --output vector-ops-results.json
~~~

NumPy/BLAS is the default vector backend. The C++ ctypes backend demonstrates safe
native interoperability and is not claimed to outperform NumPy/BLAS. Pure Python
is the fallback when NumPy is unavailable.
Loading