gauntlet 2026-05-02: smoke chain + per-component scrum + parity probe
Production-readiness gauntlet exploiting the dual Rust/Go
implementation as a measurement instrument.
## Phase 1 — Full smoke chain
21/21 PASS in ~60s. Substrate intact across the full service surface.
## Phase 2 — Per-component scrum (token-volume fix)
Prior wave (165KB diff): Kimi 62 tokens out, Qwen 297 → no useful
analysis. This wave splits today's commits into 4 focused bundles
(36-71KB each):
c1 validatord (46KB) → 0 convergent / 11 distinct
c2 vectord substrate (36KB) → 0 convergent / 10 distinct
c3 materializer (71KB) → 0 convergent / 6 distinct (Opus emitted
a BLOCK then self-retracted in same response)
c4 replay (45KB) → 0 convergent / 10 distinct
Reviewer engagement vs prior wave: Kimi went 62 → ~250 tokens out
once bundles dropped below 60KB.
scripts/scrum_review.sh hardening:
* Diff-size guard (warn >60KB, hard-fail >100KB,
SCRUM_FORCE_OVERSIZE=1 override)
* Tightened prompt — file path must appear EXACTLY as in diff
so post-processor can grep WHERE: lines reliably
* Auto-tally step dedupes by (reviewer, location); convergence
counts distinct lineages (closes the prior `opus+opus+opus`
false-convergence bug)
## Phase 3 — Cross-runtime validator parity probe (the headline finding)
scripts/cutover/parity/validator_parity.sh sends 6 identical
/v1/validate cases to Rust :3100 AND Go :4110, compares status+body.
Result: **6/6 status codes match · 5/6 body shapes diverge.**
Rust returns serde-tagged enum: {"Schema":{"field":"x","reason":"y"}}
Go returns flat exported-fields: {"Kind":"schema","Field":"x","Reason":"y"}
Both round-trip inside their own runtime; a caller swapping one for
the other would break parsing silently. Captured as new _open_ row
in docs/ARCHITECTURE_COMPARISON.md decisions tracker.
This is the "use the dual-implementation as a measurement instrument"
return — single-repo scrums can't catch this class of cross-runtime
drift.
## Phase 4 — Production assessment
ship-with-known-gap. Validator wire-format gap is documented, not
regressed. ~50 LOC future fix on Go side (custom MarshalJSON on
ValidationError to match Rust's serde shape).
Persistent stack config (/tmp/lakehouse-persistent.toml) gains
validatord on :3221 + persistent-validatord binary so operators
bringing up the persistent stack get the new daemon automatically.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
# Validator parity probe — Rust :3100 vs Go :4110
|
||||
|
||||
**Date:** 2026-05-02T08:59:17Z
|
||||
**Rust gateway:** `http://127.0.0.1:3100` · **Go gateway:** `http://127.0.0.1:4110`
|
||||
|
||||
Identical `POST /v1/validate` request → both runtimes. Match
|
||||
= identical HTTP status + identical body (modulo `elapsed_ms`).
|
||||
|
||||
| Case | Rust status | Go status | Status match | Body match |
|
||||
|---|---:|---:|:---:|:---:|
|
||||
| playbook_happy | 200 | 200 | ✓ | ✓ |
|
||||
| playbook_missing_fingerprint | 422 | 422 | ✓ | ✗ |
|
||||
| playbook_wrong_prefix | 422 | 422 | ✓ | ✗ |
|
||||
| playbook_empty_endorsed | 422 | 422 | ✓ | ✗ |
|
||||
| playbook_overfull | 422 | 422 | ✓ | ✗ |
|
||||
| fill_phantom | 422 | 422 | ✓ | ✗ |
|
||||
|
||||
**Tally:** 1 match · 5 diff (out of 6 cases)
|
||||
|
||||
## Divergences
|
||||
|
||||
<details><summary>DIFF — `playbook_missing_fingerprint`</summary>
|
||||
|
||||
**Rust** (HTTP 422):
|
||||
```json
|
||||
{
|
||||
"Schema": {
|
||||
"field": "fingerprint",
|
||||
"reason": "missing — required for Phase 25 validity window"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Go** (HTTP 422):
|
||||
```json
|
||||
{
|
||||
"Field": "fingerprint",
|
||||
"Kind": "schema",
|
||||
"Reason": "missing — required for Phase 25 validity window"
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>DIFF — `playbook_wrong_prefix`</summary>
|
||||
|
||||
**Rust** (HTTP 422):
|
||||
```json
|
||||
{
|
||||
"Schema": {
|
||||
"field": "operation",
|
||||
"reason": "expected `fill: ...` prefix, got \"sms_draft: hello\""
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Go** (HTTP 422):
|
||||
```json
|
||||
{
|
||||
"Field": "operation",
|
||||
"Kind": "schema",
|
||||
"Reason": "expected `fill: ...` prefix, got \"sms_draft: hello\""
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>DIFF — `playbook_empty_endorsed`</summary>
|
||||
|
||||
**Rust** (HTTP 422):
|
||||
```json
|
||||
{
|
||||
"Completeness": {
|
||||
"reason": "endorsed_names must be non-empty"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Go** (HTTP 422):
|
||||
```json
|
||||
{
|
||||
"Field": "",
|
||||
"Kind": "completeness",
|
||||
"Reason": "endorsed_names must be non-empty"
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>DIFF — `playbook_overfull`</summary>
|
||||
|
||||
**Rust** (HTTP 422):
|
||||
```json
|
||||
{
|
||||
"Completeness": {
|
||||
"reason": "endorsed_names (3) exceeds target_count × 2 (2)"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Go** (HTTP 422):
|
||||
```json
|
||||
{
|
||||
"Field": "",
|
||||
"Kind": "completeness",
|
||||
"Reason": "endorsed_names (3) exceeds target_count × 2 (2)"
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>DIFF — `fill_phantom`</summary>
|
||||
|
||||
**Rust** (HTTP 422):
|
||||
```json
|
||||
{
|
||||
"Consistency": {
|
||||
"reason": "fills[0].candidate_id \"W-PHANTOM-NEVER-EXISTS\" does not exist in worker roster"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Go** (HTTP 422):
|
||||
```json
|
||||
{
|
||||
"Field": "",
|
||||
"Kind": "consistency",
|
||||
"Reason": "fills[0].candidate_id \"W-PHANTOM-NEVER-EXISTS\" does not exist in worker roster"
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
Reference in New Issue
Block a user