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
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE/bug.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ body:
id: gait_version
attributes:
label: Gait version
description: Output of `gait --version`
placeholder: gait version 1.0.0
description: Output of `gait version --json` (authoritative install probe)
placeholder: '{"ok":true,"version":"1.3.6"}'
validations:
required: true
- type: input
Expand Down
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE/question.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ body:
id: gait_version
attributes:
label: Gait version
description: Output of `gait --version` if installed.
placeholder: gait version 1.0.0
description: Output of `gait version --json` if installed.
placeholder: '{"ok":true,"version":"1.3.6"}'
validations:
required: false
- type: textarea
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ perf/ui_budget_report.json
/fixtures/
/.uat_local/
/.tmp/
/.cache/
docs-site/node_modules/
docs-site/.next/
docs-site/out/
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

- Gate intent normalization now treats omitted target `discovery_method` as `unknown` instead of empty so policies can deterministically match unknown/dynamic discovery paths.
- Durable job resume now preserves the originally bound identity and rejects attempts to resume with a different identity.
- `gait version --json` now prefers clean release metadata for promoted install paths while keeping repo-local contributor builds on the explicit `0.0.0-dev` fallback.
- Public onboarding/docs copy now treats LangChain as the official middleware lane and the OpenAI example as a reference boundary demo.

### Upgrade Notes

Expand Down
15 changes: 11 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ BENCH_BASELINE ?= perf/bench_baseline.json

.PHONY: fmt lint lint-fast codeql test test-fast test-scenarios prepush prepush-full github-guardrails github-guardrails-strict test-hardening test-hardening-acceptance test-chaos test-e2e test-acceptance test-v1-6-acceptance test-v1-7-acceptance test-v1-8-acceptance test-v2-3-acceptance test-v2-4-acceptance test-v2-5-acceptance test-v2-6-acceptance test-voice-acceptance test-context-conformance test-context-chaos test-packspec-tck test-script-intent-acceptance test-ui-acceptance test-ui-unit test-ui-e2e-smoke test-ui-perf test-claude-code-hook-contract test-adoption test-adapter-parity test-ecosystem-automation test-release-smoke test-install test-install-path-versions test-contracts test-intent-receipt-conformance test-ci-regress-template test-ci-portability-templates test-live-connectors test-skill-supply-chain test-runtime-slo test-ent-consumer-contract test-uat-local test-openclaw-skill-install test-beads-bridge test-docs-storyline test-docs-consistency test-demo-recording test-github-action-runtime-guard openclaw-skill-install build bench bench-check bench-budgets context-budgets skills-validate ecosystem-validate ecosystem-release-notes demo-90s demo-hero-gif homebrew-formula wiki-publish tool-allowlist-policy ui-build ui-sync ui-deps-check
.PHONY: hooks
.PHONY: docs-site-install docs-site-build docs-site-lint docs-site-check
.PHONY: docs-site-install docs-site-build docs-site-lint docs-site-check docs-site-validate

fmt:
gofmt -w .
Expand Down Expand Up @@ -306,17 +306,24 @@ openclaw-skill-install:
@if [ -z "$(TARGET_DIR)" ]; then bash scripts/install_openclaw_skill.sh; else bash scripts/install_openclaw_skill.sh --target-dir "$(TARGET_DIR)"; fi

docs-site-install:
cd docs-site && npm ci
mkdir -p .cache/npm
cd docs-site && NPM_CONFIG_CACHE=../.cache/npm npm ci

docs-site-build:
docs-site-build: docs-site-install
cd docs-site && npm run build

docs-site-lint:
docs-site-lint: docs-site-install
cd docs-site && npm run lint

docs-site-check:
$(PYTHON) scripts/check_docs_site_validation.py --report gait-out/docs_site_validation_report.json

docs-site-validate:
mkdir -p .cache/npm
cd docs-site && NPM_CONFIG_CACHE=../.cache/npm npm ci && npm run lint && npm run build
$(PYTHON) scripts/check_docs_site_validation.py --report gait-out/docs_site_validation_report.json
rm -rf docs-site/node_modules

ui-build:
bash scripts/ui_build.sh

Expand Down
26 changes: 14 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ Managed/preloaded agent note: if you cannot intercept tool execution before side

## Install

Choose one install path:
Choose one install path. Prefer release binaries for onboarding and support flows, then confirm the installed version with `gait version --json`.

### Release Installer

```bash
curl -fsSL https://raw.githubusercontent.com/Clyra-AI/gait/main/scripts/install.sh | bash
```

### Homebrew

Expand All @@ -30,11 +36,7 @@ brew install Clyra-AI/tap/gait
go install github.com/Clyra-AI/gait/cmd/gait@latest
```

### Release Installer

```bash
curl -fsSL https://raw.githubusercontent.com/Clyra-AI/gait/main/scripts/install.sh | bash
```
Tagged module installs resolve the release version from Go build metadata. Local checkout builds remain contributor/dev builds and may still report `0.0.0-dev`.

## Start Here

Expand Down Expand Up @@ -73,10 +75,10 @@ This path writes `.gait.yaml`, reports the live policy contract, and returns ins

Use this when your agent already makes real tool calls and you want enforcement at the execution seam.

Official lanes:
Official and reference lanes:

- OpenAI Agents wrapper lane: [`examples/integrations/openai_agents/`](examples/integrations/openai_agents/)
- LangChain middleware lane: [`examples/integrations/langchain/`](examples/integrations/langchain/)
- LangChain middleware lane (official): [`examples/integrations/langchain/`](examples/integrations/langchain/)
- OpenAI-style reference boundary demo: [`examples/integrations/openai_agents/`](examples/integrations/openai_agents/)

Other supported boundary paths:

Expand Down Expand Up @@ -131,9 +133,9 @@ This is the core contract across wrappers, middleware, sidecars, and MCP boundar

## Runtime Integration Paths

### OpenAI Agents
### OpenAI Agents Reference Demo

This is the blessed top-of-funnel runtime lane: a local wrapper at the tool boundary with deterministic allow, block, and approval quickstarts.
This is the fastest in-repo reference demo for the runtime boundary contract: a local wrapper at the tool boundary with deterministic allow, block, and approval quickstarts. It is not a package-backed official SDK lane.

```bash
python3 examples/integrations/openai_agents/quickstart.py --scenario allow
Expand Down Expand Up @@ -174,7 +176,7 @@ gait enforce --json -- <child command...>

## Simple End-To-End Scenario

See [`docs/scenarios/simple_agent_tool_boundary.md`](docs/scenarios/simple_agent_tool_boundary.md) and the promoted wrapper quickstart at `examples/integrations/openai_agents/quickstart.py`.
See [`docs/scenarios/simple_agent_tool_boundary.md`](docs/scenarios/simple_agent_tool_boundary.md) and the reference wrapper quickstart at `examples/integrations/openai_agents/quickstart.py`.

## Policy Onboarding

Expand Down
2 changes: 1 addition & 1 deletion cmd/gait/approve.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func runApprove(arguments []string) int {
}

result, err := gate.MintApprovalToken(gate.MintApprovalTokenOptions{
ProducerVersion: version,
ProducerVersion: currentVersion(),
ApproverIdentity: approver,
ReasonCode: reasonCode,
IntentDigest: intentDigest,
Expand Down
2 changes: 1 addition & 1 deletion cmd/gait/approve_script.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func runApproveScript(arguments []string) int {
SchemaID: "gait.gate.approved_script_entry",
SchemaVersion: "1.0.0",
CreatedAt: nowUTC,
ProducerVersion: version,
ProducerVersion: currentVersion(),
PatternID: strings.TrimSpace(patternID),
PolicyDigest: policyDigest,
ScriptHash: scriptHash,
Expand Down
2 changes: 1 addition & 1 deletion cmd/gait/delegate.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func runDelegateMint(arguments []string) int {
}

result, err := gate.MintDelegationToken(gate.MintDelegationTokenOptions{
ProducerVersion: version,
ProducerVersion: currentVersion(),
DelegatorIdentity: delegator,
DelegateIdentity: delegate,
Scope: scopeValues,
Expand Down
10 changes: 5 additions & 5 deletions cmd/gait/demo.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ func executeDurableDemo() (demoOutput, int) {
baseNow := time.Date(2026, time.February, 14, 0, 0, 0, 0, time.UTC)
if _, err := jobruntime.Submit(jobRoot, jobruntime.SubmitOptions{
JobID: demoDurableJobID,
ProducerVersion: version,
ProducerVersion: currentVersion(),
EnvironmentFingerprint: "envfp:demo-durable",
Actor: "demo.user",
Now: baseNow,
Expand Down Expand Up @@ -301,7 +301,7 @@ func executeDurableDemo() (demoOutput, int) {
if err := os.Remove(packPath); err != nil && !errors.Is(err, os.ErrNotExist) {
return demoOutput{OK: false, Mode: string(demoModeDurable), Error: err.Error()}, exitCodeForError(err, exitInvalidInput)
}
if _, err := pack.BuildJobPackFromPath(jobRoot, demoDurableJobID, packPath, version, nil); err != nil {
if _, err := pack.BuildJobPackFromPath(jobRoot, demoDurableJobID, packPath, currentVersion(), nil); err != nil {
return demoOutput{OK: false, Mode: string(demoModeDurable), Error: err.Error()}, exitCodeForError(err, exitInvalidInput)
}
verifyResult, err := pack.Verify(packPath, pack.VerifyOptions{})
Expand Down Expand Up @@ -365,7 +365,7 @@ func executePolicyDemo() (demoOutput, int) {
SchemaID: "gait.gate.intent_request",
SchemaVersion: "1.0.0",
CreatedAt: time.Date(2026, time.February, 14, 0, 0, 0, 0, time.UTC),
ProducerVersion: version,
ProducerVersion: currentVersion(),
ToolName: "tool.delete",
Args: map[string]any{"path": "/tmp/demo/delete-me.txt"},
Targets: []schemagate.IntentTarget{{
Expand All @@ -390,7 +390,7 @@ func executePolicyDemo() (demoOutput, int) {
return demoOutput{OK: false, Mode: string(demoModePolicy), Error: err.Error()}, exitCodeForError(err, exitInvalidInput)
}

evalResult, err := gatecore.EvaluatePolicyDetailed(policy, intent, gatecore.EvalOptions{ProducerVersion: version})
evalResult, err := gatecore.EvaluatePolicyDetailed(policy, intent, gatecore.EvalOptions{ProducerVersion: currentVersion()})
if err != nil {
return demoOutput{OK: false, Mode: string(demoModePolicy), Error: err.Error()}, exitCodeForError(err, exitInvalidInput)
}
Expand Down Expand Up @@ -444,7 +444,7 @@ func buildDemoRunpack() (schemarunpack.Run, []schemarunpack.IntentRecord, []sche
SchemaID: "gait.runpack.run",
SchemaVersion: "1.0.0",
CreatedAt: ts,
ProducerVersion: "0.0.0-dev",
ProducerVersion: currentVersion(),
RunID: demoRunID,
Env: schemarunpack.RunEnv{
OS: "demo",
Expand Down
4 changes: 2 additions & 2 deletions cmd/gait/doctor.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func runDoctor(arguments []string) int {
result := doctor.Run(doctor.Options{
WorkDir: workDir,
OutputDir: outputDir,
ProducerVersion: version,
ProducerVersion: currentVersion(),
KeyMode: sign.KeyMode(strings.ToLower(strings.TrimSpace(keyMode))),
KeyConfig: sign.KeyConfig{
PrivateKeyPath: privateKeyPath,
Expand Down Expand Up @@ -151,7 +151,7 @@ func runDoctorAdoption(arguments []string) int {
if err != nil {
return writeDoctorAdoptionOutput(jsonOutput, doctorAdoptionOutput{OK: false, Error: err.Error()}, exitCodeForError(err, exitInvalidInput))
}
report := scout.BuildAdoptionReport(events, fromPath, version, time.Time{})
report := scout.BuildAdoptionReport(events, fromPath, currentVersion(), time.Time{})
return writeDoctorAdoptionOutput(jsonOutput, doctorAdoptionOutput{
OK: true,
Report: &report,
Expand Down
12 changes: 6 additions & 6 deletions cmd/gait/gate.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ func runGateEval(arguments []string) int {
}
startupWarnings = append(startupWarnings, "approved script fast-path evaluation failed; continuing with policy evaluation")
} else if match.Matched {
preApprovedOutcome, preApproveErr := buildPreApprovedOutcome(intent, version, match)
preApprovedOutcome, preApproveErr := buildPreApprovedOutcome(intent, currentVersion(), match)
if preApproveErr != nil {
return writeGateEvalOutput(jsonOutput, gateEvalOutput{OK: false, Error: preApproveErr.Error()}, exitCodeForError(preApproveErr, exitInvalidInput))
}
Expand All @@ -407,7 +407,7 @@ func runGateEval(arguments []string) int {
}
if !preApprovedFastPath {
outcome, err = gate.EvaluatePolicyDetailed(policy, intent, gate.EvalOptions{
ProducerVersion: version,
ProducerVersion: currentVersion(),
WrkrInventory: wrkrInventory,
WrkrSource: wrkrSource,
VerifiedContextEnvelope: verifiedContextEnvelope,
Expand Down Expand Up @@ -716,7 +716,7 @@ func runGateEval(arguments []string) int {
exitCode = gateEvalExitCodeForVerdict(result.Verdict, exitCode)

traceResult, err := gate.EmitSignedTrace(policy, preparedIntent, result, gate.EmitTraceOptions{
ProducerVersion: version,
ProducerVersion: currentVersion(),
CorrelationID: currentCorrelationID(),
ApprovalTokenRef: resolvedApprovalRef,
DelegationTokenRef: resolvedDelegationRef,
Expand Down Expand Up @@ -745,7 +745,7 @@ func runGateEval(arguments []string) int {
}
audit := gate.BuildApprovalAuditRecord(gate.BuildApprovalAuditOptions{
CreatedAt: result.CreatedAt,
ProducerVersion: version,
ProducerVersion: currentVersion(),
TraceID: traceResult.Trace.TraceID,
ToolName: traceResult.Trace.ToolName,
IntentDigest: traceResult.IntentDigest,
Expand All @@ -765,7 +765,7 @@ func runGateEval(arguments []string) int {
}
credentialRecord := gate.BuildBrokerCredentialRecord(gate.BuildBrokerCredentialRecordOptions{
CreatedAt: result.CreatedAt,
ProducerVersion: version,
ProducerVersion: currentVersion(),
TraceID: traceResult.Trace.TraceID,
ToolName: traceResult.Trace.ToolName,
Identity: preparedIntent.Context.Identity,
Expand All @@ -788,7 +788,7 @@ func runGateEval(arguments []string) int {
}
audit := gate.BuildDelegationAuditRecord(gate.BuildDelegationAuditOptions{
CreatedAt: result.CreatedAt,
ProducerVersion: version,
ProducerVersion: currentVersion(),
TraceID: traceResult.Trace.TraceID,
ToolName: traceResult.Trace.ToolName,
IntentDigest: traceResult.IntentDigest,
Expand Down
2 changes: 1 addition & 1 deletion cmd/gait/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func runGatewayIngest(arguments []string) int {
Source: strings.TrimSpace(source),
LogPath: strings.TrimSpace(logPath),
OutputPath: strings.TrimSpace(proofOut),
ProducerVersion: version,
ProducerVersion: currentVersion(),
SigningPrivateKey: keyPair.Private,
})
if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions cmd/gait/guard.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ func runGuardPack(arguments []string) int {
TemplateID: templateID,
RenderPDF: renderPDF,
AutoDiscoverV12: true,
ProducerVersion: version,
ProducerVersion: currentVersion(),
SignKey: keyPair.Private,
})
if err != nil {
Expand Down Expand Up @@ -373,7 +373,7 @@ func runGuardRetain(arguments []string) int {
PackTTL: parsedPackTTL,
DryRun: dryRun,
ReportOutput: reportPath,
ProducerVersion: version,
ProducerVersion: currentVersion(),
})
if err != nil {
return writeGuardRetainOutput(jsonOutput, guardRetainOutput{OK: false, Error: err.Error()}, exitCodeForError(err, exitInvalidInput))
Expand Down Expand Up @@ -440,7 +440,7 @@ func runGuardEncrypt(arguments []string) int {
KeyEnv: keyEnv,
KeyCommand: keyCommand,
KeyCommandArgs: parseCSVList(keyCommandArgs),
ProducerVersion: version,
ProducerVersion: currentVersion(),
})
if err != nil {
return writeGuardEncryptOutput(jsonOutput, guardEncryptOutput{OK: false, Error: err.Error()}, exitCodeForError(err, exitInvalidInput))
Expand Down
2 changes: 1 addition & 1 deletion cmd/gait/incident.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func runIncidentPack(arguments []string) int {
Window: parsedWindow,
TemplateID: templateID,
RenderPDF: renderPDF,
ProducerVersion: version,
ProducerVersion: currentVersion(),
})
if err != nil {
return writeIncidentPackOutput(jsonOutput, incidentPackOutput{OK: false, Error: err.Error()}, exitCodeForError(err, exitInvalidInput))
Expand Down
2 changes: 1 addition & 1 deletion cmd/gait/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func runJobSubmit(arguments []string) int {
JobID: strings.TrimSpace(jobID),
Actor: strings.TrimSpace(actor),
Identity: strings.TrimSpace(identity),
ProducerVersion: version,
ProducerVersion: currentVersion(),
EnvironmentFingerprint: jobruntime.EnvironmentFingerprint(envFingerprint),
PolicyDigest: resolvedPolicyDigest,
PolicyRef: resolvedPolicyRef,
Expand Down
14 changes: 7 additions & 7 deletions cmd/gait/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func run(arguments []string) int {

func runDispatch(arguments []string) int {
if len(arguments) < 2 {
fmt.Println("gait", version)
fmt.Println("gait", currentVersion())
return exitOK
}
if isTopLevelHelp(arguments[1]) {
Expand Down Expand Up @@ -159,10 +159,10 @@ func runVersion(arguments []string) int {
if hasJSONFlag(arguments) {
return writeJSONOutput(versionOutput{
OK: true,
Version: version,
Version: currentVersion(),
}, exitOK)
}
fmt.Println("gait", version)
fmt.Println("gait", currentVersion())
return exitOK
}

Expand Down Expand Up @@ -198,7 +198,7 @@ func writeAdoptionEvent(command string, exitCode int, elapsed time.Duration, now
return
}
workflowID := strings.TrimSpace(os.Getenv("GAIT_ADOPTION_WORKFLOW"))
event := scout.NewAdoptionEvent(command, exitCode, elapsed, version, now, workflowID)
event := scout.NewAdoptionEvent(command, exitCode, elapsed, currentVersion(), now, workflowID)
recordTelemetryWriteOutcome("adoption", scout.AppendAdoptionEvent(adoptionPath, event))
}

Expand All @@ -207,7 +207,7 @@ func writeOperationalEventStart(command string, correlationID string, now time.T
if operationalPath == "" {
return
}
event := scout.NewOperationalStartEvent(command, correlationID, version, now)
event := scout.NewOperationalStartEvent(command, correlationID, currentVersion(), now)
recordTelemetryWriteOutcome("operational_start", scout.AppendOperationalEvent(operationalPath, event))
}

Expand All @@ -223,7 +223,7 @@ func writeOperationalEventEnd(command string, correlationID string, exitCode int
category = string(resolvedCategory)
retryable = defaultRetryable(resolvedCategory)
}
event := scout.NewOperationalEndEvent(command, correlationID, version, exitCode, category, retryable, elapsed, now)
event := scout.NewOperationalEndEvent(command, correlationID, currentVersion(), exitCode, category, retryable, elapsed, now)
recordTelemetryWriteOutcome("operational_end", scout.AppendOperationalEvent(operationalPath, event))
}

Expand Down Expand Up @@ -255,7 +255,7 @@ func recordTelemetryWriteOutcome(stream string, err error) {
SchemaID: "gait.scout.telemetry_health",
SchemaVersion: "1.0.0",
CreatedAt: time.Now().UTC().Format(time.RFC3339Nano),
ProducerVersion: version,
ProducerVersion: currentVersion(),
Streams: make(map[string]telemetryStreamHealth, len(telemetryState.streams)),
}
for key, value := range telemetryState.streams {
Expand Down
Loading
Loading