@haus-tech/haus-workflow 6.0.0 → 6.1.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.
@@ -117,8 +117,11 @@ After the command completes, follow the relevant post-run steps below.
117
117
  first; user content preserved), then re-runs setup from scratch. Say what it keeps:
118
118
  an item the user edited and its lock row, and a `.claude/rules/haus.md` haus did not
119
119
  write. `haus undo --force` removes those too, after a backup, except a
120
- `.claude/rules/haus.md` that is a link or a folder, which stays either way; mention
121
- it, but never add `--force` yourself. On confirm run
120
+ `.claude/rules/haus.md` that is a link or a folder, which stays either way. In workspace
121
+ scope it also keeps a linked copy edited at the workspace root and a
122
+ `.haus-workflow/WORKSPACE.md` haus does not own; `haus workspace undo --force` removes
123
+ those too, with what each member keeps, after a backup, a link or a folder again
124
+ excepted. Mention it, but never add `--force` yourself. On confirm run
122
125
  `haus undo --yes` (project) or `haus workspace undo --yes` (workspace), then
123
126
  continue by the scope already detected in Step 1.5 — workspace: step 1 below
124
127
  (deep-docs ask, `haus workspace setup --write`, per-member documentation pass,
@@ -222,7 +222,7 @@ stay there.
222
222
  finds no tests at all. The skeleton below fills in the real body once the runner
223
223
  config is decided; what matters here is that the key exists first.
224
224
  4. Write: `haus apply --write`. It reads the profile: `.haus-workflow/WORKFLOW.md` now
225
- starts with `# PoC Workflow Standard`, and there is no `.github/workflows/haus-gate.yml`.
225
+ starts with `# PoC Workflow Standard`, and there is no haus gate (`lefthook.yml`).
226
226
 
227
227
  Fast path: the chained init already ran `haus apply --write` inside Phase 3, before this
228
228
  phase's `"manus"` script exists, so `.haus-workflow/workflow-config.md`'s "Run the manus"
@@ -236,11 +236,14 @@ its stop (`designa` step 5), with no runs row, since the design is no dispatch.
236
236
  later): an item never goes a third spec round on the same choice. Older archives
237
237
  lack the field and are read as `0`.
238
238
  - `coverage_post` mirrors the score envelope's optional `coverage_post: { yta, omfattning }`
239
- (ADR-0119). `archive.mjs add-run` copies the field from the
240
- latest main pricing file (no suffix) that carries it; a `-run2` or `-avvisad`
241
- file never sets it, whatever the stamping order. The skeleton (`ny-korning`/`ny-del`) sets
242
- it to `null`. `bluf` shows `täckningspost: <yta>` in the `anm` column when
243
- the field is set.
239
+ (ADR-0119) from the score pricing file that counts, the same file `bluf` reads the score
240
+ from: the round's main file, or the file a ruling's `galler_fil` names (ADR-0159).
241
+ `archive.mjs` recomputes it on `add-run` of a score pricing file, on `utslag` on the
242
+ score axis and on `set-status priced`, and sets it to `null` when that file has no
243
+ post or is not written yet; `respec` clears it. A `-run2` file sets it exactly when a
244
+ ruling makes it the counting file. Never write the field by hand. The skeleton
245
+ (`ny-korning`/`ny-del`) sets it to `null`. `bluf` shows `täckningspost: <yta>` in the
246
+ `anm` column when the field is set.
244
247
  - `design` is written ONLY by `archive.mjs add-design` and reset ONLY by
245
248
  `archive.mjs respec`: `{ "path": "<absolute path to the canonical file in the repo>",
246
249
  "file": "design.md", "sha256": "<marker-stripped hash of design.md, identical to the canonical file>",
@@ -998,23 +998,21 @@ if (cmd === 'add-run') {
998
998
  const rubrik = forstaRubrik(readFileSync(path.join(katalog, 'spec.md'), 'utf8'))
999
999
  if (rubrik) titelFalt = { title: rubrik, slug: slugAv(rubrik) }
1000
1000
  }
1001
- // Täckningspost (ADR-0119, #508): senaste HUVUDFILEN som bär fältet
1002
- // vinner, precis som titeln följer spec.md på disk. -run2 och -avvisad
1003
- // sätter den aldrig, oavsett stämplingsordning: under LIKA_TOLERANS blir
1004
- // den ersatta körningen -run2 och stämplas ofta efter huvudfilen, och
1005
- // dess yta nådde då state, bluf och request-syncs täckningsdeluppgift
1006
- // (Convendum 1219234151224225, 2026-10-14). Bara poängaxelns PRISfil
1007
- // (GALLANDE_RE, samma mönster som maxN läser): en guardfil har en egen
1008
- // N-sekvens och dess utfall synkas aldrig (SKILL.md, arkivträdet), så
1009
- // den får inte ersätta den prissatta postens täckning. En huvudfil som
1010
- // (ännu) inte finns är inte add-runs jobb att fälla, den stämplas ofta
1011
- // innan agenten skrivit filen. Finns den redan är trasig JSON eller ett
1012
- // icke-objekt ett arkivfel som andra anropare i den här filen, fångas
1013
- // inte här: readPricingFileOrThrow kastar oskyddad.
1014
- if (GALLANDE_RE('score').test(file) && existsSync(path.join(katalog, file))) {
1015
- const data = readPricingFileOrThrow(file, katalog)
1016
- if (data.coverage_post !== null && typeof data.coverage_post === 'object')
1017
- titelFalt = { ...titelFalt, coverage_post: data.coverage_post }
1001
+ // Täckningspost (ADR-0159, ändrar ADR-0119 beslut 4; #763, #687): fältet
1002
+ // härleds ur den poängfil som GÄLLER efter den här raden, aldrig ur filen
1003
+ // som råkar stämplas. Bara en poängprisfil (huvudfil, -run2 eller
1004
+ // -avvisad) räknar om: en guardfil har en egen N-sekvens och synkas
1005
+ // aldrig (SKILL.md, arkivträdet), och spec.md/impact.md får inte väcka en
1006
+ // post som respec redan rensat. En -run2 eller -avvisad från en NYARE
1007
+ // runda än den gällande (huvudfilen är inte stämplad än) räknar inte om
1008
+ // heller: den rundan pågår och dess gällande fil finns inte än, så fältet
1009
+ // behåller sitt värde (null efter en respec) tills rundans huvudfil
1010
+ // stämplas. Se gallandeTackning för regeln.
1011
+ const poangfil = /^score-pricing-v(\d+)(-[a-z0-9-]+)?\.json$/.exec(file)
1012
+ if (poangfil) {
1013
+ const efter = { ...state, runs: [...runs, row] }
1014
+ if (Number(poangfil[1]) <= maxN(efter.runs, 'score'))
1015
+ titelFalt = { ...titelFalt, coverage_post: gallandeTackning(efter) }
1018
1016
  }
1019
1017
  // ADR-0153 beslut 3: brädkvittot betyder att den gällande prisrundan
1020
1018
  // ligger på brädan. En ny rundas huvudfil gör ett tidigare kvitto
@@ -1239,6 +1237,13 @@ if (cmd === 'add-run') {
1239
1237
  }
1240
1238
  }
1241
1239
  state.granskarutslag = [...(state.granskarutslag ?? []), row]
1240
+ // ADR-0159: en dom på poängaxeln kan byta den gällande filen, så
1241
+ // täckningsposten räknas om ur den. En trasig gällande fil fäller domen
1242
+ // (exit 2) innan något skrivs.
1243
+ // En dom om ett äldre varv byter aldrig den gällande filen (liftingUtslag
1244
+ // kräver n === maxN) och räknar därför inte om.
1245
+ if (row.typ === 'viktning' && row.axel === 'score' && row.n === maxN(state.runs ?? [], 'score'))
1246
+ state.coverage_post = gallandeTackning(state)
1242
1247
  writeStateOrThrow(state)
1243
1248
  rowCount = state.granskarutslag.length
1244
1249
  } catch (e) {
@@ -1273,6 +1278,10 @@ if (cmd === 'add-run') {
1273
1278
  }
1274
1279
  if (stopp.length === 0) {
1275
1280
  state.status = status
1281
+ // ADR-0159: priced är steget före synken, så täckningsposten räknas om
1282
+ // här också. En huvudfil som stämplades innan den skrevs får då sin
1283
+ // post även när ingen senare stämpling kom.
1284
+ if (status === 'priced') state.coverage_post = gallandeTackning(state)
1276
1285
  writeStateOrThrow(state)
1277
1286
  }
1278
1287
  } catch (e) {
@@ -1989,6 +1998,12 @@ if (cmd === 'add-run') {
1989
1998
  nytt.board = null
1990
1999
  antal++
1991
2000
  }
2001
+ // ADR-0159: täckningsposten hörde till en prisrunda som inte längre
2002
+ // gäller; nästa poängrunda räknar fram den på nytt.
2003
+ if (nytt.coverage_post !== null && nytt.coverage_post !== undefined) {
2004
+ nytt.coverage_post = null
2005
+ antal++
2006
+ }
1992
2007
  // Ett dött kvitto (levande vägrades ovan) hör till en körning som inte
1993
2008
  // finns; en ny spec nollar det.
1994
2009
  if (nytt.korning !== null && nytt.korning !== undefined) {
@@ -3238,6 +3253,22 @@ function gallandeDokumentFil(state, typ, n) {
3238
3253
  return liftingUtslag(state, typ, n)?.galler_fil ?? `${typ}-pricing-v${n}.json`
3239
3254
  }
3240
3255
 
3256
+ /** Täckningsposten (ADR-0159, ändrar ADR-0119 beslut 4): `coverage_post` ur
3257
+ * den poängfil som gäller, `gallandeDokumentFil(state, 'score', maxN)`, så
3258
+ * att granskarens `galler_fil` styr på samma sätt som för poäng och utfall.
3259
+ * null när ingen poängrunda finns, när den gällande filen ännu inte skrivits
3260
+ * (den stämplas ofta före agentens fil; nästa omräkning fyller i den) och
3261
+ * när filen saknar ett objekt i fältet. Finns filen men är trasig kastar
3262
+ * readPricingFileOrThrow ArkivFel, samma arkivfel som andra läsare i filen. */
3263
+ function gallandeTackning(state, d = katalog) {
3264
+ const n = maxN(state.runs ?? [], 'score')
3265
+ if (n === 0) return null
3266
+ const fil = gallandeDokumentFil(state, 'score', n)
3267
+ if (!existsSync(path.join(d, fil))) return null
3268
+ const cp = readPricingFileOrThrow(fil, d).coverage_post
3269
+ return cp !== null && typeof cp === 'object' && !Array.isArray(cp) ? cp : null
3270
+ }
3271
+
3241
3272
  /** Läser en gällande pricing-fil (classification eller score) ur katalogen
3242
3273
  * och returnerar den PARSADE, validerade datan. Arkivet PÅSTÅR via sin
3243
3274
  * runs-rad (eller ett viktningsutslags `galler_fil`) att filen finns och är
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haus-tech/haus-workflow",
3
- "version": "6.0.0",
3
+ "version": "6.1.0",
4
4
  "description": "Haus AI workflow CLI for Claude Code.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -34,11 +34,12 @@
34
34
  "format": "prettier --write --cache --cache-location .prettiercache .",
35
35
  "typecheck": "tsc --noEmit",
36
36
  "pack:local": "yarn pack",
37
- "release": "GITHUB_TOKEN=$(gh auth token) LEFTHOOK_EXCLUDE=branch-guard release-it",
38
- "release:dry": "GITHUB_TOKEN=$(gh auth token) LEFTHOOK_EXCLUDE=branch-guard release-it --dry-run",
37
+ "release": "GITHUB_TOKEN=$(gh auth token) LEFTHOOK_EXCLUDE=branch-guard,ci-local release-it",
38
+ "release:dry": "GITHUB_TOKEN=$(gh auth token) LEFTHOOK_EXCLUDE=branch-guard,ci-local release-it --dry-run",
39
39
  "prepack": "yarn build",
40
40
  "verify": "node scripts/verify.mjs",
41
41
  "verify:full": "node scripts/verify.mjs --coverage",
42
+ "ci:local": "node scripts/ci-local.mjs --force",
42
43
  "check:contract-behavior": "node --import tsx scripts/contract-behavior-check.mjs",
43
44
  "check:mutation-table": "node scripts/check-mutation-table.mjs",
44
45
  "check:docs-site": "node scripts/check-docs-site.mjs",