Agentvy
Modulkontraktet
ModuleManifest fält för fält, sloten de fyller och hur en modul registreras.
page manifest · /dev/modulerid: moduler
path: /dev/moduler
titel: Modulkontraktet
summering: ModuleManifest fält för fält, sloten de fyller och hur en modul registreras.
nyckelord: modul, manifest, slots, widget, action, routes, intents, registry
## Manifestet
Kontraktet mellan skalet och modulen. Ett nytt fält gör kontraktet tyngre för alla moduler – nya fält ska bäras av minst två moduler innan de blir permanenta. Vill du se kontraktet i bruk i stället för i sammandrag: packages/modules/arenden är den kanoniska förlagan, utpekad på /dev/recept.
packages/module-sdk/src/types.ts:
```
export interface ModuleManifest {
id: string; // "ledighet"
name: string; // "Ledighetsansökningar"
roles: Role[]; // ["medarbetare"] | ["chef"] | båda
nav?: NavEntry[]; // punkter under "Verktyg" i sidopanelen
routes?: ModuleRoute[]; // egna vyer: focus under /flode, standard och full under /verktyg (ADR-004, ADR-019, ADR-025)
pages?: PageManifest[]; // redaktionella sidor under "Sidor" (ADR-016)
slots?: {
fokus?: FokusProvider; // fokusnyheten under AI-fältet, tills den stängs (ADR-021)
genvag?: Genvag[]; // knappar bredvid hälsningen; ai: true ger ✦ (ADR-025)
kpi?: Widget[]; // nyckeltalsraden, helbredd
main?: Widget[]; // vänsterspalten
side?: Widget[]; // högerspalten
actions?: ActionsProvider; // rader i "Att agera på"
};
notifications?: NotificationsProvider; // notiser i klockan (ADR-020)
ai?: {
suggestions?: (ctx: UserContext) => Suggestion[]; // renderas i AI-fältet
intents?: IntentHandler[]; // AI-fältet matchar dem
// före modellen
};
}
```
## Sloten
Slot | Plats | Form | Använd när
fokus | Under AI-fältet på startsidan, tills den stängs | FokusProvider | Något alla ska se innan de börjar – en viktig nyhet, en störning. Data, inte komponent (ADR-021).
genvag | Bredvid hälsningen på startsidan | Genvag[] | En arbetsplats man ofta går till, som Min AI. NavEntry plus ai?: boolean; skalet ritar knappen (ADR-025).
kpi | Fyra spår över hela arbetsytan | Widget[] | Ett mätvärde som tål att stå ensamt.
main | Vänsterspalten, minmax(0, 1fr) | Widget[] | Kortet är sidans huvudinnehåll eller behöver bredd.
side | Högerspalten, 36 rem | Widget[] | Stödinformation: idag, saldon, korta listor.
actions | Kortet "Att agera på" | ActionsProvider | Något kräver en handling av användaren.
order styr ordningen inom en slot – lägre först. Utan order gäller registreringsordningen i registry.ts.
Widget och ActionItem:
```
interface Widget {
id: string;
roles?: Role[]; // snävare än modulens
order?: number; // lägre först
Component: (props: { ctx: UserContext }) => ReactNode | Promise<ReactNode>;
}
interface ActionItem {
id: string;
status: RowStatus; // krit | warn | info | ok | neutral
title: string;
meta?: string;
aiNote?: string; // "✦ Utkast klart" – märker AI-underlag
chip?: { tone: ChipTone; label: string };
action: { label: string; href: string; variant?: "primary" | "ghost" };
}
```
actions är en funktion, inte en lista: raderna hämtas per användare och kan kräva ett anrop. Modulen äger raden, kortet agera äger ordningen (ADR-007).
## Registrera modulen
1. Skapa paketet
packages/modules/<namn> med samma inre form som de befintliga: manifest.ts, components/, data/, index.ts.
2. Lägg till i workspaces
Roten har redan packages/modules/* som workspace. Lägg beroendet i apps/projekt/package.json och paketet i transpilePackages i next.config.ts.
```
// apps/projekt/next.config.ts
transpilePackages: ["@startsidan/ui", …, "@startsidan/modul-<namn>"],
```
3. En rad i registret
Ordningen i registry.ts styr nav-ordningen och fallordningen i sloten när order saknas.
```
// apps/portal/src/shell/registry.ts
import { manifest as nyModul } from "@startsidan/modul-<namn>";
export const registry: ModuleManifest[] = [oversikt, agera, …, nyModul];
```
4. Verifiera rollfiltreringen
Växla demoanvändare i användarmenyn. Ser båda rollerna rätt saker är kontraktet rätt använt.
## Namngivning
Två språk, med en gräns som går vid kontraktet. Blandas de går koden att läsa men inte att känna igen.
Var | Språk | Exempel
Modulens domänkod: data, typer, fält | Svenska | hamtaLedighet, poster, rubrik, etikett, ton, klaraForAttest
Modulens komponenter | Svenska | Ledighetskort, AttAgeraPa, Nyckeltal
module-sdk och skalet | Engelska | ModuleManifest, UserContext, widgetsFor, ActionItem, roles, order
Filnamn i moduler | Som komponenten | components/Ledighetskort.tsx, data/index.ts
All text användaren ser | Svenska | "Väntar på chef", "Visa alla →"
Commit-ämnen | Svenska | feat(kalender): veckovy i högerspalten
- Gränsen går vid kontraktet: allt som module-sdk namnger är engelskt, allt modulen själv namnger är svenskt.
- Översätt aldrig ett SDK-fält. En modul skriver roles och order, inte roller och ordning.
- Svenska identifierare skrivs utan diakriter i kod: hamtaArenden, inte hämtaÄrenden.
- Kommentarer är svenska och förklarar varför, inte vad.
## Regler för moduler
- En modul importerar aldrig en annan modul. Behöver två moduler samma sak går det via module-sdk eller via slots.
- En modul skriver aldrig i shell.css. Skalets krom är porterad 1:1 från prototypen.
- Widgeten är en server component om den inte behöver interaktion.
- Mockdata ligger i modulens data/ och har mikrotjänsternas form, bakom ett adapterinterface.
- Kommer innehållet från en modell märks det: aiNote på raden, AiChip i vyn.Hela portalen i samma form: /dev/llms.txt · /dev/spec.json