Repository navigation
SatisfactionCapture: the FAILURES and Upgrades legs are English-phrase-only, so on a non-Latin-script install both corpora stay empty and look healthy #2165
Description
Version
LifeOS 7.40.4 / SatisfactionCapture 1.4.3 — also reproduced on a clean checkout of main at 5e2f2e8c.
What is broken
SatisfactionCapture has two deterministic legs that feed the self-healing corpora: detectExplicitCorrection() → captureFailure() → FAILURES, and detectStandingDirective() → addUpgrade() → the Upgrades store. Both match English literals only — a list of English phrases, plus \b-anchored ASCII alternations. On an install whose principal writes in a non-Latin script, both functions return null for every prompt of every session: no complaint is ever captured, no standing directive ever reaches the Upgrades store, and nothing anywhere records that the detectors were mute. The two corpora simply stay empty, which is also what a flawless month looks like, so the failure is invisible from the dashboard, from the corpora, and from the hook's own output.
Where (file:line)
hooks/SatisfactionCapture.hook.ts:219 (CORRECTION_PHRASES, English strings), :235 (WORK_REFERENCE, \b-anchored ASCII), :267 (DIRECTIVE_PATTERNS, English phrases plus \b(always|never) (do|use|…)\b). Writers: :478 (standing directive → addUpgrade) and :497 (correction → captureFailure).
Repro on a clean tree
# fresh checkout, nothing patched
gh api repos/danielmiessler/LifeOS/tarball/5e2f2e8c > main.tar.gz
mkdir clean && tar xzf main.tar.gz -C clean --strip-components=1
cat > r1.ts <<'TS'
const ROOT = process.env.LIFEOS_ROOT!;
const { detectExplicitCorrection, detectStandingDirective } =
await import(`${ROOT}/hooks/SatisfactionCapture.hook.ts`);
const cases = [
["correction EN", detectExplicitCorrection, "no, that's wrong, you didn't do what I asked"],
["correction RU", detectExplicitCorrection, "нет, это неправильно, ты сделал не то, о чём я просил"],
["directive EN", detectStandingDirective, "from now on always show me the verification"],
["directive RU", detectStandingDirective, "с этого момента всегда показывай мне проверку"],
] as const;
for (const [label, fn, text] of cases) console.log(label + ":", fn(text) !== null);
TS
LIFEOS_ROOT="$PWD/clean/LifeOS/install" bun r1.tsEach Russian sentence is a literal translation of the English one above it.
Negative control
On unpatched main@5e2f2e8c the probe is red: the two English sentences are detected, the two translations of those same sentences are not.
correction EN: true
correction RU: false
directive EN: true
directive RU: false
Stated without reference to any fix: a principal who says "from now on always show me the verification" gets one row in the Upgrades store; a principal who says the same sentence in Russian gets zero, every time, for the life of the install.
Suggested fix
Not an i18n configuration layer — #1706 was declined for exactly that and the reasoning holds. Two smaller, independent things:
- Make the patterns safe to extend. The
\banchors here carry the same latent trapmatchSkillshad before AlgorithmNudge skill routing is silently mute: index staleness ignores content, and matchSkills assumes English morphology #1787 andAlgorithmNudgehad before AlgorithmNudge trigger vocabulary is English-only — non-English prompts never fire the always-on prompt-side rows #1865:\bis ASCII-only in JS, so the first non-Latin term anyone adds to these lists silently never matches. The same Unicode-property boundary construction applied here costs nothing in English behaviour, and turns "extend the list" into a change that actually works. - Make a mute detector observable. The hook writes nothing when both legs return
null, so nothing distinguishes "no corrections this session" from "the detector cannot fire on this principal's prompts". One counter, wherever it fits.
Vocabulary can then arrive one language at a time from whoever needs it, the way the German depth list did in #1865. Untested as a patch — I have not written it.
Related
- I searched open and closed issues for this defect.
- The repro runs against a clean tree of the version above, not against a modified install.
- The pasted output carries no personal data — no names, no home paths, no tokens, no own content. Both Russian strings are invented test sentences.
Activity
jacobo-ortiz commentedon Sep 24, 2026
Adding one data point from a Latin-script install (the principal writes in Spanish), filed by mistake as #2236 before I saw this issue:
POSITIVE_PRAISE_WORDS/POSITIVE_PHRASES. In a sandbox (LIFEOS_DIRon a temp dir),"perfect"writes a rating and"perfecto","excelente","muy bien"write nothing. On that installratings.jsonlgot no new line for two months, and nothing flagged it.^no[,.]opener catches "No, ...", but the most frequent real corrections that month ("estás mezclando cosas", "no es lo que te pedí") were missed.So whatever vocabulary mechanism lands (#2230) should cover the praise lists too, not just the correction and directive lists.
gjaworski86 commentedon Sep 25, 2026
Confirming from a Polish install, with numbers from about 2600 of the principal's own prompts (May to September).
^no[,.]turned an approval into a FAILURES capture. The other hit was an English phrase inside a pasted, quoted review.<pasted_content>blocks. We also cap the prompt at 800 chars: over machine prompts that paste whole transcripts, the Polish phrases would otherwise fire 207 times in 1945.CORRECTION_PATTERNS(/wait,?\s+/,/actually,?\s+/, ...) run on everyuserentry in the transcript. That includes text the harness injects: skill bodies, task notifications, compaction summaries and memory-curator prompts. All 309 "corrections" it harvested here were that, and none came from the principal. MemoryRetriever then fed them back into context on every turn.Related, #1888: the imperative list from #1889 catches no Polish answers. All 9 rows in our ratings ledger were answers to numbered points ("1 - tak, zrób to"). What worked better: if the previous response offered point N, a leading N answers it, and a tail with its own item numbers or a question mark is an answer too.
We did this with one shared detector module (corrections + praise) used by SatisfactionCapture, PromptProcessing and SessionHarvester. It is close to the #2230 idea, but inline rather than per-locale files.
danielmiessler commentedon Oct 3, 2026
Thanks @IvanLeontev-stack, and @jacobo-ortiz and @gjaworski86 for the Spanish and Polish numbers. Confirmed: the correction, praise and directive vocabularies now live in one shared classifier (
LIFEOS/ERRATA/Signals.ts), but they are still English-only, so a non-English install captures little or nothing. Per-language vocabulary is a design change, so this stays open and tracked.