diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index adc6a2b..2976250 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,26 +1,171 @@ +# CI pipeline for hawk-core-contracts — a zero-hawk-eco-dependency +# foundation library. No workspace clones needed: this repo depends on +# nothing in the hawk ecosystem, only the Go standard library. + name: CI on: push: - branches: - - main + branches: [main] pull_request: + branches: [main] permissions: contents: read +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + GO_VERSION: "1.26.4" + jobs: + 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: ${{ env.GO_VERSION }} + cache: true + - name: Boundary guard + run: bash ./scripts/check-ecosystem-boundaries.sh + - name: gofumpt + run: | + go install mvdan.cc/gofumpt@v0.10.0 + out=$(gofumpt -l .) + if [ -n "$out" ]; then + echo "::error::gofumpt would reformat:" + echo "$out" + exit 1 + fi + - name: goimports + run: | + go install golang.org/x/tools/cmd/goimports@v0.30.0 + out=$(goimports -l .) + if [ -n "$out" ]; then + echo "::error::goimports would reformat:" + echo "$out" + exit 1 + fi + + module: + name: module hygiene + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version: ${{ env.GO_VERSION }} + cache: true + - name: go mod tidy + run: | + go mod tidy + if ! git diff --quiet -- go.mod go.sum; then + echo "::error::go.mod / go.sum out of date — run 'go mod tidy'" + git diff -- go.mod go.sum + exit 1 + fi + - name: go mod verify + run: go mod verify + + 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: ${{ env.GO_VERSION }} + cache: true + - name: go vet + run: go vet ./... + + lint: + name: lint + runs-on: ubuntu-latest + needs: [format, vet] + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version: ${{ env.GO_VERSION }} + cache: true + - name: Boundary guard + run: bash ./scripts/check-ecosystem-boundaries.sh + - name: Run golangci-lint + run: | + go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.0 + golangci-lint run --timeout=5m + test: + name: test (race + coverage) runs-on: ubuntu-latest + needs: [format, vet] steps: - - name: Checkout - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version: ${{ env.GO_VERSION }} + cache: true + - name: Boundary guard + run: bash ./scripts/check-ecosystem-boundaries.sh + - name: Test with race detector + run: go test ./... -race -count=1 -shuffle=on -coverprofile=coverage.out -covermode=atomic -timeout=120s + - name: Upload coverage + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: coverage-${{ github.job }} + path: coverage.out + retention-days: 30 - - name: Setup Go - uses: actions/setup-go@v5 + security: + name: security + runs-on: ubuntu-latest + needs: [format, vet] + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: - go-version-file: go.mod + go-version: ${{ env.GO_VERSION }} cache: true + - name: govulncheck + run: | + go install golang.org/x/vuln/cmd/govulncheck@v1.1.4 + govulncheck ./... + + secrets: + name: secrets + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: trufflesecurity/trufflehog@0fa069c12f0c7baf431041cd1e564a9c5058846c # main 2026-05-18 + with: + extra_args: --only-verified - - name: Run tests - run: go test ./... + 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 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version: ${{ env.GO_VERSION }} + cache: true + - name: Build (library — cross-compile all packages) + env: + GOOS: ${{ matrix.goos }} + GOARCH: ${{ matrix.goarch }} + CGO_ENABLED: "0" + run: go build -trimpath -v ./... diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..eb63cc0 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,31 @@ +# Release workflow for hawk-core-contracts (Go library — no binaries). +# Triggered when a v* tag is pushed; publishes a GitHub Release with +# auto-generated notes. Consumers depend on the tag via +# `go get github.com/GrayCodeAI/hawk-core-contracts@vX.Y.Z`. + +name: release + +on: + push: + tags: ["v*"] + +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + + - name: Create GitHub Release + uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 + with: + generate_release_notes: true + draft: false + prerelease: auto + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..4976a45 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,168 @@ +--- +description: Extending hawk-eco — how to write AGENTS.md files, custom specialists, skills, hooks, MCP servers, and plugins. +globs: "*.go, *.js, *.md, *.json, *.toml, *.yaml, *.yml" +alwaysApply: false +--- + +# Extending hawk-eco + +hawk-eco is an open-source code intelligence platform. This document describes how to extend it with custom tools, skills, hooks, and integrations. + +## 1. Drop a project `AGENTS.md` + +When hawk-eco starts in a directory, it looks for project-level instructions and injects them into the system prompt. The lookup walks from your current working directory **up to the nearest git root** and reads the first matching file at each level — general rules at the repo root, more specific rules in sub-trees. Files are labeled with their directory in the prompt (e.g. `## Project guidelines (services/api/AGENTS.md)`). + +Accepted file names, in priority order at each level: + +| Path | Notes | +| --- | --- | +| `./AGENTS.md` | The classic spot — committed to your repo, shared with the team. | +| `./ZERO.md` | Brand-specific alias. Same format, lower priority. | +| `./.zero/AGENTS.md` | Project-local, hidden, gitignored. Personal notes that stay out of git. | + +Matching is **case-insensitive** on the basename, so `AGENTS.md`, `Agents.md`, and `agents.md` resolve to the same file on Windows and macOS. The git-tracked filename in this repo is `AGENTS.md` — keep that on case-sensitive filesystems (Linux, the WSL filesystem, or a CI runner) to match what the loader looks for. + +Both files use the same format. YAML frontmatter is optional; the markdown body is loaded as instructions for the agent. hawk-eco reads the file once at session start, so changes take effect on the next launch — not mid-session. + +```markdown +# Project conventions for + +- Build with `make`, not `go build` directly. +- Tests live next to the source file (`foo_test.go` next to `foo.go`). +- Run `make lint` before opening a PR. +- Never edit files under `third_party/` — those are vendored. +``` + +Tips: + +- Keep each file under ~8 KiB. hawk-eco caps the **total** across all matched files at 32 KiB; everything past the cap is dropped. +- Re-state rules in the imperative voice: "Run `make lint`", not "you should consider running the linter". +- Don't put secrets, model IDs, or environment-specific paths in `AGENTS.md`. Use config files for those. +- In a monorepo, drop a narrower `AGENTS.md` in each sub-tree (e.g. `services/api/AGENTS.md`). hawk-eco picks those up automatically when you launch from inside the sub-tree. +- A YAML frontmatter block (`---\n...\n---`) at the top is preserved verbatim in the injected prompt but is not parsed for `globs:` or `alwaysApply:` scoping today — keep the body self-contained. + +### Personal guidelines, across every project + +For preferences that follow *you*, not a specific repo (tone, tooling habits, workflow), drop a `ZERO.md` in your user config directory: `~/.config/hawk-eco/ZERO.md` on Linux/macOS, `%AppData%\Roaming\hawk-eco\ZERO.md` on Windows — the same directory as config files and your personal specialists. Same format and 8 KiB cap as the project files above, and the same case-insensitive basename match. + +This file is injected as its own `## User guidelines` section, before the project's `AGENTS.md`/`ZERO.md`, and is labeled as personal preference in the prompt: project guidelines are the later, more specific instruction and take precedence over it when the two conflict. + +## 2. Custom specialists + +Specialists are hawk-eco's sub-agents. Three scopes, in priority order: + +| Scope | Path | Shared? | +| --- | --- | --- | +| Built-in | compiled into hawk-eco | yes | +| User | `~/.config/hawk-eco/specialists/*.md` | no — your machine only | +| Project | `./.zero/specialists/*.md` | yes — the repo team | + +Project overrides user overrides built-in when names collide. + +A specialist is a markdown manifest with frontmatter and a system prompt: + +```markdown +--- +description: Reviews API changes for breaking-change risk and missing tests. +tools: read-only,plan +--- + +You review API changes. For every changed hunk in `internal/api/` or any file +that ends in `_api.go`: + +1. Confirm the public signature is backward-compatible, or note the breaking + change explicitly with the migration path. +2. Confirm a corresponding test exists in `internal/api/*_test.go` and that + the new behaviour is exercised. +3. Flag any new exported symbol without a doc comment. + +Reply with one JSON object per finding: `{"file", "line", "severity", "message", "fix"}`. +``` + +CLI management: + +```bash +hawk-eco specialist list +hawk-eco specialist show api-reviewer +hawk-eco specialist create api-reviewer \ + --project \ + --description "Reviews API changes" \ + --tools read-only,plan \ + --prompt "$(cat api-reviewer.md)" +hawk-eco specialist edit api-reviewer --project +hawk-eco specialist delete api-reviewer --project +hawk-eco specialist path # prints the resolved specialists directory +``` + +## 3. Skills + +Skills are markdown instruction files that extend agent capabilities. They can be: +- Project-scoped: dropped in `./.zero/skills/` or `./skills/` +- User-scoped: dropped in `~/.config/hawk-eco/skills/` + +A skill manifest: + +```markdown +--- +description: How to review Go code for security issues +globs: "*.go" +alwaysApply: true +--- + +When reviewing Go code for security: + +1. Check for SQL injection patterns +2. Verify error handling doesn't expose sensitive data +3. Confirm secrets are not hardcoded +4. Validate input sanitization +``` + +## 4. Hooks + +Hooks allow custom commands to run at specific lifecycle points: +- `beforeReview` — runs before code review starts +- `afterReview` — runs after code review completes +- `sessionStart` — runs at session initialization +- `sessionEnd` — runs at session teardown + +```bash +hawk-eco hook add beforeReview --command "lint-check" +hawk-eco hook remove beforeReview +hawk-eco hook list +``` + +## 5. MCP integration + +MCP (Model Context Protocol) servers can expose tools to hawk-eco: + +```bash +hawk-eco mcp add --name server --url http://localhost:8080 +hawk-eco mcp remove server +hawk-eco mcp list +``` + +## 6. Plugins + +Plugins extend hawk-eco with custom tools and capabilities: + +```bash +hawk-eco plugin add --name my-plugin --path ./my-plugin +hawk-eco plugin remove my-plugin +hawk-eco plugin list +``` + +## 7. Verification + +hawk-eco includes a self-verification system to validate local changes before contributing: + +```bash +hawk-eco verify +hawk-eco verify --fix +``` + +## Development + +```bash +make lint +hawk-eco verify +``` diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..ca62530 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,24 @@ +# Changelog + +All notable changes to `hawk-core-contracts` are documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [0.1.0] — 2026-07-05 + +Initial governed release. Establishes `VERSION` as the source of truth and +adds repo governance (`CODEOWNERS`, `Makefile`, boundary guard, engine-grade +CI) matching the rest of the hawk-eco foundation/engine repos. + +Existing contract packages at this version: + +- `types/` — severity, findings, shared result vocabulary +- `tools/` — provider-neutral tool call and tool result contracts +- `events/` — normalized tool and trace event contracts +- `policy/` — risk, permission verdict, guardian decision, approval request contracts +- `review/` — neutral review findings, comments, stats, and result contracts +- `verify/` — neutral verification findings, stats, and report contracts +- `sessions/` — cross-repo agent session state types diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..5019045 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,19 @@ +# CODEOWNERS for hawk-core-contracts (shared cross-repo contract types) +* @GrayCodeAI/maintainers + +# Contract packages — additive-only, cross-repo API surface +/types/ @GrayCodeAI/llm-team +/tools/ @GrayCodeAI/llm-team +/events/ @GrayCodeAI/llm-team +/policy/ @GrayCodeAI/llm-team +/review/ @GrayCodeAI/llm-team +/verify/ @GrayCodeAI/llm-team +/sessions/ @GrayCodeAI/llm-team +/VERSION @GrayCodeAI/maintainers + +# CI / release +/.github/ @GrayCodeAI/devops-team +/Makefile @GrayCodeAI/devops-team + +# Documentation +*.md @GrayCodeAI/docs-team diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..616efcf --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,60 @@ +# Code of Conduct + +## Our pledge + +We — the maintainers and contributors of the hawk-core-contracts project — pledge to +make participation in our community a harassment-free experience for everyone, +regardless of age, body size, visible or invisible disability, ethnicity, sex +characteristics, gender identity and expression, level of experience, +education, socio-economic status, nationality, personal appearance, race, +religion, or sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our standards + +Examples of behaviour that contributes to a positive environment: + +- Demonstrating empathy and kindness toward other people. +- Being respectful of differing opinions, viewpoints, and experiences. +- Giving and gracefully accepting constructive feedback. +- Accepting responsibility, apologising to those affected by mistakes, and + learning from the experience. +- Focusing on what is best not just for us as individuals, but for the + overall community. + +Examples of unacceptable behaviour: + +- The use of sexualised language or imagery, and sexual attention or advances. +- Trolling, insulting or derogatory comments, and personal or political + attacks. +- Public or private harassment. +- Publishing others' private information, such as a physical or email + address, without their explicit permission. +- Other conduct which could reasonably be considered inappropriate in a + professional setting. + +## Enforcement + +Community leaders are responsible for clarifying and enforcing our standards +of acceptable behaviour, and will take appropriate and fair corrective +action in response to any behaviour they deem inappropriate, threatening, +offensive, or harmful. + +Instances of abusive, harassing, or otherwise unacceptable behaviour may be +reported to the maintainers via the contact in `SECURITY.md` or by opening a +confidential GitHub Security Advisory at +. All +complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of +the reporter of any incident. + +## Attribution + +This Code of Conduct is adapted from the +[Contributor Covenant, version 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html). + +For answers to common questions about this code of conduct, see the +Contributor Covenant FAQ at . diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0ca34c5 --- /dev/null +++ b/Makefile @@ -0,0 +1,114 @@ +# Canonical hawk-eco Makefile for Go LIBRARY repos. +# Source of truth: https://github.com/GrayCodeAI/hawk/blob/main/.shared-templates/Makefile.library.tmpl +# hawk-core-contracts is a foundation library consumed by hawk and engines; +# no standalone binary, no release beyond tagging. + +# --------------------------------------------------------------------------- +# Project metadata +# --------------------------------------------------------------------------- +NAME := hawk-core-contracts + +# --------------------------------------------------------------------------- +# Versioning — sourced from VERSION file; falls back to git describe. +# --------------------------------------------------------------------------- +VERSION ?= $(shell v=$$(cat VERSION 2>/dev/null | head -n1 | tr -d '[:space:]'); if [ -n "$$v" ]; then echo "$$v"; else git describe --tags --always --dirty 2>/dev/null || echo "dev"; fi) +COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo "none") +DATE := $(shell date -u '+%Y-%m-%dT%H:%M:%SZ') + +# --------------------------------------------------------------------------- +# Tooling — pinned, install if missing. +# --------------------------------------------------------------------------- +GOBIN_DIR := $(shell go env GOPATH)/bin +GOLANGCI := $(GOBIN_DIR)/golangci-lint +GOFUMPT := $(GOBIN_DIR)/gofumpt +GOIMPORTS := $(GOBIN_DIR)/goimports +GOVULNCHECK := $(GOBIN_DIR)/govulncheck + +# --------------------------------------------------------------------------- +# Phony declarations (alphabetical). +# --------------------------------------------------------------------------- +.PHONY: all boundaries build ci clean cover fmt help hooks lint lint-fix \ + security test test-race tidy version vet + +boundaries: ## Enforce foundation-repo import boundaries (zero GrayCodeAI/* deps). + bash ./scripts/check-ecosystem-boundaries.sh + +# --------------------------------------------------------------------------- +# Default target. +# --------------------------------------------------------------------------- +all: lint test build ## Default — lint, test, build. + +# --------------------------------------------------------------------------- +# Build. +# --------------------------------------------------------------------------- +build: ## Build all contract packages. + go build ./... + +# --------------------------------------------------------------------------- +# Tests. +# --------------------------------------------------------------------------- +test: ## Run unit tests. + go test ./... -count=1 -timeout=60s + +test-race: ## Run unit tests with the race detector. + go test ./... -race -count=1 -timeout=120s + +cover: ## Generate a coverage report (coverage.out + coverage.html). + go test ./... -race -coverprofile=coverage.out -covermode=atomic -timeout=120s + @go tool cover -func=coverage.out | grep "^total:" + @go tool cover -html=coverage.out -o coverage.html + @echo "Coverage report: coverage.html" + +# --------------------------------------------------------------------------- +# Quality gates. +# --------------------------------------------------------------------------- +fmt: ## Format source files (gofumpt + goimports). + @command -v $(GOFUMPT) >/dev/null 2>&1 || (echo "install: go install mvdan.cc/gofumpt@latest" && exit 1) + @command -v $(GOIMPORTS) >/dev/null 2>&1 || (echo "install: go install golang.org/x/tools/cmd/goimports@latest" && exit 1) + $(GOFUMPT) -w . + $(GOIMPORTS) -w . + +vet: ## Run go vet. + go vet ./... + +lint: ## Run golangci-lint. + @command -v $(GOLANGCI) >/dev/null 2>&1 || (echo "install: go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest" && exit 1) + $(GOLANGCI) run ./... --timeout=5m + +lint-fix: ## Run golangci-lint with --fix. + @command -v $(GOLANGCI) >/dev/null 2>&1 || (echo "install: go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest" && exit 1) + $(GOLANGCI) run ./... --fix --timeout=5m + +security: ## Run govulncheck. + @command -v $(GOVULNCHECK) >/dev/null 2>&1 || (echo "install: go install golang.org/x/vuln/cmd/govulncheck@latest" && exit 1) + $(GOVULNCHECK) ./... + +tidy: ## Tidy go.mod / go.sum. + go mod tidy + go mod verify + +# --------------------------------------------------------------------------- +# Composite gate used by CI and pre-push. +# --------------------------------------------------------------------------- +ci: tidy fmt vet lint boundaries test-race security ## Run everything CI runs. + @echo "All CI checks passed." + +# --------------------------------------------------------------------------- +# Misc. +# --------------------------------------------------------------------------- +version: ## Print the version that will be embedded. + @echo "Version: $(VERSION)" + @echo "Commit: $(COMMIT)" + @echo "Date: $(DATE)" + +clean: ## Remove build artefacts. + rm -rf coverage.out coverage.html + go clean -testcache + +help: ## Show this help. + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-15s\033[0m %s\n", $$1, $$2}' + +hooks: ## Install git hooks via lefthook (boundary guard, tests, co-author strip). + @command -v lefthook >/dev/null 2>&1 || (echo "install: go install github.com/evilmartians/lefthook@latest" && exit 1) + git config --unset core.hooksPath 2>/dev/null || true + lefthook install diff --git a/README.md b/README.md index d14624b..e536537 100644 --- a/README.md +++ b/README.md @@ -1,69 +1,132 @@ # hawk-core-contracts -Shared contracts for the Hawk ecosystem. +Shared contracts for the hawk ecosystem. -This repo exists to hold stable cross-repo definitions used by: +This repo holds stable cross-repo type definitions used by every engine and +`hawk` itself: severity levels, findings, tool contracts, event models, policy +verdicts, review results, verification reports, and agent session state. -- `hawk` -- `eyrie` -- `yaad` -- `tok` -- `trace` -- `sight` -- `inspect` -- Hawk SDKs and extension surfaces where needed +**Tagline:** Shared contracts for the hawk ecosystem. + +## Install + +```sh +go get github.com/GrayCodeAI/hawk-core-contracts +``` + +## Quick Reference + +| Package | Key Types | Purpose | +|---------|-----------|---------| +| `types/` | `Severity`, `Finding`, `FindingSlice`, `ParseSeverity` | Severity levels, findings, shared result vocabulary | +| `tools/` | `ToolCall`, `ToolResult` | Provider-neutral tool call and result contracts | +| `events/` | `ToolEvent`, `TraceEvent`, `UsageInfo` | Normalized tool and trace event contracts | +| `policy/` | `Risk`, `PermissionVerdict`, `Allow`, `Deny` | Risk, permission verdict, guardian decision, approval request contracts | +| `review/` | `Result`, `Finding`, `Comment` | Neutral review findings, comments, stats, and result contracts | +| `verify/` | `Report`, `Finding` | Neutral verification findings, stats, and report contracts | +| `sessions/` | `Phase`, `CostAccumulator`, `ParsePhase` | Cross-repo agent session state types | + +## Architecture + +``` +hawk-core-contracts (stdlib only) +├── types/ Severity, Finding, FindingSlice — the core vocabulary +├── tools/ ToolCall, ToolResult — provider-neutral tool contracts +├── events/ ToolEvent, TraceEvent — normalized event contracts +├── policy/ Risk, PermissionVerdict — governance contracts +├── review/ Result, Finding, Comment — review result contracts +├── verify/ Report, Finding — verification report contracts +└── sessions/ Phase, CostAccumulator — session state contracts +``` ## Scope -Allowed here: +### Allowed here -- shared enums -- shared structs -- event models -- finding/result models +- shared enums (severity levels, risk levels, phases) +- shared structs (findings, tool calls, events, verdicts, reports) +- event models (tool events, trace events, usage info) +- finding/result models (severity, confidence, status) - engine request/response contracts - policy and tool contracts -Not allowed here: +### Not allowed here -- CLI code +- CLI code (commands, flags, shell output) - provider implementations - runtime logic - storage implementations - product orchestration - -## Migration history - -The legacy `github.com/GrayCodeAI/hawk/shared/types` package has been removed. -Severity, findings, and the packages below are the supported cross-repo API. +- anything that belongs in a single consuming repo Engines should depend on this repo only when they produce or consume a shared -contract. Contract-free engines (for example `eyrie`, `yaad`, `trace`) should -not add the dependency just for consistency. - -## Package map +cross-repo contract. Contract-free engines (e.g., `eyrie`, `yaad`, `trace`) +should not add the dependency just for consistency. -- `types/` - severity, findings, and shared result vocabulary -- `tools/` - provider-neutral tool call and tool result contracts -- `events/` - normalized tool and trace event contracts -- `policy/` - risk, permission verdict, guardian decision, approval request contracts -- `review/` - neutral review findings, comments, stats, and result contracts -- `verify/` - neutral verification findings, stats, and report contracts +If a type is only used inside one repo, it should stay in that repo. -## Current status +## Migration History -Completed: +The legacy `github.com/GrayCodeAI/hawk/shared/types` package has been removed. +All shared finding and severity definitions now live here. Migration is +complete: -1. shared finding and severity definitions moved here +1. Severity and finding definitions migrated from `hawk/shared/types` 2. `sight` and `inspect` migrated to import this repo -3. Hawk docs and READMEs updated -4. tool, event, and policy contracts added -5. review and verification result contracts added - -## Governance rules - -- keep this repo implementation-free -- prefer additive changes -- avoid product-specific runtime assumptions -- do not move Hawk orchestration code here -- if a type is only used inside one repo, it should stay in that repo +3. Tool, event, and policy contracts added +4. Review and verification result contracts added +5. Sessions package added for cross-repo session state + +## Ecosystem + +hawk-core-contracts is a **foundation repo** in the hawk-eco mono-ecosystem: + +| Component | Purpose | +|-----------|---------| +| **hawk-core-contracts** | Shared cross-repo contracts (this repo) | +| **hawk-mcpkit** | Shared MCP server scaffolding | +| **eyrie** | LLM provider runtime — routing, streaming, retries, caching | +| **yaad** | Graph-based persistent memory for coding agents | +| **tok** | Tokenizer, compression, secrets scanning, rate limiting | +| **sight** | Diff-based code review and static analysis | +| **inspect** | Security audit library (CVE, API security, CI output) | +| **trace** | Session capture and replay CLI | +| **hawk** | AI coding agent (this repo) | + +`hawk` and all engines import `hawk-core-contracts` when they share a real +cross-repo contract; the repo itself never imports back. + +## Ecosystem Boundaries + +Rules that keep this repo at the foundation layer: + +- **Zero hawk-eco dependencies.** This repo imports only the Go standard + library. `make boundaries` (also run in CI) enforces this with + `scripts/check-ecosystem-boundaries.sh`. +- **Implementation-free.** See Scope above — no CLI code, provider + implementations, runtime logic, storage, or orchestration. +- **Consumers, not dependents.** `hawk` and engines import this repo when + they share a real cross-repo contract; it never imports them back. + +If a change here would require importing anything outside the standard +library, that type does not belong in this repo. + +## Package Ownership + +| Path | Team | +|------|------| +| `/types/` | `@GrayCodeAI/llm-team` | +| `/tools/` | `@GrayCodeAI/llm-team` | +| `/events/` | `@GrayCodeAI/llm-team` | +| `/policy/` | `@GrayCodeAI/llm-team` | +| `/review/` | `@GrayCodeAI/llm-team` | +| `/verify/` | `@GrayCodeAI/llm-team` | +| `/sessions/` | `@GrayCodeAI/llm-team` | +| `/VERSION` | `@GrayCodeAI/maintainers` | +| `/Makefile` | `@GrayCodeAI/devops-team` | +| `/*.md` | `@GrayCodeAI/docs-team` | +| `/.github/` | `@GrayCodeAI/devops-team` | + +## License + +MIT — see [LICENSE](LICENSE). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..00027a7 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,71 @@ +# Security Policy — hawk-core-contracts + +## Supported versions + +We support the latest minor version on each `0.x` line, and the latest two +minor versions once `1.x` ships. Older versions receive critical-severity +fixes only on a best-effort basis. + +The current canonical version is the contents of the [`VERSION`](./VERSION) +file at the repo root. See [`VERSIONING.md`](https://github.com/GrayCodeAI/hawk/blob/main/VERSIONING.md) +for the eco-wide versioning scheme. + +## Reporting a vulnerability + +**Do not open a public GitHub issue for security vulnerabilities.** Instead: + +1. Open a private [GitHub Security Advisory](https://github.com/GrayCodeAI/hawk-core-contracts/security/advisories/new), **or** +2. Email `security@graycode.ai` with the details below. + +Include in your report: + +- A description of the vulnerability and the affected component. +- Steps to reproduce, ideally with a minimal proof-of-concept. +- The version (`VERSION` file or git SHA) you tested against. +- The potential impact and any suggested mitigation. + +**Response targets:** + +- Initial acknowledgement: within **48 hours**. +- Triage and severity assessment: within **5 business days**. +- Coordinated fix and disclosure: within **30 days** for high/critical, **90 + days** for medium/low (per industry-standard responsible disclosure). + +## Disclosure policy + +We follow [coordinated vulnerability disclosure](https://en.wikipedia.org/wiki/Coordinated_vulnerability_disclosure): + +- Reporters receive credit in the advisory and CHANGELOG (unless they opt + out). +- We request that reporters refrain from public disclosure until a fix has + been released or the disclosure deadline above has elapsed. +- We will not pursue legal action against good-faith researchers acting + within this policy. + +## Security practices in this repo + +- **Dependency monitoring:** vulnerable dependencies are detected by + `govulncheck`, which runs on every CI build (see "Vulnerability scanning"). +- **Static analysis:** `golangci-lint` / `ruff` / `mypy` enforced in CI. +- **Vulnerability scanning:** `govulncheck` (Go) / `pip-audit` (Python) run + on every CI build. +- **Lockfiles:** `go.sum` / `pnpm-lock.yaml` / `pyproject.toml` are pinned + and committed. +- **Reproducible builds:** release artefacts ship with SHA-256 checksums via + goreleaser. +- **No secrets in source:** API keys are configuration, not constants. Pre- + commit hooks block accidental secret commits. + +## Scope + +This policy covers the code in this repository and the release artefacts +published from it. It does not cover: + +- Third-party dependencies (report to upstream). +- LLM provider services that hawk-core-contracts integrates with (report to the + provider). +- Local filesystem misuse where an attacker already has shell access (out of + threat model). + +For hawk-core-contracts-specific threat-model notes, see the README and any docs in +this repo. diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..6e8bf73 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.1.0 diff --git a/lefthook.yml b/lefthook.yml index 2dbf45f..f67aee3 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -1,3 +1,5 @@ +# Canonical lefthook config for hawk-eco Go repos. +# Source of truth: https://github.com/GrayCodeAI/hawk/blob/main/.shared-templates/lefthook.yml.tmpl # Install: lefthook install # Skip once: LEFTHOOK=0 git commit -m "..." @@ -12,3 +14,10 @@ commit-msg: strip-co-authored-by: run: | sed '/^[Cc]o-[Aa]uthored-[Bb]y:/d' "{1}" > "{1}.tmp" && mv "{1}.tmp" "{1}" + +pre-push: + commands: + boundaries: + run: bash ./scripts/check-ecosystem-boundaries.sh + test: + run: go test ./... -race -count=1 diff --git a/policy/policy.go b/policy/policy.go index e93349a..ce0c39c 100644 --- a/policy/policy.go +++ b/policy/policy.go @@ -1,6 +1,9 @@ package policy -import "fmt" +import ( + "fmt" + "strings" +) // Risk is the severity of a permission or policy verdict. type Risk int @@ -30,7 +33,7 @@ func (r Risk) String() string { // ParseRisk parses a risk name (case-insensitive) into a Risk value. func ParseRisk(s string) (Risk, error) { - switch lower(s) { + switch strings.ToLower(strings.TrimSpace(s)) { case "low": return RiskLow, nil case "medium", "med", "moderate": @@ -122,15 +125,3 @@ type PermissionRequest struct { ToolID string `json:"tool_id,omitempty"` Summary string `json:"summary,omitempty"` } - -func lower(s string) string { - b := make([]byte, len(s)) - for i := 0; i < len(s); i++ { - c := s[i] - if c >= 'A' && c <= 'Z' { - c += 'a' - 'A' - } - b[i] = c - } - return string(b) -} diff --git a/scripts/check-ecosystem-boundaries.sh b/scripts/check-ecosystem-boundaries.sh new file mode 100755 index 0000000..e0d5265 --- /dev/null +++ b/scripts/check-ecosystem-boundaries.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT_DIR" + +# hawk-core-contracts is a foundation library: it sits below every engine +# and below hawk itself. It must depend on nothing in the hawk ecosystem — +# only the Go standard library. Imports of this module's own path (internal +# cross-package imports between types/, tools/, events/, etc.) are fine. +FORBIDDEN='github\.com/GrayCodeAI/(?!hawk-core-contracts(/|"))' +FORBIDDEN_GREP='github\.com/GrayCodeAI/[^"]*' +SELF_MODULE='github.com/GrayCodeAI/hawk-core-contracts' + +if command -v rg >/dev/null 2>&1; then + violations="$(rg -n "$FORBIDDEN" --pcre2 --glob '*.go' . || true)" +else + violations="$(grep -rn --include='*.go' -E "$FORBIDDEN_GREP" . | grep -v "$SELF_MODULE" || true)" +fi + +if [[ -n "${violations}" ]]; then + echo "forbidden hawk-eco imports found in hawk-core-contracts:" + echo "${violations}" + echo + echo "hawk-core-contracts is a foundation repo — it must not depend on hawk, engines, or any other GrayCodeAI/* package" + exit 1 +fi + +echo "ecosystem boundary guard passed" diff --git a/sessions/sessions.go b/sessions/sessions.go index 2ae6dd1..31b2b22 100644 --- a/sessions/sessions.go +++ b/sessions/sessions.go @@ -105,10 +105,10 @@ type PhaseUsage struct { // pipeline phases within a session. It is the single source of truth for // the cost-per-resolution metric exposed by hawk's ResolutionPipeline. type CostAccumulator struct { - SessionID SessionID `json:"session_id"` - ByPhase map[Phase]PhaseUsage `json:"by_phase"` - TotalTokens int `json:"total_tokens"` - TotalCostUSD float64 `json:"total_cost_usd"` + SessionID SessionID `json:"session_id"` + ByPhase map[Phase]PhaseUsage `json:"by_phase"` + TotalTokens int `json:"total_tokens"` + TotalCostUSD float64 `json:"total_cost_usd"` } // NewCostAccumulator returns a zero-value CostAccumulator for the given session. diff --git a/version.go b/version.go new file mode 100644 index 0000000..a950ed1 --- /dev/null +++ b/version.go @@ -0,0 +1,15 @@ +// Package contracts provides cross-repo type contracts for hawk-eco. +// +// The Version variable is sourced from the VERSION file at the repo root. +package contracts + +import ( + _ "embed" + "strings" +) + +//go:embed VERSION +var versionFile string + +// Version of the hawk-core-contracts library. Single source of truth: VERSION file. +var Version = strings.TrimSpace(versionFile)