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
48 changes: 48 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
version: 2

# Automated dependency updates (issue #443).
#
# * pip — keep the declared Python dependency floors and dev toolchain
# current. Minor/patch bumps are grouped into a single weekly PR to limit
# noise; majors open individually so breaking changes get their own review.
# * github-actions — keep workflow action references current. The high-trust
# release path (publish.yml) pins actions to immutable commit SHAs (#468);
# Dependabot bumps the SHA and its `# vX` comment together. Other workflows
# track major tags. See docs/security_tooling.md.

updates:
- package-ecosystem: pip
directory: "/"
schedule:
interval: weekly
day: monday
time: "06:00"
timezone: Etc/UTC
open-pull-requests-limit: 5
groups:
python-minor-patch:
update-types:
- minor
- patch
labels:
- dependencies
commit-message:
prefix: "chore(deps)"

- package-ecosystem: github-actions
directory: "/"
schedule:
interval: weekly
day: monday
time: "06:00"
timezone: Etc/UTC
open-pull-requests-limit: 5
groups:
actions-all:
update-types:
- minor
- patch
labels:
- dependencies
commit-message:
prefix: "chore(ci)"
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ jobs:
# version in pyproject.toml. Stdlib-only; no install required.
run: python scripts/check_readme_version.py

- name: Security-policy drift check (gating, issue #691)
# Fails when SECURITY.md's supported-version table drifts from the
# package version in pyproject.toml, or when a relative link it
# references no longer resolves. Stdlib-only; no install required.
run: python scripts/check_security_policy.py

- name: Weaver-spec conformance
# Round-trip + JSON-Schema validation against the canonical contracts
# published at https://weaver-spec.dev/contracts/v0/. Gating because
Expand Down
50 changes: 50 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: CodeQL

# Static security analysis for the Python sources (issue #689, umbrella #443).
# Runs the default + security-extended query packs on every PR, on pushes to
# main, and on a weekly schedule so newly published queries surface findings
# even when the code is quiet. Findings land in the repository Security tab
# (code scanning). False positives are handled via the documented exception
# process in docs/security_tooling.md (issue #692).

on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
# Monday 07:00 UTC. Off-hours, just after the weekly scorecard/benchmark
# crons, low contention with the gating CI job.
- cron: "0 7 * * 1"

permissions:
contents: read

concurrency:
group: codeql-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
analyze:
name: Analyze (python)
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
# Required for CodeQL to upload results to the code-scanning dashboard.
security-events: write
contents: read
steps:
- uses: actions/checkout@v4

- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: python
# ``security-extended`` adds the broader security query suite on top
# of the default pack; pure-Python project, so no build step.
queries: security-extended

- name: Perform CodeQL analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:python"
61 changes: 61 additions & 0 deletions .github/workflows/ossf-scorecard.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: OpenSSF Scorecard

# OpenSSF Scorecard supply-chain health analysis (issue #552, umbrella #443).
#
# Distinct from the *benchmark* scorecard regenerated by
# scorecard-weekly.yml — that one measures routing recall / token savings.
# This workflow runs the OpenSSF Scorecard checks (branch protection, token
# permissions, pinned dependencies, dangerous workflows, maintained, etc.),
# uploads the SARIF to the code-scanning dashboard, and publishes results so
# the README badge resolves.
#
# Applying for the OpenSSF Best Practices badge is a tracked manual step — see
# docs/security_tooling.md.

on:
branch_protection_rule:
push:
branches: [main]
schedule:
# Monday 08:00 UTC.
- cron: "0 8 * * 1"

permissions:
contents: read

jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
# Upload the results to the code-scanning dashboard.
security-events: write
# Publish results to the OpenSSF REST API so the README badge resolves.
id-token: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false

- name: Run analysis
uses: ossf/scorecard-action@v2
with:
results_file: results.sarif
results_format: sarif
# publish_results enables the public badge endpoint at
# api.securityscorecards.dev (see the README badge).
publish_results: true

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: scorecard-results
path: results.sarif
retention-days: 5

- name: Upload to code-scanning
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
88 changes: 88 additions & 0 deletions .github/workflows/pip-audit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
name: pip-audit

# Dependency vulnerability scan (issue #689, umbrella #443).
#
# Policy (documented in docs/security_tooling.md, issue #692):
# * The CORE runtime dependency set is GATING — a known-vulnerable advisory
# against a core dependency fails the job, because adopters put
# contextweaver in the data path between agents and tools.
# * The DEV/test extra is report-only (continue-on-error): it pulls a large
# transitive tree (crewai, mem0ai, fastmcp, langgraph, langchain-core) that
# would otherwise make the gate noisy and flaky. Findings are still printed
# for triage and recorded in the job summary.
#
# Runs on PRs that touch dependency metadata, on pushes to main, and weekly so
# newly published advisories surface against an otherwise-quiet tree.

on:
push:
branches: [main]
paths:
- "pyproject.toml"
- ".github/workflows/pip-audit.yml"
pull_request:
paths:
- "pyproject.toml"
- ".github/workflows/pip-audit.yml"
schedule:
# Monday 07:30 UTC, after CodeQL.
- cron: "30 7 * * 1"

permissions:
contents: read

concurrency:
group: pip-audit-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
audit-core:
name: Audit core dependencies (gating)
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install core package and pip-audit
run: |
python -m pip install --upgrade pip
pip install -e .
pip install pip-audit

- name: Audit installed environment (gating)
# No --ignore-vuln entries today. Document any future exception here
# with the advisory id and a link to its tracking issue, per the
# exception process in docs/security_tooling.md.
run: pip-audit --progress-spinner off

audit-dev:
name: Audit dev extra (report-only)
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install dev extra and pip-audit
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"
pip install pip-audit

- name: Audit installed environment (report-only)
# Report-only: the dev tree carries known advisories that should not gate
# the PR (that is what the gating ``audit-core`` job is for). ``|| true``
# keeps this check green so it never reads as a blocking failure, while
# the findings stay visible in the log for triage. Promote a specific
# advisory to gating by fixing it in ``audit-core``, per
# docs/security_tooling.md.
run: pip-audit --progress-spinner off || true
70 changes: 65 additions & 5 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
name: Publish to PyPI and MCP Registry

# Release-path actions are pinned to immutable commit SHAs (issue #468). This is
# the high-trust workflow — it holds ``id-token: write`` and
# ``attestations: write`` — so a moving tag here is a supply-chain risk. The
# ``# vX`` comments record the tag each SHA was resolved from; Dependabot's
# ``github-actions`` updater (.github/dependabot.yml) keeps the SHAs current.

on:
release:
types: [published]
Expand All @@ -8,16 +14,62 @@ permissions:
contents: read

jobs:
verify:
# Release-integrity gate (issue #468). A release publishes straight to PyPI
# and the MCP Registry over OIDC, so this job is the last chance to catch a
# mis-tagged or untested release before it is immutable on PyPI. It proves:
# 1. the release tag matches the package version (no ``v0.16.0`` tag
# shipping a ``0.15.0`` artifact);
# 2. the package builds and its metadata passes ``twine check``;
# 3. the gating test suite is green at the released commit.
# ``publish`` depends on this job, so a red gate blocks the upload.
name: Verify release integrity
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"

- name: Check release tag matches package version
# ``GITHUB_REF_NAME`` is the tag the release was cut from (``vX.Y.Z``).
# The package version is the single source of truth in pyproject.toml.
run: |
tag="${GITHUB_REF_NAME}"
version="$(python scripts/check_readme_version.py --print-version)"
if [ "$tag" != "v$version" ]; then
echo "::error::Release tag '$tag' does not match package version 'v$version' (pyproject.toml)." >&2
exit 1
fi
echo "Release tag '$tag' matches package version 'v$version'."

- name: Install package and test/build tooling
run: pip install -e ".[dev]" build twine

- name: Pre-publish test suite (gating)
run: pytest -q

- name: Build and check distribution metadata
run: |
python -m build
twine check dist/*

publish:
needs: verify
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write # Required for Trusted Publisher (OIDC)
id-token: write # Required for Trusted Publisher (OIDC)
contents: read
attestations: write # Required to attach build-provenance attestations (issue #690)
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"

Expand All @@ -27,8 +79,16 @@ jobs:
- name: Build sdist and wheel
run: python -m build

- name: Attest build provenance
# Generates a signed, verifiable provenance attestation for each built
# artifact (issue #690). Verifiable later with ``gh attestation verify
# <file> --repo dgenio/contextweaver``.
uses: actions/attest-build-provenance@96b4a1ef7235a096b17240c259729fdd70c83d45 # v2
with:
subject-path: "dist/*"

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1

publish-mcp-registry:
name: Publish to MCP Registry
Expand All @@ -38,7 +98,7 @@ jobs:
contents: read
id-token: write # Required for MCP Registry GitHub OIDC authentication
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Install mcp-publisher
run: |
Expand Down
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ make test # python -m pytest --cov=contextweaver --cov-report=term-missing -
make example # run all example scripts (includes architectures via the umbrella target)
make architectures # run reference architecture scripts under examples/architectures/
make demo # python -m contextweaver demo
make ci # fmt + lint + type + test + drift-check + module-size-check + doc-snippets-check + readme-version-check + example + demo
make ci # fmt + lint + type + test + drift-check + module-size-check + doc-snippets-check + readme-version-check + security-policy-check + example + demo
make docs # mkdocs build --clean (docs site)
make docs-serve # mkdocs serve (live preview)
make benchmark # run benchmark harness (non-gating; writes benchmarks/results/latest.json)
Expand All @@ -236,6 +236,7 @@ make sweep-scoring # weight sweep for ScoringConfig (#214); writes benchmarks
make context-rot # render the context-rot demo: benchmarks/results/context_rot.json + docs/assets/context_rot.svg (#349)
make context-rot-check # verify context_rot.svg matches its committed JSON (gating in CI; exits non-zero on drift)
make readme-version-check # verify README version references match pyproject.toml (gating in CI; #347)
make security-policy-check # verify SECURITY.md supported series + links match pyproject.toml (gating in CI; #691)
make llms # regenerate llms.txt and llms-full.txt from canonical docs
make llms-check # verify llms.txt and llms-full.txt are up to date (gating in CI; #389)
make weaver-conformance # round-trip + JSON-Schema validate the weaver-spec adapter (CI gating, fetches schemas)
Expand Down
Loading
Loading