@outerlayer/cli 0.13.0 → 0.14.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.
Files changed (77) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/agent-setup-CN2HUBEJ.js +26 -0
  3. package/dist/{build-LELVCWHV.js → build-BG2YENGK.js} +2 -2
  4. package/dist/build-info.json +1 -1
  5. package/dist/{builder-UUS4ORDH.js → builder-2D3WSOOY.js} +4 -4
  6. package/dist/{check-63IDCEEV.js → check-SRGR36KI.js} +30 -30
  7. package/dist/{chunk-GI4XXD4I.js → chunk-3NLOVYJQ.js} +4 -4
  8. package/dist/{chunk-C6IV5BV7.js → chunk-4OGPT6I2.js} +11 -11
  9. package/dist/{chunk-DXQ5MHCO.js → chunk-FOJGZYFC.js} +11 -5
  10. package/dist/{chunk-HDTVTA6I.js → chunk-HFKKFTXU.js} +11 -15
  11. package/dist/{chunk-DPKX26LT.js → chunk-HFKQIZXL.js} +9 -6
  12. package/dist/{chunk-6S3H3J6H.js → chunk-JJEGKEFV.js} +5 -7
  13. package/dist/{chunk-P6FX4D43.js → chunk-JYIEHQIF.js} +1 -1
  14. package/dist/{chunk-XJKL4BNU.js → chunk-KFRM23WD.js} +2 -2
  15. package/dist/{chunk-3KTA5UU2.js → chunk-KMDLFI4P.js} +1 -1
  16. package/dist/{chunk-3D4OYFCD.js → chunk-MSASO5O4.js} +42 -6
  17. package/dist/chunk-MTF3PHVZ.js +161 -0
  18. package/dist/{chunk-SVEP7PVH.js → chunk-NHDVD7DU.js} +9 -9
  19. package/dist/{chunk-EFZLAYPE.js → chunk-NPQWKGUA.js} +1 -1
  20. package/dist/{chunk-SFFHGX3G.js → chunk-PQUM37BK.js} +1 -1
  21. package/dist/{chunk-MJXXRX4M.js → chunk-Q2UK3C4P.js} +2 -2
  22. package/dist/{chunk-MZN7AQOB.js → chunk-RBKIH5ST.js} +11 -11
  23. package/dist/{chunk-TOPDNXHZ.js → chunk-SMHOCDCI.js} +4 -4
  24. package/dist/{chunk-P4MM66C6.js → chunk-SWHQ4LUM.js} +4 -4
  25. package/dist/{agent-setup-QE6PSCEZ.js → chunk-SXWS25JC.js} +6 -19
  26. package/dist/{chunk-CNFVHDHG.js → chunk-WUYTWNMA.js} +3 -1
  27. package/dist/{chunk-R7OQTKWT.js → chunk-XCK2YAY5.js} +12 -3
  28. package/dist/{chunk-4QVZCVWT.js → chunk-YDSMROTB.js} +1 -1
  29. package/dist/{chunk-TCIW27FM.js → chunk-ZA5CHAFY.js} +1 -1
  30. package/dist/{cli-WSBIAVZU.js → cli-UW2H3UUW.js} +79 -62
  31. package/dist/{command-N7BAB4XJ.js → command-5LD7G2QQ.js} +22 -22
  32. package/dist/{connect-cmd-FGYRLLXL.js → connect-cmd-JUAW6MU4.js} +2 -2
  33. package/dist/{context-adopt-B3PWGFHL.js → context-adopt-PJUMW2EZ.js} +5 -5
  34. package/dist/{context-materialize-7S6NLLRO.js → context-materialize-FDHYKNDT.js} +5 -5
  35. package/dist/{docker-BZ55BMBJ.js → docker-4J77Q2BF.js} +5 -5
  36. package/dist/{doctor-3H25Q7WT.js → doctor-H7REBSLQ.js} +24 -24
  37. package/dist/{emit-artifact-cmd-IQCRXOH7.js → emit-artifact-cmd-FOGNJXVR.js} +15 -18
  38. package/dist/{emit-cmd-KDSM5VAO.js → emit-cmd-ZEYWLC2C.js} +3 -2
  39. package/dist/{emit-criteria-cmd-EYUXME26.js → emit-criteria-cmd-HBP6XS5C.js} +5 -8
  40. package/dist/{emit-finding-cmd-P4RKBPBB.js → emit-finding-cmd-DLI4MKMN.js} +5 -8
  41. package/dist/{emit-result-cmd-6XYENU6H.js → emit-result-cmd-MUSL7GQA.js} +9 -12
  42. package/dist/{hook-fast-PXWKQPN6.js → hook-fast-WYQD6D7T.js} +1 -1
  43. package/dist/{import-capture-cmd-2BKM5I2N.js → import-capture-cmd-6QVCOIHK.js} +8 -11
  44. package/dist/{import-ruler-cmd-IXR7YK47.js → import-ruler-cmd-KLRGRBCP.js} +5 -8
  45. package/dist/index.js +3 -3
  46. package/dist/{init-2LWVS4AH.js → init-NTXA3SN7.js} +23 -23
  47. package/dist/{init-ZL7ORPTF.js → init-UBXK6MF7.js} +23 -23
  48. package/dist/{init-cmd-JXOBCOIB.js → init-cmd-NARDUPIG.js} +13 -8
  49. package/dist/init-update-O5YYFGF4.js +165 -0
  50. package/dist/{loop-XOAE6ZIY.js → loop-SW2M5VDD.js} +30 -30
  51. package/dist/{machine-G6PY2J2J.js → machine-U66FKVDP.js} +21 -21
  52. package/dist/mcp-install-cmd-FDTPQQS6.js +9 -0
  53. package/dist/{runner-key-TDTLNAKJ.js → runner-key-3JSMDRJA.js} +23 -23
  54. package/dist/{session-sender-XVGZFWRK.js → session-sender-4M247XSV.js} +5 -5
  55. package/dist/{starter-pack-B7JPGEDO.js → starter-pack-IB4UUIX3.js} +2 -2
  56. package/dist/{status-MRTPG34B.js → status-STZYPU3W.js} +26 -26
  57. package/dist/{sync-cmd-UBUZMMDN.js → sync-cmd-UWD5JOFU.js} +4 -4
  58. package/dist/{watch-STNPEWD6.js → watch-DBNB7R6X.js} +4 -4
  59. package/dist/{work-claim-cmd-CWGYEIJE.js → work-claim-cmd-LY4U6QHN.js} +11 -11
  60. package/dist/{work-cmd-X7NMJDFL.js → work-cmd-RVRQ32EH.js} +4 -4
  61. package/dist/{work-comment-cmd-FI2TMAYQ.js → work-comment-cmd-FFUM4TJU.js} +7 -10
  62. package/dist/{work-open-pr-cmd-OVHJ5I5H.js → work-open-pr-cmd-HLDWP4T2.js} +7 -10
  63. package/dist/{work-pr-cmd-Y5TDMQ4O.js → work-pr-cmd-L77K32WK.js} +4 -4
  64. package/package.json +1 -1
  65. package/skill-pack/maintained/emitting-evidence/SKILL.md +8 -2
  66. package/skill-pack/shipped.json +70 -0
  67. package/skill-pack/template/build/SKILL.md +6 -2
  68. package/skill-pack/template/build/references/release.md +36 -8
  69. package/skill-pack/template/build/references/review-loop.md +1 -1
  70. package/skill-pack/template/spec/SKILL.md +33 -22
  71. package/skill-pack/template/writing-specs/SKILL.md +36 -3
  72. package/skill-pack/template/writing-specs/references/agents-snippet.md +57 -0
  73. package/skill-pack/template/writing-tests/SKILL.md +117 -0
  74. package/skill-pack/validators/acceptance-criteria.yaml +35 -0
  75. package/dist/mcp-install-cmd-VXVIH7PH.js +0 -9
  76. package/dist/{chunk-NX57IBNK.js → chunk-4NPZVMJU.js} +2 -2
  77. package/dist/{chunk-3FMPZPJQ.js → chunk-R7PF5OOU.js} +1 -1
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { resolveCredentials, postJson, getJson } from './chunk-P4MM66C6.js';
4
- import './chunk-EFZLAYPE.js';
3
+ import { resolveCredentials, postJson, getJson } from './chunk-SWHQ4LUM.js';
4
+ import './chunk-NPQWKGUA.js';
5
5
  import { detectActiveSession } from './chunk-IP7BIBXJ.js';
6
- import './chunk-4QVZCVWT.js';
7
- import { sanitizeTerminal } from './chunk-CNFVHDHG.js';
6
+ import './chunk-YDSMROTB.js';
7
+ import { sanitizeTerminal } from './chunk-WUYTWNMA.js';
8
8
  import { resolveRepoIdentity } from './chunk-JXYVA45C.js';
9
9
  import './chunk-4KSQKQXE.js';
10
10
  import './chunk-N6LURUEF.js';
11
- import './chunk-YRYMNBGQ.js';
11
+ import { C } from './chunk-YRYMNBGQ.js';
12
12
  import { parseTrackerNumber, trackerNumberError } from './chunk-WZBHBH3J.js';
13
13
  import { defaultFetch } from './chunk-YO4PSLVB.js';
14
14
  import './chunk-PLMTLGYH.js';
@@ -22,9 +22,6 @@ import './chunk-Z5GEFMRW.js';
22
22
  import { randomUUID } from 'crypto';
23
23
 
24
24
  createRequire(import.meta.url);
25
- var GREEN = "\x1B[32m";
26
- var DIM = "\x1B[2m";
27
- var RESET = "\x1B[0m";
28
25
  var WorkCommentCommandError = class extends Error {
29
26
  };
30
27
  function resolveAction(opts) {
@@ -78,7 +75,7 @@ async function runWorkComment(opts) {
78
75
  const thread = data.thread;
79
76
  const waitingOn = thread?.waitingOn ?? null;
80
77
  const criterionLabel = opts.criterion ? `criterion ${opts.criterion}` : "the general thread";
81
- const output = opts.json ? JSON.stringify(data) : `${GREEN}\u2713${RESET} commented on ${criterionLabel} of item #${itemNumber} ${DIM}\xB7${RESET} waiting on ${waitingOn ?? "no one"}`;
78
+ const output = opts.json ? JSON.stringify(data) : `${C.green}\u2713${C.reset} commented on ${criterionLabel} of item #${itemNumber} ${C.dim}\xB7${C.reset} waiting on ${waitingOn ?? "no one"}`;
82
79
  if (!opts.quiet) process.stdout.write(output + "\n");
83
80
  return { data, output };
84
81
  }
@@ -102,7 +99,7 @@ async function runWorkThreads(opts) {
102
99
  return { data, output };
103
100
  }
104
101
  function renderThread(label, thread) {
105
- const header = `${DIM}${label === "general" ? "general" : `criterion ${label}`} \xB7 waiting on ${thread.waitingOn ?? "no one"}${RESET}`;
102
+ const header = `${C.dim}${label === "general" ? "general" : `criterion ${label}`} \xB7 waiting on ${thread.waitingOn ?? "no one"}${C.reset}`;
106
103
  const comments = thread.comments.map(
107
104
  (c) => ` ${c.author.kind === "person" ? "person" : "agent"} ${sanitizeTerminal(c.author.name)}: ${sanitizeTerminal(c.body)}${c.action ? ` (${c.action})` : ""}`
108
105
  );
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { itemLabel, runWorkPr, WorkPrCommandError } from './chunk-6S3H3J6H.js';
4
- import { resolveCredentials } from './chunk-P4MM66C6.js';
5
- import './chunk-EFZLAYPE.js';
3
+ import { itemLabel, runWorkPr, WorkPrCommandError } from './chunk-JJEGKEFV.js';
4
+ import { resolveCredentials } from './chunk-SWHQ4LUM.js';
5
+ import './chunk-NPQWKGUA.js';
6
6
  import './chunk-IP7BIBXJ.js';
7
- import './chunk-4QVZCVWT.js';
8
- import './chunk-CNFVHDHG.js';
7
+ import './chunk-YDSMROTB.js';
8
+ import './chunk-WUYTWNMA.js';
9
9
  import './chunk-JXYVA45C.js';
10
10
  import './chunk-4KSQKQXE.js';
11
11
  import './chunk-N6LURUEF.js';
12
- import './chunk-YRYMNBGQ.js';
12
+ import { C } from './chunk-YRYMNBGQ.js';
13
13
  import { parseWorkItemEnv } from './chunk-GQD22KKN.js';
14
14
  import './chunk-WZBHBH3J.js';
15
15
  import { defaultFetch } from './chunk-YO4PSLVB.js';
@@ -25,9 +25,6 @@ import { spawnSync } from 'child_process';
25
25
  import { readFileSync } from 'fs';
26
26
 
27
27
  createRequire(import.meta.url);
28
- var GREEN = "\x1B[32m";
29
- var DIM = "\x1B[2m";
30
- var RESET = "\x1B[0m";
31
28
  var ITEM_KEY_PREFIX = "olitem_";
32
29
  var WorkOpenPrCommandError = class extends Error {
33
30
  };
@@ -163,7 +160,7 @@ async function runWorkOpenPr(opts) {
163
160
  } else {
164
161
  data = await openWithGh({ ...opts, env }, opts.title, opts.bodyFile, cwd);
165
162
  }
166
- const output = opts.json ? JSON.stringify(data) : `${GREEN}\u2713${RESET} ${data.created ? "opened" : "edited"} ${DIM}\xB7${RESET} ${data.repository}#${data.number} ${DIM}\xB7${RESET} ${data.url}${data.workItemNumber == null ? "" : ` ${DIM}\u2192${RESET} ${itemLabel({ workItemId: "", ...data })}`}`;
163
+ const output = opts.json ? JSON.stringify(data) : `${C.green}\u2713${C.reset} ${data.created ? "opened" : "edited"} ${C.dim}\xB7${C.reset} ${data.repository}#${data.number} ${C.dim}\xB7${C.reset} ${data.url}${data.workItemNumber == null ? "" : ` ${C.dim}\u2192${C.reset} ${itemLabel({ workItemId: "", ...data })}`}`;
167
164
  if (!opts.quiet) process.stdout.write(output + "\n");
168
165
  return { data, output };
169
166
  }
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- export { WorkPrCommandError, itemLabel, runWorkPr } from './chunk-6S3H3J6H.js';
4
- import './chunk-EFZLAYPE.js';
3
+ export { WorkPrCommandError, itemLabel, runWorkPr } from './chunk-JJEGKEFV.js';
4
+ import './chunk-NPQWKGUA.js';
5
5
  import './chunk-IP7BIBXJ.js';
6
- import './chunk-4QVZCVWT.js';
7
- import './chunk-CNFVHDHG.js';
6
+ import './chunk-YDSMROTB.js';
7
+ import './chunk-WUYTWNMA.js';
8
8
  import './chunk-JXYVA45C.js';
9
9
  import './chunk-4KSQKQXE.js';
10
10
  import './chunk-N6LURUEF.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@outerlayer/cli",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "OuterLayer CLI — commands: init, connect, doctor, login, logout, sync, daemon (alias: watch), emit, work, runner, hooks, mcp, import, context. Capture and sync coding-agent sessions, and record proof of factory work.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Magu Studios, Inc.",
@@ -104,9 +104,15 @@ Then run `outerlayer sync`, and copy the report's deep link from the pull reques
104
104
 
105
105
  Use `--result fail` when the review left a finding unfixed. Record it once. Outside a build, pass the item's number to `--item`.
106
106
 
107
- ## The criteria report
107
+ ## The criteria report and the `acceptance-criteria` check
108
108
 
109
- Bind the judge's report on each criterion, an HTML file, with `--for acceptance-criteria`. No check requires it. The item page links it at the top of its Criteria tab for the reviewer. Emit it only when the item has recorded criteria.
109
+ Bind the judge's report on each criterion, an HTML file, with `--for acceptance-criteria`. The item page links it at the top of its Criteria tab for the reviewer. Emit it only when the item has recorded criteria.
110
+
111
+ Then run `outerlayer sync`, copy the report's deep link from the evidence comment the same way, and record the judge's verdict on the `acceptance-criteria` check that `outerlayer init` writes:
112
+
113
+ outerlayer emit acceptance-criteria --item "$OUTERLAYER_WORK" --result pass --link <the report's deep link>
114
+
115
+ Use `--result pass` only when the judge found every criterion it judged satisfied, and `--result fail` when it found one unmet. When there is no criterion to judge, record nothing: the row reads "not proven", which is true. Skip the emit in a repository with no `.outerlayer/validators/acceptance-criteria.yaml`.
110
116
 
111
117
  A session never records a pass or fail on an artifact. It emits the artifact, and a person or a check decides.
112
118
 
@@ -0,0 +1,70 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "files": {
4
+ ".outerlayer/policy.yaml": [
5
+ "1d874f1f521a79fc286ed5400c20022921e017f48e60e474015fedc29a1130a2",
6
+ "78c95632b51600df0cb2e206d843e75789ccba2f80fd8dfab36d16239062b008"
7
+ ],
8
+ ".outerlayer/skills/build/SKILL.md": [
9
+ "5cf9bd75fb1f984f6e6b470d1f1581dc06168abf062448e6f43711fed8f6632a",
10
+ "6177ea457be0d238659640e6d29807708d60d68533ae3ff93af10fb513139ced",
11
+ "77cab8b446fb1aa865710f7cb8356c5334301a942f58f9243766bf1acf1377ad",
12
+ "a6743050c5c387e962ad4264526d9a3c10edf36823e4b6829f200bf79a4e45e7",
13
+ "b0aa9b6962675f772b67a4390c15b6b0100390983d958c656a826a066399f3d0",
14
+ "f4e1661c6f87d7ff6d23c6182530fa0a9cb87c4dc3f31e48f2be11f32ebb2c00"
15
+ ],
16
+ ".outerlayer/skills/build/references/agent-briefs.md": [
17
+ "3c875a4f287f574eea954c6daa52b17529cdeba8ec4007d78084d911350d1218",
18
+ "7c2a651e7c827d8e3fb9df889d3776b552eb02d315db5ace27d2baad78aab6a5"
19
+ ],
20
+ ".outerlayer/skills/build/references/criteria-judge.md": [
21
+ "62db8f6d77f31ca25a9071c05429e0f639f9b2bdd7f7cb04cb14a1b0db509537"
22
+ ],
23
+ ".outerlayer/skills/build/references/evidence.md": [
24
+ "ceff5488bae31198ed4738ae1a230c0119f973ced439dac360eba6e24e1604f4",
25
+ "f27502610358e436e7ffb61d4fd281289e162f6de6b2c11396f17b2fa63802c9"
26
+ ],
27
+ ".outerlayer/skills/build/references/release.md": [
28
+ "0b0af40c82473cf192f697537ed5a84801a14e79bb11790d72226d7f7f49cca0",
29
+ "1113259bdf7d8c28edcbdb9b4c83c2f2bd4c5eaf817cf30bf428478ff91694e9",
30
+ "4687f56bcec766f1926d58a208418f800a1727c073b5e6047b40e3aaac2bd6c9",
31
+ "81a764eb317fcb4672bedd35e7b9aa68d1c0b73f82fc2dfa03e5a78758db8f14",
32
+ "985e02b98105b00c356014e003b2d60e9c458c3dcbeef7378274ccb84af031fc"
33
+ ],
34
+ ".outerlayer/skills/build/references/review-briefs.md": [
35
+ "2c8b436795b583a61aa6f98f48611db554f0dd52b27a9ab748a7db5f96526aec"
36
+ ],
37
+ ".outerlayer/skills/build/references/review-loop.md": [
38
+ "155ab732ccf4c25f000853e98a27a3d2f3a022c75979601bb71d86f07983216d",
39
+ "5f30b56d226b279ec5f65ad73b823e3aeeac0c14f2ee539fb79cf7ba7b2cac6d",
40
+ "6f93e2ae74d3c02a0d4c14a6b2c805f523d234a8c56789f40a7c7c59700ef277",
41
+ "9d4d2e5e0307ea8c38cf885cd67d06a939b7fc2bed65f5080ca9610178949d74"
42
+ ],
43
+ ".outerlayer/skills/build/scripts/record-criteria.mjs": [
44
+ "36f19e730bc220fa21622b3bb0a306999f038b8220a0a07a1efe21ec0f748729",
45
+ "596e9a78e62cbea040a3bf428cd54bf59687e9e89001fad719304f88d03ca2ea",
46
+ "64de9445087b247b0781a4e6e025ef97b1259ce1c91819bf541decc26b8f8400"
47
+ ],
48
+ ".outerlayer/skills/spec/SKILL.md": [
49
+ "71e81fabf353ad6117c54e061868538bf00066b78c2528f5a8364445d1def02e",
50
+ "9039ae30d4e836b5c5cfba4a14344e46fd13694ae9e4d88e750abce34dfcd9e6"
51
+ ],
52
+ ".outerlayer/skills/writing-specs/SKILL.md": [
53
+ "a956aac4249118a058c4327c5751472883588720950d4a96f80c7e91f454bef9",
54
+ "eb8aa95f5e76a46ae9bf340989fb40e470fd750ded791ba80b5f8c5c325513e5"
55
+ ],
56
+ ".outerlayer/skills/writing-specs/references/agents-snippet.md": [
57
+ "fc47325f27cfce406a14ad71009d3687d3720deb572d40936e29fd091ca6c71d"
58
+ ],
59
+ ".outerlayer/skills/writing-tests/SKILL.md": [
60
+ "b00f5970b568159ca0bfbc75c3834938749ad5e2e73a80769d88bbe3c8c26737"
61
+ ],
62
+ ".outerlayer/validators/acceptance-criteria.yaml": [
63
+ "3b2054aa1d4bd9f7441e88eec1a3bdf61b61ad00efa79736e26a5dd601d43910"
64
+ ],
65
+ ".outerlayer/validators/code-review.yaml": [
66
+ "05041976e4d84d6256ed88995ea4fd1566abc6d3a9574863065cf855dcafcf70",
67
+ "fbdf0baca0064b09e47f5984afc322d582accd142c3377c06622bc6567b0e98c"
68
+ ]
69
+ }
70
+ }
@@ -84,8 +84,9 @@ These bind the orchestrator in every phase. Rule 5 also binds every spawned agen
84
84
  "$OUTERLAYER_WORK" --json`. Each thread with `waitingOn` of `agent` holds a
85
85
  person's comment. Answer each before the run ends, as the `amend` skill does.
86
86
  5. **A session never records a pass or fail on an artifact.** It emits
87
- artifacts. A person decides pass or fail. The one result a build records
88
- is its own review's verdict, on the `code-review` check.
87
+ artifacts. A person decides pass or fail. A build records two results,
88
+ both verdicts of its own agents: its review's, on the `code-review`
89
+ check, and its criteria judge's, on the `acceptance-criteria` check.
89
90
  6. **Observed status only.** Every status claim in the final report (a gate
90
91
  result, a CI conclusion, mergeability) quotes command output observed
91
92
  after the last change it describes. No quoted output means unknown, never
@@ -218,6 +219,9 @@ Run them in order.
218
219
 
219
220
  outerlayer emit artifact <judge-report>.html --for acceptance-criteria --caption "Each criterion judged against its test"
220
221
 
222
+ Its verdict is recorded on the `acceptance-criteria` check, as release.md
223
+ says. When the state file holds no criteria, no judge runs and that
224
+ check gets no result, so it reads "not proven", never passed.
221
225
  Watch CI until every check has concluded.
222
226
  6. **Manual test and evidence.** Read [references/evidence.md](references/evidence.md)
223
227
  when the pull request opens, and run it. Capture the proof each criterion
@@ -61,12 +61,12 @@ the final report. Never record the result with another link.
61
61
 
62
62
  ## Judge the criteria
63
63
 
64
- When the issue has criteria, spawn one fresh agent on
65
- [criteria-judge.md](criteria-judge.md), with the worktree path, the base ref,
66
- the pull request number, the path of the build's state file, and a report
67
- path under `build-reports/`. Above forty
68
- criteria, spawn one judge per group of about forty in the same wave, each with
69
- its own report path, and merge their results by concatenation.
64
+ When the state file holds criteria, from the issue or drafted, spawn one
65
+ fresh agent on [criteria-judge.md](criteria-judge.md), with the worktree path,
66
+ the base ref, the pull request number, the path of the build's state file,
67
+ and a report path under `build-reports/`. Above forty criteria, spawn one
68
+ judge per group of about forty in the same wave, each with its own report
69
+ path, and merge their results by concatenation into one report.
70
70
 
71
71
  The judge writes an HTML report with one verdict per criterion. Emit it:
72
72
 
@@ -76,8 +76,36 @@ Then run `outerlayer sync`. List every unsatisfied criterion in the final
76
76
  report. A criterion that declares a proof form such as a screenshot or video is
77
77
  proven only by an artifact bound to it on the pull request. The judge cannot
78
78
  see that, so confirm each has an artifact of the declared kind, and emit any
79
- that is missing in the evidence phase. When no criteria were recorded, no judge
80
- runs and no judge report is emitted.
79
+ that is missing in the evidence phase.
80
+
81
+ ## Record the criteria verdict
82
+
83
+ Once the judge's report is synced, copy its deep link from the pull request's
84
+ evidence comment. The link can take up to a minute to appear after the sync.
85
+ If it is not there yet, wait a minute and read the comment again. Never build
86
+ the link by hand, and never link a pull request comment. Then record the
87
+ judge's verdict on the `acceptance-criteria` check, once:
88
+
89
+ outerlayer emit acceptance-criteria --item "$OUTERLAYER_WORK" --result pass --link <the judge report's deep link>
90
+
91
+ The result is the judge's own last line. Record `--result pass` only when it
92
+ reads `result: pass`: every criterion the judge judged is satisfied. Record
93
+ `--result fail` when it reads `result: fail`, because a criterion is not
94
+ satisfied. A fail is recorded as fail, never withheld. With several judges,
95
+ record fail when any one of them failed. A criterion the judge marked
96
+ `not-judged` neither passes nor fails this check: its declared artifact
97
+ proves it on its own row. Drafted criteria are judged and recorded here like
98
+ the issue's own.
99
+
100
+ When the state file holds no criteria at all (`criteria.source` is `none`,
101
+ for example because drafting found nothing to draft), no judge runs, no
102
+ judge report is emitted, and nothing is recorded on this check. The row
103
+ then reads "not proven" once the build's claim ends. That is deliberate: no
104
+ criterion was judged, so the row must not read as passed, and a fail would
105
+ claim the judge found an unmet criterion. Name the unjudged check in the
106
+ final report. When the link has not appeared after a sync and a second look,
107
+ stop and name it as a blocker in the final report. Never record the result
108
+ with another link.
81
109
 
82
110
  ## Watch CI
83
111
 
@@ -26,7 +26,7 @@ whatever its size.
26
26
  | Floor, or larger | full | generalist + up to three | when criteria exist |
27
27
 
28
28
  An item with no criteria recorded is `small` whatever its diff, and no judge
29
- runs. That happens only when the build could not draft a single criterion, since
29
+ runs, so the `acceptance-criteria` check gets no result. That happens only when the build could not draft a single criterion, since
30
30
  an issue with no criteria gets a drafted list. A drafted list
31
31
  (`criteria.source` is `drafted`) is tiered and judged like criteria the issue
32
32
  wrote.
@@ -1,17 +1,18 @@
1
1
  ---
2
2
  name: spec
3
3
  description: >
4
- Turn an idea into an approved issue: search for duplicates, draft acceptance
5
- criteria with their proof forms, split work that is too big for one story,
6
- and file it after you approve it. Then offer to start the build. Use when you
7
- want to write up a feature or change before building it. Invoke with
4
+ Turns an idea into an approved issue: searches for duplicates, drafts
5
+ acceptance criteria with their proof forms, decides the docs and structural
6
+ verdicts, splits work that is too big for one story, and files it after you
7
+ approve it. Then offers to start the build. Use when you want to write up a
8
+ feature or change before building it. Invoke with
8
9
  /spec <what you want built>.
9
10
  ---
10
11
 
11
12
  Take one idea from a sentence to an issue a person can build from. This file
12
13
  is the procedure. The content discipline is the `writing-specs` skill: summary
13
- block, problem before solution, non-goals and Given/When/Then criteria. Follow
14
- it, and do not restate it here.
14
+ block, problem before solution, non-goals, Given/When/Then criteria and the
15
+ two verdicts. Follow it, and do not restate it here.
15
16
 
16
17
  This skill files the issue. It does not branch, write code or run tests.
17
18
  `/build` does that, on a work item that names the issue.
@@ -41,31 +42,41 @@ Run them in order. Do not skip the approval.
41
42
  only where nothing visible changes. A criterion nobody can prove is a
42
43
  criterion nobody can build to, so give every one either a proof form or a
43
44
  named test.
44
- 3. **Size.** More than twenty-four criteria is not one story. Above that, show
45
+ 3. **Verdicts.** Decide the docs verdict and the structural verdict as
46
+ `writing-specs` defines them. Give each a one-line reason. A docs-required
47
+ verdict adds a criterion naming the page to change or add and its proof
48
+ form. Record both verdicts in the issue body, so whoever builds it can
49
+ compare them with the finished change.
50
+ 4. **Size.** More than twenty-four criteria is not one story. Above that, show
45
51
  the work already split into slices that each stand alone. Slice one carries
46
52
  the criteria the others depend on, and the rest become their own specs. The
47
53
  user may redraw the split. Skipping it is not on offer.
48
- 4. **Reader pass.** Send the title and body through one cold rewrite agent. Its
54
+ 5. **Reader pass.** Send the title and body through one cold rewrite agent. Its
49
55
  only inputs are the draft and one writing rule: write for a reader who was
50
56
  not in this conversation, lead with the outcome, keep sentences under about
51
- twenty words, and use plain words. It never sees this conversation. Criteria keep
52
- their exact wording. Check that every path, identifier, number and link
53
- survived, and restore anything the rewrite dropped.
54
- 5. **Checkpoint.** Show the drafted title and body and the criteria with their
55
- proof forms, and wait for explicit approval. Before showing them, check every
56
- criterion: it has an id, a Given, When and Then, and a proof form or a named
57
- test. A criterion that fails the check goes back to step 2. Apply requested
57
+ twenty words, and use plain words. When the instructions file carries the
58
+ writing rules from the `writing-specs` skill's
59
+ `references/agents-snippet.md`, pass that section verbatim instead. The
60
+ agent never sees this conversation. Criteria keep their exact wording.
61
+ Check that every path, identifier, number and link survived, and restore
62
+ anything the rewrite dropped.
63
+ 6. **Checkpoint.** Show the drafted title and body, the criteria with their
64
+ proof forms, and both verdicts with their reasons. Wait for explicit
65
+ approval. Before showing them, check every criterion: it has an id, a
66
+ Given, When and Then, and a proof form or a named test. Check that both
67
+ verdicts are there, each with its reason. A criterion that fails the check
68
+ goes back to step 2, and a missing verdict to step 3. Apply requested
58
69
  edits, run the reader pass again over the edited text, and show it again
59
- until approved. Nothing is filed before this point. A spec the user rejects
60
- leaves no issue behind.
61
- 6. **File it.** Through the team's tracker tool, file the approved title, body
62
- and full criteria. Then file the slices from step 3, and only the ones the
70
+ until approved. Nothing is filed before this point. A spec the user
71
+ rejects leaves no issue behind.
72
+ 7. **File it.** Through the team's tracker tool, file the approved title, body
73
+ and full criteria. Then file the slices from step 4, and only the ones the
63
74
  user named. The issue is the source of truth from here on. Note its
64
75
  reference exactly as the tracker gives it: a number, or a Linear or Jira key.
65
- 7. **Ask whether to build.** Ask the user exactly one question: build it now, or
76
+ 8. **Ask whether to build.** Ask the user exactly one question: build it now, or
66
77
  not yet. Never start work unasked, because the user may want to hold the
67
78
  issue.
68
- 8. **Start the build.** On a yes, run `outerlayer work build --issue <reference>`,
79
+ 9. **Start the build.** On a yes, run `outerlayer work build --issue <reference>`,
69
80
  where the reference is the issue number or the Linear or Jira key exactly as
70
81
  the user typed it. Print the item number the command reports and the launch
71
82
  line:
@@ -80,5 +91,5 @@ Run them in order. Do not skip the approval.
80
91
 
81
92
  ## Scope limits
82
93
 
83
- One `/spec` files one issue, plus the slices the user approves in step 3. It
94
+ One `/spec` files one issue, plus the slices the user approves in step 4. It
84
95
  files nothing in another repository. It never creates a branch or edits code.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: writing-specs
3
- description: How to write a feature spec: problem framing, goals and non-goals, assumptions, and acceptance criteria in Given/When/Then, before implementation begins. Use when asked to "write a spec", "spec this out", "write a PRD", "draft acceptance criteria", or when shaping the issue for a new feature.
3
+ description: Describes how to write a feature spec before implementation begins - problem framing, goals and non-goals, assumptions, acceptance criteria in Given/When/Then, and the docs and structural verdicts. Use when asked to "write a spec", "spec this out", "write a PRD", "draft acceptance criteria", or when shaping the issue for a new feature.
4
4
  ---
5
5
 
6
6
  # Writing Specs
@@ -10,8 +10,9 @@ in the issue body: one clean spec, edited in place as the shape is agreed,
10
10
  never a trail of amendment comments. Its acceptance criteria sit under an
11
11
  `## Acceptance criteria` heading. Each criterion has an id, and an id is any
12
12
  stable label the team chooses, such as `LOGIN-01` or `AC-12-03`. Pick a scheme
13
- once and keep to it. Planning scratch is local and disposable. Nothing durable
14
- belongs there.
13
+ once and keep to it. The body also records the docs and structural verdicts,
14
+ each with a one-line reason. Planning scratch is local and disposable. Nothing
15
+ durable belongs there.
15
16
 
16
17
  ## Lead with a summary block
17
18
 
@@ -104,6 +105,30 @@ implementing. Never write them by reading the finished implementation back.
104
105
  changes. The proof shows the running page, never a component render. The
105
106
  emitting-evidence skill covers capture.
106
107
 
108
+ ## The two verdicts
109
+
110
+ Every feature spec declares two verdicts, each with a one-line reason. They
111
+ are decided when the spec is written, and decided again from the finished
112
+ change before the pull request opens.
113
+
114
+ - **Docs.** `docs-required` when the change alters what a user sees or does.
115
+ Judge it against the repository's own docs: a `README.md`, a `docs/`
116
+ directory, a docs site. A new capability with no page yet is
117
+ `docs-required` too, and its criterion names the page to add. That no page
118
+ covers it is a reason to write one, not a reason to skip. Generated
119
+ reference, such as an API spec rendered as pages, is not a docs surface. A
120
+ new endpoint still needs a guide when using it takes a step the reference
121
+ cannot show, such as a credential to create or calls to make in order. When
122
+ required, the spec carries a criterion naming the page and its proof form.
123
+ - **Structural.** `structural-required` when the change adds a package or an
124
+ app, introduces a seam between components, or moves data across a
125
+ boundary. When in doubt, say required: approval is where it gets argued
126
+ down. A structural change is worth planning before it is implemented.
127
+
128
+ A verdict reversed when the change is finished is not a fault. The gap
129
+ between the two calls is the useful signal, so state it rather than quietly
130
+ adopting the later one.
131
+
107
132
  ## Honest limits
108
133
 
109
134
  A criterion may carry prose admitting what it does not cover. Useful idioms:
@@ -132,3 +157,11 @@ the one most often skipped. Re-reading your own spec does not satisfy it. Run it
132
157
  against the issue body as published, not the draft in the working tree. A spec
133
158
  whose design lives in links the reader cannot follow fails this test, and that
134
159
  failure is invisible to whoever wrote it.
160
+
161
+ ## Writing rules for the instructions file
162
+
163
+ The reader test holds a spec to the rules in
164
+ [references/agents-snippet.md](references/agents-snippet.md): write for a
165
+ reader who was not in the session, and keep session history out of code
166
+ comments. Paste that file into the repository's instructions file, such as
167
+ `AGENTS.md`, to hold every agent's prose to the same rules.
@@ -0,0 +1,57 @@
1
+ <!-- outerlayer:writing-rules -->
2
+ ## Write for a reader who was not in the session
3
+
4
+ This applies to anything a person reads: replies, commit messages, pull
5
+ request titles and bodies, issue bodies, review comments, docs and code
6
+ comments.
7
+
8
+ The reader test: someone who knows this codebase but has never seen this
9
+ session, the skill that ran it, or any label coined while working. If they
10
+ would have to ask what a word means, explain it or cut it. Names made up
11
+ during a session never appear in anything published: reviewer names, round
12
+ labels, finding ids like C1 or N3, stage names, internal agent roles.
13
+ Describe the thing instead.
14
+
15
+ Lead with the outcome. One sentence: what changed, or what you found. Then
16
+ only the detail that changes what the reader does next. Keep exact paths,
17
+ numbers, identifiers and caveats. Cut the packaging around them.
18
+
19
+ A sentence carries one idea and stays under about 20 words. A sentence with
20
+ three clauses becomes three sentences. Break paragraphs often.
21
+
22
+ Say what you mean. Use the literal phrase, not a metaphor or a flourish:
23
+ "a parameter worth varying", not "a dial worth turning". Use everyday words:
24
+ "use", not "leverage"; "start", not "initiate"; "so", not "thereby".
25
+
26
+ Session telemetry, review-round tables, agent counts and raw command output
27
+ belong in the report to the person who asked. They never go in a pull
28
+ request or issue body.
29
+
30
+ Before posting, read the text as that outsider. If they would stall on a
31
+ term or a sentence, rewrite it.
32
+
33
+ ## Code comments
34
+
35
+ A comment states a present-tense fact the code cannot express: a why, a
36
+ constraint, a gotcha or an invariant. Everything else is noise that leaks
37
+ the history of how the code was written.
38
+
39
+ Never put these in a comment, a test title or a user-facing string:
40
+
41
+ - Change narration: "previously…", "now uses…", "removed X", "renamed from
42
+ Y". Version control holds what changed. The comment says why the current
43
+ code is the way it is.
44
+ - Issue, pull request or ticket numbers. They mean nothing to a reader
45
+ without access. If the context matters, restate its substance.
46
+ - Project codenames, phase labels and spec section references. Describe
47
+ the behavior instead.
48
+ - Labels from a working session: reviewer names, round labels, finding ids.
49
+
50
+ One exception: an acceptance-criterion id may sit in a comment directly
51
+ above the test that proves it, so a reader can find the test from the
52
+ criterion. Never put it in the test's title.
53
+
54
+ Keep comments that document constraints, invariants, footguns, security
55
+ posture and real rationale, and do not weaken them when editing nearby. If
56
+ a comment narrates a change but carries a real reason, rewrite it as
57
+ present-tense rationale rather than deleting it.
@@ -0,0 +1,117 @@
1
+ ---
2
+ name: writing-tests
3
+ description: Writes and reviews tests that catch a specific bug - assertion strength, the seam each test binds at, binding a test to an acceptance-criterion id, and checking by mutation that a test would notice a break. Use when writing or changing any test, when binding a test to a criterion id, when reviewing whether a test proves anything, or when a mutation tool reports survivors.
4
+ ---
5
+
6
+ # Writing tests
7
+
8
+ A test earns its place by catching a specific bug. Before keeping one, ask:
9
+ what is the smallest change to production code that would still pass this
10
+ test? If the answer is "almost anything", the test checks wiring, not
11
+ behavior. Strengthen it or delete it.
12
+
13
+ The rules here hold in any language and any test framework. The examples
14
+ use TypeScript with a Jest-style `expect`. Read each one as the equivalent
15
+ assertion in the repository's own framework.
16
+
17
+ ## Assertion strength
18
+
19
+ These pass for almost any implementation, so they are never the only proof
20
+ in a test:
21
+
22
+ - An "is defined" or "is not null" check passes for any value at all.
23
+ - A truthy or falsy check. Assert the exact value instead.
24
+ - "Was called" proves it ran, not what it ran with.
25
+ - "Does not throw" on its own. Assert the return value too.
26
+
27
+ Negative forms state a real expectation and are fine: "was never called",
28
+ "is absent". Prefer:
29
+
30
+ - Exact equality on the actual return value or the whole object shape.
31
+ - "Called with" and the full argument shape.
32
+ - Positional equality on lists. It catches reordering and off-by-one.
33
+ - A negative assertion that pins an injection path shut, such as
34
+ `expect(query).not.toContain(rawUserValue)`.
35
+ - On an error path, the message or status a user or operator sees, not only
36
+ that something failed.
37
+
38
+ Do not pin brittle internals: exact log strings, incidental call counts, the
39
+ order of unrelated calls. Those break on refactors without catching bugs.
40
+
41
+ ## Choose the seam
42
+
43
+ Name the seam before writing the test: the boundary the test drives the code
44
+ through.
45
+
46
+ - Business behavior is proven below the user interface, in the service or
47
+ the pure function.
48
+ - A user interface contract is proven at the component level, through what
49
+ the user sees and does.
50
+ - Behavior that lives in the data store, such as an access rule, a trigger
51
+ or the real shape of a query, is proven against the real store.
52
+ - A criterion that spans two seams gets two tests, one at each.
53
+
54
+ Check two blind spots every time:
55
+
56
+ - **Fake-seam blindness.** A fake that returns every field whatever the query
57
+ asked for proves nothing about the query. Pin the contract: assert the
58
+ request the code sent, make the fake honor what was asked for, or test
59
+ against the real dependency.
60
+ - **Structural pinning.** "X is never called for Y" needs a spy that is
61
+ present and asserts zero calls. A missing fake skips silently, exactly like
62
+ the real skip.
63
+
64
+ A field that crosses a module or package boundary needs an assertion at every
65
+ hop: writer, relay, reader. Pinning only the ends leaves the middle free to
66
+ drop it.
67
+
68
+ ## Bind a test to its criterion
69
+
70
+ When a spec has acceptance criteria with ids, the test that proves a
71
+ criterion carries the id in a comment directly above the test, never in its
72
+ title:
73
+
74
+ ```ts
75
+ // proves LOGIN-03
76
+ it("refuses a password reset for an unknown address without saying so", () => {
77
+ // ...
78
+ });
79
+ ```
80
+
81
+ The title says what the test proves to someone reading a failure. The
82
+ comment lets a reader, or a script, find the test from the criterion. Never
83
+ put a criterion id in a comment that is not that binding, because a reader
84
+ will take it as one.
85
+
86
+ Write the test from the criterion's words, at the seam it names, before the
87
+ implementation exists. Run it and read the failure. It must fail because the
88
+ behavior is absent, not because of a typo or a missing import.
89
+
90
+ A criterion with a proof form such as `(proof: screenshot)` is proven by the
91
+ evidence a reviewer sees, not by a test. It may still have one.
92
+
93
+ ## Mutation: prove the test would notice
94
+
95
+ A green suite proves the tests run, not that they would catch a bug. Check
96
+ the code the change adds or alters.
97
+
98
+ **By hand.** For every condition, guard, limit and assignment the change
99
+ adds, break it: invert it, widen it, drop half of an `and`, ignore the field.
100
+ Run the one test file that should catch it. If nothing fails, write the
101
+ assertion that would. Then take each criterion and break the production code
102
+ it describes. Its test must fail. A test bound to a criterion that survives
103
+ the break is not coverage.
104
+
105
+ **With a tool.** Mutation testing is optional. Where the repository has a
106
+ mutation tool, run it over the changed lines only. Each mutant that survives
107
+ is a test still to write, not a score to accept. Two mutants are equivalent
108
+ only when no observable output can differ; say why when you claim it. Hand
109
+ breaks still matter for files the tool does not mutate, such as SQL, shell
110
+ scripts, markup and configuration.
111
+
112
+ ## The repository's own rules
113
+
114
+ The instructions file may name test commands, required checks, mocking rules
115
+ or coverage thresholds. Those come first where they are stricter than this
116
+ skill. Add them here when they are the team's standard, because this file is
117
+ the team's to edit.