Das Problem
Ein Agent, der an einem echten Projekt arbeitet, sieht nicht das ganze Projekt. Er sieht, wonach er sucht, und er sucht, was ihm einfällt. Liegt die benötigte Hilfsfunktion in einem anderen Modul unter anderem Namen, schreibt er sie neu. Lässt eine Aufgabe eine kurze und eine lange Lösung zu, schiebt ihn nichts zur kurzen.
Die übliche Antwort sind geschriebene Regeln: eine CLAUDE.md, ein Skill, der Systemprompt. Sie helfen, aber sie sind Ratschläge. Das Modell lädt sie vielleicht nicht, vergisst sie mitten im Durchgang oder hält sie für nicht zutreffend. Die Gewichte des Modells können wir nicht ändern, aber drei Dinge steuern: was das Modell sieht, was es tun darf und wann es fertig sein darf. Sens nutzt alle drei zugleich.
Der Kreislauf
Claude Code nimmt Hooks als Callbacks im eigenen Stream-Protokoll an. Sens beantwortet fünf davon in seinem Prozess, mit einer Invariante: Eine Änderung entsteht ungeprüft. Freigeben kann sie nur eine bestandene Prüfung oder ein Mensch.
- 01
Du sendest eine Nachricht
UserPromptSubmit
Sens legt einen Prüfpunkt des Projekts an und gibt Claude bis zu acht Dinge, die es schon gibt und zur Anfrage passen, jeweils mit Signatur, Datei:Zeile und Zahl der Verwendungen.
- 02
Vor jedem Schreiben
PreToolUse · Write, Edit
Sens baut die entstehende Datei im Speicher nach und wendet die Änderungsregeln an. Es kann das Schreiben verweigern, bevor es geschieht, mit dem Grund und dem wiederzuverwendenden Code.
- 03
Vor jedem Befehl
PreToolUse · Bash, PowerShell
Sens schützt seine eigenen Pfade und die Konfiguration und behandelt git commit und git push als Ende eines Durchgangs: Nichts Ungeprüftes wird committet.
- 04
Nach jedem Werkzeug
PostToolUse
Egal welches Werkzeug: Sens sucht die auf der Festplatte geänderten Dateien und wendet dieselben Regeln an.
- 05
Am Ende des Durchgangs
Stop · SubagentStop
Sens prüft den ganzen Diff seit dem letzten freigegebenen Punkt: Änderungsregeln, verwaisten Code und, wenn sich Code geändert hat, den Prüfer. Bei blockierenden Befunden arbeitet Claude weiter; nach drei Runden wird der Durchgang für dich angehalten.
Nichts zu umgehen
Bevor wir etwas bauten, haben wir live geprüft, dass der Mechanismus einem Modell standhält, das ihn umgehen will: Ein mitten in der Sitzung geschriebenes disableAllHooks stoppt die Callbacks nicht, Subagenten laufen hindurch, und ein verweigertes git commit lässt das Repository, wie es war. Für jede Abkürzung gibt es etwas, das sie schließt:
| Abkürzung | Was sie schließt |
|---|---|
| Schreiben über das Terminal, Python oder einen anderen MCP-Server | Die Prüfung der Festplatte nach jedem Werkzeug und die Prüfung am Ende des Durchgangs |
| Subagenten und Hintergrundaufgaben | Sie laufen durch dieselben Hooks; der Durchgang wird nicht freigegeben, solange sie laufen |
| Hooks abschalten oder die Konfiguration ändern | Die Hooks leben im Prozess von Sens; Regel R7 blockiert diese Dateien und stellt sie wieder her |
| Einen eigenen Worktree öffnen | Worktree-Werkzeuge werden verweigert, und R7 deckt git worktree ab |
| Die Arbeit für fertig erklären | Über das Ende eines Durchgangs entscheidet die Prüfung, nicht das Modell |
| Einen neuen Durchgang beginnen, um zu entkommen | Die Prüfung beginnt beim letzten freigegebenen Punkt: Offenes wird übernommen |
| Ungeprüfte Arbeit committen | Ein Commit gilt als Ende eines Durchgangs |
| Endlos weitermachen | Drei Runden, dann wird der Durchgang angehalten |
Die Grenze ist ausdrücklich: Der Kreislauf schließt die Fehler und Abkürzungen eines Modells, nicht die eines feindseligen Programms auf demselben Rechner.
Ergebnisse
Einzelne Aufgaben beginnen mit einem sauberen Projekt, und der Schaden eines Agenten, der nicht wiederverwendet, liegt nicht in einer Aufgabe, sondern in der Summe. Horizonte misst die Summe: eine TypeScript-Kommandozeile für Haushaltsausgaben mit anfangs 88 Zeilen und 30 Produktanfragen in fester Reihenfolge, jede auf der vorigen aufbauend. Acht Konzepte sind eingestreut, die mehrere Aufgaben brauchen, ohne es zu sagen: Datumsangaben, Monate und Wochen, Summen, Akzente, Beträge, CSV, Befehlsoptionen. Beim ersten Mal schreibt der Agent sie; danach wäre es richtig, das Geschriebene wiederzuverwenden.
Das Kriterium wurde vor der Messung schriftlich festgelegt: Sens macht das Projekt nur dann kleiner, wenn alle drei Sequenzen mit Sens unter allen drei ohne Sens enden. Ohne echten Unterschied passiert das zufällig einmal in zwanzig Fällen.
Projektgröße nach jeder Aufgabe
- Ohne Sens
- Referenz
- Mit Sens
Codezeilen im Projekt nach jeder der 30 Aufgaben. Dünne Linien sind die einzelnen Sequenzen, dicke ihr Median. Die gestrichelte Linie ist eine auf Wiederverwendung angelegte Referenzlösung, die ihre gemeinsamen Module früh anlegt.
Daten anzeigen
| Nach Aufgabe | Ohne Sens | Referenz | Mit Sens |
|---|---|---|---|
| 0 | 88 | 88 | 88 |
| 1 | 92 | 93 | 93 |
| 2 | 102 | 113 | 103 |
| 3 | 113 | 150 | 109 |
| 4 | 121 | 161 | 118 |
| 5 | 145 | 188 | 137 |
| 6 | 162 | 202 | 161 |
| 7 | 178 | 209 | 177 |
| 8 | 223 | 234 | 195 |
| 9 | 232 | 243 | 206 |
| 10 | 232 | 243 | 206 |
| 11 | 258 | 276 | 231 |
| 12 | 277 | 282 | 247 |
| 13 | 296 | 307 | 265 |
| 14 | 304 | 309 | 269 |
| 15 | 316 | 315 | 271 |
| 16 | 346 | 345 | 301 |
| 17 | 354 | 357 | 309 |
| 18 | 367 | 369 | 326 |
| 19 | 370 | 377 | 330 |
| 20 | 381 | 390 | 341 |
| 21 | 398 | 404 | 357 |
| 22 | 415 | 413 | 373 |
| 23 | 426 | 422 | 384 |
| 24 | 432 | 422 | 388 |
| 25 | 452 | 434 | 401 |
| 26 | 453 | 436 | 402 |
| 27 | 476 | 453 | 418 |
| 28 | 485 | 455 | 426 |
| 29 | 487 | 455 | 427 |
| 30 | 496 | 455 | 436 |
Größe bei Aufgabe 30, je Sequenz
Alle drei Sequenzen mit Sens enden unter allen drei ohne Sens. Das Kriterium ist erfüllt: Median 436 Zeilen gegenüber 496, 12 % kleiner, mit einem 95-%-Intervall von −140 bis −28 Zeilen.
Verbrauchte Tokens, aufsummiert über die 30 Aufgaben
- Ohne Sens
- Mit Sens
Mit einem kleineren Projekt, das bei jeder Aufgabe zu lesen ist, verbraucht Sens weniger: 27,7 Millionen Tokens über seine drei Sequenzen gegenüber 33,7 Millionen. Die Tokens enthalten Cache-Lesezugriffe und messen den Arbeitsumfang, nicht die genauen Kosten.
Daten anzeigen
| Nach Aufgabe | Ohne Sens | Mit Sens |
|---|---|---|
| 0 | 0,0M | 0,0M |
| 1 | 0,3M | 0,3M |
| 2 | 0,5M | 0,6M |
| 3 | 0,9M | 0,8M |
| 4 | 1,2M | 1,1M |
| 5 | 1,6M | 1,3M |
| 6 | 1,8M | 1,7M |
| 7 | 2,0M | 1,9M |
| 8 | 2,5M | 2,4M |
| 9 | 3,4M | 2,7M |
| 10 | 3,7M | 3,0M |
| 11 | 4,4M | 3,4M |
| 12 | 4,7M | 3,6M |
| 13 | 5,2M | 4,0M |
| 14 | 5,4M | 4,2M |
| 15 | 5,9M | 4,5M |
| 16 | 6,2M | 5,1M |
| 17 | 6,6M | 5,3M |
| 18 | 6,9M | 5,7M |
| 19 | 7,1M | 6,1M |
| 20 | 7,5M | 6,4M |
| 21 | 8,1M | 6,7M |
| 22 | 8,5M | 7,1M |
| 23 | 8,8M | 7,4M |
| 24 | 9,0M | 7,7M |
| 25 | 9,4M | 7,9M |
| 26 | 9,8M | 8,2M |
| 27 | 10,2M | 8,5M |
| 28 | 10,7M | 8,8M |
| 29 | 11,0M | 9,1M |
| 30 | 11,3M | 9,3M |
Woher der Unterschied kommt
Nicht davon, weniger zu kopieren. Keiner der beiden Arme kopierte ernsthaft Blöcke: jscpd fand ohne Sens 0, 6 und 6 doppelte Zeilen und mit Sens keine, und die Sonden für jedes eingestreute Konzept zeigen in beiden gleiche oder fast gleiche Werte. Der Unterschied kommt davon, für dasselbe weniger zu schreiben. Mit Sens schreibt der Agent mehr und kürzere Funktionen: im Median 28 statt 20. Im Pilotlauf schrieb der Agent ohne Sens für Beschreibungen in Anführungszeichen einen ganzen CSV-Leser mit 99 Zeilen; mit Sens erkannte er, dass die Beschreibung das letzte Feld ist, und kam mit zwei einzeiligen Funktionen aus.
Tokens insgesamt: 33,7M ohne Sens · 27,7M mit Sens
Einzelne Aufgaben
Zwölf Aufgaben in drei Sprachen an zwei echten Projekten, Sens selbst in TypeScript und Rust sowie die Python-Bibliothek click bei festen Commits, jede mit versteckten Tests und einer Referenzlösung geprüft. Drei Bedingungen mit demselben Modell: Claude Code allein (C0), der Kanon als Text im Systemprompt ohne Kreislauf (C1) und Sens vollständig (C2).
| Messgröße | C0 · allein | C1 · Kanon als Text | C2 · Sens |
|---|---|---|---|
| Gültige Läufe | 32/36 | 31/36 | 67/72 |
| Läufe mit neuen Tests | 23/36 | 36/36 | 72/72 |
| Nutzt plain, fern der Änderung | 0/3 | 3/3 | 6/6 |
| Nutzt titleOf, fern der Änderung | 1/3 | 1/3 | 6/6 |
| Löst py-progress-final | 0/3 | 0/3 | 3/6 |
Der Text allein erreicht schon viel Wiederverwendung, wenn die Hilfsfunktion nahe liegt. Nicht erreicht er die Fälle, in denen sie weit weg liegt und anders heißt, titleOf, 1 von 3 gegenüber 6 von 6, und auch nicht die Aufgabe, die eine gemeinsame Ursache statt eines einzelnen Pfads zu beheben verlangt, py-progress-final, 0 von 3 gegenüber 3 von 6. Bei einzelnen Aufgaben sind Codezeilen Rauschen: C2 schreibt etwa zwei Zeilen weniger pro Aufgabe, aber das Intervall berührt die Null. Wegen dieser Schwankung gibt es Horizonte.
Läufe, die mindestens eine Testzeile hinzufügten
Mit Kanon 1.0 schrieb der Agent fast keine Tests mehr: Er las „tu, worum gebeten wird, und nicht mehr“ als Verbot und hielt die Freigabe von Sens für einen Testlauf. Kanon 1.1 sagt beides, was fehlte: Ein Test, der die Änderung beweist, gehört zur Änderung, und die Freigabe von Sens ist kein Testlauf. Claude Code allein bekommt den Kanon nicht; seine Balken sind die Basis jedes Durchlaufs.
Die Regeln
Die Änderungsregeln sind deterministisch. Sie vergleichen Fingerabdrücke jeder Funktion, Methode und Klasse und jeder Folge von vier Anweisungen, erstellt von einem Rust-Index auf tree-sitter, der das Projekt im Speicher hält: Das Repository von Sens selbst, 556 Dateien und 10.000 Einheiten, ist in unter zwei Sekunden indiziert, und die Suche nach Kopien einer Einheit dauert etwa eine Mikrosekunde. Exakte Kopien und solche mit umbenannten Namen werden per Hash erkannt, Kopien mit hinzugefügten oder entfernten Zeilen per MinHash über normalisierte Tokens. Die Schwelle von 0,80 und die Untergrenze von 80 Tokens fürs Blockieren stammen aus der Bearbeitung von 400 Funktionen eines echten Repositorys und der Prüfung jedes Treffers von Hand.
| Regel | Erkennt | Antwort |
|---|---|---|
| R1 Wiederverwenden | Eine neue Funktion, Methode oder Klasse mit demselben Fingerabdruck vom Typ 1 oder 2 wie eine vorhandene | Blockiert ab 80 Tokens; darunter soll Claude noch einmal nachdenken |
| R2 Beinahe-Kopie | Ähnlichkeit vom Typ 3 über der Schwelle oder eine kleine Funktion, die einer anderen in Form und Wortschatz gleicht | Wie R1; in Tests ein Hinweis |
| R3 Neue Abhängigkeit | Ein Manifest bekommt eine Abhängigkeit, in zehn Formaten | Fragt dich |
| R4 Verwaiste | Ein neues Symbol, das nichts erreicht, oder ein vorhandenes, das der Durchgang ungenutzt ließ | Blockiert, wenn intern; Hinweis, wenn exportiert |
| R6 Projektregeln | Regeln, die du festlegst; die erste: keine Kommentare | Blockiert |
| R7 Integrität | Schreiben in .sens/, .git/, .claude/settings*.json oder .mcp.json oder git worktree | Blockiert immer; wiederhergestellt, wenn es über das Terminal kam |
| R8 Geschützte Tests | Der Durchgang entfernt Tests oder Assertions, die Sens schon freigegeben hatte | Fragt dich |
Regeln sehen keine Urteilsfehler. Dafür liest ein Prüfer, wenn ein Durchgang mit Codeänderungen die Regeln besteht, den Diff zusammen mit den Kandidaten des Index. Seiner Ausgabe wird nicht blind vertraut: Jeder Befund, dessen Zitat nicht wörtlich im Diff steht, wird verworfen, und nur hohe Sicherheit blockiert.
| Hinweis | Erkennt |
|---|---|
| S1 | Eine Abstraktion ohne zweite Verwendung |
| S2 | Eine Korrektur am Symptom statt an der Ursache |
| S3 | Nachbauen, was die Plattform oder eine Abhängigkeit bietet |
| S4 | Spekulation: Optionen oder Zweige, um die niemand gebeten hat |
| S5 | Raffiniert, wo das Offensichtliche gereicht hätte |
| S6 | Ein gefährlicher Schnitt: Validierung, Fehlerbehandlung oder Sicherheit entfernt |
| S7 | Nachbauen, was das Projekt schon hat, mit Verweis auf einen Kandidaten |
Was nicht funktioniert hat
Jede Blockade des Kreislaufs wurde von Hand geprüft, mit Diff und Gespräch. Blockaden sind selten, sieben in 228 Läufen von Sens, daher wiegt eine einzige unberechtigte schwer. Unser Ziel waren unter 5 % unberechtigte Blockaden; in keinem Durchlauf mit Blockaden haben wir es erreicht. Jede unberechtigte hatte eine konkrete Ursache, die jetzt mit einem Test behoben ist, der sie festhält.
| Durchlauf | Blockaden | Unberechtigt | Ursache | Behebung |
|---|---|---|---|---|
| Schwere Aufgaben, C2 v3 | 1 | 0, 1 strittig | Der Prüfer meldete eine Redewendung, die das Projekt wiederholt | Ein S7 zu etwas Privatem ist nur ein Hinweis |
| Kalibrierung, C2 | 2 | 2 | R8 verglich mit der Datei vor jedem Schreiben | R8 vergleicht mit dem zuletzt freigegebenen Stand |
| Kalibrierung, C2 nach der Behebung | 0 | 0 | — | — |
| Horizonte, Pilot | 1 | 0 | — | — |
| Horizonte, Bestätigung | 3 | 1 | R8 hielt eine Hilfsfunktion in einer Testdatei für einen Test | Als Test zählt nur, was etwas prüft |
Eine falsche Liste ist schlimmer als keine. Die erste Version von Sens schlug acht Symbole ohne Bezug zur Anfrage vor; das Modell las sie, suchte nicht weiter und schrieb die Akzent-Hilfsfunktion dreimal von Hand neu, während der Kanon als Text ohne Liste sie dreimal importierte. Mit der neu gebauten Suche erscheint die Funktion unter den Vorschlägen, und Sens nutzt sie jedes Mal, ohne etwas zu blockieren.
Grenzen
- Ein Modell. Alle Läufe nutzten Claude Sonnet 5.5 bei mittlerem Aufwand.
- Ein Projekt, eine Sprache, drei Sequenzen pro Arm. Das Bestätigungskriterium ist streng, alle unter allen, aber die Größe des Effekts hat ein breites Intervall.
- Wir haben die Aufgaben geschrieben. Damit sie das Ergebnis nicht verzerren, wurden Aufgaben, Tests und Referenz vor dem ersten Lauf committet und das Kriterium vor der Messung festgelegt.
- Keine MCP-Werkzeuge im Benchmark. Sens wurde ohne die Index-Abfragen gemessen, die die App bietet; das Ergebnis ist eine Untergrenze.
- Neunzehn Sprachen noch nicht im Benchmark. Vue, Svelte und die später hinzugekommenen Sprachen sind durch Tests abgedeckt, nicht durch Agentenläufe.
- Tokens enthalten Cache-Lesezugriffe. Sie messen den Arbeitsumfang, nicht die genauen Kosten.
- Der Prüfer ist ungenau. Von den sieben eigenen Hinweisen und Blockaden, die wir geprüft haben, waren fünf falsch. Seine Hinweise stoppen nichts, erreichen aber das Modell und dich.
- Ungeschriebene Konventionen. Sens kennt die impliziten Regeln eines Projekts nicht, etwa schwere Importe innerhalb einer Funktion zu lassen.
Methode
456 Agentenläufe in fünf Durchläufen: ein Pilot, drei schwere Aufgaben an Sens selbst, zwölf Kalibrierungsaufgaben sowie Pilot und Bestätigung von Horizonte. Alle Bedingungen nutzten claude-sonnet-5-5 bei mittlerem Aufwand, mit Claude Code im --safe-mode, damit die eigene Konfiguration des Autors nicht in die Läufe gelangt. Jede Aufgabe wurde vor der Nutzung geprüft: Zu Beginn bestehen die Projekttests und die versteckten schlagen fehl, mit der Referenz bestehen beide. Eine Regression muss zweimal hintereinander fehlschlagen, um zu zählen. Unterschiede sind Mediane mit einem 95-%-Bootstrap-Intervall, 10.000 Stichproben mit festem Seed; das Kriterium von Horizonte ist ein exakter Permutationstest.
Selbst nachmessen
sens-bench validate --tasks bench/tasks
sens-bench run --tasks bench/tasks --condition C0,C1,C2 --reps 3 --out bench/results/<batch>
sens-bench sequence validate bench/sequences/cuentas
sens-bench sequence run bench/sequences/cuentas --condition C0,C2 --reps 3 --out bench/results/<batch>
sens-bench sequence report bench/results/<batch>Die Daten jedes Laufs, sein Diff und die Aufgaben liegen im Ordner bench/ des Sens-Repositorys.
Der Kanon
Der Text, den jede Sitzung erhält, Wort für Wort, auf Englisch, wie das Modell ihn liest. Erst der Kreislauf macht ihn zu mehr als einem Ratschlag.
# Sens Canon v1.1
You are working inside Sens. Sens indexes this project and judges every change you make before your turn can end. What Sens tells you about this project, in its messages, denials and reviews, is a fact about the code, not a suggestion. When Sens names something to reuse, reuse it.
## Before you write
Go down this ladder and stop at the first step that answers the need:
1. Is it needed? Do what the person asked and nothing more: no speculative options, parameters, flags or branches. A test that proves the change is part of the change, not something extra.
2. Does the project already have it? Reuse the existing function, component, type or constant. Ask Sens with `already_exists` or `find_symbol` when unsure.
3. Does the standard library or the platform give it? Use that.
4. Does an installed dependency give it? Use that. A new dependency needs the person's approval, and Sens asks them for it.
5. Only then write new code: the smallest version that is correct.
## While you write
- Fix the cause in the shared code, not the symptom in each caller.
- No abstraction without a second real use: no interface, factory, wrapper, layer or configuration for a single consumer.
- Boring over clever. Match the names, patterns and style of the code around you.
- If you would copy a block, extract it once and call it from both places.
- Delete what your change leaves unused.
## Never cut
Less code never means removing validation at trust boundaries, error handling that prevents data loss, security checks, accessibility, or anything the person asked for.
It never means skipping tests either. When your change alters behaviour and the project has tests, add or extend one that fails without your change, in the style of the tests around it, and run the tests you touched before you finish.
## Working with Sens
- A denied write comes with the reason and what to use instead. Change the approach. Retrying the same thing through the shell, another tool or a subagent does not help: Sens judges what lands on disk, however it got there.
- When you finish, Sens audits the whole turn. If it blocks, fix what it found and finish again.
- Sens judges the shape of the code, not whether it works. Its approval is not a test run: that part is yours.
- Never edit `.sens/`, `.claude/settings*.json` or `.mcp.json`.