devcontour-harness 0.1.0-alpha.5 → 0.1.0-alpha.7

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 (263) hide show
  1. package/README.md +42 -2
  2. package/docs/compatibility-versions.md +56 -0
  3. package/docs/compatibility.md +2 -2
  4. package/docs/connectors.md +323 -0
  5. package/docs/contracts.md +33 -5
  6. package/docs/integrations/hindsight.md +148 -0
  7. package/docs/quickstart.md +1 -1
  8. package/docs/release-notes/0.1.0-alpha.6.md +238 -0
  9. package/docs/release-notes/0.1.0-alpha.7.md +71 -0
  10. package/docs/review-probes.md +199 -0
  11. package/docs/review-protocol.md +251 -0
  12. package/docs/toolchains.md +167 -0
  13. package/docs/turbo-starter.md +269 -0
  14. package/docs/upgrade.md +77 -24
  15. package/examples/README.md +1 -0
  16. package/examples/toolchains/README.md +32 -0
  17. package/examples/toolchains/go/bad/label/label.go +9 -0
  18. package/examples/toolchains/go/good/label/label.go +9 -0
  19. package/examples/toolchains/go/policy.json +79 -0
  20. package/examples/toolchains/go/repo/REQUIREMENTS.md +9 -0
  21. package/examples/toolchains/go/repo/_gitignore +1 -0
  22. package/examples/toolchains/go/repo/go.mod +5 -0
  23. package/examples/toolchains/go/repo/go.sum +2 -0
  24. package/examples/toolchains/go/repo/label/label.go +9 -0
  25. package/examples/toolchains/go/repo/label/label_test.go +27 -0
  26. package/examples/toolchains/go/repo/label/version.go +9 -0
  27. package/examples/toolchains/go/task.json +25 -0
  28. package/examples/toolchains/mixed-go/bad/backend/label/label.go +9 -0
  29. package/examples/toolchains/mixed-go/bad/frontend/src/label.js +4 -0
  30. package/examples/toolchains/mixed-go/good/backend/label/label.go +9 -0
  31. package/examples/toolchains/mixed-go/good/frontend/src/label.js +4 -0
  32. package/examples/toolchains/mixed-go/policy.json +126 -0
  33. package/examples/toolchains/mixed-go/repo/REQUIREMENTS.md +9 -0
  34. package/examples/toolchains/mixed-go/repo/_gitignore +6 -0
  35. package/examples/toolchains/mixed-go/repo/backend/go.mod +3 -0
  36. package/examples/toolchains/mixed-go/repo/backend/label/label.go +9 -0
  37. package/examples/toolchains/mixed-go/repo/backend/label/label_test.go +30 -0
  38. package/examples/toolchains/mixed-go/repo/contract/check.mjs +17 -0
  39. package/examples/toolchains/mixed-go/repo/contract/label.json +9 -0
  40. package/examples/toolchains/mixed-go/repo/frontend/package.json +15 -0
  41. package/examples/toolchains/mixed-go/repo/frontend/pnpm-lock.yaml +22 -0
  42. package/examples/toolchains/mixed-go/repo/frontend/src/label.js +4 -0
  43. package/examples/toolchains/mixed-go/repo/frontend/test/label.test.js +11 -0
  44. package/examples/toolchains/mixed-go/task.json +25 -0
  45. package/examples/toolchains/mixed-python/bad/backend/src/fx_backend/__init__.py +6 -0
  46. package/examples/toolchains/mixed-python/bad/frontend/src/label.js +4 -0
  47. package/examples/toolchains/mixed-python/good/backend/src/fx_backend/__init__.py +6 -0
  48. package/examples/toolchains/mixed-python/good/frontend/src/label.js +4 -0
  49. package/examples/toolchains/mixed-python/policy.json +121 -0
  50. package/examples/toolchains/mixed-python/repo/REQUIREMENTS.md +9 -0
  51. package/examples/toolchains/mixed-python/repo/_gitignore +6 -0
  52. package/examples/toolchains/mixed-python/repo/backend/.python-version +1 -0
  53. package/examples/toolchains/mixed-python/repo/backend/pyproject.toml +19 -0
  54. package/examples/toolchains/mixed-python/repo/backend/src/fx_backend/__init__.py +6 -0
  55. package/examples/toolchains/mixed-python/repo/backend/tests/test_label.py +11 -0
  56. package/examples/toolchains/mixed-python/repo/backend/uv.lock +108 -0
  57. package/examples/toolchains/mixed-python/repo/contract/check.mjs +17 -0
  58. package/examples/toolchains/mixed-python/repo/contract/label.json +9 -0
  59. package/examples/toolchains/mixed-python/repo/frontend/package.json +15 -0
  60. package/examples/toolchains/mixed-python/repo/frontend/pnpm-lock.yaml +22 -0
  61. package/examples/toolchains/mixed-python/repo/frontend/src/label.js +4 -0
  62. package/examples/toolchains/mixed-python/repo/frontend/test/label.test.js +11 -0
  63. package/examples/toolchains/mixed-python/task.json +25 -0
  64. package/examples/toolchains/npm/bad/src/label.js +4 -0
  65. package/examples/toolchains/npm/good/src/label.js +4 -0
  66. package/examples/toolchains/npm/policy.json +82 -0
  67. package/examples/toolchains/npm/repo/REQUIREMENTS.md +9 -0
  68. package/examples/toolchains/npm/repo/_gitignore +4 -0
  69. package/examples/toolchains/npm/repo/package-lock.json +22 -0
  70. package/examples/toolchains/npm/repo/package.json +13 -0
  71. package/examples/toolchains/npm/repo/src/label.js +4 -0
  72. package/examples/toolchains/npm/repo/test/label.test.js +13 -0
  73. package/examples/toolchains/npm/task.json +25 -0
  74. package/examples/toolchains/pnpm/bad/packages/label/src/label.js +4 -0
  75. package/examples/toolchains/pnpm/good/packages/label/src/label.js +4 -0
  76. package/examples/toolchains/pnpm/policy.json +85 -0
  77. package/examples/toolchains/pnpm/repo/REQUIREMENTS.md +9 -0
  78. package/examples/toolchains/pnpm/repo/_gitignore +4 -0
  79. package/examples/toolchains/pnpm/repo/package.json +10 -0
  80. package/examples/toolchains/pnpm/repo/packages/checker/check.mjs +5 -0
  81. package/examples/toolchains/pnpm/repo/packages/checker/package.json +9 -0
  82. package/examples/toolchains/pnpm/repo/packages/label/package.json +14 -0
  83. package/examples/toolchains/pnpm/repo/packages/label/src/label.js +4 -0
  84. package/examples/toolchains/pnpm/repo/packages/label/test/label.test.js +13 -0
  85. package/examples/toolchains/pnpm/repo/pnpm-lock.yaml +29 -0
  86. package/examples/toolchains/pnpm/repo/pnpm-workspace.yaml +2 -0
  87. package/examples/toolchains/pnpm/task.json +25 -0
  88. package/examples/toolchains/python/bad/src/fx_label/__init__.py +6 -0
  89. package/examples/toolchains/python/good/src/fx_label/__init__.py +6 -0
  90. package/examples/toolchains/python/policy.json +80 -0
  91. package/examples/toolchains/python/repo/.python-version +1 -0
  92. package/examples/toolchains/python/repo/REQUIREMENTS.md +9 -0
  93. package/examples/toolchains/python/repo/_gitignore +5 -0
  94. package/examples/toolchains/python/repo/pyproject.toml +19 -0
  95. package/examples/toolchains/python/repo/src/fx_label/__init__.py +6 -0
  96. package/examples/toolchains/python/repo/tests/test_label.py +13 -0
  97. package/examples/toolchains/python/repo/uv.lock +108 -0
  98. package/examples/toolchains/python/task.json +25 -0
  99. package/examples/toolchains/turbo/bad/packages/label/src/label.js +4 -0
  100. package/examples/toolchains/turbo/good/packages/label/src/label.js +4 -0
  101. package/examples/toolchains/turbo/policy.json +110 -0
  102. package/examples/toolchains/turbo/repo/REQUIREMENTS.md +9 -0
  103. package/examples/toolchains/turbo/repo/_gitignore +4 -0
  104. package/examples/toolchains/turbo/repo/package.json +13 -0
  105. package/examples/toolchains/turbo/repo/packages/checker/check.mjs +5 -0
  106. package/examples/toolchains/turbo/repo/packages/checker/package.json +9 -0
  107. package/examples/toolchains/turbo/repo/packages/label/package.json +14 -0
  108. package/examples/toolchains/turbo/repo/packages/label/src/label.js +4 -0
  109. package/examples/toolchains/turbo/repo/packages/label/test/label.test.js +13 -0
  110. package/examples/toolchains/turbo/repo/pnpm-lock.yaml +94 -0
  111. package/examples/toolchains/turbo/repo/pnpm-workspace.yaml +2 -0
  112. package/examples/toolchains/turbo/repo/turbo.json +7 -0
  113. package/examples/toolchains/turbo/task.json +25 -0
  114. package/examples/turbo-starter/README.md +16 -0
  115. package/examples/turbo-starter/changes/issued-at-consumer-only.patch +12 -0
  116. package/examples/turbo-starter/changes/issued-at.patch +130 -0
  117. package/examples/turbo-starter/cleanup.mjs +58 -0
  118. package/examples/turbo-starter/graph.json +32 -0
  119. package/examples/turbo-starter/harness/acceptance/db-isolation.test.mjs +97 -0
  120. package/examples/turbo-starter/harness/acceptance/env-probe.test.mjs +75 -0
  121. package/examples/turbo-starter/harness/acceptance/generation.test.mjs +54 -0
  122. package/examples/turbo-starter/harness/acceptance/issued-at.test.mjs +43 -0
  123. package/examples/turbo-starter/harness/acceptance/lifecycle-probe.test.mjs +47 -0
  124. package/examples/turbo-starter/harness/compose-bridge.mjs +593 -0
  125. package/examples/turbo-starter/harness/compose.acceptance.yml +23 -0
  126. package/examples/turbo-starter/harness/env/backend.env.template +7 -0
  127. package/examples/turbo-starter/harness/env/compose.env.template +6 -0
  128. package/examples/turbo-starter/policy.json +109 -0
  129. package/examples/turbo-starter/policy.lifecycle.json +114 -0
  130. package/examples/turbo-starter/setup.mjs +352 -0
  131. package/lib/agents/cli-adapter.js +10 -1
  132. package/lib/agents/inspect.d.ts +56 -2
  133. package/lib/agents/inspect.js +264 -14
  134. package/lib/agents/review-probe.d.ts +229 -7
  135. package/lib/agents/review-probe.js +830 -72
  136. package/lib/agents/schemas.d.ts +3 -1
  137. package/lib/agents/schemas.js +7 -3
  138. package/lib/cli.js +167 -7
  139. package/lib/contracts/cli.d.ts +820 -11
  140. package/lib/contracts/cli.js +243 -4
  141. package/lib/contracts/errors.d.ts +3 -1
  142. package/lib/contracts/errors.js +2 -0
  143. package/lib/contracts/events.d.ts +37 -5
  144. package/lib/contracts/events.js +9 -0
  145. package/lib/contracts/execution.d.ts +389 -15
  146. package/lib/contracts/execution.js +76 -4
  147. package/lib/contracts/failure.js +5 -0
  148. package/lib/contracts/index.d.ts +1 -0
  149. package/lib/contracts/index.js +1 -0
  150. package/lib/contracts/policy.d.ts +15 -0
  151. package/lib/contracts/policy.js +10 -0
  152. package/lib/contracts/ports.d.ts +50 -0
  153. package/lib/contracts/profiles.d.ts +14 -0
  154. package/lib/contracts/profiles.js +4 -0
  155. package/lib/contracts/published.d.ts +279 -8
  156. package/lib/contracts/result.d.ts +171 -0
  157. package/lib/contracts/result.js +39 -1
  158. package/lib/contracts/review.d.ts +231 -0
  159. package/lib/contracts/review.js +326 -2
  160. package/lib/contracts/toolchain.d.ts +96 -0
  161. package/lib/contracts/toolchain.js +89 -0
  162. package/lib/control/doctor.d.ts +16 -1
  163. package/lib/control/doctor.js +45 -3
  164. package/lib/control/harness.d.ts +52 -6
  165. package/lib/control/harness.js +28 -1
  166. package/lib/control/recovery.d.ts +3 -1
  167. package/lib/control/review.js +23 -11
  168. package/lib/engine/budget.d.ts +3 -3
  169. package/lib/engine/classify.js +1 -0
  170. package/lib/engine/engine.d.ts +67 -17
  171. package/lib/engine/prompt.d.ts +29 -1
  172. package/lib/engine/prompt.js +57 -0
  173. package/lib/engine/property-recovery.js +2 -0
  174. package/lib/engine/result.js +52 -0
  175. package/lib/engine/reuse.js +6 -1
  176. package/lib/engine/runner.d.ts +25 -0
  177. package/lib/engine/runner.js +500 -123
  178. package/lib/environment/lifecycle.js +6 -0
  179. package/lib/environment/live-calls.js +3 -2
  180. package/lib/environment/os-tools.d.ts +16 -0
  181. package/lib/environment/os-tools.js +35 -0
  182. package/lib/environment/process.d.ts +8 -0
  183. package/lib/environment/process.js +13 -6
  184. package/lib/integrations/connector/commands.d.ts +358 -0
  185. package/lib/integrations/connector/commands.js +428 -0
  186. package/lib/integrations/connector/config.d.ts +215 -0
  187. package/lib/integrations/connector/config.js +214 -0
  188. package/lib/integrations/connector/delivery.d.ts +107 -0
  189. package/lib/integrations/connector/delivery.js +591 -0
  190. package/lib/integrations/connector/fake.d.ts +291 -0
  191. package/lib/integrations/connector/fake.js +786 -0
  192. package/lib/integrations/connector/history.d.ts +124 -0
  193. package/lib/integrations/connector/history.js +497 -0
  194. package/lib/integrations/connector/index.d.ts +16 -0
  195. package/lib/integrations/connector/index.js +16 -0
  196. package/lib/integrations/connector/redact.d.ts +19 -0
  197. package/lib/integrations/connector/redact.js +91 -0
  198. package/lib/integrations/connector/registry.d.ts +15 -0
  199. package/lib/integrations/connector/registry.js +43 -0
  200. package/lib/integrations/connector/snapshot.d.ts +131 -0
  201. package/lib/integrations/connector/snapshot.js +422 -0
  202. package/lib/integrations/connector/source.d.ts +37 -0
  203. package/lib/integrations/connector/source.js +73 -0
  204. package/lib/integrations/connector/steps.d.ts +71 -0
  205. package/lib/integrations/connector/steps.js +188 -0
  206. package/lib/integrations/connector/store.d.ts +140 -0
  207. package/lib/integrations/connector/store.js +277 -0
  208. package/lib/integrations/connector/summary.d.ts +38 -0
  209. package/lib/integrations/connector/summary.js +59 -0
  210. package/lib/integrations/connector/testing-worker.d.ts +1 -0
  211. package/lib/integrations/connector/testing-worker.js +81 -0
  212. package/lib/integrations/connector/testing.d.ts +205 -0
  213. package/lib/integrations/connector/testing.js +1615 -0
  214. package/lib/integrations/connector/transport.d.ts +240 -0
  215. package/lib/integrations/connector/transport.js +28 -0
  216. package/lib/integrations/memory/context.d.ts +120 -0
  217. package/lib/integrations/memory/context.js +239 -0
  218. package/lib/integrations/memory/index.d.ts +8 -0
  219. package/lib/integrations/memory/index.js +8 -0
  220. package/lib/integrations/memory/provider.d.ts +79 -0
  221. package/lib/integrations/memory/provider.js +22 -0
  222. package/lib/integrations/memory/sink.d.ts +56 -0
  223. package/lib/integrations/memory/sink.js +116 -0
  224. package/lib/integrations/memory/testing.d.ts +44 -0
  225. package/lib/integrations/memory/testing.js +61 -0
  226. package/lib/integrations/relay-v1/index.d.ts +122 -0
  227. package/lib/integrations/relay-v1/index.js +860 -0
  228. package/lib/integrations/relay-v2/index.d.ts +145 -0
  229. package/lib/integrations/relay-v2/index.js +752 -0
  230. package/lib/integrations/relay-v2/process.d.ts +32 -0
  231. package/lib/integrations/relay-v2/process.js +112 -0
  232. package/lib/integrations/relay-v2/protocol.d.ts +172 -0
  233. package/lib/integrations/relay-v2/protocol.js +142 -0
  234. package/lib/profiles/resolve.d.ts +5 -1
  235. package/lib/profiles/resolve.js +31 -0
  236. package/lib/storage/document.d.ts +116 -16
  237. package/lib/toolchains/index.d.ts +9 -0
  238. package/lib/toolchains/index.js +9 -0
  239. package/lib/toolchains/inspect.d.ts +105 -0
  240. package/lib/toolchains/inspect.js +831 -0
  241. package/lib/toolchains/manifests.d.ts +67 -0
  242. package/lib/toolchains/manifests.js +238 -0
  243. package/lib/toolchains/probe.d.ts +28 -0
  244. package/lib/toolchains/probe.js +54 -0
  245. package/lib/toolchains/recipes.d.ts +33 -0
  246. package/lib/toolchains/recipes.js +51 -0
  247. package/lib/verification/acceptance.d.ts +16 -0
  248. package/lib/verification/acceptance.js +16 -0
  249. package/lib/verification/gates.d.ts +38 -0
  250. package/lib/verification/gates.js +108 -26
  251. package/package.json +22 -1
  252. package/profiles/go-gotestsum.json +21 -0
  253. package/profiles/npm-ci.json +22 -0
  254. package/profiles/pnpm-frozen.json +24 -0
  255. package/profiles/python-uv.json +26 -0
  256. package/schemas/execution-document.schema.json +345 -4
  257. package/schemas/execution-policy.schema.json +48 -0
  258. package/schemas/result-envelope.schema.json +213 -1
  259. package/schemas/run-event.schema.json +93 -0
  260. package/schemas/run-result.schema.json +207 -0
  261. package/schemas/stack-profile.schema.json +43 -0
  262. package/templates/controller-mode/AGENTS.controller.md +58 -0
  263. package/templates/controller-mode/README.md +44 -0
@@ -0,0 +1,85 @@
1
+ {
2
+ "policyVersion": 1,
3
+ "repositories": [
4
+ {
5
+ "id": "app",
6
+ "path": "repo",
7
+ "targetRef": "refs/heads/harness/accepted",
8
+ "prepare": [
9
+ {
10
+ "id": "install",
11
+ "command": ["pnpm", "install", "--frozen-lockfile"],
12
+ "timeoutMs": 600000
13
+ }
14
+ ],
15
+ "gates": [
16
+ {
17
+ "id": "check",
18
+ "kind": "check",
19
+ "command": ["pnpm", "run", "check"],
20
+ "timeoutMs": 240000
21
+ },
22
+ {
23
+ "id": "unit-tests",
24
+ "kind": "test",
25
+ "command": ["pnpm", "run", "test"],
26
+ "timeoutMs": 240000,
27
+ "dependsOn": ["check"],
28
+ "report": {
29
+ "type": "junit",
30
+ "path": ".reports/junit.xml"
31
+ }
32
+ }
33
+ ],
34
+ "toolchain": {
35
+ "tools": {
36
+ "pnpm": "10.12.3"
37
+ },
38
+ "packageManagers": {
39
+ ".": "pnpm"
40
+ }
41
+ },
42
+ "protectedPaths": [
43
+ ".github/",
44
+ ".githooks/",
45
+ "package.json",
46
+ "pnpm-lock.yaml",
47
+ "pnpm-workspace.yaml",
48
+ "packages/label/test/",
49
+ "packages/label/package.json",
50
+ "packages/checker/",
51
+ "REQUIREMENTS.md"
52
+ ]
53
+ }
54
+ ],
55
+ "roles": {
56
+ "developer": {
57
+ "writer": {
58
+ "runtime": "claude"
59
+ }
60
+ }
61
+ },
62
+ "review": {
63
+ "code": {
64
+ "reviewer": {
65
+ "runtime": "codex"
66
+ }
67
+ },
68
+ "plan": {
69
+ "reviewer": {
70
+ "runtime": "codex"
71
+ }
72
+ },
73
+ "contract": {
74
+ "reviewer": {
75
+ "runtime": "codex"
76
+ }
77
+ }
78
+ },
79
+ "isolation": {
80
+ "mode": "none",
81
+ "domains": []
82
+ },
83
+ "maxAttempts": 2,
84
+ "runTimeoutMs": 900000
85
+ }
@@ -0,0 +1,9 @@
1
+ # Requirements
2
+
3
+ ## REQ-LABEL-PAD
4
+
5
+ label(n) pads the number to three digits: label(7) is "item-007", label(42) is "item-042".
6
+
7
+ ## REQ-LABEL-LONG
8
+
9
+ label(n) keeps numbers longer than three digits unchanged: label(1234) is "item-1234".
@@ -0,0 +1,4 @@
1
+ node_modules/
2
+ .reports/
3
+ .turbo/
4
+ .venv/
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "fx-label-pnpm",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "packageManager": "pnpm@10.12.3",
6
+ "scripts": {
7
+ "check": "pnpm --filter @fx/label run check",
8
+ "test": "pnpm --filter @fx/label run test"
9
+ }
10
+ }
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ // Локальный бинарник workspace: синтаксическая проверка перечисленных файлов.
3
+ import { execFileSync } from 'node:child_process';
4
+ for (const file of process.argv.slice(2)) execFileSync(process.execPath, ['--check', file]);
5
+ console.log('fx-check: ok ' + process.argv.slice(2).join(' '));
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "@fx/checker",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "bin": {
7
+ "fx-check": "./check.mjs"
8
+ }
9
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "@fx/label",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "check": "fx-check src/label.js test/label.test.js",
8
+ "test": "node --test --test-reporter=spec --test-reporter-destination=stdout --test-reporter=junit --test-reporter-destination=../../.reports/junit.xml test/*.test.js"
9
+ },
10
+ "devDependencies": {
11
+ "@fx/checker": "workspace:*",
12
+ "picocolors": "1.1.1"
13
+ }
14
+ }
@@ -0,0 +1,4 @@
1
+ // Метка позиции заказа.
2
+ export function label(n) {
3
+ return `item-${n}`;
4
+ }
@@ -0,0 +1,13 @@
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { label } from '../src/label.js';
4
+
5
+ test('label pads one digit to three', () => {
6
+ assert.equal(label(7), 'item-007');
7
+ });
8
+ test('label pads two digits to three', () => {
9
+ assert.equal(label(42), 'item-042');
10
+ });
11
+ test('label keeps long numbers', () => {
12
+ assert.equal(label(1234), 'item-1234');
13
+ });
@@ -0,0 +1,29 @@
1
+ lockfileVersion: '9.0'
2
+
3
+ settings:
4
+ autoInstallPeers: true
5
+ excludeLinksFromLockfile: false
6
+
7
+ importers:
8
+
9
+ .: {}
10
+
11
+ packages/checker: {}
12
+
13
+ packages/label:
14
+ devDependencies:
15
+ '@fx/checker':
16
+ specifier: workspace:*
17
+ version: link:../checker
18
+ picocolors:
19
+ specifier: 1.1.1
20
+ version: 1.1.1
21
+
22
+ packages:
23
+
24
+ picocolors@1.1.1:
25
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
26
+
27
+ snapshots:
28
+
29
+ picocolors@1.1.1: {}
@@ -0,0 +1,2 @@
1
+ packages:
2
+ - 'packages/*'
@@ -0,0 +1,25 @@
1
+ {
2
+ "title": "Pad item labels to three digits",
3
+ "goal": "Change label(n) in packages/label/src/label.js so the number is zero-padded to three digits (item-007), keeping longer numbers unchanged.",
4
+ "acceptance": [
5
+ "label(7) is item-007 and label(42) is item-042",
6
+ "label(1234) is item-1234",
7
+ "All gates pass with fresh JUnit reports"
8
+ ],
9
+ "writeScope": ["packages/label/src"],
10
+ "gates": ["check", "unit-tests"],
11
+ "requirements": [
12
+ {
13
+ "id": "REQ-LABEL-PAD",
14
+ "gate": "unit-tests",
15
+ "testId": "test.label pads one digit to three",
16
+ "scenario": "test.label pads one digit to three runs and passes on the accepted SHA"
17
+ },
18
+ {
19
+ "id": "REQ-LABEL-LONG",
20
+ "gate": "unit-tests",
21
+ "testId": "test.label keeps long numbers",
22
+ "scenario": "test.label keeps long numbers runs and passes on the accepted SHA"
23
+ }
24
+ ]
25
+ }
@@ -0,0 +1,6 @@
1
+ """Метки позиций заказа."""
2
+
3
+
4
+ def label(n: int) -> str:
5
+ """Неверная реализация для отрицательного прогона: два знака."""
6
+ return f"item-{n:02d}"
@@ -0,0 +1,6 @@
1
+ """Метки позиций заказа."""
2
+
3
+
4
+ def label(n: int) -> str:
5
+ """Метка позиции: номер дополняется нулями до трёх знаков."""
6
+ return f"item-{n:03d}"
@@ -0,0 +1,80 @@
1
+ {
2
+ "policyVersion": 1,
3
+ "repositories": [
4
+ {
5
+ "id": "app",
6
+ "path": "repo",
7
+ "targetRef": "refs/heads/harness/accepted",
8
+ "prepare": [
9
+ {
10
+ "id": "install",
11
+ "command": ["uv", "sync", "--locked"],
12
+ "timeoutMs": 600000
13
+ }
14
+ ],
15
+ "gates": [
16
+ {
17
+ "id": "lint",
18
+ "kind": "check",
19
+ "command": ["uv", "run", "--no-sync", "ruff", "check", "."],
20
+ "timeoutMs": 240000
21
+ },
22
+ {
23
+ "id": "acceptance",
24
+ "kind": "test",
25
+ "command": ["uv", "run", "--no-sync", "pytest", "--junitxml=.reports/junit.xml"],
26
+ "timeoutMs": 240000,
27
+ "dependsOn": ["lint"],
28
+ "report": {
29
+ "type": "junit",
30
+ "path": ".reports/junit.xml"
31
+ }
32
+ }
33
+ ],
34
+ "toolchain": {
35
+ "tools": {
36
+ "uv": "0.11.28"
37
+ }
38
+ },
39
+ "protectedPaths": [
40
+ ".github/",
41
+ ".githooks/",
42
+ "pyproject.toml",
43
+ "uv.lock",
44
+ ".python-version",
45
+ "tests/",
46
+ "REQUIREMENTS.md"
47
+ ]
48
+ }
49
+ ],
50
+ "roles": {
51
+ "developer": {
52
+ "writer": {
53
+ "runtime": "claude"
54
+ }
55
+ }
56
+ },
57
+ "review": {
58
+ "code": {
59
+ "reviewer": {
60
+ "runtime": "codex"
61
+ }
62
+ },
63
+ "plan": {
64
+ "reviewer": {
65
+ "runtime": "codex"
66
+ }
67
+ },
68
+ "contract": {
69
+ "reviewer": {
70
+ "runtime": "codex"
71
+ }
72
+ }
73
+ },
74
+ "isolation": {
75
+ "mode": "none",
76
+ "domains": []
77
+ },
78
+ "maxAttempts": 2,
79
+ "runTimeoutMs": 900000
80
+ }
@@ -0,0 +1,9 @@
1
+ # Requirements
2
+
3
+ ## REQ-LABEL-PAD
4
+
5
+ label(n) pads the number to three digits: label(7) is "item-007", label(42) is "item-042".
6
+
7
+ ## REQ-LABEL-LONG
8
+
9
+ label(n) keeps numbers longer than three digits unchanged: label(1234) is "item-1234".
@@ -0,0 +1,5 @@
1
+ .reports/
2
+ .venv/
3
+ __pycache__/
4
+ .pytest_cache/
5
+ .ruff_cache/
@@ -0,0 +1,19 @@
1
+ [project]
2
+ name = "fx-label"
3
+ version = "1.0.0"
4
+ requires-python = ">=3.12,<3.13"
5
+ dependencies = []
6
+
7
+ [dependency-groups]
8
+ dev = ["pytest==8.4.1", "ruff==0.12.4"]
9
+
10
+ [tool.uv]
11
+ package = false
12
+
13
+ [tool.pytest.ini_options]
14
+ pythonpath = ["src"]
15
+ testpaths = ["tests"]
16
+ junit_family = "xunit2"
17
+
18
+ [tool.ruff]
19
+ line-length = 100
@@ -0,0 +1,6 @@
1
+ """Метки позиций заказа."""
2
+
3
+
4
+ def label(n: int) -> str:
5
+ """Метка позиции."""
6
+ return f"item-{n}"
@@ -0,0 +1,13 @@
1
+ from fx_label import label
2
+
3
+
4
+ def test_label_pads_one_digit():
5
+ assert label(7) == "item-007"
6
+
7
+
8
+ def test_label_pads_two_digits():
9
+ assert label(42) == "item-042"
10
+
11
+
12
+ def test_label_keeps_long():
13
+ assert label(1234) == "item-1234"
@@ -0,0 +1,108 @@
1
+ version = 1
2
+ revision = 3
3
+ requires-python = "==3.12.*"
4
+
5
+ [[package]]
6
+ name = "colorama"
7
+ version = "0.4.6"
8
+ source = { registry = "https://pypi.org/simple" }
9
+ sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
10
+ wheels = [
11
+ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
12
+ ]
13
+
14
+ [[package]]
15
+ name = "fx-label"
16
+ version = "1.0.0"
17
+ source = { virtual = "." }
18
+
19
+ [package.dev-dependencies]
20
+ dev = [
21
+ { name = "pytest" },
22
+ { name = "ruff" },
23
+ ]
24
+
25
+ [package.metadata]
26
+
27
+ [package.metadata.requires-dev]
28
+ dev = [
29
+ { name = "pytest", specifier = "==8.4.1" },
30
+ { name = "ruff", specifier = "==0.12.4" },
31
+ ]
32
+
33
+ [[package]]
34
+ name = "iniconfig"
35
+ version = "2.3.1"
36
+ source = { registry = "https://pypi.org/simple" }
37
+ sdist = { url = "https://files.pythonhosted.org/packages/01/e1/2069291243c926a2ff1cd706c7f3eeb9b62144bf60f77c9fb9ff2fb26bd3/iniconfig-2.3.1.tar.gz", hash = "sha256:67f4b9c50da0dedf52af349e7749a80a9057a5031199791b906c3bb3ae878960", size = 21209, upload-time = "2026-10-06T22:48:38.076Z" }
38
+ wheels = [
39
+ { url = "https://files.pythonhosted.org/packages/56/43/4ca9e49d27a1fcf6bece6f6aec0ea46bb9112489b93d4b688fb415457bdb/iniconfig-2.3.1-py3-none-any.whl", hash = "sha256:9121e2c1fdb355232495be3194c8dfe87ccc2d5dee45947b78e68f499790d7a7", size = 7552, upload-time = "2026-10-06T22:48:36.959Z" },
40
+ ]
41
+
42
+ [[package]]
43
+ name = "packaging"
44
+ version = "26.3"
45
+ source = { registry = "https://pypi.org/simple" }
46
+ sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" }
47
+ wheels = [
48
+ { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" },
49
+ ]
50
+
51
+ [[package]]
52
+ name = "pluggy"
53
+ version = "1.7.0"
54
+ source = { registry = "https://pypi.org/simple" }
55
+ sdist = { url = "https://files.pythonhosted.org/packages/bf/db/7fc19e6f2dc92a966727031389fc2e08b558f0f25eb7403c1119ad4713cd/pluggy-1.7.0.tar.gz", hash = "sha256:d1eaa46ebb595891b860ab086b4d09c8588af65ebd4361b8e8f4bb8920b90ba8", size = 123304, upload-time = "2026-10-15T09:50:58.343Z" }
56
+ wheels = [
57
+ { url = "https://files.pythonhosted.org/packages/40/9e/2b38731e0fc536806f16490e1a12d7f0dc2a1235aa8cc07bcc75416a7daa/pluggy-1.7.0-py3-none-any.whl", hash = "sha256:7dd7b0d8832ba3cb632c306926ded123429211b83641b35dc5c41ad2d34f9bec", size = 27082, upload-time = "2026-10-15T09:50:56.808Z" },
58
+ ]
59
+
60
+ [[package]]
61
+ name = "pygments"
62
+ version = "2.21.0"
63
+ source = { registry = "https://pypi.org/simple" }
64
+ sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" }
65
+ wheels = [
66
+ { url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" },
67
+ ]
68
+
69
+ [[package]]
70
+ name = "pytest"
71
+ version = "8.4.1"
72
+ source = { registry = "https://pypi.org/simple" }
73
+ dependencies = [
74
+ { name = "colorama", marker = "sys_platform == 'win32'" },
75
+ { name = "iniconfig" },
76
+ { name = "packaging" },
77
+ { name = "pluggy" },
78
+ { name = "pygments" },
79
+ ]
80
+ sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" }
81
+ wheels = [
82
+ { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" },
83
+ ]
84
+
85
+ [[package]]
86
+ name = "ruff"
87
+ version = "0.12.4"
88
+ source = { registry = "https://pypi.org/simple" }
89
+ sdist = { url = "https://files.pythonhosted.org/packages/9b/ce/8d7dbedede481245b489b769d27e2934730791a9a82765cb94566c6e6abd/ruff-0.12.4.tar.gz", hash = "sha256:13efa16df6c6eeb7d0f091abae50f58e9522f3843edb40d56ad52a5a4a4b6873", size = 5131435, upload-time = "2025-07-17T17:27:19.138Z" }
90
+ wheels = [
91
+ { url = "https://files.pythonhosted.org/packages/ae/9f/517bc5f61bad205b7f36684ffa5415c013862dee02f55f38a217bdbe7aa4/ruff-0.12.4-py3-none-linux_armv6l.whl", hash = "sha256:cb0d261dac457ab939aeb247e804125a5d521b21adf27e721895b0d3f83a0d0a", size = 10188824, upload-time = "2025-07-17T17:26:31.412Z" },
92
+ { url = "https://files.pythonhosted.org/packages/28/83/691baae5a11fbbde91df01c565c650fd17b0eabed259e8b7563de17c6529/ruff-0.12.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:55c0f4ca9769408d9b9bac530c30d3e66490bd2beb2d3dae3e4128a1f05c7442", size = 10884521, upload-time = "2025-07-17T17:26:35.084Z" },
93
+ { url = "https://files.pythonhosted.org/packages/d6/8d/756d780ff4076e6dd035d058fa220345f8c458391f7edfb1c10731eedc75/ruff-0.12.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a8224cc3722c9ad9044da7f89c4c1ec452aef2cfe3904365025dd2f51daeae0e", size = 10277653, upload-time = "2025-07-17T17:26:37.897Z" },
94
+ { url = "https://files.pythonhosted.org/packages/8d/97/8eeee0f48ece153206dce730fc9e0e0ca54fd7f261bb3d99c0a4343a1892/ruff-0.12.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9949d01d64fa3672449a51ddb5d7548b33e130240ad418884ee6efa7a229586", size = 10485993, upload-time = "2025-07-17T17:26:40.68Z" },
95
+ { url = "https://files.pythonhosted.org/packages/49/b8/22a43d23a1f68df9b88f952616c8508ea6ce4ed4f15353b8168c48b2d7e7/ruff-0.12.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:be0593c69df9ad1465e8a2d10e3defd111fdb62dcd5be23ae2c06da77e8fcffb", size = 10022824, upload-time = "2025-07-17T17:26:43.564Z" },
96
+ { url = "https://files.pythonhosted.org/packages/cd/70/37c234c220366993e8cffcbd6cadbf332bfc848cbd6f45b02bade17e0149/ruff-0.12.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7dea966bcb55d4ecc4cc3270bccb6f87a337326c9dcd3c07d5b97000dbff41c", size = 11524414, upload-time = "2025-07-17T17:26:46.219Z" },
97
+ { url = "https://files.pythonhosted.org/packages/14/77/c30f9964f481b5e0e29dd6a1fae1f769ac3fd468eb76fdd5661936edd262/ruff-0.12.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:afcfa3ab5ab5dd0e1c39bf286d829e042a15e966b3726eea79528e2e24d8371a", size = 12419216, upload-time = "2025-07-17T17:26:48.883Z" },
98
+ { url = "https://files.pythonhosted.org/packages/6e/79/af7fe0a4202dce4ef62c5e33fecbed07f0178f5b4dd9c0d2fcff5ab4a47c/ruff-0.12.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c057ce464b1413c926cdb203a0f858cd52f3e73dcb3270a3318d1630f6395bb3", size = 11976756, upload-time = "2025-07-17T17:26:51.754Z" },
99
+ { url = "https://files.pythonhosted.org/packages/09/d1/33fb1fc00e20a939c305dbe2f80df7c28ba9193f7a85470b982815a2dc6a/ruff-0.12.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e64b90d1122dc2713330350626b10d60818930819623abbb56535c6466cce045", size = 11020019, upload-time = "2025-07-17T17:26:54.265Z" },
100
+ { url = "https://files.pythonhosted.org/packages/64/f4/e3cd7f7bda646526f09693e2e02bd83d85fff8a8222c52cf9681c0d30843/ruff-0.12.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2abc48f3d9667fdc74022380b5c745873499ff827393a636f7a59da1515e7c57", size = 11277890, upload-time = "2025-07-17T17:26:56.914Z" },
101
+ { url = "https://files.pythonhosted.org/packages/5e/d0/69a85fb8b94501ff1a4f95b7591505e8983f38823da6941eb5b6badb1e3a/ruff-0.12.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2b2449dc0c138d877d629bea151bee8c0ae3b8e9c43f5fcaafcd0c0d0726b184", size = 10348539, upload-time = "2025-07-17T17:26:59.381Z" },
102
+ { url = "https://files.pythonhosted.org/packages/16/a0/91372d1cb1678f7d42d4893b88c252b01ff1dffcad09ae0c51aa2542275f/ruff-0.12.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:56e45bb11f625db55f9b70477062e6a1a04d53628eda7784dce6e0f55fd549eb", size = 10009579, upload-time = "2025-07-17T17:27:02.462Z" },
103
+ { url = "https://files.pythonhosted.org/packages/23/1b/c4a833e3114d2cc0f677e58f1df6c3b20f62328dbfa710b87a1636a5e8eb/ruff-0.12.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:478fccdb82ca148a98a9ff43658944f7ab5ec41c3c49d77cd99d44da019371a1", size = 10942982, upload-time = "2025-07-17T17:27:05.343Z" },
104
+ { url = "https://files.pythonhosted.org/packages/ff/ce/ce85e445cf0a5dd8842f2f0c6f0018eedb164a92bdf3eda51984ffd4d989/ruff-0.12.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0fc426bec2e4e5f4c4f182b9d2ce6a75c85ba9bcdbe5c6f2a74fcb8df437df4b", size = 11343331, upload-time = "2025-07-17T17:27:08.652Z" },
105
+ { url = "https://files.pythonhosted.org/packages/35/cf/441b7fc58368455233cfb5b77206c849b6dfb48b23de532adcc2e50ccc06/ruff-0.12.4-py3-none-win32.whl", hash = "sha256:4de27977827893cdfb1211d42d84bc180fceb7b72471104671c59be37041cf93", size = 10267904, upload-time = "2025-07-17T17:27:11.814Z" },
106
+ { url = "https://files.pythonhosted.org/packages/ce/7e/20af4a0df5e1299e7368d5ea4350412226afb03d95507faae94c80f00afd/ruff-0.12.4-py3-none-win_amd64.whl", hash = "sha256:fe0b9e9eb23736b453143d72d2ceca5db323963330d5b7859d60d101147d461a", size = 11209038, upload-time = "2025-07-17T17:27:14.417Z" },
107
+ { url = "https://files.pythonhosted.org/packages/11/02/8857d0dfb8f44ef299a5dfd898f673edefb71e3b533b3b9d2db4c832dd13/ruff-0.12.4-py3-none-win_arm64.whl", hash = "sha256:0618ec4442a83ab545e5b71202a5c0ed7791e8471435b94e655b570a5031a98e", size = 10469336, upload-time = "2025-07-17T17:27:16.913Z" },
108
+ ]
@@ -0,0 +1,25 @@
1
+ {
2
+ "title": "Pad item labels to three digits",
3
+ "goal": "Change label(n) in src/fx_label/__init__.py so the number is zero-padded to three digits (item-007), keeping longer numbers unchanged.",
4
+ "acceptance": [
5
+ "label(7) is item-007 and label(42) is item-042",
6
+ "label(1234) is item-1234",
7
+ "All gates pass with fresh JUnit reports"
8
+ ],
9
+ "writeScope": ["src"],
10
+ "gates": ["lint", "acceptance"],
11
+ "requirements": [
12
+ {
13
+ "id": "REQ-LABEL-PAD",
14
+ "gate": "acceptance",
15
+ "testId": "tests.test_label.test_label_pads_one_digit",
16
+ "scenario": "tests.test_label.test_label_pads_one_digit runs and passes on the accepted SHA"
17
+ },
18
+ {
19
+ "id": "REQ-LABEL-LONG",
20
+ "gate": "acceptance",
21
+ "testId": "tests.test_label.test_label_keeps_long",
22
+ "scenario": "tests.test_label.test_label_keeps_long runs and passes on the accepted SHA"
23
+ }
24
+ ]
25
+ }
@@ -0,0 +1,4 @@
1
+ // Неверная реализация для отрицательного прогона: два знака вместо трёх.
2
+ export function label(n) {
3
+ return `item-${String(n).padStart(2, '0')}`;
4
+ }
@@ -0,0 +1,4 @@
1
+ // Метка позиции заказа: номер дополняется нулями до трёх знаков.
2
+ export function label(n) {
3
+ return `item-${String(n).padStart(3, '0')}`;
4
+ }
@@ -0,0 +1,110 @@
1
+ {
2
+ "policyVersion": 1,
3
+ "repositories": [
4
+ {
5
+ "id": "app",
6
+ "path": "repo",
7
+ "targetRef": "refs/heads/harness/accepted",
8
+ "prepare": [
9
+ {
10
+ "id": "install",
11
+ "command": ["pnpm", "install", "--frozen-lockfile"],
12
+ "timeoutMs": 600000
13
+ }
14
+ ],
15
+ "gates": [
16
+ {
17
+ "id": "check",
18
+ "kind": "check",
19
+ "command": [
20
+ "pnpm",
21
+ "exec",
22
+ "turbo",
23
+ "run",
24
+ "check",
25
+ "--filter=@fx/label",
26
+ "--output-logs=full"
27
+ ],
28
+ "timeoutMs": 240000
29
+ },
30
+ {
31
+ "id": "unit-tests",
32
+ "kind": "test",
33
+ "command": [
34
+ "pnpm",
35
+ "exec",
36
+ "turbo",
37
+ "run",
38
+ "test",
39
+ "--filter=@fx/label",
40
+ "--force",
41
+ "--output-logs=full"
42
+ ],
43
+ "timeoutMs": 240000,
44
+ "dependsOn": ["check"],
45
+ "report": {
46
+ "type": "junit",
47
+ "path": "packages/label/.reports/junit.xml"
48
+ }
49
+ }
50
+ ],
51
+ "environment": {
52
+ "values": {
53
+ "TURBO_TELEMETRY_DISABLED": "1",
54
+ "TURBO_NO_UPDATE_NOTIFIER": "1",
55
+ "DO_NOT_TRACK": "1"
56
+ }
57
+ },
58
+ "toolchain": {
59
+ "tools": {
60
+ "pnpm": "10.12.3"
61
+ },
62
+ "packageManagers": {
63
+ ".": "pnpm"
64
+ }
65
+ },
66
+ "protectedPaths": [
67
+ ".github/",
68
+ ".githooks/",
69
+ "package.json",
70
+ "pnpm-lock.yaml",
71
+ "pnpm-workspace.yaml",
72
+ "turbo.json",
73
+ "packages/label/test/",
74
+ "packages/label/package.json",
75
+ "packages/checker/",
76
+ "REQUIREMENTS.md"
77
+ ]
78
+ }
79
+ ],
80
+ "roles": {
81
+ "developer": {
82
+ "writer": {
83
+ "runtime": "claude"
84
+ }
85
+ }
86
+ },
87
+ "review": {
88
+ "code": {
89
+ "reviewer": {
90
+ "runtime": "codex"
91
+ }
92
+ },
93
+ "plan": {
94
+ "reviewer": {
95
+ "runtime": "codex"
96
+ }
97
+ },
98
+ "contract": {
99
+ "reviewer": {
100
+ "runtime": "codex"
101
+ }
102
+ }
103
+ },
104
+ "isolation": {
105
+ "mode": "none",
106
+ "domains": []
107
+ },
108
+ "maxAttempts": 2,
109
+ "runTimeoutMs": 900000
110
+ }
@@ -0,0 +1,9 @@
1
+ # Requirements
2
+
3
+ ## REQ-LABEL-PAD
4
+
5
+ label(n) pads the number to three digits: label(7) is "item-007", label(42) is "item-042".
6
+
7
+ ## REQ-LABEL-LONG
8
+
9
+ label(n) keeps numbers longer than three digits unchanged: label(1234) is "item-1234".