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
2 changes: 1 addition & 1 deletion go.work
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// run against workspace HEAD. Boatstack does not import Pitot yet — the workspace
// prepares the future boatstack -> pitot wiring and the wind-tunnel integration
// target. Released-dependency coherence is proved separately with GOWORK=off.
go 1.26
go 1.26.5

use (
./labs/12-product-engineering-loop/product-engineering-loop
Expand Down
12 changes: 6 additions & 6 deletions labs/15-pitot/pitot-distribution/UPSTREAM.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"files": {
"CONTRIBUTING.md": "0613b71aa497f8ca7d7296bf34ade87bfc7237a664d2e812d9b77b3b6befb0ad",
"README.md": "e54193fee53061889b3d5e82df7c76e72e440f265377d260b4a793475c216a7a",
"adapters/adapters.go": "80110df7289ea69f830b48667326ef297770c596e4ddbf1bc66201639f8288ad",
"adapters/adapters.go": "1b09f188ed9d3f8a1b159055556bd2a68153ad6ee9d59810b23c18a96a4a83bd",
"assets/pitot-boundary.png": "8a0ddb7d81831d94e14813f50ea4ca8670d77417f339ed2f91f0c653bf52f41d",
"assets/pitot-boundary.svg": "0c3871d70c84748573f231842091deb38a6def2862403ca34e8cc4493b9c9ebf",
"assets/pitot-hero.png": "a73532252b1e66c06273abbf5a4fe6261e98de3133b09e8d550edacfeeab92f8",
Expand All @@ -13,15 +13,15 @@
"bridge/bridge.go": "79ac2e025e16782f3c283b43cbea5b9ba4f837446583864df8f57d6346cae816",
"bridge/bridge_test.go": "23a19b7580d4b1e826224ec8322208ccca44ddd9a97b150efe15cafecc53e47f",
"cmd/generate-schema/main.go": "6e9d0030290d99e36967433f96e38385a122974f899ad9421aac1ef7e50d8fcb",
"cmd/pitot/main.go": "a8ef8a789abe13f41d8a1014de3fb4ad46522b558f0ace70786b904e81892b05",
"cmd/pitot/main_test.go": "ab2894327e7a5e72e9f3d83cae637e3ccb2e1ae05f14458be4fe9016149aeef7",
"cmd/pitot/main.go": "266cfdd1b758f2842a3cc232346e4ada4360077b1aa18c3a635697f56cab78a8",
"cmd/pitot/main_test.go": "7281ac0029d5e92c63c9feb1d2095aaef924aacf4e8a3db9af52872174d609ac",
"conformance/conformance.go": "43b692114f45c8b52958e34b35aee1cee339d8321c90f92ab4f5b963e79935bb",
"conformance/conformance_test.go": "83ab0bcc15371265a954d177e4e97d81ad3ea734bbf736a29a54628ef64b52cd",
"conformance/fixtures/negative.jsonl": "383dd001910699886bb1074d9225c91d8a6201e9fb3267d1a1f6e1c2753b0ba6",
"conformance/fixtures/positive.jsonl": "d3af0f2529dac9b33fa4900f5938e36fd0b17383088dd4f0de7e6eca269441d1",
"doc.go": "4dcd7a831a0a8ee6c3f6eeb8408c2e6898994509b11209564c0ed6b9a5218fce",
"doc.go": "9b51db0301aa428db731edcf8aa6b5a4fd71ae84f6a0d6cb2fc27656b04b1428",
"e2e/e2e_coverage_test.go": "8fac62d6d1c4ced359f8bb070379a88e77ded9912d867c642cda2d4703df23a7",
"e2e/e2e_hook_test.go": "a1c4601964eed674693306450249cf11a50978e5d2e00df152e33dc2c45ee1a0",
"e2e/e2e_hook_test.go": "cb9f27f4471c81c3320ea11be039a19514f5399b5ec5a80ae3b375dad8f8e8b5",
"examples/doc.go": "58f3f9eb7d272d7b6eecdb05f43e1613d5e3ef92d15d97c5440bd4b6990c26f9",
"examples/local-approval/main.go": "51386af324cd7d3bb07fe3ace53503884b02714b96b83073342fde81ce3b83a5",
"examples/token-meter/main.go": "4b1b9c1a43c3cf48b09dba6f607776caced9d2b5b562373496b31ed184582dd1",
Expand All @@ -42,7 +42,7 @@
"sensor/sensor.go": "939e146eaa51906972f98cf4615747eb75811b0c54d467a938825750336abeae",
"sensor/sensor_test.go": "ea9a58d45ad56d29214a40fb2cfa935e769f85334afcb1856a20fb938d486245",
"windtunnel/doc.go": "44e0bcde632da73e1f8b98beade3a34ca8e0d0ea79cdfb91d131de290b164fc4",
"windtunnel/windtunnel_test.go": "1c8b6c5d56ff5c4e66403baeae0d98108255d153c1275572b8c68e333e091493"
"windtunnel/windtunnel_test.go": "7e70ddf99411278175d2d2eb8ed73733367c77b3d04d55b4bf00738518673200"
},
"schema_version": 1
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### Add Gemini CLI as a supported host adapter

Pitot now supports the `gemini` host adapter. This integration natively parses Gemini CLI's `BeforeTool` synchronous hook events, allowing the Pitot sensor to safely monitor and project Gemini's shell command behavior along with Cursor, Claude, and Codex.
21 changes: 21 additions & 0 deletions labs/15-pitot/pitot/adapters/adapters.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const (
Cursor Host = "cursor"
Claude Host = "claude"
Codex Host = "codex"
Gemini Host = "gemini"
)

// AdapterVersion is the semantic version stamped onto normalized events so
Expand Down Expand Up @@ -122,6 +123,26 @@ var (
Controllable: []string{"PreToolUse"},
},
},
Gemini: {
MainEventName: "BeforeTool",
Parser: ParserConfig{
CanonicalEvent: []byte(`{"hook_event_name":"BeforeTool","tool_name":"run_shell_command","tool_input":{"command":"git status --short"}}`),
CommandFor: func(raw RawHookEvent) (string, bool) {
if raw.ToolInput == nil {
return "", false
}
value, present := raw.ToolInput["command"].(string)
return value, present && value != ""
},
ActionKinds: map[string]string{
"BeforeTool": "shell",
},
},
Partition: ControlPartition{
// In Gemini, the BeforeTool hook is synchronous (blocking).
Controllable: []string{"BeforeTool"},
},
},
}
)

Expand Down
4 changes: 2 additions & 2 deletions labs/15-pitot/pitot/cmd/pitot/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ func run(args []string, stdout, stderr io.Writer) error {
// from stdin, normalizes it, and exits with 0 (allow) or 2 (block/deny).
func runHook(args []string, stdout, stderr io.Writer) error {
if len(args) == 0 {
return fmt.Errorf("pitot: hook requires a host identifier (cursor, claude, codex)")
return fmt.Errorf("pitot: hook requires a host identifier (cursor, claude, codex, gemini)")
}
host := adapters.Host(args[0])
if host != adapters.Cursor && host != adapters.Claude && host != adapters.Codex {
if host != adapters.Cursor && host != adapters.Claude && host != adapters.Codex && host != adapters.Gemini {
return fmt.Errorf("pitot: unsupported hook host %q", host)
}

Expand Down
2 changes: 1 addition & 1 deletion labs/15-pitot/pitot/cmd/pitot/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func TestDoctorReportsBoundary(t *testing.T) {
t.Fatalf("doctor: %v", err)
}
out := stdout.String()
for _, want := range []string{"local boundary", "cursor", "claude", "codex", "decoder=PASS", "unauthenticated local socket: none"} {
for _, want := range []string{"local boundary", "cursor", "claude", "codex", "gemini", "decoder=PASS", "unauthenticated local socket: none"} {
if !strings.Contains(out, want) {
t.Errorf("doctor output missing %q\n%s", want, out)
}
Expand Down
2 changes: 1 addition & 1 deletion labs/15-pitot/pitot/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//
// schema/ public event and response types + versioned constants
// protocol/ newline-delimited JSON framing and state-machine helpers
// adapters/ Claude Code, Cursor, and Codex host boundaries
// adapters/ Claude Code, Cursor, Codex, and Gemini CLI host boundaries
// sensor/ normalization and observation pipeline (decoder)
// bridge/ controller routing and single-response transport
// projection/ full, sha256, and omit content policies
Expand Down
1 change: 1 addition & 0 deletions labs/15-pitot/pitot/e2e/e2e_hook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func TestE2ESensorsConformityAcrossAllAdapters(t *testing.T) {
adapters.Claude: `{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"git status --short"}}`,
adapters.Cursor: `{"hook_event_name":"beforeShellExecution","command":"git status --short"}`,
adapters.Codex: `{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"git status --short"}}`,
adapters.Gemini: `{"hook_event_name":"BeforeTool","tool_name":"run_shell_command","tool_input":{"command":"git status --short"}}`,
}

hosts := adapters.Supported()
Expand Down
1 change: 1 addition & 0 deletions labs/15-pitot/pitot/windtunnel/windtunnel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ var boatstackCanonicalEvents = map[adapters.Host]string{
adapters.Cursor: `{"hook_event_name":"beforeShellExecution","command":"git status --short"}`,
adapters.Claude: `{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"git status --short"}}`,
adapters.Codex: `{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"git status --short"}}`,
adapters.Gemini: `{"hook_event_name":"BeforeTool","tool_name":"run_shell_command","tool_input":{"command":"git status --short"}}`,
}

func TestSensorConsumesBoatstackCanonicalEvents(t *testing.T) {
Expand Down
2 changes: 2 additions & 0 deletions labs/15-pitot/tests/e2e_gemini_cli_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
exec labs/15-pitot/tests/e2e_unified_runner.sh "gemini"
5 changes: 5 additions & 0 deletions labs/20-deltawire/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/dist/
/.deltawire/
/testdata/generated/
/inspect.json
/inspect.md
11 changes: 11 additions & 0 deletions labs/20-deltawire/THIRD_PARTY_NOTICES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Third-Party Notices

DeltaWire uses the following third-party dependencies:

## github.com/santhosh-tekuri/jsonschema/v6

- License: Apache License 2.0 (or MIT based on repo)
- Version: v6.0.2
- Purpose: JSON Schema Draft 2020-12 validation

This dependency was chosen because it provides robust Draft 2020-12 support in Go and does not require CGO.
27 changes: 27 additions & 0 deletions labs/20-deltawire/assets/init/INSTRUCTIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# DeltaWire repository instructions

Use DeltaWire for repetitive generated test, fixture, benchmark, or evaluation
data.

Do not manually generate or edit a managed output when the data can be derived
from a DeltaWire plan.

Workflow:

1. Create or edit a `.deltawire/plans/*.dw.json` generation plan.
2. Keep the record schema under `.deltawire/schemas/`.
3. Run `deltawire validate <plan>`.
4. Run `deltawire inspect <plan> --format markdown` when preparing a coding plan.
5. Run `deltawire render <plan>`.
6. Run `deltawire check <plan>` before claiming completion.

Never use `deltawire render --force` without explicit human approval.

Do not claim model-token savings from byte-amplification measurements.

Generated data is complete only when:

- record-schema validation passes
- dataset assertions pass
- the output hash matches repository state
- `deltawire check` exits successfully
12 changes: 12 additions & 0 deletions labs/20-deltawire/assets/init/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": "deltawire.config.v1",
"plans_dir": ".deltawire/plans",
"schemas_dir": ".deltawire/schemas",
"state_file": ".deltawire/state.json",
"limits": {
"max_plan_bytes": 1048576,
"max_schema_bytes": 1048576,
"max_records": 100000,
"max_output_bytes": 104857600
}
}
15 changes: 15 additions & 0 deletions labs/20-deltawire/cmd/deltawire/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package main

import (
"fmt"
"os"

"deltawire/internal/cli"
)

func main() {
if err := cli.Execute(os.Args[1:]); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(cli.ExitCode(err))
}
}
10 changes: 10 additions & 0 deletions labs/20-deltawire/docs/00-grounding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Grounding Facts

| Claim | Evidence | Verification command |
| --- | --- | --- |
| Directory | `/Users/apple/Documents/GitHub/intelligence-flow/.product-loop/worktrees/revector/labs/20-deltawire` | `pwd` |
| Is Git Repo | Yes (part of intelligence-flow) | `git rev-parse --show-toplevel` |
| Git Branch | `lab/19-revector-v0` | `git branch --show-current` |
| Git Remote | `origin https://github.com/operatorstack/intelligence-flow.git` | `git remote -v` |
| Go Version | `go version go1.26.5 darwin/arm64` | `go version` |
| Go Stable | `go1.26.5` | `go env GOVERSION` |
46 changes: 46 additions & 0 deletions labs/20-deltawire/examples/auth-eval/auth-case.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "auth-case",
"type": "object",
"required": [
"id",
"suite",
"input",
"expected"
],
"properties": {
"id": {
"type": "string",
"minLength": 1
},
"suite": {
"const": "auth"
},
"input": {
"type": "object",
"required": [
"route"
],
"properties": {
"role": {
"type": [
"string",
"null"
]
},
"route": {
"type": "string",
"minLength": 1
}
},
"additionalProperties": false
},
"expected": {
"enum": [
"allow",
"deny"
]
}
},
"additionalProperties": false
}
117 changes: 117 additions & 0 deletions labs/20-deltawire/examples/auth-eval/auth-eval.dw.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
{
"version": "deltawire.plan.v1",
"id": "auth-eval",
"description": "Authorization evaluation cases",
"record_schema": ".deltawire/schemas/auth-case.schema.json",
"output": {
"path": "testdata/generated/auth-cases.ndjson",
"format": "ndjson",
"pretty": false
},
"defaults": {
"suite": "auth",
"expected": "deny"
},
"sets": {
"non_admin_roles": ["guest", "member"],
"routes": ["billing", "admin"]
},
"generators": [
{
"kind": "matrix",
"name": "non-admin routes",
"dimensions": [
{
"name": "role",
"set": "non_admin_roles"
},
{
"name": "route",
"set": "routes"
}
],
"record": {
"id": "auth/${role}/${route}",
"input": {
"role": "${role}",
"route": "/${route}"
}
}
},
{
"kind": "rows",
"name": "admin routes",
"columns": [
"/id",
"/input/role",
"/input/route",
"/expected"
],
"rows": [
[
"auth/admin/billing",
"admin",
"/billing",
"allow"
],
[
"auth/admin/admin",
"admin",
"/admin",
"allow"
]
]
},
{
"kind": "variants",
"name": "missing-role edge cases",
"base": {
"id": "auth/role/base",
"input": {
"role": "guest",
"route": "/admin"
}
},
"variants": [
{
"name": "empty",
"set": {
"/id": "auth/role/empty",
"/input/role": ""
}
},
{
"name": "null",
"set": {
"/id": "auth/role/null",
"/input/role": null
}
},
{
"name": "omitted",
"set": {
"/id": "auth/role/omitted"
},
"omit": [
"/input/role"
]
}
]
}
],
"assertions": {
"count": 9,
"unique": [
"/id"
],
"coverage": [
{
"path": "/expected",
"values": [
"allow",
"deny"
]
}
]
}
}
7 changes: 7 additions & 0 deletions labs/20-deltawire/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module deltawire

go 1.26.5

require github.com/santhosh-tekuri/jsonschema/v6 v6.0.2

require golang.org/x/text v0.14.0 // indirect
Loading
Loading