diff --git a/crates/sds-core/src/features/firing.rs b/crates/sds-core/src/features/firing.rs index 42d97b4..1ff4aa9 100644 --- a/crates/sds-core/src/features/firing.rs +++ b/crates/sds-core/src/features/firing.rs @@ -197,8 +197,13 @@ impl Feature for PKill { type Norm = Bounded; type Input<'a> = Volley<'a>; const NAME: &'static str = "p_kill"; + // Names the locations, on purpose. The old sentence - "enough damage in one + // place to destroy the target outright" - described the computation this + // replaced *and* the one that replaced it, so the basis fingerprint, which + // hashes descriptions, could not tell them apart. A sentence that survives a + // change of meaning was too vague to be load-bearing. const DESCRIPTION: &'static str = - "The chance this volley does enough damage in one place to destroy the target outright."; + "The chance the head or centre torso is destroyed, counting only the damage that lands there."; fn measure(volley: &Volley<'_>) -> Score { if !volley.has_locations() { @@ -216,7 +221,7 @@ impl Feature for PMissionKill { type Input<'a> = Volley<'a>; const NAME: &'static str = "p_mission_kill"; const DESCRIPTION: &'static str = - "The chance this volley takes a leg and leaves the target unable to fight on."; + "The chance a leg is destroyed, or the target dies, counting only the damage that lands there."; fn measure(volley: &Volley<'_>) -> Score { if !volley.has_locations() { @@ -309,7 +314,7 @@ impl Feature for Overkill { type Input<'a> = Volley<'a>; const NAME: &'static str = "overkill"; const DESCRIPTION: &'static str = - "How much of this volley's damage is expected to land on a target that is already dead."; + "How much of this volley's damage is expected to overflow the locations it lands on."; fn measure(volley: &Volley<'_>) -> Score { let mean = volley.damage.mean(); diff --git a/plan/training.md b/plan/training.md index bb398f2..7debfee 100644 --- a/plan/training.md +++ b/plan/training.md @@ -526,13 +526,30 @@ label comparison's `p_kill +0.225` at 91% agreement. Nothing in a corpus recorded before this change says which `p_kill` wrote it, and nothing would object. -- [ ] Give the feature basis a version the manifest records, bumped when a - feature's meaning changes rather than when the set does - the same rule - `sds/epoch.py` applies to a match, applied to the basis. A name list - answers "are these the same columns"; this answers "do the columns mean - the same thing", and only the second question would have caught this - change. The epoch itself is the wrong place: it versions what a match is, - and the bot's own scoring is not that +The basis fingerprint hashes each feature's name, normalisation and +one-sentence description, which is a better question than the name list and +still not the right one. Tested against this change, it reported the basis had +moved - but only because six features were added. Its `REWORDED` list was +empty: `p_kill`'s sentence was byte-identical across a change that moved its +answer by three orders of magnitude, because "enough damage in one place to +destroy the target outright" describes the old computation and the new one +equally well. Had this change touched `p_kill` alone and added nothing, the +fingerprint would have called the basis unchanged. + +The descriptions of `p_kill`, `p_mission_kill` and `overkill` now name the +quantity rather than the intent, which closes this instance. It does not close +the hole: a description is a proxy for a meaning, and a proxy that a person can +forget to update is one they will. + +- [ ] Hash a `BASIS` constant in `sds-core` that a person bumps when they change + what a feature computes, alongside the description. The same shape as + `sds/epoch.py`, and for the same reason: enumerating what might have + changed does not work, so one number is declared instead. The epoch itself + is the wrong home - it versions what a match is, and the bot's own scoring + is not that. +- [ ] Make a vague description fail rather than pass. A sentence that still fits + after the computation changed was too vague to be load-bearing, and that + is a property worth a review rule if not a test ## Leaving the map: not yet, and why