Workflows

June 28, 2026 · View on GitHub

Authoritative Commands

make fmt      # ruff format src/ tests/ examples/ scripts/
make lint     # ruff check src/ tests/ examples/ scripts/
make type     # mypy src/ examples/ scripts/  (examples + scripts gated too, #539)
make test     # pytest --cov=contextweaver --cov-report=term-missing -q
make example  # run all example scripts (includes architectures)
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 + security-policy-check + example + demo
make ci-full  # make ci + floor-deps + tool-smoke (the two isolated-env CI jobs; #710)
make floor-deps # prove declared dependency floors resolve + pass tests (mirrors the floor-deps CI job; needs uv; #710)
make tool-smoke # build the wheel + run the entry point via uvx/pipx (mirrors the Linux tool-run-smoke CI job; needs uv/pipx; #710)
make drift-check  # one gate over every generated-artifact drift check (#522; in `make ci`)
make module-size-check  # enforce the ≤300-line convention, frozen baseline (#456; in `make ci`)
make doc-snippets-check # execute README + curated docs Python snippets (#526; in `make ci`)
make docs     # mkdocs build --clean (docs site — not part of CI)
make docs-serve  # mkdocs serve (live preview)
make benchmark        # run benchmark harness (non-gating; writes benchmarks/results/latest.json)
make benchmark-matrix # benchmark + per-backend × per-size matrix (#208) and per-namespace breakdown (#209)
make scorecard        # render benchmarks/scorecard.md from benchmarks/results/latest.json
make scorecard-check  # verify scorecard.md is up to date (gating CI step; exits non-zero on drift)
make sweep-scoring    # weight sweep for ScoringConfig (#214); writes benchmarks/sweep_scoring.md
make context-rot       # render context-rot demo JSON + docs/assets/context_rot.svg (#349)
make context-rot-check # verify context_rot.svg matches its committed JSON (gating CI step; exits non-zero on drift)
make readme-version-check  # verify README package/comparison/roadmap refs and Python classifiers match sources (gating CI step; #347/#473/#531)
make security-policy-check # verify SECURITY.md supported series + relative links match pyproject.toml (gating CI step; #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 CI step; exits non-zero on drift)
make gateway-scorecard-check  # verify gateway scorecard Markdown matches its committed JSON (gating CI step)
make record-demos-check  # verify committed asciinema casts match demo output (gating CI step)
make smoke-eval  # deterministic, credential-free smoke evaluation (gating CI step on Python 3.12)
make weaver-conformance  # round-trip + JSON-Schema validate the weaver-spec adapter
                         # (fetches schemas from raw.githubusercontent.com; CI runs it as a gate)

As of #474, make ci now mirrors the gating CI checks a contributor can run offline: the consolidated generated-artifact drift gate make drift-check (#522 — schemas, scorecards, recorded demos, llms.txt, context-rot SVG, and the public-API manifest #518), plus make module-size-check (#456), make doc-snippets-check (#526), make readme-version-check (#347/#473/#531), and make security-policy-check (#691). The individual *-check targets still exist for granular use, but you no longer need to remember to run them separately before a PR.

CI-only gates (not in make ci because they need the network or are heavy): make weaver-conformance (fetches schemas) and the docs build job. make smoke-eval (#392/#491) is gating on the Python 3.12 CI cell; the raw benchmark job remains informational. The two gating CI jobs that build isolated environments now have local equivalents — make floor-deps and make tool-smoke (#710), bundled into make ci-full; only the macOS cell of tool-run-smoke stays CI-only.

All targets invoke $(PYTHON) (defaults to python3); override with make <target> PYTHON=python3.11 where no bare python exists (#712).

make ci runs all declared targets in sequence. The CI-only gates above run on every PR regardless; run them locally when the affected integrations change.

Command-Selection Rules

GoalCommand
Quick format checkmake fmt
Quick lint checkmake lint
Full validationmake ci (always — do not skip targets)
Full validation + isolated-env jobsmake ci-full (#710; adds floor-deps + tool-smoke)
Verify dependency floors resolvemake floor-deps (#710; needs uv)
Wheel / entry-point smokemake tool-smoke (#710; needs uv/pipx)
Run a single testpytest tests/test_<module>.py or pytest -k "test_name"
Run all testsmake test
Verify examples workmake example
Interactive demomake demo
Verify recorded demo castsmake record-demos-check
Build docs sitemake docs
Live docs previewmake docs-serve
Run benchmark harnessmake benchmark (non-gating; writes benchmarks/results/latest.json)
Run full per-backend × per-size matrixmake benchmark-matrix (#208 + #209)
Verify gateway benchmark scorecardmake gateway-scorecard-check
Run deterministic smoke evaluationmake smoke-eval (gating on the Python 3.12 CI cell)
Run scoring-weight sweepmake sweep-scoring (#214; writes benchmarks/sweep_scoring.md)
Add an eval for a featurefollow .github/prompts/add-eval.prompt.md (#216)
Regenerate llms.txt / llms-full.txtmake llms (after editing canonical docs)
Check llms.txt / llms-full.txt for driftmake llms-check (exits non-zero if regeneration needed)

Do not use make test alone as a validation gate. Always run make ci before declaring a change complete — it includes example and demo verification that catch integration issues make test misses.

Setup (one-time)

pip install -e ".[dev]"
pre-commit install

Pre-commit hooks run ruff format, ruff check --fix, and file hygiene checks on every commit. Hooks may modify files — re-stage with git add if needed.

Preparing a Release

Before creating the release tag, refresh and commit the deterministic benchmark history for the package version:

make benchmark
python scripts/render_trend.py --snapshot <version> --from benchmarks/results/latest.json
make trend
make trend-check

The publish workflow verifies that benchmarks/results/history/<version>.json exists and that benchmarks/trend.md matches the committed history. Latency is excluded from release snapshots because it is host-dependent.

Adding a Feature

  1. Identify the relevant module (see module map in AGENTS.md).
  2. Modify only the targeted module.
  3. Update protocols.py if adding a new protocol.
  4. Add tests in tests/test_<module>.py.
  5. Run make ci — all declared targets must pass.
  6. Update CHANGELOG.md under ## [Unreleased].
  7. Add Google-style docstrings to any new public APIs.
  8. Update examples/demos if the feature is user-facing.
  9. Update agent-facing docs if the pipeline or public API changed.
  10. If the feature can move recall@k / dropped / dedup_removed / prompt_tokens, follow .github/prompts/add-eval.prompt.md (#216) to extend the gold set or scenarios and regenerate benchmarks/scorecard.md. The sticky CI benchmark-delta comment (#211) surfaces any matrix-cell ⚠️ markers on the PR.

Definition of Done

A change is complete when all of the following are true:

  • make ci passes (all declared targets)
  • CHANGELOG.md updated
  • Google-style docstrings on all new public APIs
  • Type hints on all new public functions and methods
  • Tests added for new functionality
  • Examples/demos updated if the feature is user-facing
  • Agent-facing docs updated if pipeline, public API, or conventions changed

Fixing a Bug

  1. Write a failing test that reproduces the bug.
  2. Fix the bug.
  3. Run make ci.
  4. Update CHANGELOG.md.
  5. If the bug revealed a reusable lesson, record it per the process in lessons-learned.md.

Adding a Store Backend

  1. Implement the store class in src/contextweaver/store/<name>.py.
  2. The class must implement the relevant protocol from protocols.py.
  3. Export from src/contextweaver/store/__init__.py.
  4. Add tests in tests/test_store_<name>.py.
  5. Update StoreBundle if appropriate.

Adding an Adapter

  1. Create the adapter in src/contextweaver/adapters/<protocol>.py.
  2. Pure stateless converter — no state, no core-type leakage.
  3. External format dependencies stay at the adapter boundary.
  4. Add tests in tests/test_adapters.py.
  5. Add an example in examples/.

Deprecating an API

Use the runtime machinery in src/contextweaver/_deprecation.py (issue #517); do not call warnings.warn ad hoc.

  1. Add a Deprecation(name, since, removal, instead) entry to the _SHIMS table in _deprecation.py (the single source of truth). Use the next minor for since and "1.0.0" for removal unless decided otherwise.
  2. At the call site, emit the warning: warn_deprecated("<name>") inside a property/method/branch, or decorate a callable with @deprecated("<name>", since=..., removal=..., instead=...). Keep behavior identical; route in-library callers through a private helper so the canonical path does not trip the warning.
  3. Migrate every in-repo caller (src, examples, docs snippets, tests) off the deprecated surface; intentional shim tests assert the warning with pytest.warns(DeprecationWarning). The filterwarnings gate in pyproject.toml escalates first-party deprecations to errors, so a leftover caller fails CI.
  4. Add the surface to the inventory table in docs/upgrading.md and a CHANGELOG.md "Deprecated" entry naming the replacement.

Documentation-only exception. Do not add a runtime warning when the only call site would live in a module barred from side effects — a re-export-only __init__.py (hard rule: only re-exports) or a pure-data module such as types.py (invariant: no side effects in the data layer), or an internal serialization key on a hot path. Keep the surface a plain alias/accessor, skip steps 1–2 (no _SHIMS entry, no warn_deprecated), and record it as a documentation-only row in docs/upgrading.md. The ToolCard alias is the reference example.

Documentation Governance

When docs must be updated

  • Any PR that changes the context pipeline stages, routing pipeline, or public API.
  • Any PR that adds, removes, or renames a module.
  • Any PR that changes project conventions, commands, or the definition of done.

Who triggers updates

  • The author of the PR is responsible for updating docs in the same PR.
  • Reviewers should check the review checklist for doc-update requirements.

Resolving contradictions

If two docs disagree:

  1. AGENTS.md is authoritative for agent guidance and shared rules.
  2. docs/architecture.md is authoritative for architecture detail.
  3. Makefile is ground truth for command definitions.
  4. Source code is ground truth for implementation details.

Fix the less-authoritative source to match.

Promoting lessons into canonical docs

When a lesson from lessons-learned.md represents a durable pattern:

  1. Add it to the appropriate canonical doc (AGENTS.md, invariants.md, or workflows.md).
  2. Keep the lesson entry but mark it as promoted with a cross-reference.

Avoiding duplicate authority

Each piece of guidance should have exactly one canonical home. Use cross-references instead of copies. Exception: hard rules (the 2 auto-reject items) may be briefly restated in tool-specific override files for visibility, since those files may be the only context an agent loads.

Updating navigation tables

When adding a new canonical doc under docs/agent-context/, update the navigation tables in all three routing files:

  • AGENTS.md (Documentation Map)
  • .github/copilot-instructions.md (Canonical References)
  • .claude/CLAUDE.md (Canonical References)