Repository navigation
Description
What exists
benchmarks/lib/longitudinal_runner.py drip-feeds N synthetic memories spread uniformly across the past 365 days, then probes old vs recent facts at wall-clock time. Its own docstring states the object of measurement: "how cleanly the decay-aware retrieval pipeline preserves recall as memories age" (lines 1-7). Ground truth is deterministic by construction — "the chosen number for memory <uid> is <hash(uid) % 10000>" (lines 8-12) — and the run uses a dedicated database that is dropped and recreated at start (lines 23-26).
That is a sound measurement of one component. It is not a measurement of the claim the product makes.
The gap
The claim is behavioural: a session with Cortex reaches a correct result faster, does not repeat an approach already known to fail, and does not re-derive a settled decision. Nothing in the suite measures that. Concretely, the existing harness:
- uses synthetic memories with synthetic probes, so a retrieval win cannot be shown to become a behavioural win;
- scores top-1 retrieval of a seeded fact — a proxy whose correlation with outcome is assumed, never measured;
- runs on a throwaway database, so it never meets the distribution of a real store (tool events, checkpoints, wiki pointers, superseded versions, conflicting facts).
What to build
A longitudinal outcome benchmark on a real repository.
- Corpus. A repo with genuine Cortex history rather than a seeded one.
- Tasks with checkable answers, drawn from that history and verifiable independently of Cortex: "which commit introduced behaviour X", "was approach Y tried, and what was the recorded reason it stopped", "what is the decided value of constant Z and its cited source".
- Two arms: agent with recall vs agent without. Same model, same effort level, same task set, same ordering.
- Score on outcome, not on retrieval: task correct/incorrect against the independent answer key, plus turns-to-answer and tokens spent.
- Repeat, and report the delta with a variance estimate. A single paired run is an anecdote, not a result.
Constraints
- Clean database, single process, reproducible across runs — the rule already applied to the other benchmarks.
- No tuned constant in the scoring rubric. Thresholds come from the task definitions or they do not exist.
- If the delta is null or negative, that is the result, and it is published as-is.
Why it is worth the effort
Every positioning statement about this project rests on an outcome claim, and the suite currently measures a component proxy instead. Until this exists, the honest statement is "retrieval degrades gracefully with age", which is a narrower claim than the one being made.
Activity
docs(changelog): record the outcome scoring slice of #343
cdeust commentedon Nov 4, 2026
CI autopilot : l'issue est trop large pour une seule PR. Elle est découpée en quatre tranches, et la tranche 1 est livrée dans #854 (
Refs #343).Tranche 1 (#854), le noyau de scoring. Elle couvre les étapes 4 et 5 de l'issue. Le module
benchmarks/outcome/paired_delta.pyapparie strictement les essaisrecall/no_recallpar (tâche, répétition). Pour la justesse, les tours et les tokens, il rapporte :Un essai sans contrepartie lève une erreur au lieu d'être ignoré.
Tranches restantes :
2. Schéma des tâches et vérificateur de réponse indépendant de Cortex, pour les trois familles de l'issue : commit qui introduit X, approche Y tentée et raison de l'arrêt, valeur décidée de Z avec sa source. Une question est à trancher : la raison d'arrêt se vérifie-t-elle par correspondance normalisée avec une liste de réponses acceptées, ou faut-il un juge ? L'issue interdit les seuils réglés.
3. Corpus : quel dépôt, avec un historique Cortex réel ? Décision du propriétaire.
4. Runner à deux bras : même modèle, même niveau d'effort, mêmes tâches, même ordre, base propre, un seul processus. Décision du propriétaire sur le modèle, l'effort et le budget de tokens par répétition.
Generated by Claude Code
fix(bench): address the claude[bot] review of paired outcome scoring
fix(bench): reject a non-int bootstrap seed instead of drawing from O…
feat(bench): paired two-arm outcome scoring for the longitudinal outc…
docs(changelog): record the outcome scoring slice of #343
fix(bench): address the claude[bot] review of paired outcome scoring
test(bench): pin the per-sweep std, task-level bootstrap and repeat-l…
fix(bench): reject a non-int bootstrap seed instead of drawing from O…
feat(bench): paired two-arm outcome scoring for the longitudinal outc…
docs(changelog): record the answer-key slice of #343
cdeust commentedon Nov 4, 2026
CI autopilot : la tranche 2 est livrée dans #858 (
Refs #343).Le module
benchmarks/outcome/answer_key.pydéfinit les tâches des trois familles et un vérificateur indépendant de Cortex ::ligne.Il n'y a aucun seuil de similarité. Pour la question laissée ouverte au commentaire précédent, cette PR retient donc la correspondance normalisée, la seule option sans seuil. Un juge resterait possible comme vérificateur supplémentaire, sur décision du propriétaire.
Tranches restantes, toutes deux en attente d'une décision du propriétaire :
3. Le corpus : quel dépôt porte un historique Cortex réel ?
4. Le runner à deux bras : quel modèle, quel niveau d'effort et quel budget de tokens par répétition ?
Generated by Claude Code
fixup! feat(bench): task definitions and Cortex-independent answer ve…
fixup! feat(bench): task definitions and Cortex-independent answer ve…
fixup! feat(bench): task definitions and Cortex-independent answer ve…
11 remaining items
feat(bench): task definitions and Cortex-independent answer verifier …
cdeust commentedon Nov 4, 2026
CI autopilot : issue mise en hold. Les tranches 1 (#854) et 2 (#858) sont fusionnées ; les deux tranches restantes demandent une décision du propriétaire.
Questions à trancher :
Dès qu'un commentaire de
cdeusttranche l'une des deux questions, l'autopilot lève le hold et livre la tranche correspondante.Generated by Claude Code
cdeust commentedon Nov 12, 2026
Décision : cette issue n'attend plus de réponse du propriétaire.
Le propriétaire a demandé le 12 novembre 2026 que les issues avancent sans lui ; il ne répondra pas aux questions posées ici. Ce commentaire de
cdeustlève le hold et précise la demande. Il vaut pour toute la suite de cette issue.docs/, CONTRIBUTING.md), la plus récente ; le texte d'une autre issue n'en est pas une ; (b) ce que le texte de cette issue propose ou exclut lui-même ; (c) les standards du dépôt ; une valeur qui manque se mesure, elle ne s'invente pas ; (d) à défaut, la plus petite tranche qui réalise ce que l'issue demande. « Documenter la limite et fermer » ou « sans objet » ne se retient que si (a) ou (b) le désigne et couvre tout ce que l'issue demande ; sinon traite ce qui reste. Une décision choisit parmi ce que l'issue demande, elle n'ajoute pas de périmètre.<!-- ci-autopilot decision -->: ce qui est retenu, la raison avec sa source, ce qui est écarté, comment revenir en arrière. Puis livre la tranche dans une PR. Une décision ainsi écrite vaut pour les passages suivants : elle s'applique sans être rejouée. Revérifie tout fait repris d'un ancien commentaire avant de t'en servir. Si tu juges l'issue déjà faite, vérifie chaque point surorigin/mainet écris la preuve ; ce qui reste se livre dans une PR qui porteCloses #<numéro>.autopilot:holdpour une question. Le hold reste permis, en nommant exactement ce qui manque, dans ces cas seulement : l'irréversible (suppression de branche, de dépôt ou de données, dépense d'argent, envoi d'un message externe, publication) ; un accès que tu n'as pas (matériel, système, secret, compte, permission du token) ; une dépendance nommée encore ouverte ; le plafond de tentatives de la règle anti-boucle.Écrit par la session « écosystème autonome » sur instruction du propriétaire (12 novembre 2026).
Generated by Claude Code
cdeust commentedon Nov 12, 2026
Hold
autopilot:holdlevé : un commentaire decdeust(« Décision : cette issue n'attend plus de réponse du propriétaire », 12/11/2026) a été posté après la pose du hold.Generated by Claude Code
cdeust commentedon Nov 12, 2026
Décision (tranche 3, le corpus).
cdeust/Cortexlui-même, épinglé au commit5205f6313601ca93057ec2f271afca92ee08938d(tête demainau 12/11/2026, 1930 commits). Les tâches sont tirées de cet historique pour les trois familles, et chaque tâche porte sa preuve, vérifiée contre le commit épinglé : chemin ajouté pour « quel commit a introduit X », message de commit pour « approche tentée et raison de l'arrêt », citation dans chaque fichier source pour « valeur décidée ». La copie figée est ungit clone --barequi contient ce commit, comme l'exige déjà le docstring decommit_resolver.# source:sur chaque constante). (d) La tranche reste la plus petite : un manifeste, un jeu de tâches et le contrôle de leurs preuves.CORPUS_PINet le fichier de tâches. Le contrôle des preuves refuse toute tâche que le nouveau pin ne justifie pas.Tranche restante : 4, le runner à deux bras.
Generated by Claude Code
cdeust commentedon Nov 12, 2026
CI autopilot : la tranche 3 (le corpus) est livrée dans #1010 (
Refs #343).Le corpus est l'historique de ce dépôt épinglé à
5205f631.benchmarks/outcome/cortex_tasks.jsonporte neuf tâches, trois par famille, etcheck_evidencevérifie chaque clé contre le commit épinglé : chemin ajouté par le commit clé, raison écrite dans le message de commit, citation de la valeur dans chaque source.Tranche restante : 4, le runner à deux bras (même modèle, même effort, mêmes tâches et même ordre ; copie en lecture seule pour le vérificateur ; instantané de la base mémoire du bras recall).
Generated by Claude Code
feat(bench): the Cortex corpus of the outcome benchmark and the evide…
cdeust commentedon Nov 12, 2026
Décision (tranche 4, le runner à deux bras).
benchmarks/outcome/two_arm_runner.py. Il prend une copie nue (git clone --bare --no-local --no-hardlinks) du corpus avant le premier essai, la met en lecture seule, y vérifie le pin et les clés de commit (check_commit_keys, comme l'exige le docstring decommit_resolver), puis lance pour chaque répétition le brasrecallpuis le brasno_recallsur les mêmes tâches, dans le même ordre, avec le même modèle et le même effort. L'agent est injecté (un appelable qui reçoit la tâche, le bras et la spécification) et doit rapporter le modèle et l'effort qui ont réellement servi : un écart lèveValueError, sans essai écarté en silence. Chaque réponse est notée paranswer_key.verifysur la copie, et le résultat est la liste deTrialOutcomequepaired_delta.summarizeattend.commit_resolver(tranche 2) fixe la copie en lecture seule prise avant l'agent. (d) La plus petite tranche autonome : l'orchestration et ses garanties se testent avec un agent factice, sans accès à un modèle.recall) : il dépend d'un accès modèle et de la base, c'est la tranche 5. La campagne de mesure et sa publication : tranche 6.two_arm_runner.pyet son test ; aucun autre module ne change.Generated by Claude Code
cdeust commentedon Nov 12, 2026
CI autopilot : la tranche 4 (le runner à deux bras) est dans #1011 (
Refs #343).benchmarks/outcome/two_arm_runner.pyprend une copie nue en lecture seule du corpus avant le premier essai, y vérifie le pin et les clés de commit, puis lance à chaque répétition le brasrecallpuis le brasno_recallsur les mêmes tâches, dans le même ordre, avec le même modèle et le même effort ; un essai servi par un autre modèle ou un autre effort arrête la course. Les réponses sont notées paranswer_key.verifyet donnent lesTrialOutcomedepaired_delta.summarize. 12 tests nouveaux, 151 testsoutcomeverts en local ; les commandesunitetregressiondeci.jsonn'ont pas pu tourner ici (téléchargement detorchrefusé par le réseau).Tranches restantes :
recall) ;Generated by Claude Code
feat(bench): the two-arm runner of the outcome benchmark (#343, slice…
fix(bench): snapshot_corpus reads relative paths from the caller's di…
cdeust commentedon Nov 12, 2026
CI autopilot : #343 mise en
autopilot:hold— la tranche 5 (pilote réel de l'agent) demande un accès que la routine n'a pas, et la tranche 6 une dépense.Ce qui manque exactement (vérifié le 2026-11-12 sur
origin/main@07dca218) :claude --bare -p … --output-format json, la forme recommandée pour les scripts). En mode--bare, Claude Code exigeANTHROPIC_API_KEY(docs headless, « Start faster with bare mode ») ; cette variable est absente de l'environnement de la routine. Lancer des sessions réelles est aussi une dépense d'argent, cas de hold permis par la décision du propriétaire.run_two_armsexige que l'agent rapporte l'effort qui a réellement servi (AgentResult.effort,_check_served). Le messageresultdeclaude -pn'a pas de champ d'effort, et le champeffortdu messagesystem/initn'est émis qu'aux clients Remote Control, pas à l'application qui lit la sortie (référence TypeScript du SDK,SDKResultMessageetSDKSystemMessage). Le modèle servi, lui, est mesurable (message.modelde chaque messageassistantdu fil principal,modelUsage). Rapporter l'effort demandé comme effort servi serait une valeur inventée ; la tranche 5 doit donc mesurer l'effort autrement (requête API observée) ou le runner doit être amendé par écrit.recall. Sa forme dépend de la façon dont la session lance le serveur Cortex (backend SQLite ou PostgreSQL géré, ADR-1114) ; sans session réelle, rien ne vérifie que la session lit bien l'instantané.Ce hold se lève dès qu'un de ces faits change (clé API fournie à l'environnement, ou décision écrite sur la mesure de l'effort).
Generated by Claude Code