§3.8 second slice: real modes wired (matrix.relevance/downgrade/search,

distillation.score, drift.scorer)

Lands the workflow.Mode adapters for the §3.4 components + the
distillation scorer + drift quantifier. Workflows can now compose
real measurement capabilities; the substrate's parallel
capabilities become composable Lego bricks (per the prior commit's
closing insight).

Modes registered (in observerd's registerBuiltinModes):

  Pure-function wrappers (no I/O):
    - matrix.relevance    → matrix.FilterChunks
    - matrix.downgrade    → matrix.MaybeDowngrade
    - distillation.score  → distillation.ScoreRecord
    - drift.scorer        → drift.ComputeScorerDrift

  HTTP-backed:
    - matrix.search       → POST matrixd /matrix/search
                             (registered only when matrixd_url is set)

  Fixture (kept from §3.8 first slice):
    - fixture.echo, fixture.upper

internal/workflow/modes.go:
  Each mode follows the same glue pattern: marshal generic input
  through a typed struct (free schema validation + clear error
  messages), call the underlying capability, return a generic
  output map. Roundtrip-via-JSON gives us schema validation
  without writing custom field-by-field coercion.

internal/workflow/modes_test.go (10 tests, all PASS):
  - matrix.relevance filters adjacency pollution (Connector kept,
    catalogd::Registry dropped — same headline as the relevance
    smoke, run through the workflow mode)
  - matrix.downgrade flips lakehouse→isolation on strong model;
    keeps lakehouse on weak (qwen3.5:latest); errors on missing
    fields
  - distillation.score rates scrum_review attempt_1 as accepted;
    rejects empty record
  - drift.scorer reports zero drift on matched inputs; errors on
    empty inputs slice
  - matrix.search HTTP flow round-trips through httptest fake
    matrixd; non-OK status surfaces a clear error

scripts/workflow_smoke.sh (5 assertions PASS, was 4):
  New assertion #5: real-mode chain
    matrix.downgrade (lakehouse + grok-4.1-fast → isolation)
    → distillation.score (scrum_review attempt_1 → accepted)
  Proves §3.4 components compose through the workflow runner with
  no fixture intermediation. Both nodes ran successfully, runner
  recorded provenance, status=succeeded.

  Mode listing assertion now expects 7 modes (5 real + 2 fixture)
  instead of just the fixtures.

17-smoke regression all green. SPEC §3.8 acceptance gate G3.8.D
("Mode catalog dispatches matrix.search invocation to the matrixd
backend without going through HTTP") still pending — current path
goes through HTTP for matrix.search, which is the cleaner service-
mesh shape but slower than direct in-process. In-process dispatch
when matrixd is co-resident is a future optimization.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
root
2026-04-29 20:39:26 -05:00
co-authored by Claude Opus 4.7
parent e30da6e5aa
commit c7e3124208
4 changed files with 511 additions and 20 deletions
+49 -6
View File
@@ -72,14 +72,19 @@ poll_health 3110 || { echo "gateway failed"; tail /tmp/gateway.log; exit 1; }
FAILED=0
# ── 1. /observer/workflow/modes lists registered modes ────────────
echo "[workflow-smoke] /observer/workflow/modes lists fixture modes:"
echo "[workflow-smoke] /observer/workflow/modes lists fixtures + real modes:"
RESP="$(curl -sS http://127.0.0.1:3110/v1/observer/workflow/modes)"
HAS_ECHO="$(echo "$RESP" | jq -r '.modes | index("fixture.echo") != null')"
HAS_UPPER="$(echo "$RESP" | jq -r '.modes | index("fixture.upper") != null')"
if [ "$HAS_ECHO" = "true" ] && [ "$HAS_UPPER" = "true" ]; then
echo " ✓ fixture.echo + fixture.upper registered"
EXPECTED=("fixture.echo" "fixture.upper" "matrix.relevance" "matrix.downgrade" "distillation.score" "drift.scorer" "matrix.search")
MISSING=""
for m in "${EXPECTED[@]}"; do
if [ "$(echo "$RESP" | jq -r --arg m "$m" '.modes | index($m) != null')" != "true" ]; then
MISSING="$MISSING $m"
fi
done
if [ -z "$MISSING" ]; then
echo " ✓ all 7 expected modes registered (fixtures + 4 pure + matrix.search HTTP)"
else
echo " ✗ resp: $RESP"; FAILED=1
echo " ✗ missing modes:$MISSING"; FAILED=1
fi
# ── 2. 3-node DAG with $-ref substitution ─────────────────────────
@@ -141,6 +146,44 @@ else
echo " ✗ http=$HTTP err=$ERR"; FAILED=1
fi
# ── 5. Real-mode chain: matrix.downgrade → distillation.score ─────
# This proves the §3.4 components compose through the workflow runner.
# Two pure modes, no external service deps, deterministic input/output.
echo "[workflow-smoke] real-mode chain: downgrade → distillation.score"
REAL_WORKFLOW='{
"workflow": {
"name": "real-mode-chain",
"nodes": [
{"id":"gate", "mode":"matrix.downgrade",
"inputs":{"mode":"codereview_lakehouse", "model":"x-ai/grok-4.1-fast"}},
{"id":"score", "mode":"distillation.score",
"inputs":{"record":{
"run_id":"r-1", "task_id":"t-1",
"timestamp":"2026-04-29T12:00:00Z", "schema_version":1,
"provenance":{"source_file":"data/_kb/scrum_reviews.jsonl",
"sig_hash":"x", "recorded_at":"2026-04-29T12:00:01Z"},
"success_markers":["accepted_on_attempt_1"]
}}}
]
}
}'
RUN="$(curl -sS -X POST http://127.0.0.1:3110/v1/observer/workflow/run \
-H 'Content-Type: application/json' -d "$REAL_WORKFLOW")"
STATUS="$(echo "$RUN" | jq -r '.status')"
GATE_MODE="$(echo "$RUN" | jq -r '.nodes[0].output.mode')"
GATE_FROM="$(echo "$RUN" | jq -r '.nodes[0].output.downgraded_from')"
SCORE_CAT="$(echo "$RUN" | jq -r '.nodes[1].output.category')"
if [ "$STATUS" = "succeeded" ] \
&& [ "$GATE_MODE" = "codereview_isolation" ] \
&& [ "$GATE_FROM" = "codereview_lakehouse" ] \
&& [ "$SCORE_CAT" = "accepted" ]; then
echo " ✓ downgrade flipped lakehouse→isolation; scorer rated scrum_review attempt_1=accepted"
else
echo " ✗ status=$STATUS gate=$GATE_MODE from=$GATE_FROM score=$SCORE_CAT"
echo " full: $RUN"
FAILED=1
fi
if [ "$FAILED" -eq 0 ]; then
echo "[workflow-smoke] Workflow runner acceptance: PASSED"
exit 0