Agentvy
Startsidans ekosystem
Vad ekosystemet är, vilka regler som gäller och var du börjar – för människa och agent.
page manifest · /devid: start
path: /dev
titel: Startsidans ekosystem
summering: Vad ekosystemet är, vilka regler som gäller och var du börjar – för människa och agent.
nyckelord: start, översikt, ekosystem, grundregler, portal
## Vad detta är
Startsidan är ett skal som komponerar moduler. Användaren ser en sammanhängande sida; varje funktion är ett fristående paket med ett tydligt kontrakt mot skalet. Det är det som gör att nya funktioner kan byggas löpande utan att kärnan rörs.
- Skalet: apps/portal/src/shell. Layout, roller, modulregister, rutnät och AI-fältet. Vet vilka slots som finns, aldrig vad en widget innehåller.
- Modulerna: packages/modules/*. Äger sitt innehåll och sin data, och beskriver sig för skalet via ett manifest. En ny funktion = ett nytt paket + en rad i registret.
- Designgrunden: packages/tokens och packages/ui. Tokens definierar färg, form och typografi; primitiverna är de enda byggstenar en modul behöver.
- Den här portalen: apps/portal/src/app/dev. Ingången för den som ska bygga. Samma innehåll i två vyer: en för människa, en för agent.
Peka en agent hit och den ska kunna bygga rätt utan att gissa: /dev/llms.txt är hela portalen som text, /dev/ui/llms.txt är designsystemet.
## Grundregler
Det som gäller oavsett vad du bygger. Bryter något mot en av dem är det ändringen som är fel, inte regeln.
- Nya funktioner byggs som moduler under packages/modules/<namn>. Skalet rörs bara när kontraktet faktiskt saknar något.
- Designen kommer ur packages/tokens och packages/ui. Inga hex-värden, inga godtyckliga px-mått.
- Grönt är status och betyder aldrig AI. AI-ytor tonas med --color-ai-* i blått, och allt AI-genererat märks synligt med AiChip (ADR-026).
- Servern fattar besluten. Att modellen sätter create: true räcker aldrig – route handlern validerar själv att allt obligatoriskt finns.
- Inga hemligheter i repo eller klientkod. AI-anrop går via en route handler, aldrig direkt från webbläsaren.
- TypeScript strict, inga any. Varianter är unionstyper, inte fria strängar.
- Server components som default. "use client" bara på det som faktiskt behöver interaktion.
- Mockdata har samma form som kommunens mikrotjänst-API:er skulle ge – bytet till riktiga källor ska bli en adapterfråga.
- Svenska i all UI-text och alla commit-ämnen.
- Ändras en token, en primitiv eller ett kontrakt uppdateras posten i portalen i samma commit.
- En funktion är inte klar förrän den är dokumenterad. Ny funktion = ny katalogpost + ett dok på /dev/docs som uppfyller dokumentationskontraktet. Kod utan dok räknas som påbörjad, inte levererad.
## Antimönster
Grundreglerna ovan säger vad du ska göra. Det här säger vad du är på väg att göra i stället – de misstag som faktiskt går att göra i den här kodbasen, med rättelsen bredvid. Kolumnen längst till höger är hederlig: står det "ingen" fångas felet av ingen grind, och då är den här raden det enda som står i vägen.
Gör inte | Gör i stället | Varför | Fångas av
En egen sidmodell för en redaktionell sida – egna typer för sektionsingång eller artikel. | Använd PageManifest och ContentBlock ur module-sdk (ADR-016). Saknas något, föreslå det där. | Ett parallellt sidbegrepp blir omöjligt att slå ihop med det beslutade när det väl bär innehåll. (ADR-016) | ingen
Anropa modellen från en klientkomponent, eller lägga en API-nyckel i något klienten läser. | Gå via en route handler. Nyckeln bor på servern och når aldrig webbläsaren. | En nyckel i klientkoden är läckt i samma sekund som sidan levereras, oavsett hur den är obfuskerad. | ingen – granskas i kodgranskning
Lägga funktionslogik i skalet därför att det går snabbare än att skapa en modul. | Bygg modulen under packages/modules/<namn>. Skalet rörs först när kontraktet bevisligen saknar något. | Skalet är gemensamt för alla moduler. Logik som hamnar där kan ingen modul äga, och ingen vågar ta bort. | ingen
Skapa något därför att modellen svarade create: true. | Route handlern kontrollerar själv att allt obligatoriskt finns innan något skapas. | Modellens flagga är ett förslag, inte ett beslut. Servern är den enda part som kan hållas ansvarig. | ingen – täcks av modulens egna tester
Hårdkoda färg eller mått: className="text-[#005595]" eller className="text-[14px]". | Tokens och primitiver: className="text-blue", className="text-sm". | Ett hårdkodat värde överlever inte nästa temaändring och syns inte i designsystemets specifikation (R4). | yarn lint
Be modellen svara med enbart JSON och sedan parsa svaret. | Structured outputs via output_config.format, eller strict: true på verktyget. Formen är då modellens kontrakt, inte en förhoppning. | En modell som ombeds svara med JSON gör det nästan alltid – och felen kommer när det räknas, inte när man testar. | ingen
Läsa åtkomstkakan för att veta vem användaren är. | Vänta på SAML-sessionen i fas 4. Behöver modulen identitet nu: flagga behovet, bygg inte runt det. | Koden delas av alla som provar demon och säger ingenting om vem någon är. Ett bygge ovanpå den blir fel den dag riktig inloggning kommer. (ADR-012) | ingen
Skriva en ny CSS-regel i shell.css från en modul. | Tailwind-utilities i modulen. shell.css ägs av skalet och är porterad 1:1 från prototypen. | Ändras skalets krom från en modul går måtten isär mot designfacit, och ingen ser vilken modul som gjorde det (R6). | ingen
Lägga till en blocktyp i Blockvy.tsx utan att lägga till den i text.ts. | Två renderare, alltid. En blocktyp utan textrenderare är ett fel, inte en förenkling. | Innehållet försvinner tyst ur agentvyn och ur llms.txt. Portalen ser hel ut för människan och är trasig för agenten. (ADR-010) | ingen
Skicka tillbaka fältens tidigare värden till modellen utan serverns signatur. | Förankra fälten med serverns egen HMAC och verifiera den vid varje tur. | Osignerade fält är en inmatningsväg förbi samtalet: klienten kan skriva vad som helst direkt in i det som ska skapas. (ADR-013) | yarn test
Lämna över en ny funktion utan katalogpost och dok, med tanken att dokumentera sedan. | Katalogpost i katalog.ts och dok i dok.ts, i samma commit som koden. | Kod utan dok är påbörjad, inte levererad. Det som inte står i portalen finns inte för nästa agent. (ADR-011) | yarn test
Rendera en källas HTML med dangerouslySetInnerHTML, eller sanera den och rendera ändå. | Översätt källan till ContentBlock i en adapter och låt skalet rendera blocken som JSX. | Markup ur en källa tar med sig källans skript, stilar och länkar. Sanering är en lista över det man kom ihåg att förbjuda; block är en lista över det man tillåter. (ADR-016) | yarn lint (react/no-danger)
## Stanna och fråga
Ekosystemet är byggt i faser, och några kontrakt är beslutade men inte implementerade. Möter du något av det här ska du inte improvisera fram en lösning – det är ett arkitekturbeslut, och det skrivs som en ADR i docs/beslut/ innan koden. Statuskolumnen finns som fält i /dev/spec.json under stanna, så att svaret på "finns det redan?" går att läsa av utan att tolka tabellen.
Om kravet leder hit | Läget i dag | Status | Gör detta
En redaktionell sida (sektionsingång eller artikel) | PageManifest finns i module-sdk (ADR-016). En modul bidrar med sidor via manifestets pages; skalet renderar dem under /sidor. | begransad, implementerad, ADR-016 | Använd PageManifest och ContentBlock. Bygg inget eget sidbegrepp och ingen egen renderare – saknas en blocktyp, föreslå den i module-sdk.
Assistenten ska förstå fritext och utlösa något | AI-fältet (POST /api/assistant) matchar ai.intents med enkla mönster före modellen. Bara navigate har en hanterare. | begransad, fas 3, implementerad | Deklarera intenten i modulen med mönster och ett navigate-mål. Behövs answer eller LLM-matchning: stanna och fråga.
Ett nytt fält i ModuleManifest | Kontraktet ska hållas tunt. Nya fält ska bäras av minst två moduler innan de blir permanenta. | begransad, implementerad | Visa att två moduler behöver det, skriv en ADR, ändra sedan.
En ändring i skalet eller i shell.css | Skalets krom är porterad 1:1 från prototypen och ägs inte av moduler. | begransad, implementerad, ADR-004 | Kontrollera först att slots eller routes inte redan räcker.
En riktig extern källa, eller en ny hemlighet | Fas 4 (BFF) är inte byggd. Nycklar bor på servern, aldrig i repo eller klient. Undantaget är öppna, anonyma källor: webbsidor läser sundsvall.se utan nyckel (ADR-016). | planerad, fas 4, ej implementerad | Kräver källan inloggning: håll dig till modulens datafunktion med mockform, och flagga behovet. Är den öppen: följ webbsidor eller nyheter – server-only, force-cache, timeout, zod och inspelad reserv.
En ny flödestyp utöver felanmalan | Två flöden finns: felanmalan med platta fält och utvecklingsarende med typade avsnitt (ADR-015). Motorn lyfts först i M4, och ADR-005 är inte skriven. | planerad, fas M4, ej implementerad, ADR-005 | Bygg flödet som data i modulen, så att M4 blir en flytt och inte en omskrivning. Läs båda formerna först – ADR-005 ska skrivas mot dem, inte mot den ena.
Saknas ett svar helt är det portalen som är ofullständig, inte du som ska gissa. Säg vad du sökte och var det tog slut – posten läggs till i apps/portal/src/app/dev/portal/ i samma commit som ändringen.
## Snabbstart
Kör och verifiera:
```
yarn dev # utveckling på http://localhost:3000
yarn lint && yarn test # grindarna – ska vara gröna före varje commit
yarn build && yarn start # produktionsbygge lokalt
docker compose up --build # verifiera containern före push
```
Hämta hela sammanhanget som agent:
```
curl -s http://localhost:3000/dev/llms.txt # portalen: arkitektur, kontrakt, recept
curl -s http://localhost:3000/dev/ui/llms.txt # designsystemet: tokens, primitiver, felindex
curl -s http://localhost:3000/dev/spec.json # samma portal, maskinläsbart
```
Samma sak från produktion, utan att klona repot:
```
curl -s https://start.sundsvall.dev/llms.txt # kartan: var kontexten finns
curl -s https://start.sundsvall.dev/dev/llms.txt # portalen
curl -s https://start.sundsvall.dev/dev/ui/llms.txt # designsystemet
curl -s https://start.sundsvall.dev/dev/spec.json # portalen som JSON
# Adresserna ovan ligger utanför åtkomstkoden (ADR-012) och går att peka en
# extern AI-tjänst på direkt. Demon på /start gör det inte.
```
## Vidare härifrån
- /dev/kom-igang — Kom igång (miljö, kommandon, din första ändring)
- /dev/arkitektur — Arkitekturen (skal, moduler, ytformer, dataflöde)
- /dev/moduler — Modulkontraktet (ModuleManifest fält för fält)
- /dev/recept — Recept (steg för de fem vanligaste uppgifterna)
- /dev/ui — Designsystemet (tokens, primitiver, mönster, felindex)
- /dev/katalog — Katalogen (allt som finns att bygga vidare på)Hela portalen i samma form: /dev/llms.txt · /dev/spec.json