@haus-tech/haus-workflow 4.4.0 → 4.5.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.
@@ -14,6 +14,13 @@ whenever this procedure is reached with **no name** — whether from the no-arg
14
14
  no argument — do **not** silently fall through to Mode B (workspace); ask first, via
15
15
  `AskUserQuestion`:
16
16
 
17
+ **Skip this question when `SKILL.md`'s Step 1.5 already determined the cwd is Workspace
18
+ scope** (a `haus.workspace.yaml`/`repos.manifest.json` already exists at the git root) —
19
+ in that case go straight to Mode B (unless a `name` was already given, in which case Mode
20
+ A takes over as usual). Asking "single repo or whole workspace?" again here would
21
+ contradict a scope Step 1.5 already resolved. Only ask below when Step 1.5 found Project
22
+ or Not-set-up scope (or no scope check ran, e.g. reached from an older caller).
23
+
17
24
  ```
18
25
  "Clone a single repo, or a whole workspace?"
19
26
  Options:
@@ -63,3 +70,187 @@ Clone a whole **workspace** from its manifest. Workspace-only (a `repos.manifest
63
70
  **Transport fallback.** Manifest URLs may be SSH (`git@github.com:org/repo.git` or `ssh://…`). Probe SSH connectivity once up front (`ssh -T git@github.com`); if it fails, **auto-fall back to the HTTPS URL** (`https://github.com/org/repo.git`) using your `gh auth` credentials, clone over HTTPS, and **report that you switched transport** so the user knows their SSH is down. Don't halt the run for an SSH outage when HTTPS works.
64
71
 
65
72
  7. After the loop, report which repos were cloned, reused (local), skipped (already present), and failed. Remind the user that installing dependencies and configuring each repo (`.env`, services) is still a manual step for now.
73
+
74
+ ## Local-dev setup (optional)
75
+
76
+ **Always ask before doing this work — never assume.** Everything from here through Step 4
77
+ hits the network, needs auth, and can touch databases; confirm before each phase, and
78
+ respect repos the user already has.
79
+
80
+ Use `AskUserQuestion`:
81
+
82
+ ```
83
+ "Set the cloned repo(s) up for local dev too?"
84
+ Options:
85
+ 1. Yes
86
+ 2. No
87
+ ```
88
+
89
+ The `cloneandsetup` and `project:cloneandsetup` aliases pre-answer **Yes** — when reached
90
+ via one of those aliases, **skip this `AskUserQuestion` call entirely** and carry the
91
+ resulting repo list straight into Step 1 below. On a bare **No** answer (not via one of
92
+ those aliases), stop after the clone report. On a manual **Yes**, carry the resulting repo
93
+ list into the setup phases below.
94
+
95
+ ### Step 1 — Prerequisite gate (one consolidated check)
96
+
97
+ Before any setup work, probe **everything** the workspace will need and surface **all** gaps in a single prompt — never discover them piecemeal mid-flow. For the repos being set up, check:
98
+
99
+ - **Auth tokens** — `NODE_AUTH_TOKEN` exported (private `@`-scoped npm packages fail without it); `~/.composer/auth.json` (or a repo-local `auth.json`) present if any repo has private composer deps.
100
+ - **Docker daemon** — running (`docker info`); required for any repo's `needs:` services.
101
+ - **PHP environment** — `valet`, `herd`, `ddev`, or `php` on PATH, if any repo is a PHP/WordPress site.
102
+ - **WP-CLI** — `wp --version`, if any repo's `seed:` pulls a WordPress DB.
103
+ - **Node versions** — the versions named in each repo's `.nvmrc` / `engines.node` are installable via `nvm`.
104
+ - **Data-access preflight** — for every data load the workspace will run (each repo's `seed` **and** the workspace `seeds`), check up front that the user can actually reach the source, so nobody ends up with a half-set-up environment that only fails at the seed step. See below.
105
+
106
+ #### Data-access preflight
107
+
108
+ The single most common way cloneAndSetup leaves a **half-completed environment** is discovering at the `seed:` step — after cloning, installing, and bringing up services — that the user lacks the AWS/SSH/etc. access to pull the data. Catch it **here**, before any heavy work, and make the data outcome a deliberate choice.
109
+
110
+ 1. **Collect every seed:** each repo's `seed` plus the workspace `seeds`. For each, determine its required access:
111
+ - If `seed` is an **object with `access`**, run each entry's `check` (a read-only probe; exit 0 = reachable).
112
+ - If `seed` is a **bare string**, infer the channel and probe generically: `aws …`/SSM → `aws sts get-caller-identity`; `dep db:pull …` / `ssh`/`scp`/`rsync <user@host>` → `ssh -o BatchMode=yes -o ConnectTimeout=5 <host> true`; `northflank …` → `northflank whoami`. A purely local seed (no remote) needs nothing.
113
+ 2. **Report one table** alongside the rest of the gate — per data source: its `label`, the access it needs, and ✓ reachable / ✗ + the `login` remedy (verbatim).
114
+ 3. **Resolve every ✗ with one consolidated choice**, then remember it for the run:
115
+ - **Set up access now** → print the `login` text, let the user do it, **re-check**, then seed normally.
116
+ - **Create that DB EMPTY for now** (only when `empty-ok` is true — the default) → the datastore comes up empty (schema via migrations/`needs:`), the app still boots, and the user can run that one seed later. Name the exact command to fill it.
117
+ - **Abort.**
118
+ 4. **Only a gap on a seed with `empty-ok: false` is a HARD STOP** (the repo is genuinely broken without data — log in or abort). Every other gap offers the empty path; one source's missing access never blocks another's.
119
+
120
+ This is the one prompt the "no half-completed environment" goal justifies. The two valid end-states are **seeded** or **deliberately empty** — never a third "did the expensive work then died at seed."
121
+
122
+ Then list the repos and what each will run (node, deps, localdev steps, **data sources + the chosen seed/empty plan**), **report every gap at once**, and get a single go-ahead. For **reused** local clones, ask whether to re-run setup. For each gap, name what it blocks; the user decides whether to fix it now (**ask before any global/system install**) or proceed and skip the affected steps. Don't begin per-repo work until this gate is acknowledged.
123
+
124
+ ### Step 2 — Per-repo dependency pass
125
+
126
+ For each repo, in its own directory, detect and install from the repo's own files (read its `docs/setup.md` / `CLAUDE.md` / `README.md` first — they win). Select node from `.nvmrc`/`engines.node` (`nvm install`), enable the pinned package manager (`corepack enable`), install JS deps (`yarn`/`pnpm`/`npm` by lockfile), composer deps if `composer.json` + `composer` present. Run each repo's steps in one login shell so the node version stays active. Per-repo failure is reported and skipped, not fatal.
127
+
128
+ **Leave `.env` to Step 3 → "Env".** The dependency pass installs only; the env phase writes each repo's `.env` deterministically once services, links, and values are in place.
129
+
130
+ ### Step 3 — Local-dev orchestration
131
+
132
+ This is the phase that takes the workspace from "installed" to "ready to run." It is driven by `localdev.yml` files; a repo with none is set up by Step 2 only.
133
+
134
+ #### House conventions (the "how", so repos specify only the "what")
135
+
136
+ Specify the least. Infer the rest from the repo's stack + these conventions, and work out the concrete commands at run time:
137
+
138
+ - **IMPORTANT — never install anything without asking first; global/system tools especially** (Homebrew, Docker, Laravel Herd, global `npm`/`composer` packages). Detect what's already present; if something required is missing, name it, say why it's needed, and get an explicit **yes** before installing. Prefer the least-invasive option, and don't switch or override tools the dev already has.
139
+ - **PHP / WordPress sites are served by the developer's own PHP environment.** If they already have one — detect `valet`, `herd`, `ddev`, or `php` on PATH — **use it**: just satisfy the env contract (docroot → the repo's `web/`, HTTPS) and report the URL; never override what they already run. **Only when no local PHP environment exists** (a completely fresh machine) suggest installing **[Laravel Herd](https://herd.laravel.com)** as the default (asking first) and point its docroot at `web/` + `herd secure`.
140
+ - **Databases and other services (a repo's `needs:`) run in Docker as a shared, workspace-owned `docker-compose` project** — grouped and self-contained (`up`/`down` together, named volumes), namespaced per workspace instance via `COMPOSE_PROJECT_NAME` + its own host ports so the workspace can run multiple times side by side. Image/port/env per the house convention (`mysql:8`, `postgres:16`, …). Don't reuse a repo's own compose that bind-mounts repo-relative init files (`seed.sql`) — author the service in the workspace compose. Services come up **empty** (seeding is the separate `seed:` step); record chosen host ports in the workspace `localdev.yml` `env` map.
141
+ - **Dependencies** install from the lockfile (Step 2).
142
+ - A repo's `localdev.yml` carries **only what can't be inferred** — its `needs`, repo-specific `build`/`seed` commands, env keys, and URL. Detect the stack (e.g. Bedrock = `composer.json` + `web/` docroot + `wp-cli.yml`; Vendure/Node = `docker-compose.yml` + `@vendure/*`) and apply the matching convention.
143
+
144
+ #### The `localdev.yml` format
145
+
146
+ **Per-repo — `<repo>/.haus-workflow/localdev.yml`** (how to set up THAT repo alone; sibling-agnostic):
147
+
148
+ All fields optional. **Prefer intent (`needs`/`build`/`seed`/`serve`) over explicit `steps`** —
149
+ the conventions above turn intent into commands.
150
+
151
+ ```yaml
152
+ needs: [mysql] # services this repo requires; provisioned in Docker, env wired to them
153
+ needsEnv: [WP_HOME, DB_NAME] # env keys that must be present to run/serve
154
+ build: 'yarn build' # optional: the build command (run after deps)
155
+ seed: 'dep db:pull staging-oderland' # optional: how to load data; remote/destructive → confirm first
156
+ # `seed` may also be an OBJECT when the load needs external access (AWS, SSH, …) so the
157
+ # Step-1 data-access preflight can check it up front and offer an empty-DB fallback:
158
+ seed:
159
+ run: './scripts/pull-aws-db.sh --yes' # the seed command
160
+ empty-ok: true # default true: skipping yields a coherent EMPTY DB (schema via migrations).
161
+ # # false = the repo is broken without data → a missing-access gap HARD-STOPS.
162
+ access: # what the user must have to run `run`; each entry probed in the Step-1 preflight
163
+ - label: 'AWS CLI credentials — eu-north-1 / 842675986884'
164
+ check: 'aws sts get-caller-identity --region eu-north-1' # read-only probe; exit 0 = OK
165
+ login: 'Set up the AWS CLI (brew install awscli; aws configure …) — or pick empty DB.'
166
+ serve: # optional: how it runs (printed as a next-step, never auto-started)
167
+ via: herd # herd | valet | docker | command — for PHP envs, bring-your-own (don't install)
168
+ url: 'https://example.test'
169
+ start: 'yarn dev'
170
+ steps: # optional escape hatch: explicit ordered shell steps when intent isn't enough
171
+ - run: 'composer install'
172
+ node: 10 # nvm version for THIS step
173
+ remote: true # SSH → confirm first
174
+ destructive: true # overwrites data → confirm first
175
+ optional: true # failure is non-fatal, continue
176
+ ```
177
+
178
+ **Workspace — `<workspace>/.haus-workflow/localdev.yml`** (the glue BETWEEN repos):
179
+
180
+ The workspace `env` map is the **single source of truth for cross-repo values** — DB names, ports, host URLs are chosen once and recorded here as literals. The env phase reads values **from here** and writes them into each repo's `.env`. Recording them here (not only in a repo's `.env`) means a later `seed:` / `db:pull` step can find them without depending on env-file load order.
181
+
182
+ ```yaml
183
+ order: [repo-a, repo-b] # setup/startup order, by manifest id
184
+ links:
185
+ - { type: symlink, from: <repo-folder>, to: <repo-folder>/path/to/link }
186
+ - { type: composer-path, in: <repo>, dep: <sibling-repo> }
187
+ - { type: yarn-link, in: [<repo>, ...], dep: <sibling-package-repo> }
188
+ env:
189
+ - value: 'app_local' # a chosen literal — the recorded source of truth, OR …
190
+ # source: { repo: <repo>, provides: '<value>' } # … a value produced by another repo
191
+ sinks:
192
+ - { repo: <repo>, key: DB_NAME } # written under `key` into each sink's .env
193
+ seeds: # optional: data sources NOT owned by a manifest repo (e.g. a shared WordPress host
194
+ # pulled by a workspace script). Same shape as a per-repo `seed` object; included in
195
+ # the Step-1 data-access preflight.
196
+ - label: 'WordPress host (example.se)'
197
+ run: './scripts/pull-wp-example.sh'
198
+ empty-ok: true
199
+ access:
200
+ - label: 'SSH user@host'
201
+ check: 'ssh -o BatchMode=yes -o ConnectTimeout=5 user@host true'
202
+ login: 'Add your SSH key to the host (ssh-add …) / request access — or skip the pull.'
203
+ ```
204
+
205
+ #### Run order
206
+
207
+ 1. **Discover** `.haus-workflow/localdev.yml` in the workspace root and each repo.
208
+ 2. **Resolve order** from the workspace `order` (repos not listed run last, in manifest order). No workspace file → manifest order.
209
+ 3. **Per repo, in order**, apply intent via the conventions:
210
+ - **`needs`** → add the service to the **workspace compose project** and `docker compose up -d` if not already running (namespaced by `COMPOSE_PROJECT_NAME`, free host ports per instance; not the repo's own bind-mounting compose). Comes up **empty**; record host ports in the workspace `localdev.yml` `env` map — data lands in `seed:`.
211
+ - **`build`** → run it (honor any node version the repo's docs note).
212
+ - **`serve`** → for `via: herd` / PHP envs, install nothing — verify the dev's environment serves `web/`, and report the URL.
213
+ - **`seed`** → populate the empty datastore (`seed.run`, or the bare-string form). **Always a distinct, confirm-gated step, separate from `needs:` bring-up** (don't conflate "DB up" with "DB has data"). **Honor the data plan chosen in the Step-1 data-access preflight** — don't re-discover access here: if the user chose _empty_ for this source, skip it (the DB stays empty, schema via migrations) and print the command to fill it later; if they chose _seed_, run it. Still verify the run-time prerequisites the preflight couldn't (the target repo's **`.env` written** — done in the Env step above; WP-CLI present for `wp`/`db:pull` seeds) and **confirm first** for every remote (SSH/AWS) or destructive (overwrites data) seed — every run, even on re-run. Missing prerequisite → skip with a clear message, don't guess.
214
+ - **`steps`** (escape hatch) → run in order, selecting `node:` per step, `optional:` failures continue, **confirming before any `remote:`/`destructive:` step** — every run, even on re-run.
215
+ 4. **Links** (workspace-owned, performed generically — do NOT call a repo's own `setup-dev-mode.sh`, which is deprecated):
216
+ - `symlink` → `ln -s <from> <to>`; replace an existing symlink, but never clobber a real directory without confirmation.
217
+ - `composer-path` → in `in`'s `composer.json`, set the `dep`'s require to `{ "type": "path", "url": "../<dep-folder>", "options": { "symlink": true } }`, then `composer update <vendor/dep>`.
218
+ - `yarn-link` → `yarn link` in the `dep` repo, then `yarn link <pkg-name>` in each `in` repo (read `<pkg-name>` from the dep's `package.json`).
219
+ 5. **Env (deterministic):** write each repo's `.env` from known values — no improvising.
220
+ 1. **Compute** each repo's values, in this precedence: the workspace `localdev.yml` `env` map (chosen literals + cross-repo `source` values — the single source of truth) → generated secrets (DB passwords/tokens minted this run) → **a value already present locally** in a sibling repo's `.env.example` / `.env` (matched by key, or by the same value under a framework-prefix swap `MIX_` ↔ `VITE_` ↔ `NEXT_PUBLIC_` ↔ `REACT_APP_`) — reuse it: it is already on the machine, so copying it into this repo's local (gitignored) `.env` adds nothing new to git → the **dev-defaults table** below for anything still unset.
221
+ 2. **Write** them into each repo's `.env` (create it if absent; **upsert** keys, never clobbering a value the user already set). These are local dev files — write them directly.
222
+ 3. **Secrets that exist nowhere locally** (third-party API keys, prod credentials, license keys the machine doesn't already have — §1 reuse only applies when the value is present locally) can't be invented, so the flow **explicitly tells the user it needs them** — never silently leaving a blank and proceeding as if set up. Put each in as a clearly-marked `KEY=` blank and report it, naming **which key**, **what it unlocks** (the step or feature that fails without it), and **how to get it manually**: from the provider's own account/dashboard (e.g. the Laravel Nova licence page, the Mediaflow API account), by copying it from the deployed environment's env vars (the server / hosting config), or by asking a colleague who already has it. **NEVER write a secret value into a git-tracked file:** `.env` writes are local and gitignored (fine — reuse freely), but the workspace `localdev.yml` `env` map is committed, so it carries non-secret cross-repo literals only (DB names, ports, host URLs), never credentials. Report all blanks together as one actionable list, not piecemeal.
223
+
224
+ **Dev-defaults table** — used only when neither `localdev.yml` nor a generated secret supplies the value:
225
+
226
+ | Key | Default |
227
+ | -------------------------------- | ------------------------------------------------------------------- |
228
+ | `DB_HOST` | `127.0.0.1` |
229
+ | `DB_PORT` | `3306` (mysql) / `5432` (postgres) — or the port chosen on conflict |
230
+ | `DB_USER` / `DB_PASSWORD` | `root` / a generated secret |
231
+ | `WP_HOME`, `WP_SITEURL`, `*_URL` | the repo's `serve.url` |
232
+ | `WP_ENV` / `APP_ENV` | `development` |
233
+
234
+ 6. **Report, then offer to start.** Give the per-repo summary, then **ask the user whether to start everything now.**
235
+ - **Yes** → start each repo in the workspace `order` (its `serve.start`, e.g. `yarn dev`; bring up any remaining foreground services), run any follow-ups (e.g. `wp sync-products sync`), then **print the live URLs** (each repo's `serve.url`).
236
+ - **No** → just print the ordered start commands + follow-ups as next steps; start nothing.
237
+
238
+ **Default is "ready to run" (D2):** the preparation — datastores up (workspace `docker compose` project), seeds applied (confirm-gated), links, builds, each repo's `.env` written — always happens. Starting the **foreground** dev servers and the initial product sync happen **only if the user says yes** to the start prompt above; otherwise they're printed, not run.
239
+
240
+ ### Step 4 — Report and define "done"
241
+
242
+ **"Done" is an explicit terminal state**, not "looks set up". The preparation reaches:
243
+
244
+ - datastores up (workspace `docker compose` project),
245
+ - dependencies installed and builds green,
246
+ - links wired,
247
+ - each repo's `.env` written from known values.
248
+
249
+ From there, **live URLs are reachable only if** every required secret is satisfiable **and** the user starts the servers. If a secret can't be minted (a third-party key, a prod credential), say exactly which keys are still blank and what the user must fill — don't imply links a missing secret will quietly break.
250
+
251
+ Summarise per repo (node, deps, localdev steps, links, env). Then **ask whether to start everything now**:
252
+
253
+ - **Yes** → start the dev servers in workspace `order`, run any follow-ups, and **print the live URLs** so the user can open the running app.
254
+ - **No** → print the ordered start commands + follow-ups instead, and start nothing.
255
+
256
+ Never start the app without that explicit yes.
@@ -10,6 +10,21 @@ the agent; collect answers with AskUserQuestion and pass everything as flags.
10
10
 
11
11
  ## Phase 0 — Entry
12
12
 
13
+ **Scope check first.** If `SKILL.md`'s Step 1.5 found the cwd is **Workspace** scope (or
14
+ **Member repo of a workspace** resolved to "Whole workspace"), ask via `AskUserQuestion`
15
+ before anything else: "Add the new project as a member of this workspace, or scaffold it
16
+ standalone elsewhere?"
17
+
18
+ - **Add as workspace member** — scaffold under the workspace root (default folder: the
19
+ project name, sibling to the other member repos) instead of the cwd. After Phase 3
20
+ creates the repo, tell the user to add an entry for it to `haus.workspace.yaml` /
21
+ `repos.manifest.json` themselves — this flow only scaffolds and does not write either
22
+ file (no CLI command mutates them yet).
23
+ - **Standalone** — proceed exactly as below, unaffected by the workspace.
24
+
25
+ Skip this ask entirely when Step 1.5 found Project or Not-set-up scope — proceed straight
26
+ to the rest of Phase 0.
27
+
13
28
  A description argument ("a frontend app for shopping") goes straight to Phase 1.
14
29
  No-argument invocation: ask "What do you want to build?" as free text. Do not present
15
30
  a stack menu.
@@ -41,6 +41,11 @@ Befintligt mönster som återanvänds, eller något som inte finns idag. Namnge
41
41
  **Verifiering**
42
42
  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.
43
43
 
44
+ Skriv dessutom ut **om den berörda ytan har automatiska tester i dag**, och exakt vilken del som saknar dem. Det avgör om posten ska få en egen testpost, byggd före ändringen. Blanda inte ihop det med sådant som kräver en människa av sin natur — kontroll mot en extern tjänsts testläge, visuell granskning, flera enheter eller miljöer — det står kvar oavsett testsvit.
45
+
46
+ **Delbarhet**
47
+ Faller arbetet i delar som var för sig går att uppskatta? Ett kort svar räcker normalt: ja med delarna uppräknade i en mening, eller nej med skälet. Begär poängsättningen en uppdelning skrivs delarna ut fullständigt, se nedan.
48
+
44
49
  **Konsekvens av fel**
45
50
  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.
46
51
 
@@ -55,6 +60,25 @@ Vad som måste vara klart först — andra poster, ramverk, tredje part.
55
60
 
56
61
  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.
57
62
 
63
+ ## När poängsättningen begär en uppdelning
64
+
65
+ Kommer posten tillbaka med utfallet `DELA_UPP` gick den inte att ringa in mot skalan som helhet. Din uppgift är då att peka ut delarna. Poängsättningen får varken läsa repot eller skriva underlag, så den kan inte göra det själv.
66
+
67
+ **Skriv ut varje del med tre saker:** vad den omfattar, uttryckt som vilken del av specen den täcker; om den går att driftsätta och lämna i produktion på egen hand; och vad den delar med de övriga i fråga om manuell genomgång.
68
+
69
+ Den sista punkten avgör om uppdelningen är värd något. Kräver alla delar samma manuella genomgång av samma flöden i samma miljöer, så multipliceras det tyngsta arbetet av uppdelningen i stället för att delas. Skriv i så fall ut det.
70
+
71
+ **Två sorters delar, och de hamnar olika:**
72
+
73
+ - Går delen att leverera på egen hand blir den en **egen request**, med egen spec och egen leverans. Skriv det, men skriv inte specen — det är `request-write-spec` som gör det.
74
+ - Går den inte att leverera på egen hand blir den en **del av samma post**, som levereras tillsammans med resten.
75
+
76
+ **Delarna ärver postens spec och den här analysen.** Skriv ingen egen spec och ingen egen analys per del. Varje del får en omfattningsrad som säger vilken del av specen den täcker, och poängsätts mot det gemensamma underlaget. Kunden har beställt en sak; uppdelningen är intern struktur.
77
+
78
+ **Dela ett steg.** Kommer en del tillbaka och inte går att ringa in heller, ska den offereras — inte delas vidare. Delar man tills varje bit får plats under skalans tak blir summan för hög av skäl som inte har med arbetet att göra.
79
+
80
+ **Minst två delar.** Blir det bara en har posten inte delats, och då är svaret att den inte är delbar.
81
+
58
82
  ## Vägval
59
83
 
60
84
  **Du beslutar, du frågar inte.** Ett vägval är en gaffel där båda vägarna fungerar. Du väljer och redovisar valet — det är därför steget klarar sig utan en människa, och det är också vad kunden betalar oss för.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: request-score
3
- description: Sätter poäng på en spec plus påverkansanalys genom att jämföra uppdraget mot en skala av sexton referensuppdrag. Använd alltid när användaren vill "sätt poäng", "poängsätt", "kör poängen", "vad landar det här på", "var på skalan ligger det", eller när en spec har en påverkansanalys och ska prissättas. Används även som grind på en fullständig implementationsplan före första kodraden — "kör guarden", "stämmer poängen fortfarande". Använd den även när poängsättning bara antyds, t.ex. när någon klistrar in en spec med underlag och frågar hur stort det är. Output är JSON med poäng och belägg, aldrig ett pris.
3
+ description: Sätter poäng på en spec plus påverkansanalys genom att jämföra uppdraget mot en skala av sjutton referensuppdrag. Använd alltid när användaren vill "sätt poäng", "poängsätt", "kör poängen", "vad landar det här på", "var på skalan ligger det", eller när en spec har en påverkansanalys och ska prissättas. Används även som grind på en fullständig implementationsplan före första kodraden — "kör guarden", "stämmer poängen fortfarande". Använd den även när poängsättning bara antyds, t.ex. när någon klistrar in en spec med underlag och frågar hur stort det är. Output är JSON med poäng och belägg, aldrig ett pris.
4
4
  ---
5
5
 
6
6
  # request-score
@@ -29,44 +29,73 @@ underlaget är svaret låg konfidens, inte en egen efterforskning.
29
29
 
30
30
  ## Stoppfrågorna först
31
31
 
32
- Poäng sätts bara på en post som går att bygga. Tre utfall stoppar poängsättningen:
32
+ Poäng sätts bara på en post som går att bygga. Fyra utfall stoppar poängsättningen:
33
33
 
34
- | Utfall | När | Vad som ska hända |
35
- | --- | --- | --- |
36
- | `KONCEPT` | Formen är okänd — designfråga, oavgjort vägval, flera lösningsvägar utan beslut | Egen post som levererar ett förslag och en poäng på bygget |
37
- | `DIAGNOS` | Orsaken är okänd och går att undersöka. Gäller framför allt fel där ingen vet varför något är trasigt | Tidsboxad undersökning som levererar besked och en poängsatt åtgärd |
38
- | `TILLBAKA_TILL_SPEC` | Specen lämnar ett utfallsval obesvarat **och valet ändrar arbetsmängden** | Specen kompletteras, sedan ny körning |
34
+ | Utfall | När | Vad som ska hända |
35
+ | -------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
36
+ | `KONCEPT` | Formen är okänd — designfråga, oavgjort vägval, flera lösningsvägar utan beslut | Egen post som levererar ett förslag och en poäng på bygget |
37
+ | `DIAGNOS` | Orsaken är okänd och går att undersöka. Gäller framför allt fel där ingen vet varför något är trasigt | Tidsboxad undersökning som levererar besked och en poängsatt åtgärd |
38
+ | `TILLBAKA_TILL_SPEC` | Specen lämnar ett utfallsval obesvarat **och valet ändrar arbetsmängden** | Specen kompletteras, sedan ny körning |
39
+ | `DELA_UPP` | Posten går inte att ringa in på egen hand — se nedan | Ny analysrunda som pekar ut delarna, sedan en körning per del |
39
40
 
40
- ### Obesvarade utfallsval: förutsättning eller stopp
41
+ ### När posten inte går att ringa in
42
+
43
+ Två lägen ger `DELA_UPP`:
44
+
45
+ - Du hittar inget tak, alltså inget referensuppdrag som posten säkert är mindre än.
46
+ - Golv och tak ligger långt isär och inget referensuppdrag finns emellan, alltså det
47
+ läge som annars bara hade gett `confidence: lag`.
48
+
49
+ Skriv i `notes` vilket av lägena det var, och vilka delar av påverkansanalysen som
50
+ pekar mot att posten rymmer flera skilda arbeten. **Peka inte ut delarna själv.** Du
51
+ får varken läsa repot eller skriva underlaget, och att avgöra vad som går att leverera
52
+ för sig kräver båda. `request-assess-impact` gör uppdelningen; du säger bara att den
53
+ behövs.
54
+
55
+ Delningen görs ett steg. Kommer en del tillbaka och inte går att ringa in heller, blir
56
+ den `OVER_SKALAN` och offereras — den delas inte vidare.
57
+
58
+ **En post som redan har poängsatta delar poängsätts aldrig som helhet.** Föräldern bär
59
+ summan av sina delar, inte en egen siffra.
60
+
61
+ ### Obesvarade utfallsval
41
62
 
42
63
  Påverkansanalysens granskarblock listar under _Obesvarade utfallsval_ de val specen
43
64
  lämnat öppna, var och en med alternativen, om de skiljer sig i vad som måste byggas,
44
- och vilket alternativ analysen är skriven utifrån. De ska inte behandlas lika. Ställ en
45
- fråga per val:
65
+ och vilket alternativ analysen är skriven utifrån.
46
66
 
47
- **Ändrar valet hur mycket arbete posten kräver?** Analysens rad om huruvida
48
- alternativen skiljer sig i vad som måste byggas är svaret. Saknas den raden — analysen
49
- kan vara skriven före den här regeln — behandla valet som ett stopp och skriv i `notes`
50
- att analysen behöver kompletteras.
67
+ En post stoppas nästan aldrig för ett sådant val. Gå igenom dem i fyra steg.
51
68
 
52
- **Nej** — då är det en **förutsättning**, inte ett stopp. Välj det rimligaste
53
- alternativet, skriv in det i `assumptions_stated` med vad som valts och varför, och
54
- poängsätt vidare. Förutsättningarna följer med förslaget till kund tillsammans med
55
- specen och priset, så kunden godkänner alla tre på en gång. Väljer kunden annorlunda
56
- är det en ändring som inte rör priset.
69
+ **Steg 1 — stryk det som inte löser problemet.** Läs specens rubrik _Problem_. Ett
70
+ alternativ som inte löser det problemet är inget alternativ, det är en otillräcklig
71
+ lösning, och det ska varken väljas eller presenteras. Att prissätta det vore att sälja
72
+ något som inte gör det kunden bett om.
57
73
 
58
- Typexempel: vilket av två befintliga sätt att visa att något laddar som används, vilken
59
- av två likvärdiga formuleringar en text får, var i en befintlig vy ett fält placeras.
74
+ Testet är specens egna ord, inte din uppfattning om vad kunden borde vilja. Löser
75
+ alternativet problemet som det är beskrivet? Delvis räcker inte.
60
76
 
61
- **Ja** — då stoppar posten. Utfallsvalet avgör vad som ska byggas och därmed vad det
62
- kostar, och att gissa vore att prissätta något kunden inte beställt.
77
+ **Steg 2 — återstår ingenting, stoppa.** Då motsäger specen sig själv: problemet går
78
+ inte att lösa inom det den beskriver. Sätt `outcome` till `TILLBAKA_TILL_SPEC` och
79
+ skriv i `customer_rationale` vad som behöver avgöras, i kundens språk.
63
80
 
64
- Typexempel: om en funktion ska finnas på både mobil och dator eller bara en av dem, om
65
- befintlig data ska konverteras eller lämnas, om ett flöde ska ha ett steg till.
81
+ **Steg 3 — ändrar valet arbetsmängden?** Analysens rad om huruvida alternativen skiljer
82
+ sig i vad som måste byggas är svaret. Gör de inte det är valet en **förutsättning**:
83
+ välj det rimligaste, skriv in det i `assumptions_stated` med vad som valts och varför,
84
+ och poängsätt vidare. Kunden godkänner förutsättningen tillsammans med spec och pris.
66
85
 
67
- Är du osäker på om valet ändrar arbetsmängden: behandla det som ett stopp och skriv
68
- ut varför. Ett onödigt stopp kostar några minuter, ett gissat utfallsval kostar en
69
- felprissatt post.
86
+ Typexempel på förutsättning: vilket av två befintliga sätt att visa att något laddar som
87
+ används, vilken av två likvärdiga formuleringar en text får, var i en befintlig vy ett
88
+ fält placeras.
89
+
90
+ **Steg 4 — flera alternativ som alla löser problemet.** Poängsätt **det enklaste** som
91
+ postens poäng. Poängsätt varje övrigt alternativ för sig, med egen inringning, och lägg
92
+ dem i `alternatives`. De går till kund som alternativa lösningar med egna priser.
93
+
94
+ Saknas analysens rad om arbetsmängd — den kan vara skriven före den regeln — avgör
95
+ själv utifrån vad analysen beskriver, och skriv i `notes` att raden saknades.
96
+
97
+ Varje alternativ ringas in precis som huvudalternativet. Ett alternativ med en poäng
98
+ men utan inringning är lika ogiltigt som en post utan.
70
99
 
71
100
  Ett antagande som inte går att undersöka är inget stopp. Det blir ett villkor på posten:
72
101
  "bygger på att X; faller X tidigt avbryts posten utan debitering". Osäkerhet löses med
@@ -86,8 +115,9 @@ mellan två körningar. Ringa in i stället, i fyra steg. Alla fyra redovisas i
86
115
  Får du inget golv: posten är mindre än A1 och får A1:s poäng. Skalans nedre ände är
87
116
  ändå dominerad av den fasta processavgiften.
88
117
 
89
- Får du inget tak: posten är större än skalans högsta referensuppdrag. Sätt utfallet
90
- till `OVER_SKALAN` och sätt ingen poäng — underlaget för den nivån är för tunt för att
118
+ Får du inget tak: posten är större än skalans högsta referensuppdrag. Sätt då `DELA_UPP`
119
+ och ingen poäng, så att posten först prövas i delar. Är detta redan en del av en delad
120
+ post sätter du i stället `OVER_SKALAN` — underlaget för den nivån är för tunt för att
91
121
  jämföra mot, och posten ska offereras separat.
92
122
 
93
123
  **Steg 3 — snäva in.** Gå igenom referensuppdragen mellan golv och tak. För varje: är
@@ -118,9 +148,9 @@ ligger högt.
118
148
  repot saknar testsvit. Räkna bara det manuella arbete som återstår när en fungerande
119
149
  testsvit gjort sitt: det som kräver en människa av sin natur, alltså kontroll mot en
120
150
  extern tjänsts testläge, visuell granskning, eller genomgång på flera enheter eller i
121
- flera miljöer. Att ett repo saknar tester är Haus egen tekniska skuld, och den ska
122
- kunden inte betala för. Skriv i så fall en rad om det under `notes`, så att skulden
123
- syns i stället för att bakas in i priset.
151
+ flera miljöer. Saknas testsviten är det inte en rabatt: täckningen byggs som en egen
152
+ post före ändringen och poängsätts för sig. Skriv en rad under `notes` om vilken yta
153
+ som saknar täckning, så att den posten går att skapa.
124
154
 
125
155
  **Vad som händer om det blir fel.** Felaktig betalning, felaktig åtkomst till någon
126
156
  annans uppgifter, förlorad data eller bruten befintlig funktion väger tungt. Fel text
@@ -164,7 +194,7 @@ Bygger fullplanen ett annat vägval än det som såldes är det en händelse att
164
194
  {
165
195
  "request_id": "",
166
196
  "mode": "pricing | guard",
167
- "outcome": "POANG | KONCEPT | DIAGNOS | TILLBAKA_TILL_SPEC | OVER_SKALAN | INPUT_SAKNAS",
197
+ "outcome": "POANG | KONCEPT | DIAGNOS | TILLBAKA_TILL_SPEC | DELA_UPP | OVER_SKALAN | INPUT_SAKNAS",
168
198
  "score": null,
169
199
  "type": "",
170
200
  "bracket": {
@@ -177,6 +207,7 @@ Bygger fullplanen ett annat vägval än det som såldes är det en händelse att
177
207
  "confidence": "hog | medel | lag",
178
208
  "conditions": [],
179
209
  "assumptions_stated": [],
210
+ "alternatives": [],
180
211
  "dependencies": [],
181
212
  "notes": [],
182
213
  "customer_rationale": "",
@@ -198,9 +229,20 @@ påverkansanalysen. Citat, inte parafras.
198
229
  arbetsmängden: vad du valt, och varför. De går med förslaget till kund som
199
230
  förutsättningar och godkänns tillsammans med specen och priset.
200
231
 
232
+ `alternatives` är de övriga vägar som också löser problemet, en post var med `label`
233
+ (vad alternativet innebär, i kundens språk), `score`, `bracket` och `adds` (vad som
234
+ tillkommer jämfört med det valda). Alternativ som inte löser specens problem hör inte
235
+ hit och ska utelämnas helt. Är listan tom fanns bara en väg som löser problemet.
236
+
201
237
  `notes` är iakttagelser som inte hör till poängen men som någon bör känna till, till
202
238
  exempel att ett repo saknar testsvit.
203
239
 
240
+ **Ett antagande som analysen flaggat som overifierat ska alltid ut.** Går det att
241
+ undersöka hör det till `notes` med hur det undersöks. Går det inte att undersöka blir
242
+ det ett villkor i `conditions`, formulerat som "bygger på att X; faller X tidigt
243
+ avbryts posten utan debitering". Ett overifierat antagande som varken syns i det ena
244
+ eller det andra försvinner tyst, och då bär Haus en risk ingen beslutat om.
245
+
204
246
  `confidence` sätts till `lag` när posten inte liknar något referensuppdrag, när golv och
205
247
  tak ligger långt isär och inget referensuppdrag finns emellan, eller när
206
248
  påverkansanalysen anger att något inte kunnat verifieras och det påverkar placeringen.
@@ -213,6 +255,10 @@ ser rimlig ut.
213
255
  hur skalan räknade. Nämn aldrig poäng, referensuppdrag, ankare eller skalan. Kunden ska
214
256
  kunna invända mot omfattningen, inte mot modellen.
215
257
 
258
+ Finns alternativ: beskriv dem sist, som egna vägar med var sitt pris. En mening var
259
+ räcker — vad kunden får i stället, och vad som tillkommer. Priset räknas ur poängen
260
+ någon annanstans; skriv aldrig ut poängen.
261
+
216
262
  Den ska vara förenlig med specens _Ingår inte_. Motiverar du poängen med arbete som
217
263
  specen uttryckligen undantar har du poängsatt fel post.
218
264