Skip to content

chore: trim dead SDK/skill repos from manifest and live docs (#278) #1326

chore: trim dead SDK/skill repos from manifest and live docs (#278)

chore: trim dead SDK/skill repos from manifest and live docs (#278) #1326

Workflow file for this run

# Strict CI pipeline for GrayCodeAI Go repos.
# All jobs must pass — no continue-on-error except where explicitly noted.
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
security-events: write
pull-requests: read
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
GO_VERSION: "1.26.6"
# Module resolution: the committed third_party/modproxy serves GrayCodeAI pins
# whose repositories no longer exist (and which the public proxy never cached,
# so it 404s them). Public modules resolve from proxy.golang.org as usual;
# direct-VCS fallback is retained for anything else. Do NOT set GOPRIVATE for
# GrayCodeAI paths: it would force direct git and bypass the committed proxy.
GOPROXY: "file://${{ github.workspace }}/third_party/modproxy,https://proxy.golang.org,direct"
# CI must resolve the dependency versions pinned in go.mod/go.sum, not whatever
# happens to sit in the sibling checkouts that the committed go.work points at.
# Go's reference is explicit that a committed go.work "may cause a continuous
# integration (CI) system to select and thus test the wrong versions of a
# module's dependencies" and that "CI systems should generally not be allowed to
# use the go.work file". Because ./.github/actions/checkout-eyrie clones the
# siblings at branch HEAD into the workspace parent, workspace mode would silently
# hide version skew (e.g. a shared pin that no longer matches).
# The `module` job opts back in (GOWORK: "auto") since `go work sync` and the
# go.work content checks genuinely require the workspace.
GOWORK: "off"
jobs:
# -------------------------------------------------------------------------
# 1. Format — gofumpt + goimports must be clean (zero tolerance).
# -------------------------------------------------------------------------
format:
name: format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version: ${{ env.GO_VERSION }}
cache: true
- name: gofumpt
run: |
go install mvdan.cc/gofumpt@v0.10.0
out=$(git ls-files -- '*.go' | xargs gofumpt -l)
if [ -n "$out" ]; then
echo "::error::gofumpt would reformat:"
echo "$out" | head -20
exit 1
fi
- name: goimports
run: |
go install golang.org/x/tools/cmd/goimports@v0.30.0
out=$(git ls-files -- '*.go' | xargs goimports -l)
if [ -n "$out" ]; then
echo "::error::goimports would reformat:"
echo "$out" | head -20
exit 1
fi
- name: shared types import guard
run: bash ./scripts/check-shared-types-imports.sh
- name: ecosystem boundary guard
run: bash ./scripts/check-ecosystem-boundaries.sh
- name: internal layer boundary guard
run: bash ./scripts/check-internal-layer-imports.sh
- name: eyrie client boundary guard
run: bash ./scripts/check-eyrie-client-imports.sh
- name: eyrie engine facade boundary guard
run: bash ./scripts/check-eyrie-engine-boundary.sh
# -------------------------------------------------------------------------
# 2. Module hygiene — tidy, verify Graycode plus the sibling Go modules via go.work.
# -------------------------------------------------------------------------
module:
name: module hygiene
runs-on: ubuntu-latest
# Workspace hygiene is this job's entire purpose (`go work sync`, the go.work
# content checks), so it deliberately re-enables the workspace that the
# workflow-level GOWORK: "off" disables. The steps here that must see the
# pinned versions already set GOWORK=off inline.
env:
GOWORK: "auto"
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- uses: ./.github/actions/checkout-eyrie
with:
ref: ${{ github.head_ref == 'feat/ecosystem-wiring-pr' && 'feat/ecosystem-wiring' || github.head_ref || github.ref_name }}
- name: Generate ecosystem workspace
run: bash ./scripts/generate-workspace.sh
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version: ${{ env.GO_VERSION }}
cache: true
- name: go work sync + module consistency
run: |
# Sibling repos are independent checkouts in the workspace parent.
# go mod tidy can mis-resolve workspace modules here; go work sync is
# the supported workspace hygiene step.
go work sync
go build -mod=readonly -o /dev/null ./cmd/graycode
if ! git diff --quiet -- go.mod go.sum go.work go.work.sum; then
echo "::error::go.mod / go.sum / go.work files out of date — run 'go work sync' locally and commit"
git diff -- go.mod go.sum go.work go.work.sum
exit 1
fi
- name: go mod verify
run: go mod verify
- name: workspace references sibling checkouts
run: |
workspace_file="${GITHUB_WORKSPACE}/../go.work"
while IFS= read -r module; do
[ "$module" = graycode ] && continue
if ! grep -q "./${module}" "$workspace_file"; then
echo "::error::go.work must include ./${module}."
cat "$workspace_file"
exit 1
fi
done < <(./scripts/ecosystem-manifest.sh list workspace)
- name: Validate every Go module independently
run: |
while IFS= read -r modfile; do
dir=$(dirname "$modfile")
echo "==> validating $dir"
(cd "$dir" && GOWORK=off go mod tidy -diff)
(cd "$dir" && GOWORK=off go mod verify)
(cd "$dir" && GOWORK=off go test ./... -count=1 -timeout=300s -skip='TestDefaultSkillDirsCrossAgent|TestCopySelectionE2E')
done < <(find . -name go.mod -not -path './.git/*' -print | sort)
public-modules:
name: public module graph
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- uses: ./.github/actions/checkout-eyrie
with:
ref: ${{ github.head_ref || github.ref_name }}
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version: ${{ env.GO_VERSION }}
cache: true
- name: Verify public dependency graph
env:
GOWORK: "off"
run: |
go mod download
go mod verify
go build -mod=readonly ./cmd/graycode
go test ./... -count=1 -timeout=300s -skip='TestDefaultSkillDirsCrossAgent|TestCopySelectionE2E'
release-parity:
name: workspace and module parity
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- uses: ./.github/actions/checkout-eyrie
with:
ref: ${{ github.head_ref || github.ref_name }}
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version: ${{ env.GO_VERSION }}
cache: true
- run: bash ./scripts/check-module-release-parity.sh
# -------------------------------------------------------------------------
# 3. Vet + static analysis — compiler-level correctness.
# -------------------------------------------------------------------------
vet:
name: vet
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- uses: ./.github/actions/checkout-eyrie
with:
ref: ${{ github.head_ref || github.ref_name }}
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version: ${{ env.GO_VERSION }}
cache: true
- name: go vet
run: go vet ./...
- name: graycode harness evaluation audit
run: go run ./cmd/graycode harness --out-dir .graycode/harness
- name: support repo coupling guard
run: bash ./scripts/check-support-repo-coupling.sh
# -------------------------------------------------------------------------
# 4. Lint — golangci-lint with project-specific config.
# -------------------------------------------------------------------------
lint:
name: lint
runs-on: ubuntu-latest
needs: [format, vet]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- uses: ./.github/actions/checkout-eyrie
with:
ref: ${{ github.head_ref || github.ref_name }}
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version: ${{ env.GO_VERSION }}
cache: true
- name: Run golangci-lint
run: |
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.0
golangci-lint run --timeout=5m
# -------------------------------------------------------------------------
# 5. Tests — race detector, coverage threshold, test shuffling.
# -------------------------------------------------------------------------
test:
name: test (race + coverage)
runs-on: ubuntu-latest
# NOTE: depends only on `format`, deliberately NOT on `vet`.
# A `vet` (or upstream) failure must never skip the test job — that
# historically masked real test regressions (see graycode PR #155, where
# DrainAlerts code shipped with failing tests because `test` was skipped
# when `vet` failed on the trailer-strip force-push). Tests must always
# run so regressions surface instead of being hidden.
needs: [format]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- uses: ./.github/actions/checkout-eyrie
with:
ref: ${{ github.head_ref || github.ref_name }}
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version: ${{ env.GO_VERSION }}
cache: true
- name: Test with race detector
run: go test ./... -race -count=1 -shuffle=on -coverprofile=coverage.out -covermode=atomic -timeout=300s -skip='TestDefaultSkillDirsCrossAgent|TestCopySelectionE2E'
- name: Coverage summary
run: |
coverage=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | tr -d '%' | tail -1)
echo "Coverage: ${coverage}%"
echo "COVERAGE=${coverage}" >> "$GITHUB_ENV"
- name: Coverage threshold (minimum 65%)
run: |
if (( $(echo "${COVERAGE} < 65" | bc -l) )); then
echo "::error::Coverage ${COVERAGE}% is below minimum 65%"
exit 1
fi
- name: Upload coverage
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-${{ github.job }}
path: coverage.out
retention-days: 30
- name: Enforce test skip policy
continue-on-error: true
run: |
# Find all t.Skip() calls without a tracking issue comment
# Policy: t.Skip() should have a comment with a GitHub issue link on the same or previous line
violations=""
while IFS= read -r line; do
file=$(echo "$line" | cut -d: -f1)
lineno=$(echo "$line" | cut -d: -f2)
# Check previous line for issue link
prev_line=$((lineno - 1))
if [ "$prev_line" -gt 0 ]; then
prev_content=$(sed -n "${prev_line}p" "$file")
if echo "$prev_content" | grep -qE '(github\.com/.*issues/|#\d+|TODO|FIXME|HACK)'; then
continue
fi
fi
# Check same line for issue link
content=$(sed -n "${lineno}p" "$file")
if echo "$content" | grep -qE '(github\.com/.*issues/|#\d+|TODO|FIXME|HACK)'; then
continue
fi
violations="${violations}\n${file}:${lineno}: t.Skip() without tracking issue"
done < <(grep -rn 't\.Skip(' --include='*_test.go' . 2>/dev/null | grep -v '// nolint' || true)
if [ -n "$violations" ]; then
echo "::warning::Found t.Skip() without tracking issue:${violations}"
echo ""
echo "Policy: Every t.Skip() should have a comment linking to a GitHub issue."
echo "Example:"
echo " // TODO: https://github.com/GrayCodeAI/graycode-cli/issues/123"
echo " t.Skip(\"not yet implemented\")"
fi
# -------------------------------------------------------------------------
# 6. Security — vulnerability scan + secret detection.
# -------------------------------------------------------------------------
security:
name: security
runs-on: ubuntu-latest
needs: [format, vet]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- uses: ./.github/actions/checkout-eyrie
with:
ref: ${{ github.head_ref || github.ref_name }}
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version: ${{ env.GO_VERSION }}
cache: true
- name: govulncheck
run: |
go install golang.org/x/vuln/cmd/govulncheck@v1.1.4
# Fail only on vulnerabilities reachable from our code (findings with a
# non-empty call trace). Transitive advisories that our code does not
# call are surfaced but do not block CI — they cannot be remediated
# without an upstream fix and are not in our attack surface.
govulncheck -json ./... > /tmp/vuln.json || true
reachable=$(python3 - <<'PY'
import json, sys
n = 0
for line in open("/tmp/vuln.json"):
line = line.strip()
if not line:
continue
try:
o = json.loads(line)
except json.JSONDecodeError:
continue
if not isinstance(o, dict):
continue
if o.get("module") == "finding" and o.get("trace"):
n += 1
print(f"REACHABLE: {o.get('osv', {}).get('id')} in {o.get('package', {}).get('path')}", file=sys.stderr)
print(n)
PY
)
echo "Reachable vulnerabilities: $reachable"
if [ "$reachable" -gt 0 ]; then
echo "::error::govulncheck found $reachable reachable vulnerability(ies) in our code"
exit 1
fi
echo "No reachable vulnerabilities in our code."
- name: gosec
continue-on-error: true
run: |
go install github.com/securego/gosec/v2/cmd/gosec@v2.22.4
# Advisory scan. The blocking gosec gate lives in the `lint` job
# (see .golangci.yml), which applies the repo's G301/G304
# exclusions for intentional 0755 dirs and user-supplied file
# paths. This job reports findings without failing CI so the
# advisory signal is visible but not a merge blocker.
gosec -quiet ./...
# -------------------------------------------------------------------------
# 7. Secret scan — detect leaked API keys, tokens, credentials.
# -------------------------------------------------------------------------
secrets:
name: secrets
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: trufflesecurity/trufflehog@27b0417c16317ca9a472a9a8092acce143b49c55 # v3.95.9
with:
extra_args: --only-verified
# -------------------------------------------------------------------------
# 8. Markdown lint — validate documentation quality.
# -------------------------------------------------------------------------
markdown:
name: markdown
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Run markdownlint-cli2
run: |
npm install -g markdownlint-cli2
markdownlint-cli2 '**/*.md'
# -------------------------------------------------------------------------
# 9. API reference generation — build HTML docs from the OpenAPI spec.
# -------------------------------------------------------------------------
api-reference:
name: api reference
runs-on: ubuntu-latest
needs: [vet]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Validate OpenAPI spec
run: |
npm install -g @redocly/cli
redocly lint api/openapi.yaml
- name: Generate API reference (HTML)
run: |
npm install -g redoc-cli
redoc-cli bundle api/openapi.yaml -o api/reference.html
- name: Upload API reference artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: api-reference
path: api/reference.html
retention-days: 30
# -------------------------------------------------------------------------
# Dead code detection.
# -------------------------------------------------------------------------
deadcode:
name: deadcode
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- uses: ./.github/actions/checkout-eyrie
with:
ref: ${{ github.head_ref || github.ref_name }}
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c
with:
go-version: "${{ env.GO_VERSION }}"
cache: true
- name: deadcode
run: |
go install golang.org/x/tools/cmd/deadcode@v0.30.0
output=$(mktemp)
trap 'rm -f "$output"' EXIT
deadcode ./... >"$output"
count=$(wc -l <"$output" | tr -d ' ')
echo "deadcode inventory: ${count} unreachable functions"
{
echo "### Deadcode inventory (${count} findings; first 50 shown)"
echo
echo '```text'
head -50 "$output"
echo '```'
} >>"$GITHUB_STEP_SUMMARY"
# -------------------------------------------------------------------------
# Duplication detection — jscpd.
# -------------------------------------------------------------------------
jscpd:
name: duplication
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '24'
- name: jscpd
run: |
npx jscpd --min-lines 5 --min-tokens 50 --reporters console --blame . 2>&1 | head -50
# -------------------------------------------------------------------------
# 9. Cross-platform build matrix — zero CGO, all targets.
# -------------------------------------------------------------------------
build:
name: build (${{ matrix.goos }}/${{ matrix.goarch }})
runs-on: ubuntu-latest
needs: [format, lint, test, security]
strategy:
fail-fast: false
matrix:
goos: [linux, darwin, windows]
goarch: [amd64, arm64]
exclude:
- goos: windows
goarch: arm64
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- uses: ./.github/actions/checkout-eyrie
with:
ref: ${{ github.head_ref || github.ref_name }}
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version: ${{ env.GO_VERSION }}
cache: true
- name: Build
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
CGO_ENABLED: "0"
run: go build -trimpath -v ./...
- name: Binary size check (linux/amd64 only)
if: matrix.goos == 'linux' && matrix.goarch == 'amd64'
run: |
# Use -ldflags="-s -w" to match release build size (Makefile LDFLAGS).
# This catches binary bloat regressions against the actual shipped artifact size.
size=$(go build -trimpath -ldflags="-s -w" -o /tmp/graycode-bin ./cmd/graycode && wc -c < /tmp/graycode-bin)
size_mb=$((size / 1024 / 1024))
echo "Binary size: ${size_mb}MB"
# Threshold history: 110MB → 80MB (binary was ~76MB) → 98MB.
# The 80MB line became stale: the audit-sweep merges (swift
# rebrand, harrier at-rest encryption, engine features) grew the
# release binary to ~95MB (linux/amd64, -trimpath -ldflags="-s -w").
# Re-baselined to 98MB (3MB headroom) so this check detects new
# bloat regressions instead of warning on every run. A real size
# reduction (e.g. swapping modernc.org/sqlite for cgo sqlite,
# lazy-loading WASM/CEL) is tracked separately.
if [ "$size_mb" -gt 98 ]; then
echo "::warning::Binary size ${size_mb}MB exceeds 98MB threshold"
fi
rm -f /tmp/graycode-bin
# -------------------------------------------------------------------------
# Fuzz — short corpus runs to catch panics in fuzz targets.
# -------------------------------------------------------------------------
fuzz:
name: fuzz (60s)
runs-on: ubuntu-latest
needs: [test]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- uses: ./.github/actions/checkout-eyrie
with:
ref: ${{ github.head_ref || github.ref_name }}
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version: ${{ env.GO_VERSION }}
cache: true
- name: Run fuzz targets
run: |
go test -fuzz=FuzzScanForAIComments -fuzztime=60s ./cmd/... || true
go test -fuzz=FuzzValidateSettings -fuzztime=60s ./internal/config/... || true
go test -fuzz=FuzzIsSuspicious -fuzztime=60s ./internal/tool/... || true
go test -fuzz=FuzzIsSafeGitCommit -fuzztime=60s ./internal/tool/... || true
go test -fuzz=FuzzParseMessage -fuzztime=60s ./internal/session/... || true
go test -fuzz=FuzzParseSessionMeta -fuzztime=60s ./internal/session/... || true
# -------------------------------------------------------------------------
# 10. Smoke — build graycode and verify ecosystem CLI wiring.
# -------------------------------------------------------------------------
smoke:
name: smoke
runs-on: ubuntu-latest
needs: [format, vet]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- uses: ./.github/actions/checkout-eyrie
with:
ref: ${{ github.head_ref || github.ref_name }}
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version: ${{ env.GO_VERSION }}
cache: true
- name: smoke-graycode.sh
run: ./scripts/smoke-graycode.sh