validatord: /v1/validate + /v1/iterate HTTP surface (port 3221)

Closes the last "Go primary" backlog item in
docs/ARCHITECTURE_COMPARISON.md. Go now owns the entire validator
path end-to-end — no Rust dep for staffing safety net.

Architecture: cmd/validatord on :3221 hosts both endpoints. Calls
chatd directly for the iterate loop's LLM hop (no gateway
self-loopback like the Rust shape). Gateway proxies /v1/validate +
/v1/iterate to validatord.

What's in:
- internal/validator/playbook.go — 3rd validator kind (PRD checks:
  fill: prefix, endorsed_names ≤ target_count×2, fingerprint required)
- internal/validator/lookup_jsonl.go — JSONL roster loader (Parquet
  deferred; producer one-liner documented in package comment)
- internal/validator/iterate.go — ExtractJSON helper + Iterate
  orchestrator with ChatCaller seam for unit tests
- cmd/validatord/main.go — HTTP routes, roster load, chat client
- internal/shared/config.go — ValidatordConfig + gateway URL field
- lakehouse.toml — [validatord] section
- cmd/gateway/main.go — proxy routes for /v1/validate + /v1/iterate

Smoke: 5/5 PASS through gateway :3110:
  ✓ playbook happy path
  ✓ playbook missing fingerprint → 422 schema/fingerprint
  ✓ phantom candidate W-PHANTOM → 422 consistency
  ✓ unknown kind → 400
  ✓ roster loaded with 3 records

go test ./... green across 33 packages.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
root
2026-05-02 03:53:20 -05:00
co-authored by Claude Opus 4.7
parent 09299a27b7
commit f9e72412c1
15 changed files with 1583 additions and 6 deletions
+33
View File
@@ -30,6 +30,7 @@ type Config struct {
Matrixd MatrixdConfig `toml:"matrixd"`
Observerd ObserverdConfig `toml:"observerd"`
Chatd ChatdConfig `toml:"chatd"`
Validatord ValidatordConfig `toml:"validatord"`
S3 S3Config `toml:"s3"`
Models ModelsConfig `toml:"models"`
Log LogConfig `toml:"log"`
@@ -70,6 +71,7 @@ type GatewayConfig struct {
MatrixdURL string `toml:"matrixd_url"`
ObserverdURL string `toml:"observerd_url"`
ChatdURL string `toml:"chatd_url"`
ValidatordURL string `toml:"validatord_url"`
}
// EmbeddConfig drives the embed service. ProviderURL points at the
@@ -143,6 +145,28 @@ type ChatdConfig struct {
TimeoutSecs int `toml:"timeout_secs"`
}
// ValidatordConfig drives the validator service (cmd/validatord).
// Hosts /validate (FillValidator + EmailValidator + PlaybookValidator)
// and /iterate (generate→validate→correct loop). Routes to chatd via
// ChatdURL for the iteration loop's LLM hops.
//
// RosterPath points at a JSONL roster (one WorkerRecord per line) that
// FillValidator and EmailValidator use for worker-existence checks.
// Empty disables the roster — worker-existence checks all fail
// Consistency, which is the correct behavior when the roster isn't
// configured. Production sets a stable path under /var/lib/lakehouse/.
type ValidatordConfig struct {
Bind string `toml:"bind"`
ChatdURL string `toml:"chatd_url"`
RosterPath string `toml:"roster_path"`
// Per-call cap on the iteration loop. 0 = 3 (Phase 43 default).
DefaultMaxIterations int `toml:"default_max_iterations"`
// Per-call cap on chat hop max_tokens. 0 = 4096.
DefaultMaxTokens int `toml:"default_max_tokens"`
// Per-call timeout for the chat hop in seconds. 0 = 240s.
ChatTimeoutSecs int `toml:"chat_timeout_secs"`
}
// ObserverdConfig drives the observer service (cmd/observerd).
// PersistPath: file path to the JSONL ops log; empty = in-memory
// only (test/dev). Production sets a stable path under
@@ -328,6 +352,7 @@ func DefaultConfig() Config {
MatrixdURL: "http://127.0.0.1:3218",
ObserverdURL: "http://127.0.0.1:3219",
ChatdURL: "http://127.0.0.1:3220",
ValidatordURL: "http://127.0.0.1:3221",
},
Storaged: ServiceConfig{Bind: "127.0.0.1:3211"},
Catalogd: CatalogConfig{Bind: "127.0.0.1:3212", StoragedURL: "http://127.0.0.1:3211"},
@@ -361,6 +386,14 @@ func DefaultConfig() Config {
Bind: "127.0.0.1:3219",
// PersistPath empty by default = in-memory only.
},
Validatord: ValidatordConfig{
Bind: "127.0.0.1:3221",
ChatdURL: "http://127.0.0.1:3220",
RosterPath: "", // empty = no roster; worker-existence checks fail Consistency
DefaultMaxIterations: 3,
DefaultMaxTokens: 4096,
ChatTimeoutSecs: 240,
},
Chatd: ChatdConfig{
Bind: "127.0.0.1:3220",
OllamaURL: "http://localhost:11434",