@haus-tech/haus-workflow 4.1.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.2.0](https://github.com/WeAreHausTech/haus-workflow/compare/v4.1.0...v4.2.0) (2026-08-21)
4
+
5
+ ### Features
6
+
7
+ - **install:** bundle request-pipeline and slack-cowork global skills ([#332](https://github.com/WeAreHausTech/haus-workflow/issues/332)) ([1978b5d](https://github.com/WeAreHausTech/haus-workflow/commit/1978b5d2cb5098a8c87e9db7148633ebef5b4c33))
8
+
9
+ ### Bug Fixes
10
+
11
+ - haus new empty-cache flag and setupOk signal ([#333](https://github.com/WeAreHausTech/haus-workflow/issues/333)) ([5a9004a](https://github.com/WeAreHausTech/haus-workflow/commit/5a9004ae23d7189d5645dc7e830e3b4cbb420203)), closes [#329](https://github.com/WeAreHausTech/haus-workflow/issues/329) [#330](https://github.com/WeAreHausTech/haus-workflow/issues/330)
12
+
3
13
  ## [4.1.0](https://github.com/WeAreHausTech/haus-workflow/compare/v4.0.1...v4.1.0) (2026-08-21)
4
14
 
5
15
  ### Features
package/README.md CHANGED
@@ -138,13 +138,16 @@ waiting on timeouts (catalog sync, npm version check, workflow-template fetch, l
138
138
  reference fetch, global-plugin install; the read-only local plugin probe still runs — see
139
139
  [docs/cli.md](docs/cli.md) → `--offline` for why). It never changes what gets installed.
140
140
 
141
- The honest-shortfall reporting covers `haus apply`, `haus init`, and `haus workspace
142
- setup` (issue #325 extended `apply`'s original ADR-0060 gate to the other two): a run
143
- that cannot install everything it wanted reports `K of N catalog items installed` and
144
- exits 1 if it installed none, unless `--allow-empty-cache` opts into writing core files
145
- anyway (still exits 1 having installed nothing). `workspace setup` reports this per
146
- member repo — a repo that installed zero catalog items is recorded `failed`, not `ok`.
147
- See [docs/cli.md](docs/cli.md) for each command's exact flags and behaviour.
141
+ The honest-shortfall reporting covers `haus apply`, `haus init`, `haus new`, and
142
+ `haus workspace setup` (issue #325 extended `apply`'s original ADR-0060 gate to `init`
143
+ and `workspace setup`; issue #329 threaded the same `--allow-empty-cache` flag through
144
+ `haus new`): a run that cannot install everything it wanted reports `K of N catalog
145
+ items installed` and exits 1 if it installed none, unless `--allow-empty-cache` opts
146
+ into writing core files anyway (still exits 1 having installed nothing). `workspace
147
+ setup` reports this per member repo — a repo that installed zero catalog items, hit
148
+ the zero-signal guard, or was refused for an unsupported catalog format is recorded
149
+ `failed`, not `ok` (issue #330). See [docs/cli.md](docs/cli.md) for each command's
150
+ exact flags and behaviour.
148
151
 
149
152
  ## Internal docs
150
153
 
package/dist/cli.js CHANGED
@@ -10585,8 +10585,10 @@ async function runSetupCore(root, opts) {
10585
10585
  hooksOk: false,
10586
10586
  written: [],
10587
10587
  // Pre-existing zero-signal guard, not the #325 catalog-install honesty check —
10588
- // deliberately left out of scope for `catalogInstallOk` (see its doc comment).
10589
- catalogInstallOk: true
10588
+ // `catalogInstallOk` stays true; `setupOk` is false so workspace setup cannot
10589
+ // record this repo as ok (issue #330).
10590
+ catalogInstallOk: true,
10591
+ setupOk: false
10590
10592
  };
10591
10593
  }
10592
10594
  await writeJson(hausPath(root, "recommendation.json"), recommendation);
@@ -10616,7 +10618,8 @@ async function runSetupCore(root, opts) {
10616
10618
  warnings: warningLines,
10617
10619
  hooksOk,
10618
10620
  written: [],
10619
- catalogInstallOk: true
10621
+ catalogInstallOk: true,
10622
+ setupOk: true
10620
10623
  };
10621
10624
  const envelope = (hooksResult) => ({
10622
10625
  scan: scanResult,
@@ -10645,7 +10648,7 @@ async function runSetupCore(root, opts) {
10645
10648
  error(sync.formatRefusal);
10646
10649
  process.exitCode = 1;
10647
10650
  emitJsonEnvelope(json, quiet, envelope(hooks));
10648
- return baseResult;
10651
+ return { ...baseResult, setupOk: false };
10649
10652
  }
10650
10653
  if (sync.newItems.length > 0) {
10651
10654
  say(`Catalog cache populated: ${sync.newItems.length} new item(s).`);
@@ -10680,7 +10683,7 @@ async function runSetupCore(root, opts) {
10680
10683
  error(message2);
10681
10684
  process.exitCode = 1;
10682
10685
  emitJsonEnvelope(json, quiet, envelope(hooks));
10683
- return { ...baseResult, catalogInstallOk: false };
10686
+ return { ...baseResult, catalogInstallOk: false, setupOk: false };
10684
10687
  }
10685
10688
  } else {
10686
10689
  warn(
@@ -10727,7 +10730,13 @@ async function runSetupCore(root, opts) {
10727
10730
  say(`- HOOKS OK: ${hooksAfter.message}`);
10728
10731
  }
10729
10732
  emitJsonEnvelope(json, quiet, envelope(hooksAfter));
10730
- return { ...baseResult, hooksOk: hooksOkAfter, written: files, catalogInstallOk };
10733
+ return {
10734
+ ...baseResult,
10735
+ hooksOk: hooksOkAfter,
10736
+ written: files,
10737
+ catalogInstallOk,
10738
+ setupOk: catalogInstallOk
10739
+ };
10731
10740
  }
10732
10741
 
10733
10742
  // src/commands/init.ts
@@ -11153,13 +11162,15 @@ async function createProject(opts, deps = defaultDeps) {
11153
11162
  phase = "init";
11154
11163
  if (!opts.json) log("\u25B6 init: haus setup pipeline");
11155
11164
  const exitCodeBeforeSetup = process.exitCode;
11165
+ let setupResult;
11156
11166
  try {
11157
- await deps.setup(projectPath, {
11167
+ setupResult = await deps.setup(projectPath, {
11158
11168
  json: false,
11159
11169
  apply: true,
11160
11170
  dryRun: false,
11161
11171
  quiet: opts.json,
11162
- confirm: opts.yes ? async () => true : () => confirm("Approve and write Claude files now?")
11172
+ confirm: opts.yes ? async () => true : () => confirm("Approve and write Claude files now?"),
11173
+ allowEmptyCache: opts.allowEmptyCache
11163
11174
  });
11164
11175
  } catch (err) {
11165
11176
  steps.push({
@@ -11169,15 +11180,26 @@ async function createProject(opts, deps = defaultDeps) {
11169
11180
  });
11170
11181
  return { ok: false, projectPath, repoUrl, installHint, steps };
11171
11182
  }
11183
+ const catalogInstallRefused = setupResult?.catalogInstallOk === false;
11184
+ const retryCmd = catalogInstallRefused ? `cd ${opts.name} && haus init --allow-empty-cache` : `cd ${opts.name} && haus init`;
11172
11185
  if (process.exitCode === 1 && exitCodeBeforeSetup !== 1) {
11173
- steps.push({
11174
- step: "init",
11175
- status: "failed",
11176
- detail: `haus init reported failure. Project and repo are intact \u2014 cd ${opts.name} && haus init to retry.`
11177
- });
11178
- return { ok: false, projectPath, repoUrl, installHint, steps };
11186
+ if (opts.allowEmptyCache && catalogInstallRefused) {
11187
+ steps.push({
11188
+ step: "init",
11189
+ status: "done",
11190
+ detail: "core files written; catalog items skipped (--allow-empty-cache)"
11191
+ });
11192
+ } else {
11193
+ steps.push({
11194
+ step: "init",
11195
+ status: "failed",
11196
+ detail: `haus init reported failure. Project and repo are intact \u2014 ${retryCmd} to retry.`
11197
+ });
11198
+ return { ok: false, projectPath, repoUrl, installHint, steps };
11199
+ }
11200
+ } else {
11201
+ steps.push({ step: "init", status: "done" });
11179
11202
  }
11180
- steps.push({ step: "init", status: "done" });
11181
11203
  phase = "overlay";
11182
11204
  if (!opts.json) log("\u25B6 overlay: haus config items");
11183
11205
  let overlay;
@@ -11264,7 +11286,7 @@ async function createProject(opts, deps = defaultDeps) {
11264
11286
  }
11265
11287
  if (!opts.json) log(`\u2713 Created ${projectPath}${repoUrl ? ` \u2192 ${repoUrl}` : ""}
11266
11288
  ${installHint}`);
11267
- return { ok: true, projectPath, repoUrl, installHint, steps };
11289
+ return { ok: process.exitCode !== 1, projectPath, repoUrl, installHint, steps };
11268
11290
  }
11269
11291
 
11270
11292
  // src/commands/new.ts
@@ -11297,7 +11319,8 @@ function resolveNewOptions(stackArg, nameArg, flags, parentDir) {
11297
11319
  pm,
11298
11320
  yes: flags.yes === true,
11299
11321
  dryRun,
11300
- json: flags.json === true
11322
+ json: flags.json === true,
11323
+ allowEmptyCache: flags.allowEmptyCache === true
11301
11324
  }
11302
11325
  };
11303
11326
  }
@@ -14232,8 +14255,8 @@ async function runWorkspaceSetup(workspaceRoot, options = {}) {
14232
14255
  error(`Setup failed for ${repo.name}: ${message2}`);
14233
14256
  continue;
14234
14257
  }
14235
- if (!res.catalogInstallOk) {
14236
- const message2 = "No catalog items were installed for this repo (zero installable content) \u2014 see the per-repo output above, or re-run with --allow-empty-cache.";
14258
+ if (!res.setupOk) {
14259
+ const message2 = !res.catalogInstallOk ? "No catalog items were installed for this repo (zero installable content) \u2014 see the per-repo output above, or re-run with --allow-empty-cache." : "Setup wrote nothing for this repo \u2014 see the per-repo output above.";
14237
14260
  statuses.push({
14238
14261
  name: repo.name,
14239
14262
  path: repo.path,
@@ -14769,7 +14792,10 @@ program.command("init").description("Full setup: scan, recommend, and optionally
14769
14792
  "--allow-empty-cache",
14770
14793
  "Proceed when no catalog item has installable content: write core files and skip the catalog items. Still exits 1 if none installed (ADR-0060)"
14771
14794
  ).action(runInit);
14772
- program.command("new").description("Create a new project from an allowed Haus stack, push it to GitHub, and run init").argument("[stack]", "Stack id (omit to pick interactively; see --list)").argument("[name]", "Project directory and repo name").option("--list", "Print the allowed stacks and exit").option("--org <org>", "GitHub org to create the repo in (default: personal account)").option("--public", "Create the GitHub repo public").option("--private", "Create the GitHub repo private (default)").option("--no-github", "Skip GitHub repo creation and push").option("--pm <pm>", "Package manager: yarn | npm | pnpm", "yarn").option("-y, --yes", "Skip confirmations (non-interactive)").option("--dry-run", "Print the full plan without executing anything").option("--json", "Emit a machine-readable result document").action(runNew);
14795
+ program.command("new").description("Create a new project from an allowed Haus stack, push it to GitHub, and run init").argument("[stack]", "Stack id (omit to pick interactively; see --list)").argument("[name]", "Project directory and repo name").option("--list", "Print the allowed stacks and exit").option("--org <org>", "GitHub org to create the repo in (default: personal account)").option("--public", "Create the GitHub repo public").option("--private", "Create the GitHub repo private (default)").option("--no-github", "Skip GitHub repo creation and push").option("--pm <pm>", "Package manager: yarn | npm | pnpm", "yarn").option("-y, --yes", "Skip confirmations (non-interactive)").option("--dry-run", "Print the full plan without executing anything").option("--json", "Emit a machine-readable result document").option(
14796
+ "--allow-empty-cache",
14797
+ "Proceed when no catalog item has installable content: write core files and skip the catalog items. Still exits 1 if none installed (ADR-0060)"
14798
+ ).action(runNew);
14773
14799
  program.command("clone").description("Clone a single git repository by URL").argument("<url>", "Git URL to clone").argument("[dir]", "Target directory (default: repo name derived from the URL)").option("--dry-run", "Print what would happen without cloning").action(
14774
14800
  (url, dir, opts) => runClone(url, { dir, dryRun: opts.dryRun })
14775
14801
  );
@@ -0,0 +1,97 @@
1
+ ---
2
+ name: request-assess-impact
3
+ description: Skriv en påverkansanalys för en godkänd spec — vad ändringen rör i systemet och vad som följer av det. Använd alltid när en spec passerat granskning och ska klassas, när användaren säger "kör påverkansanalys", "vad rör det här", "kolla vad som påverkas", eller när `request-classify` behöver underlag och inget finns. Läser kodbasen, avgör tekniska vägval själv och redovisar dem — ställer aldrig frågor tillbaka. Output är ett dokument, aldrig en klass och aldrig ett pris.
4
+ ---
5
+
6
+ # request-assess-impact
7
+
8
+ Du undersöker vad en godkänd spec faktiskt rör i systemet, och skriver ner det. Någon annan klassar. Du vet inte vilka klasser som finns, du nämner dem inte, och du skriver aldrig något som liknar en bedömning av omfattning.
9
+
10
+ Det är inte artighet — det är hela poängen. Skriver du analysen med klassningen i huvudet formulerar du fälten så att svaren blir enkla, och då klassar du i smyg medan klassaren bara kopierar. Din uppgift är att dokumentera systemet.
11
+
12
+ Du ställer aldrig frågor tillbaka. Räcker inte underlaget avgör du och redovisar, eller skriver ordagrant att du inte kunnat verifiera.
13
+
14
+ ## Ingång
15
+
16
+ **Godkänd spec** i Haus specformat. Ett rått önskemål duger inte — då analyserar du din egen tolkning. Saknas specen: avbryt och säg det, skriv den inte själv (`request-write-spec` gör det).
17
+
18
+ **Kodbasen.** Utan repo kan du inte besvara det mesta nedan. Säg till innan du börjar; skriv inte en analys av gissningar.
19
+
20
+ ## Formatet
21
+
22
+ Fasta rubriker, prosa under varje. Inga ja/nej-fält, inga kryssrutor, ingen tabell som liknar ett formulär — en analys som ser ut som ett svarsformulär läses som ett svarsformulär, och då är det du som klassat.
23
+
24
+ **Berörda repon och filer**
25
+ Konkret, med sökvägar. Ligger något under delad kod eller i driftmiljö — infrastruktur, CI/CD, Dockerfiles, miljövariabler, hostingkonfiguration — skriv ut det och skriv vem mer som använder det.
26
+
27
+ **Datalagring**
28
+ Vad som lagras var. Skilj tydligt på serverlagrad struktur (tabell, kolumn, entitet, relation, constraint) och klientlagring (localStorage, sessionStorage, minne). Skriv vilket det är, även när det verkar självklart. Ändras serverstruktur: skriv vad det drar med sig — migrering, driftsättningsordning, backup, gallring — och om planen hanterar det.
29
+
30
+ **Integrationspunkter**
31
+ Kopplingar till system utanför applikationens egen kodbas. Vilka, och om de är nya eller befintliga.
32
+
33
+ **Flöden**
34
+ Sekvenser användaren passerar i ordning, och hur många steg som ändras. Ändras något som nås från flera ställen i gränssnittet: skriv ut att det är flera ingångar till samma åtgärd, så det inte kan missläsas som en sekvens.
35
+
36
+ **Gränssnitt**
37
+ Befintligt mönster som återanvänds, eller något som inte finns idag. Namnge mönstret och var det finns i koden.
38
+
39
+ **Verifiering**
40
+ Vad som kan bevisas automatiskt, och vad som kräver att en människa kör igenom det — i hur många scenarier, på hur många enheter, i hur många miljöer. Endast bygget. Kundens acceptanstest hör inte hit; skriv det inte ens som notering.
41
+
42
+ **Konsekvens av fel**
43
+ Vad som händer i värsta fall om posten blir fel. Konkret, inte kategoriskt: "en notis visas i fel ordning" respektive "en användare ser en annan kunds order" är olika saker. Återanvänds befintlig säkerhets- eller behörighetsmekanik enligt etablerat mönster — skriv det, och skriv att det är etablerat.
44
+
45
+ **Vägval**
46
+ Se nedan.
47
+
48
+ **Antaganden**
49
+ Se nedan.
50
+
51
+ **Beroenden**
52
+ Vad som måste vara klart först — andra poster, ramverk, tredje part.
53
+
54
+ Varje påstående ska vara belagt i koden eller specen. Kan du inte belägga något: skriv ordagrant att du inte kunnat verifiera det, och varför. Det är ett bättre utfall än en rad som ser säker ut — klassaren läser osäkerheten och eskalerar, och det är rätt.
55
+
56
+ ## Vägval
57
+
58
+ En verklig gaffel där båda vägarna fungerar. Du väljer och redovisar valet — det är därför ingen människa behövs i det här steget.
59
+
60
+ Två sorter får du inte ta:
61
+
62
+ **Design, utseende, flöde och UX.** De är kundens. Skriv i stället ut att frågan är öppen, med alternativen. Klassaren skickar posten till Koncept, och det är rätt.
63
+
64
+ **Vägval som ändrar arbetets omfattning väsentligt** — typiskt att lagra på servern i stället för i webbläsaren, eller att bygga nytt i stället för att återanvända. Välj inte tyst. Redovisa båda vägarna med varsin konsekvensbeskrivning och skriv ut att valet är öppet, så att klassaren kan klassa båda och kunden får se dem med varsin prislapp.
65
+
66
+ Du avgör _inte_ om skillnaden når över den gränsen — du känner inte klasserna. Regeln är enkel: rör alternativen olika lager (server mot klient, nytt mot återanvänt, ett flöde mot flera), redovisa båda.
67
+
68
+ Ett valt och redovisat vägval är inget öppet vägval. Skriv "vi lagrar X i Y eftersom Z", inte "X kan lagras i Y eller Z".
69
+
70
+ ## Antaganden
71
+
72
+ Något du tror om systemet men inte verifierat. Inget val.
73
+
74
+ Skriv för varje: vad du antar, och **om det går att verifiera och i så fall hur**. Den uppgiften avgör vad som händer sen — går det att undersöka blir det en Diagnos, går det inte blir det ett villkor i kundens förslag. Utan den kan klassaren inte skilja dem åt, och en hanterad risk blir felaktigt en utredning.
75
+
76
+ Gäller antagandet redan i systemet idag och är känt: skriv det, och skriv var man letar om det visar sig falskt. Då är det ärvd, hanterad risk.
77
+
78
+ Har du repot ska listan vara kort. Det mesta går att verifiera, och varje verifierat antagande är ett villkor mindre senare.
79
+
80
+ ## Output
81
+
82
+ Analysen som ett dokument, en sida. Sedan ett kort block till granskaren:
83
+
84
+ ```
85
+ ---
86
+ ## Till granskaren
87
+
88
+ **Ej verifierat:** [vad du inte kunnat belägga i koden, och varför]
89
+ **Öppna vägval:** [de du lämnat öppna med flit — "inga" om inga]
90
+ **Avvikelse mot specen:** [om koden säger något annat än specen förutsätter]
91
+ ```
92
+
93
+ Sista raden är den viktigaste. Förutsätter specen något som inte stämmer i systemet är det ett fel som ska tillbaka till specen, inte något som ska klassas.
94
+
95
+ ## Vad du aldrig gör
96
+
97
+ Nämner klasser, prisnivåer, timmar eller storleksord. Bedömer om posten borde byggas. Föreslår lösningar utöver det specen redan beskriver. Skriver "detta är enkelt" eller "detta är omfattande" — det är klassarens jobb, och en sådan mening färgar den.
@@ -0,0 +1,86 @@
1
+ ---
2
+ name: request-classify
3
+ description: Kör Haus klassningsprotokoll på en spec plus underlag och levererar klass, belägg per fråga och en kundvänlig motivering. Använd alltid när användaren vill "klassa", "kör klassningen", "sätt klass", "vilken klass blir det här", eller när en spec har en påverkansanalys och ska prissättas. Används även som grind i byggläget på en fullständig implementationsplan före första kodraden — "kör guarden", "omklassa", "stämmer klassen fortfarande". Använd den även när klassning bara antyds, t.ex. när någon klistrar in en spec med underlag och frågar vad det landar på. Output är JSON, aldrig ett pris.
4
+ ---
5
+
6
+ # request-classify
7
+
8
+ Du klassar en post enligt Haus klassningsprotokoll. Reglerna finns i `references/klassningsinstruktion.md` — läs den innan du klassar första posten i en session.
9
+
10
+ **Går filen inte att läsa: klassa inte.** Säg att `references/klassningsinstruktion.md` saknas och att skillen inte kan köras utan den. Fortsätt aldrig på minnet av hur protokollet brukar se ut — det är precis det den här skillen finns för att förhindra, och en klassning som ser rimlig ut men inte följer kriterierna går inte att upptäcka i efterhand. Den är auktoritativ. Det här dokumentet restatar aldrig ett kriterium; två versioner av samma regel glider isär, och då mäter ni protokollet mot sig självt.
11
+
12
+ **Du sätter aldrig pris.** Klass → pris görs av prislistan, av `request-offer`. Ligger prislistan i sammanhanget: använd den inte och säg till. Vet du vad klasserna kostar drar du mot den klass som känns rimlig att ta betalt för.
13
+
14
+ **Du skriver aldrig underlaget.** Saknas det, avbryt med `INPUT_SAKNAS`. Skriv inte en påverkansanalys själv — då bedömer du din egen text, och belägglogiken blir teater. `request-assess-impact` gör det, och helst i en egen körning.
15
+
16
+ ## Två lägen
17
+
18
+ **Prisklassning.** Spec + påverkansanalys. Utfallet blir prisunderlaget till kund och hamnar i `Class (offered)`.
19
+
20
+ **Grind.** Spec + fullständig implementationsplan, efter kundens godkännande och före första kodraden. Utfallet hamnar i `Class (revised)` och diffas fråga för fråga mot prisklassningen.
21
+
22
+ Samma instruktion, samma frågor, samma skill. Bygg aldrig en separat variant för grinden: divergerar lägena mäter grinden skillnaden mellan två promptar i stället för mellan tunn och tjock information, och då är `Class (offered)` mot `Class (revised)` värdelöst som kalibreringsdata.
23
+
24
+ I grindläget: endast ny strukturell information ur fullplanen får ändra ett svar, aldrig en omvärdering av samma fakta. Rapportera vilken fråga som bytte svar och citatet som orsakade bytet. Grinden är symmetrisk — en lägre klass rapporteras lika tydligt som en högre.
25
+
26
+ Bygger fullplanen ett annat vägval än det som såldes är det en klasshändelse även när klassen råkar bli densamma. Skriv ut det.
27
+
28
+ ## Läs underlaget som ett dokument
29
+
30
+ Du hämtar ordagranna citat ur det. Det är bevismaterial, inte parametrar — och det är därför du inte får ha skrivit det.
31
+
32
+ Ge inte dig själv repot i den här körningen. Har du det börjar du verifiera saker vid sidan av underlaget, och då klassar du inte längre det du fått. Saknas något i underlaget är svaret låg konfidens, inte en egen efterforskning.
33
+
34
+ ## Output
35
+
36
+ ```json
37
+ {
38
+ "request_id": "",
39
+ "mode": "pricing | guard",
40
+ "classification": {},
41
+ "customer": {
42
+ "class": "",
43
+ "rationale": "",
44
+ "tradeoffs": [],
45
+ "conditions": [],
46
+ "dependencies": []
47
+ },
48
+ "guard_diff": []
49
+ }
50
+ ```
51
+
52
+ `classification` är instruktionens JSON från Steg 4, **ordagrant och orört**, med sina svenska nycklar. Ändra ingenting där — den ska gå att diffa mot maskinversionen för att se om skillen driftat. Att ytterlagret är engelskt och innerlagret protokollets är avsiktligt, inte slarv.
53
+
54
+ `customer` är ditt eget lager. Domänord, aldrig fältnamn ur något ärendesystem — mappningen till Asana eller annat sker i `request-sync` och ingen annanstans.
55
+
56
+ `guard_diff` fylls bara i grindläget: en post per fråga som bytte svar, med frågans id, båda svaren och citatet ur fullplanen.
57
+
58
+ ### Motiveringen
59
+
60
+ Kundens språk. Beskriver arbetet som ska göras, inte hur protokollet räknade. Inga protokolltermer — bump, basklass, blast radius, touchpoint, B1, S2. Kunden ska kunna invända mot omfattningen, inte mot modellen; en motivering som exponerar mekaniken bjuder in till att argumentera med protokollet i stället för med scopet.
61
+
62
+ Den ska vara förenlig med specens _Ingår inte_. Motiverar du klassen med arbete som specen uttryckligen undantar har du klassat fel post.
63
+
64
+ ### Utfall utan klass
65
+
66
+ Blir utfallet `KONCEPT`, `DIAGNOS` eller `TILLBAKA_TILL_SPEC` sätts `customer.class` till utfallet och `rationale` beskriver i kundvänligt språk vad som behöver hända och varför — de två första går till kund som ett eget förslag.
67
+
68
+ `INPUT_SAKNAS` och `ESKALERA` är interna. `customer` lämnas tomt; de ska aldrig nå kund.
69
+
70
+ ### Öppna vägval
71
+
72
+ Lämnade `request-assess-impact` ett vägval öppet med flit: klassa **båda** vägarna och lägg dem i `tradeoffs` med varsin klass och en rads konsekvens. Välj inte åt kunden.
73
+
74
+ Kalibreringsloggens Vafab-fall är precis det: samma kundbehov, backend- eller webbläsarlagring, en klass i prisskillnad — och valet kom ur intern designdialog snarare än ur kundens behov. Regeln därifrån gäller.
75
+
76
+ ## Efter körning
77
+
78
+ Granskaren läser svaren fråga för fråga, inte slutklassen. Ett rätt slutresultat med fel belägg är en felkalibrering som råkade landa rätt, och den kommer tillbaka i en post där den inte gör det.
79
+
80
+ Korrigeringar går till kriterietexten och kalibreringsloggen — aldrig till priset på den enskilda posten.
81
+
82
+ ## Acceptanstestet — granskarens sak, inte din
83
+
84
+ Skillen verifieras genom att köras mot fall med känt facit. Det underlaget ligger med flit **inte** i den här mappen — en körning som kan läsa facit kan inte längre testas. Du ska varken leta efter det eller rapportera det som saknat; du får underlaget som vanlig input när testet körs, utan att veta att det är ett test.
85
+
86
+ För granskaren: testet gäller **svaren**, inte bara slutklassen. Ett rätt slutresultat på fel belägg är en felkalibrering som råkade landa rätt.
@@ -0,0 +1,146 @@
1
+ # Klassningsinstruktion (maskinversion)
2
+
3
+ **v0.5 · Internt.** Körbar instruktion för AI-klassning av ett inkommande önskemål. Principerna finns i klassningsprotokollet, som är överordnat vid konflikt. Det dokumentet är för människor och ingår inte i körningen — hittar du en regelkonflikt eskalerar du enligt regel 6, och granskaren avgör mot protokollet. v0.5: kriterier måste vara oberoende — samma förhållande får inte räknas två gånger, och touchpoint-kriteriet räknar bara lager som inget annat kriterium redan gjort anspråk på. Hålet uppstod i v0.4 när kriterier gick från antingen-eller till att adderas. v0.4: LARGE tillagd och stegen gjorda enhetliga — ett Medium-kriterium är basen, varje ytterligare kriterium och varje bump är ett steg upp, taket är LARGE. Tidigare saknades steget mellan MEDIUM och offert helt, vilket gjorde varje Medium med en bump till offert. v0.3 efter divergenstest (två körningar, samma underlag, olika klass utan eskalering): flerstegsflöde och datamodelländring omdefinierade. v0.2 efter två oberoende testkörningar: konsekvens-B1, bygg-avgränsad B2, undantag för ärvda antaganden och robusthetsinstruktioner, genomförbarhetsvillkor.
4
+
5
+ ## Roll och uppgift
6
+
7
+ Du klassar ett utvecklingsönskemål till prisklass enligt reglerna nedan. Du fattar inga andra beslut: du bedömer inte om posten bör byggas, du föreslår inga lösningar, du estimerar inte pris. Ditt utfall är antingen en klass, en omdirigering (Koncept/Diagnos/spec) eller en eskalering till människa. Du gissar aldrig — ett svar utan belägg i underlaget är fel svar även om det råkar bli rätt.
8
+
9
+ ## Obligatoriskt underlag — två körlägen
10
+
11
+ Instruktionen körs i två lägen med samma frågor:
12
+
13
+ **Läge 1 — prisklassning:** spec (godkänd av Haus) + klassningsplan (en sida, strukturlagret: berörda repon/filer, serverdatamodell ja/nej, integrationspunkter, sekvensflöden som ändras, designbehov, testbehov i bygget, risker/antaganden, beroenden). Utfallet är prisunderlaget till kund.
14
+
15
+ **Läge 2 — guard:** spec + fullständig implementationsplan (efter kundens godkännande, före första kodraden). Utfallet diffas fråga för fråga mot läge 1. Avvikelse rapporteras med exakt vilken fråga som bytte svar och citatet ur fullplanen som orsakade bytet — endast ny strukturell information får ändra klass.
16
+
17
+ Saknas underlaget för läget, eller är det ofullständigt på någon punkt du behöver för en fråga: avbryt med utfall `INPUT_SAKNAS` och lista exakt vad som saknas. Klassa aldrig på enbart önskemålets formulering.
18
+
19
+ ## Konfiguration [JUSTERA MOT KODBAS — defaults gäller tills dess]
20
+
21
+ ```
22
+ DELAD_KOD: paths/paket som används av >1 kund eller av plattformen
23
+ [DEFAULT: allt under packages/shared, gemensamma Vendure-plugins,
24
+ Elementor-widgets i centralt bibliotek, npm-paket i privat registry]
25
+ DRIFTMILJÖ: [DEFAULT: infrastruktur-repon, CI/CD-konfig, Dockerfiles,
26
+ miljövariabler, Northflank/Vercel-konfiguration]
27
+ KONSEKVENSRISK: fel i posten kan orsaka: felaktig betalning/pris · felaktig
28
+ åtkomst (någon ser/gör det den inte får) · förlust av data som
29
+ inte kan återskapas · att befintlig funktion slutar fungera
30
+ för användare. Typiska hemvister: auth/behörighet, checkout,
31
+ radering/migrering, personuppgiftsflöden — men hemvisten är
32
+ exempel, KONSEKVENSEN är frågan. Ny lagring av användarkopplad
33
+ data räknas alltid (drar med sig gallring/registerutdrag/
34
+ raderingskedjor) — dock som designkrav, inte automatisk bump:
35
+ bump endast om planen INTE redan hanterar det (nyckelval,
36
+ radering, gallring adresserade = hanterat). Återanvändning av
37
+ befintlig säkerhetsmekanik enligt etablerat mönster räknas
38
+ ALDRIG som träff.
39
+ INTEGRATIONSPUNKT: koppling till system utanför applikationens egen kodbas
40
+ [DEFAULT: ERP, betalväxel, e-post/SMS-tjänst, extern API,
41
+ filimport/-export mot tredje part]
42
+ OFFERTGRÄNS: strukturell, aldrig tid — träffas om något gäller:
43
+ paket av ≥3 relaterade delposter · flera fristående flöden/system
44
+ ändras i kombination · ny fristående komponent/plugin byggs
45
+ (inte ändring i befintlig) · planen listar ≥2 av varandra
46
+ oberoende arbetsblock som vart och ett ensamt vore Medium
47
+ KONFIDENSGRÄNS: 0.8 — under denna på någon enskild fråga → eskalera
48
+ ```
49
+
50
+ ## Ordning
51
+
52
+ Kör stegen i exakt denna ordning. Första stopp/omdirigering avslutar körningen.
53
+
54
+ ### Steg 0 — Inputkontroll
55
+
56
+ Finns spec + plan, och täcker planen: berörda filer, datamodell, integrationer, testbehov? Nej → `INPUT_SAKNAS`.
57
+
58
+ ### Steg 1 — Stoppfrågor (JA på någon → omdirigera, klassa inte)
59
+
60
+ **S1 — Oavgjort vägval:** Innehåller planen fler än en föreslagen lösningsväg utan att en är vald, en formulering i stil med "beroende på", "alternativt", "behöver beslutas", eller en designfråga (utseende, flöde, UX) som inte är avgjord i specen? → JA = utfall `KONCEPT`
61
+
62
+ **S2 — Okänd orsak/omfattning:** Bygger planen på en hypotes om vad som är fel eller hur systemet beter sig ("troligen", "bör undersökas", "om det visar sig att")? Är felbild eller nuläge obekräftat — och går det att undersöka? → JA = utfall `DIAGNOS`
63
+
64
+ _Undantag (räknas INTE som S2-träff):_ ett antagande som redan gäller i det befintliga systemet, är dokumenterat i planen och riskmarkerat ("om X visar sig falskt, leta här") är ärvd, hanterad risk — inte en hypotes. Detsamma gäller antaganden som inte GÅR att undersöka (t.ex. kräver åtkomst som bara kunden har): de blir ett genomförbarhetsvillkor (se Steg 3b), aldrig ett stopp — en Diagnos som inte kan utföras löser ingenting.
65
+
66
+ **S3 — Låg konfidens i planen:** Flaggar planen själv osäkerhet om VAD som ska byggas, eller finns obesvarade frågor i specen som påverkar bygget? → JA = utfall `TILLBAKA_TILL_SPEC`
67
+
68
+ _Undantag (räknas INTE som S3-träff):_ robusthetsinstruktioner — "om verktyg X klagar, gör Y", "träffar inte selektorn, byt till det som renderas" — är anvisningar för oväntat verktygsbeteende under bygget. En plan som anger vad man gör när något beter sig oväntat är säkrare än en som inte gör det, inte osäkrare.
69
+
70
+ Osäkerhet får aldrig lösas genom att välja en högre klass "för säkerhets skull".
71
+
72
+ ### Steg 2 — Basklass (första raden uppifrån som stämmer)
73
+
74
+ **ÖVER KLASSERNA** om något av villkoren i OFFERTGRÄNS träffas. → utfall `OFFERT`
75
+
76
+ **LARGE** om exakt 2 av MEDIUM-kriterierna nedan träffas av samma sammanhängande ändring — delar som inte kan levereras var för sig. Går de att leverera separat är det inte en post: den ska delas i flera requests, en per beslut. Träffas ≥3 kriterier → utfall `OFFERT`.
77
+
78
+ **MEDIUM** om exakt ett gäller:
79
+
80
+ - **Flerstegsflöde:** en sekvens användaren passerar i ordning (wizard, checkout, ansökan) där ≥2 steg ändras. Att en funktion syns eller triggas på flera ställen i UI:t är INTE ett flerstegsflöde — flera ingångar till samma markering/åtgärd räknas som ett flöde. Antal ändrade filer, komponenter eller vyer är aldrig i sig ett klasskriterium.
81
+ - **Datamodelländring:** ny/ändrad serverlagrad struktur — tabell, kolumn, entitet, relation eller constraint i databas — även "bara ett unikt-villkor" räknas. Det som gör den till Medium är följdkostnaderna: migrering, driftsättningsordning, backup, gallringsansvar. Klientlagring räknas inte (localStorage/sessionStorage/minne), oavsett hur strukturerad den är — den har inga av följdkostnaderna.
82
+ - **Designinsats:** nytt utseende eller nytt UI-mönster som inte finns i systemet idag (återanvändning av befintlig komponent räknas inte)
83
+ - **≥3 touchpoints** av: frontend · backend · databas · integration. Räkna endast lager som inget annat träffat kriterium redan gör anspråk på — träffar datamodellkriteriet är databaslagret förbrukat, träffar designinsatsen är frontend förbrukat. Kriteriet utvärderas sist och finns för att fånga bredd hos poster som inte träffar något specifikt; det läggs aldrig ovanpå ett kriterium som redan slagit.
84
+
85
+ **SMALL** om: logik ändras (villkor, beräkning, validering, beteende) inom ett avgränsat flöde · högst 1 integrationspunkt berörs · ingen datamodelländring.
86
+
87
+ **TINY** om: ingen logik ändras — text, översättning, konfigurationsvärde, stil/CSS, byte av länk/bild — och planen anger att inga nya tester behöver skrivas.
88
+
89
+ _Rimlighetskontroll:_ om filantal/repon i planen skarpt motsäger klassen (t.ex. Tiny men 15 filer i 3 repon) → sänk konfidensen på basklassfrågan under KONFIDENSGRÄNS.
90
+
91
+ ### Steg 3 — Bumpar (varje JA = ett steg upp; passeras LARGE → `OFFERT`)
92
+
93
+ Klasstegen är TINY → SMALL → MEDIUM → LARGE. Omfattning och risk räknas likadant: ett Medium-kriterium är basen, varje ytterligare kriterium är ett steg upp, och varje bump är ett steg upp. Passeras LARGE åt något håll → `OFFERT`.
94
+
95
+ Alltså: 1 kriterium + 0 bumpar = MEDIUM · 1 + 1 = LARGE · 2 + 0 = LARGE · 2 + 1 = OFFERT · 3 + 0 = OFFERT.
96
+
97
+ Eftersom kriterier nu adderas måste de vara oberoende. Samma förhållande får aldrig räknas två gånger — varken två basklasskriterier som beskriver samma lager, eller en bump som upprepar det basklassen redan fångat. Är du osäker på om två träffar beskriver samma sak: räkna en gång och notera tvekan i frågeraden.
98
+
99
+ Bumpar mäter konsekvens av fel, aldrig att posten tekniskt vidrör en kategori. Taket är undantaget (historiskt ~3 % av poster) — slår protokollet ofta i taket är protokollet felkalibrerat, inte posterna.
100
+
101
+ **B1 — Konsekvensrisk:** Kan ett fel i just denna post orsaka något av det som listas i KONSEKVENSRISK? Ställ frågan konkret: "vad är det värsta som händer om det här blir fel?" — visas en notis felaktigt är svaret nej; debiteras någon fel eller ser någon annans data är svaret ja. (Att koden anropar eller ligger nära känslig mekanik räcker inte; befintliga mönster som återanvänds räknas aldrig.)
102
+
103
+ **B2 — Mänskligt testbehov i bygget:** Kräver byggets egen verifiering manuell mänsklig testning i fler än ett scenario, på flera enheter eller i flera miljöer? Belägg får endast hämtas ur byggets verifieringssteg — acceptanstest, kundmoment och verifiering som utförs av någon annan än Haus räknas aldrig (de är leveransvillkor, inte byggkostnad). Automatiska tester och enkel okulär kontroll räknas inte.
104
+
105
+ **B3 — Blast radius:** Ändras något inom DELAD_KOD eller DRIFTMILJÖ så att fler kunder än beställaren, eller SLA-ytan, kan påverkas?
106
+
107
+ ### Steg 3b — Genomförbarhetsvillkor (påverkar inte klassen)
108
+
109
+ Om planen vilar på ett dokumenterat antagande som inte går att verifiera före bygget (S2-undantaget): notera det som villkor i output. Posten klassas som vanligt; villkoret betyder "bygger på att X — faller X tidigt avbryts posten utan debitering". Kommersiellt är det Haus som tar risken, inte kunden; enstaka avbrutna poster är portföljkostnad, som överdrag. Villkoret ska alltid med i förslaget kunden ser.
110
+
111
+ Notera även **beroenden**: leveranser som måste vara klara först (andra poster, ramverk, tredje part). Beroenden påverkar aldrig klassen — de påverkar leveranslöftet och ska synas i kundens förslag.
112
+
113
+ ### Steg 4 — Output
114
+
115
+ Svara med exakt denna struktur och inget annat:
116
+
117
+ ```json
118
+ {
119
+ "utfall": "TINY | SMALL | MEDIUM | LARGE | OFFERT | KONCEPT | DIAGNOS | TILLBAKA_TILL_SPEC | INPUT_SAKNAS | ESKALERA",
120
+ "basklass": "TINY | SMALL | MEDIUM | LARGE | OVER | null",
121
+ "fragor": [
122
+ {"id": "S1", "svar": "JA/NEJ", "belagg": "ordagrant citat ur spec/plan", "konfidens": 0.0}
123
+ // ... samtliga S1–S3, basklassrad, B1–B3
124
+ ],
125
+ "bumpar_traffade": ["B1", ...],
126
+ "genomforbarhetsvillkor": ["'bygger på att X' — tom lista om inga"],
127
+ "beroenden": ["leveranser som måste vara klara först — tom lista om inga"],
128
+ "slutklass_motivering": "en mening: basklass X pga <kriterium>, +N bump pga <B#>",
129
+ "eskaleringsskal": "endast vid ESKALERA: vilken fråga och varför",
130
+ "saknas": ["endast vid INPUT_SAKNAS"]
131
+ }
132
+ ```
133
+
134
+ ## Hårda regler
135
+
136
+ 1. **Belägg är obligatoriskt.** Varje JA/NEJ ska ha ett ordagrant citat ur spec eller plan. Hittar du inget citat är svaret inte NEJ — det är låg konfidens. Beläggets källa spelar roll: för B2 gäller endast byggets verifieringssteg; citat ur acceptans-/kundmoment är ogiltigt belägg.
137
+ 2. **Konfidens < 0.8 på någon fråga → utfall `ESKALERA`.** Klassa aldrig på gissning. En eskalerad post klassas av människa; det är ett korrekt utfall, inte ett misslyckande. Undantag: om frågan inte är utfallsavgörande (svaret ändrar inte slutklassen åt något håll) räcker det att notera den låga konfidensen i frågeraden.
138
+ 3. **Du klassar planen, inte önskemålet.** Önskemålets ton, storleksord ("en liten grej") eller kundens egen bedömning påverkar ingenting.
139
+ 4. **Tid existerar inte i klassningen.** Timmar, estimat och tidsord förekommer varken i din bedömning eller i något outputfält — alla gränser i denna instruktion är strukturella (vad som ändras), aldrig temporala (hur länge det tar).
140
+ 5. **Ingen prisberäkning.** Klass → pris görs av prislistan, aldrig av dig.
141
+ 6. **Vid regelkonflikt:** eskalera med skälet — ändra aldrig en regel själv.
142
+ 7. **Konsekvens före kategori.** När du tvekar på en bump, ställ alltid frågan "vad är det värsta som händer om posten blir fel?" — och svara på den, inte på om posten nuddar en kategori.
143
+
144
+ ## Efter körning (människans del — inte din)
145
+
146
+ Människan granskar svaren fråga för fråga (inte bara slutklassen), godkänner eller korrigerar, och loggar. Korrigeringar matas tillbaka som kalibrering av konfig och kriterier — aldrig som prisändring på den enskilda posten.