Spring til indhold
DApks-cli-toolboxbrain

To deterministiske brain-tools på én session — pks brain scan og commit-plan

Vi byggede to nye pks brain kommandoer på samme aften: en der finder sessions ud fra filer (deterministisk), og en der grupperer ucommittede filer og henter de prompts brugeren faktisk skrev. AI-tokens til syntese, jq til discovery.

17 tomme feature-filer i .pks/brain/features/. 5522 session-JSONL-filer i ~/.claude/projects/. Og ét spørgsmål jeg sad og stillede mig selv:

hvilke af mine 5522 session-jsonl-filer har faktisk redigeret den her fil?

Den slags spørgsmål er der to måder at svare på. Den dyre måde: fyr AI på alle 5522 filer, lad den læse dem, og spørg pænt om den vil sige hvilke sessions der betyder noget. Det er den måde alle mine værktøjer indtil videre havde svaret den slags spørgsmål — fordi det er den nemme måde at skrive et tool på. Du har en LLM. Du putter ting ind. Du får ting ud.

Den billige måde: jq. Hver session er en JSONL-fil. Tool-calls er strukturerede. Filer der bliver redigeret står i tool_input.file_path. Det er bare en søgning. Ingen tokens. Ingen ventetid. Ingen tvivl om at svaret er rigtigt fordi koden er deterministisk.

Posten her handler om at vi byggede begge topks brain scan filepath der kortlægger session→fil-kanter deterministisk, og pks brain commit-plan der grupperer ucommittede filer og henter de prompts der lå bag dem — på samme aften, og at den anden faktisk fangede den førstes egen fødsels-session i sin egen graf. Rekursiv self-discovery, hvis du vil have det fancy.

Den vigtigere pointe er arbejdsdelingen: AI-tokens til syntese, deterministisk kode til discovery. Det er en regel jeg har skrevet i mange varianter før, men det her var første gang den blev konkret nok til at skære helt igennem hvordan tool'et var bygget.

Den første prompt — »lets just implement the tool in pks cli«

Jeg sad i en lang session om at hælde struktur på pks-cli med product-cli. Halvvejs igennem skiftede vi gear. Min besked:

i think we should just materialize the 17 empty files with frontmatter and then we can spawn seperate childs for each to find relevant sessions that had tool call that edited those files? this is where i think it make sense that we use deterministisk code where pks brain scan filepath --session-files and it simply scan all the 6000 files for toolcalls that edit that file or shell commands where filename is included so we dont wait ai tokens on parsing all sessions. So we can start constructing the graph of sessions that are relevant for the given file?

Jeg fik et forslag tilbage: »vi kan lave det med bash og jq«. Det var ikke det jeg ville:

min prompttmux child: brain-scan-impl
1 lines2026-05-24 21:05
but instead of doing it in bash and such, lets just implement the tool in pks cli so we can use that and then we know it works also when done
Bygget af et tmux-spawnet barn — overvåget herfra, tick for tick.
3 værktøjer26K tok6 min

Det er den her sætning der gør forskellen. En bash one-liner er fin til at afprøve idéen. Men et tool i pks-cli er noget jeg kan stole på — det har test, det har en kontrakt, det opfører sig ens i alle mine devcontainere, og det er en kommando jeg kan dokumentere i en skill og sige til en agent: »brug den her, ikke noget bash«.

Forskellen mellem en bash-snippet og en command i CLI'en er ikke størrelsen af koden. Det er antallet af steder hvor det kan gå galt før jeg opdager det.

Hvad scan filepath faktisk gør

pks brain scan filepath ./Commands/Brain/ --format text

Den kører rg (ripgrep) som prefilter for at undgå at læse 5522 filer ind i memory. For hver match parses JSONL'en linje for linje, og for hver tool-call der rører filen (Edit, Write, Read, eller en Bash der har filnavnet i sin command) emitteres en kant: Session → ToolCall → File. Det er ren strukturparsing. Ingen LLM.

Smoke-test fra rod-folderen:

41 edge(s) across 2 session(s) across 5522 JSONL(s) scanned.

Den tager millisekunder. Den siger præcis hvilke sessions der har rørt den her fil. Hvis det er »alle 5522« så er det fordi mit prefilter er for løst og det skal fixes — det er ikke fordi modellen tænkte forkert.

Det er den slags fejl jeg har lyst til at have i mit liv. Ikke den slags hvor LLM'en glemte halvdelen af mine 5522 filer.

Den anden idé kom under commit

Først efter scan filepath var grøn, kom den næste prompt:

would we not just commit the relevant files for this feature now and then use it for each file in the submodule that is not staged to see why it was made or changed and then maybe do the command to take a list of files and it group them based on if they was made in the same session files and such?

Det er det samme greb, en oktav op. Hvis jeg kan finde sessions ud fra en fil, kan jeg også vende det om: tag en bunke ucommittede filer, gruppér dem efter hvilken session der skabte dem, og brug det som basis for commits.

Algoritmen blev en greedy set-cover. Find den session der dækker flest ucommittede filer. Claim dem. Gentag indtil alle er dækket. Hver gruppe = én commit, fordi filerne hører til samme stykke arbejde.

Første gang vi dogfoodede den på pks-clis egne ucommittede filer var resultatet alarmerende godt — og lidt for godt:

That's gold. Session 02f82b87 is huge (7838 entries) and covers things like cross-stream broadcast, ALP business line import + broadcast toggle, and agent-inbox migration. Those prompts tell us intent; the file diff tells us mechanics. Together → good commit message.

But notice: session 02f82b87 is so big it's likely touched those 5 files in service of OTHER work, not authored the current uncommitted diff. The "most files touched" heuristic overweights long-lived sessions.

Det var her jeg skulle have stoppet og smilet over hvor smart værktøjet var. Det gjorde jeg ikke. Jeg fangede præcis det her: en gigantisk catch-all session med 7838 entries der havde rørt alt fik æren for alle grupperne, bare fordi den havde flest tool-calls. Det er klassisk set-cover bias — den længste session vinder altid det første greedy-skridt.

Det er ikke fordi koden var forkert. Det er fordi heuristikken var forkert.

Patch én: skift fra »most touched« til »last edit author«

Forslaget jeg fik tilbage var rent:

Switch the heuristic from "touched most files" to "last-edit-author per file, aggregated" — more accurate for uncommitted work

Tankegangen: for ucommitted filer er det interessante »hvem skrev den her version« — ikke »hvem har rørt filen mest gennem tiden«. Last edit author er sessionen der lavede den seneste write/edit før commit. For hver fil. Aggregér op. Gruppér.

Den lavpraktiske patch var en omskrivning af planneren. Test først, så implementation. Da det var bygget, kørte vi det igen mod de samme ucommittede filer. Resultatet:

GruppeFilerSession (forfatter)Hvad sessionen gjorde
14 filer2bf720b7Tilføj --include-prompts + last-edit heuristic til commit-plan
24 filer (BrainCommitPlan*)2bf720b7 (this session)De just-created Commit-plan filer

Læg mærke til: gruppe 1 indeholder commit-plan værktøjets egen kode. Sessionen 2bf720b7 er den der byggede den. Værktøjet finder sin egen forfatter. Det er ikke magi, det er bare deterministisk struktur, men der er noget tilfredsstillende ved at se rekursionen lukke.

Den gamle heuristik havde sat 02f82b87 (7838-entry catch-all) som primær for alt. Den nye heuristik degraderede den korrekt til »Contributing« — fordi den havde rørt filerne, men den havde ikke skrevet dem sidst.

Patch to: »tag også prompts med«

Jeg sad og kiggede på output og tænkte: groups er fede, men de er bare lister af filer. Hvor er intent? Det blev til:

min prompt
1 lines2026-05-24 21:39
we would like to do nice commit messages based on the session and prompts, so how does this play along, does the commit-plan also find the prompts that was done prior to the tool calls that edited/wrote teh file? so commit-plan can be used to gather information prior of committing? or whats the recomended step here?
1 værktøj12K tok

Svaret skulle ikke være »lad orkestratoren prompte en agent der læser sessions og kommer tilbage med commit-beskeder«. Det skulle være: tag user-prompts med ud direkte. Deterministisk. Så har orkestratoren materiale at arbejde med, og kun selve formuleringen af commit-message er AI.

Flaget hedder --include-prompts. For hver gruppe henter den de user-prompts der lå inden de relevante tool-calls i den vindende sessions vindue. Ikke alle 7838 entries — kun dem der hører til de filer. Output bliver noget i retning af:

Group 1 (4 files) — Session 2bf720b7
  Prompts (excerpt):
    • "switch the heuristic from touched-most to last-edit-author"
    • "we would like to do nice commit messages based on the session and prompts"
  Files:
    • BrainCommitPlanCommand.cs
    • BrainCommitPlanner.cs
    • IBrainCommitPlanner.cs
    • BrainCommitPlanTests.cs

Orkestratoren får filer + prompts + intent uden at have læst en eneste session-jsonl. Den eneste AI-token der bliver brugt er den der formulerer commit-message-strengen. Og den er billig — det er bare en sætning.

Hvor extracten næsten gik galt

Da vi kørte den dogfooded på alle aktuelle ucommittede filer fangede vi én sjov bug — eller rettere, ikke en bug, et artefakt:

Group 5 (GitProxy): 808c2e3d — (auth-prompt content was the most recent user-msg; functional but noisy)

Prompts plausibly explain the file changes in groups 2–4. Group 5's prompt is a captured auth-pause banner that happens to live as a "user" message — a known artifact of how Claude Code records control flow, not a bug in the extractor.

Claude Code logger nogle gange auth-prompts og lignende kontrol-flow som »user« messages. Hvis du blindt tager den seneste user-message risikerer du at få »please complete authentication« som din commit-message. Vi noterede det, planlagde en filter-patch til en senere release, og lod det stå — det er bedre at vide artefaktet eksisterer end at skjule det med for tidlig oprydning.

Den deterministiske/AI-fri faktorering

Det her er den større pointe der står tilbage efter aftenen. To kommandoer, samme princip:

Hvert tool i pks-cli kommer til at have den her opdeling. Du vil ikke bruge en LLM på »find filer der hedder noget med Brain« når find kan det på 4 ms. Du vil ikke bruge en LLM på »find sessions der har edited den her fil« når jq kan det. Du vil bruge LLM'en der hvor den faktisk tænker — på sproget.

Det skifter også hvad mine subagent-orkestrationer ligner. Tidligere var et typisk fan-out »spawn 17 child-claudes, hver læser én feature-fil ved at scanne sessions«. Nu er det »kør pks brain scan filepath 17 gange (deterministisk, hurtigt, gratis), saml resultatet, og spawn så én child-claude der får hele materialet og skriver feature-beskrivelserne«.

Det er billigere. Det er hurtigere. Og det er reproducerbart — hvis jeg kører discovery-trinnet igen i morgen får jeg samme svar. Den slags determinisme savner jeg efterhånden i resten af mit agentiske setup.

Tests passerede første gang. Det er ikke et godt tegn.

dotnet test --filter "FullyQualifiedName~BrainScanFilepathTests": Passed 5/5, 84ms.

Jeg er længe ophørt med at glæde mig over »tests passerede første gang«. Det betyder mest at jeg har skrevet testene ud fra samme misforståelse som koden. Det er først når jeg dogfooder mod den faktiske 5522-fils projekt-folder at jeg ved om det virker — og der havde vi netop set en heuristic-bias som tests aldrig ville have fanget, fordi mine testdata var pænere end virkeligheden.

Det er derfor jeg insisterer på at bygge tools i pks-cli og bruge dem fra første dag mod rigtige data. Hvis du ikke kan dogfood et tool på den maskine du sidder ved, er det enten det forkerte tool eller det forkerte design.

Hvad der kommer næste

Begge kommandoer er på main. De er små. De er hurtige. De har test der dækker det de skal dække, og en kendt artefakt-grænse jeg vil rydde op i senere.

Næste skridt — som er en separat session og en separat blogpost — er at bruge commit-plan --include-prompts til at lave de faktiske commits af alle de ucommittede filer i denne devcontainer (der er en del). Det er den dogfooding der lukker loopet: tool blev født her, og bruges nu til at committe sin egen fødsel ind i historikken.

Hvis den oplevelse er lige så ren som dagens skal jeg revidere hvor mange af mine andre AI-værktøjer der egentlig burde være deterministiske kommandoer i stedet.

Dette er del 2 af serien Værktøjskassen pks-cli(kommer snart).

Denne post er blevet revideret 2 gange — se hele historikken
  1. Første udkast; først Opus-reviewet 10. juni 2026 (score 64) — calque-pass og number-first hook fulgte i v1.

  2. Første Opus-review (score 64, Terminology 2/5) pegede på uoversat udviklerjargon: 'der matter', 'fik kredit', 'sårbar overfor', 'pre-mature cleanup', 'synthese' — alle fordansket. Hook gjort number-first (17 tomme feature-filer / 5522 session-filer). Død link til ikke-eksisterende opfølger fjernet.

  3. De to pivot-citater opgraderet fra blockquotes til sessionstory-blokke fra session de4200b6 (24. maj): tmux-childet brain-scan-impl der byggede scan filepath (monitor-ticks verbatim), og 'nice commit messages'-prompten med commit-plan-gap-analysen.