Sync ctx a4e14d3
Browse filesGitHub commit: a4e14d3ac2e5af5324d58ff178ce1a9da78cda63
This view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +2 -4
- .githooks/pre-commit +47 -44
- .github/workflows/clean-host-contract.yml +35 -35
- .github/workflows/docs.yml +88 -73
- .github/workflows/huggingface-sync.yml +108 -0
- .github/workflows/publish.yml +481 -475
- .github/workflows/test.yml +750 -704
- .github/workflows/xdist-experiment.yml +90 -90
- .gitignore +103 -103
- .hypothesis/.gitignore +0 -9
- .hypothesis/constants/0019c6a267d025e1 +0 -4
- .hypothesis/constants/004e722d2b39195a +0 -4
- .hypothesis/constants/009298dc5190c8bd +0 -4
- .hypothesis/constants/010258c38047fd64 +0 -4
- .hypothesis/constants/017531f0e829ce4f +0 -4
- .hypothesis/constants/0197b3c42bd36715 +0 -4
- .hypothesis/constants/0199ac9602548c81 +0 -4
- .hypothesis/constants/01e01103d68a3193 +0 -4
- .hypothesis/constants/0213343f1393a3fa +0 -4
- .hypothesis/constants/031f85f1c11bb00b +0 -4
- .hypothesis/constants/03a0bc33c3be41ac +0 -4
- .hypothesis/constants/03d65cf548ce3b23 +0 -4
- .hypothesis/constants/041b82baf95116f0 +0 -4
- .hypothesis/constants/044c9664c732c0e2 +0 -4
- .hypothesis/constants/0570a7c52cd987f9 +0 -4
- .hypothesis/constants/0628f2618034526e +0 -4
- .hypothesis/constants/068c9e16d4886008 +0 -4
- .hypothesis/constants/06a795a0e79c102f +0 -4
- .hypothesis/constants/072d43d60d40fd51 +0 -4
- .hypothesis/constants/0736e2d4acddd01a +0 -4
- .hypothesis/constants/07475514dc0bbadb +0 -4
- .hypothesis/constants/075aa0dd31e12c03 +0 -4
- .hypothesis/constants/084c48810f649441 +0 -4
- .hypothesis/constants/08a45b14b7a1aa82 +0 -4
- .hypothesis/constants/0964dd49257e9b00 +0 -4
- .hypothesis/constants/0967afddd3cfffcc +0 -4
- .hypothesis/constants/09a20f6b52e6dcc7 +0 -4
- .hypothesis/constants/09ae28b390a241d0 +0 -4
- .hypothesis/constants/09bba2fe6bc8792d +0 -4
- .hypothesis/constants/09e51e4d7e8d7e87 +0 -4
- .hypothesis/constants/0a6403dce3b6e280 +0 -4
- .hypothesis/constants/0ac765fb060691b5 +0 -4
- .hypothesis/constants/0b1d2411e5eaa76a +0 -4
- .hypothesis/constants/0bb70d1826311e40 +0 -4
- .hypothesis/constants/0bc3b2f77e74ecd9 +0 -4
- .hypothesis/constants/0c23327e39eaa492 +0 -4
- .hypothesis/constants/0c347c1a609186cf +0 -4
- .hypothesis/constants/0c7b341cd0f9acb2 +0 -4
- .hypothesis/constants/0cfa12dfbf9e9abb +0 -4
- .hypothesis/constants/0d65a410db824952 +0 -4
.gitattributes
CHANGED
|
@@ -1,4 +1,2 @@
|
|
| 1 |
-
graph/wiki-graph.tar.gz filter=lfs diff=lfs merge=lfs -text
|
| 2 |
-
graph/wiki-graph-runtime.tar.gz filter=lfs diff=lfs merge=lfs -text
|
| 3 |
-
.hypothesis/unicode_data/14.0.0/charmap.json.gz filter=lfs diff=lfs merge=lfs -text
|
| 4 |
-
graph/skills-sh-catalog.json.gz filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 1 |
+
graph/wiki-graph.tar.gz filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
graph/wiki-graph-runtime.tar.gz filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
.githooks/pre-commit
CHANGED
|
@@ -1,44 +1,47 @@
|
|
| 1 |
-
#!/usr/bin/env bash
|
| 2 |
-
# pre-commit -- refresh cheap repo stats without leaking local wiki state.
|
| 3 |
-
#
|
| 4 |
-
# Enable with: git config core.hooksPath .githooks
|
| 5 |
-
# Disable with: git config --unset core.hooksPath
|
| 6 |
-
#
|
| 7 |
-
# This hook intentionally does not rebuild graph/wiki artifacts from
|
| 8 |
-
# ~/.claude/skill-wiki. That directory can contain private local entities.
|
| 9 |
-
# Release graph artifacts must be created by explicit, reproducible commands
|
| 10 |
-
# and staged by the user or CI.
|
| 11 |
-
|
| 12 |
-
set -u
|
| 13 |
-
|
| 14 |
-
REPO_ROOT="$(git rev-parse --show-toplevel)"
|
| 15 |
-
UPDATER="$REPO_ROOT/src/update_repo_stats.py"
|
| 16 |
-
|
| 17 |
-
PYTHON="${PYTHON:-python3}"
|
| 18 |
-
command -v "$PYTHON" >/dev/null 2>&1 || PYTHON="python"
|
| 19 |
-
STATS_TIMEOUT="${CTX_REPO_STATS_TIMEOUT:-240s}"
|
| 20 |
-
|
| 21 |
-
STAGED_CHANGES=$(git diff --cached --name-only)
|
| 22 |
-
log() { echo "[pre-commit] $*" >&2; }
|
| 23 |
-
|
| 24 |
-
if echo "$STAGED_CHANGES" | grep -qE '^(README\.md|docs/index\.md|graph/(wiki-graph\.tar\.gz|communities\.json|skills-sh-catalog\.json\.gz)|src/update_repo_stats\.py)$' ; then
|
| 25 |
-
if [[ -f "$UPDATER" ]]; then
|
| 26 |
-
STATS_CMD=("$PYTHON" "$UPDATER")
|
| 27 |
-
if
|
| 28 |
-
STATS_CMD=(
|
| 29 |
-
fi
|
| 30 |
-
if
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
log "
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
# pre-commit -- refresh cheap repo stats without leaking local wiki state.
|
| 3 |
+
#
|
| 4 |
+
# Enable with: git config core.hooksPath .githooks
|
| 5 |
+
# Disable with: git config --unset core.hooksPath
|
| 6 |
+
#
|
| 7 |
+
# This hook intentionally does not rebuild graph/wiki artifacts from
|
| 8 |
+
# ~/.claude/skill-wiki. That directory can contain private local entities.
|
| 9 |
+
# Release graph artifacts must be created by explicit, reproducible commands
|
| 10 |
+
# and staged by the user or CI.
|
| 11 |
+
|
| 12 |
+
set -u
|
| 13 |
+
|
| 14 |
+
REPO_ROOT="$(git rev-parse --show-toplevel)"
|
| 15 |
+
UPDATER="$REPO_ROOT/src/update_repo_stats.py"
|
| 16 |
+
|
| 17 |
+
PYTHON="${PYTHON:-python3}"
|
| 18 |
+
command -v "$PYTHON" >/dev/null 2>&1 || PYTHON="python"
|
| 19 |
+
STATS_TIMEOUT="${CTX_REPO_STATS_TIMEOUT:-240s}"
|
| 20 |
+
|
| 21 |
+
STAGED_CHANGES=$(git diff --cached --name-only)
|
| 22 |
+
log() { echo "[pre-commit] $*" >&2; }
|
| 23 |
+
|
| 24 |
+
if echo "$STAGED_CHANGES" | grep -qE '^(README\.md|docs/(index|knowledge-graph|catalog)\.md|graph/(wiki-graph\.tar\.gz|communities\.json|skills-sh-catalog\.json\.gz)|src/update_repo_stats\.py|src/tests/.*\.py)$' ; then
|
| 25 |
+
if [[ -f "$UPDATER" ]]; then
|
| 26 |
+
STATS_CMD=("$PYTHON" "$UPDATER")
|
| 27 |
+
if echo "$STAGED_CHANGES" | grep -qE '^src/tests/.*\.py$' ; then
|
| 28 |
+
STATS_CMD=(env CTX_UPDATE_REPO_STATS_LIVE_TESTS=1 "${STATS_CMD[@]}")
|
| 29 |
+
fi
|
| 30 |
+
if command -v timeout >/dev/null 2>&1; then
|
| 31 |
+
STATS_CMD=(timeout "$STATS_TIMEOUT" "${STATS_CMD[@]}")
|
| 32 |
+
fi
|
| 33 |
+
if ! "${STATS_CMD[@]}" 2> >(sed 's/^/[pre-commit stats] /' >&2); then
|
| 34 |
+
log "stats updater failed or timed out; continuing without README refresh"
|
| 35 |
+
elif ! git diff --quiet -- README.md docs/index.md docs/knowledge-graph.md docs/catalog.md; then
|
| 36 |
+
git add README.md docs/index.md docs/knowledge-graph.md docs/catalog.md
|
| 37 |
+
log "README.md, docs/index.md, docs/knowledge-graph.md, and docs/catalog.md refreshed and re-staged"
|
| 38 |
+
fi
|
| 39 |
+
fi
|
| 40 |
+
fi
|
| 41 |
+
|
| 42 |
+
if echo "$STAGED_CHANGES" | grep -qE '^(skills|agents|harnesses|imported-skills|graph/skills-sh-catalog\.json\.gz)' ; then
|
| 43 |
+
log "entity source changed; graph/wiki tarball not rebuilt by pre-commit"
|
| 44 |
+
log "run the explicit graph release command and stage graph/wiki-graph.tar.gz if needed"
|
| 45 |
+
fi
|
| 46 |
+
|
| 47 |
+
exit 0
|
.github/workflows/clean-host-contract.yml
CHANGED
|
@@ -1,35 +1,35 @@
|
|
| 1 |
-
name: Clean Host Contract
|
| 2 |
-
|
| 3 |
-
on:
|
| 4 |
-
workflow_dispatch:
|
| 5 |
-
schedule:
|
| 6 |
-
- cron: "17 3 * * 1"
|
| 7 |
-
|
| 8 |
-
env:
|
| 9 |
-
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
| 10 |
-
|
| 11 |
-
permissions:
|
| 12 |
-
contents: read
|
| 13 |
-
|
| 14 |
-
jobs:
|
| 15 |
-
clean-host-contract:
|
| 16 |
-
name: Clean wheel install and A-Z contract
|
| 17 |
-
runs-on: ubuntu-latest
|
| 18 |
-
timeout-minutes: 25
|
| 19 |
-
|
| 20 |
-
steps:
|
| 21 |
-
- name: Checkout
|
| 22 |
-
uses: actions/checkout@v5
|
| 23 |
-
|
| 24 |
-
- name: Set up Python 3.12
|
| 25 |
-
uses: actions/setup-python@v6
|
| 26 |
-
with:
|
| 27 |
-
python-version: "3.12"
|
| 28 |
-
cache: pip
|
| 29 |
-
cache-dependency-path: pyproject.toml
|
| 30 |
-
|
| 31 |
-
- name: Upgrade pip
|
| 32 |
-
run: python -m pip install --upgrade pip
|
| 33 |
-
|
| 34 |
-
- name: Run clean-host contract
|
| 35 |
-
run: python scripts/clean_host_contract.py --fast
|
|
|
|
| 1 |
+
name: Clean Host Contract
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
workflow_dispatch:
|
| 5 |
+
schedule:
|
| 6 |
+
- cron: "17 3 * * 1"
|
| 7 |
+
|
| 8 |
+
env:
|
| 9 |
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
| 10 |
+
|
| 11 |
+
permissions:
|
| 12 |
+
contents: read
|
| 13 |
+
|
| 14 |
+
jobs:
|
| 15 |
+
clean-host-contract:
|
| 16 |
+
name: Clean wheel install and A-Z contract
|
| 17 |
+
runs-on: ubuntu-latest
|
| 18 |
+
timeout-minutes: 25
|
| 19 |
+
|
| 20 |
+
steps:
|
| 21 |
+
- name: Checkout
|
| 22 |
+
uses: actions/checkout@v5
|
| 23 |
+
|
| 24 |
+
- name: Set up Python 3.12
|
| 25 |
+
uses: actions/setup-python@v6
|
| 26 |
+
with:
|
| 27 |
+
python-version: "3.12"
|
| 28 |
+
cache: pip
|
| 29 |
+
cache-dependency-path: pyproject.toml
|
| 30 |
+
|
| 31 |
+
- name: Upgrade pip
|
| 32 |
+
run: python -m pip install --upgrade pip
|
| 33 |
+
|
| 34 |
+
- name: Run clean-host contract
|
| 35 |
+
run: python scripts/clean_host_contract.py --fast
|
.github/workflows/docs.yml
CHANGED
|
@@ -1,73 +1,88 @@
|
|
| 1 |
-
name: Deploy docs to GitHub Pages
|
| 2 |
-
|
| 3 |
-
# Build the MkDocs Material site and publish to GitHub Pages on every push
|
| 4 |
-
# to main. Also deployable on demand via the workflow_dispatch trigger.
|
| 5 |
-
|
| 6 |
-
on:
|
| 7 |
-
push:
|
| 8 |
-
branches:
|
| 9 |
-
- main
|
| 10 |
-
paths:
|
| 11 |
-
- "docs/**"
|
| 12 |
-
- "mkdocs.yml"
|
| 13 |
-
- "requirements-docs.txt"
|
| 14 |
-
- ".github/workflows/docs.yml"
|
| 15 |
-
workflow_dispatch:
|
| 16 |
-
|
| 17 |
-
env:
|
| 18 |
-
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
| 19 |
-
|
| 20 |
-
permissions:
|
| 21 |
-
contents: read
|
| 22 |
-
|
| 23 |
-
# Allow only one concurrent deploy; cancel in-progress runs on a new push.
|
| 24 |
-
concurrency:
|
| 25 |
-
group: "pages"
|
| 26 |
-
cancel-in-progress: true
|
| 27 |
-
|
| 28 |
-
jobs:
|
| 29 |
-
build:
|
| 30 |
-
name: Build site
|
| 31 |
-
runs-on: ubuntu-latest
|
| 32 |
-
steps:
|
| 33 |
-
- name: Checkout
|
| 34 |
-
uses: actions/checkout@v5
|
| 35 |
-
with:
|
| 36 |
-
fetch-depth: 0 # Required by mkdocs git-revision plugins if added.
|
| 37 |
-
|
| 38 |
-
- name: Set up Python
|
| 39 |
-
uses: actions/setup-python@v6
|
| 40 |
-
with:
|
| 41 |
-
python-version: "3.11"
|
| 42 |
-
cache: "pip"
|
| 43 |
-
cache-dependency-path: requirements-docs.txt
|
| 44 |
-
|
| 45 |
-
- name: Install docs dependencies
|
| 46 |
-
run: |
|
| 47 |
-
python -m pip install --upgrade pip
|
| 48 |
-
python -m pip install -r requirements-docs.txt
|
| 49 |
-
|
| 50 |
-
- name: Build site (strict)
|
| 51 |
-
run: |
|
| 52 |
-
python -m mkdocs build --strict
|
| 53 |
-
|
| 54 |
-
- name:
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Deploy docs to GitHub Pages
|
| 2 |
+
|
| 3 |
+
# Build the MkDocs Material site and publish to GitHub Pages on every push
|
| 4 |
+
# to main. Also deployable on demand via the workflow_dispatch trigger.
|
| 5 |
+
|
| 6 |
+
on:
|
| 7 |
+
push:
|
| 8 |
+
branches:
|
| 9 |
+
- main
|
| 10 |
+
paths:
|
| 11 |
+
- "docs/**"
|
| 12 |
+
- "mkdocs.yml"
|
| 13 |
+
- "requirements-docs.txt"
|
| 14 |
+
- ".github/workflows/docs.yml"
|
| 15 |
+
workflow_dispatch:
|
| 16 |
+
|
| 17 |
+
env:
|
| 18 |
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
| 19 |
+
|
| 20 |
+
permissions:
|
| 21 |
+
contents: read
|
| 22 |
+
|
| 23 |
+
# Allow only one concurrent deploy; cancel in-progress runs on a new push.
|
| 24 |
+
concurrency:
|
| 25 |
+
group: "pages"
|
| 26 |
+
cancel-in-progress: true
|
| 27 |
+
|
| 28 |
+
jobs:
|
| 29 |
+
build:
|
| 30 |
+
name: Build site
|
| 31 |
+
runs-on: ubuntu-latest
|
| 32 |
+
steps:
|
| 33 |
+
- name: Checkout
|
| 34 |
+
uses: actions/checkout@v5
|
| 35 |
+
with:
|
| 36 |
+
fetch-depth: 0 # Required by mkdocs git-revision plugins if added.
|
| 37 |
+
|
| 38 |
+
- name: Set up Python
|
| 39 |
+
uses: actions/setup-python@v6
|
| 40 |
+
with:
|
| 41 |
+
python-version: "3.11"
|
| 42 |
+
cache: "pip"
|
| 43 |
+
cache-dependency-path: requirements-docs.txt
|
| 44 |
+
|
| 45 |
+
- name: Install docs dependencies
|
| 46 |
+
run: |
|
| 47 |
+
python -m pip install --upgrade pip
|
| 48 |
+
python -m pip install -r requirements-docs.txt
|
| 49 |
+
|
| 50 |
+
- name: Build site (strict)
|
| 51 |
+
run: |
|
| 52 |
+
python -m mkdocs build --strict
|
| 53 |
+
|
| 54 |
+
- name: Archive Pages artifact
|
| 55 |
+
run: |
|
| 56 |
+
tar \
|
| 57 |
+
--dereference --hard-dereference \
|
| 58 |
+
--directory site \
|
| 59 |
+
-cvf "$RUNNER_TEMP/artifact.tar" \
|
| 60 |
+
--exclude=.git \
|
| 61 |
+
--exclude=.github \
|
| 62 |
+
--exclude=.[^/]* \
|
| 63 |
+
.
|
| 64 |
+
|
| 65 |
+
- name: Upload Pages artifact
|
| 66 |
+
uses: actions/upload-artifact@v4
|
| 67 |
+
with:
|
| 68 |
+
name: github-pages
|
| 69 |
+
path: ${{ runner.temp }}/artifact.tar
|
| 70 |
+
retention-days: 1
|
| 71 |
+
if-no-files-found: error
|
| 72 |
+
overwrite: true
|
| 73 |
+
|
| 74 |
+
deploy:
|
| 75 |
+
name: Deploy to GitHub Pages
|
| 76 |
+
needs: build
|
| 77 |
+
runs-on: ubuntu-latest
|
| 78 |
+
permissions:
|
| 79 |
+
contents: read
|
| 80 |
+
pages: write
|
| 81 |
+
id-token: write
|
| 82 |
+
environment:
|
| 83 |
+
name: github-pages
|
| 84 |
+
url: ${{ steps.deployment.outputs.page_url }}
|
| 85 |
+
steps:
|
| 86 |
+
- name: Deploy
|
| 87 |
+
id: deployment
|
| 88 |
+
uses: actions/deploy-pages@v5
|
.github/workflows/huggingface-sync.yml
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Sync Hugging Face
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
push:
|
| 5 |
+
branches:
|
| 6 |
+
- main
|
| 7 |
+
workflow_dispatch:
|
| 8 |
+
|
| 9 |
+
env:
|
| 10 |
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
| 11 |
+
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
| 12 |
+
|
| 13 |
+
permissions:
|
| 14 |
+
contents: read
|
| 15 |
+
|
| 16 |
+
concurrency:
|
| 17 |
+
group: "huggingface-sync-main"
|
| 18 |
+
cancel-in-progress: true
|
| 19 |
+
|
| 20 |
+
jobs:
|
| 21 |
+
sync:
|
| 22 |
+
name: Sync model repo
|
| 23 |
+
runs-on: ubuntu-latest
|
| 24 |
+
timeout-minutes: 60
|
| 25 |
+
steps:
|
| 26 |
+
- name: Checkout
|
| 27 |
+
uses: actions/checkout@v5
|
| 28 |
+
with:
|
| 29 |
+
fetch-depth: 0
|
| 30 |
+
lfs: false
|
| 31 |
+
|
| 32 |
+
- name: Skip when HF_TOKEN is not configured
|
| 33 |
+
if: ${{ env.HF_TOKEN == '' }}
|
| 34 |
+
run: |
|
| 35 |
+
echo "::notice title=Hugging Face sync skipped::Set the HF_TOKEN repository secret to publish Stevesolun/ctx automatically."
|
| 36 |
+
|
| 37 |
+
- name: Classify sync scope
|
| 38 |
+
id: scope
|
| 39 |
+
if: ${{ env.HF_TOKEN != '' }}
|
| 40 |
+
shell: bash
|
| 41 |
+
env:
|
| 42 |
+
BEFORE_SHA: ${{ github.event.before }}
|
| 43 |
+
EVENT_NAME: ${{ github.event_name }}
|
| 44 |
+
HEAD_SHA: ${{ github.sha }}
|
| 45 |
+
run: |
|
| 46 |
+
set -euo pipefail
|
| 47 |
+
if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then
|
| 48 |
+
echo "sync_mode=full" >> "$GITHUB_OUTPUT"
|
| 49 |
+
exit 0
|
| 50 |
+
fi
|
| 51 |
+
BASE="$BEFORE_SHA"
|
| 52 |
+
if [[ -z "$BASE" || "$BASE" =~ ^0+$ ]] || ! git cat-file -e "$BASE^{commit}" 2>/dev/null; then
|
| 53 |
+
BASE="$(git rev-parse "$HEAD_SHA^")"
|
| 54 |
+
fi
|
| 55 |
+
git diff --name-only "$BASE" "$HEAD_SHA" > changed-files.txt
|
| 56 |
+
python - <<'PY' >> "$GITHUB_OUTPUT"
|
| 57 |
+
from pathlib import Path
|
| 58 |
+
|
| 59 |
+
files = [
|
| 60 |
+
line.strip().replace("\\", "/")
|
| 61 |
+
for line in Path("changed-files.txt").read_text(encoding="utf-8").splitlines()
|
| 62 |
+
if line.strip()
|
| 63 |
+
]
|
| 64 |
+
card_only_prefixes = (".github/", "docs/", "src/tests/")
|
| 65 |
+
card_only_files = {"README.md", "CHANGELOG.md"}
|
| 66 |
+
card_only = bool(files) and all(
|
| 67 |
+
path in card_only_files or path.startswith(card_only_prefixes)
|
| 68 |
+
for path in files
|
| 69 |
+
)
|
| 70 |
+
print(f"sync_mode={'card' if card_only else 'full'}")
|
| 71 |
+
PY
|
| 72 |
+
|
| 73 |
+
- name: Hydrate required graph artifacts from release assets
|
| 74 |
+
if: ${{ env.HF_TOKEN != '' && steps.scope.outputs.sync_mode == 'full' }}
|
| 75 |
+
env:
|
| 76 |
+
GH_TOKEN: ${{ github.token }}
|
| 77 |
+
run: |
|
| 78 |
+
latest_tag="$(gh release view --repo "$GITHUB_REPOSITORY" --json tagName --jq .tagName)"
|
| 79 |
+
echo "Hydrating graph artifacts from release ${latest_tag}"
|
| 80 |
+
rm -f graph/wiki-graph.tar.gz graph/wiki-graph-runtime.tar.gz graph/skills-sh-catalog.json.gz
|
| 81 |
+
gh release download "$latest_tag" --repo "$GITHUB_REPOSITORY" --dir graph --pattern wiki-graph.tar.gz
|
| 82 |
+
gh release download "$latest_tag" --repo "$GITHUB_REPOSITORY" --dir graph --pattern wiki-graph-runtime.tar.gz
|
| 83 |
+
gh release download "$latest_tag" --repo "$GITHUB_REPOSITORY" --dir graph --pattern skills-sh-catalog.json.gz
|
| 84 |
+
|
| 85 |
+
- name: Set up Python
|
| 86 |
+
if: ${{ env.HF_TOKEN != '' }}
|
| 87 |
+
uses: actions/setup-python@v6
|
| 88 |
+
with:
|
| 89 |
+
python-version: "3.11"
|
| 90 |
+
cache: "pip"
|
| 91 |
+
cache-dependency-path: pyproject.toml
|
| 92 |
+
|
| 93 |
+
- name: Install sync dependencies
|
| 94 |
+
if: ${{ env.HF_TOKEN != '' }}
|
| 95 |
+
run: |
|
| 96 |
+
python -m pip install --upgrade pip
|
| 97 |
+
python -m pip install -e ".[dev]" huggingface_hub
|
| 98 |
+
|
| 99 |
+
- name: Sync Hugging Face model repo
|
| 100 |
+
if: ${{ env.HF_TOKEN != '' }}
|
| 101 |
+
env:
|
| 102 |
+
SYNC_MODE: ${{ steps.scope.outputs.sync_mode }}
|
| 103 |
+
run: |
|
| 104 |
+
if [[ "$SYNC_MODE" == "card" ]]; then
|
| 105 |
+
python scripts/sync_huggingface.py --repo . --repo-id Stevesolun/ctx --repo-type model --card-only
|
| 106 |
+
else
|
| 107 |
+
python scripts/sync_huggingface.py --repo . --repo-id Stevesolun/ctx --repo-type model
|
| 108 |
+
fi
|
.github/workflows/publish.yml
CHANGED
|
@@ -1,475 +1,481 @@
|
|
| 1 |
-
name: Publish to PyPI
|
| 2 |
-
|
| 3 |
-
# Publish claude-ctx to PyPI on every tag that looks like a version
|
| 4 |
-
# (v0.5.0, v0.5.0-rc1, v1.0.0, …). Uses PyPI Trusted Publishing —
|
| 5 |
-
# no API token needed once the pending publisher is configured at
|
| 6 |
-
# https://pypi.org/manage/account/publishing/.
|
| 7 |
-
#
|
| 8 |
-
# Also deployable on demand to TestPyPI via workflow_dispatch. Production
|
| 9 |
-
# PyPI publishes must use a version tag push so graph release assets are
|
| 10 |
-
# uploaded before the wheel is published.
|
| 11 |
-
|
| 12 |
-
on:
|
| 13 |
-
push:
|
| 14 |
-
tags:
|
| 15 |
-
- "v*"
|
| 16 |
-
workflow_dispatch:
|
| 17 |
-
inputs:
|
| 18 |
-
repository:
|
| 19 |
-
description: "PyPI repository (pypi or testpypi)"
|
| 20 |
-
required: true
|
| 21 |
-
default: "pypi"
|
| 22 |
-
type: choice
|
| 23 |
-
options:
|
| 24 |
-
- pypi
|
| 25 |
-
- testpypi
|
| 26 |
-
|
| 27 |
-
env:
|
| 28 |
-
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
| 29 |
-
|
| 30 |
-
permissions:
|
| 31 |
-
contents: read
|
| 32 |
-
|
| 33 |
-
jobs:
|
| 34 |
-
build:
|
| 35 |
-
name: Build sdist + wheel
|
| 36 |
-
runs-on: ubuntu-latest
|
| 37 |
-
permissions:
|
| 38 |
-
contents: read
|
| 39 |
-
outputs:
|
| 40 |
-
graph_assets_available: ${{ steps.
|
| 41 |
-
steps:
|
| 42 |
-
- name: Checkout
|
| 43 |
-
uses: actions/checkout@v5
|
| 44 |
-
with:
|
| 45 |
-
lfs: false
|
| 46 |
-
|
| 47 |
-
- name: Set up Python
|
| 48 |
-
uses: actions/setup-python@v6
|
| 49 |
-
with:
|
| 50 |
-
python-version: "3.12"
|
| 51 |
-
|
| 52 |
-
- name: Install release tooling
|
| 53 |
-
run: |
|
| 54 |
-
python -m pip install --upgrade pip
|
| 55 |
-
python -m pip install ".[dev]" build packaging twine
|
| 56 |
-
|
| 57 |
-
- name: Validate release target
|
| 58 |
-
run: |
|
| 59 |
-
python - <<'PY'
|
| 60 |
-
import os
|
| 61 |
-
import tomllib
|
| 62 |
-
from packaging.version import Version
|
| 63 |
-
|
| 64 |
-
event = os.environ["GITHUB_EVENT_NAME"]
|
| 65 |
-
target_repository = os.environ.get("INPUT_REPOSITORY", "pypi")
|
| 66 |
-
ref_type = os.environ.get("GITHUB_REF_TYPE", "")
|
| 67 |
-
ref_name = os.environ["GITHUB_REF_NAME"]
|
| 68 |
-
|
| 69 |
-
if event == "workflow_dispatch" and target_repository == "pypi":
|
| 70 |
-
raise SystemExit(
|
| 71 |
-
"Manual PyPI publish is disabled; push a version tag so "
|
| 72 |
-
"graph release assets are uploaded before publishing"
|
| 73 |
-
)
|
| 74 |
-
if event == "workflow_dispatch" and target_repository == "testpypi":
|
| 75 |
-
print(f"manual TestPyPI publish allowed from {ref_type}:{ref_name}")
|
| 76 |
-
raise SystemExit(0)
|
| 77 |
-
|
| 78 |
-
if ref_type != "tag":
|
| 79 |
-
raise SystemExit(
|
| 80 |
-
"PyPI release must run from a version tag; "
|
| 81 |
-
f"got {ref_type}:{ref_name}"
|
| 82 |
-
)
|
| 83 |
-
tag = ref_name
|
| 84 |
-
if not tag.startswith("v"):
|
| 85 |
-
raise SystemExit(f"release tag must start with v: {tag}")
|
| 86 |
-
|
| 87 |
-
with open("pyproject.toml", "rb") as fh:
|
| 88 |
-
package_version = tomllib.load(fh)["project"]["version"]
|
| 89 |
-
|
| 90 |
-
tag_version = str(Version(tag[1:]))
|
| 91 |
-
normalized_package_version = str(Version(package_version))
|
| 92 |
-
if tag_version != normalized_package_version:
|
| 93 |
-
raise SystemExit(
|
| 94 |
-
f"tag {tag!r} does not match pyproject version {package_version!r}"
|
| 95 |
-
)
|
| 96 |
-
print(f"release version {package_version} matches tag {tag}")
|
| 97 |
-
PY
|
| 98 |
-
env:
|
| 99 |
-
INPUT_REPOSITORY: ${{ github.event.inputs.repository || 'pypi' }}
|
| 100 |
-
|
| 101 |
-
- name: Reject already published PyPI version
|
| 102 |
-
run: |
|
| 103 |
-
python - <<'PY'
|
| 104 |
-
import os
|
| 105 |
-
import tomllib
|
| 106 |
-
import urllib.error
|
| 107 |
-
import urllib.request
|
| 108 |
-
|
| 109 |
-
target_repository = os.environ.get("INPUT_REPOSITORY", "pypi")
|
| 110 |
-
if target_repository != "pypi":
|
| 111 |
-
print(f"skipping PyPI reuse check for {target_repository}")
|
| 112 |
-
raise SystemExit(0)
|
| 113 |
-
|
| 114 |
-
with open("pyproject.toml", "rb") as fh:
|
| 115 |
-
project = tomllib.load(fh)["project"]
|
| 116 |
-
|
| 117 |
-
name = project["name"]
|
| 118 |
-
package_version = project["version"]
|
| 119 |
-
url = f"https://pypi.org/pypi/{name}/{package_version}/json"
|
| 120 |
-
try:
|
| 121 |
-
with urllib.request.urlopen(url, timeout=15):
|
| 122 |
-
raise SystemExit(
|
| 123 |
-
f"{name} {package_version} already exists on PyPI; "
|
| 124 |
-
"bump the version before publishing"
|
| 125 |
-
)
|
| 126 |
-
except urllib.error.HTTPError as exc:
|
| 127 |
-
if exc.code == 404:
|
| 128 |
-
print(f"{name} {package_version} is not present on PyPI")
|
| 129 |
-
raise SystemExit(0)
|
| 130 |
-
raise
|
| 131 |
-
PY
|
| 132 |
-
env:
|
| 133 |
-
INPUT_REPOSITORY: ${{ github.event.inputs.repository || 'pypi' }}
|
| 134 |
-
|
| 135 |
-
- name: Resolve release graph
|
| 136 |
-
id:
|
| 137 |
-
env:
|
| 138 |
-
GH_TOKEN: ${{ github.token }}
|
| 139 |
-
TAG_NAME: ${{ github.ref_name }}
|
| 140 |
-
run: |
|
| 141 |
-
set -euo pipefail
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
import
|
| 149 |
-
import
|
| 150 |
-
import
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
def
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
if
|
| 203 |
-
continue
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
--
|
| 260 |
-
--
|
| 261 |
-
--
|
| 262 |
-
--
|
| 263 |
-
--
|
| 264 |
-
--
|
| 265 |
-
--expected-
|
| 266 |
-
--expected-
|
| 267 |
-
--expected-
|
| 268 |
-
--expected-
|
| 269 |
-
--expected-
|
| 270 |
-
--
|
| 271 |
-
--
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
- name:
|
| 300 |
-
run:
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
. .
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
python -m pip install
|
| 336 |
-
python -
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
)
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
)
|
| 382 |
-
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
|
| 396 |
-
|
| 397 |
-
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
- name: Upload
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
| 416 |
-
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
- name:
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
--
|
| 446 |
-
|
| 447 |
-
|
| 448 |
-
|
| 449 |
-
|
| 450 |
-
|
| 451 |
-
|
| 452 |
-
|
| 453 |
-
|
| 454 |
-
|
| 455 |
-
|
| 456 |
-
|
| 457 |
-
|
| 458 |
-
|
| 459 |
-
|
| 460 |
-
|
| 461 |
-
|
| 462 |
-
|
| 463 |
-
|
| 464 |
-
|
| 465 |
-
|
| 466 |
-
|
| 467 |
-
- name:
|
| 468 |
-
|
| 469 |
-
|
| 470 |
-
|
| 471 |
-
|
| 472 |
-
|
| 473 |
-
|
| 474 |
-
|
| 475 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Publish to PyPI
|
| 2 |
+
|
| 3 |
+
# Publish claude-ctx to PyPI on every tag that looks like a version
|
| 4 |
+
# (v0.5.0, v0.5.0-rc1, v1.0.0, …). Uses PyPI Trusted Publishing —
|
| 5 |
+
# no API token needed once the pending publisher is configured at
|
| 6 |
+
# https://pypi.org/manage/account/publishing/.
|
| 7 |
+
#
|
| 8 |
+
# Also deployable on demand to TestPyPI via workflow_dispatch. Production
|
| 9 |
+
# PyPI publishes must use a version tag push so graph release assets are
|
| 10 |
+
# uploaded before the wheel is published.
|
| 11 |
+
|
| 12 |
+
on:
|
| 13 |
+
push:
|
| 14 |
+
tags:
|
| 15 |
+
- "v*"
|
| 16 |
+
workflow_dispatch:
|
| 17 |
+
inputs:
|
| 18 |
+
repository:
|
| 19 |
+
description: "PyPI repository (pypi or testpypi)"
|
| 20 |
+
required: true
|
| 21 |
+
default: "pypi"
|
| 22 |
+
type: choice
|
| 23 |
+
options:
|
| 24 |
+
- pypi
|
| 25 |
+
- testpypi
|
| 26 |
+
|
| 27 |
+
env:
|
| 28 |
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
| 29 |
+
|
| 30 |
+
permissions:
|
| 31 |
+
contents: read
|
| 32 |
+
|
| 33 |
+
jobs:
|
| 34 |
+
build:
|
| 35 |
+
name: Build sdist + wheel
|
| 36 |
+
runs-on: ubuntu-latest
|
| 37 |
+
permissions:
|
| 38 |
+
contents: read
|
| 39 |
+
outputs:
|
| 40 |
+
graph_assets_available: ${{ steps.resolve_graph_assets.outcome == 'success' }}
|
| 41 |
+
steps:
|
| 42 |
+
- name: Checkout
|
| 43 |
+
uses: actions/checkout@v5
|
| 44 |
+
with:
|
| 45 |
+
lfs: false
|
| 46 |
+
|
| 47 |
+
- name: Set up Python
|
| 48 |
+
uses: actions/setup-python@v6
|
| 49 |
+
with:
|
| 50 |
+
python-version: "3.12"
|
| 51 |
+
|
| 52 |
+
- name: Install release tooling
|
| 53 |
+
run: |
|
| 54 |
+
python -m pip install --upgrade pip
|
| 55 |
+
python -m pip install ".[dev]" build packaging twine
|
| 56 |
+
|
| 57 |
+
- name: Validate release target
|
| 58 |
+
run: |
|
| 59 |
+
python - <<'PY'
|
| 60 |
+
import os
|
| 61 |
+
import tomllib
|
| 62 |
+
from packaging.version import Version
|
| 63 |
+
|
| 64 |
+
event = os.environ["GITHUB_EVENT_NAME"]
|
| 65 |
+
target_repository = os.environ.get("INPUT_REPOSITORY", "pypi")
|
| 66 |
+
ref_type = os.environ.get("GITHUB_REF_TYPE", "")
|
| 67 |
+
ref_name = os.environ["GITHUB_REF_NAME"]
|
| 68 |
+
|
| 69 |
+
if event == "workflow_dispatch" and target_repository == "pypi":
|
| 70 |
+
raise SystemExit(
|
| 71 |
+
"Manual PyPI publish is disabled; push a version tag so "
|
| 72 |
+
"graph release assets are uploaded before publishing"
|
| 73 |
+
)
|
| 74 |
+
if event == "workflow_dispatch" and target_repository == "testpypi":
|
| 75 |
+
print(f"manual TestPyPI publish allowed from {ref_type}:{ref_name}")
|
| 76 |
+
raise SystemExit(0)
|
| 77 |
+
|
| 78 |
+
if ref_type != "tag":
|
| 79 |
+
raise SystemExit(
|
| 80 |
+
"PyPI release must run from a version tag; "
|
| 81 |
+
f"got {ref_type}:{ref_name}"
|
| 82 |
+
)
|
| 83 |
+
tag = ref_name
|
| 84 |
+
if not tag.startswith("v"):
|
| 85 |
+
raise SystemExit(f"release tag must start with v: {tag}")
|
| 86 |
+
|
| 87 |
+
with open("pyproject.toml", "rb") as fh:
|
| 88 |
+
package_version = tomllib.load(fh)["project"]["version"]
|
| 89 |
+
|
| 90 |
+
tag_version = str(Version(tag[1:]))
|
| 91 |
+
normalized_package_version = str(Version(package_version))
|
| 92 |
+
if tag_version != normalized_package_version:
|
| 93 |
+
raise SystemExit(
|
| 94 |
+
f"tag {tag!r} does not match pyproject version {package_version!r}"
|
| 95 |
+
)
|
| 96 |
+
print(f"release version {package_version} matches tag {tag}")
|
| 97 |
+
PY
|
| 98 |
+
env:
|
| 99 |
+
INPUT_REPOSITORY: ${{ github.event.inputs.repository || 'pypi' }}
|
| 100 |
+
|
| 101 |
+
- name: Reject already published PyPI version
|
| 102 |
+
run: |
|
| 103 |
+
python - <<'PY'
|
| 104 |
+
import os
|
| 105 |
+
import tomllib
|
| 106 |
+
import urllib.error
|
| 107 |
+
import urllib.request
|
| 108 |
+
|
| 109 |
+
target_repository = os.environ.get("INPUT_REPOSITORY", "pypi")
|
| 110 |
+
if target_repository != "pypi":
|
| 111 |
+
print(f"skipping PyPI reuse check for {target_repository}")
|
| 112 |
+
raise SystemExit(0)
|
| 113 |
+
|
| 114 |
+
with open("pyproject.toml", "rb") as fh:
|
| 115 |
+
project = tomllib.load(fh)["project"]
|
| 116 |
+
|
| 117 |
+
name = project["name"]
|
| 118 |
+
package_version = project["version"]
|
| 119 |
+
url = f"https://pypi.org/pypi/{name}/{package_version}/json"
|
| 120 |
+
try:
|
| 121 |
+
with urllib.request.urlopen(url, timeout=15):
|
| 122 |
+
raise SystemExit(
|
| 123 |
+
f"{name} {package_version} already exists on PyPI; "
|
| 124 |
+
"bump the version before publishing"
|
| 125 |
+
)
|
| 126 |
+
except urllib.error.HTTPError as exc:
|
| 127 |
+
if exc.code == 404:
|
| 128 |
+
print(f"{name} {package_version} is not present on PyPI")
|
| 129 |
+
raise SystemExit(0)
|
| 130 |
+
raise
|
| 131 |
+
PY
|
| 132 |
+
env:
|
| 133 |
+
INPUT_REPOSITORY: ${{ github.event.inputs.repository || 'pypi' }}
|
| 134 |
+
|
| 135 |
+
- name: Resolve release graph artifacts from release assets
|
| 136 |
+
id: resolve_graph_assets
|
| 137 |
+
env:
|
| 138 |
+
GH_TOKEN: ${{ github.token }}
|
| 139 |
+
TAG_NAME: ${{ github.ref_name }}
|
| 140 |
+
run: |
|
| 141 |
+
set -euo pipefail
|
| 142 |
+
echo "Resolving graph artifacts from matching release assets to avoid Git LFS bandwidth."
|
| 143 |
+
python - <<'PY'
|
| 144 |
+
import hashlib
|
| 145 |
+
import json
|
| 146 |
+
import os
|
| 147 |
+
from pathlib import Path
|
| 148 |
+
import subprocess
|
| 149 |
+
import time
|
| 150 |
+
import urllib.request
|
| 151 |
+
|
| 152 |
+
repo = os.environ["GITHUB_REPOSITORY"]
|
| 153 |
+
current_tag = os.environ.get("TAG_NAME", "")
|
| 154 |
+
release_asset_wait_seconds = 300
|
| 155 |
+
release_asset_poll_seconds = 10
|
| 156 |
+
expected_graph_assets = {
|
| 157 |
+
"graph/wiki-graph.tar.gz": {
|
| 158 |
+
"sha256": "91b30795e7d200cf31a62a8749969d12658f5f74636d2de06d6b2b24b393c12f",
|
| 159 |
+
"size": 457545675,
|
| 160 |
+
},
|
| 161 |
+
"graph/wiki-graph-runtime.tar.gz": {
|
| 162 |
+
"sha256": "334fb19bace3fd6e4b92087850f17297fb248032957d123f3f1432dfde2e36c0",
|
| 163 |
+
"size": 175773376,
|
| 164 |
+
},
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
def load_releases() -> list[dict]:
|
| 168 |
+
return json.loads(subprocess.check_output(
|
| 169 |
+
["gh", "api", f"repos/{repo}/releases?per_page=50"],
|
| 170 |
+
text=True,
|
| 171 |
+
))
|
| 172 |
+
|
| 173 |
+
def hydrate_from_release(path_name: str, hydrated_min_size: int) -> None:
|
| 174 |
+
graph_tar = Path(path_name)
|
| 175 |
+
fallback = expected_graph_assets[path_name]
|
| 176 |
+
expected_oid = fallback["sha256"]
|
| 177 |
+
expected_size = int(fallback["size"])
|
| 178 |
+
if graph_tar.exists():
|
| 179 |
+
pointer = graph_tar.read_text(encoding="utf-8", errors="replace")
|
| 180 |
+
for line in pointer.splitlines():
|
| 181 |
+
if line.startswith("oid sha256:"):
|
| 182 |
+
expected_oid = line.split(":", 1)[1].strip()
|
| 183 |
+
elif line.startswith("size "):
|
| 184 |
+
expected_size = int(line.split(" ", 1)[1].strip())
|
| 185 |
+
if not pointer.startswith("version https://git-lfs.github.com/spec/v1") and graph_tar.stat().st_size > hydrated_min_size:
|
| 186 |
+
print(f"{graph_tar} is already hydrated")
|
| 187 |
+
return
|
| 188 |
+
|
| 189 |
+
deadline = time.monotonic() + release_asset_wait_seconds
|
| 190 |
+
while True:
|
| 191 |
+
candidates = []
|
| 192 |
+
for release in load_releases():
|
| 193 |
+
tag_name = str(release.get("tag_name") or "")
|
| 194 |
+
is_graph_cache = tag_name.startswith("graph-artifacts-")
|
| 195 |
+
if release.get("draft") or (
|
| 196 |
+
release.get("prerelease") and not is_graph_cache
|
| 197 |
+
):
|
| 198 |
+
continue
|
| 199 |
+
if tag_name == current_tag:
|
| 200 |
+
continue
|
| 201 |
+
for asset in release.get("assets", []):
|
| 202 |
+
if asset.get("name") != graph_tar.name:
|
| 203 |
+
continue
|
| 204 |
+
digest = str(asset.get("digest") or "")
|
| 205 |
+
size = int(asset.get("size") or 0)
|
| 206 |
+
if size != expected_size:
|
| 207 |
+
continue
|
| 208 |
+
if digest and digest != f"sha256:{expected_oid}":
|
| 209 |
+
continue
|
| 210 |
+
candidates.append((tag_name, asset))
|
| 211 |
+
|
| 212 |
+
if candidates:
|
| 213 |
+
break
|
| 214 |
+
if time.monotonic() >= deadline:
|
| 215 |
+
raise SystemExit(
|
| 216 |
+
f"No previous release asset matches {path_name} "
|
| 217 |
+
f"sha256:{expected_oid} size:{expected_size}"
|
| 218 |
+
)
|
| 219 |
+
print(
|
| 220 |
+
f"Waiting for matching release asset {graph_tar.name} "
|
| 221 |
+
f"sha256:{expected_oid} size:{expected_size}"
|
| 222 |
+
)
|
| 223 |
+
time.sleep(release_asset_poll_seconds)
|
| 224 |
+
|
| 225 |
+
source_tag, asset = candidates[0]
|
| 226 |
+
tmp = graph_tar.with_name(f"{graph_tar.name}.download")
|
| 227 |
+
sha = hashlib.sha256()
|
| 228 |
+
total = 0
|
| 229 |
+
with urllib.request.urlopen(asset["browser_download_url"], timeout=300) as resp: # noqa: S310
|
| 230 |
+
with tmp.open("wb") as fh:
|
| 231 |
+
while True:
|
| 232 |
+
chunk = resp.read(1024 * 1024)
|
| 233 |
+
if not chunk:
|
| 234 |
+
break
|
| 235 |
+
sha.update(chunk)
|
| 236 |
+
total += len(chunk)
|
| 237 |
+
fh.write(chunk)
|
| 238 |
+
actual_oid = sha.hexdigest()
|
| 239 |
+
if actual_oid != expected_oid or total != expected_size:
|
| 240 |
+
tmp.unlink(missing_ok=True)
|
| 241 |
+
raise SystemExit(
|
| 242 |
+
f"Downloaded {path_name} does not match LFS pointer: "
|
| 243 |
+
f"sha256:{actual_oid} size:{total}"
|
| 244 |
+
)
|
| 245 |
+
tmp.replace(graph_tar)
|
| 246 |
+
print(
|
| 247 |
+
f"Hydrated {path_name} from {source_tag} release asset "
|
| 248 |
+
f"sha256:{actual_oid} size:{total}"
|
| 249 |
+
)
|
| 250 |
+
|
| 251 |
+
hydrate_from_release("graph/wiki-graph.tar.gz", 100_000_000)
|
| 252 |
+
hydrate_from_release("graph/wiki-graph-runtime.tar.gz", 10_000_000)
|
| 253 |
+
PY
|
| 254 |
+
|
| 255 |
+
- name: Validate release graph artifacts
|
| 256 |
+
if: steps.resolve_graph_assets.outcome == 'success'
|
| 257 |
+
run: |
|
| 258 |
+
python src/validate_graph_artifacts.py \
|
| 259 |
+
--graph-dir graph \
|
| 260 |
+
--deep \
|
| 261 |
+
--min-nodes 100000 \
|
| 262 |
+
--min-edges 2000000 \
|
| 263 |
+
--min-skills-sh-nodes 89000 \
|
| 264 |
+
--min-semantic-edges 1000000 \
|
| 265 |
+
--expected-nodes 102928 \
|
| 266 |
+
--expected-edges 2913960 \
|
| 267 |
+
--expected-semantic-edges 1683193 \
|
| 268 |
+
--expected-harness-nodes 207 \
|
| 269 |
+
--expected-skills-sh-nodes 89471 \
|
| 270 |
+
--expected-skills-sh-catalog-entries 89465 \
|
| 271 |
+
--expected-skills-sh-converted 89465 \
|
| 272 |
+
--expected-skill-pages 91464 \
|
| 273 |
+
--expected-agent-pages 467 \
|
| 274 |
+
--expected-mcp-pages 10790 \
|
| 275 |
+
--expected-harness-pages 207 \
|
| 276 |
+
--line-threshold 180 \
|
| 277 |
+
--max-stage-lines 40
|
| 278 |
+
|
| 279 |
+
- name: Validate README and docs stats
|
| 280 |
+
if: steps.resolve_graph_assets.outcome == 'success'
|
| 281 |
+
run: python src/update_repo_stats.py --check
|
| 282 |
+
|
| 283 |
+
- name: Static gates
|
| 284 |
+
run: |
|
| 285 |
+
python -m ruff check src hooks scripts
|
| 286 |
+
python -m mypy src
|
| 287 |
+
|
| 288 |
+
- name: Clean-host contract
|
| 289 |
+
run: python scripts/clean_host_contract.py --fast
|
| 290 |
+
|
| 291 |
+
- name: Release canary tests
|
| 292 |
+
run: |
|
| 293 |
+
python -m pytest -q --no-cov \
|
| 294 |
+
src/tests/test_ci_classifier.py \
|
| 295 |
+
src/tests/test_package_scaffold.py \
|
| 296 |
+
src/tests/test_clean_host_contract.py \
|
| 297 |
+
src/tests/test_alive_loop_e2e.py
|
| 298 |
+
|
| 299 |
+
- name: Build distributions
|
| 300 |
+
run: python -m build
|
| 301 |
+
|
| 302 |
+
- name: Check distributions
|
| 303 |
+
run: python -m twine check dist/*
|
| 304 |
+
|
| 305 |
+
- name: Check distribution contents
|
| 306 |
+
run: |
|
| 307 |
+
python - <<'PY'
|
| 308 |
+
import tarfile
|
| 309 |
+
import zipfile
|
| 310 |
+
from pathlib import Path
|
| 311 |
+
|
| 312 |
+
wheel = next(Path("dist").glob("*.whl"))
|
| 313 |
+
sdist = next(Path("dist").glob("*.tar.gz"))
|
| 314 |
+
|
| 315 |
+
with zipfile.ZipFile(wheel) as zf:
|
| 316 |
+
wheel_names = set(zf.namelist())
|
| 317 |
+
required = {"ctx/config.json", "ctx/skill-registry.json"}
|
| 318 |
+
missing = sorted(required - wheel_names)
|
| 319 |
+
if missing:
|
| 320 |
+
raise SystemExit(f"wheel missing packaged defaults: {missing}")
|
| 321 |
+
|
| 322 |
+
with tarfile.open(sdist, "r:gz") as tf:
|
| 323 |
+
names = set(tf.getnames())
|
| 324 |
+
forbidden = ("/src/tests/", "/.claude/", "/.a5c/")
|
| 325 |
+
leaked = sorted(name for name in names if any(part in name for part in forbidden))
|
| 326 |
+
if leaked:
|
| 327 |
+
raise SystemExit("sdist contains local/test artifacts:\n" + "\n".join(leaked[:20]))
|
| 328 |
+
print(f"checked distribution contents: {wheel.name}, {sdist.name}")
|
| 329 |
+
PY
|
| 330 |
+
|
| 331 |
+
- name: Smoke install wheel
|
| 332 |
+
run: |
|
| 333 |
+
python -m venv .venv-smoke
|
| 334 |
+
. .venv-smoke/bin/activate
|
| 335 |
+
python -m pip install --upgrade pip
|
| 336 |
+
wheel="$(python - <<'PY'
|
| 337 |
+
from pathlib import Path
|
| 338 |
+
print(next(Path("dist").glob("*.whl")))
|
| 339 |
+
PY
|
| 340 |
+
)"
|
| 341 |
+
python -m pip install "$wheel"
|
| 342 |
+
python -m pip check
|
| 343 |
+
python - <<'PY'
|
| 344 |
+
import tomllib
|
| 345 |
+
from importlib.metadata import entry_points, version
|
| 346 |
+
|
| 347 |
+
import ctx
|
| 348 |
+
import ctx_config
|
| 349 |
+
|
| 350 |
+
with open("pyproject.toml", "rb") as fh:
|
| 351 |
+
expected_scripts = set(tomllib.load(fh)["project"]["scripts"])
|
| 352 |
+
|
| 353 |
+
dist_version = version("claude-ctx")
|
| 354 |
+
if ctx.__version__ != dist_version:
|
| 355 |
+
raise SystemExit(
|
| 356 |
+
f"ctx.__version__={ctx.__version__!r} != metadata {dist_version!r}"
|
| 357 |
+
)
|
| 358 |
+
if ctx_config.cfg.recommendation_top_k != 5:
|
| 359 |
+
raise SystemExit("packaged default config was not loaded")
|
| 360 |
+
scripts = {
|
| 361 |
+
ep.name: ep for ep in entry_points(group="console_scripts")
|
| 362 |
+
if ep.name == "ctx" or ep.name.startswith("ctx-")
|
| 363 |
+
}
|
| 364 |
+
missing = sorted(expected_scripts - set(scripts))
|
| 365 |
+
extra = sorted(set(scripts) - expected_scripts)
|
| 366 |
+
if missing or extra:
|
| 367 |
+
raise SystemExit(
|
| 368 |
+
"wheel console-script surface mismatch\n"
|
| 369 |
+
f"missing: {missing}\n"
|
| 370 |
+
f"extra: {extra}"
|
| 371 |
+
)
|
| 372 |
+
failures = []
|
| 373 |
+
for ep in scripts.values():
|
| 374 |
+
try:
|
| 375 |
+
ep.load()
|
| 376 |
+
except Exception as exc:
|
| 377 |
+
failures.append(f"{ep.name}: {exc!r}")
|
| 378 |
+
if failures:
|
| 379 |
+
raise SystemExit("console script load failures:\n" + "\n".join(failures))
|
| 380 |
+
unsafe_help = {"ctx-mcp-server"}
|
| 381 |
+
safe_help = sorted(expected_scripts - unsafe_help)
|
| 382 |
+
with open("ctx-console-help.txt", "w", encoding="utf-8", newline="\n") as fh:
|
| 383 |
+
fh.write("\n".join(safe_help) + "\n")
|
| 384 |
+
print(
|
| 385 |
+
f"loaded {len(scripts)} ctx console scripts from wheel "
|
| 386 |
+
f"{dist_version}; help-smoke={len(safe_help)}"
|
| 387 |
+
)
|
| 388 |
+
PY
|
| 389 |
+
while IFS= read -r cmd; do
|
| 390 |
+
cmd="${cmd%$'\r'}"
|
| 391 |
+
[[ -z "$cmd" ]] && continue
|
| 392 |
+
echo "help smoke: $cmd"
|
| 393 |
+
"$cmd" --help >/dev/null
|
| 394 |
+
done < ctx-console-help.txt
|
| 395 |
+
python -m pip install "${wheel}[harness]"
|
| 396 |
+
python - <<'PY'
|
| 397 |
+
import litellm
|
| 398 |
+
|
| 399 |
+
print(f"harness extra import ok: litellm {getattr(litellm, '__version__', 'unknown')}")
|
| 400 |
+
PY
|
| 401 |
+
|
| 402 |
+
- name: Upload dist artifact
|
| 403 |
+
uses: actions/upload-artifact@v7
|
| 404 |
+
with:
|
| 405 |
+
name: dist
|
| 406 |
+
path: dist/
|
| 407 |
+
|
| 408 |
+
- name: Upload graph artifact bundle
|
| 409 |
+
if: steps.resolve_graph_assets.outcome == 'success'
|
| 410 |
+
uses: actions/upload-artifact@v7
|
| 411 |
+
with:
|
| 412 |
+
name: graph-release-assets
|
| 413 |
+
path: |
|
| 414 |
+
graph/wiki-graph.tar.gz
|
| 415 |
+
graph/wiki-graph-runtime.tar.gz
|
| 416 |
+
graph/skills-sh-catalog.json.gz
|
| 417 |
+
graph/communities.json
|
| 418 |
+
graph/entity-overlays.jsonl
|
| 419 |
+
|
| 420 |
+
release-assets:
|
| 421 |
+
name: Upload graph release assets
|
| 422 |
+
needs: build
|
| 423 |
+
if: github.event_name == 'push' && needs.build.outputs.graph_assets_available == 'true'
|
| 424 |
+
runs-on: ubuntu-latest
|
| 425 |
+
permissions:
|
| 426 |
+
contents: write
|
| 427 |
+
steps:
|
| 428 |
+
- name: Download graph artifact bundle
|
| 429 |
+
uses: actions/download-artifact@v7
|
| 430 |
+
with:
|
| 431 |
+
name: graph-release-assets
|
| 432 |
+
path: graph-release-assets
|
| 433 |
+
|
| 434 |
+
- name: Upload graph assets to GitHub release
|
| 435 |
+
env:
|
| 436 |
+
GH_TOKEN: ${{ github.token }}
|
| 437 |
+
TAG_NAME: ${{ github.ref_name }}
|
| 438 |
+
run: |
|
| 439 |
+
gh release view "$TAG_NAME" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1 \
|
| 440 |
+
|| gh release create "$TAG_NAME" \
|
| 441 |
+
--repo "$GITHUB_REPOSITORY" \
|
| 442 |
+
--title "$TAG_NAME" \
|
| 443 |
+
--notes "ctx $TAG_NAME"
|
| 444 |
+
gh release upload "$TAG_NAME" \
|
| 445 |
+
--repo "$GITHUB_REPOSITORY" \
|
| 446 |
+
graph-release-assets/wiki-graph.tar.gz \
|
| 447 |
+
graph-release-assets/wiki-graph-runtime.tar.gz \
|
| 448 |
+
graph-release-assets/skills-sh-catalog.json.gz \
|
| 449 |
+
graph-release-assets/communities.json \
|
| 450 |
+
graph-release-assets/entity-overlays.jsonl \
|
| 451 |
+
--clobber
|
| 452 |
+
|
| 453 |
+
publish:
|
| 454 |
+
name: Publish to PyPI
|
| 455 |
+
needs:
|
| 456 |
+
- build
|
| 457 |
+
- release-assets
|
| 458 |
+
if: ${{ always() && needs.build.result == 'success' && (needs.release-assets.result == 'success' || (github.event_name == 'workflow_dispatch' && github.event.inputs.repository == 'testpypi')) }}
|
| 459 |
+
runs-on: ubuntu-latest
|
| 460 |
+
permissions:
|
| 461 |
+
contents: read
|
| 462 |
+
id-token: write # required for Trusted Publishing
|
| 463 |
+
environment:
|
| 464 |
+
name: pypi
|
| 465 |
+
url: https://pypi.org/project/claude-ctx/
|
| 466 |
+
steps:
|
| 467 |
+
- name: Download dist artifact
|
| 468 |
+
uses: actions/download-artifact@v7
|
| 469 |
+
with:
|
| 470 |
+
name: dist
|
| 471 |
+
path: dist/
|
| 472 |
+
|
| 473 |
+
- name: Publish to PyPI
|
| 474 |
+
if: github.event_name == 'push'
|
| 475 |
+
uses: pypa/gh-action-pypi-publish@release/v1
|
| 476 |
+
|
| 477 |
+
- name: Publish to TestPyPI
|
| 478 |
+
if: github.event_name == 'workflow_dispatch' && github.event.inputs.repository == 'testpypi'
|
| 479 |
+
uses: pypa/gh-action-pypi-publish@release/v1
|
| 480 |
+
with:
|
| 481 |
+
repository-url: https://test.pypi.org/legacy/
|
.github/workflows/test.yml
CHANGED
|
@@ -1,704 +1,750 @@
|
|
| 1 |
-
name: Tests
|
| 2 |
-
|
| 3 |
-
on:
|
| 4 |
-
push:
|
| 5 |
-
branches: [main]
|
| 6 |
-
pull_request:
|
| 7 |
-
branches: [main]
|
| 8 |
-
|
| 9 |
-
env:
|
| 10 |
-
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
| 11 |
-
|
| 12 |
-
permissions:
|
| 13 |
-
contents: read
|
| 14 |
-
|
| 15 |
-
concurrency:
|
| 16 |
-
group: tests-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
| 17 |
-
cancel-in-progress: true
|
| 18 |
-
|
| 19 |
-
jobs:
|
| 20 |
-
classify:
|
| 21 |
-
name: "Change classifier"
|
| 22 |
-
runs-on: ubuntu-latest
|
| 23 |
-
outputs:
|
| 24 |
-
browser_changed: ${{ steps.classify.outputs.browser_changed }}
|
| 25 |
-
ci_changed: ${{ steps.classify.outputs.ci_changed }}
|
| 26 |
-
docs_changed: ${{ steps.classify.outputs.docs_changed }}
|
| 27 |
-
docs_only: ${{ steps.classify.outputs.docs_only }}
|
| 28 |
-
graph_artifact_changed: ${{ steps.classify.outputs.graph_artifact_changed }}
|
| 29 |
-
graph_changed: ${{ steps.classify.outputs.graph_changed }}
|
| 30 |
-
graph_only: ${{ steps.classify.outputs.graph_only }}
|
| 31 |
-
package_changed: ${{ steps.classify.outputs.package_changed }}
|
| 32 |
-
similarity_changed: ${{ steps.classify.outputs.similarity_changed }}
|
| 33 |
-
source_changed: ${{ steps.classify.outputs.source_changed }}
|
| 34 |
-
steps:
|
| 35 |
-
- name: Checkout with full history
|
| 36 |
-
uses: actions/checkout@v5
|
| 37 |
-
with:
|
| 38 |
-
fetch-depth: 0
|
| 39 |
-
lfs: false
|
| 40 |
-
|
| 41 |
-
- name: Classify changed paths
|
| 42 |
-
id: classify
|
| 43 |
-
shell: bash
|
| 44 |
-
env:
|
| 45 |
-
BEFORE_SHA: ${{ github.event.before }}
|
| 46 |
-
EVENT_NAME: ${{ github.event_name }}
|
| 47 |
-
HEAD_SHA: ${{ github.sha }}
|
| 48 |
-
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
| 49 |
-
run: |
|
| 50 |
-
set -euo pipefail
|
| 51 |
-
BASE="$BEFORE_SHA"
|
| 52 |
-
if [[ "$EVENT_NAME" == "pull_request" && -n "$PR_BASE_SHA" ]]; then
|
| 53 |
-
BASE="$PR_BASE_SHA"
|
| 54 |
-
fi
|
| 55 |
-
if [[ -z "$BASE" || "$BASE" =~ ^0+$ ]]; then
|
| 56 |
-
BASE="$(git rev-list --max-parents=0 "$HEAD_SHA")"
|
| 57 |
-
fi
|
| 58 |
-
if git cat-file -e "$BASE^{commit}" 2>/dev/null; then
|
| 59 |
-
git diff --name-only "$BASE" "$HEAD_SHA" > changed-files.txt
|
| 60 |
-
elif git cat-file -e "$HEAD_SHA^" 2>/dev/null; then
|
| 61 |
-
git diff --name-only "$HEAD_SHA^" "$HEAD_SHA" > changed-files.txt
|
| 62 |
-
else
|
| 63 |
-
git diff-tree --no-commit-id --name-only -r "$HEAD_SHA" > changed-files.txt
|
| 64 |
-
fi
|
| 65 |
-
|
| 66 |
-
python scripts/ci_classifier.py changed-files.txt
|
| 67 |
-
|
| 68 |
-
static:
|
| 69 |
-
name: "Static quality gates"
|
| 70 |
-
needs: classify
|
| 71 |
-
if: ${{ github.event_name != 'pull_request' || (needs.classify.outputs.docs_only != 'true' && needs.classify.outputs.graph_only != 'true') }}
|
| 72 |
-
runs-on: ubuntu-latest
|
| 73 |
-
steps:
|
| 74 |
-
- name: Checkout
|
| 75 |
-
uses: actions/checkout@v5
|
| 76 |
-
with:
|
| 77 |
-
lfs: false
|
| 78 |
-
|
| 79 |
-
- name: Set up Python 3.11
|
| 80 |
-
uses: actions/setup-python@v6
|
| 81 |
-
with:
|
| 82 |
-
python-version: "3.11"
|
| 83 |
-
cache: pip
|
| 84 |
-
cache-dependency-path: pyproject.toml
|
| 85 |
-
|
| 86 |
-
- name: Install dependencies
|
| 87 |
-
run: |
|
| 88 |
-
python -m pip install --upgrade pip
|
| 89 |
-
python -m pip install ".[dev]"
|
| 90 |
-
|
| 91 |
-
- name: Run static quality gates
|
| 92 |
-
run: python -m ruff check src hooks scripts && python -m mypy src && python -m pip check
|
| 93 |
-
|
| 94 |
-
unit-linux:
|
| 95 |
-
name: "unit-linux"
|
| 96 |
-
needs: classify
|
| 97 |
-
if: ${{ github.event_name != 'pull_request' || (needs.classify.outputs.docs_only != 'true' && needs.classify.outputs.graph_only != 'true') }}
|
| 98 |
-
runs-on: ubuntu-latest
|
| 99 |
-
steps:
|
| 100 |
-
- name: Checkout
|
| 101 |
-
uses: actions/checkout@v5
|
| 102 |
-
with:
|
| 103 |
-
lfs: false
|
| 104 |
-
|
| 105 |
-
- name: Set up Python 3.11
|
| 106 |
-
uses: actions/setup-python@v6
|
| 107 |
-
with:
|
| 108 |
-
python-version: "3.11"
|
| 109 |
-
cache: pip
|
| 110 |
-
cache-dependency-path: pyproject.toml
|
| 111 |
-
|
| 112 |
-
- name: Install dependencies
|
| 113 |
-
run: |
|
| 114 |
-
python -m pip install --upgrade pip
|
| 115 |
-
python -m pip install ".[dev]"
|
| 116 |
-
|
| 117 |
-
- name: Run tests with coverage gate
|
| 118 |
-
# Bump this number as the coverage sprint ships - never lower it.
|
| 119 |
-
run: pytest -q -m "not browser and not integration" --cov=src --cov-report=term-missing --cov-fail-under=40
|
| 120 |
-
|
| 121 |
-
- name: Upload coverage artifact
|
| 122 |
-
uses: actions/upload-artifact@v7
|
| 123 |
-
with:
|
| 124 |
-
name: coverage
|
| 125 |
-
path: .coverage
|
| 126 |
-
include-hidden-files: true
|
| 127 |
-
retention-days: 7
|
| 128 |
-
|
| 129 |
-
similarity-integration:
|
| 130 |
-
name: "Similarity precision/recall"
|
| 131 |
-
needs: classify
|
| 132 |
-
if: ${{ github.event_name != 'pull_request' || (needs.classify.outputs.docs_only != 'true' && needs.classify.outputs.similarity_changed == 'true') }}
|
| 133 |
-
runs-on: ubuntu-latest
|
| 134 |
-
steps:
|
| 135 |
-
- name: Checkout
|
| 136 |
-
uses: actions/checkout@v5
|
| 137 |
-
with:
|
| 138 |
-
lfs: false
|
| 139 |
-
|
| 140 |
-
- name: Set up Python 3.11
|
| 141 |
-
uses: actions/setup-python@v6
|
| 142 |
-
with:
|
| 143 |
-
python-version: "3.11"
|
| 144 |
-
cache: pip
|
| 145 |
-
cache-dependency-path: pyproject.toml
|
| 146 |
-
|
| 147 |
-
- name: Install embedding dependencies
|
| 148 |
-
run: |
|
| 149 |
-
python -m pip install --upgrade pip
|
| 150 |
-
python -m pip install ".[dev,embeddings]"
|
| 151 |
-
|
| 152 |
-
- name:
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
python -m pip install
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
|
| 382 |
-
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
|
| 396 |
-
|
| 397 |
-
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
| 416 |
-
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
|
| 447 |
-
|
| 448 |
-
|
| 449 |
-
|
| 450 |
-
|
| 451 |
-
|
| 452 |
-
|
| 453 |
-
|
| 454 |
-
|
| 455 |
-
|
| 456 |
-
|
| 457 |
-
|
| 458 |
-
|
| 459 |
-
|
| 460 |
-
|
| 461 |
-
|
| 462 |
-
|
| 463 |
-
|
| 464 |
-
|
| 465 |
-
|
| 466 |
-
|
| 467 |
-
|
| 468 |
-
|
| 469 |
-
|
| 470 |
-
|
| 471 |
-
|
| 472 |
-
|
| 473 |
-
|
| 474 |
-
- name:
|
| 475 |
-
run:
|
| 476 |
-
|
| 477 |
-
|
| 478 |
-
name: "
|
| 479 |
-
needs: classify
|
| 480 |
-
if: ${{ github.event_name
|
| 481 |
-
runs-on: ubuntu-latest
|
| 482 |
-
steps:
|
| 483 |
-
- name: Checkout
|
| 484 |
-
uses: actions/checkout@v5
|
| 485 |
-
with:
|
| 486 |
-
lfs: false
|
| 487 |
-
|
| 488 |
-
- name: Set up Python 3.12
|
| 489 |
-
uses: actions/setup-python@v6
|
| 490 |
-
with:
|
| 491 |
-
python-version: "3.12"
|
| 492 |
-
cache: pip
|
| 493 |
-
cache-dependency-path: pyproject.toml
|
| 494 |
-
|
| 495 |
-
- name:
|
| 496 |
-
run: |
|
| 497 |
-
python -m pip install --upgrade pip
|
| 498 |
-
python -m pip install
|
| 499 |
-
|
| 500 |
-
|
| 501 |
-
|
| 502 |
-
|
| 503 |
-
|
| 504 |
-
|
| 505 |
-
|
| 506 |
-
|
| 507 |
-
|
| 508 |
-
|
| 509 |
-
|
| 510 |
-
|
| 511 |
-
|
| 512 |
-
|
| 513 |
-
|
| 514 |
-
|
| 515 |
-
|
| 516 |
-
|
| 517 |
-
|
| 518 |
-
|
| 519 |
-
|
| 520 |
-
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
|
| 524 |
-
|
| 525 |
-
|
| 526 |
-
if: ${{ github.event_name != 'pull_request' || (needs.classify.outputs.docs_only != 'true' && needs.classify.outputs.graph_only != 'true') }}
|
| 527 |
-
runs-on:
|
| 528 |
-
|
| 529 |
-
|
| 530 |
-
|
| 531 |
-
|
| 532 |
-
|
| 533 |
-
|
| 534 |
-
|
| 535 |
-
|
| 536 |
-
|
| 537 |
-
|
| 538 |
-
|
| 539 |
-
|
| 540 |
-
|
| 541 |
-
- name:
|
| 542 |
-
|
| 543 |
-
|
| 544 |
-
python-
|
| 545 |
-
|
| 546 |
-
|
| 547 |
-
|
| 548 |
-
|
| 549 |
-
|
| 550 |
-
|
| 551 |
-
|
| 552 |
-
|
| 553 |
-
|
| 554 |
-
|
| 555 |
-
|
| 556 |
-
|
| 557 |
-
|
| 558 |
-
|
| 559 |
-
|
| 560 |
-
|
| 561 |
-
|
| 562 |
-
|
| 563 |
-
|
| 564 |
-
|
| 565 |
-
|
| 566 |
-
|
| 567 |
-
|
| 568 |
-
|
| 569 |
-
|
| 570 |
-
|
| 571 |
-
|
| 572 |
-
|
| 573 |
-
|
| 574 |
-
|
| 575 |
-
|
| 576 |
-
|
| 577 |
-
|
| 578 |
-
|
| 579 |
-
|
| 580 |
-
|
| 581 |
-
|
| 582 |
-
|
| 583 |
-
|
| 584 |
-
|
| 585 |
-
|
| 586 |
-
|
| 587 |
-
|
| 588 |
-
|
| 589 |
-
|
| 590 |
-
|
| 591 |
-
|
| 592 |
-
|
| 593 |
-
|
| 594 |
-
|
| 595 |
-
|
| 596 |
-
|
| 597 |
-
|
| 598 |
-
|
| 599 |
-
|
| 600 |
-
|
| 601 |
-
|
| 602 |
-
|
| 603 |
-
|
| 604 |
-
|
| 605 |
-
|
| 606 |
-
|
| 607 |
-
|
| 608 |
-
|
| 609 |
-
|
| 610 |
-
|
| 611 |
-
|
| 612 |
-
|
| 613 |
-
|
| 614 |
-
|
| 615 |
-
|
| 616 |
-
|
| 617 |
-
|
| 618 |
-
|
| 619 |
-
|
| 620 |
-
|
| 621 |
-
|
| 622 |
-
|
| 623 |
-
|
| 624 |
-
|
| 625 |
-
|
| 626 |
-
|
| 627 |
-
|
| 628 |
-
|
| 629 |
-
|
| 630 |
-
|
| 631 |
-
|
| 632 |
-
|
| 633 |
-
|
| 634 |
-
|
| 635 |
-
|
| 636 |
-
|
| 637 |
-
|
| 638 |
-
|
| 639 |
-
|
| 640 |
-
|
| 641 |
-
|
| 642 |
-
|
| 643 |
-
|
| 644 |
-
|
| 645 |
-
|
| 646 |
-
|
| 647 |
-
|
| 648 |
-
|
| 649 |
-
|
| 650 |
-
|
| 651 |
-
|
| 652 |
-
|
| 653 |
-
|
| 654 |
-
|
| 655 |
-
|
| 656 |
-
|
| 657 |
-
|
| 658 |
-
|
| 659 |
-
|
| 660 |
-
|
| 661 |
-
|
| 662 |
-
|
| 663 |
-
|
| 664 |
-
|
| 665 |
-
|
| 666 |
-
|
| 667 |
-
|
| 668 |
-
|
| 669 |
-
|
| 670 |
-
|
| 671 |
-
|
| 672 |
-
|
| 673 |
-
|
| 674 |
-
|
| 675 |
-
|
| 676 |
-
|
| 677 |
-
|
| 678 |
-
|
| 679 |
-
|
| 680 |
-
|
| 681 |
-
|
| 682 |
-
|
| 683 |
-
|
| 684 |
-
|
| 685 |
-
-
|
| 686 |
-
|
| 687 |
-
|
| 688 |
-
-
|
| 689 |
-
|
| 690 |
-
|
| 691 |
-
|
| 692 |
-
|
| 693 |
-
|
| 694 |
-
|
| 695 |
-
|
| 696 |
-
|
| 697 |
-
|
| 698 |
-
|
| 699 |
-
|
| 700 |
-
|
| 701 |
-
|
| 702 |
-
|
| 703 |
-
|
| 704 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Tests
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
push:
|
| 5 |
+
branches: [main]
|
| 6 |
+
pull_request:
|
| 7 |
+
branches: [main]
|
| 8 |
+
|
| 9 |
+
env:
|
| 10 |
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
| 11 |
+
|
| 12 |
+
permissions:
|
| 13 |
+
contents: read
|
| 14 |
+
|
| 15 |
+
concurrency:
|
| 16 |
+
group: tests-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
| 17 |
+
cancel-in-progress: true
|
| 18 |
+
|
| 19 |
+
jobs:
|
| 20 |
+
classify:
|
| 21 |
+
name: "Change classifier"
|
| 22 |
+
runs-on: ubuntu-latest
|
| 23 |
+
outputs:
|
| 24 |
+
browser_changed: ${{ steps.classify.outputs.browser_changed }}
|
| 25 |
+
ci_changed: ${{ steps.classify.outputs.ci_changed }}
|
| 26 |
+
docs_changed: ${{ steps.classify.outputs.docs_changed }}
|
| 27 |
+
docs_only: ${{ steps.classify.outputs.docs_only }}
|
| 28 |
+
graph_artifact_changed: ${{ steps.classify.outputs.graph_artifact_changed }}
|
| 29 |
+
graph_changed: ${{ steps.classify.outputs.graph_changed }}
|
| 30 |
+
graph_only: ${{ steps.classify.outputs.graph_only }}
|
| 31 |
+
package_changed: ${{ steps.classify.outputs.package_changed }}
|
| 32 |
+
similarity_changed: ${{ steps.classify.outputs.similarity_changed }}
|
| 33 |
+
source_changed: ${{ steps.classify.outputs.source_changed }}
|
| 34 |
+
steps:
|
| 35 |
+
- name: Checkout with full history
|
| 36 |
+
uses: actions/checkout@v5
|
| 37 |
+
with:
|
| 38 |
+
fetch-depth: 0
|
| 39 |
+
lfs: false
|
| 40 |
+
|
| 41 |
+
- name: Classify changed paths
|
| 42 |
+
id: classify
|
| 43 |
+
shell: bash
|
| 44 |
+
env:
|
| 45 |
+
BEFORE_SHA: ${{ github.event.before }}
|
| 46 |
+
EVENT_NAME: ${{ github.event_name }}
|
| 47 |
+
HEAD_SHA: ${{ github.sha }}
|
| 48 |
+
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
| 49 |
+
run: |
|
| 50 |
+
set -euo pipefail
|
| 51 |
+
BASE="$BEFORE_SHA"
|
| 52 |
+
if [[ "$EVENT_NAME" == "pull_request" && -n "$PR_BASE_SHA" ]]; then
|
| 53 |
+
BASE="$PR_BASE_SHA"
|
| 54 |
+
fi
|
| 55 |
+
if [[ -z "$BASE" || "$BASE" =~ ^0+$ ]]; then
|
| 56 |
+
BASE="$(git rev-list --max-parents=0 "$HEAD_SHA")"
|
| 57 |
+
fi
|
| 58 |
+
if git cat-file -e "$BASE^{commit}" 2>/dev/null; then
|
| 59 |
+
git diff --name-only "$BASE" "$HEAD_SHA" > changed-files.txt
|
| 60 |
+
elif git cat-file -e "$HEAD_SHA^" 2>/dev/null; then
|
| 61 |
+
git diff --name-only "$HEAD_SHA^" "$HEAD_SHA" > changed-files.txt
|
| 62 |
+
else
|
| 63 |
+
git diff-tree --no-commit-id --name-only -r "$HEAD_SHA" > changed-files.txt
|
| 64 |
+
fi
|
| 65 |
+
|
| 66 |
+
python scripts/ci_classifier.py changed-files.txt
|
| 67 |
+
|
| 68 |
+
static:
|
| 69 |
+
name: "Static quality gates"
|
| 70 |
+
needs: classify
|
| 71 |
+
if: ${{ github.event_name != 'pull_request' || (needs.classify.outputs.docs_only != 'true' && needs.classify.outputs.graph_only != 'true') }}
|
| 72 |
+
runs-on: ubuntu-latest
|
| 73 |
+
steps:
|
| 74 |
+
- name: Checkout
|
| 75 |
+
uses: actions/checkout@v5
|
| 76 |
+
with:
|
| 77 |
+
lfs: false
|
| 78 |
+
|
| 79 |
+
- name: Set up Python 3.11
|
| 80 |
+
uses: actions/setup-python@v6
|
| 81 |
+
with:
|
| 82 |
+
python-version: "3.11"
|
| 83 |
+
cache: pip
|
| 84 |
+
cache-dependency-path: pyproject.toml
|
| 85 |
+
|
| 86 |
+
- name: Install dependencies
|
| 87 |
+
run: |
|
| 88 |
+
python -m pip install --upgrade pip
|
| 89 |
+
python -m pip install ".[dev]"
|
| 90 |
+
|
| 91 |
+
- name: Run static quality gates
|
| 92 |
+
run: python -m ruff check src hooks scripts && python -m mypy src && python -m pip check
|
| 93 |
+
|
| 94 |
+
unit-linux:
|
| 95 |
+
name: "unit-linux"
|
| 96 |
+
needs: classify
|
| 97 |
+
if: ${{ github.event_name != 'pull_request' || (needs.classify.outputs.docs_only != 'true' && needs.classify.outputs.graph_only != 'true') }}
|
| 98 |
+
runs-on: ubuntu-latest
|
| 99 |
+
steps:
|
| 100 |
+
- name: Checkout
|
| 101 |
+
uses: actions/checkout@v5
|
| 102 |
+
with:
|
| 103 |
+
lfs: false
|
| 104 |
+
|
| 105 |
+
- name: Set up Python 3.11
|
| 106 |
+
uses: actions/setup-python@v6
|
| 107 |
+
with:
|
| 108 |
+
python-version: "3.11"
|
| 109 |
+
cache: pip
|
| 110 |
+
cache-dependency-path: pyproject.toml
|
| 111 |
+
|
| 112 |
+
- name: Install dependencies
|
| 113 |
+
run: |
|
| 114 |
+
python -m pip install --upgrade pip
|
| 115 |
+
python -m pip install ".[dev]"
|
| 116 |
+
|
| 117 |
+
- name: Run tests with coverage gate
|
| 118 |
+
# Bump this number as the coverage sprint ships - never lower it.
|
| 119 |
+
run: pytest -q -m "not browser and not integration" --cov=src --cov-report=term-missing --cov-fail-under=40
|
| 120 |
+
|
| 121 |
+
- name: Upload coverage artifact
|
| 122 |
+
uses: actions/upload-artifact@v7
|
| 123 |
+
with:
|
| 124 |
+
name: coverage
|
| 125 |
+
path: .coverage
|
| 126 |
+
include-hidden-files: true
|
| 127 |
+
retention-days: 7
|
| 128 |
+
|
| 129 |
+
similarity-integration:
|
| 130 |
+
name: "Similarity precision/recall"
|
| 131 |
+
needs: classify
|
| 132 |
+
if: ${{ github.event_name != 'pull_request' || (needs.classify.outputs.docs_only != 'true' && needs.classify.outputs.similarity_changed == 'true') }}
|
| 133 |
+
runs-on: ubuntu-latest
|
| 134 |
+
steps:
|
| 135 |
+
- name: Checkout
|
| 136 |
+
uses: actions/checkout@v5
|
| 137 |
+
with:
|
| 138 |
+
lfs: false
|
| 139 |
+
|
| 140 |
+
- name: Set up Python 3.11
|
| 141 |
+
uses: actions/setup-python@v6
|
| 142 |
+
with:
|
| 143 |
+
python-version: "3.11"
|
| 144 |
+
cache: pip
|
| 145 |
+
cache-dependency-path: pyproject.toml
|
| 146 |
+
|
| 147 |
+
- name: Install embedding dependencies
|
| 148 |
+
run: |
|
| 149 |
+
python -m pip install --upgrade pip
|
| 150 |
+
python -m pip install ".[dev,embeddings]"
|
| 151 |
+
|
| 152 |
+
- name: Cache MiniLM model
|
| 153 |
+
uses: actions/cache@v4
|
| 154 |
+
with:
|
| 155 |
+
path: ~/.cache/huggingface
|
| 156 |
+
key: hf-sentence-transformers-all-MiniLM-L6-v2-v1
|
| 157 |
+
|
| 158 |
+
- name: Pre-download MiniLM model
|
| 159 |
+
env:
|
| 160 |
+
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
| 161 |
+
run: |
|
| 162 |
+
python - <<'PY'
|
| 163 |
+
import time
|
| 164 |
+
from sentence_transformers import SentenceTransformer
|
| 165 |
+
|
| 166 |
+
model = "sentence-transformers/all-MiniLM-L6-v2"
|
| 167 |
+
for attempt in range(1, 5):
|
| 168 |
+
try:
|
| 169 |
+
SentenceTransformer(model)
|
| 170 |
+
print(f"ready: {model}")
|
| 171 |
+
break
|
| 172 |
+
except Exception as exc:
|
| 173 |
+
print(f"attempt {attempt} failed: {exc}", flush=True)
|
| 174 |
+
if attempt == 4:
|
| 175 |
+
raise
|
| 176 |
+
time.sleep(15 * attempt)
|
| 177 |
+
PY
|
| 178 |
+
|
| 179 |
+
- name: Run similarity precision/recall gate
|
| 180 |
+
env:
|
| 181 |
+
CTX_REQUIRE_SIMILARITY_EVAL: "1"
|
| 182 |
+
run: pytest -q --no-cov -m integration src/tests/test_similarity_precision_recall.py
|
| 183 |
+
|
| 184 |
+
test:
|
| 185 |
+
name: pytest (${{ matrix.os }} / py${{ matrix.python-version }})
|
| 186 |
+
if: ${{ github.event_name != 'pull_request' }}
|
| 187 |
+
runs-on: ${{ matrix.os }}
|
| 188 |
+
strategy:
|
| 189 |
+
fail-fast: false
|
| 190 |
+
matrix:
|
| 191 |
+
include:
|
| 192 |
+
- os: ubuntu-latest
|
| 193 |
+
python-version: "3.12"
|
| 194 |
+
- os: windows-latest
|
| 195 |
+
python-version: "3.11"
|
| 196 |
+
- os: windows-latest
|
| 197 |
+
python-version: "3.12"
|
| 198 |
+
- os: macos-latest
|
| 199 |
+
python-version: "3.11"
|
| 200 |
+
- os: macos-latest
|
| 201 |
+
python-version: "3.12"
|
| 202 |
+
|
| 203 |
+
steps:
|
| 204 |
+
- name: Checkout
|
| 205 |
+
uses: actions/checkout@v5
|
| 206 |
+
with:
|
| 207 |
+
lfs: false
|
| 208 |
+
|
| 209 |
+
- name: Set up Python ${{ matrix.python-version }}
|
| 210 |
+
uses: actions/setup-python@v6
|
| 211 |
+
with:
|
| 212 |
+
python-version: ${{ matrix.python-version }}
|
| 213 |
+
cache: pip
|
| 214 |
+
cache-dependency-path: pyproject.toml
|
| 215 |
+
|
| 216 |
+
- name: Install dependencies
|
| 217 |
+
run: |
|
| 218 |
+
python -m pip install --upgrade pip
|
| 219 |
+
python -m pip install ".[dev]"
|
| 220 |
+
|
| 221 |
+
- name: Run tests without coverage
|
| 222 |
+
run: pytest -q -m "not browser and not integration" --no-cov
|
| 223 |
+
|
| 224 |
+
contract-compat:
|
| 225 |
+
name: "Contract compatibility (${{ matrix.os }})"
|
| 226 |
+
needs: classify
|
| 227 |
+
if: ${{ github.event_name != 'pull_request' || (needs.classify.outputs.docs_only != 'true' && needs.classify.outputs.graph_only != 'true') }}
|
| 228 |
+
runs-on: ${{ matrix.os }}
|
| 229 |
+
strategy:
|
| 230 |
+
fail-fast: false
|
| 231 |
+
matrix:
|
| 232 |
+
os: [windows-latest, macos-latest]
|
| 233 |
+
|
| 234 |
+
steps:
|
| 235 |
+
- name: Checkout
|
| 236 |
+
uses: actions/checkout@v5
|
| 237 |
+
with:
|
| 238 |
+
lfs: false
|
| 239 |
+
|
| 240 |
+
- name: Set up Python 3.12
|
| 241 |
+
uses: actions/setup-python@v6
|
| 242 |
+
with:
|
| 243 |
+
python-version: "3.12"
|
| 244 |
+
|
| 245 |
+
- name: Install dependencies
|
| 246 |
+
run: |
|
| 247 |
+
python -m pip install --upgrade pip
|
| 248 |
+
python -m pip install ".[dev]"
|
| 249 |
+
|
| 250 |
+
- name: Run cross-OS contract tests
|
| 251 |
+
run: python -m pytest -q --no-cov src/tests/test_clean_host_contract.py src/tests/test_package_scaffold.py
|
| 252 |
+
|
| 253 |
+
e2e-canary:
|
| 254 |
+
name: "A-Z alive-loop E2E canary"
|
| 255 |
+
needs: classify
|
| 256 |
+
if: ${{ github.event_name != 'pull_request' || (needs.classify.outputs.docs_only != 'true' && needs.classify.outputs.graph_only != 'true') }}
|
| 257 |
+
runs-on: ubuntu-latest
|
| 258 |
+
steps:
|
| 259 |
+
- name: Checkout
|
| 260 |
+
uses: actions/checkout@v5
|
| 261 |
+
with:
|
| 262 |
+
lfs: false
|
| 263 |
+
|
| 264 |
+
- name: Set up Python 3.11
|
| 265 |
+
uses: actions/setup-python@v6
|
| 266 |
+
with:
|
| 267 |
+
python-version: "3.11"
|
| 268 |
+
cache: pip
|
| 269 |
+
cache-dependency-path: pyproject.toml
|
| 270 |
+
|
| 271 |
+
- name: Install dependencies
|
| 272 |
+
run: |
|
| 273 |
+
python -m pip install --upgrade pip
|
| 274 |
+
python -m pip install ".[dev]"
|
| 275 |
+
|
| 276 |
+
- name: Run E2E + fuzz canary suite
|
| 277 |
+
# These two files pin the critical-path invariants:
|
| 278 |
+
# test_alive_loop_e2e.py: A-Z user journey (signals → bundle →
|
| 279 |
+
# install → unload → purge) — any handoff regression trips it.
|
| 280 |
+
# test_fuzz_yaml_rendering.py: property-based YAML injection
|
| 281 |
+
# coverage on install/enrich render_scalar.
|
| 282 |
+
# They live in the main matrix job already, but pulling them
|
| 283 |
+
# forward as a separate fail-fast canary surfaces the regression
|
| 284 |
+
# in seconds instead of waiting for the full test run.
|
| 285 |
+
run: |
|
| 286 |
+
pytest -q --no-cov \
|
| 287 |
+
src/tests/test_alive_loop_e2e.py \
|
| 288 |
+
src/tests/test_fuzz_yaml_rendering.py
|
| 289 |
+
|
| 290 |
+
docs-check:
|
| 291 |
+
name: "Docs strict build"
|
| 292 |
+
needs: classify
|
| 293 |
+
if: ${{ github.event_name == 'pull_request' && needs.classify.outputs.docs_changed == 'true' }}
|
| 294 |
+
runs-on: ubuntu-latest
|
| 295 |
+
steps:
|
| 296 |
+
- name: Checkout
|
| 297 |
+
uses: actions/checkout@v5
|
| 298 |
+
with:
|
| 299 |
+
lfs: false
|
| 300 |
+
|
| 301 |
+
- name: Set up Python 3.11
|
| 302 |
+
uses: actions/setup-python@v6
|
| 303 |
+
with:
|
| 304 |
+
python-version: "3.11"
|
| 305 |
+
cache: pip
|
| 306 |
+
cache-dependency-path: requirements-docs.txt
|
| 307 |
+
|
| 308 |
+
- name: Build docs strictly
|
| 309 |
+
run: |
|
| 310 |
+
python -m pip install --upgrade pip
|
| 311 |
+
python -m pip install -r requirements-docs.txt
|
| 312 |
+
python -m mkdocs build --strict
|
| 313 |
+
|
| 314 |
+
graph-check:
|
| 315 |
+
name: "Graph artifact check"
|
| 316 |
+
needs: classify
|
| 317 |
+
if: ${{ github.event_name == 'pull_request' && needs.classify.outputs.graph_artifact_changed == 'true' }}
|
| 318 |
+
runs-on: ubuntu-latest
|
| 319 |
+
steps:
|
| 320 |
+
- name: Checkout graph artifacts
|
| 321 |
+
uses: actions/checkout@v5
|
| 322 |
+
with:
|
| 323 |
+
lfs: false
|
| 324 |
+
|
| 325 |
+
- name: Set up Python
|
| 326 |
+
uses: actions/setup-python@v6
|
| 327 |
+
with:
|
| 328 |
+
python-version: "3.12"
|
| 329 |
+
|
| 330 |
+
- name: Install graph check dependencies
|
| 331 |
+
run: |
|
| 332 |
+
python -m pip install --upgrade pip
|
| 333 |
+
python -m pip install ".[dev]"
|
| 334 |
+
|
| 335 |
+
- name: Resolve graph artifacts from release assets
|
| 336 |
+
shell: bash
|
| 337 |
+
env:
|
| 338 |
+
GH_TOKEN: ${{ github.token }}
|
| 339 |
+
run: |
|
| 340 |
+
set -euo pipefail
|
| 341 |
+
echo "Resolving graph artifacts from matching release assets to avoid Git LFS bandwidth."
|
| 342 |
+
python - <<'PY'
|
| 343 |
+
import hashlib
|
| 344 |
+
import json
|
| 345 |
+
import os
|
| 346 |
+
from pathlib import Path
|
| 347 |
+
import subprocess
|
| 348 |
+
import time
|
| 349 |
+
import urllib.request
|
| 350 |
+
|
| 351 |
+
repo = os.environ["GITHUB_REPOSITORY"]
|
| 352 |
+
release_asset_wait_seconds = 300
|
| 353 |
+
release_asset_poll_seconds = 10
|
| 354 |
+
expected_graph_assets = {
|
| 355 |
+
"graph/wiki-graph.tar.gz": {
|
| 356 |
+
"sha256": "91b30795e7d200cf31a62a8749969d12658f5f74636d2de06d6b2b24b393c12f",
|
| 357 |
+
"size": 457545675,
|
| 358 |
+
},
|
| 359 |
+
"graph/wiki-graph-runtime.tar.gz": {
|
| 360 |
+
"sha256": "334fb19bace3fd6e4b92087850f17297fb248032957d123f3f1432dfde2e36c0",
|
| 361 |
+
"size": 175773376,
|
| 362 |
+
},
|
| 363 |
+
}
|
| 364 |
+
|
| 365 |
+
def load_releases() -> list[dict]:
|
| 366 |
+
return json.loads(subprocess.check_output(
|
| 367 |
+
["gh", "api", f"repos/{repo}/releases?per_page=50"],
|
| 368 |
+
text=True,
|
| 369 |
+
))
|
| 370 |
+
|
| 371 |
+
def hydrate_from_release(path_name: str, hydrated_min_size: int) -> None:
|
| 372 |
+
graph_tar = Path(path_name)
|
| 373 |
+
fallback = expected_graph_assets[path_name]
|
| 374 |
+
expected_oid = fallback["sha256"]
|
| 375 |
+
expected_size = int(fallback["size"])
|
| 376 |
+
if graph_tar.exists():
|
| 377 |
+
pointer = graph_tar.read_text(encoding="utf-8", errors="replace")
|
| 378 |
+
for line in pointer.splitlines():
|
| 379 |
+
if line.startswith("oid sha256:"):
|
| 380 |
+
expected_oid = line.split(":", 1)[1].strip()
|
| 381 |
+
elif line.startswith("size "):
|
| 382 |
+
expected_size = int(line.split(" ", 1)[1].strip())
|
| 383 |
+
if not pointer.startswith("version https://git-lfs.github.com/spec/v1") and graph_tar.stat().st_size > hydrated_min_size:
|
| 384 |
+
print(f"{graph_tar} is already hydrated")
|
| 385 |
+
return
|
| 386 |
+
|
| 387 |
+
deadline = time.monotonic() + release_asset_wait_seconds
|
| 388 |
+
while True:
|
| 389 |
+
candidates = []
|
| 390 |
+
for release in load_releases():
|
| 391 |
+
tag_name = str(release.get("tag_name") or "")
|
| 392 |
+
is_graph_cache = tag_name.startswith("graph-artifacts-")
|
| 393 |
+
if release.get("draft") or (
|
| 394 |
+
release.get("prerelease") and not is_graph_cache
|
| 395 |
+
):
|
| 396 |
+
continue
|
| 397 |
+
for asset in release.get("assets", []):
|
| 398 |
+
if asset.get("name") != graph_tar.name:
|
| 399 |
+
continue
|
| 400 |
+
digest = str(asset.get("digest") or "")
|
| 401 |
+
size = int(asset.get("size") or 0)
|
| 402 |
+
if size != expected_size:
|
| 403 |
+
continue
|
| 404 |
+
if digest and digest != f"sha256:{expected_oid}":
|
| 405 |
+
continue
|
| 406 |
+
candidates.append((tag_name, asset))
|
| 407 |
+
|
| 408 |
+
if candidates:
|
| 409 |
+
break
|
| 410 |
+
if time.monotonic() >= deadline:
|
| 411 |
+
raise SystemExit(
|
| 412 |
+
f"No previous release asset matches {path_name} "
|
| 413 |
+
f"sha256:{expected_oid} size:{expected_size}"
|
| 414 |
+
)
|
| 415 |
+
print(
|
| 416 |
+
f"Waiting for matching release asset {graph_tar.name} "
|
| 417 |
+
f"sha256:{expected_oid} size:{expected_size}"
|
| 418 |
+
)
|
| 419 |
+
time.sleep(release_asset_poll_seconds)
|
| 420 |
+
|
| 421 |
+
source_tag, asset = candidates[0]
|
| 422 |
+
tmp = graph_tar.with_name(f"{graph_tar.name}.download")
|
| 423 |
+
sha = hashlib.sha256()
|
| 424 |
+
total = 0
|
| 425 |
+
with urllib.request.urlopen(asset["browser_download_url"], timeout=300) as resp: # noqa: S310
|
| 426 |
+
with tmp.open("wb") as fh:
|
| 427 |
+
while True:
|
| 428 |
+
chunk = resp.read(1024 * 1024)
|
| 429 |
+
if not chunk:
|
| 430 |
+
break
|
| 431 |
+
sha.update(chunk)
|
| 432 |
+
total += len(chunk)
|
| 433 |
+
fh.write(chunk)
|
| 434 |
+
actual_oid = sha.hexdigest()
|
| 435 |
+
if actual_oid != expected_oid or total != expected_size:
|
| 436 |
+
tmp.unlink(missing_ok=True)
|
| 437 |
+
raise SystemExit(
|
| 438 |
+
f"Downloaded {path_name} does not match LFS pointer: "
|
| 439 |
+
f"sha256:{actual_oid} size:{total}"
|
| 440 |
+
)
|
| 441 |
+
tmp.replace(graph_tar)
|
| 442 |
+
print(
|
| 443 |
+
f"Hydrated {path_name} from {source_tag} release asset "
|
| 444 |
+
f"sha256:{actual_oid} size:{total}"
|
| 445 |
+
)
|
| 446 |
+
|
| 447 |
+
hydrate_from_release("graph/wiki-graph.tar.gz", 100_000_000)
|
| 448 |
+
hydrate_from_release("graph/wiki-graph-runtime.tar.gz", 10_000_000)
|
| 449 |
+
PY
|
| 450 |
+
|
| 451 |
+
- name: Validate shipped graph artifacts
|
| 452 |
+
run: |
|
| 453 |
+
python src/validate_graph_artifacts.py \
|
| 454 |
+
--graph-dir graph \
|
| 455 |
+
--deep \
|
| 456 |
+
--min-nodes 100000 \
|
| 457 |
+
--min-edges 2000000 \
|
| 458 |
+
--min-skills-sh-nodes 89000 \
|
| 459 |
+
--min-semantic-edges 1000000 \
|
| 460 |
+
--expected-nodes 102928 \
|
| 461 |
+
--expected-edges 2913960 \
|
| 462 |
+
--expected-semantic-edges 1683193 \
|
| 463 |
+
--expected-harness-nodes 207 \
|
| 464 |
+
--expected-skills-sh-nodes 89471 \
|
| 465 |
+
--expected-skills-sh-catalog-entries 89465 \
|
| 466 |
+
--expected-skills-sh-converted 89465 \
|
| 467 |
+
--expected-skill-pages 91464 \
|
| 468 |
+
--expected-agent-pages 467 \
|
| 469 |
+
--expected-mcp-pages 10790 \
|
| 470 |
+
--expected-harness-pages 207 \
|
| 471 |
+
--line-threshold 180 \
|
| 472 |
+
--max-stage-lines 40
|
| 473 |
+
|
| 474 |
+
- name: Validate README and docs stats
|
| 475 |
+
run: python src/update_repo_stats.py --check
|
| 476 |
+
|
| 477 |
+
browser-security:
|
| 478 |
+
name: "Browser monitor security"
|
| 479 |
+
needs: classify
|
| 480 |
+
if: ${{ github.event_name == 'push' || needs.classify.outputs.browser_changed == 'true' }}
|
| 481 |
+
runs-on: ubuntu-latest
|
| 482 |
+
steps:
|
| 483 |
+
- name: Checkout
|
| 484 |
+
uses: actions/checkout@v5
|
| 485 |
+
with:
|
| 486 |
+
lfs: false
|
| 487 |
+
|
| 488 |
+
- name: Set up Python 3.12
|
| 489 |
+
uses: actions/setup-python@v6
|
| 490 |
+
with:
|
| 491 |
+
python-version: "3.12"
|
| 492 |
+
cache: pip
|
| 493 |
+
cache-dependency-path: pyproject.toml
|
| 494 |
+
|
| 495 |
+
- name: Install browser Python dependencies
|
| 496 |
+
run: |
|
| 497 |
+
python -m pip install --upgrade pip
|
| 498 |
+
python -m pip install ".[dev,browser]"
|
| 499 |
+
|
| 500 |
+
- name: Detect Playwright version
|
| 501 |
+
id: playwright
|
| 502 |
+
run: |
|
| 503 |
+
python - <<'PY' >> "$GITHUB_OUTPUT"
|
| 504 |
+
from importlib.metadata import version
|
| 505 |
+
|
| 506 |
+
print(f"version={version('playwright')}")
|
| 507 |
+
PY
|
| 508 |
+
|
| 509 |
+
- name: Cache Playwright browsers
|
| 510 |
+
uses: actions/cache@v4
|
| 511 |
+
with:
|
| 512 |
+
path: ~/.cache/ms-playwright
|
| 513 |
+
key: playwright-${{ runner.os }}-${{ steps.playwright.outputs.version }}
|
| 514 |
+
|
| 515 |
+
- name: Install browser runtime
|
| 516 |
+
run: |
|
| 517 |
+
python -m playwright install-deps chromium
|
| 518 |
+
python -m playwright install chromium
|
| 519 |
+
|
| 520 |
+
- name: Run browser security tests
|
| 521 |
+
run: pytest -q --no-cov -m browser src/tests/test_ctx_monitor_browser.py
|
| 522 |
+
|
| 523 |
+
package-build:
|
| 524 |
+
name: "Build wheel package"
|
| 525 |
+
needs: classify
|
| 526 |
+
if: ${{ github.event_name != 'pull_request' || (needs.classify.outputs.docs_only != 'true' && needs.classify.outputs.graph_only != 'true') }}
|
| 527 |
+
runs-on: ubuntu-latest
|
| 528 |
+
steps:
|
| 529 |
+
- name: Checkout
|
| 530 |
+
uses: actions/checkout@v5
|
| 531 |
+
with:
|
| 532 |
+
lfs: false
|
| 533 |
+
|
| 534 |
+
- name: Set up Python 3.12
|
| 535 |
+
uses: actions/setup-python@v6
|
| 536 |
+
with:
|
| 537 |
+
python-version: "3.12"
|
| 538 |
+
cache: pip
|
| 539 |
+
cache-dependency-path: pyproject.toml
|
| 540 |
+
|
| 541 |
+
- name: Build and inspect wheel
|
| 542 |
+
run: |
|
| 543 |
+
python -m pip install --upgrade pip
|
| 544 |
+
python -m pip install build twine
|
| 545 |
+
python -m build
|
| 546 |
+
python -m twine check dist/*
|
| 547 |
+
python - <<'PY'
|
| 548 |
+
from pathlib import Path
|
| 549 |
+
|
| 550 |
+
wheels = sorted(Path("dist").glob("*.whl"))
|
| 551 |
+
if len(wheels) != 1:
|
| 552 |
+
raise SystemExit(f"expected exactly one wheel, found {len(wheels)}")
|
| 553 |
+
wheel = wheels[0]
|
| 554 |
+
if not wheel.name.endswith("-py3-none-any.whl"):
|
| 555 |
+
message = f"expected a universal pure-Python wheel, got {wheel.name}"
|
| 556 |
+
raise SystemExit(message)
|
| 557 |
+
print(f"built package-smoke wheel: {wheel.name}")
|
| 558 |
+
PY
|
| 559 |
+
|
| 560 |
+
- name: Upload wheel artifact
|
| 561 |
+
uses: actions/upload-artifact@v7
|
| 562 |
+
with:
|
| 563 |
+
name: package-smoke-wheel
|
| 564 |
+
path: dist/*.whl
|
| 565 |
+
retention-days: 7
|
| 566 |
+
|
| 567 |
+
package-smoke:
|
| 568 |
+
name: "Wheel package smoke (${{ matrix.os }})"
|
| 569 |
+
needs:
|
| 570 |
+
- classify
|
| 571 |
+
- package-build
|
| 572 |
+
if: ${{ github.event_name != 'pull_request' || (needs.classify.outputs.docs_only != 'true' && needs.classify.outputs.graph_only != 'true') }}
|
| 573 |
+
runs-on: ${{ matrix.os }}
|
| 574 |
+
strategy:
|
| 575 |
+
fail-fast: false
|
| 576 |
+
matrix:
|
| 577 |
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
| 578 |
+
defaults:
|
| 579 |
+
run:
|
| 580 |
+
shell: bash
|
| 581 |
+
steps:
|
| 582 |
+
- name: Checkout
|
| 583 |
+
uses: actions/checkout@v5
|
| 584 |
+
with:
|
| 585 |
+
lfs: false
|
| 586 |
+
|
| 587 |
+
- name: Set up Python 3.12
|
| 588 |
+
uses: actions/setup-python@v6
|
| 589 |
+
with:
|
| 590 |
+
python-version: "3.12"
|
| 591 |
+
cache: pip
|
| 592 |
+
cache-dependency-path: pyproject.toml
|
| 593 |
+
|
| 594 |
+
- name: Download wheel artifact
|
| 595 |
+
uses: actions/download-artifact@v7
|
| 596 |
+
with:
|
| 597 |
+
name: package-smoke-wheel
|
| 598 |
+
path: dist
|
| 599 |
+
|
| 600 |
+
- name: Install wheel in clean venv
|
| 601 |
+
run: |
|
| 602 |
+
python -m venv .venv-smoke
|
| 603 |
+
if [[ "$RUNNER_OS" == "Windows" ]]; then
|
| 604 |
+
. .venv-smoke/Scripts/activate
|
| 605 |
+
else
|
| 606 |
+
. .venv-smoke/bin/activate
|
| 607 |
+
fi
|
| 608 |
+
python -m pip install --upgrade pip
|
| 609 |
+
python -m pip install dist/*.whl
|
| 610 |
+
python -m pip check
|
| 611 |
+
python - <<'PY'
|
| 612 |
+
import tomllib
|
| 613 |
+
from importlib.metadata import entry_points, version
|
| 614 |
+
|
| 615 |
+
import ctx
|
| 616 |
+
|
| 617 |
+
with open("pyproject.toml", "rb") as fh:
|
| 618 |
+
expected_scripts = set(tomllib.load(fh)["project"]["scripts"])
|
| 619 |
+
|
| 620 |
+
dist_version = version("claude-ctx")
|
| 621 |
+
if ctx.__version__ != dist_version:
|
| 622 |
+
raise SystemExit(
|
| 623 |
+
f"ctx.__version__={ctx.__version__!r} != metadata {dist_version!r}"
|
| 624 |
+
)
|
| 625 |
+
|
| 626 |
+
script_eps = {
|
| 627 |
+
ep.name: ep
|
| 628 |
+
for ep in entry_points(group="console_scripts")
|
| 629 |
+
if ep.name == "ctx" or ep.name.startswith("ctx-")
|
| 630 |
+
}
|
| 631 |
+
missing = sorted(expected_scripts - set(script_eps))
|
| 632 |
+
extra = sorted(set(script_eps) - expected_scripts)
|
| 633 |
+
if missing or extra:
|
| 634 |
+
raise SystemExit(
|
| 635 |
+
"wheel console-script surface mismatch\n"
|
| 636 |
+
f"missing: {missing}\n"
|
| 637 |
+
f"extra: {extra}"
|
| 638 |
+
)
|
| 639 |
+
failures = []
|
| 640 |
+
for ep in script_eps.values():
|
| 641 |
+
try:
|
| 642 |
+
ep.load()
|
| 643 |
+
except Exception as exc:
|
| 644 |
+
failures.append(f"{ep.name}: {exc!r}")
|
| 645 |
+
if failures:
|
| 646 |
+
raise SystemExit("console script load failures:\n" + "\n".join(failures))
|
| 647 |
+
|
| 648 |
+
unsafe_help = {"ctx-mcp-server"}
|
| 649 |
+
safe_help = sorted(expected_scripts - unsafe_help)
|
| 650 |
+
with open(
|
| 651 |
+
"ctx-console-help.txt", "w", encoding="utf-8", newline="\n"
|
| 652 |
+
) as fh:
|
| 653 |
+
fh.write("\n".join(safe_help) + "\n")
|
| 654 |
+
print(
|
| 655 |
+
f"loaded {len(script_eps)} ctx console scripts from wheel "
|
| 656 |
+
f"{dist_version}; help-smoke={len(safe_help)}"
|
| 657 |
+
)
|
| 658 |
+
PY
|
| 659 |
+
while IFS= read -r cmd; do
|
| 660 |
+
cmd="${cmd%$'\r'}"
|
| 661 |
+
[[ -z "$cmd" ]] && continue
|
| 662 |
+
echo "help smoke: $cmd"
|
| 663 |
+
"$cmd" --help >/dev/null
|
| 664 |
+
done < ctx-console-help.txt
|
| 665 |
+
|
| 666 |
+
clean-host-contract:
|
| 667 |
+
name: "Clean host contract"
|
| 668 |
+
needs: classify
|
| 669 |
+
if: ${{ github.event_name != 'pull_request' || (needs.classify.outputs.docs_only != 'true' && needs.classify.outputs.graph_only != 'true') }}
|
| 670 |
+
runs-on: ubuntu-latest
|
| 671 |
+
timeout-minutes: 25
|
| 672 |
+
steps:
|
| 673 |
+
- name: Checkout
|
| 674 |
+
uses: actions/checkout@v5
|
| 675 |
+
with:
|
| 676 |
+
lfs: false
|
| 677 |
+
|
| 678 |
+
- name: Set up Python 3.12
|
| 679 |
+
uses: actions/setup-python@v6
|
| 680 |
+
with:
|
| 681 |
+
python-version: "3.12"
|
| 682 |
+
cache: pip
|
| 683 |
+
cache-dependency-path: pyproject.toml
|
| 684 |
+
|
| 685 |
+
- name: Upgrade pip
|
| 686 |
+
run: python -m pip install --upgrade pip
|
| 687 |
+
|
| 688 |
+
- name: Run clean-host contract
|
| 689 |
+
run: python scripts/clean_host_contract.py --fast
|
| 690 |
+
|
| 691 |
+
no-test-no-merge:
|
| 692 |
+
name: "Contract changes must touch tests"
|
| 693 |
+
needs: classify
|
| 694 |
+
runs-on: ubuntu-latest
|
| 695 |
+
if: ${{ github.event_name == 'pull_request' && needs.classify.outputs.docs_only != 'true' && needs.classify.outputs.graph_only != 'true' }}
|
| 696 |
+
steps:
|
| 697 |
+
- name: Checkout with full history
|
| 698 |
+
uses: actions/checkout@v5
|
| 699 |
+
with:
|
| 700 |
+
fetch-depth: 0
|
| 701 |
+
lfs: false
|
| 702 |
+
|
| 703 |
+
- name: Enforce test-coverage-per-PR policy
|
| 704 |
+
# Policy: any product or CI/package contract change
|
| 705 |
+
# must be accompanied by at least one test file change in the
|
| 706 |
+
# same PR. Exemptions:
|
| 707 |
+
# - Pure docs / comment changes (matched by a trivial heuristic)
|
| 708 |
+
# - Config JSON (src/config.json) — schema changes get tests
|
| 709 |
+
# for the accessor, not the JSON itself.
|
| 710 |
+
# - Changes ONLY inside __pycache__, .pyc files, etc.
|
| 711 |
+
run: |
|
| 712 |
+
set -euo pipefail
|
| 713 |
+
BASE="${{ github.event.pull_request.base.sha }}"
|
| 714 |
+
HEAD="${{ github.event.pull_request.head.sha }}"
|
| 715 |
+
LABELS='${{ toJson(github.event.pull_request.labels.*.name) }}'
|
| 716 |
+
python scripts/ci_no_test_policy.py \
|
| 717 |
+
--base "$BASE" \
|
| 718 |
+
--head "$HEAD" \
|
| 719 |
+
--labels-json "$LABELS"
|
| 720 |
+
|
| 721 |
+
ci-required:
|
| 722 |
+
name: "CI required"
|
| 723 |
+
runs-on: ubuntu-latest
|
| 724 |
+
needs:
|
| 725 |
+
- classify
|
| 726 |
+
- static
|
| 727 |
+
- unit-linux
|
| 728 |
+
- similarity-integration
|
| 729 |
+
- test
|
| 730 |
+
- contract-compat
|
| 731 |
+
- e2e-canary
|
| 732 |
+
- docs-check
|
| 733 |
+
- graph-check
|
| 734 |
+
- browser-security
|
| 735 |
+
- package-smoke
|
| 736 |
+
- package-build
|
| 737 |
+
- clean-host-contract
|
| 738 |
+
- no-test-no-merge
|
| 739 |
+
if: ${{ always() }}
|
| 740 |
+
steps:
|
| 741 |
+
- name: Checkout
|
| 742 |
+
uses: actions/checkout@v5
|
| 743 |
+
with:
|
| 744 |
+
lfs: false
|
| 745 |
+
|
| 746 |
+
- name: Check required job results
|
| 747 |
+
env:
|
| 748 |
+
EVENT_NAME: ${{ github.event_name }}
|
| 749 |
+
NEEDS_JSON: ${{ toJson(needs) }}
|
| 750 |
+
run: python scripts/ci_required.py
|
.github/workflows/xdist-experiment.yml
CHANGED
|
@@ -1,90 +1,90 @@
|
|
| 1 |
-
name: Xdist experiment
|
| 2 |
-
|
| 3 |
-
on:
|
| 4 |
-
workflow_dispatch:
|
| 5 |
-
pull_request:
|
| 6 |
-
branches: [main]
|
| 7 |
-
paths:
|
| 8 |
-
- ".github/workflows/xdist-experiment.yml"
|
| 9 |
-
|
| 10 |
-
env:
|
| 11 |
-
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
| 12 |
-
|
| 13 |
-
concurrency:
|
| 14 |
-
group: xdist-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
| 15 |
-
cancel-in-progress: true
|
| 16 |
-
|
| 17 |
-
permissions:
|
| 18 |
-
contents: read
|
| 19 |
-
|
| 20 |
-
jobs:
|
| 21 |
-
xdist:
|
| 22 |
-
name: xdist (${{ matrix.os }} / py${{ matrix.python-version }})
|
| 23 |
-
runs-on: ${{ matrix.os }}
|
| 24 |
-
strategy:
|
| 25 |
-
fail-fast: false
|
| 26 |
-
matrix:
|
| 27 |
-
os: [ubuntu-latest, windows-latest, macos-latest]
|
| 28 |
-
python-version: ["3.12"]
|
| 29 |
-
defaults:
|
| 30 |
-
run:
|
| 31 |
-
shell: bash
|
| 32 |
-
steps:
|
| 33 |
-
- name: Checkout
|
| 34 |
-
uses: actions/checkout@v5
|
| 35 |
-
with:
|
| 36 |
-
lfs: false
|
| 37 |
-
|
| 38 |
-
- name: Set up Python ${{ matrix.python-version }}
|
| 39 |
-
uses: actions/setup-python@v6
|
| 40 |
-
with:
|
| 41 |
-
python-version: ${{ matrix.python-version }}
|
| 42 |
-
cache: pip
|
| 43 |
-
cache-dependency-path: pyproject.toml
|
| 44 |
-
|
| 45 |
-
- name: Install experiment dependencies
|
| 46 |
-
run: |
|
| 47 |
-
python -m pip install --upgrade pip
|
| 48 |
-
python -m pip install ".[dev]" pytest-xdist
|
| 49 |
-
|
| 50 |
-
- name: Run xdist experiment
|
| 51 |
-
run: |
|
| 52 |
-
set +e
|
| 53 |
-
start="$(python - <<'PY'
|
| 54 |
-
import time
|
| 55 |
-
print(time.monotonic())
|
| 56 |
-
PY
|
| 57 |
-
)"
|
| 58 |
-
python -m pytest -q -m "not browser" --no-cov \
|
| 59 |
-
-n 2 \
|
| 60 |
-
--dist=loadfile \
|
| 61 |
-
--max-worker-restart=0 \
|
| 62 |
-
--durations=25
|
| 63 |
-
status="$?"
|
| 64 |
-
end="$(python - <<'PY'
|
| 65 |
-
import time
|
| 66 |
-
print(time.monotonic())
|
| 67 |
-
PY
|
| 68 |
-
)"
|
| 69 |
-
elapsed="$(python - "$start" "$end" <<'PY'
|
| 70 |
-
import sys
|
| 71 |
-
start = float(sys.argv[1])
|
| 72 |
-
end = float(sys.argv[2])
|
| 73 |
-
print(f"{end - start:.1f}")
|
| 74 |
-
PY
|
| 75 |
-
)"
|
| 76 |
-
|
| 77 |
-
{
|
| 78 |
-
echo "## pytest-xdist experiment"
|
| 79 |
-
echo ""
|
| 80 |
-
echo "- OS: $RUNNER_OS"
|
| 81 |
-
echo "- Python: ${{ matrix.python-version }}"
|
| 82 |
-
echo "- Command: \`python -m pytest -q -m \"not browser\" --no-cov -n 2 --dist=loadfile --max-worker-restart=0 --durations=25\`"
|
| 83 |
-
echo "- Exit code: $status"
|
| 84 |
-
echo "- Elapsed seconds: $elapsed"
|
| 85 |
-
} >> "$GITHUB_STEP_SUMMARY"
|
| 86 |
-
|
| 87 |
-
if [ "$status" -ne 0 ]; then
|
| 88 |
-
echo "::warning::xdist experiment failed on $RUNNER_OS with exit code $status"
|
| 89 |
-
fi
|
| 90 |
-
exit "$status"
|
|
|
|
| 1 |
+
name: Xdist experiment
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
workflow_dispatch:
|
| 5 |
+
pull_request:
|
| 6 |
+
branches: [main]
|
| 7 |
+
paths:
|
| 8 |
+
- ".github/workflows/xdist-experiment.yml"
|
| 9 |
+
|
| 10 |
+
env:
|
| 11 |
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
| 12 |
+
|
| 13 |
+
concurrency:
|
| 14 |
+
group: xdist-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
| 15 |
+
cancel-in-progress: true
|
| 16 |
+
|
| 17 |
+
permissions:
|
| 18 |
+
contents: read
|
| 19 |
+
|
| 20 |
+
jobs:
|
| 21 |
+
xdist:
|
| 22 |
+
name: xdist (${{ matrix.os }} / py${{ matrix.python-version }})
|
| 23 |
+
runs-on: ${{ matrix.os }}
|
| 24 |
+
strategy:
|
| 25 |
+
fail-fast: false
|
| 26 |
+
matrix:
|
| 27 |
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
| 28 |
+
python-version: ["3.12"]
|
| 29 |
+
defaults:
|
| 30 |
+
run:
|
| 31 |
+
shell: bash
|
| 32 |
+
steps:
|
| 33 |
+
- name: Checkout
|
| 34 |
+
uses: actions/checkout@v5
|
| 35 |
+
with:
|
| 36 |
+
lfs: false
|
| 37 |
+
|
| 38 |
+
- name: Set up Python ${{ matrix.python-version }}
|
| 39 |
+
uses: actions/setup-python@v6
|
| 40 |
+
with:
|
| 41 |
+
python-version: ${{ matrix.python-version }}
|
| 42 |
+
cache: pip
|
| 43 |
+
cache-dependency-path: pyproject.toml
|
| 44 |
+
|
| 45 |
+
- name: Install experiment dependencies
|
| 46 |
+
run: |
|
| 47 |
+
python -m pip install --upgrade pip
|
| 48 |
+
python -m pip install ".[dev]" pytest-xdist
|
| 49 |
+
|
| 50 |
+
- name: Run xdist experiment
|
| 51 |
+
run: |
|
| 52 |
+
set +e
|
| 53 |
+
start="$(python - <<'PY'
|
| 54 |
+
import time
|
| 55 |
+
print(time.monotonic())
|
| 56 |
+
PY
|
| 57 |
+
)"
|
| 58 |
+
python -m pytest -q -m "not browser" --no-cov \
|
| 59 |
+
-n 2 \
|
| 60 |
+
--dist=loadfile \
|
| 61 |
+
--max-worker-restart=0 \
|
| 62 |
+
--durations=25
|
| 63 |
+
status="$?"
|
| 64 |
+
end="$(python - <<'PY'
|
| 65 |
+
import time
|
| 66 |
+
print(time.monotonic())
|
| 67 |
+
PY
|
| 68 |
+
)"
|
| 69 |
+
elapsed="$(python - "$start" "$end" <<'PY'
|
| 70 |
+
import sys
|
| 71 |
+
start = float(sys.argv[1])
|
| 72 |
+
end = float(sys.argv[2])
|
| 73 |
+
print(f"{end - start:.1f}")
|
| 74 |
+
PY
|
| 75 |
+
)"
|
| 76 |
+
|
| 77 |
+
{
|
| 78 |
+
echo "## pytest-xdist experiment"
|
| 79 |
+
echo ""
|
| 80 |
+
echo "- OS: $RUNNER_OS"
|
| 81 |
+
echo "- Python: ${{ matrix.python-version }}"
|
| 82 |
+
echo "- Command: \`python -m pytest -q -m \"not browser\" --no-cov -n 2 --dist=loadfile --max-worker-restart=0 --durations=25\`"
|
| 83 |
+
echo "- Exit code: $status"
|
| 84 |
+
echo "- Elapsed seconds: $elapsed"
|
| 85 |
+
} >> "$GITHUB_STEP_SUMMARY"
|
| 86 |
+
|
| 87 |
+
if [ "$status" -ne 0 ]; then
|
| 88 |
+
echo "::warning::xdist experiment failed on $RUNNER_OS with exit code $status"
|
| 89 |
+
fi
|
| 90 |
+
exit "$status"
|
.gitignore
CHANGED
|
@@ -1,103 +1,103 @@
|
|
| 1 |
-
# Babysitter orchestration — runs, state, and local SDK install
|
| 2 |
-
.a5c/
|
| 3 |
-
|
| 4 |
-
# Python
|
| 5 |
-
__pycache__/
|
| 6 |
-
*.py[cod]
|
| 7 |
-
*.pyo
|
| 8 |
-
*.pyd
|
| 9 |
-
.Python
|
| 10 |
-
*.egg-info/
|
| 11 |
-
dist/
|
| 12 |
-
build/
|
| 13 |
-
*.egg
|
| 14 |
-
.eggs/
|
| 15 |
-
.venv/
|
| 16 |
-
.venv-release/
|
| 17 |
-
.venv-smoke/
|
| 18 |
-
venv/
|
| 19 |
-
env/
|
| 20 |
-
.env
|
| 21 |
-
|
| 22 |
-
# Node / npm
|
| 23 |
-
node_modules/
|
| 24 |
-
npm-debug.log*
|
| 25 |
-
yarn-debug.log*
|
| 26 |
-
yarn-error.log*
|
| 27 |
-
package-lock.json
|
| 28 |
-
|
| 29 |
-
# Temp / runtime outputs
|
| 30 |
-
/tmp/
|
| 31 |
-
*.tmp
|
| 32 |
-
*.bak
|
| 33 |
-
*.swp
|
| 34 |
-
*.swo
|
| 35 |
-
.DS_Store
|
| 36 |
-
Thumbs.db
|
| 37 |
-
|
| 38 |
-
# Skill system runtime files (generated, not source)
|
| 39 |
-
/skills/skill-router/check-gates.md
|
| 40 |
-
/skills/skill-router/failure-log.md
|
| 41 |
-
|
| 42 |
-
# Agent directives (cloned from global ~/.claude/CLAUDE.md — personal, not for sharing)
|
| 43 |
-
CLAUDE.md
|
| 44 |
-
|
| 45 |
-
# Per-workspace Claude Code settings (local permissions, user-specific)
|
| 46 |
-
.claude/
|
| 47 |
-
|
| 48 |
-
# Wiki (runtime knowledge store, not source)
|
| 49 |
-
# Uncomment to exclude the wiki from the repo:
|
| 50 |
-
# ~/.claude/skill-wiki/
|
| 51 |
-
|
| 52 |
-
# Intent log and pending signals (session runtime)
|
| 53 |
-
# These live in ~/.claude/ so already outside the repo
|
| 54 |
-
|
| 55 |
-
# Internal review artifacts and working notes (not public docs)
|
| 56 |
-
/docs/reports/
|
| 57 |
-
/docs/plans/20[0-9][0-9]-*/
|
| 58 |
-
/docs/plans/20[0-9][0-9]-*.md
|
| 59 |
-
/internal/reviews/
|
| 60 |
-
|
| 61 |
-
# Generated graph review artifacts (regenerate locally when needed)
|
| 62 |
-
/graph/*-report.md
|
| 63 |
-
/graph/*-report.json
|
| 64 |
-
/graph/*-report.json.gz
|
| 65 |
-
/graph/*.staged
|
| 66 |
-
/graph/*.partial
|
| 67 |
-
/graph/*.lock
|
| 68 |
-
*.promotion.json
|
| 69 |
-
/graph/tag-backfill.md
|
| 70 |
-
/graph/tag-backfill.json
|
| 71 |
-
|
| 72 |
-
# Secrets / config overrides
|
| 73 |
-
skill-system-config.json
|
| 74 |
-
.env.local
|
| 75 |
-
.env.development
|
| 76 |
-
.env.production
|
| 77 |
-
.envrc
|
| 78 |
-
|
| 79 |
-
# Strix security scanner (local venv, instructions, output)
|
| 80 |
-
.strix-venv/
|
| 81 |
-
.strix-instructions.txt
|
| 82 |
-
.strix-output/
|
| 83 |
-
.strix-out/
|
| 84 |
-
.strix-run.log
|
| 85 |
-
.strix-preview-wiki/
|
| 86 |
-
.imported-deployed/
|
| 87 |
-
strix_runs/
|
| 88 |
-
|
| 89 |
-
# Test coverage
|
| 90 |
-
.coverage
|
| 91 |
-
.ci-preflight-dist/
|
| 92 |
-
htmlcov/
|
| 93 |
-
.pytest_cache/
|
| 94 |
-
.mypy_cache/
|
| 95 |
-
.mypy_cache*/
|
| 96 |
-
.ruff_cache/
|
| 97 |
-
.hypothesis/
|
| 98 |
-
|
| 99 |
-
# Local code-review graph experiments
|
| 100 |
-
.code-review-graph/
|
| 101 |
-
|
| 102 |
-
# MkDocs build output
|
| 103 |
-
/site/
|
|
|
|
| 1 |
+
# Babysitter orchestration — runs, state, and local SDK install
|
| 2 |
+
.a5c/
|
| 3 |
+
|
| 4 |
+
# Python
|
| 5 |
+
__pycache__/
|
| 6 |
+
*.py[cod]
|
| 7 |
+
*.pyo
|
| 8 |
+
*.pyd
|
| 9 |
+
.Python
|
| 10 |
+
*.egg-info/
|
| 11 |
+
dist/
|
| 12 |
+
build/
|
| 13 |
+
*.egg
|
| 14 |
+
.eggs/
|
| 15 |
+
.venv/
|
| 16 |
+
.venv-release/
|
| 17 |
+
.venv-smoke/
|
| 18 |
+
venv/
|
| 19 |
+
env/
|
| 20 |
+
.env
|
| 21 |
+
|
| 22 |
+
# Node / npm
|
| 23 |
+
node_modules/
|
| 24 |
+
npm-debug.log*
|
| 25 |
+
yarn-debug.log*
|
| 26 |
+
yarn-error.log*
|
| 27 |
+
package-lock.json
|
| 28 |
+
|
| 29 |
+
# Temp / runtime outputs
|
| 30 |
+
/tmp/
|
| 31 |
+
*.tmp
|
| 32 |
+
*.bak
|
| 33 |
+
*.swp
|
| 34 |
+
*.swo
|
| 35 |
+
.DS_Store
|
| 36 |
+
Thumbs.db
|
| 37 |
+
|
| 38 |
+
# Skill system runtime files (generated, not source)
|
| 39 |
+
/skills/skill-router/check-gates.md
|
| 40 |
+
/skills/skill-router/failure-log.md
|
| 41 |
+
|
| 42 |
+
# Agent directives (cloned from global ~/.claude/CLAUDE.md — personal, not for sharing)
|
| 43 |
+
CLAUDE.md
|
| 44 |
+
|
| 45 |
+
# Per-workspace Claude Code settings (local permissions, user-specific)
|
| 46 |
+
.claude/
|
| 47 |
+
|
| 48 |
+
# Wiki (runtime knowledge store, not source)
|
| 49 |
+
# Uncomment to exclude the wiki from the repo:
|
| 50 |
+
# ~/.claude/skill-wiki/
|
| 51 |
+
|
| 52 |
+
# Intent log and pending signals (session runtime)
|
| 53 |
+
# These live in ~/.claude/ so already outside the repo
|
| 54 |
+
|
| 55 |
+
# Internal review artifacts and working notes (not public docs)
|
| 56 |
+
/docs/reports/
|
| 57 |
+
/docs/plans/20[0-9][0-9]-*/
|
| 58 |
+
/docs/plans/20[0-9][0-9]-*.md
|
| 59 |
+
/internal/reviews/
|
| 60 |
+
|
| 61 |
+
# Generated graph review artifacts (regenerate locally when needed)
|
| 62 |
+
/graph/*-report.md
|
| 63 |
+
/graph/*-report.json
|
| 64 |
+
/graph/*-report.json.gz
|
| 65 |
+
/graph/*.staged
|
| 66 |
+
/graph/*.partial
|
| 67 |
+
/graph/*.lock
|
| 68 |
+
*.promotion.json
|
| 69 |
+
/graph/tag-backfill.md
|
| 70 |
+
/graph/tag-backfill.json
|
| 71 |
+
|
| 72 |
+
# Secrets / config overrides
|
| 73 |
+
skill-system-config.json
|
| 74 |
+
.env.local
|
| 75 |
+
.env.development
|
| 76 |
+
.env.production
|
| 77 |
+
.envrc
|
| 78 |
+
|
| 79 |
+
# Strix security scanner (local venv, instructions, output)
|
| 80 |
+
.strix-venv/
|
| 81 |
+
.strix-instructions.txt
|
| 82 |
+
.strix-output/
|
| 83 |
+
.strix-out/
|
| 84 |
+
.strix-run.log
|
| 85 |
+
.strix-preview-wiki/
|
| 86 |
+
.imported-deployed/
|
| 87 |
+
strix_runs/
|
| 88 |
+
|
| 89 |
+
# Test coverage
|
| 90 |
+
.coverage
|
| 91 |
+
.ci-preflight-dist/
|
| 92 |
+
htmlcov/
|
| 93 |
+
.pytest_cache/
|
| 94 |
+
.mypy_cache/
|
| 95 |
+
.mypy_cache*/
|
| 96 |
+
.ruff_cache/
|
| 97 |
+
.hypothesis/
|
| 98 |
+
|
| 99 |
+
# Local code-review graph experiments
|
| 100 |
+
.code-review-graph/
|
| 101 |
+
|
| 102 |
+
# MkDocs build output
|
| 103 |
+
/site/
|
.hypothesis/.gitignore
DELETED
|
@@ -1,9 +0,0 @@
|
|
| 1 |
-
# This .gitignore file was automatically created by Hypothesis. Hypothesis gitignores
|
| 2 |
-
# .hypothesis by default, because we generally recommend that .hypothesis not be checked
|
| 3 |
-
# into version control.
|
| 4 |
-
#
|
| 5 |
-
# If you *would* like to check .hypothesis into version control, you should delete this
|
| 6 |
-
# file. Hypothesis will not re-create this .gitignore unless .hypothesis is deleted (and
|
| 7 |
-
# if it does, that's a bug - please report it!)
|
| 8 |
-
|
| 9 |
-
*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/0019c6a267d025e1
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\networkx\utils\__init__.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
[]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/004e722d2b39195a
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\_pytest\freeze_support.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
['.']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/009298dc5190c8bd
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\networkx\algorithms\polynomials.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
['chromatic_polynomial', 'contraction_idx', 'directed', 'tutte_polynomial', 'x', 'y']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/010258c38047fd64
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\coverage\lcovreport.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
[',', '-', '0', '1', '=', 'LCOV report', 'ascii', 'end_of_record\n', 'function', 'utf-8']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/017531f0e829ce4f
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\networkx\algorithms\summarization.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
['Supernode-', '[edge_attributes]', '[node_attributes]', 'dedensify', 'group', 'not copy', 'snap_aggregation', 'types']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/0197b3c42bd36715
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Steves_Files\Work\Research_and_Papers\ctx\src\ctx\core\graph\resolve_graph.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
[0.0, 1.0, ',', ', ', '--hops', '--json', '--matched', '--tags', '--top', '1', ':', 'Output as JSON', '__main__', 'agent:', 'ann:v1:', 'ann_attach', 'artifacts', 'attach_key', 'communities', 'ctx_graph_path', 'delta', 'edges', 'export_id', 'graph', 'graph-walk', 'graph.json', 'graphify-out', 'harness:', 'id', 'kind', 'label', 'links', 'matching_tags', 'mcp-server:', 'mode', 'name', 'never_load', 'node_id', 'nodes', 'normalized_score', 'on', 'replace_scope', 'report', 'results', 'score', 'semantic_build_floor', 'semantic_sim', 'shared_tags', 'skill', 'skill:', 'source', 'store_true', 'tag-search', 'tag_sim', 'tags', 'target', 'token_sim', 'true', 'type', 'utf-8', 'via', 'weight', 'y', 'yes', '~/.claude/skill-wiki']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/0199ac9602548c81
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\networkx\algorithms\connectivity\edge_augmentation.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
['MetaEdge', 'directed', 'generator', 'is_k_edge_connected', 'k_edge_augmentation', 'mapping', 'members', 'meta_uv', 'multigraph', 'no available edges', 'uv', 'w', 'weight']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/01e01103d68a3193
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\networkx\algorithms\centrality\katz.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
[1e-06, 0.1, 1.0, 1000, 'katz_centrality', 'multigraph', 'weight']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/0213343f1393a3fa
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\networkx\algorithms\components\connected.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
['connected_components', 'directed', 'is_connected']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/031f85f1c11bb00b
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\numpy\_typing\_nbit_base.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
['NBitBase', '_128Bit', '_16Bit', '_32Bit', '_64Bit', '_8Bit', '_96Bit', 'numpy._typing', 'numpy.typing']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/03a0bc33c3be41ac
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\_pytest\config\findpaths.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
['-', '.cfg', '.ini', '.pytest.ini', '.pytest.toml', '.toml', '::', '=', 'file', 'ini', 'ini_options', 'override', 'pyproject.toml', 'pytest', 'pytest.ini', 'pytest.toml', 'setup.cfg', 'setup.py', 'toml', 'tool', 'tool:pytest', 'tox.ini', 'utf-8']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/03d65cf548ce3b23
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\numpy\_core\memmap.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
[b'\x00', -100.0, 'C', '_mmap', 'b', 'c', 'copyonwrite', 'flush', 'memmap', 'name', 'numpy', 'r', 'r+', 'read', 'readonly', 'readwrite', 'w+', 'write']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/041b82baf95116f0
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\_pytest\stash.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
['D', 'Stash', 'StashKey', 'T', '_storage']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/044c9664c732c0e2
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\networkx\algorithms\community\label_propagation.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
['asyn_lpa_communities', 'directed', 'seed', 'weight']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/0570a7c52cd987f9
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\yaml\resolver.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
['!&*', '-+0123456789', '-+0123456789.', '0123456789', '<', '=', 'BaseResolver', 'N', 'Resolver', '^(?:!|&|\\*)$', '^(?:<<)$', '^(?:=)$', 'n', 'yYnNtTfFoO', 'yaml_path_resolvers', '~']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/0628f2618034526e
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\anyio\abc\_resources.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
['T']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/068c9e16d4886008
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\_pytest\terminalprogress.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
['TERM', 'dumb', 'terminalreporter']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/06a795a0e79c102f
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Steves_Files\Work\Research_and_Papers\ctx\scripts\build_dashboard_graph_index.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
[0.0, 1.0, 10000, ',', '--graph-json', '--output', '--top-k', ':', 'VACUUM', '__main__', 'degree', 'description', 'edge_reasons', 'edges', 'edges_count', 'export_id', 'graph', 'id', 'label', 'links', 'max_degree', 'nodes', 'nodes_count', 'quality_score', 'reasons', 'semantic', 'shared_tags', 'slug_token_sim', 'source', 'source_overlap', 'tag_sim', 'tags', 'target', 'top_k', 'type', 'usage_score', 'utf-8', 'version', 'weight']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/072d43d60d40fd51
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Steves_Files\Work\Research_and_Papers\ctx\src\backup_config.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
[1024, '%Y%m%dT%H%M%SZ', '..', '.claude.json', '.credentials.json', '/', ':', 'AGENTS.md', 'CLAUDE.md', '\\', '\\\\', 'agents', 'backup', 'claude.json', 'dest', 'excludes', 'full', 'hybrid', 'incremental', 'keep_daily', 'keep_latest', 'max_file_bytes', 'memory_glob', 'name_format', 'pending-skills.json', 'retention', 'scope', 'settings.json', 'skill-manifest.json', 'skill-registry.json', 'skills', 'snapshot_dir', 'src', 'stats-cache.json', 'timestamp_format', 'top_files', 'trees', 'trees[].dest', 'trees[].src', 'user-profile.json', 'utf-8', '{timestamp}', '~/.claude/backups']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/0736e2d4acddd01a
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\yaml\error.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
['\x00\r\n\x85\u2028\u2029', ' ... ', ':\n', 'Mark', 'MarkedYAMLError', 'YAMLError', '^']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/07475514dc0bbadb
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\pytest\__main__.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
['__main__']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/075aa0dd31e12c03
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\xdist\_version.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
['3.8.0', '__version__', '__version_tuple__', 'version', 'version_tuple']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/084c48810f649441
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\networkx\readwrite\text.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
[' ...', '!', '+', '+-- ', ', ', '-', '...', ': ', '<-', 'L-- ', 'L-> ', '_', 'as_dict', 'backedge', 'collapse', 'empty', 'label', 'mapping', 'vertical_edge', 'w', 'write', 'write_network_text', '|', '| ', '|-- ', '|-> ', '─', '│', '│ ', '└── ', '└─╼ ', '├── ', '├─╼ ', '╎ ', '╙', '╙── ', '╟── ', '╽', '╾']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/08a45b14b7a1aa82
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\xdist\_path.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
[]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/0964dd49257e9b00
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\markdown\treeprocessors.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
['\n%s', '%04d', 'br', 'code', 'd', 'inline', 'pre', 'prettify', 'unescape', '{}(\\d+){}', '{}{}{}', '{}{}{}{}']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/0967afddd3cfffcc
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\networkx\algorithms\centrality\eigenvector.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
[1e-06, 100, 'LR', 'multigraph', 'weight']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/09a20f6b52e6dcc7
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\networkx\algorithms\minors\__init__.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
['contracted_edge', 'contracted_nodes', 'equivalence_classes', 'identified_nodes', 'quotient_graph']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/09ae28b390a241d0
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\sortedcontainers\sorteddict.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
[50331648, ', ', '<not-given>', 'items', 'iteritems', 'iterkeys', 'itervalues', 'keys', 'values', 'viewitems', 'viewkeys', 'viewvalues', '{0!r}, ', '{0!r}: {1!r}', '{0}({1}{{{2}}})']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/09bba2fe6bc8792d
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Steves_Files\Work\Research_and_Papers\ctx\src\ctx\core\source_registry.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
['-', '--json', '--registry', '-nc', 'Apache-2.0', 'CC BY-NC 4.0', 'ExternalSourceRecord', 'MIT', '_', '__main__', 'agents-md', 'agpl', 'apache-2.0', 'bsd-2-clause', 'bsd-3-clause', 'cc-by-4.0', 'cc0-1.0', 'copyleft license', 'explicit-permission', 'external-link', 'full', 'full-body', 'gpl', 'harness', 'import_mode', 'isc', 'julius-caveman', 'knowledge-protocol', 'lat-md', 'lgpl', 'license', 'mattpocock-skills', 'metadata-only', 'mit', 'mpl-2.0', 'name', 'no-explicit', 'noncommercial', 'notes', 'optillm', 'owner-permission', 'permission_reference', 'permission_status', 'revision', 'skill-suite', 'source_kind', 'sources', 'store_true', 'unknown', 'unknown license', 'unlicense', 'url', 'utf-8']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/09e51e4d7e8d7e87
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\networkx\algorithms\connectivity\disjoint_paths.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
['G', 'auxiliary', 'auxiliary?', 'capacity', 'cutoff', 'edge_disjoint_paths', 'flow', 'flow_func', 'flow_value', 'id', 'inf', 'mapping', 'node_disjoint_paths', 'residual', 'two_phase', 'value_only']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/0a6403dce3b6e280
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\coverage\env.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
['+', 'COVERAGE_COVERAGE', 'COVERAGE_TESTING', 'CPython', 'PYBEHAVIOR', 'PyPy', 'True', '_', '_is_gil_enabled', 'alpha', 'darwin', 'debug_info', 'linux', 'monitoring', 'win32']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/0ac765fb060691b5
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\networkx\algorithms\isolate.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
['is_isolate', 'isolates', 'number_of_isolates']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/0b1d2411e5eaa76a
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\networkx\algorithms\__init__.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
[]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/0bb70d1826311e40
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\execnet\gateway_bootstrap.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
[b'1', 255, 'except NameError:', 'import socket', 'import sys', 'remoteaddress', 'sys.stdout.flush()', 'try: execmodel', 'utf-8']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/0bc3b2f77e74ecd9
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\pytest_mock\__init__.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
['AsyncMockType', 'MockFixture', 'MockType', 'MockerFixture', 'PytestMockWarning', 'class_mocker', 'mocker', 'module_mocker', 'package_mocker', 'pytest_addoption', 'pytest_configure', 'session_mocker']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/0c23327e39eaa492
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\networkx\algorithms\components\__init__.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
[]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/0c347c1a609186cf
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Steves_Files\Work\Research_and_Papers\ctx\src\ctx\adapters\generic\compaction.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
[0.3, 600, 60000, ')', ', ', 'CompactionResult', '[Compacted', '[summary was empty]', '] ', 'assistant', 'system', 'tool', 'user']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/0c7b341cd0f9acb2
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Steves_Files\Work\Research_and_Papers\ctx\src\mcp_fetch.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
[',', '--limit', '--list-sources', '--refresh', '--source', '--verbose', '-v', ':', 'NAME', '__main__', 'all', 'count', 'ctx-mcp-fetch', 'reconfigure', 'replace', 'store_true', 'utf-8']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/0cfa12dfbf9e9abb
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\colorama\initialise.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
['win32']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hypothesis/constants/0d65a410db824952
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
# file: C:\Users\solun\AppData\Roaming\Python\Python311\site-packages\yaml\representer.py
|
| 2 |
-
# hypothesis_version: 6.152.1
|
| 3 |
-
|
| 4 |
-
[0.0, 1.0, 1e+300, '%r', '%r%rj', '%r+%rj', '%rj', '%s.%s', '-.inf', '.', '.0e', '.inf', '.nan', 'BaseRepresenter', 'Representer', 'RepresenterError', 'SafeRepresenter', '__getstate__', '__newobj__', '__reduce__', '__reduce_ex__', 'args', 'ascii', 'dictitems', 'e', 'encodebytes', 'false', 'items', 'listitems', 'null', 'state', 'true', 'yaml_representers', '|']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|