Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,35 @@ jobs:
with:
extra_args: --only-verified

proto:
name: proto (lint, breaking, generate)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# buf breaking compares against the main branch's history; a
# shallow checkout wouldn't have those commits available.
fetch-depth: 0
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version: ${{ env.GO_VERSION }}
cache: true
- name: Install buf and protoc-gen-go
run: |
go install github.com/bufbuild/buf/cmd/buf@v1.71.0
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.11
- name: buf lint
run: buf lint
- name: buf breaking (against main)
run: |
if git cat-file -e main:proto 2>/dev/null; then
buf breaking --against '.git#branch=main'
else
echo "::notice::proto/ does not exist on main yet — nothing to compare against (this is expected for the PR that first introduces it); breaking-change detection activates for every PR after this one merges."
fi
- name: buf generate (smoke test — confirm codegen still succeeds)
run: buf generate

build:
name: build (${{ matrix.goos }}/${{ matrix.goarch }})
runs-on: ubuntu-latest
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
.gocache/
coverage.out

# Generated code from proto/ — regenerate with `buf generate`. Not
# committed: fully derivable, and no consumer depends on a checked-in copy
# yet (see README's Codegen section).
gen/

# Go workspace (local dev only)
go.work
go.work.sum
Expand Down
19 changes: 19 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,27 @@ go test ./... # Run tests
go vet ./... # Static analysis
go mod tidy # Tidy modules
make boundaries # Enforce ecosystem boundary rules
make proto # Lint proto/, check breaking changes, regenerate gen/
```

## Proto / Cross-Language Schema

`proto/` mirrors every exported Go type for schema-level breaking-change
detection and Python/TypeScript codegen (`gen/`, not committed — regenerate
with `make proto` / `buf generate`). It is additive, not authoritative: the
hand-written Go package above is still the source of truth for Go
consumers, and `gen/go/` lives in its own nested module
(`gen/go/go.mod`) specifically so pulling in `google.golang.org/protobuf`
there never touches the root module's zero-dependency guarantee.

**When you change an exported Go type, update the matching `.proto`
message in the same PR.** They are not generated from each other and can
drift silently otherwise. CI's `proto` job (`buf lint` + `buf breaking`
against `main`) will fail the build on an accidental breaking change to the
schema — if the break is intentional, explain why in the PR description and
add a CHANGELOG entry under a new version, same as any other breaking
change to this package.

## Scope Rules

**Allowed:** shared enums, structs, event models, policy/tool contracts
Expand Down
65 changes: 59 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,71 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.1.0] — 2026-07-05
### Added

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.
- `proto/` — a Buf-managed protobuf schema mirroring every exported Go type,
used for schema-level breaking-change detection (`buf breaking`, gated in
CI on every PR once this lands) and for generating Python/TypeScript
clients (`buf generate` → `gen/python/`, `gen/typescript/`; not committed,
regenerate on demand). The hand-written Go package is unaffected and stays
the source of truth for Go consumers — see `proto/README.md` (or the repo
README's Codegen section) for the mapping rules between the two.
- CI: `proto` job runs `buf lint` and `buf breaking` (against `main`) on
every PR that touches `proto/`.

Existing contract packages at this version:
No contract package changes since `v0.1.4` — this release line so far is
CI/tooling and the addition above.

## [0.1.4] — 2026-07-11

### Added

- `VERSION` file + `//go:embed`-based `contracts.Version`, replacing ad hoc
versioning with a single source of truth (#6).
- CI: race-detector + coverage-threshold gate.

### Changed

- `policy.ParseRisk` now uses `strings.ToLower`/`strings.TrimSpace` instead
of a hand-rolled lowercasing helper — internal only, no behavior change.
- Go version bumped to 1.26.5.

No wire-format / contract-shape changes in this release.

## [0.1.3] — 2026-07-05

Added the MIT `LICENSE` file. No contract changes.

## [0.1.2] — 2026-07-04

### Added

- **New `sessions/` package**: `Phase`, `SessionID`, `ContextSnapshot`,
`ToolCallRecord`, `PhaseUsage`, `CostAccumulator` — cross-repo agent
session identity, pipeline-phase tagging, and cost accounting, so engines
and hawk's orchestrator share one vocabulary for this without a circular
dependency.
- `CODEOWNERS`, CI governance workflow.

## [0.1.1] — 2026-06-25

### Added

- `review.SASTFusionResult` (`Confirmed`/`Dismissed`/`Unaddressed` finding
lists) on `review.Result`, populated when SAST-LLM fusion is active (#1).

## [0.1.0] — 2026-06-21

Initial governed release. Establishes repo governance (`Makefile`, boundary
guard, engine-grade CI) matching the rest of the hawk-eco foundation/engine
repos.

Contract packages at this version — six packages; `sessions/` did not exist
yet (added in `0.1.2`, see above):

- `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
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/review/ @GrayCodeAI/llm-team
/verify/ @GrayCodeAI/llm-team
/sessions/ @GrayCodeAI/llm-team
/proto/ @GrayCodeAI/llm-team
/VERSION @GrayCodeAI/maintainers

# CI / release
Expand Down
17 changes: 16 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ 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
proto security test test-race tidy version vet

boundaries: ## Enforce foundation-repo import boundaries (zero GrayCodeAI/* deps).
bash ./scripts/check-ecosystem-boundaries.sh
Expand Down Expand Up @@ -87,6 +87,21 @@ tidy: ## Tidy go.mod / go.sum.
go mod tidy
go mod verify

# ---------------------------------------------------------------------------
# Proto (cross-language schema — not part of the `ci` composite target,
# since it needs `buf` installed separately; run explicitly or see the
# `proto` job in .github/workflows/ci.yml for what actually gates merges).
# ---------------------------------------------------------------------------
proto: ## Lint proto/, check for breaking changes vs main, and regenerate gen/.
@command -v buf >/dev/null 2>&1 || (echo "install: go install github.com/bufbuild/buf/cmd/buf@v1.71.0" && exit 1)
buf lint
@if git cat-file -e main:proto 2>/dev/null; then \
buf breaking --against '.git#branch=main'; \
else \
echo "proto/ does not exist on main yet — skipping breaking-change check."; \
fi
buf generate

# ---------------------------------------------------------------------------
# Composite gate used by CI and pre-push.
# ---------------------------------------------------------------------------
Expand Down
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,40 @@ Rules that keep this repo at the foundation layer:
If a change here would require importing anything outside the standard
library, that type does not belong in this repo.

## Codegen

`proto/hawk/contracts/v1/*.proto` mirrors every exported type above, one
`.proto` file per Go package. It exists for two things the hand-written Go
package alone can't give you:

- **Schema-level breaking-change detection.** CI's `proto` job runs
`buf breaking` against `main` on every PR — catches the class of bug
already seen once in this ecosystem (`hawk-sdk-go` independently
hand-rolled its own `ToolResult` with a field named `tool_call_id`,
diverging from this repo's `tool_use_id`, because nothing checked for it).
- **Python / TypeScript codegen**, for whenever a non-Go consumer wants this
vocabulary instead of hand-porting it. `buf generate` produces
`gen/go/`, `gen/python/`, `gen/typescript/` — not committed (regenerate
with `make proto`), and not currently imported by anything: `hawk-sdk-go`,
`hawk-sdk-python`, and `yaad`'s TypeScript SDK all still hand-port their
own subset of this vocabulary today (`hawk-sdk-python/src/hawk/sessions.py`
and `types.py` are the main example) and haven't adopted the generated
packages. That's a real adoption decision for those repos to make on
their own schedule, not something this repo forces.

**The `.proto` files and the Go structs are two independent, hand-kept-in-sync
definitions** — `gen/go/` is deliberately its own nested Go module (see
`gen/go/go.mod`) precisely so depending on `google.golang.org/protobuf`
there never touches this repo's zero-dependency root module. When you add
or change an exported Go type, update the matching `.proto` message in the
same PR — see `AGENTS.md`.

Some fields don't map 1:1 by protobuf convention; each divergence is
commented in the `.proto` source at the point it occurs (e.g. `Severity`'s
zero value is `SEVERITY_INFO`, not an `_UNSPECIFIED` sentinel, to keep its
numeric values identical to the Go `iota` constants that
`map[Severity]int` fields serialize by).

## Package Ownership

| Path | Team |
Expand All @@ -122,6 +156,7 @@ library, that type does not belong in this repo.
| `/review/` | `@GrayCodeAI/llm-team` |
| `/verify/` | `@GrayCodeAI/llm-team` |
| `/sessions/` | `@GrayCodeAI/llm-team` |
| `/proto/` | `@GrayCodeAI/llm-team` |
| `/VERSION` | `@GrayCodeAI/maintainers` |
| `/Makefile` | `@GrayCodeAI/devops-team` |
| `/*.md` | `@GrayCodeAI/docs-team` |
Expand Down
20 changes: 20 additions & 0 deletions buf.gen.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
version: v2
plugins:
# Go: generated for potential future opt-in use only — NOT wired into the
# existing hand-written package (types/, tools/, events/, policy/,
# review/, verify/, sessions/), which stays the source of truth for the
# three existing Go consumers (sight, inspect, hawk). See buf.yaml /
# README for why.
- local: protoc-gen-go
out: gen/go
opt: paths=source_relative
# Python and TypeScript: no existing consumers adopt these yet (see
# README) — generated so hawk-sdk-python / yaad's TS SDK can opt in later
# without hawk-core-contracts needing another release.
- remote: buf.build/protocolbuffers/python
out: gen/python
- remote: buf.build/protocolbuffers/pyi
out: gen/python
- remote: buf.build/bufbuild/es
out: gen/typescript
opt: target=ts
12 changes: 12 additions & 0 deletions buf.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
version: v2
modules:
- path: proto
name: buf.build/graycodeai/hawk-core-contracts
deps:
- buf.build/protocolbuffers/wellknowntypes
lint:
use:
- STANDARD
breaking:
use:
- FILE
44 changes: 44 additions & 0 deletions proto/hawk/contracts/v1/events.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
syntax = "proto3";

package hawk.contracts.v1;

import "google/protobuf/struct.proto";
import "google/protobuf/timestamp.proto";

option go_package = "github.com/GrayCodeAI/hawk-core-contracts/gen/go/hawk/contracts/v1;contractsv1";

// ToolEvent mirrors events.ToolEvent (events/events.go) — a normalized
// tool event emitted by Hawk workflows.
message ToolEvent {
string tool_name = 1;
google.protobuf.Struct tool_input = 2;
string cwd = 3;
google.protobuf.Timestamp timestamp = 4;
string session_id = 5;
string transcript = 6;
}

// TraceEvent mirrors events.TraceEvent — a normalized trace record for
// model/runtime activity.
message TraceEvent {
string id = 1;
string name = 2;
string input = 3;
string output = 4;
string model = 5;
google.protobuf.Timestamp start_time = 6;
google.protobuf.Timestamp end_time = 7;
map<string, string> metadata = 8;
// Optional in Go (*UsageInfo, omitempty). proto3 message fields are
// inherently optional/presence-tracked, so no extra wrapper is needed.
UsageInfo usage = 9;
}

// UsageInfo mirrors events.UsageInfo — token and cost information for a
// trace event.
message UsageInfo {
int32 prompt_tokens = 1;
int32 completion_tokens = 2;
int32 total_tokens = 3;
double cost_usd = 4;
}
41 changes: 41 additions & 0 deletions proto/hawk/contracts/v1/finding.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
syntax = "proto3";

package hawk.contracts.v1;

import "google/protobuf/timestamp.proto";
import "hawk/contracts/v1/severity.proto";

option go_package = "github.com/GrayCodeAI/hawk-core-contracts/gen/go/hawk/contracts/v1;contractsv1";

// Finding mirrors types.Finding (types/finding.go) — a unified analysis
// concern sourced from Hawk support engines.
message Finding {
string id = 1;
string source = 2;
string concern = 3;
Severity severity = 4;
string file = 5;
string url = 6;
int32 line = 7;
int32 end_line = 8;
string message = 9;
string cwe = 10;
double confidence = 11;
string fix = 12;
string reasoning = 13;
repeated string tags = 14;
map<string, string> metadata = 15;
google.protobuf.Timestamp created_at = 16;
}

// FindingSummary mirrors types.FindingSummary — aggregate counts over a
// set of findings. by_severity is keyed by Severity.String() (e.g.
// "critical") in Go, since it's built from a map[string]int, not
// map[Severity]int — unlike review.Stats/verify.Stats below, this one
// really is string-keyed on the wire today.
message FindingSummary {
int32 total = 1;
map<string, int32> by_source = 2;
map<string, int32> by_severity = 3;
double avg_confidence = 4;
}
Loading
Loading