Apply ground-game's rulings: mastery in points, four boards, and a
Some checks failed
ci / check (push) Failing after 4s
Some checks failed
ci / check (push) Failing after 4s
vendor tool that covers what the gate checks
They ruled on all seven items the same day. Two were actionable here.
F28 RULED: points. Modes.csv MODE_COOP clarified upstream to say
"penalties apply to points, not card count"; mastery is now
total - blame - denied. A recorded scenario went red on it --
gr-e02-shared-ground pinned 0 (2 claimed CARDS - 1 - 1) and now expects
2 (4 POINTS - 1 - 1). The number moved because the rule was decided, not
because the engine drifted, and the scenario records both rulings; its
schema has no field for a second one, so both live in ruled_note with
`ruled` carrying the LATEST date.
F29 RULED not-intended and APPLIED upstream: SCN_02's suits re-tuned the
same day. The characterisation test is how we found out -- it pinned the
duplication, went red on the re-tune, and that red WAS the notification.
It now asserts every pair distinct, the stronger statement the
duplication had made unavailable. SCN_02 re-measures at 73 at 2p, not
67: its own board now.
F26/F30 ruled and recorded. F30's ruling incidentally confirms our
reading -- they name priority-2's suit as the first lever, which is the
difference we identified without having measured causation.
vendor-editions grew twice, both times because it covered less than the
gate it exists to satisfy:
- It refused to touch ground-darvo-r0/ on the reasoning that the
baseline is "a separate record". That was wrong within the hour:
ground-game clarified Modes.csv and `make vendor` reported a clean
sync while edition-check went red. A sync tool that covers less than
its check reports success into a red gate.
- Its two-block rewrite DETECTED which fence held which set and
preserved the arrangement -- faithfully preserving a swap an earlier
write had introduced, leaving each fence under a heading describing
the other. edition-check reads every sha256 line flat and passed
throughout: a document can be self-consistently wrong and green.
Order is now asserted, with a control that goes red on a swap.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
98f600b1d3
commit
704b99975b
15 changed files with 373 additions and 154 deletions
|
|
@ -1056,16 +1056,20 @@ mod card_text_tests {
|
|||
}
|
||||
}
|
||||
|
||||
/// **Two of the four scenarios are the same board** (CB-WP-0047).
|
||||
/// **The four scenarios are four boards** (F29).
|
||||
///
|
||||
/// SCN_01 and SCN_02 have identical suit-and-value profiles at every
|
||||
/// priority, so they play identically and differ only in prose. Not
|
||||
/// a defect — a designed reskin is a legitimate choice — but it is a
|
||||
/// fact about what "four scenarios" buys, and measuring them as four
|
||||
/// independent boards would be measuring two of them twice.
|
||||
/// They were not. SCN_01 and SCN_02 had identical suit-and-value
|
||||
/// profiles at every priority — they played identically and differed
|
||||
/// only in prose, so "four scenarios" bought three boards.
|
||||
///
|
||||
/// This is a **characterisation** test: it pins what is true today so
|
||||
/// that a change to either deck is a decision rather than a drift.
|
||||
/// Reported as F29; ground-game ruled it **unintended** and re-tuned
|
||||
/// SCN_02's suits the same day. **This test is how we found out**: it
|
||||
/// was a characterisation pinning the duplication, it went red on the
|
||||
/// re-tune, and that red was the notification, not a defect.
|
||||
///
|
||||
/// It now asserts the property that should hold from here — every
|
||||
/// pair distinct — which is the stronger statement the duplication
|
||||
/// had made unavailable.
|
||||
#[test]
|
||||
fn which_scenarios_are_mechanically_distinct() {
|
||||
let profile = |id: &str| -> Vec<(u8, String, u8)> {
|
||||
|
|
@ -1077,24 +1081,17 @@ mod card_text_tests {
|
|||
v.sort();
|
||||
v
|
||||
};
|
||||
assert_eq!(
|
||||
profile("SCN_01"),
|
||||
profile("SCN_02"),
|
||||
"SCN_01 and SCN_02 diverged — they were identical boards; \
|
||||
if this is intended, the panel now measures four real boards"
|
||||
);
|
||||
for pair in [
|
||||
("SCN_01", "SCN_03"),
|
||||
("SCN_01", "SCN_04"),
|
||||
("SCN_03", "SCN_04"),
|
||||
] {
|
||||
assert_ne!(
|
||||
profile(pair.0),
|
||||
profile(pair.1),
|
||||
"{} and {} became the same board",
|
||||
pair.0,
|
||||
pair.1
|
||||
);
|
||||
let ids = ["SCN_01", "SCN_02", "SCN_03", "SCN_04"];
|
||||
for (i, a) in ids.iter().enumerate() {
|
||||
for b in &ids[i + 1..] {
|
||||
assert_ne!(
|
||||
profile(a),
|
||||
profile(b),
|
||||
"{a} and {b} are the same board — four scenarios must be \
|
||||
four boards, or a panel treating them as independent \
|
||||
samples counts one of them twice"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1880,12 +1880,18 @@ impl GroundState {
|
|||
.map(|p| p.blame_from.len() as i32)
|
||||
.sum();
|
||||
let denied = self.problems.values().filter(|p| p.denied).count() as i32;
|
||||
let claimed = self
|
||||
.problems
|
||||
.values()
|
||||
.filter(|p| p.claimed_by.is_some())
|
||||
.count() as i32;
|
||||
let mastery = claimed - blame - denied;
|
||||
// **Points, not card count** — F28, ruled by ground-game
|
||||
// 2026-08-08 and clarified in `Modes.csv`: *"start from
|
||||
// that shared point total and subtract 1 point for each
|
||||
// Blame token still in play and 1 point for each Denied
|
||||
// Problem (penalties apply to points, not card count)."*
|
||||
//
|
||||
// This counted CLAIMED CARDS. Both readings fit the
|
||||
// sentence as it stood, and they differ on every game
|
||||
// where a 3-point Problem is claimed — which is most of
|
||||
// them. Reported rather than guessed; the card is the
|
||||
// authority (ADR-0015) and it now says so outright.
|
||||
let mastery = total as i32 - blame - denied;
|
||||
let winners = if group_success {
|
||||
self.players.keys().copied().collect()
|
||||
} else {
|
||||
|
|
@ -3552,7 +3558,7 @@ mod tests {
|
|||
/// does today and shows the size of the disagreement, so the ruling
|
||||
/// has a number in front of it.
|
||||
#[test]
|
||||
fn the_mastery_rating_and_the_shared_score_count_different_things() {
|
||||
fn the_mastery_rating_is_in_points_as_the_mode_card_now_says() {
|
||||
let mut s = setup_3p(11);
|
||||
s.mode = ScoringMode::SharedGround;
|
||||
// Two claimed Problems worth 2 and 3 — five points, two cards.
|
||||
|
|
@ -3587,16 +3593,40 @@ mod tests {
|
|||
assert_eq!(o.total, 5, "the shared score is claimed VALUE");
|
||||
assert_eq!(
|
||||
o.mastery,
|
||||
Some(2),
|
||||
"mastery is the claimed COUNT, with no penalties in play"
|
||||
Some(5),
|
||||
"mastery starts from the shared POINT total, not the card count"
|
||||
);
|
||||
// The disagreement, stated as a number rather than as a worry.
|
||||
assert_ne!(
|
||||
i32::try_from(o.total).unwrap(),
|
||||
o.mastery.unwrap(),
|
||||
"the two readings agree on this board, so F28 has no bite here \
|
||||
and the example needs replacing"
|
||||
// The board is chosen so the two readings disagree — two cards
|
||||
// worth five points. Without that the test would pass under the
|
||||
// reading it exists to exclude.
|
||||
assert_eq!(
|
||||
s.problems
|
||||
.values()
|
||||
.filter(|p| p.claimed_by.is_some())
|
||||
.count(),
|
||||
2,
|
||||
"the fixture must claim a number of CARDS different from the \
|
||||
POINT total, or it cannot tell the two readings apart"
|
||||
);
|
||||
|
||||
// **The edition's own sentence, not our memory of the ruling.**
|
||||
// `Modes.csv` was clarified upstream; if that clarification is
|
||||
// ever reverted this goes red rather than quietly diverging.
|
||||
let (card, _) = crate::edition::modes()
|
||||
.expect("Modes.csv")
|
||||
.into_iter()
|
||||
.find(|(c, _)| c.id == "MODE_COOP")
|
||||
.expect("MODE_COOP");
|
||||
assert!(
|
||||
card.rules_text.contains("not card count"),
|
||||
"the Mode card no longer rules out the card-count reading: {:?}",
|
||||
card.rules_text
|
||||
);
|
||||
|
||||
// And the penalties still bite, in points.
|
||||
let seat = seats[0];
|
||||
s.players.get_mut(&seat).unwrap().blame_from = vec![seats[1]];
|
||||
assert_eq!(s.score().mastery, Some(4), "a Blame token costs 1 POINT");
|
||||
}
|
||||
|
||||
/// **Every scenario is reachable through `setup`** (CB-WP-0047).
|
||||
|
|
@ -3634,14 +3664,13 @@ mod tests {
|
|||
.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
// SCN_01 and SCN_02 are the same board by design, so four
|
||||
// scenarios yield THREE distinct boards. Asserting 4 here would
|
||||
// be asserting a fact about the edition that is not true.
|
||||
// Four scenarios, four boards (F29). This asserted THREE until
|
||||
// 2026-08-08, because SCN_01 and SCN_02 were identical decks;
|
||||
// ground-game ruled that unintended and re-tuned SCN_02.
|
||||
assert_eq!(
|
||||
boards.len(),
|
||||
3,
|
||||
"the four scenarios yield {} distinct boards; SCN_01 and SCN_02 \
|
||||
were identical and the other two differ",
|
||||
4,
|
||||
"the four scenarios yield {} distinct boards at 4p",
|
||||
boards.len()
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue