root
|
e30da6e5aa
|
§3.8 first slice: workflow runner skeleton + DAG executor + observerd integration
Lands the structural piece of SPEC §3.8 (Observer-KB workflow runner)
documented in 97dd3f8: types + DAG runner + reference substitution +
provenance recording into observerd. Real-mode integrations
(matrix.search, distillation.score, drift.scorer, llm.chat) come in
follow-up commits — this commit proves the mechanics.
internal/workflow/types.go:
- Workflow / Node / NodeResult / RunResult types matching Archon's
YAML shape so existing workflows (e.g. lakehouse-architect-review.yaml)
load directly. Optional `mode` field added — implicit fall-back is
"llm.chat" matching Archon's convention.
- Mode signature: func(Context, map[string]any) (map[string]any, error)
- 4 sentinel errors: ErrCycle, ErrMissingDep, ErrUnknownMode,
ErrDuplicateNodeID, ErrUnresolvedRef
- Validate enforces structural invariants: unique IDs, every
depends_on resolves, no cycles
internal/workflow/runner.go:
- Kahn's-algorithm topological sort, stable for declaration-order
ties (deterministic execution + JSON output across runs)
- Reference substitution: $node_id.output.key.path resolves through
nested maps; $node_id alone resolves to the whole output map
- Skip cascade: a node whose dependency failed/skipped is skipped
with explicit "upstream node X failed" error in NodeResult, never
silently dropped
- Per-node provenance: NodeResult.StartedAt + DurationMs captured
for every execution
- Mode pre-validation: every node's mode checked against registry
BEFORE any node runs — typo catches in 5ms not after 6 nodes
internal/workflow/runner_test.go (14 tests, all PASS):
- Validate: missing name, no nodes, duplicate IDs, missing deps, cycles
- Run: single node, 3-node DAG with chained $-refs (shape→weakness→improvement),
failed-node skip cascade with independent siblings still running,
unknown-mode abort, unresolved-reference error, implicit
llm.chat fallback, provenance fields populated, inputs (not just
prompt) honor $-refs, topological-sort stability for ties
cmd/observerd extended:
- POST /observer/workflow/run executes a workflow, records each
node's execution as an ObservedOp (source="workflow"), returns
the full RunResult
- GET /observer/workflow/modes lists the registered mode names
- registerBuiltinModes wires fixture.echo + fixture.upper for v0;
real modes register here in follow-up commits
scripts/workflow_smoke.sh (4 assertions PASS):
- GET /modes lists fixture.echo + fixture.upper
- 3-node DAG executes: shape (uppercase "hello world") → weakness
(sees "HELLO WORLD" via $shape.output.upper ref) → improvement
(sees "HELLO WORLD" propagated through 2-hop $weakness.output.prompt)
- /observer/stats shows by_source.workflow == 3 (one per node) and
total == 3 — provenance lands as expected
- Unknown mode → 400 with "unknown mode" in error body
17-smoke regression all green. Acceptance gates G3.8.A (Archon-shape
workflow loads + executes topologically) + G3.8.B (per-node ObservedOps)
+ G3.8.C ($prior_node.output ref resolves, error on missing ref) all
satisfied. G3.8.D (in-process matrix.search dispatch) deferred until
a real mode is wired.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
2026-04-29 20:34:30 -05:00 |
|
root
|
bc9ab93afe
|
H: observerd — autonomous-iteration witness loop (SPEC §2 port)
Port of the load-bearing pieces of mcp-server/observer.ts (Rust
system, 852 lines TS) per SPEC §2's named target. Implements PRD
loop 3 ("Observer loop — watches each run, refines configs").
Routes (all under /v1/observer/* via gateway):
GET /observer/health — liveness
GET /observer/stats — total / successes / failures /
by_source / recent_scenario_ops
(matches Rust JSON shape exactly)
POST /observer/event — record one ObservedOp; auto-defaults
timestamp + source, validates required
fields (endpoint), persists to JSONL,
appends to ring buffer
Architecture:
- internal/observer/types.go — ObservedOp model + Source taxonomy
(mcp / scenario / langfuse / overseer_correction). Mirrors the
Rust shape so JSON round-trips during cutover.
- internal/observer/store.go — Store + Persistor. Ring buffer cap
matches Rust's 2000; recent_scenarios cap matches Rust's 10.
Same persist-then-apply order as pathwayd; same corruption-
tolerant replay (skip malformed lines + warn).
- cmd/observerd — :3219 HTTP service, fronted by gateway as
/v1/observer/*.
- lakehouse.toml + DefaultConfig — [observerd] block matches the
pathwayd pattern (Bind + PersistPath; empty path = ephemeral).
Tests + smoke (all PASS):
- 7 unit tests in store_test.go: validation, default fields,
stats aggregation, recent-scenarios cap + ordering, ring-buffer
rollover at cap, JSONL round-trip persistence, corruption-
tolerant replay (1 valid + 1 corrupt + 1 valid → 2 applied)
- scripts/observer_smoke.sh: 4 assertions through gateway —
record 5 events (3 ok / 2 fail across 2 sources), stats
aggregates correctly, empty-endpoint→400, kill+restart preserves
via JSONL replay (5 ops, 3 ok, 2 err survive)
Deferred (named in package + cmd doc, not in this commit):
- POST /observer/review (cloud-LLM hand-review fall-back). The
heuristic-only path could land cheaply but the productized
cloud path (qwen3-coder fall-back) is multi-day port.
- Background loops: analyzeErrors, consolidatePlaybooks,
tailOverseerCorrections (read overseer_corrections.jsonl into
the ring buffer once per cycle).
- escalateFailureClusterToLLMTeam (failure clustering trigger
that posts to LLM Team's /api/run with code_review mode).
/relevance is NOT duplicated — already ported in 9588bd8 to
internal/matrix/relevance.go (component 3 of SPEC §3.4).
16-smoke regression all green (D1-D6, G1, G1P, G2, storaged_cap,
pathway, matrix, relevance, downgrade, playbook, observer).
13 binaries now: gateway, storaged, catalogd, ingestd, queryd,
vectord, embedd, pathwayd, matrixd, observerd, mcpd, fake_ollama
(plus catalogd-only test build).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
2026-04-29 20:18:02 -05:00 |
|