Merge pull request #19 from devonartis/feature/architecture-diagrams #52
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
| name: CI | |
| # GATE_LIST_START | |
| # - build | |
| # - vet | |
| # - lint | |
| # - format | |
| # - contamination | |
| # - unit-tests | |
| # - gosec | |
| # - govulncheck | |
| # - go-mod-verify | |
| # - unit-tests-race | |
| # - docker-build | |
| # - smoke-l25 | |
| # - sbom | |
| # - no-tracked-ignored | |
| # GATE_LIST_END | |
| on: | |
| pull_request: | |
| branches: [develop] | |
| push: | |
| branches: [develop, main] | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| # Parameterized — no hardcoded owner/repo names. Rebrand-resilient per Decision 015. | |
| permissions: | |
| contents: read | |
| jobs: | |
| build: | |
| name: build | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - run: go build ./cmd/broker ./cmd/awrit | |
| vet: | |
| name: vet | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - run: go vet ./... | |
| lint: | |
| name: lint | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| # DO NOT use golangci/golangci-lint-action here. Its pre-built | |
| # binaries are compiled against an older Go toolchain (≤ 1.23) | |
| # and exit 3 on our code because the go.mod toolchain directive | |
| # is 1.25.9 — the embedded linter can't parse 1.25 stdlib/SSA. | |
| # | |
| # Instead, `go install` golangci-lint from source. This compiles | |
| # it with the CI runner's Go (matching our toolchain.go1.25.9) | |
| # so it parses the same way as local developers' brew-installed | |
| # binary. Migration to golangci-lint v2 (which fixes this) is | |
| # tracked for a later cycle. | |
| - name: Install golangci-lint | |
| run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.64.8 | |
| - name: Run lint | |
| run: golangci-lint run --config .golangci.yml ./... | |
| format: | |
| name: format | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 | |
| with: | |
| go-version-file: go.mod | |
| - run: | | |
| unformatted=$(gofmt -l .) | |
| if [[ -n "$unformatted" ]]; then | |
| echo "The following files are not gofmt'd:" | |
| echo "$unformatted" | |
| exit 1 | |
| fi | |
| contamination: | |
| name: contamination | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Enterprise contamination check | |
| run: | | |
| if grep -ri 'hitl\|approval\|oidc\|federation\|cloud\|sidecar' internal/ cmd/ 2>/dev/null; then | |
| echo "FAIL: enterprise references found in core code" | |
| exit 1 | |
| fi | |
| echo "PASS: no enterprise contamination" | |
| - name: Brand alignment check | |
| run: | | |
| # Catches brand strings that should have been renamed during the | |
| # AgentAuth → AgentWrit rebrand. See TD-RUNTIME-001 and TD-OBS-001 | |
| # for the gap this check closes (obs.go metric names and | |
| # problemdetails.go URN namespace were missed in the first rebrand | |
| # pass because the hardcoded identity audit only scoped JWT/config | |
| # values, not Prometheus names or RFC 7807 URNs). | |
| if grep -rn 'urn:agentauth\|agentauth_\|github\.com/devonartis/agentauth[^-]' internal/ cmd/ --include='*.go' 2>/dev/null; then | |
| echo "FAIL: stale 'agentauth' brand strings found in runtime code" | |
| echo "Rename to 'agentwrit' and update tests / docs to match." | |
| exit 1 | |
| fi | |
| echo "PASS: no stale brand strings" | |
| unit-tests: | |
| name: unit-tests | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - run: go test -short -count=1 ./... | |
| unit-tests-race: | |
| name: unit-tests-race | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - run: go test -race -count=1 -coverprofile=coverage.out ./... | |
| - uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 | |
| with: | |
| files: ./coverage.out | |
| fail_ci_if_error: false | |
| verbose: true | |
| gosec: | |
| name: gosec | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 | |
| with: | |
| go-version-file: go.mod | |
| # Exclusions (G117, G304, G101) are documented in .gosec.yml and | |
| # mirrored in scripts/gates.sh and .golangci.yml. | |
| - uses: securego/gosec@223e19b8856e00f02cc67804499a83f77e208f3c # v2.25.0 | |
| with: | |
| args: '-quiet -conf .gosec.yml -exclude=G117,G304,G101 -severity=medium ./...' | |
| govulncheck: | |
| name: govulncheck | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 | |
| with: | |
| go-version-file: go.mod | |
| - run: | | |
| go install golang.org/x/vuln/cmd/govulncheck@latest | |
| govulncheck ./... | |
| go-mod-verify: | |
| name: go-mod-verify | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 | |
| with: | |
| go-version-file: go.mod | |
| - run: | | |
| go mod verify | |
| go mod tidy | |
| if ! git diff --exit-code go.mod go.sum; then | |
| echo "FAIL: go.mod or go.sum changed after 'go mod tidy'" | |
| exit 1 | |
| fi | |
| docker-build: | |
| name: docker-build | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Build image | |
| run: docker build -t agentwrit-ci:${{ github.sha }} . | |
| smoke-l25: | |
| name: smoke-l25 | |
| runs-on: ubuntu-latest | |
| needs: [docker-build] | |
| env: | |
| AA_ADMIN_SECRET: live-test-secret-32bytes-long-ok # known test fixture | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Install python cryptography (for Ed25519 challenge-response) | |
| run: python3 -m pip install --user cryptography | |
| - name: Start broker | |
| run: | | |
| export AA_ADMIN_SECRET="$AA_ADMIN_SECRET" | |
| ./scripts/stack_up.sh | |
| for i in {1..30}; do | |
| if curl -sf http://localhost:8080/v1/health >/dev/null 2>&1; then | |
| echo "Broker up after $i seconds" | |
| break | |
| fi | |
| sleep 1 | |
| done | |
| - name: Run L2.5 core contract smoke | |
| run: ./scripts/smoke/core-contract.sh | |
| - name: Teardown | |
| if: always() | |
| run: ./scripts/stack_down.sh || true | |
| sbom: | |
| name: sbom | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0 | |
| with: | |
| path: . | |
| format: spdx-json | |
| output-file: sbom.spdx.json | |
| - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 | |
| with: | |
| name: sbom | |
| path: sbom.spdx.json | |
| retention-days: 30 | |
| # dep-review runs on every PR to catch disallowed licenses and known | |
| # CVEs in introduced dependencies BEFORE they merge. Uses the GitHub | |
| # Dependency Graph API, which is free on public repos. Was parked | |
| # while the repo was private (would have required paid GHAS); re-enabled | |
| # 2026-04-12 after the public flip (TD-VUL-005). | |
| dep-review: | |
| name: dep-review | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4.9.0 | |
| with: | |
| fail-on-severity: high | |
| comment-summary-in-pr: true | |
| changelog: | |
| name: changelog | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check CHANGELOG.md diff | |
| run: | | |
| if echo '${{ toJson(github.event.pull_request.labels) }}' | grep -q '"skip-changelog"'; then | |
| echo "Label 'skip-changelog' present — bypassing CHANGELOG check" | |
| exit 0 | |
| fi | |
| BASE_SHA='${{ github.event.pull_request.base.sha }}' | |
| if git diff --name-only "$BASE_SHA" HEAD | grep -q '^CHANGELOG.md$'; then | |
| echo "PASS: CHANGELOG.md touched in this PR" | |
| else | |
| echo "FAIL: This PR does not touch CHANGELOG.md" | |
| echo "Add a CHANGELOG entry or apply the 'skip-changelog' label if the PR is docs/tests-only." | |
| exit 1 | |
| fi | |
| gate-parity: | |
| name: gate-parity | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - run: ./scripts/test-gate-parity.sh | |
| # no-tracked-ignored — fails if any gitignored path is present in the git index. | |
| # | |
| # Replaces the old main-hygiene + strip_for_main.sh model. Under the sister-folder | |
| # pattern (Decision 019), dev-internal artifacts live in ~/proj/devflow/agentwrit/ | |
| # or stay on disk as gitignored files for Claude Code discovery. None of them | |
| # should ever enter the git index on any branch. .gitignore prevents accidental | |
| # `git add .`; this gate is the narrow safety net against `git add -f` force-adds. | |
| # | |
| # Runs on every push and PR (not just main) so the failure mode is caught as | |
| # early as possible rather than at merge time. | |
| no-tracked-ignored: | |
| name: no-tracked-ignored | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: No gitignored paths in the tracked index | |
| run: | | |
| tracked_ignored=$(git ls-files -c --exclude-standard --ignored || true) | |
| if [[ -n "$tracked_ignored" ]]; then | |
| echo "FAIL: gitignored paths found in the git index:" | |
| echo "$tracked_ignored" | sed 's/^/ /' | |
| echo "" | |
| echo "These paths match .gitignore patterns and must not be tracked." | |
| echo "Fix: git rm --cached <path> (keeps the file on disk, untracked)" | |
| exit 1 | |
| fi | |
| echo "PASS: no gitignored paths are tracked" | |
| gates-passed: | |
| name: gates-passed | |
| runs-on: ubuntu-latest | |
| needs: | |
| - build | |
| - vet | |
| - lint | |
| - format | |
| - contamination | |
| - unit-tests | |
| - unit-tests-race | |
| - gosec | |
| - govulncheck | |
| - go-mod-verify | |
| - docker-build | |
| - smoke-l25 | |
| - sbom | |
| - gate-parity | |
| - no-tracked-ignored | |
| if: always() | |
| steps: | |
| - name: Check all gates passed | |
| run: | | |
| if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then | |
| echo "One or more gates failed" | |
| exit 1 | |
| fi | |
| if [[ "${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then | |
| echo "One or more gates were cancelled" | |
| exit 1 | |
| fi | |
| echo "All gates passed" |