v0.14.0 — Cross-platform content modularization (platform sidecars + os-region injection gating) #90
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Security note: This workflow uses NO github.event.* inputs and NO ${{ ... }} | |
| # interpolation in `run:` blocks. All dynamic values come from GitHub-provided | |
| # safe env vars ($GITHUB_WORKSPACE) or hard-coded literals. No injection surface. | |
| name: validate | |
| on: | |
| push: | |
| branches: [main, dev] | |
| pull_request: | |
| branches: [main] | |
| permissions: | |
| contents: read | |
| jobs: | |
| fast-tests: | |
| name: Fast self-tests (${{ matrix.os }}) | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 5 | |
| strategy: | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.x' | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '20' | |
| - name: Plugin self-consistency (validate-anchor) | |
| run: bash scripts/validate-anchor.sh | |
| - name: Manifest validation (validate-manifests) | |
| run: bash scripts/validate-manifests.sh | |
| - name: Manifest negative fixtures | |
| run: bash scripts/validate-manifests.sh --fixtures tests/manifest-fixtures | |
| # Windows arm runs the curated core subset first release (spec §9); expand once | |
| # proven stable — a flaky gate is worse than none. | |
| - name: Command frontmatter negative fixtures | |
| if: runner.os != 'Windows' | |
| run: bash scripts/check-allowed-tools.sh --fixtures tests/command-fixtures | |
| # Glob, not an enumerated list: a hardcoded list silently omits newly added | |
| # tests (it had already omitted post-tool-use-tidy-fidelity.sh once). | |
| # </dev/null: a hook that reads stdin must not inherit the runner's. | |
| - name: Hook contracts (every tests/hook-contracts/*.sh) | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| for t in tests/hook-contracts/*.sh; do | |
| echo "=== $t" | |
| bash "$t" </dev/null | |
| done | |
| - name: POSIX compatibility | |
| if: runner.os != 'Windows' | |
| run: bash tests/posix-compat.sh | |
| - name: Windows compatibility (static invariants, all platforms) | |
| run: bash tests/windows-compat.sh | |
| - name: E2E fixture structural checks | |
| if: runner.os != 'Windows' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| FIXTURE_DIR=$(bash tests/e2e-cpp-fixture/bootstrap.sh) | |
| echo "Fixture: $FIXTURE_DIR" | |
| # Verify key files exist | |
| for f in feature_list.json AGENTS.md init.sh PROJECT-TOC.md session-handoff.md progress.md .clang-format .clang-tidy CMakeLists.txt scripts/lint.sh scripts/sanitizer-build.sh; do | |
| test -f "$FIXTURE_DIR/$f" | |
| echo "OK $f present" | |
| done | |
| # Verify feature_list.json parses and validates | |
| python3 -c " | |
| import json, re, sys | |
| with open('$FIXTURE_DIR/feature_list.json') as f: | |
| data = json.load(f) | |
| assert data.get('project'), 'missing project' | |
| assert len(data.get('features', [])) == 3, 'expected 3 features' | |
| for feat in data['features']: | |
| for field in ['id', 'name', 'description', 'status', 'done_criteria']: | |
| assert field in feat, f'missing {field} in {feat.get(\"id\")}' | |
| assert re.match(r'^[a-z0-9][a-z0-9-]*$', feat['id']), f'bad id: {feat[\"id\"]}' | |
| if feat['status'] == 'pass': | |
| assert feat.get('evidence') is not None, f'{feat[\"id\"]} pass but evidence null' | |
| print('OK feature_list.json valid') | |
| " | |
| - name: Context budget measurement | |
| if: runner.os != 'Windows' | |
| run: bash scripts/measure-context.sh | |
| - name: Hook timing benchmark | |
| if: runner.os != 'Windows' | |
| run: bash tests/bench/hook-timing.sh | |
| - name: cpp-detect — fixture coverage | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| for fixture in cmake meson make bazel; do | |
| echo "--- ${fixture}-fixture ---" | |
| output=$(bash scripts/cpp-detect.sh --target "tests/cpp-detection/${fixture}-fixture") | |
| echo "${output}" | python3 -m json.tool | |
| EXPECTED_BS="${fixture}" \ | |
| python3 -c " | |
| import json, sys, os | |
| expected_bs = os.environ['EXPECTED_BS'] | |
| d = json.loads(sys.stdin.read()) | |
| assert d['is_cpp_project'] is True, f'{expected_bs}-fixture not detected as cpp project' | |
| assert d['build_system'] == expected_bs, f'{expected_bs}-fixture wrong build_system: ' + d['build_system'] | |
| print(f'OK {expected_bs}-fixture: build_system={d[\"build_system\"]}') | |
| " <<< "${output}" | |
| done | |
| - name: index-builder smoke test on temp repo | |
| if: runner.os != 'Windows' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| tmp=$(mktemp -d) | |
| cd "${tmp}" | |
| git init -q | |
| git config user.email test@example.com | |
| git config user.name test | |
| echo "// main.c - hello" > main.c | |
| git add -A | |
| git commit -qm init | |
| node "${GITHUB_WORKSPACE}/scripts/index-builder.mjs" --target . | |
| test -f PROJECT-TOC.md | |
| grep -q 'main.c' PROJECT-TOC.md | |
| grep -q 'generated-at-commit' PROJECT-TOC.md | |
| echo "OK index-builder smoke pass" | |
| # Glob for the same reason as the hook-contracts step: enumeration rots. | |
| - name: Script unit tests (every tests/unit/*.sh) | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| for t in tests/unit/*.sh; do | |
| echo "=== $t" | |
| bash "$t" </dev/null | |
| done | |
| - name: cpp-detect — negative fixture (non-C/C++ stays dormant) | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| out=$(bash scripts/cpp-detect.sh --target tests/cpp-detection/non-cpp-fixture) | |
| echo "${out}" | python3 -m json.tool | |
| echo "${out}" | python3 -c "import json,sys; d=json.load(sys.stdin); assert d['is_cpp_project'] is False, 'non-cpp-fixture wrongly detected as cpp'; print('OK non-cpp-fixture: is_cpp_project=false')" | |
| - name: Skill-triggering coverage (structural, no LLM) | |
| if: runner.os != 'Windows' | |
| run: bash tests/skill-triggering/check-coverage.sh | |
| shellcheck: | |
| name: ShellCheck (lint shell scripts) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Install shellcheck | |
| run: sudo apt-get update && sudo apt-get install -y shellcheck | |
| - name: Lint hooks + scripts + tests | |
| shell: bash | |
| run: | | |
| set -uo pipefail | |
| files=() | |
| while IFS= read -r f; do files+=("$f"); done < <(find hooks -type f ! -name '*.json' ! -name '*.cmd'; find scripts tests -type f -name '*.sh'; find templates -type f -name '*.sh.tpl') | |
| printf 'Linting %d files:\n' "${#files[@]}"; printf ' %s\n' "${files[@]}" | |
| echo "== full findings, all levels (informational) ==" | |
| shellcheck --severity=style --exclude=SC1091 "${files[@]}" || true | |
| echo "== gate: fail on warning-severity and above ==" | |
| shellcheck --severity=warning --exclude=SC1091 "${files[@]}" |