diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 11f1e4e..2658adb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/.gitignore b/.gitignore index 0d81c3b..1809bd1 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/AGENTS.md b/AGENTS.md index 32343e8..894f61b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index ca62530..debf25a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,13 +7,67 @@ 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 @@ -21,4 +75,3 @@ Existing contract packages at this version: - `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 index 5019045..c947d73 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -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 diff --git a/Makefile b/Makefile index 0ca34c5..dff69ad 100644 --- a/Makefile +++ b/Makefile @@ -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 @@ -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. # --------------------------------------------------------------------------- diff --git a/README.md b/README.md index e536537..3827637 100644 --- a/README.md +++ b/README.md @@ -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 | @@ -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` | diff --git a/buf.gen.yaml b/buf.gen.yaml new file mode 100644 index 0000000..6ae1d0d --- /dev/null +++ b/buf.gen.yaml @@ -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 diff --git a/buf.yaml b/buf.yaml new file mode 100644 index 0000000..7f11def --- /dev/null +++ b/buf.yaml @@ -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 diff --git a/proto/hawk/contracts/v1/events.proto b/proto/hawk/contracts/v1/events.proto new file mode 100644 index 0000000..0c04807 --- /dev/null +++ b/proto/hawk/contracts/v1/events.proto @@ -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 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; +} diff --git a/proto/hawk/contracts/v1/finding.proto b/proto/hawk/contracts/v1/finding.proto new file mode 100644 index 0000000..c40a276 --- /dev/null +++ b/proto/hawk/contracts/v1/finding.proto @@ -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 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 by_source = 2; + map by_severity = 3; + double avg_confidence = 4; +} diff --git a/proto/hawk/contracts/v1/policy.proto b/proto/hawk/contracts/v1/policy.proto new file mode 100644 index 0000000..074a0ff --- /dev/null +++ b/proto/hawk/contracts/v1/policy.proto @@ -0,0 +1,45 @@ +syntax = "proto3"; + +package hawk.contracts.v1; + +option go_package = "github.com/GrayCodeAI/hawk-core-contracts/gen/go/hawk/contracts/v1;contractsv1"; + +// Risk mirrors policy.Risk (policy/policy.go) — severity of a permission +// or policy verdict. Like Severity, Go's zero value (Low) is meaningful, +// not a sentinel, so the numeric values are kept identical to the Go iota +// constants (Low=0 .. Blocked=3) rather than inserting an artificial +// _UNSPECIFIED at 0. +enum Risk { + // buf:lint:ignore ENUM_ZERO_VALUE_SUFFIX + RISK_LOW = 0; + RISK_MEDIUM = 1; + RISK_HIGH = 2; + RISK_BLOCKED = 3; +} + +// PermissionVerdict mirrors policy.PermissionVerdict — the unified outcome +// type for permission subsystems. +message PermissionVerdict { + bool allowed = 1; + string reason = 2; + string rule = 3; + Risk risk = 4; + double confidence = 5; + string source = 6; +} + +// GuardianDecision mirrors policy.GuardianDecision — a provider-neutral +// automatic permission review response. +message GuardianDecision { + bool allowed = 1; + string reason = 2; + double confidence = 3; +} + +// PermissionRequest mirrors policy.PermissionRequest — a user-facing +// approval request. +message PermissionRequest { + string tool_name = 1; + string tool_id = 2; + string summary = 3; +} diff --git a/proto/hawk/contracts/v1/review.proto b/proto/hawk/contracts/v1/review.proto new file mode 100644 index 0000000..7ea3700 --- /dev/null +++ b/proto/hawk/contracts/v1/review.proto @@ -0,0 +1,88 @@ +syntax = "proto3"; + +package hawk.contracts.v1; + +import "hawk/contracts/v1/severity.proto"; + +option go_package = "github.com/GrayCodeAI/hawk-core-contracts/gen/go/hawk/contracts/v1;contractsv1"; + +// ReviewFinding mirrors review.Finding (review/review.go) — the neutral +// review finding contract shared across Hawk and review engines. Named +// ReviewFinding here (proto has one flat message namespace per package; +// Go disambiguates by package instead) — distinct from Finding +// (finding.proto, mirrors types.Finding) and VerifyFinding (verify.proto): +// these are three genuinely different field sets in Go today, not one +// shape, so they stay three separate messages here too. +message ReviewFinding { + string concern = 1; + Severity severity = 2; + string file = 3; + int32 line = 4; + int32 end_line = 5; + string message = 6; + string fix = 7; + string reasoning = 8; + string cwe = 9; + double confidence = 10; + bool sast_source = 11; +} + +// ReviewInlineComment mirrors review.InlineComment — a review finding +// mapped to a concrete diff position. +message ReviewInlineComment { + string path = 1; + int32 start_line = 2; + int32 end_line = 3; + string body = 4; + string suggestion = 5; +} + +// ReviewStats mirrors review.Stats — review execution metrics. by_severity +// is map keyed by the Severity numeric value (Go's +// map[contracts.Severity]int marshals to integer-string JSON keys, not +// severity names — see severity.proto's comment on Severity's numbering). +// duration_per_concern is int64 nanoseconds per key, matching Go's default +// JSON marshaling of time.Duration (a plain int64, not +// google.protobuf.Duration's "3.000001s"-style string). +message ReviewStats { + int32 files_reviewed = 1; + int32 hunks_analyzed = 2; + int32 findings_total = 3; + map by_severity = 4; + map by_concern = 5; + int32 tokens_used = 6; + map duration_per_concern = 7; + double average_confidence = 8; + int32 high_confidence_count = 9; + int32 low_confidence_count = 10; +} + +// ReviewConfidenceBreakdown mirrors review.ConfidenceBreakdown — review +// findings grouped by confidence band. +message ReviewConfidenceBreakdown { + repeated ReviewFinding high = 1; + repeated ReviewFinding medium = 2; + repeated ReviewFinding low = 3; +} + +// ReviewSASTFusionResult mirrors review.SASTFusionResult — tracks how the +// LLM handled SAST findings during a review. Only populated in Go when +// SAST-LLM fusion is active. +message ReviewSASTFusionResult { + repeated ReviewFinding confirmed = 1; + repeated ReviewFinding dismissed = 2; + repeated ReviewFinding unaddressed = 3; +} + +// ReviewResult mirrors review.Result — the neutral review result contract. +message ReviewResult { + repeated ReviewFinding findings = 1; + repeated ReviewInlineComment comments = 2; + ReviewStats stats = 3; + string report = 4; + Severity fail_on = 5; + // Optional in Go (*SASTFusionResult, omitempty). + ReviewSASTFusionResult sast_fusion = 6; + // Optional in Go (*ConfidenceBreakdown, omitempty). + ReviewConfidenceBreakdown confidence_breakdown = 7; +} diff --git a/proto/hawk/contracts/v1/sessions.proto b/proto/hawk/contracts/v1/sessions.proto new file mode 100644 index 0000000..53adad3 --- /dev/null +++ b/proto/hawk/contracts/v1/sessions.proto @@ -0,0 +1,69 @@ +syntax = "proto3"; + +package hawk.contracts.v1; + +import "google/protobuf/timestamp.proto"; + +option go_package = "github.com/GrayCodeAI/hawk-core-contracts/gen/go/hawk/contracts/v1;contractsv1"; + +// Phase mirrors sessions.Phase (sessions/sessions.go) — the pipeline stage +// that generated a token-usage event. Go represents this as a STRING enum +// (type Phase string) whose zero value ("") already means "unknown", so +// PHASE_UNSPECIFIED=0 matches Go's own PhaseUnknown semantics exactly — +// no numeric-parity conflict here the way Severity/Risk have, since Go's +// Phase was never int-backed to begin with. +enum Phase { + PHASE_UNSPECIFIED = 0; + PHASE_LOCALIZE = 1; + PHASE_REPAIR = 2; + PHASE_VALIDATE = 3; + PHASE_REVIEW = 4; + PHASE_PLANNING = 5; +} + +// ContextSnapshot mirrors sessions.ContextSnapshot — a point-in-time, +// compressed view of an agent's working memory. +message ContextSnapshot { + string session_id = 1; + Phase phase = 2; + int32 token_count = 3; + google.protobuf.Timestamp compressed_at = 4; + string summary = 5; + repeated string anchors = 6; +} + +// ToolCallRecord mirrors sessions.ToolCallRecord — a single tool +// invocation within a session, tagged with its pipeline phase. +message ToolCallRecord { + string session_id = 1; + Phase phase = 2; + string tool_name = 3; + int32 input_tokens = 4; + int32 output_tokens = 5; + int64 duration_ms = 6; + string error = 7; + google.protobuf.Timestamp timestamp = 8; +} + +// PhaseUsage mirrors sessions.PhaseUsage — token and cost usage for a +// single pipeline phase. +message PhaseUsage { + int32 input_tokens = 1; + int32 output_tokens = 2; + int32 total_tokens = 3; + double cost_usd = 4; +} + +// CostAccumulator mirrors sessions.CostAccumulator — token/cost tracking +// across all pipeline phases within a session. by_phase is +// map keyed by the Phase's actual string value (e.g. +// "localize") — unlike Severity (an int-kind Go type, see severity.proto), +// Phase's Kind is string, so Go's encoding/json already uses the phase +// name itself as the map key, not a numeric index. This is the one map in +// this schema that genuinely is string-keyed on the wire today. +message CostAccumulator { + string session_id = 1; + map by_phase = 2; + int32 total_tokens = 3; + double total_cost_usd = 4; +} diff --git a/proto/hawk/contracts/v1/severity.proto b/proto/hawk/contracts/v1/severity.proto new file mode 100644 index 0000000..06aa620 --- /dev/null +++ b/proto/hawk/contracts/v1/severity.proto @@ -0,0 +1,43 @@ +syntax = "proto3"; + +package hawk.contracts.v1; + +option go_package = "github.com/GrayCodeAI/hawk-core-contracts/gen/go/hawk/contracts/v1;contractsv1"; + +// Severity represents the impact level of a finding. Mirrors types.Severity +// (types/severity.go) exactly: this is an int-backed Go enum whose zero +// value ("info") is itself meaningful, not a sentinel — so unlike most +// proto3 enums, the first value here is SEVERITY_INFO, not an artificial +// _UNSPECIFIED, to keep the numeric values identical to the Go iota +// constants (Info=0 .. Critical=4). This matters because +// review.Stats.by_severity / verify.Stats.by_severity are +// map keyed by this exact numeric value, matching what Go's +// encoding/json already emits for a map keyed by a non-string int type. +enum Severity { + // buf:lint:ignore ENUM_ZERO_VALUE_SUFFIX + SEVERITY_INFO = 0; + SEVERITY_LOW = 1; + SEVERITY_MEDIUM = 2; + SEVERITY_HIGH = 3; + SEVERITY_CRITICAL = 4; +} + +// TokenSeverity mirrors types.TokenSeverity (compression/token error rule +// severity). Go represents this as a string enum with no defined zero +// value; proto3 requires one, so TOKEN_SEVERITY_UNSPECIFIED=0 is added. +enum TokenSeverity { + TOKEN_SEVERITY_UNSPECIFIED = 0; + TOKEN_SEVERITY_LOW = 1; + TOKEN_SEVERITY_MEDIUM = 2; + TOKEN_SEVERITY_HIGH = 3; + TOKEN_SEVERITY_CRITICAL = 4; +} + +// AuditSeverity mirrors types.AuditSeverity (security audit finding +// severity; Go values are uppercase strings CRITICAL/WARNING/INFO). +enum AuditSeverity { + AUDIT_SEVERITY_UNSPECIFIED = 0; + AUDIT_SEVERITY_INFO = 1; + AUDIT_SEVERITY_WARNING = 2; + AUDIT_SEVERITY_CRITICAL = 3; +} diff --git a/proto/hawk/contracts/v1/tool.proto b/proto/hawk/contracts/v1/tool.proto new file mode 100644 index 0000000..e3e7f63 --- /dev/null +++ b/proto/hawk/contracts/v1/tool.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; + +package hawk.contracts.v1; + +import "google/protobuf/struct.proto"; + +option go_package = "github.com/GrayCodeAI/hawk-core-contracts/gen/go/hawk/contracts/v1;contractsv1"; + +// ToolCall mirrors tools.ToolCall (tools/tool.go) — a provider-neutral +// tool invocation contract. arguments is arbitrary JSON in Go +// (map[string]interface{}); google.protobuf.Struct is the standard proto +// representation of an arbitrary JSON object. +message ToolCall { + string id = 1; + string name = 2; + google.protobuf.Struct arguments = 3; +} + +// ToolResult mirrors tools.ToolResult — a provider-neutral tool execution +// result contract. Field name is deliberately tool_use_id (not +// tool_call_id): hawk-sdk-go independently hand-rolled its own ToolResult +// with tool_call_id, which is exactly the kind of naming drift this schema +// (and buf breaking) exists to catch and prevent going forward — this +// proto is authoritative for the tool_use_id spelling. +message ToolResult { + string tool_use_id = 1; + string content = 2; + bool is_error = 3; +} diff --git a/proto/hawk/contracts/v1/verify.proto b/proto/hawk/contracts/v1/verify.proto new file mode 100644 index 0000000..fdcdf1a --- /dev/null +++ b/proto/hawk/contracts/v1/verify.proto @@ -0,0 +1,44 @@ +syntax = "proto3"; + +package hawk.contracts.v1; + +import "hawk/contracts/v1/severity.proto"; + +option go_package = "github.com/GrayCodeAI/hawk-core-contracts/gen/go/hawk/contracts/v1;contractsv1"; + +// VerifyFinding mirrors verify.Finding (verify/verify.go) — the neutral +// verification finding contract shared across Hawk and verification +// engines. See review.proto's ReviewFinding comment for why this is a +// separate message from Finding/ReviewFinding rather than a shared shape. +message VerifyFinding { + string check = 1; + Severity severity = 2; + string url = 3; + string element = 4; + string message = 5; + string fix = 6; + string evidence = 7; +} + +// VerifyStats mirrors verify.Stats — verification execution metrics. +// Same map-key and duration encoding rationale as review.proto's +// ReviewStats. +message VerifyStats { + int32 pages_scanned = 1; + int32 findings_total = 2; + map by_severity = 3; + map by_check = 4; + map duration_per_check = 5; +} + +// VerifyReport mirrors verify.Report — the neutral verification report +// contract. duration is int64 nanoseconds, matching Go's default JSON +// marshaling of time.Duration. +message VerifyReport { + string target = 1; + repeated VerifyFinding findings = 2; + VerifyStats stats = 3; + int32 crawled_urls = 4; + int64 duration = 5; + Severity fail_on = 6; +}