Skip to content

Commit 6c50bfa

Browse files
Merge BROCA compaction-provider 0.7.10/0.7.11 sample fixtures (decode check on the placed provider)
Co-authored-by: Alfonso <alfonso-magic-context@users.noreply.github.com>
2 parents 273c24c + 9d98ca9 commit 6c50bfa

35 files changed

Lines changed: 742 additions & 0 deletions

File tree

‎crates/mc-module/src/providers/async_provider/tests.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use cortexkit_bus_inmemory::{InMemoryBus, InMemoryConfig};
33
mod adoption_recovery;
44
mod broca_0_7;
55
mod broca_account_change_samples;
6+
mod broca_compaction_0710;
67
mod broca_provider_fallback;
78
mod broca_rich_ui_stamp;
89
mod broca_tag_rows;
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
//! Replay Broca's conformance captures against the 0.7.10 provider.
2+
use super::*;
3+
use sha2::{Digest, Sha256};
4+
5+
const ROOT: &str = concat!(
6+
env!("CARGO_MANIFEST_DIR"),
7+
"/testdata/broca-compaction-0710/"
8+
);
9+
const MANIFEST_SHA256: &str = "c679754e05680772890502cced3e0f1902bad79cce8d2f9569457e991a84c3ed";
10+
11+
fn fixture(name: &str) -> Value {
12+
serde_json::from_slice(&std::fs::read(Path::new(ROOT).join(name)).unwrap()).unwrap()
13+
}
14+
15+
#[test]
16+
fn checksums_cover_all_31_captured_files() {
17+
let manifest = std::fs::read(Path::new(ROOT).join("SHA256SUMS")).unwrap();
18+
assert_eq!(format!("{:x}", Sha256::digest(&manifest)), MANIFEST_SHA256);
19+
let mut listed = BTreeSet::new();
20+
for line in std::str::from_utf8(&manifest).unwrap().lines() {
21+
let (expected, name) = line.split_once(" ").unwrap();
22+
assert!(listed.insert(name.to_owned()), "duplicate fixture: {name}");
23+
let bytes = std::fs::read(Path::new(ROOT).join(name)).unwrap();
24+
assert_eq!(format!("{:x}", Sha256::digest(&bytes)), expected, "{name}");
25+
}
26+
let present: BTreeSet<_> = std::fs::read_dir(ROOT)
27+
.unwrap()
28+
.map(|entry| entry.unwrap().file_name().into_string().unwrap())
29+
.filter(|name| name.ends_with(".json"))
30+
.collect();
31+
assert_eq!(listed, present);
32+
assert_eq!(listed.len(), 31);
33+
}
34+
35+
fn invalid_params(outcome: HandlerOutcome) -> String {
36+
match outcome {
37+
HandlerOutcome::Error { code, message }
38+
| HandlerOutcome::ErrorWithDetail { code, message, .. } => {
39+
assert_eq!(code, "invalid_params");
40+
message
41+
}
42+
other => panic!("expected invalid_params, got {other:?}"),
43+
}
44+
}
45+
46+
fn no_truncate(value: &Value) {
47+
match value {
48+
Value::Object(fields) => {
49+
assert!(!fields.contains_key("truncate"), "{value}");
50+
for child in fields.values() {
51+
no_truncate(child);
52+
}
53+
}
54+
Value::Array(values) => {
55+
for child in values {
56+
no_truncate(child);
57+
}
58+
}
59+
_ => {}
60+
}
61+
}
62+
63+
async fn replay_case(case: &str, fetches: usize, notices: usize, models: usize) {
64+
let dir = tempfile::tempdir().unwrap();
65+
let setup_name = format!("{case}-setup-0.json");
66+
let setup = fixture(&setup_name);
67+
let params = &setup["params"];
68+
let session = params["session"].as_str().unwrap();
69+
let harness = params["harness"].as_str().unwrap();
70+
let rig = Rig::without_setup(
71+
dir.path(),
72+
"broca",
73+
("runner", session),
74+
(harness, session),
75+
|_| {},
76+
)
77+
.await;
78+
79+
// The published Setup decoder ignores future fields, not their whole
80+
// request. The captured scripted preset is not one ck-mc implements.
81+
let decoded: compact::setup::SetupRequest = decode(params).unwrap();
82+
assert_eq!(decoded.view_due_max_ms(), Some(30_000));
83+
assert_eq!(decoded.ride_wait_max_ms, Some(120_000));
84+
assert_eq!(
85+
params["view_features"],
86+
json!(["truncate", "tag_bytes_edits"])
87+
);
88+
let known = serde_json::to_value(decoded).unwrap();
89+
assert!(known.get("view_features").is_none());
90+
let error = invalid_params(rig.h.dispatch_value(7, setup.clone()).await);
91+
assert_eq!(error, "unknown provider preset");
92+
assert!(load_head(rig.h.store.get().unwrap(), &rig.reference)
93+
.unwrap()
94+
.is_none());
95+
println!("{setup_name}: PASS decode (view_due_max_ms=30000, view_features ignored); handler REJECT invalid_params: {error}");
96+
97+
// This is a separate compatibility probe, not a rewrite of the capture:
98+
// remove only the future member and leave view_due_max_ms unchanged.
99+
let mut v0710 = params.clone();
100+
v0710.as_object_mut().unwrap().remove("view_features");
101+
let decoded: compact::setup::SetupRequest = decode(&v0710).unwrap();
102+
assert_eq!(serde_json::to_value(decoded).unwrap(), known);
103+
let error = invalid_params(
104+
rig.h
105+
.dispatch_value(7, json!({"method":"compaction.setup","params":v0710}))
106+
.await,
107+
);
108+
assert_eq!(error, "unknown provider preset");
109+
println!("{setup_name}: without view_features, decode PASS (view_due_max_ms=30000); handler REJECT scripted preset");
110+
111+
for index in 0..notices {
112+
let name = format!("{case}-notice-{index}.json");
113+
let headers: BTreeMap<String, String> = serde_json::from_value(fixture(&name)).unwrap();
114+
let decoded = notice::AdvanceNotice::from_headers(&headers).unwrap();
115+
assert_eq!(decoded.to_headers(), headers, "{name}: header lost");
116+
let message = cortexkit_bus_trait::Message {
117+
subject: notice::event_subject("fixture", "broca", notice::ADVANCED_EVENT),
118+
id: bus_id(&decoded.session_ref, "advanced", decoded.seq),
119+
digest: decoded.digest().parse().unwrap(),
120+
headers,
121+
};
122+
// Setup was refused. The actual notice handler acknowledges an unknown
123+
// conversation without reading the runner or publishing a view.
124+
rig.h.advance_compaction("fixture", &message).await.unwrap();
125+
assert!(load_head(rig.h.store.get().unwrap(), &decoded.session_ref)
126+
.unwrap()
127+
.is_none());
128+
let mut corrupt = message.clone();
129+
corrupt.id.push_str("-wrong");
130+
assert!(rig.h.advance_compaction("fixture", &corrupt).await.is_err());
131+
println!("{name}: PASS decode and handler (unknown conversation acknowledged)");
132+
}
133+
134+
for index in 0..fetches {
135+
let name = format!("{case}-fetch-{index}.json");
136+
let capture = fixture(&name);
137+
let _: view::ViewGetRequest = decode(&capture["request"]["params"]).unwrap();
138+
let answer = response(rig.h.dispatch_value(7, capture["request"].clone()).await);
139+
let _: view::ViewGetAnswer = decode(&answer).unwrap();
140+
assert_eq!(answer["answer"], "refuse");
141+
assert_eq!(answer["code"], "unknown_session");
142+
no_truncate(&answer);
143+
144+
// These captured answers come from a scripted provider, not ck-mc.
145+
// An unknown truncate field decodes but cannot supply the action that
146+
// the 0.7.10 view validator requires (bytes or drop).
147+
let scripted: view::ViewGetAnswer = decode(&capture["answer"]).unwrap();
148+
if case == "part_edits_truncate_renders_with_own_tag" {
149+
let view::ViewGetAnswer::View { view } = scripted else {
150+
panic!("expected a scripted view")
151+
};
152+
assert_eq!(view.check(), Err(view::ViewProblem::EditAction));
153+
println!("{name}: request PASS; ck-mc answer PASS (unknown_session); scripted answer PASS decode, FAIL validation (EditAction)");
154+
} else {
155+
if let view::ViewGetAnswer::View { view } = scripted {
156+
assert_eq!(view.check(), Ok(()), "{name}");
157+
}
158+
println!("{name}: request PASS; ck-mc answer PASS (unknown_session); scripted answer PASS decode/validation");
159+
}
160+
}
161+
162+
for index in 0..models {
163+
let name = format!("{case}-model-{index}.json");
164+
let rows = fixture(&name);
165+
assert!(!rows.as_array().unwrap().is_empty());
166+
for row in rows.as_array().unwrap() {
167+
assert!(matches!(row["role"].as_str(), Some("user" | "assistant")));
168+
assert!(row["content"].is_string());
169+
}
170+
println!("{name}: N/A model input (Broca -> model, not a ck-mc request/answer)");
171+
}
172+
assert!(rig.runner.reads.lock().unwrap().is_empty());
173+
}
174+
175+
macro_rules! case {
176+
($name:ident, $fetches:expr, $notices:expr, $models:expr) => {
177+
#[tokio::test]
178+
async fn $name() {
179+
replay_case(stringify!($name), $fetches, $notices, $models).await;
180+
}
181+
};
182+
}
183+
184+
case!(view_due_first_model_uses_view, 1, 2, 1);
185+
case!(view_due_later_step_retries, 2, 3, 1);
186+
case!(view_due_refused_fetch_blocks_model, 1, 1, 0);
187+
case!(part_edits_truncate_renders_with_own_tag, 1, 4, 2);
188+
case!(part_edits_bytes_render_with_own_tag, 1, 4, 2);
189+
190+
#[tokio::test]
191+
async fn supported_broca_setup_and_published_edits_never_contain_truncate() {
192+
let dir = tempfile::tempdir().unwrap();
193+
let rig = Rig::open(
194+
dir.path(),
195+
"broca",
196+
"broca",
197+
json!({"serializer_profile":"owned-broca"}),
198+
)
199+
.await;
200+
let setup: compact::setup::SetupAnswer = decode(&rig.state().initial).unwrap();
201+
no_truncate(&serde_json::to_value(setup).unwrap());
202+
// A supported ck-mc session is a separate publication witness: the captures
203+
// omit session.read pages and their scripted Setup is not a ck-mc preset.
204+
*rig.runner.entries.lock().unwrap() = (0..40)
205+
.map(|n| {
206+
ReadMessage::new(
207+
n,
208+
format!("m{n}"),
209+
json!({"role":if n % 2 == 0 {"user"} else {"assistant"},"content":[{"type":"text","text":format!("message {n} ")+&"durable words ".repeat(16)}]}),
210+
)
211+
.with_tags(vec![tags::PartTag::tagged(format!("m{n}#0"), format!("§{}§", n + 1))])
212+
})
213+
.collect();
214+
rig.notice(1, 100, None).await;
215+
rig.queue(&[2]).await;
216+
let published = rig
217+
.ride("ttl_expired")
218+
.await
219+
.expect("a reduction publishes");
220+
let answer = rig
221+
.op(
222+
"view.get",
223+
json!({"session_ref":rig.reference,"view_version":published.view_version}),
224+
)
225+
.await;
226+
assert_eq!(answer["answer"], "view");
227+
let _: view::ViewGetAnswer = decode(&answer).unwrap();
228+
no_truncate(&answer);
229+
let edits: Vec<_> = answer["view"]["ranges"]
230+
.as_array()
231+
.unwrap()
232+
.iter()
233+
.filter_map(|range| range["edits"].as_array())
234+
.flatten()
235+
.collect();
236+
assert!(
237+
!edits.is_empty(),
238+
"exercise a real edit, not an unchanged view"
239+
);
240+
assert!(edits
241+
.iter()
242+
.all(|edit| edit.get("drop").is_some() || edit.get("bytes").is_some()));
243+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Broca compaction-provider 0.7.10 / 0.7.11 decode captures
2+
3+
Source: Broca's five conformance-kit cases, supplied read-only at
4+
`/Users/ufukaltinok/Work/Projects/CortexKit-scratch/shared-mc-broca/compaction-0710-samples-1031/`.
5+
The 31 JSON files and `SHA256SUMS` are byte-for-byte copies. Every checksum was
6+
verified before inspecting the samples. The manifest's own SHA-256 is
7+
`c679754e05680772890502cced3e0f1902bad79cce8d2f9569457e991a84c3ed` (not a Git ref).
8+
9+
The tests in `src/providers/async_provider/tests/broca_compaction_0710.rs` use
10+
ck-mc's production dispatch, `providers::decode`, `AdvanceNotice::from_headers`,
11+
and the actual notice and view-get handlers. There is one test per named case,
12+
plus fixture-integrity and independent ck-mc publication witnesses. The crate
13+
pins `cortexkit-role-compaction-provider = 0.7.10` at the deployed ck-mc build's source commit
14+
`a2ba9a341f52318f48489a7c8ef66196f88b7e3a`. No compatibility shim or production
15+
behavior change accompanies these fixtures.
16+
17+
## Outcomes and boundaries
18+
19+
- **Setup decoding passes:** `view_due_max_ms = 30000` is understood;
20+
`view_features = ["truncate", "tag_bytes_edits"]` is ignored. There is **no
21+
strict unknown-field decoding error**. The pinned crate's `CONTRACT.md` §14
22+
says: “**Lenient** (unknown fields ignored): `role.describe`, the Setup
23+
request, the fields of every answer, the fields of `view.get` and
24+
`compaction.ride` requests and of their `overflow`, of a view, its cut and
25+
its part edits, and of `ceiling`. Unknown notice and view-ready headers are
26+
ignored.” `src/setup.rs` calls `SetupRequest` “Decoded leniently.”
27+
- **Literal Setup handling fails for a different reason:** all five requests
28+
select the kit's `preset: "scripted"`. ck-mc returns
29+
`invalid_params: unknown provider preset`, with and without `view_features`.
30+
The captures also omit `context_window`, which ck-mc needs for a fresh
31+
supported Setup. Tests do not silently change these requests into successful
32+
enrollments. These are scripted-provider kit runs, not successful ck-mc runs.
33+
- All 14 notices decode with every captured header preserved. Production notice
34+
handling acknowledges their unknown conversations after checking claim IDs
35+
and digests. This is **not** proof of a successful enrolled session replay:
36+
Setup failed and these captures contain no `session.read` pages. No new
37+
unrecognized notice header is present in this sample set.
38+
- All six `view.get` requests decode and traverse the real handler. With no
39+
enrolled conversation, ck-mc produces `refuse / unknown_session`, which also
40+
decodes. Captured answers are from the **scripted provider**, not ck-mc.
41+
Five captured answers also pass the pinned view validator. The truncate
42+
answer decodes leniently (the unknown field is ignored), then fails
43+
`CompactionView::check()` with `ViewProblem::EditAction`: 0.7.10 requires a
44+
`bytes` or `drop` action. This is not an inbound ck-mc view or a ck-mc output.
45+
- No view is published for these literal failed Setups, hence none contains
46+
`truncate`. A separate supported `owned-broca` session witness uses
47+
a synthetic text transcript, queues a real reduction, fetches its
48+
published view, requires nonempty edits, and checks that Setup and view
49+
answers contain no `truncate`. ck-mc's range builder emits only `drop` and
50+
`bytes` part edits. This witness is not presented as one of the supplied runs.
51+
- Six `model-*.json` files are Broca-to-model message arrays, **not** ck-mc
52+
requests or answers. They are retained and checksummed, but cannot honestly
53+
be run through a ck-mc protocol decoder. The supplied 31 files contain no
54+
ride requests or Setup answers.
55+
56+
## Per-file results
57+
58+
“PASS / preset FAIL” means decoding passes, but literal handling returns the
59+
exact unsupported-preset error above. “PASS / unknown” means decoding and actual
60+
unknown-conversation handling pass. “PASS / view FAIL” distinguishes decoding
61+
from validation of the scripted answer. These expected failures are asserted;
62+
a green test suite is not a claim that these runs can enroll with ck-mc.
63+
64+
| File | Decode / handling result |
65+
| --- | --- |
66+
| `view_due_first_model_uses_view-setup-0.json` | PASS / preset FAIL |
67+
| `view_due_first_model_uses_view-fetch-0.json` | Request PASS / unknown; scripted answer PASS |
68+
| `view_due_first_model_uses_view-notice-0.json` | PASS / unknown |
69+
| `view_due_first_model_uses_view-notice-1.json` | PASS / unknown |
70+
| `view_due_first_model_uses_view-model-0.json` | N/A: model-only |
71+
| `view_due_later_step_retries-setup-0.json` | PASS / preset FAIL |
72+
| `view_due_later_step_retries-fetch-0.json` | Request PASS / unknown; scripted answer PASS |
73+
| `view_due_later_step_retries-fetch-1.json` | Request PASS / unknown; scripted answer PASS |
74+
| `view_due_later_step_retries-notice-0.json` | PASS / unknown |
75+
| `view_due_later_step_retries-notice-1.json` | PASS / unknown |
76+
| `view_due_later_step_retries-notice-2.json` | PASS / unknown |
77+
| `view_due_later_step_retries-model-0.json` | N/A: model-only |
78+
| `view_due_refused_fetch_blocks_model-setup-0.json` | PASS / preset FAIL |
79+
| `view_due_refused_fetch_blocks_model-fetch-0.json` | Request PASS / unknown; scripted refusal PASS |
80+
| `view_due_refused_fetch_blocks_model-notice-0.json` | PASS / unknown |
81+
| `part_edits_truncate_renders_with_own_tag-setup-0.json` | PASS / preset FAIL |
82+
| `part_edits_truncate_renders_with_own_tag-fetch-0.json` | Request PASS / unknown; scripted answer PASS / view FAIL (`EditAction`) |
83+
| `part_edits_truncate_renders_with_own_tag-notice-0.json` | PASS / unknown |
84+
| `part_edits_truncate_renders_with_own_tag-notice-1.json` | PASS / unknown |
85+
| `part_edits_truncate_renders_with_own_tag-notice-2.json` | PASS / unknown |
86+
| `part_edits_truncate_renders_with_own_tag-notice-3.json` | PASS / unknown |
87+
| `part_edits_truncate_renders_with_own_tag-model-0.json` | N/A: model-only |
88+
| `part_edits_truncate_renders_with_own_tag-model-1.json` | N/A: model-only |
89+
| `part_edits_bytes_render_with_own_tag-setup-0.json` | PASS / preset FAIL |
90+
| `part_edits_bytes_render_with_own_tag-fetch-0.json` | Request PASS / unknown; scripted answer PASS |
91+
| `part_edits_bytes_render_with_own_tag-notice-0.json` | PASS / unknown |
92+
| `part_edits_bytes_render_with_own_tag-notice-1.json` | PASS / unknown |
93+
| `part_edits_bytes_render_with_own_tag-notice-2.json` | PASS / unknown |
94+
| `part_edits_bytes_render_with_own_tag-notice-3.json` | PASS / unknown |
95+
| `part_edits_bytes_render_with_own_tag-model-0.json` | N/A: model-only |
96+
| `part_edits_bytes_render_with_own_tag-model-1.json` | N/A: model-only |
97+
98+
## Reproduce on Linux
99+
100+
Use a throwaway `HOME`, retain the existing `CARGO_HOME` / `RUSTUP_HOME` values
101+
so Rust can still find its installed toolchain and dependency cache,
102+
and run `git config --global --add safe.directory '*'` in that HOME first.
103+
104+
```sh
105+
cargo fmt --all -- --check
106+
cargo test --locked -p mc-module --lib broca_compaction_0710 -- --nocapture
107+
cargo clippy --locked -p mc-module --all-targets -- -D warnings
108+
```
109+
110+
The repository delivery gate remains `bash scripts/gate.sh full`.

0 commit comments

Comments
 (0)