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
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ health.json

# Go modules
vendor/
go.sum

# Python
__pycache__/
Expand Down
45 changes: 45 additions & 0 deletions contracts/howl/SOURCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Vendored `howl.*` contract schemas

The five `.schema.json` files in this directory are vendored, byte-for-byte
copies of the generated JSON Schema published by HowlDream. They are the
authoritative shape of the `howl.*` ecosystem envelopes this repo consumes
and produces (`howl.candidate/v1` in, `howl.assessment/v1` out).

**Source repository:** https://github.com/howlcipher/howldream
**Pinned commit:** `bd10b18b7182e5216b494d728d015dbbba9b32d0`
**Source path:** `schemas/*.schema.json`
**Vendored:** 2026-09-12

## Why vendored, not fetched at build/runtime

See `howldream`'s `schemas/README.md` for the full comparison of distribution
models. Summary: all producing/consuming repositories are owned by the same
org with full git access, so a pinned, vendored copy (re-vendored as a
deliberate, visible diff) is the simplest architecture justified by current
scale — no live network fetch, no new shared package/repo.

## How to re-vendor

```sh
cp /path/to/howldream/schemas/howl.*.schema.json contracts/howl/
```

Then update the pinned commit above to the exact `howldream` commit the
copied files came from. Re-vendoring is a deliberate act, not automatic —
bumping the pin should be its own visible diff, so a schema change is never
silently absorbed.

## What this proves, and what it doesn't

`contract_test.go` in this package validates real envelope fixtures against
these vendored schemas using a pure-Go JSON Schema validator
(`github.com/santhosh-tekuri/jsonschema/v5`) — **no Python, no `howldream`
import, no network access at test time.** This is the cross-language contract
test referenced in `howldream/issues.md` item 1: proof that the schema is
useful to a consumer that has never seen HowlDream's Python implementation.

As documented in `howldream`'s `AUTHORITY_INVARIANT.md`, schema validation
proves an envelope is *shaped* correctly — it does not prove the envelope's
claims are true. HowlFrame's own `apps/candidate_evaluator` is the runtime
control that independently evaluates candidate claims; it does not trust a
candidate's self-reported `trust`/`status`/`disposition` fields.
151 changes: 151 additions & 0 deletions contracts/howl/contract_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// Cross-language contract test: proves the vendored howl.* JSON Schema is
// genuinely useful to a consumer that has never seen HowlDream's Python
// implementation. This test imports no Python, no `howldream` package, and
// makes no network calls — only the local vendored schema files and this
// pure-Go JSON Schema validator.
package howl

import (
"encoding/json"
"testing"

"github.com/santhosh-tekuri/jsonschema/v5"
)

// validExplorationResult is a representative, schema-conformant
// howl.exploration_result/v1 envelope, including a nested candidate.
const validExplorationResult = `{
"schema_version": "howl.exploration_result/v1",
"exploration_id": "exp-go-001",
"parent_request_id": "req-go-001",
"objective": "Explore alternative diagnostic strategies",
"originating_component": "howlplane",
"authority": {"type": "ADVISORY", "executable": false},
"candidates": [
{
"schema_version": "howl.candidate/v1",
"candidate_id": "cand-go-001",
"source_run_id": "run-go-001",
"parent_request_id": "req-go-001",
"objective": "Explore alternative diagnostic strategies",
"text": "IDEA: add a bounded retry with jitter",
"trust": "UNVERIFIED",
"status": "GENERATED",
"authority": {"type": "ADVISORY", "executable": false},
"provenance": {"run_id": "run-go-001", "producer_component": "howldream"}
}
],
"verification_status": "UNVERIFIED",
"recommended_disposition": "DEFER",
"provenance": {"run_id": "run-go-001", "producer_component": "howldream"}
}`

func compileExplorationResult(t *testing.T) *jsonschema.Schema {
t.Helper()
schema, err := Compile(ExplorationResult)
if err != nil {
t.Fatalf("compile %s: %v", ExplorationResult, err)
}
return schema
}

// mutate returns a deep copy of validExplorationResult with the given
// mutator applied to its decoded form, re-encoded to JSON.
func mutate(t *testing.T, mutator func(env map[string]interface{})) []byte {
t.Helper()
var env map[string]interface{}
if err := json.Unmarshal([]byte(validExplorationResult), &env); err != nil {
t.Fatalf("unmarshal fixture: %v", err)
}
mutator(env)
out, err := json.Marshal(env)
if err != nil {
t.Fatalf("marshal mutated fixture: %v", err)
}
return out
}

func TestValidEnvelopeValidates(t *testing.T) {
schema := compileExplorationResult(t)
if err := ValidateJSON(schema, []byte(validExplorationResult)); err != nil {
t.Fatalf("expected valid envelope to validate, got: %v", err)
}
}

func TestMissingRequiredFieldFails(t *testing.T) {
schema := compileExplorationResult(t)
bad := mutate(t, func(env map[string]interface{}) {
delete(env, "objective")
})
if err := ValidateJSON(schema, bad); err == nil {
t.Fatal("expected missing required field 'objective' to fail validation")
}
}

func TestUnsupportedSchemaVersionFails(t *testing.T) {
schema := compileExplorationResult(t)
bad := mutate(t, func(env map[string]interface{}) {
env["schema_version"] = "howl.bogus/v99"
})
if err := ValidateJSON(schema, bad); err == nil {
t.Fatal("expected forged schema_version to fail validation")
}
}

func TestInvalidEnumFails(t *testing.T) {
schema := compileExplorationResult(t)
bad := mutate(t, func(env map[string]interface{}) {
env["verification_status"] = "VERIFIED"
})
if err := ValidateJSON(schema, bad); err == nil {
t.Fatal("expected out-of-enum verification_status to fail validation")
}
}

func TestForgedAuthorityExecutableFails(t *testing.T) {
schema := compileExplorationResult(t)
bad := mutate(t, func(env map[string]interface{}) {
env["authority"] = map[string]interface{}{"type": "ADVISORY", "executable": true}
})
if err := ValidateJSON(schema, bad); err == nil {
t.Fatal("expected authority.executable=true to fail validation")
}
}

func TestInjectedPrivilegedFieldFails(t *testing.T) {
schema := compileExplorationResult(t)
for _, field := range []string{"executor", "execution_capability", "approved", "bypass_review"} {
field := field
t.Run(field, func(t *testing.T) {
bad := mutate(t, func(env map[string]interface{}) {
env[field] = true
})
if err := ValidateJSON(schema, bad); err == nil {
t.Fatalf("expected injected privileged field %q to fail validation (additionalProperties: false)", field)
}
})
}
}

func TestNestedCandidateForgedTrustFails(t *testing.T) {
schema := compileExplorationResult(t)
bad := mutate(t, func(env map[string]interface{}) {
candidates := env["candidates"].([]interface{})
cand := candidates[0].(map[string]interface{})
cand["trust"] = "VERIFIED"
})
if err := ValidateJSON(schema, bad); err == nil {
t.Fatal("expected nested candidate with forged trust='VERIFIED' to fail validation")
}
}

func TestAllFiveVendoredSchemasCompile(t *testing.T) {
for _, name := range []string{Exploration, Candidate, Assessment, DevelopmentResult, ExplorationResult} {
name := name
t.Run(name, func(t *testing.T) {
if _, err := Compile(name); err != nil {
t.Fatalf("expected vendored schema %s to compile, got: %v", name, err)
}
})
}
}
Loading
Loading