docs(wording): reframe alerts and report from verdict to inquiry (tiltak 2)

BRIEF-vurdering-v2.md tiltak 2: "design for the investigator, not the
validator". The hook alerts stated short conclusions a reader can only
accept or dismiss ("Rapid-fire: N consecutive fast interactions",
"possible stuck/spiral", "Consider a break").

Each alert in tool-tracker.mjs now follows the form the read-dominant
edit-ratio message introduced in 2c9e2de — observation, the counter-signal
that changes how to read it, then what to check:

- burst: names the interval and that edits were among the calls, and asks
  whether each change was verified before the next
- edit ratio: carries the read percentage and asks what the remaining
  calls are doing and whether the approach is converging
- soft warning: closes on framing instead of prescribing a break
- hard warning: asks the model to name its observations and ask what they
  reflect; the required stop action is unchanged

commands/interaction-report.md gains the same rule for Observations and
trend reporting, plus an explicit "investigator, not validator" tone rule
and a "report the difference, not a label for it" rule.

Wording only — thresholds, heuristics, data model and required actions
are untouched. README examples and threshold-basis cells updated to match.

Tests first (Iron Law): 6 new/updated assertions on message text in
tests/tool-tracker.test.mjs and tests/interaction-report.test.mjs, red
before the change. node --test tests/*.test.mjs: 269 pass, 5 fail — the
pre-existing perf wall-clock cases only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013U8ZH25KiMtts89yWRuVWD
This commit is contained in:
Kjell Tore Guttormsen 2026-08-13 21:10:39 +02:00
commit 86c3eae30c
6 changed files with 168 additions and 25 deletions

View file

@ -120,21 +120,25 @@ if (dayCount >= THRESHOLD_HARD_SESSIONS) {
messages.push(`${dayCount} sessions today.`);
}
// Burst — a run of read-only tools is bulk reading, not rapid-fire work
// Burst — a run of read-only tools is bulk reading, not fast editing. What
// remains is reported as the observation plus what would tell a fast-but-
// deliberate run apart from an unchecked one — never as a verdict label.
const burstMessage = `Fast tool calls: ${burstCount} in a row under ${THRESHOLD_BURST_INTERVAL}s apart, edits among them — check whether each change was verified before the next one started.`;
if (!burstReadOnly) {
if (burstCount >= THRESHOLD_HARD_BURST) {
level = 'hard';
messages.push(`Rapid-fire: ${burstCount} consecutive fast interactions.`);
messages.push(burstMessage);
} else if (burstCount >= THRESHOLD_SOFT_BURST) {
if (!level) level = 'soft';
messages.push(`Rapid-fire: ${burstCount} consecutive fast interactions.`);
messages.push(burstMessage);
}
}
// Low edit ratio (only after minimum duration). A read-dominant session has
// a structurally low edit ratio whether or not the work is productive, so it
// is reported as context — never as a stuck/spiral claim, and never as the
// sole reason for a warning.
// is reported as context — never as a diagnosis, and never as the sole reason
// for a warning.
const readRatio = toolCount > 0 ? Math.floor(readCount * 100 / toolCount) : 0;
const readDominant = readRatio >= THRESHOLD_READ_DOMINANT_RATIO;
@ -145,7 +149,7 @@ if (durationMin >= THRESHOLD_LOW_EDIT_MIN_DURATION && editRatio < THRESHOLD_LOW_
}
} else {
if (!level) level = 'soft';
messages.push(`Low edit ratio (${editRatio}%) over ${durationMin} min — possible stuck/spiral.`);
messages.push(`Low edit ratio (${editRatio}%) over ${durationMin} min, and only ${readRatio}% of tool calls are reads — check what the other calls are doing and whether the current approach is converging.`);
}
}
@ -181,9 +185,9 @@ let warning;
if (level === 'hard') {
state = readState();
const depFlags = Number(state.dep_flags) || 0;
warning = `INTERACTION AWARENESS: ${messages.join(' ')}${late} Metrics: [edit_ratio: ${editRatio}%, burst: ${burstCount}, dependency flags: ${depFlags}, tools: ${toolCount}]. Your instructions require you to suggest stopping.`;
warning = `INTERACTION AWARENESS: ${messages.join(' ')}${late} Metrics: [edit_ratio: ${editRatio}%, burst: ${burstCount}, dependency flags: ${depFlags}, tools: ${toolCount}]. Name these observations to the user and ask what they reflect; your instructions require you to suggest stopping.`;
} else {
warning = `${messages.join(' ')}${late} Consider a break.`;
warning = `${messages.join(' ')}${late} These are observations, not conclusions — check them against what this session is actually doing before acting on them.`;
}
// Record warning time