---
inclusion: always
---

# NodeOS Udviklingsplatform — Steering Template

<!-- TILPAS: Erstat <DIT PROJEKT> med dit projekt-/organisationsnavn og gennemgå alle TILPAS-markeringer. -->

## Formål

<DIT PROJEKT> bruger **NodeOS Udviklingsplatform** (https://nodeos.dk/api/public/v1) som aktivt styringsværktøj for support, fejl, udviklingsønsker og tekniske ændringer.

AI-agenten (KIRO, Cursor, Claude Code, Codex m.fl.) skal altid oprette, opdatere og afslutte relevante sager i NodeOS, når der arbejdes i repoet.

**NodeOS er source of truth for:**

- Supportsager
- Fejl/bugs
- Udviklingsønsker
- Tekniske forbedringer
- Release-noter
- Beslutninger og status

## API-konfiguration

NodeOS API-nøglen må **aldrig** committes i repoet.

Environment variables (sættes i `.env` lokalt, i deploy-miljø via secrets):

```
NODEOS_API_KEY=nw_live_<hex>
NODEOS_API_BASE_URL=https://nodeos.dk
```

- **Base URL**: Brug altid `https://nodeos.dk` (apex domain). IKKE `www.nodeos.dk` — www laver 302-redirect og stripper `Authorization`-header.
- **Auth header**: `Authorization: Bearer nw_live_...`
- **Scope**: API-nøglen er scoped til én kunde/organisation. Ingen separat customer-ID påkrævet.
- **Versioning**: Alle endpoints er under `/api/public/v1/`. Responses inkluderer `X-API-Version: v1`.
- **Status/priority**: English aliases (`in_progress`, `done`, `high`, etc.) accepteres på write. Responses bruger altid danske enum-værdier.

**Hvis API-konfiguration mangler**, må KIRO ikke ignorere NodeOS-flowet. AI-agenten skal tydeligt skrive:

> NodeOS-opdatering kunne ikke udføres pga. manglende konfiguration (NODEOS_API_KEY ikke sat).

## Client/Helper

En Node.js-baseret NodeOS-client ligger i `scripts/nodeos-client.js`. Den bruges til alle interaktioner.

> **Bemærk:** Brug `.mjs` eller `.js` — se `README.md` i Agent Starter Kittet for detaljer om ESM-konfiguration.

```bash
# Introspect API-nøgle (scopes, org, areas)
node scripts/nodeos-client.js me

# List projekter/areas
node scripts/nodeos-client.js projects

# List opgaver (filtreret)
node scripts/nodeos-client.js tasks --status Ny
node scripts/nodeos-client.js tasks --status "Under udvikling"

# Hent enkelt opgave
node scripts/nodeos-client.js task <uuid>

# Completion Checklist (kvalitetsscore)
node scripts/nodeos-client.js completion <uuid>

# Kvalitetsoverblik (dashboard - alle sager med score)
node scripts/nodeos-client.js dashboard
node scripts/nodeos-client.js dashboard --status Released --limit 30

# Opret opgave (--area slår området op på navn, --parent gør den til subtask)
node scripts/nodeos-client.js create --title "..." --type bug --priority high --description "..." \
  --area "<OMRÅDE>" --parent <parent-uuid>

# Opret flere opgaver + relationer i ét kald (ref/parent_ref binder hierarkiet)
node scripts/nodeos-client.js bulk-create --file tasks.json --area "<OMRÅDE>"

# Opdater opgave
node scripts/nodeos-client.js update <uuid> --status in_progress --priority high

# Opdater kun status (convenience)
node scripts/nodeos-client.js status <uuid> done

# Tilføj kommentar
node scripts/nodeos-client.js comment <uuid> "Root cause fundet: ..."

# Luk opgave (sætter status til done + optional kommentar)
node scripts/nodeos-client.js close <uuid> --comment "Implementeret og verificeret"

# Tilføj commit/PR link til opgave
node scripts/nodeos-client.js link <uuid> --type commit --url "https://github.com/.../commit/abc123" --title "feat: ..."

# List links på opgave
node scripts/nodeos-client.js links <uuid>

# Fjern link
node scripts/nodeos-client.js unlink <uuid> <link-id>

# Vis status-historik (tidsstempler for hvert statusskift)
node scripts/nodeos-client.js history <uuid>

# List alle tags med antal
node scripts/nodeos-client.js tags

# Opret relation mellem to opgaver
node scripts/nodeos-client.js relate <uuid> <target-uuid> --type relates_to

# List relationer
node scripts/nodeos-client.js relations <uuid>

# Fjern relation
node scripts/nodeos-client.js unrelate <uuid> <relation-id>

# Healthcheck (ingen auth)
node scripts/nodeos-client.js health
```

### API-endpoints reference

| Metode | Endpoint                                            | Beskrivelse                                                                                                                          |
| ------ | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| GET    | `/api/public/health`                                | Healthcheck (ingen auth)                                                                                                             |
| GET    | `/api/public/v1/me`                                 | Introspect API-nøgle                                                                                                                 |
| GET    | `/api/public/v1/projects`                           | List areas                                                                                                                           |
| GET    | `/api/public/v1/tasks`                              | List tasks (query: status, area_id, limit, offset, tags, type, priority, created_after, created_before, search, search_field, exact) |
| POST   | `/api/public/v1/tasks`                              | Create task (inkl. `parent_item_id`)                                                                                                 |
| POST   | `/api/public/v1/tasks/bulk`                         | Bulk-create tasks + relationer (max 50/100)                                                                                          |
| GET    | `/api/public/v1/tasks/{id}`                         | Get task                                                                                                                             |
| PATCH  | `/api/public/v1/tasks/{id}`                         | Update task                                                                                                                          |
| PATCH  | `/api/public/v1/tasks/{id}/status`                  | Update status only                                                                                                                   |
| GET    | `/api/public/v1/tasks/{id}/comments`                | List comments                                                                                                                        |
| POST   | `/api/public/v1/tasks/{id}/comments`                | Add comment                                                                                                                          |
| GET    | `/api/public/v1/tasks/{id}/links`                   | List links (commit/PR/URL)                                                                                                           |
| POST   | `/api/public/v1/tasks/{id}/links`                   | Add link                                                                                                                             |
| DELETE | `/api/public/v1/tasks/{id}/links/{link_id}`         | Delete link                                                                                                                          |
| GET    | `/api/public/v1/tasks/{id}/status-history`          | Status transition history                                                                                                            |
| GET    | `/api/public/v1/tasks/{id}/relations`               | List relationer (`?direction=outgoing\|incoming\|all`)                                                                               |
| GET    | `/api/public/v1/tags`                               | All unique tags with count                                                                                                           |
| GET    | `/api/public/v1/tasks/{id}/relations`               | List relations                                                                                                                       |
| POST   | `/api/public/v1/tasks/{id}/relations`               | Create relation                                                                                                                      |
| DELETE | `/api/public/v1/tasks/{id}/relations/{relation_id}` | Delete relation                                                                                                                      |

### Danske enum-værdier (API-responses)

**Type:** `Fejl`, `Udviklingsønske`, `Ændringsønske`, `Beslutning`, `Teknisk opgave`, `Support`
**English write aliases:** `bug`, `feature`, `change`, `decision`, `task`, `support`

**Status:** `Ny`, `Afventer afklaring`, `Godkendt`, `Planlagt`, `Under udvikling`, `Klar til test`, `Godkendt af kunde`, `Released`, `Parkeret`, `Afvist`
**English write aliases:** `in_progress` → Under udvikling, `ready_for_review` → Klar til test, `done` → Released

**Priority:** `Lav`, `Medium`, `Høj`, `Kritisk`
**English write aliases:** `low`, `medium`, `high`, `critical`

**Roadmap bucket:** `Nu`, `Næste`, `Senere`, `Parkeret`

## Arbejdsregel for AI-agenten

### Det kanoniske arbejdsloop (ingen trin må springes over)

```text
me (identitet + scopes)
  ↓
doc-sources / doc-reviews  → er dokumentationen frisk? venter der et review?
  ↓
tasks / task               → læs den aktuelle udviklingskontekst
  ↓
memory "<query>"           → søg historik, når fortiden kan påvirke beslutningen
  ↓
create / update            → opret eller opdatér sagen
  ↓
session-start              → START ARBEJDSSESSION FØR IMPLEMENTERING
  ↓
implementér + event        → registrér meningsfulde work events
  ↓
session-complete           → --summary + --outcome
  ↓
evidence-add               → indsend leveringsevidens (commit, PR, build, test)
  ↓
evidence-get / evidence-supersede → læs egen evidens tilbage, erstat ved behov
  ↓
(menneske verificerer — agenten kan aldrig selv verificere)
  ↓
status → Released          → Released = leveret
  ↓
doc-review-assess          → behandl dokumentationsreview ved kontraktændring
```

Den gamle løkke "hent sag → implementér → kommentér → skift status" er
**ikke længere gyldig**. Kommentarer er ikke evidens.

> **Development Memory:** slå op i `memory "<query>"` **før** arkitektur-,
> sikkerheds-, tenancy- eller rettighedsbeslutninger, og når problemet lyder
> bekendt. Dokumentation fortæller, hvordan systemet skal fungere nu; memory
> fortæller, hvordan og hvorfor tidligere arbejde skete. Ved uenighed vinder
> den aktuelle autoritative dokumentation. Citér `source_type` +
> `canonical_path` i sessionen eller evidensen.

Når KIRO får en supportsag, fejlrapport, udviklingsønske eller teknisk opgave, skal KIRO **altid**:

### 1. Søg først

```
NodeOS-check:
- Henter opgaver fra NodeOS (list tasks, eventuelt filtreret på status/area)
- Scanner titler og beskrivelser for match med aktuel opgave
- Vurderer om dette er ny sag eller opdatering af eksisterende
```

> **Søgning:** Brug `GET /api/public/v1/tasks?search=<term>` — server-side
> case-insensitive substring match på titel, description og exact tag match.
> Til dublet-tjek: tilføj `&search_field=title` (kun titel) og evt. `&exact=true`
> (hele titlen skal matche) for at undgå brede, irrelevante hits.
> CLI: `search "<term>" --search-field title --exact`.

### 2. Vurdér dublet

Før oprettelse skal KIRO søge efter:

- Samme brugerproblem
- Samme feature
- Samme route/component/API
- Samme fejlbesked
- Samme område i platformen

**Dubletregel:** Hvis en eksisterende sag matcher 70% eller mere, skal den **opdateres** i stedet for at oprette ny.

### 3. Opret ny sag (hvis ingen match)

Ny sag oprettes med:

- `title`: Kort, præcis titel (max 500 tegn)
- `description`: Klar problemformulering (teknisk fritekst, bagudkompatibelt)
- `type`: `bug`, `feature`, `change`, `decision`, `task`, `support`
- `priority`: `low`, `medium`, `high`, `critical`
- `tags`: Relevante labels (max 20, fx `["auth", "referee", "kiro"]`)
- `area_id`: Projekt/area UUID (hent via `projects`-endpoint)
- `start_date` / `deadline`: Datoer i YYYY-MM-DD format, hvis relevant
- `customer_summary`: Kundevendt problembeskrivelse (se §6)
- `customer_solution`: Kundevendt løsningsbeskrivelse (se §6)
- `customer_value`: Kundevendt værdibeskrivelse (se §6)
- `release_note`: Changelog-linje til Leverancer-siden (se §6)
- `technical_notes`: Intern teknisk analyse (se §6)
- `payment_responsibility`: Betales af (se §7)
- `billable_status`: Faktureringsstatus (se §7)
- `reporter_name`: Indmelders navn (se §7)
- `reporter_email`: Indmelders email (se §7)
- `reporter_channel`: Kanal (`email`, `phone`, `portal`, `other`) (se §7)
- `roadmap_bucket`: Roadmap-placering (`Nu`, `Næste`, `Senere`, `Parkeret`) — kun hvis klart

### 4. Opdater under arbejdet

AI-agenten skal opdatere sagen ved væsentlige trin (brug English alias på write):

- Oprettet med status `Ny` (default)
- `in_progress` → Under udvikling (analyse eller implementering påbegyndt)
- `ready_for_review` → Klar til test
- `done` → Released (verificeret og afsluttet)

Andre statusser der kan bruges:

- `Afventer afklaring` — venter på svar fra bruger/team
- `Planlagt` — godkendt, planlagt til sprint
- `Parkeret` — udskudt til senere
- `Afvist` — lukket uden implementering

### 5. Afslut med implementeringsrapport

En sag må kun afsluttes, når KIRO har skrevet en afsluttende kommentar:

```
Implementeringsrapport:
- Problem: [kort beskrivelse]
- Root cause: [teknisk årsag]
- Løsning: [hvad blev gjort]
- Ændrede filer: [liste]
- Test/verifikation: [hvad blev testet]
- Risiko: [eventuelle risici]
- Næste skridt: [opfølgning, hvis relevant]
```

Status sættes derefter til `done`.

### 6. Udfyld målgruppefelter (AI-genereret indhold)

Når KIRO opretter eller opdaterer en sag, skal følgende fem felter udfyldes intelligent. KIRO agerer som en erfaren **Product Manager** og stiller sig selv disse spørgsmål inden noget skrives:

- Hvad er det egentlige problem?
- Hvem oplever problemet?
- Hvorfor er det vigtigt?
- Hvilken værdi skaber løsningen?
- Hvad er den tekniske årsag?

---

#### `customer_summary` — Kundevendt problembeskrivelse

Beskriv problemet set fra brugerens perspektiv. Fokus på oplevelse, ikke system.

**Regler:**

- Almindeligt dansk, 2–5 linjer
- ❌ Ingen filnavne, SQL, commits, linjenumre, API-navne
- ✅ Forklar hvad brugeren oplevede / hvad der gik galt

**Eksempel:**

> Turneringsledere kunne i nogle tilfælde få vist baner som ledige, selvom en tidligere kamp endnu ikke var afsluttet. Det kunne føre til planlægningsfejl og ekstra manuelt arbejde.

---

#### `customer_solution` — Kundevendt løsningsbeskrivelse

Beskriv hvad der er ændret — ikke hvordan. Undgå tekniske detaljer.

**Regler:**

- Klart dansk, 2–4 linjer
- ❌ Ingen kode, interne navne, implementeringsdetaljer
- ✅ Forklar den funktionelle ændring brugeren vil opleve

**Eksempel:**

> Søgefunktionen er forbedret, så den nu tager højde for kampenes varighed og overlap, når ledige tider beregnes.

---

#### `customer_value` — Kundevendt værdibeskrivelse

Svar på: "Hvorfor skal kunden være glad for denne ændring?"

**Regler:**

- Maks 4 bullets
- Konkrete fordele: præcision, hastighed, færre fejl, mindre manuelt arbejde

**Eksempel:**

- Mere præcise forslag til ledige tider
- Færre fejl ved banebooking
- Hurtigere arbejdsgang for turneringsledere
- Større driftssikkerhed

---

#### `release_note` — Changelog-linje

Skrives som en changelog-entry. Bruges direkte på Leverancer-siden uden redigering.

**Regler:**

- 1–2 korte sætninger
- Skal kunne stå alene uden kontekst

**Eksempel:**

> Forbedret søgning efter ledige baner. Systemet tager nu højde for kampenes varighed og overlap ved beregning af ledige tider.

---

#### `technical_notes` — Intern teknisk analyse

Her må KIRO være meget teknisk. Feltet er kun synligt for interne brugere.

**Inkludér efter behov:**

- Root cause / teknisk analyse
- Berørte filer og API'er
- Database-ændringer / queries
- Afhængigheder og kompleksitet
- Kendte risici
- Relaterede sager

**Regler:**

- Læsbarhed er vigtigere end længde
- Må indeholde filnavne, SQL, commits, linjenumre — alt teknisk
- Strukturér med overskrifter ved behov

---

#### Kvalitetsprincipper for feltgenerering

1. **Omskriv, ikke blot omflyt.** Oversæt tekniske problemstillinger til forretningsværdi.
2. **Værdi frem for implementering.** Spørg: "Hjælper denne information kunden?" Hvis nej → `technical_notes`.
3. **Ingen teknisk lækage.** Filnavne, SQL, commits og API-navne må ALDRIG forekomme i `customer_summary`, `customer_solution`, `customer_value` eller `release_note`.
4. **release_note skal kunne publiceres direkte** på Leverancer-siden uden manuel redigering.
5. **Alle fem felter udfyldes** ved oprettelse og opdateres ved afslutning. Ingen må være tomme på en `Released`-sag.

---

#### Acceptance Criteria (maskinverificerbart)

Ved oprettelse eller afslutning af en sag:

- [ ] `customer_summary` udfyldt (2–5 linjer, rent dansk)
- [ ] `customer_solution` udfyldt (2–4 linjer, ingen kode)
- [ ] `customer_value` udfyldt (1–4 bullets)
- [ ] `release_note` udfyldt (1–2 sætninger)
- [ ] `technical_notes` udfyldt (teknisk analyse)
- [ ] Ingen tekniske detaljer i kundefelterne
- [ ] `release_note` klar til direkte visning på Leverancer-siden

### 7. Økonomi, kundesvar og Completion Checklist (v1.10)

NodeOS har en Completion Checklist (`GET /tasks/{id}/completion`) der scorer sagskvaliteten. AI-agenten skal sikre at alle required-felter udfyldes, så sager opnår score 100 (grøn).

---

#### `payment_responsibility` — Betales af

Angiver hvem der betaler for arbejdet. Sættes ved oprettelse eller senest ved `in_progress`.

**Enum-værdier:**

- `customer` — Kunden betaler. Brug ved arbejde bestilt af eller udført for kunden.
- `owner` — Intern investering. Brug ved tekniske opgaver, platform-infrastruktur, sikkerhed, refaktorering.
- `shared` — Delt mellem kunde og ejer.
- `not_billable` — Ikke fakturerbar (fx nedetid pga. ekstern leverandør).
- `not_decided` — Ikke afgjort endnu (default, men skal ændres inden `Released`).

<!-- TILPAS: Skriv jeres egne tommelfingerregler her. Eksempel:
- Fejl/bugs i systemet → `not_billable`
- Support-henvendelser (afklaring, vejledning) → `customer`
- Nye features ønsket af kunden → `customer`
- Intern tech debt, sikkerhed, observability → `owner`
- Nedetid/incidents uden for vores kontrol → `not_billable`
-->

**Tommelfingerregler for <DIT PROJEKT>:** _(udfyldes ved opsætning)_

---

#### `billable_status` — Faktureringsstatus

Skal vurderes inden `Released`. Default er `Ikke vurderet`.

<!-- TILPAS: Angiv jeres default, fx "Alle sager er `Ikke fakturerbar` (fast aftale)" eller "vurderes pr. sag". -->

**Default for <DIT PROJEKT>:** _(udfyldes ved opsætning)_

---

#### `reporter_name` / `reporter_email` / `reporter_channel` — Indmelder (v1.11)

Dokumenterer hvem der indmeldte sagen og ad hvilken kanal.

**Regler:**

- Udfyld altid ved oprettelse af Support- og Fejl-sager baseret på brugerhenvendelsen
- `reporter_name`: Fuldt navn (fx "Maria Glahn")
- `reporter_email`: Email-adresse (fx "turneringsleder@sisu.dk")
- `reporter_channel`: Enum — `email`, `phone`, `portal`, `other`
- NodeOS matcher automatisk mod profildatabasen og sætter `requested_by` hvis emailen kendes

**Hvornår:**

- Altid ved Support/Fejl hvor en bruger har henvendt sig
- Ikke påkrævet for interne tekniske opgaver eller udviklingsønsker uden ekstern henvender

---

#### `roadmap_bucket` — Roadmap-placering (v1.11)

Angiver hvornår sagen forventes behandlet.

**Enum-værdier:** `Nu`, `Næste`, `Senere`, `Parkeret`

**Regler:**

- Sæt ved oprettelse hvis prioritet og timing er klar
- KIRO kan foreslå baseret på prioritet:
  - P0 + aktiv → `Nu`
  - P1 + planlagt → `Næste`
  - P2 + ny → `Senere`
  - Status Parkeret → `Parkeret`
- Må gerne stå tom hvis roadmap-placering ikke er besluttet

---

#### `start_date` — Startdato (automatisk fra v1.11)

**NodeOS sætter automatisk** `start_date` ved første overgang til status "Under udvikling".

KIRO behøver ikke sætte dette felt manuelt. Det håndteres af en database-trigger.

---

#### `estimate_hours` — Estimeret tidsforbrug

Udfyldes kun hvis KIRO kan dokumentere et estimat. Angiv i timer (numerisk).

**Regler:**

- Udfyld kun ved eksplicit estimering (fx "dette tager ca. 4 timer")
- Gæt aldrig
- Kan udfyldes ved oprettelse eller planfase

---

#### `actual_hours` — Faktisk tidsforbrug

Udfyldes kun ved `Released` hvis det kan dokumenteres (fx fra tidsregistrering eller eksplicit angivelse).

**Regler:**

- Gæt aldrig
- Kan udfyldes i implementeringsrapporten hvis kendt

---

#### `customer_reply` — Kundesvar

Dokumenterer hvad kunden fik at vide. Udfyldes ved afslutning af Support- og Fejl-sager.

**Regler:**

- Skriv den tekst der er (eller kunne være) sendt til kunden
- Kort, klart dansk — ingen tekniske detaljer
- Skal kunne sendes direkte til brugeren uden redigering
- Udfyld kun hvis et reelt svar kan formuleres
- Opfind aldrig et kundesvar

**Eksempel:**

> Rettet. Søg ledige tider tager nu højde for kampvarighed, så baner der er optaget ikke længere vises som ledige.

**Hvornår:**

- Ved `Released` for Support- og Fejl-sager med ekstern henvender
- Ikke påkrævet for interne tekniske opgaver, beslutninger eller udviklingsønsker uden brugerhenvendelse

---

#### Completion Checklist — Brug i workflow

KIRO kan hente sagskvalitet via:

```bash
node scripts/nodeos-client.js completion <uuid>
```

Eller programmatisk: `GET /api/public/v1/tasks/{id}/completion`

Response indeholder:

- `quality_score` (0–100)
- `color` (red/yellow/green)
- `missing[]` — liste af manglende felter med `severity` (required/recommended)

**Mål:** Alle sager skal være grønne (score 100) inden `Released`.

**Batch-opdatering (v1.13):**

Brug `batchUpdateTasks()` til at opdatere mange sager i ét kald (max 50):

```javascript
import { batchUpdateTasks } from "./scripts/nodeos-client.js";
const result = await batchUpdateTasks(
  [
    { id: "uuid-1", patch: { payment_responsibility: "not_billable" } },
    { id: "uuid-2", patch: { status: "done", customer_reply: "..." } },
  ],
  { include: ["completion"] },
);
// result.data.results[].status + result.data.results[].task.completion.quality_score
```

**Kundesvar via kommentar (v1.13):**

Brug `is_customer_reply: true` på kommentarer for at auto-synce til `customer_reply`-feltet:

```javascript
import { addComment } from "./scripts/nodeos-client.js";
await addComment(taskId, "Rettet. Søgningen tager nu højde for...", { is_customer_reply: true });
// Sætter kommentar (visibility: customer) OG opdaterer customer_reply-feltet i ét kald
```

**Felter KIRO ikke kan udfylde:**

- `area_id` — kræver at organisationen opretter areas i portalen
- `actual_hours` — kræver tidsregistrering (kan ikke dokumenteres fra kode)
- `estimate_hours` — kun hvis eksplicit estimeret

Disse felter markeres som `recommended` og blokerer ikke score 100.

## Work session lifecycle

> **Status: LIVE (Phase 1, API v1.17.0).** Endpoints:
> `POST /work-sessions` (start), `POST /work-sessions/{id}/events`,
> `POST /work-sessions/{id}/lifecycle` (`pause`/`resume`/`wait`/`complete`),
> `GET /work-sessions[/{id}]`. Kræver scopes `work.read` / `work.write`.
> `outcome` er påkrævet ved `complete`. Se API.md §19.

Normativ reference (begreber, tidsmodel, definitioner):
`docs/WORK_SESSIONS.md`. Operationel agentkontrakt:
`docs/agent-kit/AGENT_CONTRACT.md`.

> **Work Sessions Contract Version: 1.0** (`NodeOS-Work-Sessions-Contract`).
> Denne skabelon implementerer Contract v1.0. Følg den, indtil NodeOS
> annoncerer en ny version.

Gentag ikke definitionerne her — link til dem.

### Konceptuel sekvens

```text
Find sag
-> Start session
-> Register event
-> Enter waiting state
-> Resume session
-> Register evidence
-> Complete session
-> Opdater sag
```

### Regler

- **Sessionspligt:** væsentligt arbejde skal foregå inden for en aktiv work
  session med et eksplicit formål. Håndhæves blødt i første version
  (checklist-advarsel, ikke API-afvisning).
- **Én session = én sag:** i v1 er en session altid knyttet til præcis én sag.
  Flere sessioner må køre parallelt på samme sag.
- **Eksplicit ventetilstand:** markér `waiting_for_human`,
  `waiting_for_system` eller `waiting_for_external`. Ventetid er ikke aktiv
  arbejdstid.
- **Eksplicit afslutning:** agenten afslutter selv sessionen med `summary` og
  `outcome`. Timeout-lukning er et sikkerhedsnet, ikke en normal afslutning.
  En afsluttet session kan ikke genåbnes.
- **Evidenskrav:** leveringsevidens registreres struktureret (commit, PR,
  test, build, deployment, migration, dokument, review). Agentskabt evidens er
  ikke verificeret; fritekst opfylder ikke alene en evidensregel.
- **Serveren ejer tiden:** alle autoritative tidsstempler sættes server-side.
  Agentens timestamp er diagnostisk og bruges aldrig til varighed, ordering
  eller fakturering. Eventrækkefølge sikres med server-tildelt `seq`.
- **Idempotens:** writes sendes med idempotensnøgle, så retries ikke skaber
  dubletter. Én nøgle = én operation: genbrug af nøglen med en anden payload
  giver `409 idempotency_key_reuse`, og et retry mens det første kald stadig
  kører giver `409 idempotency_in_progress` (vent kort, samme nøgle igen).
- **Tid summeres ikke på tværs af sessioner:** `wall_clock_span` gælder pr.
  session. To parallelle sessioner à 4 s er ikke 8 sekunders arbejde.

### Fakturering (gælder allerede nu)

- Ingen automatisk fakturering. Sessionstid er ikke automatisk fakturerbar.
- Ingen menneskelig ækvivalent tid. Begrebet må ikke anvendes.
- Agenten må ikke sætte `actual_hours` ud fra sin runtime eller sessionstid.
- `actual_hours` — **current state:** manuelt felt med uændret runtime-adfærd.
  **Target semantics:** godkendt fakturerbar mængde i timer, godkendt af et
  menneske. Se `docs/WORK_SESSIONS.md` §2.9.
- Send aldrig prompts, credentials eller persondata i metadata.

## Sagstyper

| English alias (write) | Dansk enum (response) | Beskrivelse                                |
| --------------------- | --------------------- | ------------------------------------------ |
| `bug`                 | Fejl                  | Noget virker forkert                       |
| `support`             | Support               | Bruger oplever problem eller har spørgsmål |
| `feature`             | Udviklingsønske       | Nyt udviklingsønske                        |
| `change`              | Ændringsønske         | Ændring af eksisterende funktion           |
| `task`                | Teknisk opgave        | Refaktorering, test, deployment, sikkerhed |
| `decision`            | Beslutning            | Arkitekturbeslutning, valg, retning        |

## Prioritering (projektkontekst)

<!-- TILPAS: Beskriv jeres domæne og hvad der er forretningskritisk. Eksempel:
1. **Kritisk**: Flows der stopper drift eller omsætning
2. **Høj**: Kerneadministration og brugerroller
3. **Medium**: Onboarding, invitationer, rapportering
4. **Lav**: Nice-to-have der ikke påvirker kritisk drift
-->

_(udfyldes ved opsætning)_

## Kvalitetskrav

AI-agenten skal altid:

- Lave minimal, sikker ændring
- Undgå brede refaktoreringer uden grund
- Forklare risiko før deploy
- Teste relevante flows
- Skrive tydelig status i NodeOS
- Holde staging og production adskilt
- Aldrig eksponere API-nøgler
- Aldrig lukke en sag uden verificering

## Definition of Done

En opgave er først færdig, når:

- [ ] Koden er implementeret
- [ ] Relevant test/typecheck er kørt
- [ ] NodeOS-sagen er opdateret med alle trin (kommentarer undervejs)
- [ ] Målgruppefelter er udfyldt (customer_summary, customer_solution, customer_value, release_note, technical_notes)
- [ ] Økonomifelter er sat (payment_responsibility, billable_status)
- [ ] `customer_reply` er udfyldt (for Support/Fejl med ekstern henvender)
- [ ] Completion Checklist er grøn (score 100)
- [ ] Implementeringsrapport er skrevet som afsluttende kommentar
- [ ] Sagen er sat til status `done` (→ Released i NodeOS)

## Integration med andre steering files

<!-- TILPAS: Referér jeres egne steering-/regelfiler her, fx support-workflow, spec-workflow og kodestandarder. -->

- Supportsager skal også oprettes/opdateres i NodeOS
- Specs og features skal have en tilhørende NodeOS-sag
- Projektets øvrige kvalitetskrav gælder fuldt ud

## Webhooks

NodeOS sender HMAC-signerede POST-callbacks ved events (`task.created`, `task.status_changed`, `comment.created`). Webhooks er konfigureret via API'et (`/api/public/v1/webhooks`).

**AI-agentens rolle:**

- AI-agenten skal **ikke** oprette eller administrere webhook-endpoints — det er infra/ops-ansvar
- Hvis agenten modtager en webhook-payload (fx via et hook eller en trigger), skal den behandle eventet som autoritativt
- Fuld webhook-dokumentation inkl. signatur-verifikation: den fulde API-reference (`https://nodeos.dk/api/public/v1/docs`) §9

## API-reference

Fuld maskinlæsbar dokumentation kan altid hentes live:

- **Markdown**: `GET https://nodeos.dk/api/public/v1/docs`
- **OpenAPI**: `https://nodeos.dk/openapi.yaml`
- **Human-readable**: `https://nodeos.dk/docs`

## Audit ved første opsætning

Ved første konfiguration skal KIRO:

1. Hente eksisterende åbne sager fra NodeOS
2. Gruppere dem efter område
3. Vurdere om repoet allerede indeholder ændringer, der relaterer sig til dem
4. Foreslå hvilke sager der bør prioriteres først
5. Undgå at oprette dubletter af eksisterende sager

## Dokumentationsreview (pligt, ikke notifikation)

Når NodeOS publicerer en dokumentations-release, oprettes et review til din
agent-integration. Behandl det, før du fortsætter med almindeligt arbejde:

```bash
node scripts/nodeos-client.mjs doc-reviews
node scripts/nodeos-client.mjs doc-review-start <review-uuid>
node scripts/nodeos-client.mjs doc-documents
node scripts/nodeos-client.mjs doc-review-assess <review-uuid> \
  --summary "Læst og anvendt i steering-filen" \
  --action "applied:Opdaterede arbejdsloopet" \
  --action "no_change_needed:Ingen ændring i faktureringsregler"
```

`kind` skal være `applied`, `planned`, `no_change_needed` eller `blocked`.
Kræver scopes `docs.read` + `docs.sync` (eksplicitte — de gives **ikke** af
`admin.full`) og en nøgle bundet til en aktiv agent-integration.
Kanonisk kilde: `docs/DOCUMENTATION_SYNC.md`.

## Scopes du skal bede om

`tasks.read`, `tasks.create`, `tasks.update`, `comments.read`,
`comments.create`, `status.update`, `work.read`, `work.write`,
`evidence.read`, `evidence.write`, `memory.read`, `docs.read`, `docs.sync`.

Bed **aldrig** om `evidence.verify` — verifikation er menneskeligt arbejde og
gives ikke til agentnøgler.
