@haus-tech/haus-workflow 5.6.1 → 5.7.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.
@@ -1,23 +1,23 @@
1
1
  ---
2
2
  name: request-sync
3
- description: Läser och skriver requests mot ärendesystemet (idag Asana), och sätter upp kopplingen mellan ett repo och rätt kundbräda. Använd alltid när användaren vill "sätt upp", "koppla repot mot Asana", "kör uppsättningen", "hämta requests", "vad ligger för granskning", "lägg in poängen i Asana", "skriv tillbaka", "flytta ärendet", "skapa en request", eller när output från `request-write-spec`, `request-assess-impact` eller `request-score` ska landa på brädan. Detta är den enda skillen som känner till fältnamn, statusvärden och brädor — alla andra skills i request-familjen arbetar mot domän-JSON.
3
+ description: Reads and writes requests against the ticket system (Asana today), and sets up the binding between a repo and the right customer board. Always use when the user wants "sätt upp", "koppla repot mot Asana", "kör uppsättningen", "hämta requests", "vad ligger för granskning", "lägg in poängen i Asana", "skriv tillbaka", "flytta ärendet", "skapa en request", or when output from `request-write-spec`, `request-assess-impact` or `request-score` is to land on the board. This is the only skill that knows field names, status values and boards; every other skill in the request family works against domain JSON.
4
4
  ---
5
5
 
6
6
  # request-sync
7
7
 
8
- Du är gränssnittet mot ärendesystemet. Allt som vet vad ett fält heter finns här, och ingen annanstans i request-familjen. Byts Asana ut skrivs den här skillen om och de andra rörs inte.
8
+ You are the interface to the ticket system. Everything that knows what a field is called lives here, and nowhere else in the request family. If Asana is replaced, this skill is rewritten and the others are not touched.
9
9
 
10
- ## Två brädor, en riktning
10
+ ## Two boards, one direction
11
11
 
12
- **Kundbrädan** är ursprunget. Varje kundobjekt har en egen, och det är den kunden ser. Alla requests skapas här.
12
+ **The customer board** is the origin. Every customer object has its own, and it is the one the customer sees. All requests are created here.
13
13
 
14
- **Samlingsbrädan** — TECH · Offer pipeline, `1217668109816859` — är projektionen. Requests hamnar där automatiskt och det är där agenterna plockar arbete.
14
+ **The collection board**, TECH · Offer pipeline, `1217668109816859`, is the projection. Requests end up there automatically, and it is where the agents pick up work.
15
15
 
16
- Skapa aldrig en request direkt på samlingsbrädan. En request som föds där syns aldrig för kunden, och felet upptäcks först när någon undrar varför den saknas.
16
+ Never create a request directly on the collection board. A request born there is never visible to the customer, and the error is discovered only when someone wonders why it is missing.
17
17
 
18
- ## Sessionsstart
18
+ ## Session start
19
19
 
20
- Läs `.haus-workflow/request.json` i repo-roten:
20
+ Read `.haus-workflow/request.json` in the repo root:
21
21
 
22
22
  ```json
23
23
  {
@@ -29,47 +29,47 @@ Läs `.haus-workflow/request.json` i repo-roten:
29
29
  }
30
30
  ```
31
31
 
32
- `customer_section` saknas när brädan inte är indelad i sektioner, och när uppsättningen kördes innan sektionssteget fanns. `customer_section_name` finns bara för att kunna visas i bekräftelser — **matcha alltid på gid, aldrig på namn.**
32
+ `customer_section` is missing when the board is not divided into sections, and when the setup was run before the section step existed. `customer_section_name` exists only so it can be shown in confirmations: **always match on gid, never on name.**
33
33
 
34
- **Saknas filen: kör uppsättningen nedan.** Gissa aldrig utifrån repo-namn eller mappnamn.
34
+ **If the file is missing: run the setup below.** Never guess from the repo name or the folder name.
35
35
 
36
- Finns filen: **verifiera brädan en gång, före första skrivningen.** Slå upp `customer_board` på gid och skriv ut namnet: _"Skriver till Vafab Mina sidor — stämmer det?"_ Att lägga en kunds request på fel kundbräda är det enda felet i hela flödet som en annan kund kan se, och det går inte att städa i tysthet.
36
+ If the file exists: **verify the board once, before the first write.** Look up `customer_board` by gid and print the name: _"Writing to Vafab Mina sidor, is that right?"_ Putting a customer's request on the wrong customer board is the only error in the entire flow that another customer can see, and it cannot be cleaned up quietly.
37
37
 
38
- Brädnamn är inte unika och flera brädor kan höra till samma kund. Använd alltid gid, aldrig namnsökning.
38
+ Board names are not unique, and several boards can belong to the same customer. Always use gid, never a name search.
39
39
 
40
- ## Uppsättning
40
+ ## Setup
41
41
 
42
- Kör den här när `.haus-workflow/request.json` saknas, och när användaren ber om det — "sätt upp", "koppla repot", "kör uppsättningen igen". Ett steg i taget, med användarens svar emellan. Fråga aldrig allt på en gång.
42
+ Run this when `.haus-workflow/request.json` is missing, and when the user asks for it: "sätt upp", "koppla repot", "kör uppsättningen igen". One step at a time, with the user's answer in between. Never ask everything at once.
43
43
 
44
- **1 — Var är repo-roten?**
44
+ **1. Where is the repo root?**
45
45
 
46
- Normalt arbetskatalogen. Är du osäker eller ligger du i en undermapp: fråga. Filen ska ligga i roten, inte där sessionen råkar stå.
46
+ Normally the working directory. If you are unsure, or you are in a subfolder: ask. The file must be in the root, not wherever the session happens to be.
47
47
 
48
- **2 — Vilken kundbräda?**
48
+ **2. Which customer board?**
49
49
 
50
- Be om en Asana-URL — `.../project/<gid>/...` ger dig gid:t direkt och är minst felbenäget. Får du ett kundnamn i stället: sök fram brädorna och visa träffarna med namn och gid, en per rad. Välj aldrig åt användaren när det finns fler än en.
50
+ Ask for an Asana URL: `.../project/<gid>/...` gives you the gid directly and is the least error-prone. If you get a customer name instead: search for the boards and show the hits with name and gid, one per line. Never choose for the user when there is more than one.
51
51
 
52
- Slå upp gid:t och läs upp namnet för bekräftelse. **Ser namnet ut som en test- eller provbräda — "test", "sandbox", "torrsim", ett årtal som passerat — säg det rakt ut och fråga en gång till.** En felkopplad kundbräda upptäcks först när en kund ser någon annans request.
52
+ Look up the gid and read the name back for confirmation. **If the name looks like a test or trial board ("test", "sandbox", "torrsim", a year that has passed), say so plainly and ask once more.** A miswired customer board is discovered only when a customer sees someone else's request.
53
53
 
54
- **3 — Vilken sektion på kundbrädan?**
54
+ **3. Which section on the customer board?**
55
55
 
56
- Vissa kundbrädor är indelade i sektioner, där varje sektion motsvarar ett projekt. En task som skapas utan sektion hamnar i brädans default och blir svår att hitta.
56
+ Some customer boards are divided into sections, where each section corresponds to a project. A task created without a section ends up in the board's default and becomes hard to find.
57
57
 
58
- Hämta brädans sektioner. **En sektion:** ingen fråga, använd den. **Flera sektioner:** lista dem med namn och gid, en per rad, och fråga vilken requests från det här repot ska hamna i.
58
+ Fetch the board's sections. **One section:** no question, use it. **Several sections:** list them with name and gid, one per line, and ask which one the requests from this repo should land in.
59
59
 
60
- **4 — Samlingsbräda?**
60
+ **4. Collection board?**
61
61
 
62
- `1217668109816859` (TECH · Offer pipeline) om inget annat sägs. Nämn den, be om bekräftelse, gå vidare.
62
+ `1217668109816859` (TECH · Offer pipeline) unless told otherwise. Mention it, ask for confirmation, move on.
63
63
 
64
- **5 — Kontrollera uppsättningen**
64
+ **5. Check the setup**
65
65
 
66
- Hämta fälten på båda brädorna och gå igenom tre saker:
66
+ Fetch the fields on both boards and go through three things:
67
67
 
68
- - **Finns fälten?** Kundbrädan behöver `Original request`, `Original request title`, `Customer`, `Requested by` och kundstatusfältet. Samlingsbrädan behöver dessa plus `Offer status`, `Source`, `Request type`, `Funding`, `Score`, `Has conditions`, `Decision`, `Decision reason`, `Spec iterations`, `Score iterations`.
69
- - **Är de delade fälten samma fält?** Jämför gid, inte namn. Två fält med samma namn och olika gid är en dubblett — de driftar isär omedelbart, och felet syns först hos kunden.
70
- - **Går alternativen att slå upp?** Normaliserat, enligt regeln ovan. Rapportera vilka som inte gick att matcha och vilka alternativ som faktiskt finns.
68
+ - **Do the fields exist?** The customer board needs `Original request`, `Original request title`, `Customer`, `Requested by` and the customer status field (`Kundstatus`). The collection board needs these plus `Offer status`, `Source`, `Request type`, `Funding`, `Score`, `Has conditions`, `Decision`, `Decision reason`, `Spec iterations`, `Score iterations`.
69
+ - **Are the shared fields the same field?** Compare gid, not name. Two fields with the same name and different gids are a duplicate: they drift apart immediately, and the error shows up first with the customer.
70
+ - **Can the options be looked up?** Normalized, per the rule above. Report which ones could not be matched and which options actually exist.
71
71
 
72
- **6 — Skriv filen**
72
+ **6. Write the file**
73
73
 
74
74
  ```json
75
75
  {
@@ -81,17 +81,17 @@ Hämta fälten på båda brädorna och gå igenom tre saker:
81
81
  }
82
82
  ```
83
83
 
84
- Skriv den även om steg 5 hittade problem — filen handlar om vilka brädor som gäller, fältuppsättningen är ett annat problem och ska inte blockera. Redovisa innehållet, och säg att filen bör checkas in: ett fel board-gid syns då i en diff i stället för att upptäckas i efterhand.
84
+ Write it even if step 5 found problems: the file is about which boards apply, the field setup is a separate problem and must not block. Present the content, and say that the file should be checked in: a wrong board gid then shows up in a diff instead of being discovered after the fact.
85
85
 
86
- **7 — Rapportera och gå vidare**
86
+ **7. Report and move on**
87
87
 
88
- Sammanfatta vad som skrevs och vad som saknas, i den ordningen. Fortsätt sedan med det användaren faktiskt bad om — uppsättningen är en förutsättning, inte ett ärende i sig.
88
+ Summarize what was written and what is missing, in that order. Then continue with what the user actually asked for: the setup is a prerequisite, not a task in itself.
89
89
 
90
- Hittade steg 5 problem som gör nästa operation omöjlig, säg vilket och stanna där. Halvskapade requests är dyrare att reda ut än ett avbrott.
90
+ If step 5 found problems that make the next operation impossible, say which one and stop there. Half-created requests are more expensive to untangle than an interruption.
91
91
 
92
- ## Slå upp fälten, hårdkoda dem aldrig
92
+ ## Look up the fields, never hardcode them
93
93
 
94
- Fält och alternativ har GID:n som byter när någon bygger om. Hämta dem en gång per session:
94
+ Fields and options have GIDs that change when someone rebuilds. Fetch them once per session:
95
95
 
96
96
  ```
97
97
  get_project(project_id, opt_fields="custom_field_settings.custom_field.name,
@@ -100,63 +100,63 @@ get_project(project_id, opt_fields="custom_field_settings.custom_field.name,
100
100
  custom_field_settings.custom_field.enum_options.gid")
101
101
  ```
102
102
 
103
- Bygg en map namn → gid, och för enum-fält även alternativnamn → gid. Skrivningar sker alltid med GID.
103
+ Build a map name → gid, and for enum fields also option name → gid. Writes always use GID.
104
104
 
105
- **Matcha alternativnamn normaliserat.** Människor redigerar dem i Asana — emoji läggs till, ord byts, mellanslag flyttas. Jämför på den alfabetiska kärnan: strippa emoji, symboler och kringliggande blanksteg, och jämför skiftlägesokänsligt. `✅ Ordered` och `Ordered` är samma alternativ.
105
+ **Match option names normalized.** People edit them in Asana: emoji get added, words get swapped, spaces get moved. Compare on the alphabetic core: strip emoji, symbols and surrounding whitespace, and compare case-insensitively. `✅ Ordered` and `Ordered` are the same option.
106
106
 
107
- Värdenamnen nedan är därför beskrivningar av betydelse, inte strängar att kräva exakt. Hittar du ett alternativ vars kärna motsvarar det du söker: använd det. Hittar du flera kandidater eller ingen: avbryt och visa vilka alternativ som finns.
107
+ The value names below are therefore descriptions of meaning, not strings to require exactly. If you find an option whose core corresponds to what you are looking for: use it. If you find several candidates or none: abort and show which options exist.
108
108
 
109
- Hittar du inte ett fält du ska skriva till: avbryt och säg vilket. Skapa aldrig ett nytt fält och gissa aldrig på ett närliggande — ett värde i fel fält är värre än inget värde, eftersom det ser rätt ut i en vy.
109
+ If you cannot find a field you are to write to: abort and say which one. Never create a new field and never guess at a nearby one: a value in the wrong field is worse than no value, because it looks right in a view.
110
110
 
111
- ## Mappningen
111
+ ## The mapping
112
112
 
113
- Domän-JSON till vänster, fältnamn till höger. Ingen annan skill får se högerkolumnen.
113
+ Domain JSON on the left, field names on the right. No other skill may see the right-hand column.
114
114
 
115
- | Domän | Fält | Not |
116
- | --------------------------- | ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- |
117
- | `request_id` | task gid | — |
118
- | `original_request` | Original request + beskrivningens `## Ursprunglig förfrågan` | Skrivs en gång. Ändras aldrig, inte ens för stavfel |
119
- | `original_request_title` | Original request title | Skrivs en gång. Ändras aldrig, inte ens för stavfel. Tomt när kunden aldrig skrev en rubrik |
120
- | `source` | Source | Kunden skrev själv · Mejl · Möte · Haus initierade |
121
- | `customer` | Customer | |
122
- | `requested_by` | Requested by | people-fält, kräver user gid |
123
- | `type` | Request type | Bug · Improvement · Wear |
124
- | `funding` | Funding | |
125
- | `spec` | beskrivningens `## Specifikation` | Bara den sektionen. Låst efter `Decision`, se nedan |
126
- | `impact` | subtask `Påverkansanalys vN` | Ny subtask varje varv, aldrig en redigering |
127
- | `bracket` + `drivers` | subtask `Poängsättning vN` | Ny subtask varje varv, aldrig en redigering |
128
- | `score` | Score | Skrivs aldrig över efter kundbeslut. Aldrig på en förälder som har poängsatta delar |
129
- | `outcome: DELA_UPP` | ingen `Score`, posten stannar på ✅ Spec ok | Se _Uppdelade poster_. Räknarna rörs inte — de räknar granskningsvarv, och ingen människa har skickat tillbaka posten |
130
- | delposter | subtask per del, med `Score` | Se _Uppdelade poster_ |
131
- | `customer_rationale` | beskrivningens `## Lösning och omfattning` | Låst efter `Decision`, se nedan |
132
- | `assumptions_stated` ej tom | beskrivningens `## Lösning och omfattning` | Kunden godkänner dem med priset |
133
- | `alternatives` ej tom | beskrivningens `## Lösning och omfattning` | Egna vägar med var sitt pris |
134
- | `conditions` ej tom | Has conditions | se nedan |
135
- | `decision` | Decision | Beställd · Ej beställd · Uppskjuten |
136
- | `decision_reason` | Decision reason | |
115
+ | Domain | Field | Note |
116
+ | ------------------------------ | --------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
117
+ | `request_id` | task gid | — |
118
+ | `original_request` | Original request + the description's `## Ursprunglig förfrågan` | Written once. Never changed, not even for typos |
119
+ | `original_request_title` | Original request title | Written once. Never changed, not even for typos. Empty when the customer never wrote a title |
120
+ | `source` | Source | Kunden skrev själv · Mejl · Möte · Haus initierade |
121
+ | `customer` | Customer | |
122
+ | `requested_by` | Requested by | people field, requires a user gid |
123
+ | `type` | Request type | Bug · Improvement · Wear |
124
+ | `funding` | Funding | |
125
+ | `spec` | the description's `## Specifikation` | Only that section. Locked after `Decision`, see below |
126
+ | `impact` | subtask `Påverkansanalys vN` | A new subtask every round, never an edit |
127
+ | `bracket` + `drivers` | subtask `Poängsättning vN` | A new subtask every round, never an edit |
128
+ | `score` | Score | Never overwritten after a customer decision. Never on a parent that has scored parts |
129
+ | `outcome: DELA_UPP` | no `Score`, the item stays at ✅ Spec ok | See _Split items_. The counters are not touched: they count review rounds, and no human has sent the item back |
130
+ | split parts | a subtask per part, with `Score` | See _Split items_ |
131
+ | `customer_rationale` | the description's `## Lösning och omfattning` | Locked after `Decision`, see below |
132
+ | `assumptions_stated` non-empty | the description's `## Lösning och omfattning` | The customer approves them with the price |
133
+ | `alternatives` non-empty | the description's `## Lösning och omfattning` | Separate routes, each with its own price |
134
+ | `conditions` non-empty | Has conditions | see below |
135
+ | `decision` | Decision | Beställd · Ej beställd · Uppskjuten |
136
+ | `decision_reason` | Decision reason | |
137
137
 
138
- **`Class rationale` skrivs inte längre.** Motiveringen bor i beskrivningens `## Lösning och omfattning`,
139
- där kunden faktiskt läser den. Att skriva samma text på två ställen gav två versioner
140
- som kunde glida isär, och fältet fanns bara för att kunna filtrera på en text ingen
141
- filtrerar på. Fältet får ligga kvar i Asana med sina gamla värden som historik.
138
+ **`Class rationale` is no longer written.** The rationale lives in the description's `## Lösning och omfattning`,
139
+ where the customer actually reads it. Writing the same text in two places gave two versions
140
+ that could drift apart, and the field existed only to filter on a text nobody
141
+ filters on. The field may stay in Asana with its old values as history.
142
142
 
143
- **Fältnamnen byttes 2026-09-08 (ADR-0121):** `Score iterations` hette `Class iterations`
144
- och `Has conditions` hette `Conditional classification`. Innehållet är oförändrat i sak,
145
- en räknare över poängsättningsvarv och en ja/nej-flagga för villkor. En bräda som
146
- fortfarande bär de gamla namnen är inte uppdaterad: säg till i stället för att gissa.
143
+ **The field names changed on 2026-09-08 (ADR-0121):** `Score iterations` was called `Class iterations`
144
+ and `Has conditions` was called `Conditional classification`. The content is unchanged in substance:
145
+ a counter over scoring rounds and a yes/no flag for conditions. A board that
146
+ still carries the old names is not updated: say so instead of guessing.
147
147
 
148
- `Score` är ett taltfält. Skriv poängen som tal, inte som text, så att den går att
149
- sortera och summera i listvyer.
148
+ `Score` is a number field. Write the score as a number, not as text, so that it can be
149
+ sorted and summed in list views.
150
150
 
151
- `Has conditions` är härledd: villkorslistan är tom eller inte. Sätt alltid ett värde när en poängsättning skrivs — aldrig tomt, eftersom tomt är reserverat för "ingen har poängsatt än". Bär fältet flera värden samtidigt är det felkonfigurerat som flerval; skriv ett värde och säg till.
151
+ `Has conditions` is derived: the conditions list is empty or it is not. Always set a value when a score is written, never empty, because empty is reserved for "nobody has scored yet". If the field carries several values at once, it is misconfigured as multi-select; write one value and say so.
152
152
 
153
- Asana konverterar markdown till `html_notes`. Använd `html_notes` för beskrivningen, för subtaskarnas innehåll och för kommentarer, inte `notes`, annars tappas struktur — rubriker, kursiv text och de vågräta linjerna mellan sektionerna försvinner alla med `notes`.
153
+ Asana converts markdown to `html_notes`. Use `html_notes` for the description, for the subtasks' content and for comments, not `notes`, otherwise structure is lost: headings, italic text and the horizontal lines between the sections all disappear with `notes`.
154
154
 
155
- ## Beskrivningen och subtaskarna
155
+ ## The description and the subtasks
156
156
 
157
- **Beskrivningen är det enda någon faktiskt läser, och det är dessutom vad kunden ser.** Den ska därför bära requesten i läsbar form och ingenting annat. Maskineriet — påverkansanalys och poängsättning — ligger i subtasks, som inte ärver kundbrädan och därmed är interna utan att någon behöver komma ihåg det.
157
+ **The description is the only thing anyone actually reads, and it is also what the customer sees.** It must therefore carry the request in readable form and nothing else. The machinery (impact analysis and scoring) lives in subtasks, which do not inherit the customer board and are therefore internal without anyone needing to remember it.
158
158
 
159
- Beskrivningen har de här rubrikerna, i den här ordningen, åtskilda av en vågrät linje:
159
+ The description has these headings, in this order, separated by a horizontal line:
160
160
 
161
161
  ```
162
162
  ## Ursprunglig förfrågan
@@ -179,189 +179,190 @@ _Vad vi gör, vad det bygger på och vad det kostar — uppdaterad 2026-08-21_
179
179
  [kundvänlig motivering, därunder eventuella förutsättningar]
180
180
  ```
181
181
 
182
- **Linjen mellan sektionerna** skrivs som `<hr/>`. Asana renderar den i tasks — till skillnad från `<h3>`, som inte finns i beskrivningsfältet. Håll dig därför till `<h2>` för rubrikerna (`##` i markdown), aldrig en nivå djupare.
182
+ **The line between the sections** is written as `<hr/>`. Asana renders it in tasks, unlike `<h3>`, which does not exist in the description field. So stick to `<h2>` for the headings (`##` in markdown), never a level deeper.
183
183
 
184
- **Raden under varje rubrik** säger vad sektionen är och när den senast skrevs. Den finns för att sektionerna kommer från skilda tillfällen: utan den läses beskrivningen som en text skriven i ett svep, och då ser det ut som att allt beslutats samtidigt. Datumet gör det synligt att specen och priset tillkom efter kundens ursprungliga ord — och att de kan ha tillkommit olika dagar.
184
+ **The line under each heading** says what the section is and when it was last written. It exists because the sections come from separate occasions: without it the description reads as a text written in one sweep, and then it looks as if everything was decided at the same time. The date makes it visible that the spec and the price came after the customer's original words, and that they may have come on different days.
185
185
 
186
- Raden är i kundens språk, precis som resten av beskrivningen. Aldrig skillnamn, aldrig protokolltermer, aldrig "genererad av". Datumformat `ÅÅÅÅ-MM-DD`. `## Ursprunglig förfrågan` får skapandedagens datum och behåller det för alltid — den sektionen skrivs aldrig om, så "uppdaterad" hör inte hemma där.
186
+ The line is in the customer's language, just like the rest of the description. Never skill names, never protocol terms, never "generated by". Date format `YYYY-MM-DD`. `## Ursprunglig förfrågan` gets the creation date and keeps it forever: that section is never rewritten, so the "uppdaterad" (updated) wording does not belong there.
187
187
 
188
- **Ta datumet från systemet, inte ur minnet.** En modell som skriver dagens datum på känsla skriver ofta fel år, och ett felaktigt datum är värre än inget: det ser ut som ett faktum.
188
+ **Take the date from the system, not from memory.** A model that writes today's date by feel often writes the wrong year, and a wrong date is worse than none: it looks like a fact.
189
189
 
190
- Varje sektion har exakt en källa:
190
+ Each section has exactly one source:
191
191
 
192
- | Sektion | Källa | Skrivs |
193
- | --------------------------- | --------------------------------------------------------------------------- | ---------------------------- |
194
- | `## Ursprunglig förfrågan` | kundens ord, eller utdraget ur mejl/brief | vid skapandet, sedan aldrig |
195
- | `## Specifikation` | `request-write-spec` | vid varje specvarv |
196
- | `## Lösning och omfattning` | `request-score`: `customer_rationale`, `assumptions_stated`, `alternatives` | vid varje poängsättningsvarv |
192
+ | Section | Source | Written |
193
+ | --------------------------- | --------------------------------------------------------------------------- | ----------------------- |
194
+ | `## Ursprunglig förfrågan` | the customer's words, or the excerpt from the email/brief | at creation, then never |
195
+ | `## Specifikation` | `request-write-spec` | on every spec round |
196
+ | `## Lösning och omfattning` | `request-score`: `customer_rationale`, `assumptions_stated`, `alternatives` | on every scoring round |
197
197
 
198
- - **Varje steg uppdaterar bara sin egen sektion och lämnar resten orörd.** Tidigare skrev bara en skill till beskrivningen; nu skriver flera, och ett steg som ersätter hela fältet raderar de andras arbete. Det gäller även datumraden: du uppdaterar din egen sektions rad, aldrig någon annans.
199
- - **Ursprunglig förfrågan** är kundens ord ordagrant, eller det relevanta utdraget ur ett längre mejl eller en brief. Skrivs en gång, redigeras aldrig. Fältet `Original request` behålls parallellt som skrivskyddad kopia.
200
- - **Lösning och omfattning** är den kundvänliga motiveringen, de förutsättningar posten vilar på, och eventuella alternativa lösningar med var sitt pris. Inga protokolltermer, inga frågerader, **och aldrig poängen** — den är ett internt mått och står bara i `Score`-fältet. Ordningen är motivering, förutsättningar, alternativ. Förutsättningarna godkänner kunden tillsammans med priset; alternativen är andra vägar kunden kan välja i stället, var och en med sitt eget pris.
198
+ - **Each step updates only its own section and leaves the rest untouched.** Previously only one skill wrote to the description; now several do, and a step that replaces the whole field erases the others' work. This also applies to the date line: you update your own section's line, never anyone else's.
199
+ - **`## Ursprunglig förfrågan`** is the customer's words verbatim, or the relevant excerpt from a longer email or brief. Written once, never edited. The `Original request` field is kept in parallel as a read-only copy.
200
+ - **`## Lösning och omfattning`** is the customer-friendly rationale, the assumptions the item rests on, and any alternative solutions, each with its own price. No protocol terms, no question rows, **and never the score**: it is an internal measure and appears only in the `Score` field. The order is rationale, assumptions, alternatives. The customer approves the assumptions together with the price; the alternatives are other routes the customer can choose instead, each with its own price.
201
201
 
202
- Beskrivningen har ingen sektion för vägval, och ska inte få en. Val som påverkar vad kunden får besvaras i specen — som acceptanskriterium eller under _Ingår inte_ — och är därmed redan en del av `## Specifikation`. Val som bara rör hur vi bygger är interna och ligger i påverkansanalysens subtask.
202
+ The description has no section for route choices, and must not get one. Choices that affect what the customer gets are answered in the spec, as an acceptance criterion or under _Ingår inte_ / _Not included_, and are thereby already part of `## Specifikation`. Choices that only concern how we build are internal and live in the impact analysis's subtask.
203
203
 
204
- ### Beskrivningen låses vid kundbeslut
204
+ ### The description is locked at the customer decision
205
205
 
206
- Så snart `Decision` har ett värde är beskrivningen det kunden sagt ja till. **Skriv inte om någon sektion efter den punkten.** Det gäller alla tre sektionerna, oavsett vad som ändrats.
206
+ As soon as `Decision` has a value, the description is what the customer has said yes to. **Do not rewrite any section after that point.** This applies to all three sections, regardless of what has changed.
207
207
 
208
- Ägartabellen ovan beskriver vad som gäller fram till kundbeslutet.
208
+ The ownership table above describes what applies up to the customer decision.
209
209
 
210
- **Läs `Decision` innan varje skrivning till beskrivningen.** Det är en kontroll som ska utföras, inte något du antar utifrån ärendets status — ett ärende kan ha fått ett beslut mellan två av dina körningar.
210
+ **Read `Decision` before every write to the description.** It is a check that must be performed, not something you assume from the ticket's status: a ticket can have received a decision between two of your runs.
211
211
 
212
- Körs poängsättningen om på ett ärende som redan har ett `Decision`: skriv inte över
213
- `Score`. Lägg poängsättningen som ny subtask, flytta posten till ⚖️ och stanna där. En
214
- människa avgör vad kunden får se, och det blir i så fall ett nytt beslut — inte en tyst
215
- redigering av ett gammalt. Kontrollera `Decision` innan du skriver, oavsett varför
216
- körningen gjordes.
212
+ If the scoring is rerun on a ticket that already has a `Decision`: do not overwrite
213
+ `Score`. Add the scoring as a new subtask, move the item to ⚖️ and stop there. A
214
+ human decides what the customer gets to see, and in that case it becomes a new decision, not a silent
215
+ edit of an old one. Check `Decision` before you write, regardless of why the
216
+ run was made.
217
217
 
218
- Skälet: en poäng som stiger är en prishöjning. Sker den genom att beskrivningen skrivs om finns inget spår av vad kunden faktiskt godkände, och den fria exiten — modellens centrala kundlöfte — går inte att lösa ut, eftersom det inte längre går att visa vad som ändrats.
218
+ The reason: a score that rises is a price increase. If it happens by rewriting the description, there is no trace of what the customer actually approved, and the free exit (the model's central customer promise) cannot be exercised, because it is no longer possible to show what has changed.
219
219
 
220
- Låset gäller beskrivningen och `Score`, som bär den poäng priset räknades ur. Det som är internt får alltid skrivas — subtaskarna (det är hela poängen med dem: de är löpnumrerade och ärver inte kundbrädan, så en poängsättning efter kundbeslut landar där som vanligt), `Has conditions` och räknarna.
220
+ The lock covers the description and `Score`, which carries the score the price was calculated from. What is internal may always be written: the subtasks (that is the whole point of them: they are numbered sequentially and do not inherit the customer board, so a scoring after a customer decision lands there as usual), `Has conditions` and the counters.
221
221
 
222
- ### Grindläget stöds inte
222
+ ### Guard mode is not supported
223
223
 
224
- `request-score` kan köras som grind på en fullständig implementationsplan, med
225
- `mode: "guard"`. **Den utdatan skrivs inte till Asana.** Det finns inget fält för en
226
- reviderad poäng och inget ska skapas förrän någon bestämt hur en avvikelse ska hanteras
227
- mot kund.
224
+ `request-score` can be run as a gate on a complete implementation plan, with
225
+ `mode: "guard"`. **That output is not written to Asana.** There is no field for a
226
+ revised score, and none may be created until someone has decided how a deviation is to be handled
227
+ toward the customer.
228
228
 
229
- Får du en körning med `mode: "guard"`: avbryt, säg att grindläget inte synkas, och
230
- lämna ärendet orört. Skriv aldrig guard-poängen till `Score` — det fältet bär den
231
- poäng kunden fick sitt pris på.
229
+ If you get a run with `mode: "guard"`: abort, say that guard mode is not synced, and
230
+ leave the ticket untouched. Never write the guard score to `Score`: that field carries the
231
+ score the customer got their price on.
232
232
 
233
- **Påverkansanalys och poängsättning läggs som subtasks, inte som kommentarer.**
233
+ **Impact analysis (`Påverkansanalys vN`) and scoring (`Poängsättning vN`) are added as subtasks, not as comments.**
234
234
 
235
235
  ```
236
236
  Ticket
237
- ├── beskrivning: de tre sektionerna ovan
238
- └── Analys (subtask, ingen Score)
239
- ├── Påverkansanalys vN (under Analys, ingen Score)
240
- └── Poängsättning vN (under Analys, ingen Score)
237
+ ├── description: the three sections above
238
+ └── Analys (subtask, no Score)
239
+ ├── Påverkansanalys vN (under Analys, no Score)
240
+ └── Poängsättning vN (under Analys, no Score)
241
241
  ```
242
242
 
243
- Är posten uppdelad, eller saknar den berörda ytan tester, tillkommer arbetsrader ovanför
244
- Analys:
243
+ If the item is split, or the affected area lacks tests, work rows are added above
244
+ `Analys`:
245
245
 
246
246
  ```
247
- Ticket (ingen egen Score — bär summan av delarna)
248
- ├── beskrivning: de tre sektionerna ovan
249
- ├── Testtäckning – <yta> (subtask, egen Score)
250
- ├── <delmängd A> (subtask, egen Score)
251
- ├── <delmängd B> (subtask, egen Score)
252
- └── Analys (subtask, ingen Score)
253
- ├── Påverkansanalys vN (under Analys, ingen Score)
254
- └── Poängsättning vN (under Analys, ingen Score)
247
+ Ticket (no Score of its own; carries the sum of the parts)
248
+ ├── description: the three sections above
249
+ ├── Testtäckning – <yta> (subtask, own Score)
250
+ ├── <subset A> (subtask, own Score)
251
+ ├── <subset B> (subtask, own Score)
252
+ └── Analys (subtask, no Score)
253
+ ├── Påverkansanalys vN (under Analys, no Score)
254
+ └── Poängsättning vN (under Analys, no Score)
255
255
  ```
256
256
 
257
- Se _Analysraderna_ längre ned för vad Analys är och när den skapas.
257
+ See _The `Analys` rows_ further down for what `Analys` is and when it is created.
258
258
 
259
- - Subtasks ärver inte kundbrädan och är därmed interna. Kontrollera att de inte multi-homas dit.
260
- - **Ett nytt varv skapar en ny subtask, den ersätter inte den förra.** Namnge med löpnummer: "Poängsättning v1", "Poängsättning v2". Beskrivningen kan inte bära historik, och det är subtaskarna som ger den tillbaka — i läsbar form, till skillnad från en kommentarsström.
261
- - Poängsättningens subtask innehåller inringningen i läsbar form — vilka referensuppdrag posten vägdes mot, om den är större eller mindre än vart och ett, och på vilken grund — och därunder den råa JSON:en. Inringningen är det granskaren läser: en rimlig poäng med fel jämförelser är en felkalibrering som råkade landa rätt.
262
- - Finns flera parallella underlag namnges subtaskarna så parningen syns: "Poängsättning — delad komponent", "Poängsättning — specialfall".
259
+ - Subtasks do not inherit the customer board and are therefore internal. Check that they are not multi-homed there.
260
+ - **`Analys` and its rows carry English body text under a Swedish title.** Their content is internal, like the analysis and the scoring it holds; the titles `Analys`, `Påverkansanalys vN` and `Poängsättning vN` are lookup keys and are never translated. The description's three sections follow the customer's language instead (_The language contract_ in `request/SKILL.md`).
261
+ - **A new round creates a new subtask; it does not replace the previous one.** Name them with a sequence number: "Poängsättning v1", "Poängsättning v2". The description cannot carry history, and it is the subtasks that give it back, in readable form, unlike a comment stream.
262
+ - The `Poängsättning vN` subtask holds the scorer's comparison in readable form (the `bracket` in the JSON): which items on the reference scale the item was weighed against, whether it is larger or smaller than each of them, and on what grounds; below that, the raw JSON. The comparison is what the reviewer reads: a reasonable score with the wrong comparisons is a miscalibration that happened to land right.
263
+ - If there are several parallel sets of source material, the subtasks are named so the pairing shows: "Poängsättning — delad komponent", "Poängsättning — specialfall".
263
264
 
264
- **Kommentarer används inte längre för påverkansanalys eller poängsättning.** De är kvar för det som faktiskt kommenteras — feedback vid bakåtflytt — och där gäller fortsatt att de är append-only och aldrig redigeras.
265
+ **Comments are no longer used for impact analysis or scoring.** They remain for what is actually commented on, feedback on a backward move, and there it still applies that they are append-only and never edited.
265
266
 
266
- ## Uppdelade poster
267
+ ## Split items
267
268
 
268
- Har poängsättningen svarat `DELA_UPP` och analysen pekat ut delarna, skriv dem så här.
269
+ If the scoring has answered `DELA_UPP` and the analysis has pointed out the parts, write them like this.
269
270
 
270
- **Delar som går att leverera för sig blir egna requests** på kundbrädan, alltså vanliga
271
- tasks och inte subtasks. De har egen spec, eget beslut och egen leverans. Länka dem till
272
- ursprungsposten.
271
+ **Parts that can be delivered on their own become separate requests** on the customer board, that is, ordinary
272
+ tasks and not subtasks. They have their own spec, their own decision and their own delivery. Link them to
273
+ the original item.
273
274
 
274
- **Delar som inte går att leverera för sig blir subtasks** under posten, en per del, med
275
- sin poäng i `Score` och en rad överst som säger vilken del av specen delen täcker.
275
+ **Parts that cannot be delivered on their own become subtasks** under the item, one per part, with
276
+ its score in `Score` and a line at the top that says which part of the spec the part covers.
276
277
 
277
- **Föräldern får ingen egen `Score`.** Har fältet ett värde sedan tidigare nollas det när
278
- den första poängsatta subtasken skapas. Asana rullar upp barnens värden till den
279
- hopfällda föräldern, så ett eget värde där dubbelräknas eller döljer summan.
278
+ **The parent gets no `Score` of its own.** If the field has a value from before, it is cleared when
279
+ the first scored subtask is created. Asana rolls the children's values up to the
280
+ collapsed parent, so a value of its own there is double-counted or hides the sum.
280
281
 
281
- **`Score` skrivs bara på arbetsrader.** `Analys` och raderna under den lämnas tomma.
282
- Analysraderna blir fler för varje granskningsvarv, och ett värde där skulle räkna
283
- omarbetning som omfattning.
282
+ **`Score` is written only on work rows.** `Analys` and the rows under it are left empty.
283
+ The `Analys` rows grow in number with every review round, and a value there would count
284
+ rework as scope.
284
285
 
285
- **En del som blev `OVER_SKALAN` får ingen siffra.** Föräldern visar då en summa plus en
286
- rad utan värde. Det är ett riktigt utfall, inte ett fel — skriv i beskrivningens
287
- `## Lösning och omfattning` att en del offereras separat, i kundens språk.
286
+ **A part that became `OVER_SKALAN` gets no number.** The parent then shows a sum plus a
287
+ row without a value. It is a real outcome, not an error: write in the description's
288
+ `## Lösning och omfattning` that a part is quoted separately, in the customer's language.
288
289
 
289
- ### Saknad testtäckning
290
+ ### Missing test coverage
290
291
 
291
- Rapporterar analysen att den berörda ytan saknar automatiska tester: skapa en subtask
292
- `Testtäckning – <yta>` med egen poäng, och gör implementationens subtask beroende av den
293
- med `add_dependencies`.
292
+ If the analysis reports that the affected area lacks automated tests: create a subtask
293
+ `Testtäckning – <yta>` with its own score, and make the implementation's subtask depend on it
294
+ with `add_dependencies`.
294
295
 
295
- Ordningen bär något. Testerna ska beskriva hur ytan fungerar innan ändringen görs;
296
- skrivna efteråt beskriver de det nya beteendet och bevisar ingenting om vad som gick
297
- sönder. Testraden hör därför till bygget, inte till förarbetet, trots att den kommer
298
- först.
296
+ The order carries something. The tests must describe how the area works before the change is made;
297
+ written afterwards, they describe the new behavior and prove nothing about what
298
+ broke. The test row therefore belongs to the build, not to the preparatory work, even though it comes
299
+ first.
299
300
 
300
- ### Analysraderna
301
+ ### The `Analys` rows
301
302
 
302
- **Alla rader som hör till undersökningen ligger under en subtask som heter `Analys`** –
303
- påverkansanalys och poängsättning, ett varv i taget. De ligger alltså ett steg
304
- längre in än arbetsraderna, inte bredvid dem.
303
+ **All rows that belong to the investigation sit under one subtask called `Analys`**:
304
+ impact analysis and scoring, one round at a time. They thus sit one step
305
+ further in than the work rows, not beside them.
305
306
 
306
- Skälet är att analysraderna blir många. Varje granskningsvarv lägger till en, och de
307
- läses sällan efter att posten är prissatt. Ligger de på samma nivå som arbetet dränker de
308
- det som faktiskt ska byggas.
307
+ The reason is that the analysis rows become many. Every review round adds one, and they are
308
+ rarely read after the item is priced. If they sit at the same level as the work, they drown
309
+ what is actually to be built.
309
310
 
310
- **En `Analys` per post.** Den skapas första gången en analysrad ska skrivas och
311
- återanvänds sedan. Ett nytt varv lägger en ny rad under den, aldrig en ny `Analys`.
312
- Hittar du redan en: använd den, skapa ingen andra.
311
+ **One `Analys` per item.** It is created the first time an analysis row is to be written and
312
+ is then reused. A new round adds a new row under it, never a new `Analys`.
313
+ If you already find one: use it, do not create a second.
313
314
 
314
- **`Analys` ligger sist i listan**, under arbetsraderna. Arbetet är det man öppnar posten
315
- för att se.
315
+ **`Analys` sits last in the list**, below the work rows. The work is what one opens the item
316
+ to see.
316
317
 
317
- **Varken `Analys` eller dess rader får `Score`.** Asana rullar upp barnens värden till
318
- föräldern, så ett värde där skulle räknas in i postens summa. Analysraderna blir dessutom
319
- fler för varje granskningsvarv, och ett värde på dem skulle räkna omarbetning som
320
- omfattning.
318
+ **Neither `Analys` nor its rows may carry a `Score`.** Asana rolls the children's values up to
319
+ the parent, so a value there would be counted into the item's sum. The analysis rows also grow
320
+ in number with every review round, and a value on them would count rework as
321
+ scope.
321
322
 
322
- **Arbetsraderna ligger kvar direkt under posten.** Testtäckning enligt _Saknad
323
- testtäckning_ och delarna enligt _Uppdelade poster_ är arbete som ska byggas och bär egen
324
- `Score`, så de ska synas utan att någon fäller ut något.
323
+ **The work rows stay directly under the item.** Test coverage per _Missing
324
+ test coverage_ and the parts per _Split items_ are work to be built and carry their own
325
+ `Score`, so they must be visible without anyone expanding anything.
325
326
 
326
- Bygg ingen gruppering ovanpå den här. Namnprefix på raderna och Asanas avdelare
327
- (`is_rendered_as_separator`) användes tidigare för att skilja bygget från förarbetet, och
328
- båda är borttagna: nästlingen gör samma sak, och avdelarna gick bara att sätta med ett
329
- direkt anrop mot Asanas API som de flesta sessioner inte kan göra.
327
+ Build no grouping on top of this. Name prefixes on the rows and Asana's separators
328
+ (`is_rendered_as_separator`) were used earlier to separate the build from the preparatory work, and
329
+ both are removed: the nesting does the same thing, and the separators could only be set with a
330
+ direct call against Asana's API that most sessions cannot make.
330
331
 
331
- ## Var en request kommer ifrån
332
+ ## Where a request comes from
332
333
 
333
- Ursprunget avgör vad du gör, och det avgör också hur underlaget ska tolkas senare. Sätt alltid `Source`.
334
+ The origin decides what you do, and it also decides how the source material is to be interpreted later. Always set `Source`.
334
335
 
335
- ### Kunden skrev själv på sin bräda
336
+ ### The customer wrote on their own board (`Source` = `Kunden skrev själv`)
336
337
 
337
- Kundens ord ligger i beskrivningen och i task name, och båda skrivs över senare: beskrivningen görs om till sektionsform, namnet byts till det förändrade tillståndet när specen skrivs. Det är de **enda destruktiva operationerna i hela flödet**, och ordningen är inte förhandlingsbar:
338
+ The customer's words sit in the description and in the task name, and both are overwritten later: the description is turned into section form, the name is changed to the changed state when the spec is written. Those are the **only destructive operations in the entire flow**, and the order is not negotiable:
338
339
 
339
- 1. Kopiera beskrivningen ordagrant till `Original request` och task name ordagrant till `Original request title`
340
- 2. **Läs tillbaka båda fälten och verifiera att texten landat**
341
- 3. Först då skriv om beskrivningen, samma text ordagrant under `## Ursprunglig förfrågan`, specen under `## Specifikation`
342
- 4. **Byt aldrig task name innan `Original request title` är skrivet och läst tillbaka.** Namnbytet sker när specen skrivs, ofta i en senare körning, och då finns kundens rubrik bara i det fältet
340
+ 1. Copy the description verbatim to `Original request` and the task name verbatim to `Original request title`
341
+ 2. **Read both fields back and verify that the text has landed**
342
+ 3. Only then rewrite the description: the same text verbatim under `## Ursprunglig förfrågan`, the spec under `## Specifikation`
343
+ 4. **Never change the task name before `Original request title` has been written and read back.** The rename happens when the spec is written, often in a later run, and by then the customer's title exists only in that field
343
344
 
344
- Hoppar du över steg 2 och skrivningen tyst misslyckas är kundens egna ord borta för alltid — vilket är precis det `Original request` finns för att förhindra, och ett namnbyte utan steg 4 gör samma sak med rubriken. `Source` = kunden skrev själv.
345
+ If you skip step 2 and the write silently fails, the customer's own words are gone forever, which is exactly what `Original request` exists to prevent, and a rename without step 4 does the same thing to the title. `Source` = `Kunden skrev själv`.
345
346
 
346
- ### Mejl, möte eller annat underlag
347
+ ### Email, meeting or other source material
347
348
 
348
- Här skapar agenten både task och spec i ett svep, och kundens ord ligger kvar utanför Asana. Då står granskaren med en spec och inget att hålla den mot.
349
+ Here the agent creates both the task and the spec in one sweep, and the customer's words stay outside Asana. Then the reviewer is left with a spec and nothing to hold it against.
349
350
 
350
- **Klistra in det relevanta stycket ur mejlet eller anteckningen ordagrant** i `Original request` och under `## Ursprunglig förfrågan` i beskrivningen — inte en sammanfattning, inte en parafras. Risken för glid är störst just här, eftersom underlaget är råare och agenten tolkat mer.
351
+ **Paste the relevant passage from the email or the note verbatim** into `Original request` and under `## Ursprunglig förfrågan` in the description: not a summary, not a paraphrase. The risk of drift is greatest exactly here, because the source material is rawer and the agent has interpreted more.
351
352
 
352
- **Rubriken** är mejlets ärenderad eller anteckningens egen rubrik, ordagrant, i `Original request title`. Skrev kunden aldrig någon rubrik lämnas fältet tomt. En rubrik vi formulerat själva ser i granskningen ut som kundens ord, och ett fält som blandar de två är sämre än ett tomt.
353
+ **The title** is the email's subject line or the note's own title, verbatim, in `Original request title`. If the customer never wrote a title, the field is left empty. A title we formulated ourselves looks like the customer's words in the review, and a field that mixes the two is worse than an empty one.
353
354
 
354
- Blir det flera requests ur samma underlag får var och en **sitt eget relevanta stycke**, inte hela mejlet klistrat en gång per post. Ett fält som innehåller allt håller ingenting.
355
+ If several requests come from the same source material, each one gets **its own relevant passage**, not the whole email pasted once per item. A field that contains everything holds nothing.
355
356
 
356
- `Source` = mejl eller möte. Statusarna sätts enligt skapandesekvensen nedan. Notera bara att en task inte får hamna i ett granskningsläge utan att specen faktiskt är skriven — en tom task i granskningskön ser ut som ett fel i skillen när det är ett fel i regeln.
357
+ `Source` = `Mejl` or `Möte`. The statuses are set per the creation sequence below. Note only that a task must not end up in a review state without the spec actually having been written: an empty task in the review queue looks like a bug in the skill when it is a bug in the rule.
357
358
 
358
- **Länka syskonposter.** Ett mejl eller ett möte producerar ofta flera saker, och `request-write-spec` avgör var snitten går. Beroendet mellan posterna måste finnas på ärendena, inte bara som en mening i ett granskarblock ingen läser om en vecka.
359
+ **Link sibling items.** An email or a meeting often produces several things, and `request-write-spec` decides where the cuts go. The dependency between the items must exist on the tickets, not just as a sentence in a reviewer block that nobody rereads a week later.
359
360
 
360
- ## Vilka fält som bor var
361
+ ## Which fields live where
361
362
 
362
- Kundbrädan bär bara det kunden ska se. Interna fält finns enbart på samlingsbrädan, och en task kan inte bära dem förrän den ligger i båda projekten.
363
+ The customer board carries only what the customer is to see. Internal fields exist only on the collection board, and a task cannot carry them until it sits in both projects.
363
364
 
364
- | Bara samlingsbrädan | Delade, syns på båda |
365
+ | Collection board only | Shared, visible on both |
365
366
  | ---------------------------------- | ------------------------------------------ |
366
367
  | Offer status · Source | Kundstatus |
367
368
  | Spec iterations · Score iterations | Original request · Customer · Requested by |
@@ -369,85 +370,85 @@ Kundbrädan bär bara det kunden ska se. Interna fält finns enbart på samlings
369
370
  | | Score · Decision · Decision reason |
370
371
  | | Original request title |
371
372
 
372
- Ett delat fält är **samma** fält på båda brädorna. Finns två fält med samma namn och olika gid är det en dubblett, inte en delning — de driftar isär omedelbart och felet syns först när en kund tittar på något som inte uppdaterats. Upptäcker du en sådan: skriv inte till någon av dem, säg till.
373
+ A shared field is the **same** field on both boards. If there are two fields with the same name and different gids, it is a duplicate, not a share: they drift apart immediately, and the error shows up only when a customer looks at something that has not been updated. If you discover one: write to neither of them, say so.
373
374
 
374
- ## Vid skapande
375
+ ## At creation
375
376
 
376
- Ordningen spelar roll, eftersom de interna fälten inte existerar på kundbrädan.
377
+ The order matters, because the internal fields do not exist on the customer board.
377
378
 
378
- **1 — Skapa på kundbrädan** med det som finns där: task name, `Original request`, `Original request title`, `Customer`, `Requested by`. Lägg tasken i rätt sektion (se nedan), och **sätt `Request status` redan här**, explicit:
379
+ **1. Create on the customer board** with what exists there: task name, `Original request`, `Original request title`, `Customer`, `Requested by`. Put the task in the right section (see below), and **set `Request status` already here**, explicitly:
379
380
 
380
- | Läge vid skapandet | Request status |
381
- | ------------------------------------ | --------------- |
382
- | Inget skrivet än, bara önskemålet | Mottaget |
383
- | Spec finns och ligger för granskning | Under bedömning |
381
+ | State at creation | Request status |
382
+ | ------------------------------------- | --------------- |
383
+ | Nothing written yet, only the request | Mottaget |
384
+ | Spec exists and is up for review | Under bedömning |
384
385
 
385
- **2 — Verifiera att den landat på samlingsbrädan.** Speglingen sker via en Asana-regel och är asynkron — läs tillbaka uppgiftens projekt och vänta in den. Har den inte kommit fram, lägg till den dit själv.
386
+ **2. Verify that it has landed on the collection board.** The mirroring happens via an Asana rule and is asynchronous: read back the task's projects and wait for it. If it has not arrived, add it there yourself.
386
387
 
387
- **3 — Sätt `Source` och `Offer status`.** Först nu finns fälten. `Offer status` ska motsvara det `Request status` du satte i steg 1:
388
+ **3. Set `Source` and `Offer status`.** Only now do the fields exist. `Offer status` must correspond to the `Request status` you set in step 1:
388
389
 
389
- | Läge | Offer status |
390
- | ---------------- | --------------------------- |
391
- | Inget skrivet än | 📥 Inkommet |
392
- | Spec finns | 🔍 Spec redo för granskning |
390
+ | State | Offer status |
391
+ | ------------------- | --------------------------- |
392
+ | Nothing written yet | 📥 Inkommet |
393
+ | Spec exists | 🔍 Spec redo för granskning |
393
394
 
394
- **4 — Verifiera att båda statusfälten har värden.** Läs tillbaka tasken och kontrollera `Request status` och `Offer status`. Har något inte landat: säg det och stanna.
395
+ **4. Verify that both status fields have values.** Read the task back and check `Request status` and `Offer status`. If something has not landed: say so and stop.
395
396
 
396
- Försöker du sätta `Offer status` eller `Source` i steg 1 avbryter uppslagningen, och du står med en halvskapad request.
397
+ If you try to set `Offer status` or `Source` in step 1, the lookup aborts, and you are left with a half-created request.
397
398
 
398
- **En task du skapar lämnar aldrig skillen med tomt `Request status` eller tomt `Offer status`.** Kundbrädan grupperas på det ena fältet, offertbrädan på det andra — en task utan värde hamnar utanför alla grupper. Den finns, men ingen ser den, och felet upptäcks först när någon undrar var den tog vägen. Glappet är verkligt: `Offer status` existerar bara på offertbrädan, så mellan steg 1 och steg 3 är det fältet oundvikligen tomt. Därför sätts `Request status` i steg 1 — den täcker glappet.
399
+ **A task you create never leaves the skill with an empty `Request status` or an empty `Offer status`.** The customer board is grouped on one field, the offer board on the other: a task without a value ends up outside all groups. It exists, but nobody sees it, and the error is discovered only when someone wonders where it went. The gap is real: `Offer status` exists only on the offer board, so between step 1 and step 3 that field is unavoidably empty. That is why `Request status` is set in step 1: it covers the gap.
399
400
 
400
- Efter skapandet rör du aldrig `Request status` igen. Då härleds den av Asana-regeln ur `Offer status`.
401
+ After creation you never touch `Request status` again. From then on it is derived by the Asana rule from `Offer status`.
401
402
 
402
- Task name ska vara det förändrade tillståndet, inte uppgiften, samma regel som specrubriken. Finns ingen spec än: använd önskemålets egna ord, och byt namn när specen skrivs. Men namnbytet förutsätter att `Original request title` bär den ursprungliga rubriken och att den är läst tillbaka, se _Kunden skrev själv på sin bräda_.
403
+ The task name must be the changed state, not the job, the same rule as for the spec title. If there is no spec yet: use the request's own words, and rename when the spec is written. But the rename requires that `Original request title` carries the original title and that it has been read back, see _The customer wrote on their own board_.
403
404
 
404
- ### Sektion på kundbrädan
405
+ ### Section on the customer board
405
406
 
406
- - **Finns `customer_section` i konfigurationen:** använd den, men verifiera först att sektionen fortfarande finns på brädan — sektioner byter namn och tas bort. Är den borta: fråga vilken som gäller nu och uppdatera `.haus-workflow/request.json`.
407
- - **Saknas `customer_section` och brädan har flera sektioner:** fråga **en gång** och använd svaret för hela omgången. Skapar du tio tasks ur ett mejl ska du inte fråga tio gånger. Erbjud att spara svaret i konfigurationsfilen så frågan inte återkommer.
408
- - **Har brädan bara en sektion:** fråga inte.
409
- - **Spänner omgången flera sektioner** — ett mejl innehåller ofta önskemål som hör till olika projekt — gissa inte utifrån innehållet. Fråga en gång om alla ska till samma sektion, och be om fördelningen om svaret är nej.
407
+ - **If `customer_section` is in the configuration:** use it, but first verify that the section still exists on the board: sections get renamed and removed. If it is gone: ask which one applies now and update `.haus-workflow/request.json`.
408
+ - **If `customer_section` is missing and the board has several sections:** ask **once** and use the answer for the whole batch. If you create ten tasks from an email, you must not ask ten times. Offer to save the answer in the configuration file so the question does not come back.
409
+ - **If the board has only one section:** do not ask.
410
+ - **If the batch spans several sections** (an email often contains requests that belong to different projects), do not guess from the content. Ask once whether all of them go to the same section, and ask for the distribution if the answer is no.
410
411
 
411
- Matcha alltid på gid. `customer_section_name` finns bara för att kunna visas i en bekräftelse.
412
+ Always match on gid. `customer_section_name` exists only so it can be shown in a confirmation.
412
413
 
413
- ## Statusarna
414
+ ## The statuses
414
415
 
415
- **Offer status** driver arbetet. Varje läge är en inkorg:
416
+ **Offer status** drives the work. Each state is an inbox:
416
417
 
417
- | Status | Väntar på |
418
+ | Status | Waiting for |
418
419
  | ------------------------------------ | ----------------------------------------- |
419
420
  | 📥 Inkommet | `request-write-spec` |
420
- | 🔍 Spec redo för granskning | människa |
421
+ | 🔍 Spec redo för granskning | human |
421
422
  | ✅ Spec ok | `request-assess-impact` + `request-score` |
422
- | ⚖️ Poängsättning redo för granskning | människa |
423
- | 📤 Klar – till kund | kundens svar, se `Decision` |
423
+ | ⚖️ Poängsättning redo för granskning | human |
424
+ | 📤 Klar – till kund | the customer's answer, see `Decision` |
424
425
 
425
- **Request status** är kundens vy och härleds av en Asana-regel ur `Offer status`. **Sätt den en enda gång — vid skapandet, innan `Offer status` finns att härleda ur.** Därefter rör du den aldrig; regeln äger den. Startvärdet ska vara exakt det regeln hade härlett, så att fälten aldrig kan säga emot varandra.
426
+ **Request status** is the customer's view and is derived by an Asana rule from `Offer status`. **Set it one single time: at creation, before `Offer status` exists to derive from.** After that you never touch it; the rule owns it. The starting value must be exactly what the rule would have derived, so that the fields can never contradict each other.
426
427
 
427
- Skälet: `Request status` är det enda i hela flödet en kund ser. Två fält som båda sätts för hand driftar isär, och just den driften syns hos någon annan.
428
+ The reason: `Request status` is the only thing in the entire flow that a customer sees. Two fields that are both set by hand drift apart, and exactly that drift shows up with someone else.
428
429
 
429
- **Att hämta en kö** är det vanligaste anropet: alla poster med ett givet Offer status. Filtrera alltid på samlingsbrädan.
430
+ **Fetching a queue** is the most common call: all items with a given Offer status. Always filter on the collection board.
430
431
 
431
- ## Bakåtflytt
432
+ ## Backward move
432
433
 
433
- Feedback är en kommentar plus en flytt bakåt till närmaste AI-läge. Två regler:
434
+ Feedback is a comment plus a move backward to the nearest AI state. Two rules:
434
435
 
435
- **Bumpa räknaren.** Bakåt till 📥 höjer `Spec iterations`. Bakåt till ✅ höjer `Score iterations` (räknar poängsättningsvarv). Läs nuvarande värde och skriv värdet plus ett — sätt aldrig ett absolut tal från minnet.
436
+ **Raise the counter.** Back to 📥 raises `Spec iterations`. Back to ✅ raises `Score iterations` (counts scoring rounds). Read the current value and write the value plus one: never set an absolute number from memory.
436
437
 
437
- **Ändrad spec ogiltigförklarar poängsättningen.** Går en post bakåt till 📥 ska den passera ⚖️ igen på vägen tillbaka. Skriv inte över `Score` med ett nytt värde utan att posten gjort den resan.
438
+ **A changed spec invalidates the scoring.** If an item goes back to 📥, it must pass ⚖️ again on its way back. Do not overwrite `Score` with a new value without the item having made that journey.
438
439
 
439
- En bakåtflytt producerar en ny spec, och den ska in i beskrivningen — läs därför `Decision` först. Har posten redan ett kundbeslut gäller låsregeln och beskrivningen rörs inte.
440
+ A backward move produces a new spec, and it is to go into the description: so read `Decision` first. If the item already has a customer decision, the lock rule applies and the description is not touched.
440
441
 
441
- ## Vad du aldrig gör
442
+ ## What you never do
442
443
 
443
- **Skriver till kund.** Du sätter fält och flyttar statusar. Att skicka förslaget är en människas beslut.
444
+ **Write to the customer.** You set fields and move statuses. Sending the proposal is a human's decision.
444
445
 
445
- **Skriver över beskrivningen utan att först ha verifierat `Original request`** — och skriver aldrig över en sektion som ett annat steg äger.
446
+ **Overwrite the description without first having verified `Original request`**, and never overwrite a section that another step owns.
446
447
 
447
- **Skriver till beskrivningen utan att ha läst `Decision`.** Har ärendet ett kundbeslut är beskrivningen låst, oavsett vad som ändrats.
448
+ **Write to the description without having read `Decision`.** If the ticket has a customer decision, the description is locked, regardless of what has changed.
448
449
 
449
- **Rör `Score` efter att `Decision` är satt.** De bär samma innehåll som beskrivningen och är låsta med den. Blir poängen högre vid en omkörning är det ett nytt beslut, inte en överskrivning — poängsättningen läggs som subtask, posten flyttas tillbaka till ⚖️, och människan avgör vad kunden får se.
450
+ **Touch `Score` after `Decision` is set.** They carry the same content as the description and are locked with it. If the score comes out higher on a rerun, it is a new decision, not an overwrite: the scoring is added as a subtask, the item is moved back to ⚖️, and the human decides what the customer gets to see.
450
451
 
451
- **Sätter `Decision` själv.** Det är kundens svar, det kommer utifrån.
452
+ **Set `Decision` yourself.** It is the customer's answer; it comes from outside.
452
453
 
453
- **Raderar eller redigerar kommentarer.** Feedbackloggen är append-only.
454
+ **Delete or edit comments.** The feedback log is append-only.