feat(m2): add sak skill with case templates

This commit is contained in:
Kjell Tore Guttormsen 2026-09-05 21:55:37 +02:00
commit 4496c19045
4 changed files with 556 additions and 0 deletions

133
skills/sak/SKILL.md Normal file
View file

@ -0,0 +1,133 @@
---
name: sak
description: "Oppretter og vedlikeholder saksmapper i arbeidsmappa: sak-id, saksfila fra malen, en tom kontakter.md og den append-only hendelsesloggen. Delegerer hvert statusspoersmaal til statusmaskinen og utleder aldri status i prosa. Utloeses av opprett en sak, lag sak for denne stillingen, hva er status på saken, hva er status pa saken, legg til en hendelse i saken og oppdater saksmappa."
triggers:
- opprett en sak
- lag sak for denne stillingen
- hva er status på saken
- hva er status pa saken
- legg til en hendelse i saken
- oppdater saksmappa
---
# sak
Eier `saker/<sak-id>/` i arbeidsmappa. Skriver saksfila, den tomme
`kontakter.md` og hendelsesloggen — og **utleder aldri status selv**.
Statusmaskinen er et script, ikke en vurdering.
All operatørrettet tekst er norsk bokmål.
## Løs opp arbeidsmappa først
Samme rekkefølge som overalt ellers: `--workspace`, så `JOBBSOK_WORKSPACE`, så
en `workspace:`-linje i `jobbsok.conf` under plugin-datamappa. Svarer ingen av
de tre, si det og be om stien. Ingen hjemmekatalog gjettes.
## Sak-id
Formatet er `YYYY-MM-<arbeidsgiver-slug>-<rolle-slug>`, og året og måneden er
den måneden saken opprettes. Bygg den aldri for hånd — kall `sak_id(...)` fra
`${CLAUDE_PLUGIN_ROOT}/scripts/jobbsok_lib/paths.py`. Den folder `æ`, `ø` og
`å` til ASCII og normaliserer først, slik at et navn skrevet dekomponert på
macOS ikke blir en annen mappe enn det samme navnet skrevet komponert.
Finnes id-en fra før, er det samme sak. En sak som starter på nytt er en **ny**
sak med ny id — statusmaskinen nekter å åpne en avsluttet sak igjen.
## Opprett saken
1. Lag `saker/<sak-id>/`.
2. Skriv `sak.md` fra `${CLAUDE_PLUGIN_ROOT}/templates/sak.md`. Bytt ut hver
`{{...}}`-plassholder. Det som ikke er kjent, skrives `null` — aldri et
gjett, og aldri en tom verdi.
3. Lag en tom `kontakter.md`. Den er tom med vilje: den fylles av
`korrespondanse` og tømmes av `datahygiene`.
4. Lag `logg.jsonl` og legg til `opprettet` som første hendelse.
5. Kjør oppdateringen under.
Frontmatteren er kontrakten fra build-brief 5.2 og har alle tretten nøklene,
også de som er `null`. `status` og `ventende_part` står som `vurderer` og `meg`
i malen fordi det er nøyaktig det en sak med bare `opprettet` utleder — ikke
fordi de er skrevet av skjønn.
Kroppen er operatørens. Fyll «Hvorfor denne» fra beslutningens begrunnelse, ikke
fra annonsens egen selvbeskrivelse.
## Hendelsesloggen
`logg.jsonl` er append-only. En retting er en **ny** linje, aldri en redigering
av en gammel.
Én hendelse er én linje:
```json
{"ts": "2026-09-03T12:00:00+02:00", "hendelse": "soknad_sendt", "kilde": "e-post", "ref": "soknad-v1.md", "notat": null}
```
`ts` bærer alltid UTC-forskyvning. Norsk lokaltid gjentar 02:30 den siste
søndagen i oktober, og uten forskyvningen er to hendelser en time fra hverandre
umulige å sortere i etterkant.
Vokabularet er lukket på elleve hendelser. Hele lista, tilstandene og de tre
stillhetsreglene står i
`${CLAUDE_PLUGIN_ROOT}/skills/sak/references/status.md`. Les den før du skriver
en hendelse du ikke har skrevet før. En hendelse utenfor lista blir avvist ved
navn når loggen leses — den blir ikke stille droppet.
Beslutningen `ja` speiles inn i saksloggen av `beslutning` som en
beslutnings-post, ikke som en hendelse. Uten den kommer saken aldri videre fra
`vurderer`.
## Etter hver tilføyde hendelse: oppdater hurtigbufferen
Dette steget hoppes aldri over:
```
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/sak_status.py \
--workspace <arbeidsmappa> --sak <sak-id> --oppdater
```
Den skriver tilbake nøyaktig fire nøkler i frontmatteren — `status`,
`ventende_part`, `sist_aktivitet` og `neste_frist` — og rører ingenting annet.
Frontmatteren er en hurtigbuffer over loggen; en hurtigbuffer ingen oppdaterer
er bare permanent avvik med bedre manerer.
En utledning som er tom overskriver ikke det som står der. `neste_frist` i
`vurderer` og `soker` er søknadsfristen, som er operatørens egne data og som
ingen maskin kan regne ut på nytt.
## Statusspørsmål besvares av scriptet
Spør operatøren om status, ventende part, frist eller stillhet, så **kjør
scriptet og gjengi det**. Ikke les loggen og resonner deg fram til et svar:
```
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/sak_status.py \
--workspace <arbeidsmappa> --today <YYYY-MM-DD> --format json
```
På Windows heter tolken `python`, ikke `python3`; alt annet i kommandoen er
likt.
`--check` svarer på et annet spørsmål: den avslutter med kode 1 hvis en
hurtigbuffer er uenig med loggen, og navngir saken. Er svaret 1, kjør
`--oppdater` og si hva som flyttet seg — ikke rett i `sak.md` for hånd.
Står det `ingen innkommende hendelse er registrert`, gjengi det som det er.
Det betyr ikke at ingen har svart; e-postserveren er utsatt, og fraværet av en
registrering er ikke et fravær i verden.
## Grenser
- Ingen overgang utledes av tonen i en e-post. En tilstand endrer seg på en
eksplisitt bekreftelse fra operatøren eller på et entydig strukturelt signal.
- Ingenting eskalerer, sendes eller lukkes av seg selv.
- Ingenting skrives utenfor arbeidsmappa.
## Uten verktøyserveren
Er `jobbsok-tools` ikke tilgjengelig, degraderer skillen i stedet for å stoppe:
be operatøren kjøre kommandoene over fra kommandolinja, og les svaret som ble
skrevet ut. Ikke regn ut status selv som en mellomløsning — da er den ikke
lenger reproduserbar, og hele poenget med å ha en statusmaskin er borte.

View file

@ -0,0 +1,89 @@
# Statusmaskinen: tilstander, hendelser og stillhet
Referanse for `sak`. Alt her er implementert i
`${CLAUDE_PLUGIN_ROOT}/scripts/sak_status.py` og utledes derfra — denne fila
beskriver, den bestemmer ikke. Er de to uenige, er scriptet fasit og uenigheten
en feil som skal meldes.
## Tilstander
Hovedkjeden, i rekkefølge:
| Tilstand | Betyr | Ventende part ved ankomst |
|---|---|---|
| `vurderer` | Saken finnes, beslutningen er ikke tatt | `meg` |
| `soker` | Beslutningen er `ja`, søknaden er ikke sendt | `meg` |
| `sendt` | Søknaden er sendt | `dem` |
| `dialog` | Det er en toveis samtale i gang | veksler |
| `intervju` | Intervju er avtalt eller gjennomført | `ingen` / `dem` |
| `tilbud` | Tilbud er mottatt | `meg` |
| `avsluttet` | Operatøren har lukket saken | `ingen` |
To terminale sidetilstander: `avslag` og `trukket`.
Ingenting går ut av en terminal tilstand. En hendelse etter `avsluttet`,
`avslag` eller `trukket` er en feil, ikke en gjenåpning: en sak som starter på
nytt er en ny sak med ny sak-id.
## Hendelser — lukket vokabular på elleve
Build-brief 5.3. Ingen tolvte finnes, og en hendelse utenfor lista blir avvist
ved navn når loggen leses.
| Hendelse | Skriver seg fra | Lander på |
|---|---|---|
| `opprettet` | saken lages | `vurderer` |
| `soknad_sendt` | søknaden er sendt | `sendt` |
| `bekreftelse_mottatt` | automatisk kvittering | `sendt` |
| `henvendelse_mottatt` | de tok kontakt | `dialog` |
| `svar_sendt` | operatøren svarte | `dialog` |
| `intervju_avtalt` | tidspunkt er satt | `intervju` |
| `intervju_gjennomfort` | intervjuet er holdt | `intervju` |
| `tilbud_mottatt` | tilbud foreligger | `tilbud` |
| `avslag` | de sa nei | `avslag` |
| `trukket` | operatøren trakk seg | `trukket` |
| `stille` | operatøren noterer at det er stille | ingen overgang |
`stille` flytter ingenting. Den er en notering, ikke en tilstandsendring, og
den legges bare til av operatøren — ingenting i denne pluginen skriver den av
seg selv.
## To poster som ikke er hendelser
To rader i overgangstabellen kommer ikke fra hendelses-enumen, og de skrives
med `type` i stedet for `hendelse`:
| Post | Form i saksloggen | Lander på |
|---|---|---|
| Beslutning `ja` | `{"type": "beslutning", "beslutning": "ja", ...}` | `soker` |
| Operatøren lukker | `{"type": "utfall", "utfall": "avsluttet", ...}` | `avsluttet` |
Begge er poster build-brief 5.7 uansett speiler inn i saksloggen. `beslutning`
skriver den første; uten den kommer ingen sak videre fra `vurderer`. En
`beslutning: nei` er ingen overgang — et nei skaper ingen sak.
## Stillhet: tre regler
Terskler i dager, målt mot den datoen som faktisk står i loggen:
| Flagg | Regel | Terskel |
|---|---|---|
| `sendt_14` | `sendt` uten noe innkommende | 14 dager |
| `dialog_7` | `dialog` med ventende part `dem` uten noe innkommende | 7 dager |
| `intervju_10` | intervju gjennomført uten utfall | 10 dager |
**En automatisk kvittering er ikke kontakt.** `bekreftelse_mottatt` teller ikke
som innkommende og nullstiller derfor ikke stillhetsklokka. Det er hele grunnen
til at den står utenfor lista over innkommende hendelser.
`neste_frist` som statusmaskinen utleder, er den tidligste terskeldatoen blant
reglene som gjelder nå — altså **datoen saken går stille**. I `vurderer` og
`soker` gjelder ingen regel, og da står operatørens egen søknadsfrist urørt i
frontmatteren.
## «Ingen innkommende registrert» er ikke «ingen har svart»
Rapporterer scriptet at ingen innkommende hendelse er registrert, er det et
utsagn om denne loggen og ikke om verden. E-postserveren er utsatt til M4, så
ingenting leser innboksen ennå. Gjengi merknaden som den er, og ikke presenter
saken som stille på grunnlag av den.