@1aboveio/skills 1.0.10 → 1.0.12

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.
Files changed (214) hide show
  1. package/.codex-plugin/plugin.json +1 -1
  2. package/.codex-plugin/resources.json +298 -216
  3. package/README.md +3 -3
  4. package/package.json +10 -8
  5. package/skills/app-dev/airflow/SKILL.md +1 -1
  6. package/skills/app-dev/android-foundation/SKILL.md +35 -37
  7. package/skills/app-dev/android-foundation/references/delivery-focus.md +4 -2
  8. package/skills/app-dev/android-foundation/references/feature-work.md +50 -0
  9. package/skills/app-dev/android-foundation/references/setup.md +24 -9
  10. package/skills/app-dev/android-foundation/references/workspace.md +81 -0
  11. package/skills/app-dev/android-foundation/scripts/workspace-runtime/bin/workspaces.mjs +52 -0
  12. package/skills/app-dev/android-foundation/scripts/workspace-runtime/native-workspace.mjs +239 -0
  13. package/skills/app-dev/android-foundation/scripts/workspace-runtime/workspace-lifecycle.mjs +2154 -0
  14. package/skills/app-dev/app-debug/SKILL.md +1 -1
  15. package/skills/app-dev/better-auth/SKILL.md +1 -1
  16. package/skills/app-dev/data-catalog/SKILL.md +1 -1
  17. package/skills/app-dev/data-quality/SKILL.md +1 -1
  18. package/skills/app-dev/ios-foundation/SKILL.md +15 -10
  19. package/skills/app-dev/ios-foundation/references/setup.md +25 -9
  20. package/skills/app-dev/ios-foundation/references/workspace.md +82 -0
  21. package/skills/app-dev/ios-foundation/scripts/workspace-runtime/bin/workspaces.mjs +52 -0
  22. package/skills/app-dev/ios-foundation/scripts/workspace-runtime/native-workspace.mjs +239 -0
  23. package/skills/app-dev/ios-foundation/scripts/workspace-runtime/workspace-lifecycle.mjs +2154 -0
  24. package/skills/app-dev/monorepo/SKILL.md +1 -1
  25. package/skills/app-dev/nextjs-fullstack/SKILL.md +1 -1
  26. package/skills/app-dev/prisma-setup/SKILL.md +1 -1
  27. package/skills/app-dev/pyspark/SKILL.md +1 -1
  28. package/skills/app-dev/python-backend/SKILL.md +1 -1
  29. package/skills/app-dev/shadcn/SKILL.md +1 -1
  30. package/skills/app-dev/zod-v4/SKILL.md +1 -1
  31. package/skills/cicd/cloud-build/SKILL.md +1 -1
  32. package/skills/cicd/cloud-debug/SKILL.md +1 -1
  33. package/skills/cicd/cloud-deploy/SKILL.md +1 -1
  34. package/skills/cicd/docker/SKILL.md +1 -1
  35. package/skills/cicd/github-actions/SKILL.md +1 -1
  36. package/skills/cicd/github-flow/SKILL.md +1 -1
  37. package/skills/cicd/google-cloud/SKILL.md +1 -1
  38. package/skills/cicd/mergify/SKILL.md +1 -1
  39. package/skills/cicd/podman/SKILL.md +1 -1
  40. package/skills/content/article-review/SKILL.md +1 -1
  41. package/skills/content/article-to-image/SKILL.md +1 -1
  42. package/skills/content/flowchart-svg/SKILL.md +1 -1
  43. package/skills/content/image-gen/SKILL.md +1 -1
  44. package/skills/content/mermaid/SKILL.md +1 -1
  45. package/skills/content/wechat-article-writer/SKILL.md +1 -1
  46. package/skills/content/wechat-publisher/SKILL.md +1 -1
  47. package/skills/content/wechat-search/SKILL.md +1 -1
  48. package/skills/content/xai-search/SKILL.md +1 -1
  49. package/skills/core/distill-writing/SKILL.md +3 -3
  50. package/skills/core/distill-writing/references/extraction.md +1 -1
  51. package/skills/core/doc-to-md/SKILL.md +1 -1
  52. package/skills/core/editorial-review/SKILL.md +82 -12
  53. package/skills/core/editorial-review/references/checklist.md +26 -1
  54. package/skills/core/house-style/SKILL.md +8 -3
  55. package/skills/core/house-style/references/analytical-reporting.md +123 -0
  56. package/skills/core/house-style/references/analytical-writing-patterns.md +79 -0
  57. package/skills/core/house-style/references/analytical-writing-research.md +129 -0
  58. package/skills/core/{write-doc → house-style}/references/bilingual.md +7 -5
  59. package/skills/core/house-style/references/definitions-and-metrics.md +86 -0
  60. package/skills/core/house-style/references/document-workflow.md +193 -0
  61. package/skills/core/house-style/references/initialization.md +66 -0
  62. package/skills/core/house-style/references/persistence.md +9 -4
  63. package/skills/core/house-style/references/rules.md +11 -4
  64. package/skills/core/house-style/scripts/init-guidance.mjs +213 -0
  65. package/skills/core/house-style/templates/analytical-report.md +54 -0
  66. package/skills/core/house-style/templates/gap-analysis.md +1 -1
  67. package/skills/core/plain-language/SKILL.md +2 -2
  68. package/skills/core/polish-cn/SKILL.md +9 -2
  69. package/skills/core/reg-parser/SKILL.md +3 -3
  70. package/skills/core/terminology/SKILL.md +8 -4
  71. package/skills/core/write-doc/SKILL.md +40 -151
  72. package/skills/core/write-report/SKILL.md +129 -0
  73. package/skills/core/{write-doc → write-report}/references/compliance-reports.md +4 -4
  74. package/skills/engineering/better-review/SKILL.md +37 -37
  75. package/skills/engineering/better-review/references/passes.md +35 -41
  76. package/skills/engineering/better-review/references/result.md +10 -9
  77. package/skills/engineering/e2e-test/SKILL.md +17 -17
  78. package/skills/engineering/e2e-test/references/authoring/playwright-config.md +11 -13
  79. package/skills/engineering/e2e-test/references/presentation-sweep.md +7 -7
  80. package/skills/engineering/e2e-test/references/reviewing.md +9 -11
  81. package/skills/engineering/e2e-test/references/route-manifest.md +1 -1
  82. package/skills/engineering/engineering/SKILL.md +201 -0
  83. package/skills/engineering/engineering/references/engineering-conventions.md +32 -0
  84. package/skills/engineering/engineering/references/feature-delivery.md +8 -0
  85. package/skills/engineering/engineering/references/init-scope.md +45 -0
  86. package/skills/engineering/engineering/references/scope-management.md +85 -0
  87. package/skills/engineering/engineering/scripts/init.mjs +160 -0
  88. package/skills/engineering/{contributing → engineering}/scripts/persist-conventions.mjs +1 -1
  89. package/skills/engineering/engineering-runtime/SKILL.md +1 -1
  90. package/skills/engineering/engineering-runtime/coherence/workflow.json +19 -19
  91. package/skills/engineering/engineering-runtime/scripts/workflow-policy.mjs +1 -1
  92. package/skills/engineering/ensure-coverage/SKILL.md +22 -109
  93. package/skills/engineering/ensure-coverage/references/breadth/coverage-ledger.md +7 -5
  94. package/skills/engineering/ensure-coverage/references/breadth/surface-baseline.md +4 -4
  95. package/skills/engineering/ensure-coverage/references/breadth/surface-discovery.md +3 -3
  96. package/skills/engineering/ensure-coverage/references/depth/characterization.md +4 -65
  97. package/skills/engineering/ensure-coverage/references/depth/grading.md +5 -7
  98. package/skills/engineering/ensure-coverage/references/depth/mock-policy.md +6 -12
  99. package/skills/engineering/ensure-coverage/references/enforcement/ci-contract.md +1 -1
  100. package/skills/engineering/ensure-coverage/references/presentation/axis.md +6 -6
  101. package/skills/engineering/ensure-coverage/references/process/audit-mode.md +7 -23
  102. package/skills/engineering/ensure-coverage/references/process/output-template.md +18 -133
  103. package/skills/engineering/ensure-coverage/references/process/test-strategy.md +7 -12
  104. package/skills/engineering/ensure-coverage/scripts/README.md +12 -4
  105. package/skills/engineering/golden-path/SKILL.md +1 -1
  106. package/skills/engineering/harness-runtime/SKILL.md +3 -1
  107. package/skills/engineering/harness-runtime/bin/workspaces.mjs +52 -0
  108. package/skills/engineering/harness-runtime/native-workspace.mjs +239 -0
  109. package/skills/engineering/harness-runtime/references/workspaces.md +78 -0
  110. package/skills/engineering/harness-runtime/workspace-lifecycle.mjs +2154 -0
  111. package/skills/engineering/resolve-issues/SKILL.md +40 -61
  112. package/skills/engineering/resolve-issues/references/ci.md +13 -3
  113. package/skills/engineering/resolve-issues/references/delivery-progress.md +4 -3
  114. package/skills/engineering/resolve-issues/references/done.md +11 -2
  115. package/skills/engineering/resolve-issues/references/expire.md +9 -2
  116. package/skills/engineering/resolve-issues/references/implementation.md +33 -17
  117. package/skills/engineering/resolve-issues/references/preflight.md +3 -1
  118. package/skills/engineering/resolve-issues/references/principles.md +52 -0
  119. package/skills/engineering/resolve-issues/references/review.md +25 -15
  120. package/skills/engineering/resolve-issues/references/task-plan.md +64 -27
  121. package/skills/engineering/resolve-issues/references/workspaces.md +164 -0
  122. package/skills/engineering/resolve-issues/scripts/cleanup-runs.mjs +185 -317
  123. package/skills/engineering/resolve-issues/scripts/next-actions.mjs +44 -7
  124. package/skills/engineering/resolve-issues/scripts/rehydrate.mjs +36 -2
  125. package/skills/engineering/resolve-issues/scripts/review-verdict.mjs +9 -8
  126. package/skills/engineering/resolve-issues/scripts/workspace-lifecycle.mjs +1 -0
  127. package/skills/engineering/resolve-release/SKILL.md +1 -1
  128. package/skills/engineering/rush-issues/SKILL.md +7 -5
  129. package/skills/engineering/rush-release/SKILL.md +1 -1
  130. package/skills/engineering/test-quality/SKILL.md +19 -4
  131. package/skills/engineering/test-quality/references/lint.md +1 -1
  132. package/skills/engineering/test-quality/references/principles.md +2 -2
  133. package/skills/engineering/test-quality/references/testing-conventions.md +1 -1
  134. package/skills/risk-compliance/compliance-audit/SKILL.md +4 -8
  135. package/skills/risk-compliance/compliance-audit/references/audience-and-reports.md +12 -0
  136. package/skills/risk-compliance/contract-compare/SKILL.md +17 -1
  137. package/skills/risk-compliance/contract-review/SKILL.md +6 -1
  138. package/skills/risk-compliance/contract-review/references/report-stage.md +17 -0
  139. package/skills/risk-compliance/doc-authenticity/SKILL.md +11 -1
  140. package/skills/risk-compliance/fraud-detection/LICENSE +3 -0
  141. package/skills/risk-compliance/fraud-detection/SKILL.md +148 -0
  142. package/skills/risk-compliance/{fraud-analysis → fraud-detection}/references/archetypes/first-party-fraud.md +3 -2
  143. package/skills/risk-compliance/fraud-detection/references/archetypes/third-party-fraud.md +47 -0
  144. package/skills/risk-compliance/fraud-detection/references/collection-payout-features.md +151 -0
  145. package/skills/risk-compliance/{fraud-analysis → fraud-detection}/references/contexts/bank-transfer.md +4 -3
  146. package/skills/risk-compliance/fraud-detection/references/contexts/card-payment.md +70 -0
  147. package/skills/risk-compliance/fraud-detection/references/contexts/payment-collection.md +34 -0
  148. package/skills/risk-compliance/fraud-detection/references/contexts/payout.md +34 -0
  149. package/skills/risk-compliance/fraud-detection/references/enrichment.md +74 -0
  150. package/skills/risk-compliance/fraud-detection/references/entities.md +48 -0
  151. package/skills/risk-compliance/{fraud-analysis → fraud-detection}/references/feature-engineering.md +69 -33
  152. package/skills/risk-compliance/fraud-detection/references/feature-sql.md +150 -0
  153. package/skills/risk-compliance/{fraud-analysis → fraud-detection}/references/report-rationale.md +4 -2
  154. package/skills/risk-compliance/{fraud-analysis → fraud-detection}/references/report-template.md +45 -23
  155. package/skills/risk-compliance/{fraud-analysis → fraud-detection}/references/review-checklist.md +60 -20
  156. package/skills/risk-compliance/fraud-detection/references/workflow.md +216 -0
  157. package/skills/risk-compliance/fraud-detection/release-check.mjs +95 -0
  158. package/skills/risk-compliance/payment-analysis/SKILL.md +36 -17
  159. package/skills/risk-compliance/payment-analysis/references/auth-rate-actions.md +2 -2
  160. package/skills/risk-compliance/payment-analysis/references/report-template.md +10 -2
  161. package/skills/risk-compliance/payment-analysis/references/visualization.md +11 -1
  162. package/skills/risk-compliance/portfolio-payment-analysis/LICENSE +3 -0
  163. package/skills/risk-compliance/portfolio-payment-analysis/SKILL.md +170 -0
  164. package/skills/risk-compliance/portfolio-payment-analysis/references/identity-and-reconciliation.md +46 -0
  165. package/skills/risk-compliance/portfolio-payment-analysis/references/metrics-and-reporting.md +170 -0
  166. package/skills/risk-compliance/portfolio-payment-analysis/references/risk-history.md +89 -0
  167. package/skills/risk-compliance/portfolio-payment-analysis/release-check.mjs +95 -0
  168. package/skills/risk-compliance/portfolio-payment-analysis/scripts/analyze.py +636 -0
  169. package/skills/risk-compliance/portfolio-payment-analysis/scripts/build_report.py +386 -0
  170. package/skills/risk-compliance/portfolio-payment-analysis/scripts/comparison_charts.py +425 -0
  171. package/skills/risk-compliance/portfolio-payment-analysis/scripts/csv_export.py +52 -0
  172. package/skills/risk-compliance/portfolio-payment-analysis/scripts/render.py +160 -0
  173. package/skills/risk-compliance/portfolio-payment-analysis/scripts/risk_history.py +435 -0
  174. package/skills/risk-compliance/similar-domain/SKILL.md +9 -4
  175. package/skills/risk-compliance/tld-detection/SKILL.md +124 -239
  176. package/skills/risk-compliance/tld-detection/agents/openai.yaml +1 -1
  177. package/skills/risk-compliance/tld-detection/references/clean.md +18 -51
  178. package/skills/risk-compliance/tld-detection/references/fx-conversion.md +56 -219
  179. package/skills/risk-compliance/tld-detection/references/metric-contract.md +109 -0
  180. package/skills/risk-compliance/tld-detection/references/pattern.md +99 -0
  181. package/skills/risk-compliance/tld-detection/references/report-template.md +49 -272
  182. package/skills/risk-compliance/tld-detection/scripts/detect_ladders.py +46 -13
  183. package/skills/risk-compliance/tld-detection/scripts/detect_patterns.py +346 -0
  184. package/skills/risk-compliance/tld-detection/scripts/detect_streams.py +51 -19
  185. package/skills/risk-compliance/tld-detection/scripts/profile_groups.py +109 -0
  186. package/skills/risk-compliance/tld-detection/scripts/render_charts.py +218 -0
  187. package/skills/risk-compliance/tld-detection/scripts/tld_common.py +243 -0
  188. package/skills/engineering/contributing/SKILL.md +0 -70
  189. package/skills/risk-compliance/fraud-analysis/SKILL.md +0 -114
  190. package/skills/risk-compliance/fraud-analysis/references/archetypes/third-party-fraud.md +0 -27
  191. package/skills/risk-compliance/fraud-analysis/references/contexts/card-payment.md +0 -30
  192. package/skills/risk-compliance/fraud-analysis/references/contexts/payment-collection.md +0 -20
  193. package/skills/risk-compliance/fraud-analysis/references/contexts/payout.md +0 -20
  194. package/skills/risk-compliance/fraud-analysis/references/workflow.md +0 -175
  195. package/skills/risk-compliance/tld-detection/references/chinese-polish.md +0 -55
  196. package/skills/risk-compliance/tld-detection/references/clustering.md +0 -235
  197. package/skills/risk-compliance/tld-detection/references/decision.md +0 -189
  198. package/skills/risk-compliance/tld-detection/references/streams.md +0 -141
  199. package/skills/risk-compliance/tld-detection/references/terminology.md +0 -95
  200. package/skills/risk-compliance/tld-detection/references/workflow.md +0 -95
  201. /package/skills/{engineering/contributing → core/write-report}/LICENSE +0 -0
  202. /package/skills/core/{write-doc → write-report}/references/reports/audit-report-template.md +0 -0
  203. /package/skills/core/{write-doc → write-report}/references/reports/external-assessment-template.md +0 -0
  204. /package/skills/core/{write-doc → write-report}/references/reports/management-report-template.md +0 -0
  205. /package/skills/core/{write-doc → write-report}/references/reports/vars-report-template.md +0 -0
  206. /package/skills/{engineering/contributing → core/write-report}/release-check.mjs +0 -0
  207. /package/skills/{risk-compliance/fraud-analysis → engineering/engineering}/LICENSE +0 -0
  208. /package/skills/engineering/{contributing → engineering}/references/tracker-conventions.md +0 -0
  209. /package/skills/{risk-compliance/fraud-analysis → engineering/engineering}/release-check.mjs +0 -0
  210. /package/skills/engineering/{contributing → engineering}/scripts/persist-conventions-core.mjs +0 -0
  211. /package/skills/risk-compliance/{fraud-analysis → fraud-detection}/references/archetypes/authorized-payment-scam.md +0 -0
  212. /package/skills/risk-compliance/{fraud-analysis → fraud-detection}/references/mechanisms/account-takeover.md +0 -0
  213. /package/skills/risk-compliance/{fraud-analysis → fraud-detection}/references/taxonomy.md +0 -0
  214. /package/skills/risk-compliance/{fraud-analysis → fraud-detection}/references/terminology.md +0 -0
@@ -11,7 +11,7 @@
11
11
  //
12
12
  // add-unit, drop-unit, set-deps, move-link, blocked-outside, cycle
13
13
  // the plan differs from the spec's GitHub issue tree (plan-sync.mjs): apply
14
- // it through update_plan. --resume and --sync fetch the tree (one GraphQL
14
+ // it through the available checkpoint mechanism. --resume and --sync fetch the tree (one GraphQL
15
15
  // query) and save it; every other call diffs the saved tree, with no
16
16
  // GitHub call. Sync after the run files an issue
17
17
  // unblock a unit marked blocked without a reason from BLOCK_REASONS
@@ -176,6 +176,12 @@ export function normalizeCapacity(input) {
176
176
  const hasWorker = (u) => u.worker !== undefined && u.worker !== null && u.worker !== ''
177
177
  const codeDone = (u) => u.stage === 'done'
178
178
 
179
+ export function workspaceCleanupSettled(unit) {
180
+ const result = unit.lifecycle?.cleanup
181
+ return typeof unit.headSha === 'string' && unit.headSha.length > 0 && result?.headSha === unit.headSha &&
182
+ (result.state === 'complete' || (result.state === 'retained' && typeof result.reason === 'string' && result.reason.trim().length > 0))
183
+ }
184
+
179
185
  // Pure decision from the plan and each unit's newest review round.
180
186
  // `capacity`: a fresh Harness snapshot, or an explicitly reconciled total workflow budget.
181
187
  // Snapshot availableSlots is already net of all running and reserved workers.
@@ -191,6 +197,10 @@ const codeDone = (u) => u.stage === 'done'
191
197
  // `key` is the result file, relative to the run, that a `decisions` entry's `answers` names.
192
198
  export function nextActions(plan, reviews = {}, capacity, { resume = false, now, activity = {}, quietMinutes = DEFAULT_QUIET_MINUTES, sync = null, seen = {}, questions = {} } = {}) {
193
199
  const effectiveCapacity = normalizeCapacity(capacity)
200
+ if (Array.isArray(plan?.units) && Array.isArray(plan?.issues) && JSON.stringify(plan.units) !== JSON.stringify(plan.issues)) {
201
+ throw new NextActionsError('task plan has conflicting units and issues rosters')
202
+ }
203
+ if (plan?.workspaceLifecycle !== undefined && plan.workspaceLifecycle !== 1) throw new NextActionsError('unsupported workspaceLifecycle version')
194
204
  const saved = (Array.isArray(plan?.units) ? plan.units : plan?.issues) ?? []
195
205
  if (!Array.isArray(saved) || saved.length === 0) throw new NextActionsError('task plan has no units')
196
206
  const excluded = excludedIds(plan)
@@ -328,6 +338,17 @@ export function nextActions(plan, reviews = {}, capacity, { resume = false, now,
328
338
  }
329
339
  const actions = []
330
340
  const add = (action, u, detail) => actions.push({ action, unit: String(u.id), detail })
341
+ const managed = (u) => plan.workspaceLifecycle === 1 || u.lifecycle?.version === 1
342
+ for (const u of units) {
343
+ if (TERMINAL.has(u.status) && hasWorker(u) && (u.status !== 'blocked' || reasonOk(u))) {
344
+ add('check-worker', u, `unit ${u.id} is ${u.status} but still records worker ${u.worker}: collect its terminal result and stop its owned services before handoff or workspace cleanup; never remove a running environment`)
345
+ continue
346
+ }
347
+ if (!managed(u) || u.status !== 'succeeded' || !u.workspace) continue
348
+ if (!hasWorker(u) && !workspaceCleanupSettled(u)) {
349
+ add('cleanup-workspace', u, `unit ${u.id} landed: record lifecycle.cleanup with state applying and headSha ${u.headSha ?? '(record the delivered HEAD first)'}, then run cleanup-runs.mjs cleanup --run <run> --unit ${u.id} --revision <saved planRevision> --apply --json; inspect and persist the returned lifecycle. A refusal is retained with its reason, never complete; do this before finish and alongside independent units`)
350
+ }
351
+ }
331
352
  const chainReady = []
332
353
  const chainOpen = (u) => {
333
354
  const id = String(u.id)
@@ -385,6 +406,7 @@ export function nextActions(plan, reviews = {}, capacity, { resume = false, now,
385
406
  const quietMs = quietMinutes * 60_000
386
407
  for (const u of units) {
387
408
  if (!hasWorker(u)) continue
409
+ if (TERMINAL.has(u.status)) continue
388
410
  const since = Date.parse(u.workerSince ?? '')
389
411
  if (!Number.isFinite(since)) continue
390
412
  const last = Math.max(since, activity[String(u.id)] ?? 0)
@@ -610,7 +632,7 @@ export function nextActions(plan, reviews = {}, capacity, { resume = false, now,
610
632
  }
611
633
  const SYNC = ['drop-unit', 'add-unit', 'set-deps', 'move-link', 'blocked-outside', 'cycle']
612
634
  for (const a of sync?.actions ?? []) actions.push({ action: a.action, unit: a.unit, detail: a.detail })
613
- const order = [...SYNC, 'worker-kind', 'decide', 'split', 'unblock', 'check-worker', 'restack', 'carry', 'review-verdict', 'start-fix', 'start-review', 'deliver', 'open-pr', 'resume', 'fill-slot']
635
+ const order = [...SYNC, 'worker-kind', 'decide', 'split', 'unblock', 'check-worker', 'cleanup-workspace', 'restack', 'carry', 'review-verdict', 'start-fix', 'start-review', 'deliver', 'open-pr', 'resume', 'fill-slot']
614
636
  actions.sort((a, b) => order.indexOf(a.action) - order.indexOf(b.action))
615
637
  // One restack per unit per pass: the first reason found; the next pass sees the new HEAD.
616
638
  const restacked = new Set()
@@ -625,7 +647,18 @@ export function nextActions(plan, reviews = {}, capacity, { resume = false, now,
625
647
  const waiting = []
626
648
  for (const a of actions) {
627
649
  if (!NEEDS_SLOT.has(a.action)) { due.push(a); continue }
628
- if (free > 0) { due.push(unlimited ? { ...a, detail: `${a.detail} [needs a free worker slot]` } : a); free-- } else waiting.push(a)
650
+ if (free > 0) {
651
+ const u = byId.get(a.unit)
652
+ const reviewReady = a.action !== 'start-review' || u.lifecycle?.resources?.some((r) => r.role === 'review' && r.state === 'ready')
653
+ const needsPreparation = managed(u) && (u.lifecycle?.workspace?.state !== 'ready' || !reviewReady)
654
+ const removalPending = managed(u) && u.lifecycle?.cleanup?.state === 'applying'
655
+ const admitted = removalPending ? { action: 'recover-workspace', unit: a.unit,
656
+ detail: `before ${a.action} for unit ${a.unit}, resolve its applying cleanup: inspect cleanup-runs.mjs recover --repo <repo-root> --json, collect any running cleanup operation's terminal result, and verify its receipt and every owned resource before updating lifecycle. Never admit a worker while removal of its environment is unresolved` }
657
+ : needsPreparation ? { action: 'prepare-workspace', unit: a.unit,
658
+ detail: `before ${a.action} for unit ${a.unit}, record its exact workspace, branch, base SHA and lifecycle preparation intent${reviewReady ? '' : ', including its one review clone'}; run cleanup-runs.mjs prepare --run <run> --unit ${a.unit} --revision <saved planRevision> --apply --json and persist its returned ready identity. Reuse an existing matching workspace; never reset, replace or create another one on retry` } : a
659
+ due.push(unlimited ? { ...admitted, detail: `${admitted.detail} [needs a free worker slot]` } : admitted)
660
+ free--
661
+ } else waiting.push(a)
629
662
  }
630
663
  if (native) oneChange(due)
631
664
  const treeChanged = (sync?.actions ?? []).length > 0
@@ -645,7 +678,8 @@ export function nextActions(plan, reviews = {}, capacity, { resume = false, now,
645
678
  const parked = units.filter(notStarted).map((u) => `${u.id} (waits on ${waitsOn(u)})`)
646
679
  const built = units.filter((u) => builtWaiting(u) && !u.pr).map((u) => `${u.id} (waits on ${waitsOn(u)})`)
647
680
  const openWaiting = units.filter((u) => builtWaiting(u) && u.pr).map((u) => `${u.id} (${u.pr}, waits on ${waitsOn(u)})`)
648
- if (!ready.length && units.every((u) => (TERMINAL.has(u.status) || notStarted(u) || builtWaiting(u)) && settledBlock(u) && !sizeHeld(u) && !deciding(u)) && !treeChanged) {
681
+ const cleanupPending = due.some((a) => a.action === 'cleanup-workspace')
682
+ if (!ready.length && !cleanupPending && units.every((u) => !hasWorker(u) && (TERMINAL.has(u.status) || notStarted(u) || builtWaiting(u)) && settledBlock(u) && !sizeHeld(u) && !deciding(u)) && !treeChanged) {
649
683
  if (sync?.fetchError) due.push({ action: 'sync', unit: null, detail: `every unit is settled, but the issue tree could not be read (${sync.fetchError.map((e) => e.error).join('; ')}): rerun with --sync until it reads; never finish on a tree you could not read` })
650
684
  else if (sync && !String(sync.source).startsWith('live')) due.push({ action: 'sync', unit: null, detail: 'every unit is settled: rerun with --sync to check the issue tree for new issues before finishing' })
651
685
  else if (retryable.some((u) => !u.blockedAt || !Number.isFinite(now))) {
@@ -653,7 +687,10 @@ export function nextActions(plan, reviews = {}, capacity, { resume = false, now,
653
687
  } else if (retryable.length) {
654
688
  const next = retryable.reduce((a, u) => (dueAt(u) < dueAt(a) ? u : a))
655
689
  due.push({ action: 'wait', unit: String(next.id), detail: `unit ${next.id} is blocked (${next.blockedReason}); its next automatic attempt is at ${new Date(dueAt(next)).toISOString()}: keep a heartbeat until then and do not finish` })
656
- } else due.push({ action: 'finish', unit: null, detail: `every unit is delivered, blocked, cancelled, or not started behind one of those: write the handoff${parked.length ? `; not started: ${parked.join(', ')}` : ''}${built.length ? `; built, PR not opened: ${built.join(', ')}` : ''}${openWaiting.length ? `; PR open, cannot land: ${openWaiting.join(', ')}` : ''}` })
690
+ } else {
691
+ const retained = units.filter((u) => managed(u) && u.lifecycle?.cleanup?.state === 'retained').map((u) => `${u.id}: ${u.lifecycle.cleanup.reason}`)
692
+ due.push({ action: 'finish', unit: null, detail: `every unit is delivered, blocked, cancelled, or not started behind one of those: write the handoff${parked.length ? `; not started: ${parked.join(', ')}` : ''}${built.length ? `; built, PR not opened: ${built.join(', ')}` : ''}${openWaiting.length ? `; PR open, cannot land: ${openWaiting.join(', ')}` : ''}${retained.length ? `; retained environments (not cleaned): ${retained.join('; ')}` : ''}` })
693
+ }
657
694
  }
658
695
  // How long each due action has gone undone, from when it was first printed.
659
696
  const nextSeen = {}
@@ -823,7 +860,7 @@ if (isMainModule(import.meta.url)) {
823
860
  if (opts.json) process.stdout.write(`${JSON.stringify({ ...result, mode, exitDue }, null, 2)}\n`)
824
861
  else if (mode !== 'full') {
825
862
  const view = narrowView(plan, result)
826
- const guardNames = [...new Set(result.due.filter((a) => /^guards: (?!none)/m.test(view) && ['decide', 'review-verdict', 'unblock', 'worker-kind', 'split', 'sync', 'add-unit', 'drop-unit', 'set-deps', 'move-link', 'blocked-outside'].includes(a.action)).map((a) => a.action))]
863
+ const guardNames = [...new Set(result.due.filter((a) => /^guards: (?!none)/m.test(view) && ['recover-workspace', 'prepare-workspace', 'cleanup-workspace', 'decide', 'review-verdict', 'unblock', 'worker-kind', 'split', 'sync', 'add-unit', 'drop-unit', 'set-deps', 'move-link', 'blocked-outside'].includes(a.action)).map((a) => a.action))]
827
864
  process.stdout.write(`${mode === 'off' ? `${view.split('\n')[0].replace(/: .*$/, '')}${guardNames.length ? ` (clear first: ${guardNames.join(', ')})` : ''}` : view}\n`)
828
865
  process.stdout.write(`${budgetLine(result.capacity)}\n`)
829
866
  process.stdout.write(rulesLine(`${mode === 'off' ? 'Scheduler mode off: plan every step yourself from the plan and results.' : 'Scheduler mode narrow: clear the guards, then choose each unit\'s next step yourself.'} The run ends only when this prints "run done".`))
@@ -843,7 +880,7 @@ if (isMainModule(import.meta.url)) {
843
880
  // The narrow view: whether the run is done, which units have work, the guards, and what waits on
844
881
  // the human. It leaves each unit's next step to the coordinator.
845
882
  export function narrowView(plan, result) {
846
- const GUARDS = new Set(['decide', 'review-verdict', 'unblock', 'worker-kind', 'split', 'sync', 'add-unit', 'drop-unit', 'set-deps', 'move-link', 'blocked-outside'])
883
+ const GUARDS = new Set(['recover-workspace', 'prepare-workspace', 'cleanup-workspace', 'decide', 'review-verdict', 'unblock', 'worker-kind', 'split', 'sync', 'add-unit', 'drop-unit', 'set-deps', 'move-link', 'blocked-outside'])
847
884
  const units = new Map((plan.units ?? []).map((u) => [String(u.id), u]))
848
885
  const guards = result.due.filter((a) => GUARDS.has(a.action)).map((a) => `${a.unit ?? 'run'}: ${a.detail}`)
849
886
  const work = [...new Set([...result.due, ...result.waiting].filter((a) => a.unit && !GUARDS.has(a.action) && a.action !== 'wait').map((a) => a.unit))]
@@ -22,7 +22,7 @@ import { fileURLToPath } from 'node:url'
22
22
 
23
23
  import { workerResources } from '../../engineering-runtime/scripts/installation-resolver.mjs'
24
24
  import { isMainModule } from '../../engineering-runtime/scripts/main-module.mjs'
25
- import { RULES, SKILL_PATH } from './next-actions.mjs'
25
+ import { RULES, RUN_SKILLS, SKILL_PATH, workspaceCleanupSettled } from './next-actions.mjs'
26
26
 
27
27
  export const ACTIVE_HOURS = 24
28
28
  const SCRIPTS = dirname(fileURLToPath(import.meta.url))
@@ -52,6 +52,7 @@ export function activeRuns(cwd, now = Date.now(), hours = ACTIVE_HOURS) {
52
52
  if (now - mtime > hours * 3_600_000) continue
53
53
  let plan
54
54
  try { plan = JSON.parse(readFileSync(file, 'utf8')) } catch { continue }
55
+ if (plan.skill !== undefined && !RUN_SKILLS.includes(plan.skill)) continue
55
56
  const units = (Array.isArray(plan?.units) ? plan.units : plan?.issues) ?? []
56
57
  const open = units.filter((u) => !DONE.has(u?.status)).length
57
58
  if (!open) continue
@@ -61,6 +62,39 @@ export function activeRuns(cwd, now = Date.now(), hours = ACTIVE_HOURS) {
61
62
  return runs.sort((a, b) => b.mtime - a.mtime).map(({ mtime, ...r }) => r)
62
63
  }
63
64
 
65
+ // Recovery discovery must not activate a workflow, and has no age cutoff.
66
+ export function cleanupRuns(cwd) {
67
+ const root = runsRoot(cwd)
68
+ if (!root) return []
69
+ let names
70
+ try { names = readdirSync(root) } catch { return [] }
71
+ const out = []
72
+ for (const name of names) {
73
+ const dir = join(root, name)
74
+ let plan
75
+ try { plan = JSON.parse(readFileSync(join(dir, 'task-plan.json'), 'utf8')) } catch { continue }
76
+ if (Array.isArray(plan.units) && Array.isArray(plan.issues) && JSON.stringify(plan.units) !== JSON.stringify(plan.issues)) {
77
+ out.push({ dir, units: [], reason: 'conflicting units and issues rosters; ownership requires reconciliation' })
78
+ continue
79
+ }
80
+ const units = Array.isArray(plan.units) ? plan.units : plan.issues
81
+ if (!Array.isArray(units)) continue
82
+ const pending = units.filter((u) => u.status === 'succeeded' && u.delivery === 'merged' && u.workspace && !workspaceCleanupSettled(u))
83
+ if (pending.length) out.push({ dir, units: pending.map((u) => String(u.id)),
84
+ ...(pending.some((u) => plan.workspaceLifecycle !== 1 && u.lifecycle?.version !== 1) ? { reason: 'legacy workspace ownership is unresolved; inventory only, never automatic adoption or deletion' } : {}) })
85
+ }
86
+ return out.sort((a, b) => a.dir.localeCompare(b.dir))
87
+ }
88
+
89
+ export function cleanupMessage(runs) {
90
+ if (!runs.length) return ''
91
+ return [
92
+ 'Read-only recovery notice: these delivered units still have pending or interrupted workspace cleanup:',
93
+ ...runs.map((r) => `- ${r.dir}: ${r.units.join(', ') || 'ownership unresolved'}${r.reason ? ` (${r.reason})` : ''}`),
94
+ 'This notice does not activate a workflow or authorize deletion. When the owning work resumes, inspect cleanup-runs.mjs recover --repo <owning repo> --json, then use its recorded ownership and current revision. Never infer completed cleanup from age or an absent path.',
95
+ ].join('\n')
96
+ }
97
+
64
98
  export function message(runs, { skillPath = SKILL_PATH, rushSkillPath = RUSH_SKILL_PATH } = {}) {
65
99
  if (!runs.length) return ''
66
100
  if (runs.some((run) => run.installation)) {
@@ -103,7 +137,7 @@ export function main(argv) {
103
137
  if (hook) {
104
138
  try { cwd = JSON.parse(readFileSync(0, 'utf8'))?.cwd || cwd } catch { /* no input: keep cwd */ }
105
139
  }
106
- const text = message(activeRuns(cwd))
140
+ const text = [message(activeRuns(cwd)), cleanupMessage(cleanupRuns(cwd))].filter(Boolean).join('\n\n')
107
141
  if (text) process.stdout.write(hook ? `${hookOutput(text)}\n` : `${text}\n`)
108
142
  } catch (error) {
109
143
  process.stderr.write(`rehydrate: ${error.message}\n`)
@@ -32,15 +32,16 @@
32
32
  // the fix worker's whole finding list. `action` says how to fix, from a
33
33
  // comparison with the previous round's file (`<unit>-r<N-1>.json` beside
34
34
  // it, or --prior):
35
- // fix first blocking round: fix each finding's rule at its one
36
- // owner and close every row in its paths
35
+ // fix first blocking round: fix each finding's rule across its
36
+ // applicable paths and close every row
37
37
  // test-first a P0/P1 came back after a fix: name the rule it breaks,
38
- // list every path that can break it, test each path, see
39
- // the tests fail, then fix until all pass, with the
38
+ // list the applicable paths, reuse sufficient tests and add
39
+ // focused cases for missing proof, see the open cases fail,
40
+ // then fix until all pass, with the
40
41
  // escalation model
41
42
  // one-by-one only new P0/P1, introduced by the last fix: one finding
42
43
  // per fix worker, in turn, the smallest change that closes
43
- // every row at the rule's owner
44
+ // every row across the applicable paths
44
45
  // 5 wait: no P0/P1 is open, but a GitHub reviewer whose changes request a fix brief
45
46
  // already carried has not re-reviewed (`awaiting`, from github-reviews.mjs merge);
46
47
  // request their re-review and run the merge and this verdict again when it lands
@@ -332,9 +333,9 @@ export function carryRecord(prior, head, identity = {}) {
332
333
  }
333
334
 
334
335
  const INSTRUCTIONS = {
335
- 'fix': 'Fix only these findings. For each, fix its `rule` at the one place every path passes through, never by copying a guard to each path, and close every row in its `paths`.',
336
- 'test-first': 'The findings listed in `recurring` survived a fix: the last fix closed one path and missed others. For each, before changing code: name the rule the bug breaks; list every code path that can break it, including the trigger of this finding id in each earlier round\'s review file beside this one; write a test per path from its trigger and run them: see it fail on each open path. Then fix the rule at its one owner, the place every path passes through, not path by path, until every test passes. "Fixed" is every path\'s test passing. Fix the rest as usual.',
337
- 'one-by-one': 'The last fix introduced these findings. Fix them one at a time, one fix worker each, with the smallest change that closes every row of its `paths` at the rule\'s owner; touch nothing else.',
336
+ 'fix': 'Fix only these findings. For each, make the smallest complete fix for its `rule` across the applicable paths and close every row in its `paths`. Use a shared owner when useful, but do not require a refactor; focused fixes on individual paths are acceptable.',
337
+ 'test-first': 'The findings listed in `recurring` survived a fix: the last fix closed one path and missed others. For each, before changing code: name the rule the bug breaks; list every code path that can break it, including the trigger of this finding id in each earlier round\'s review file beside this one; reuse sufficient tests and add focused cases from the concrete triggers, then run them to see it fail on each open path. One table-driven test may cover several paths. Then make the smallest complete fix across the applicable paths until every test passes. Use a shared owner when useful, but do not require a refactor; focused fixes on individual paths are acceptable. "Fixed" is every path\'s test passing. Fix the rest as usual.',
338
+ 'one-by-one': 'The last fix introduced these findings. Fix them one at a time, one fix worker each, with the smallest complete change that closes every row of its `paths`. Use a shared owner when useful, but do not require a refactor; focused fixes on individual paths are acceptable. Touch nothing else.',
338
339
  }
339
340
 
340
341
  // The fix worker's brief carries only P0/P1 findings; P2/P3 never reach it.
@@ -0,0 +1 @@
1
+ export * from '../../harness-runtime/workspace-lifecycle.mjs'
@@ -4,7 +4,7 @@ description: "Slash/explicit-only (/resolve-release). Full GitHub Flow release f
4
4
  disable-model-invocation: true
5
5
  metadata:
6
6
  workflow-role: coordinator
7
- release-version: 0.44.1
7
+ release-version: 0.45.1
8
8
  ---
9
9
  **Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
10
10
  # Resolve release
@@ -6,7 +6,7 @@ metadata:
6
6
  workflow-role: coordinator
7
7
  dependencies:
8
8
  - resolve-issues
9
- release-version: 0.44.1
9
+ release-version: 0.45.1
10
10
  ---
11
11
  **Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
12
12
  # Rush issues (shortcut of resolve-issues)
@@ -55,7 +55,9 @@ question.
55
55
  mode too: keep one immediate checkpoint per active unit, justify infrastructure and
56
56
  reassess repeated non-convergence. More worker activity is not a reason to reset it.
57
57
  The shared [task-plan checkpoints](../resolve-issues/references/task-plan.md#checkpoints)
58
- apply too: on Pi, bind or reload through `update_plan` with the absolute
59
- workflow path and persisted revision (no event means read-only), then
60
- save transitions through its `workflow` payload. The TUI refreshes
61
- automatically; never edit the bound plan directly.
58
+ apply too: when `update_todo` is advertised, capture the known outcome before
59
+ substantive work, then bind or reload with the absolute workflow path and
60
+ persisted revision. No event means the plan is read-only; save transitions
61
+ through its `workflow` payload. Never edit a bound plan directly. When the
62
+ optional tool is absent or disabled, use the workflow's file transition; never
63
+ enable the interface as a precondition.
@@ -6,7 +6,7 @@ metadata:
6
6
  workflow-role: coordinator
7
7
  dependencies:
8
8
  - resolve-release
9
- release-version: 0.44.1
9
+ release-version: 0.45.1
10
10
  ---
11
11
  **Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
12
12
  # Rush release (shortcut of resolve-release)
@@ -1,14 +1,14 @@
1
1
  ---
2
2
  name: test-quality
3
3
  description: "Seven principles for tests that catch real bugs, and an audit that reports weak, redundant, and not-coverage tests without fixing them. Use when writing, changing, or reviewing tests, or when asked whether tests are useful, redundant, over-mocked, flaky, only assert static text, or test config files, or asked to make a slow test suite faster without losing coverage, or when the user asks to write the testing conventions into a repo's CONTRIBUTING.md. NOT defining what a spec must prove (ensure-coverage) or authoring browser tests (e2e-test)."
4
- release-version: 0.44.1
4
+ release-version: 0.45.1
5
5
  ---
6
6
  **Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
7
7
  # Test Quality
8
8
 
9
9
  A test is worth keeping when it fails for a real bug and stays green through a harmless change. This skill states seven principles that decide that and audits a suite against them. Each principle's examples, common failures, and look-alikes: [references/principles.md](references/principles.md).
10
10
 
11
- - **Write mode**: the principles are the bar while you write or change tests. Check each new assertion against the one question, and before you commit run `node "$SKILL_DIR/scripts/lint-tests.mjs" --diff <base>` from the repo root ([lint](#the-lint)); it covers uncommitted and untracked tests.
11
+ - **Write mode**: judge the tests needed for the requested behavior using the principles and the one question. Run `node "$SKILL_DIR/scripts/lint-tests.mjs" --diff <base>` when requested or required by the repository ([lint](#the-lint)); it covers uncommitted and untracked tests. An optional lint result does not create a new acceptance gate.
12
12
  - **Audit mode**: reviewer subagents sweep a suite or a diff and report findings, quick by default ([audit](#audit-mode)). The audit **never edits, rewrites, or deletes a test**. It names the principle broken and the evidence; the fix belongs to the implementer.
13
13
  - **Persist mode**: only when the user asks, write the principles into the repo's `CONTRIBUTING.md` ([persist](#persist-mode)).
14
14
 
@@ -22,6 +22,21 @@ A test is worth keeping when it fails for a real bug and stays green through a h
22
22
  | 6 | [Don't test config files](references/principles.md#6-dont-test-config-files) | Restating a config file in a test | a Dockerfile, workflow, `cloudbuild.yaml`, `.mergify.yml`, `package.json`, or other JSON/YAML/TOML config read, then any assertion |
23
23
  | 7 | [Same code, same result](references/principles.md#7-same-code-same-result) | A result that depends on luck | `Math.random`/`uuid()`/`Date.now()` in checked output; `sleep(…)`; passes in the suite, fails alone; scratch files under the repo or `cwd` |
24
24
 
25
+ ## Scope while writing
26
+
27
+ Use these principles to judge the tests the task needs, not to start a suite-wide
28
+ cleanup. First identify the requested behavior or concrete regression and reuse
29
+ existing tests that prove it. Add focused assertions at the lowest level that
30
+ observes the real outcome; do not automatically duplicate them across test layers.
31
+
32
+ Before testing a defensive branch, check that its behavior belongs to the supported
33
+ contract. A new guard or fallback does not create its own testing requirement.
34
+ Speculative failures, coverage percentages, and test counts do not justify expanding
35
+ a feature into a harness, framework, or gate project. Keep binding checks and material
36
+ security, money, tenancy, and data-integrity proof; report unrelated debt rather than
37
+ fixing it. Stop once the agreed behavior is sufficiently proven. Broad audits and
38
+ convention persistence remain separate, explicitly requested tasks.
39
+
25
40
  ## The one question
26
41
 
27
42
  For every assertion, answer both halves:
@@ -35,7 +50,7 @@ A good assertion has a concrete answer to (1) and none to (2). An assertion with
35
50
 
36
51
  1. **Test behavior, not text.** Run the code and observe what it does. The text of a doc, prompt, source file, or migration is not behavior. A test whose only assertions are text matches over checked-in files is **`Not coverage`** for the behavior the text describes, never partial proof.
37
52
  2. **The expected value comes from somewhere other than the code under test.** Take it from the spec, an external contract, a separately written simpler model, or the input: never from the code under test or a literal copied out of it. When a version really is the contract, pin the major only.
38
- 3. **Fake only what you don't own.** Fake third-party APIs, providers, the clock, and randomness. Your database and services run for real, because a fake cannot see transactions, tenant filters, locking, or idempotency. An over-mocked test is `Not coverage` for the behavior the fake hides.
53
+ 3. **Fake only what you don't own for boundary proof.** Use real database and service behavior when proving transactions, tenant filters, locking, or idempotency; fake external providers, clock, and randomness. A focused unit test may isolate an internal collaborator, but proves only behavior it actually executes. An over-mocked test is `Not coverage` for the behavior the fake hides. Follow stricter adopted repo rules where required.
39
54
  4. **An assertion must be able to fail.** Every test can go red and checks what its name promises: read the name's verbs ("atomically", "then", "corrected to") and find the assertion for each.
40
55
  5. **Test each behavior once, and only for code that still runs.** Prove each behavior once, at the lowest level that proves it for real. Layering (a pure unit for the decision table plus one real-DB test per rule) is not duplication; many similar cases merge into a table rather than being deleted.
41
56
  6. **Don't test config files.** Dockerfiles, `cloudbuild.yaml`, `.github/workflows/*`, `.mergify.yml`, `package.json`, `tsconfig.json`, `pyproject.toml`, `.env`, and any other JSON, YAML, or TOML a tool reads get no tests: the tool that reads the file is the test. Test the code that reads config; run scripts, because scripts are code. A pin added after a real bug is still a config test: delete it and name the missing behavior check as a follow-up. JSON the app loads as data (locales, price tables) is not config.
@@ -69,7 +84,7 @@ It writes the seven principles ([references/testing-conventions.md](references/t
69
84
 
70
85
  ## The lint
71
86
 
72
- Scripts live under **this skill's directory**: set `SKILL_DIR` to the directory you read this file from, never your CWD. `node "$SKILL_DIR/scripts/lint-tests.mjs"` scans the tree; `--diff <base>` only lines changed since the merge-base. A **gate** rule is decidable and fails the exit code; a **lead** also matches legitimate tests, so read each hit against the principle's look-alikes. Rules, flags, and what a clean run proves (little, for Python and Go): [references/lint.md](references/lint.md).
87
+ Scripts live under **this skill's directory**: set `SKILL_DIR` to the directory you read this file from, never your CWD. `node "$SKILL_DIR/scripts/lint-tests.mjs"` scans the tree; `--diff <base>` only lines changed since the merge-base. A tool-local **gate** rule fails the command's exit status; it is not automatically a repository merge requirement. Assess optional hits in context and preserve existing adopted gates. Rules, flags, and what a clean run proves (little, for Python and Go): [references/lint.md](references/lint.md).
73
88
 
74
89
  ## Common wrong answers
75
90
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  Scripts live under **this skill's directory**. Set `SKILL_DIR` to the directory you read this file from and run `node "$SKILL_DIR/scripts/lint-tests.mjs"`. Never resolve against your CWD. A missing script in the audited repo is not a blocker; the scripts belong to this skill.
6
6
 
7
- A **gate** rule is decidable and fails the exit code; a **lead** rule also matches some legitimate tests, so read each hit against the principle's "Not a failure" cases.
7
+ A tool-local **gate** rule fails the command's exit status; a **lead** rule reports a pattern for inspection. Run this optional tool when requested or required by the repository, scoped to the task. Its classifier detects syntax, not whether a test proves its claimed behavior. An optional hit does not create a new merge requirement or require replacing valid unit isolation with infrastructure. Preserve adopted repo gates and report their actual failures; changing those gates requires approval.
8
8
 
9
9
  | rule | principle | severity | finds |
10
10
  |---|---|---|---|
@@ -49,14 +49,14 @@
49
49
 
50
50
  ## 3. Fake only what you don't own
51
51
 
52
- **Principle.** Real internals, faked externals. Substitute only what you do not own: third-party APIs, providers, clock, randomness (faking the clock and randomness is also how a test stays repeatable: [principle 7](#7-same-code-same-result)). Your database and services run for real, because a fake cannot see what lives in them: transactions, tenant filters, locking, idempotency.
52
+ **Principle.** For proof of a real boundary, use real internals and fake externals: third-party APIs, providers, clock, and randomness. A fake database cannot prove transactions, tenant filters, locking, or idempotency. A focused unit test may isolate an internal collaborator to exercise the caller's own decision logic; it does not prove the collaborator's behavior. Existing stricter repo rules remain binding.
53
53
 
54
54
  **Done right.**
55
55
  - Real Postgres with migrations; only the payment provider faked.
56
56
  - A spy at an external boundary where **the call is the behavior**: the backoff delay, the precondition header sent to storage, "the provider was not called after validation failed".
57
57
 
58
58
  **Common failures.**
59
- - *First-party mock:* `vi.mock('@/lib/prisma')`, patching the class's own `_private` helper. The `mock-internal-seam` rule catches the JS module form. Your own thin adapter around a provider (`@/lib/stripe-client`) *is* the external boundary: declare it in `coverage.config.json` under `mockPolicy.externals` rather than calling it a finding.
59
+ - *First-party mock counted as boundary proof:* `vi.mock('@/lib/prisma')` cannot prove real persistence; patching a private helper cannot prove the code replaced. The `mock-internal-seam` scanner flags the JS module form, but an optional hit needs this semantic check. Your own thin adapter around a provider (`@/lib/stripe-client`) is the external boundary; declare it in `coverage.config.json` under `mockPolicy.externals` where the repo uses that topology.
60
60
  - *Blind injected fake:* a `deps` fake returns rows whatever the query says, and the test asserts `findUnique({ where: { id } })`. Delete the tenant check and it stays green. No rule sees `deps` injection; read for it.
61
61
  - *Mocked transaction:* "updates atomically" with `$transaction: (fn) => fn()`.
62
62
  - *Layout in jsdom:* `toHaveClass('min-h-11')` to prove a 44px target. jsdom has no layout.
@@ -2,7 +2,7 @@
2
2
 
3
3
  1. Run the code and assert what it does; never match the text of docs, prompts, source files, or migrations.
4
4
  2. Take expected values from the spec, an external contract, or the input, never from the code under test; pin versions to the major only.
5
- 3. Fake only third-party services, the clock, and randomness; run your own database and services for real.
5
+ 3. Prove transactions, tenant filters, locking, and idempotency against real database and service behavior; fake external providers, the clock, and randomness. Internal unit-test substitutes prove only the caller behavior they execute, not the replaced boundary. Preserve stricter adopted repository requirements.
6
6
  4. Make every assertion able to fail, and assert everything the test name promises.
7
7
  5. Test each behavior once, at the lowest level that proves it; delete tests of dead code.
8
8
  6. Don't test config files (Dockerfiles, CI workflows, `cloudbuild.yaml`, merge-queue config, `package.json`, other JSON/YAML/TOML a tool reads); test the code that reads them.
@@ -2,7 +2,7 @@
2
2
  name: compliance-audit
3
3
  version: 2.9.0
4
4
  description: "Audit operations against financial-compliance checklists (AML/KYC, VARS, PCI-DSS, TPA oversight, onboarding, transaction monitoring). Produces tracker.md (with a tracker.xlsx export) and phase-trail.md with layered validation; cites evidence by original filename. Input: reg-parser or the built-in VARS checklist."
5
- release-version: 0.44.1
5
+ release-version: 0.45.1
6
6
  ---
7
7
  **Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
8
8
  # Compliance Audit
@@ -12,13 +12,9 @@ keyed by control item that the client can update through remediation —
12
12
  alongside a phase trail companion file (work product) that preserves what each
13
13
  phase saw before the tracker was finalised.
14
14
 
15
- ## Audience
16
-
17
- The user is a **compliance auditor or analyst** working in regulated financial
18
- services — payments, banking, fintech, insurance. They audit against financial
19
- regulatory frameworks: AML regulations, KYC requirements, financial reporting
20
- standards, local financial authority rules, and card-scheme standards such as
21
- VARS.
15
+ Read [audience and report handoff](references/audience-and-reports.md) before starting;
16
+ it defines the auditor audience and the optional report-writing handoff without replacing
17
+ this assessment or its validation.
22
18
 
23
19
  ## Audit Workflow
24
20
 
@@ -0,0 +1,12 @@
1
+ # Audience and report handoff
2
+
3
+ The user is a **compliance auditor or analyst** working in regulated financial
4
+ services -- payments, banking, fintech, insurance. They audit against financial
5
+ regulatory frameworks: AML regulations, KYC requirements, financial reporting
6
+ standards, local financial authority rules, and card-scheme standards such as
7
+ VARS.
8
+
9
+ When the user also requests an analytical report, hand the validated tracker and phase trail
10
+ to `write-report` when installed. Its compliance branch owns audit, external, management and
11
+ VARS report writing; it does not replace this assessment or its validation. Without that skill,
12
+ deliver the audit artifacts and state that the report-writing stage remains outstanding.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: contract-compare
3
3
  description: "Compare two or more contracts side-by-side for structured diff, gap analysis, and benchmarking. Use when: comparing contracts or agreements, contract diff, compare terms, vendor-vs-vendor, compare our template vs theirs, compliance gap between contracts. NOT for single-contract risk review — use contract-review for that."
4
- release-version: 0.44.1
4
+ release-version: 0.45.1
5
5
  ---
6
6
  **Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
7
7
  # Contract Compare
@@ -97,6 +97,22 @@ Wait for all dimension subagents to return.
97
97
 
98
98
  ## Step 4: Build Comparison Report
99
99
 
100
+ Required report-stage resource: the installed `house-style` skill. This skill remains available
101
+ as an ungrouped native selection; native Skills CLI does not install dependencies automatically.
102
+ Select `house-style` with `contract-compare`, or install the Core and Risk & Compliance npm packages.
103
+ Resolve its installed base directory, not a repository-only `skills/writing/` path.
104
+
105
+ Before drafting or substantively reviewing an analytical report, read
106
+ `{house-style baseDir}/references/analytical-reporting.md`,
107
+ `{house-style baseDir}/references/analytical-writing-patterns.md`, and
108
+ `{house-style baseDir}/references/definitions-and-metrics.md` directly. Follow their
109
+ loading and delegation instructions; report a missing or unreadable reference as a
110
+ blocker rather than claiming the report stage is complete. Apply them within the
111
+ requested scope, including a chat-only response; do not create a report for an
112
+ audit-only or narrower request. Preserve the selected dimensions, clause evidence,
113
+ assessments, legal caveats, optional mandate tracing, and the required report format.
114
+ Generic editorial checks do not replace specialist legal review.
115
+
100
116
  **House style.** Use the nearest `WRITING.md`: start in the first contract's folder (pasted text: the output
101
117
  folder, else the working directory), then parents up to the repository root; never the home
102
118
  directory or above. Rate every issue from the evidence first; the style changes
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: contract-review
3
3
  description: "Review contracts for legal, commercial, and compliance risks via convert → chunk review → merge → validate (+ optional mandate preprocessing). Use for reviewing, auditing, or checking any contract, agreement, NDA, MSA, or SLA; input a file path (PDF/DOCX/TXT) or pasted text."
4
- release-version: 0.44.1
4
+ release-version: 0.45.1
5
5
  ---
6
6
  **Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
7
7
  # Contract Review
@@ -106,6 +106,10 @@ Start all chunk subagents in parallel and wait for all to complete.
106
106
 
107
107
  ## Step 3: Merge Results
108
108
 
109
+ Before drafting or substantively reviewing the report, read and follow
110
+ [the analytical report-stage instructions](references/report-stage.md), including all three
111
+ required `house-style` references and their loading/delegation rules. Preserve Step 4 validation.
112
+
109
113
  **House style.** Use the nearest `WRITING.md`: start in the contract file's folder (pasted text: the output
110
114
  folder, else the working directory), then parents up to the repository root; never the home
111
115
  directory or above. Rate every issue from the evidence first: the style changes wording, never a rating,
@@ -182,6 +186,7 @@ Substitute `{contract-review baseDir}` and `{reg-parser baseDir}` as in Step 2:
182
186
  You are validating a contract review report for completeness and consistency.
183
187
 
184
188
  Load the review criteria from: {contract-review baseDir}/references/review-criteria.md
189
+ Load the analytical reporting instructions from: {house-style baseDir}/references/analytical-reporting.md, {house-style baseDir}/references/analytical-writing-patterns.md, and {house-style baseDir}/references/definitions-and-metrics.md
185
190
  Load any preprocessed mandate files from: {reg-parser baseDir}/references/mandates/ (read all .md files present)
186
191
  Use mandate files to verify P10 annotations ([Mandated]/[Permitted]/[Counterparty-Added]) are accurate.
187
192
 
@@ -0,0 +1,17 @@
1
+ # Analytical report stage
2
+
3
+ Required report-stage resource: the installed `house-style` skill. This skill remains available
4
+ as an ungrouped native selection; native Skills CLI does not install dependencies automatically.
5
+ Select `house-style` with `contract-review`, or install the Core and Risk & Compliance npm packages.
6
+ Resolve its installed base directory, not a repository-only `skills/writing/` path.
7
+
8
+ Before drafting or substantively reviewing an analytical report, read
9
+ `{house-style baseDir}/references/analytical-reporting.md`,
10
+ `{house-style baseDir}/references/analytical-writing-patterns.md`, and
11
+ `{house-style baseDir}/references/definitions-and-metrics.md` directly. Follow their
12
+ loading and delegation instructions; report a missing or unreadable reference as a
13
+ blocker rather than claiming the report stage is complete. Apply them within the
14
+ requested scope, including a chat-only response; do not create a report for an
15
+ audit-only or narrower request. Preserve this skill's review criteria, evidence,
16
+ severity ratings, mandated layout, and Step 4 specialist validation. Generic editorial
17
+ checks do not replace legal validation.
@@ -3,7 +3,7 @@ name: doc-authenticity
3
3
  version: 0.4.1
4
4
  description: "Screen PDF documents and raster images for fabrication, tampering, Photoshop or other image-editor use, and content validity. Detects file type, runs format-specific authenticity forensics, classifies bank statements, conversation logs, invoices, agreements, and other documents, then applies type-specific validity checks. Produces separate authenticity (NO_EVIDENCE_OF_MODIFICATION / SUSPICIOUS / LIKELY_MODIFIED / INCONCLUSIVE) and validity (VALID / INVALID / INCONCLUSIVE) verdicts. Use whenever a user asks whether a document, screenshot, scan, statement, invoice, contract, chat log, receipt, or payment evidence is genuine, original, edited, Photoshopped, manipulated, fake, or valid."
5
5
  disable-model-invocation: true
6
- release-version: 0.44.1
6
+ release-version: 0.45.1
7
7
  ---
8
8
  **Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
9
9
  # Document and Image Authenticity
@@ -117,6 +117,16 @@ future specialized profile is added.
117
117
 
118
118
  ### 5. Verdict and report
119
119
 
120
+ Before drafting or substantively reviewing an analytical report, read
121
+ `{house-style baseDir}/references/analytical-reporting.md`,
122
+ `{house-style baseDir}/references/analytical-writing-patterns.md`, and
123
+ `{house-style baseDir}/references/definitions-and-metrics.md` directly. Follow their
124
+ loading and delegation instructions; report a missing or unreadable reference as a
125
+ blocker rather than claiming the report stage is complete. Apply them within the
126
+ requested scope, including a chat-only response; do not create a report for an
127
+ audit-only or narrower request. Preserve forensic and validity methods, evidence,
128
+ independent verdicts, required checks, report layout, schema, and validation.
129
+
120
130
  Read `references/verdict-and-report.md`. Apply its rubrics independently to:
121
131
 
122
132
  - authenticity: `NO_EVIDENCE_OF_MODIFICATION | SUSPICIOUS | LIKELY_MODIFIED | INCONCLUSIVE`
@@ -0,0 +1,3 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 1AboveIO