opencode-codeops 2.4.0 → 2.6.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 (48) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +49 -2
  3. package/_shared/discovery.md +2 -1
  4. package/_shared/spec-first-ordering.md +6 -2
  5. package/_shared/test-selection.md +130 -0
  6. package/bin/codeops-test.mjs +855 -0
  7. package/bin/lib/test-branch-policy.d.mts +51 -0
  8. package/bin/lib/test-branch-policy.mjs +185 -0
  9. package/bin/lib/test-map-validate.d.mts +42 -0
  10. package/bin/lib/test-map-validate.mjs +618 -0
  11. package/bin/lib/test-map.d.mts +114 -0
  12. package/bin/lib/test-map.mjs +384 -0
  13. package/bin/lib/test-provenance.d.mts +73 -0
  14. package/bin/lib/test-provenance.mjs +188 -0
  15. package/bin/lib/test-runner.d.mts +72 -0
  16. package/bin/lib/test-runner.mjs +350 -0
  17. package/bin/lib/test-selection-changes.d.mts +76 -0
  18. package/bin/lib/test-selection-changes.mjs +272 -0
  19. package/bin/lib/test-selection-explain.mjs +75 -0
  20. package/bin/lib/test-selection-glob.d.mts +32 -0
  21. package/bin/lib/test-selection-glob.mjs +206 -0
  22. package/bin/lib/test-selection.d.mts +80 -0
  23. package/bin/lib/test-selection.mjs +418 -0
  24. package/package.json +4 -2
  25. package/schemas/test-map.schema.json +164 -0
  26. package/scripts/__pycache__/codeops_plan.cpython-314.pyc +0 -0
  27. package/scripts/__pycache__/install_agents.cpython-314.pyc +0 -0
  28. package/scripts/discovery-toggle.mjs +236 -0
  29. package/skills/analyze-project/SKILL.md +9 -2
  30. package/skills/exec-plan/SKILL.md +16 -8
  31. package/skills/exec-plan/execution-protocol.md +15 -2
  32. package/skills/git-commit/SKILL.md +1 -1
  33. package/skills/make-plan/SKILL.md +10 -3
  34. package/skills/make-plan/quality-checklist.md +4 -0
  35. package/skills/make-plan/templates.md +19 -1
  36. package/skills/organize-tests/SKILL.md +169 -0
  37. package/skills/organize-tests/references/agents-testing-block.md +80 -0
  38. package/skills/organize-tests/references/analysis-protocol.md +94 -0
  39. package/skills/organize-tests/references/ci-github-actions.md +178 -0
  40. package/skills/organize-tests/references/language-adapters.md +54 -0
  41. package/skills/organize-tests/references/map-health.md +47 -0
  42. package/skills/organize-tests/references/project-wrapper.md +72 -0
  43. package/skills/organize-tests/references/shadow-mode.md +60 -0
  44. package/skills/setup-codeops/SKILL.md +30 -3
  45. package/standards/coding-standards-full.md +28 -0
  46. package/standards/coding-standards.md +29 -0
  47. package/scripts/__pycache__/codeops_plan.cpython-312.pyc +0 -0
  48. package/scripts/__pycache__/install_agents.cpython-312.pyc +0 -0
@@ -0,0 +1,236 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Enable or disable Discovery in an existing CodeOps project.
4
+ *
5
+ * The toggle is deliberately small and deterministic: it edits only
6
+ * `codeops/codeops.json` and, when the managed block exists, the Discovery
7
+ * line in `AGENTS.md`. It refuses symlinks and malformed configuration, and
8
+ * preserves every unrelated key.
9
+ *
10
+ * @module discovery-toggle
11
+ */
12
+
13
+ import { existsSync, lstatSync, readFileSync, writeFileSync } from "node:fs"
14
+ import { join, resolve } from "node:path"
15
+ import { fileURLToPath } from "node:url"
16
+
17
+ /** The managed AGENTS.md line for an enabled Discovery page. */
18
+ export const DISCOVERY_LINE =
19
+ "- Discovery: enabled — question rounds open in the local Discovery page"
20
+
21
+ const START_MARKER = "<!-- CODEOPS-PROJECT:START -->"
22
+ const END_MARKER = "<!-- CODEOPS-PROJECT:END -->"
23
+
24
+ /** A user-facing toggle failure. */
25
+ export class DiscoveryToggleError extends Error {}
26
+
27
+ /** True for a non-null, non-array object. */
28
+ function isPlainObject(value) {
29
+ return value !== null && typeof value === "object" && !Array.isArray(value)
30
+ }
31
+
32
+ /** Refuse a symlink or a missing regular file. */
33
+ function assertRegularFile(path, label) {
34
+ if (!existsSync(path)) {
35
+ throw new DiscoveryToggleError(`${label} does not exist.`)
36
+ }
37
+ const stats = lstatSync(path)
38
+ if (stats.isSymbolicLink()) {
39
+ throw new DiscoveryToggleError(`${label} is a symlink; refusing to write through it.`)
40
+ }
41
+ if (!stats.isFile()) {
42
+ throw new DiscoveryToggleError(`${label} is not a regular file.`)
43
+ }
44
+ }
45
+
46
+ /** Refuse a symlinked codeops directory. */
47
+ function assertDirectory(path, label) {
48
+ if (!existsSync(path)) {
49
+ throw new DiscoveryToggleError(`${label} does not exist; run setup-codeops first.`)
50
+ }
51
+ const stats = lstatSync(path)
52
+ if (stats.isSymbolicLink()) {
53
+ throw new DiscoveryToggleError(`${label} is a symlink; refusing to write through it.`)
54
+ }
55
+ if (!stats.isDirectory()) {
56
+ throw new DiscoveryToggleError(`${label} is not a directory.`)
57
+ }
58
+ }
59
+
60
+ /** Normalize a config's discovery object without discarding known keys. */
61
+ function nextDiscovery(existing, enable) {
62
+ if (existing !== undefined && !isPlainObject(existing)) {
63
+ throw new DiscoveryToggleError("codeops/codeops.json has a malformed discovery object.")
64
+ }
65
+ const discovery = { ...(existing ?? {}) }
66
+ discovery.enabled = enable
67
+ if (enable && discovery.chatMaxQuestions === undefined) {
68
+ discovery.chatMaxQuestions = 3
69
+ }
70
+ if (
71
+ discovery.chatMaxQuestions !== undefined &&
72
+ (!Number.isInteger(discovery.chatMaxQuestions) || discovery.chatMaxQuestions < 0)
73
+ ) {
74
+ throw new DiscoveryToggleError("discovery.chatMaxQuestions must be a non-negative integer.")
75
+ }
76
+ return discovery
77
+ }
78
+
79
+ /** Add or remove the managed Discovery line inside an AGENTS.md managed block. */
80
+ function updateAgents(text, enable) {
81
+ const eol = text.includes("\r\n") ? "\r\n" : "\n"
82
+ const lines = text.split(/\r?\n/)
83
+ const starts = lines
84
+ .map((line, index) => (line.trim() === START_MARKER ? index : -1))
85
+ .filter((index) => index >= 0)
86
+ const ends = lines
87
+ .map((line, index) => (line.trim() === END_MARKER ? index : -1))
88
+ .filter((index) => index >= 0)
89
+ if (starts.length !== 1 || ends.length !== 1 || starts[0] > ends[0]) {
90
+ return { status: "block-missing", changed: false, text }
91
+ }
92
+ const start = starts[0]
93
+ const end = ends[0]
94
+ const block = lines.slice(start, end + 1)
95
+ const existing = block.findIndex((line) => line.trim() === DISCOVERY_LINE)
96
+ if (enable) {
97
+ if (existing >= 0) return { status: "already-enabled", changed: false, text }
98
+ const anchor = block.findIndex((line) => line.trim().startsWith("- Specialist coverage:"))
99
+ if (anchor === -1) {
100
+ return { status: "anchor-missing", changed: false, text }
101
+ }
102
+ block.splice(anchor + 1, 0, DISCOVERY_LINE)
103
+ } else {
104
+ if (existing === -1) return { status: "already-disabled", changed: false, text }
105
+ block.splice(existing, 1)
106
+ }
107
+ lines.splice(start, end - start + 1, ...block)
108
+ return { status: enable ? "enabled" : "disabled", changed: true, text: lines.join(eol) }
109
+ }
110
+
111
+ /**
112
+ * Toggle Discovery for one project.
113
+ *
114
+ * @param {{ root?: string, enable: boolean, dryRun?: boolean }} options
115
+ * @returns {{ configPath: string, discovery: object, changed: boolean,
116
+ * agents: string, dryRun: boolean }}
117
+ */
118
+ export function toggleDiscovery(options) {
119
+ if (typeof options.enable !== "boolean") {
120
+ throw new DiscoveryToggleError("enable must be true or false.")
121
+ }
122
+ const root = resolve(options.root ?? process.cwd())
123
+ const dryRun = options.dryRun === true
124
+ const codeopsDir = join(root, "codeops")
125
+ assertDirectory(codeopsDir, "codeops/")
126
+ const configPath = join(codeopsDir, "codeops.json")
127
+ assertRegularFile(configPath, "codeops/codeops.json")
128
+
129
+ const original = readFileSync(configPath, "utf8")
130
+ let config
131
+ try {
132
+ config = JSON.parse(original)
133
+ } catch {
134
+ throw new DiscoveryToggleError("codeops/codeops.json is not valid JSON.")
135
+ }
136
+ if (!isPlainObject(config)) {
137
+ throw new DiscoveryToggleError("codeops/codeops.json must contain a JSON object.")
138
+ }
139
+ const discovery = nextDiscovery(config.discovery, options.enable)
140
+ const nextConfig = { ...config, discovery }
141
+ const nextText = JSON.stringify(nextConfig, null, 2) + "\n"
142
+ const configChanged = nextText !== original
143
+
144
+ const agentsPath = join(root, "AGENTS.md")
145
+ let agents = "missing"
146
+ let agentsText = null
147
+ let agentsChanged = false
148
+ if (existsSync(agentsPath)) {
149
+ const stats = lstatSync(agentsPath)
150
+ if (stats.isSymbolicLink()) {
151
+ agents = "symlink"
152
+ } else if (!stats.isFile()) {
153
+ agents = "not-a-file"
154
+ } else {
155
+ const result = updateAgents(readFileSync(agentsPath, "utf8"), options.enable)
156
+ agents = result.status
157
+ agentsChanged = result.changed
158
+ agentsText = result.text
159
+ }
160
+ }
161
+
162
+ if (!dryRun) {
163
+ if (configChanged) writeFileSync(configPath, nextText)
164
+ if (agentsChanged && agentsText !== null) writeFileSync(agentsPath, agentsText)
165
+ }
166
+
167
+ return {
168
+ configPath,
169
+ discovery,
170
+ changed: configChanged,
171
+ agents,
172
+ dryRun,
173
+ }
174
+ }
175
+
176
+ /** Print command usage. */
177
+ function printUsage() {
178
+ console.log(`Toggle Discovery in an existing CodeOps project.
179
+
180
+ Usage:
181
+ node scripts/discovery-toggle.mjs --root <repo> --enable-discovery [--dry-run]
182
+ node scripts/discovery-toggle.mjs --root <repo> --disable-discovery [--dry-run]
183
+
184
+ Options:
185
+ --root <dir> Repository root (defaults to the current directory)
186
+ --enable-discovery Set discovery.enabled to true
187
+ --disable-discovery Set discovery.enabled to false
188
+ --dry-run Show the result without writing files
189
+ -h, --help Show this help
190
+ `)
191
+ }
192
+
193
+ /** Run the command line. */
194
+ function main() {
195
+ const args = process.argv.slice(2)
196
+ if (args.includes("--help") || args.includes("-h")) {
197
+ printUsage()
198
+ return
199
+ }
200
+ const rootIndex = args.indexOf("--root")
201
+ const root = rootIndex >= 0 ? args[rootIndex + 1] : process.cwd()
202
+ if (root === undefined) {
203
+ throw new DiscoveryToggleError("--root requires a directory.")
204
+ }
205
+ const enable = args.includes("--enable-discovery")
206
+ const disable = args.includes("--disable-discovery")
207
+ if (enable === disable) {
208
+ throw new DiscoveryToggleError("Use exactly one of --enable-discovery or --disable-discovery.")
209
+ }
210
+ const result = toggleDiscovery({
211
+ root,
212
+ enable,
213
+ dryRun: args.includes("--dry-run"),
214
+ })
215
+ const action = enable ? "enabled" : "disabled"
216
+ console.log(
217
+ `${result.dryRun ? "[dry-run] " : ""}Discovery ${action} in ${result.configPath} ` +
218
+ `(chatMaxQuestions: ${result.discovery.chatMaxQuestions ?? "default 3"}).`,
219
+ )
220
+ console.log(`AGENTS.md: ${result.agents}.`)
221
+ if (result.dryRun && result.changed) {
222
+ console.log("No files were written.")
223
+ }
224
+ }
225
+
226
+ const invokedDirectly =
227
+ process.argv[1] !== undefined && resolve(process.argv[1]) === fileURLToPath(import.meta.url)
228
+
229
+ if (invokedDirectly) {
230
+ try {
231
+ main()
232
+ } catch (error) {
233
+ console.error(error instanceof Error ? error.message : String(error))
234
+ process.exitCode = 1
235
+ }
236
+ }
@@ -20,7 +20,14 @@ description: Analyze the current repository and create or refresh concise CodeOp
20
20
  ```
21
21
 
22
22
  5. Include project type, principal languages/frameworks, authoritative build/test/verify commands, high-level structure, generated-file warnings, and CodeOps artifact/config locations.
23
- 6. If the managed section exists, replace only its contents. Preserve all other text byte-for-byte.
23
+ 6. If the managed section exists, replace only its generated contents outside the `CODEOPS-TESTING` block. Preserve all other text byte-for-byte.
24
+ 6b. **Testing block.** Detect `codeops/test-map.json` or `codeops/test-map/` and the project's
25
+ `full` command. The `<!-- CODEOPS-TESTING:START -->` / `<!-- CODEOPS-TESTING:END -->` block
26
+ inside the managed section is owned by the `organize-tests` skill: preserve it byte-for-byte,
27
+ never render or overwrite it, and warn when it is missing or stale. With no map and no block,
28
+ report that no map is present and do not create a block; an existing no-map block is preserved
29
+ as-is. Warn (advisory only) when CI has no full gate on a release/acceptance branch. The block
30
+ contract is in [_shared/test-selection.md](../../_shared/test-selection.md).
24
31
  7. On a non-integration branch, preview changes to repository-wide guidance and ask before writing unless repository policy explicitly permits branch-local updates.
25
32
  8. In compact mode, remove duplication and stale generated detail from the managed section only. Flag suspected hand-authored bloat; never silently rewrite it.
26
33
  9. Validate every recorded command or mark it explicitly unverified.
@@ -28,7 +35,7 @@ description: Analyze the current repository and create or refresh concise CodeOp
28
35
 
29
36
  ## Discovery switch
30
37
 
31
- `analyze-project` accepts `--with-discovery` and `--without-discovery`; passing both flags is a usage error. Both flags validate `codeops/codeops.json` against `schemas/codeops-config.schema.json` before changing anything: an invalid file stops the run with its schema errors and writes nothing (never discard malformed quality, routing, or metrics policy). Refuse to write when `codeops/` or `codeops/codeops.json` is a symlink or not a regular file; never write through a link.
38
+ `analyze-project` accepts `--with-discovery` and `--without-discovery`; passing both flags is a usage error. Existing projects can also use `setup-codeops --enable-discovery` / `--disable-discovery` for the config-only toggle. Both flags validate `codeops/codeops.json` against `schemas/codeops-config.schema.json` before changing anything: an invalid file stops the run with its schema errors and writes nothing (never discard malformed quality, routing, or metrics policy). Refuse to write when `codeops/` or `codeops/codeops.json` is a symlink or not a regular file; never write through a link.
32
39
 
33
40
  - `--with-discovery`: set `discovery.enabled: true`, keeping every other key. When no file exists, create the minimal AR-35 file with the layout resolved from `_shared/layout-convention.md` (`nested` → `codeops`, `flat` → `.`):
34
41
  ```json
@@ -145,7 +145,9 @@ summary template. The essentials:
145
145
 
146
146
  ### Step 1 — Load the plan
147
147
 
148
- 1. Read `plans/$ARGUMENTS/99-execution-plan.md`.
148
+ 1. Read `plans/$ARGUMENTS/99-execution-plan.md`. An old plan with a single `**Verify**` line
149
+ uses that line as the gate command; with a map, derive the task command, otherwise it serves
150
+ both.
149
151
  2. Find incomplete tasks — both `[ ]` and implemented-but-unverified `[~]`; read supporting specs
150
152
  in `plans/$ARGUMENTS/`.
151
153
  3. Determine the starting point: a `[~]` task is resumed first (re-verify, then promote or keep
@@ -177,12 +179,17 @@ For each task, in order:
177
179
  points in [execution-protocol.md](execution-protocol.md) §Progress Reporting; a failed or
178
180
  missing report never blocks any step.
179
181
 
180
- 3. **Verify** — run your project's verify command (from the project's AGENTS.md, or detected
181
- project conventions), output captured per the protocol's **Verify-output capture rule**
182
- (PASS one-liner; on failure the last 50 log lines + log path). Pass → promote `[~]` → `[x]`;
183
- fail → fix and re-verify (mark stays `[~]`). Immediately after every `[x]` promotion, run the
184
- protocol's read-only progress-bar command for the selected plan and show its exact output in the
185
- next commentary update. Only `[x]` contributes to the displayed completion count.
182
+ 3. **Verify** — run the task command: the plan's `**Task verify**` line, or the map's `task`
183
+ gate with `--baseline <phase-tree>` and `--include <spec-file>` for the plan's declared spec
184
+ file(s). With neither, run the plan's `**Verify (gate)**` command, or the project's full verify
185
+ (from AGENTS.md or detected conventions), output captured per the protocol's
186
+ **Verify-output capture rule** (PASS one-liner; on failure
187
+ the last 50 log lines + log path). A changed path outside the phase's expected modification
188
+ set, or a shared/config/test-infrastructure change, forces the full gate. Pass → promote `[~]`
189
+ → `[x]`; fail → fix and re-verify (mark stays `[~]`). Immediately after every `[x]` promotion,
190
+ run the protocol's read-only progress-bar command for the selected plan and show its exact
191
+ output in the next commentary update. Only `[x]` contributes to the displayed completion
192
+ count.
186
193
  4. **Commit** per the active commit mode (see [commit-modes.md](commit-modes.md)) — the commit
187
194
  gate keys off `[x]`.
188
195
  5. **Techdocs check (after each phase):** if the phase introduced architectural changes and
@@ -215,7 +222,8 @@ For each task, in order:
215
222
 
216
223
  1. Finish the current task before stopping.
217
224
  2. **🚨 First, update `99-execution-plan.md`** with all completed tasks (before anything else).
218
- 3. Run the verify command.
225
+ 3. Run the task command when the plan is not complete and the session stops inside a phase;
226
+ run the full gate at plan completion or a phase boundary.
219
227
  4. Handle the commit per the active commit mode.
220
228
  5. Report a session summary (must state `Execution Plan Updated: ✅`). Template in
221
229
  [execution-protocol.md](execution-protocol.md).
@@ -50,6 +50,11 @@ Suggestion only — the user may proceed without upgrading.
50
50
 
51
51
  ### Phase start
52
52
 
53
+ Record the phase's expected modification set from its task target paths and deliverables. The set
54
+ drives the fallback rule: a task-time change outside it, or a shared/configuration/test-infrastructure
55
+ change, runs the full gate. For an old plan, the rule is unchanged: a single `**Verify**` line is the gate
56
+ command; with a map, derive the task command, otherwise use the same line for tasks.
57
+
53
58
  Before a phase's first task (and before a T-NN mini-plan's first task), snapshot the complete
54
59
  non-ignored worktree—including staged, unstaged, and untracked files—into a temporary Git tree,
55
60
  without changing the real index:
@@ -102,8 +107,16 @@ complete. For each task, in order:
102
107
  and update the Progress header. Do this before running verification or anything else — if the
103
108
  session crashes now, the implementation progress is preserved and the resume session knows the
104
109
  task still needs verification.
105
- 4. Run verification (your project's verify command — from the project's AGENTS.md, or detected
106
- project conventions), with output captured per the **Verify-output capture rule** below.
110
+ 4. Run verification, with output captured per the **Verify-output capture rule** below. Use the
111
+ task command for a normal task: the plan's validated `**Task verify**` line, or the map's
112
+ `task` gate with `--baseline <phase-baseline-tree>` and `--include <declared-spec-file>` for
113
+ every plan-declared specification test file. If neither exists, use the plan's
114
+ `**Verify (gate)**` command, or the project's full verify command (from AGENTS.md or detected
115
+ conventions). Run the full gate at phase exit, plan
116
+ completion, before push/release, and whenever a changed path falls outside the phase's
117
+ expected modification set or touches shared/configuration/test infrastructure. A single
118
+ `**Verify**` line is the gate command; a map may derive the task command from it, otherwise
119
+ that line serves both.
107
120
  - **PASS** → before promoting, run the **documentation-standard self-check** below on the files
108
121
  this task changed. Missing documentation and leaked plan references are invisible to
109
122
  build+test, so a green verify is NOT sufficient — the self-check is a hard part of the
@@ -14,7 +14,7 @@ A request to commit authorizes a local commit only. Push only when the user expl
14
14
  1. Resolve the repository root and inspect `git status --short`, staged/unstaged diffs, and recent commit style.
15
15
  2. If clean, report `nothing to commit` and stop.
16
16
  3. Inspect every untracked file. Stop and ask before staging likely secrets, credentials, build output, large binaries, or unrelated scratch files.
17
- 4. Resolve the authoritative verification command from `AGENTS.md`, CodeOps plan Verify lines, or project manifests. Run it with output captured to a log under `$CODEOPS_TMPDIR` (fallback: the OS temp directory), never in the repository. On failure, report the failure tail and do not stage or commit.
17
+ 4. Resolve the authoritative verification command from `AGENTS.md`, CodeOps plan Verify lines, or project manifests. For a normal commit, a validated scoped `**Task verify**` command or the map's `task` gate may replace the full command; record the command and scope in the verification note. Run the full gate for a release, merge, or push commit, and whenever the staged diff touches shared, configuration, manifest, or test infrastructure. Never bypass hooks. Run with output captured to a log under `$CODEOPS_TMPDIR` (fallback: the OS temp directory), never in the repository. On failure, report the failure tail and do not stage or commit. The gate rules are in [_shared/test-selection.md](../../_shared/test-selection.md).
18
18
  5. Stage deliberately by explicit paths or coherent groups. Avoid `git add .` when the working directory may be a monorepo subtree or unrelated changes exist.
19
19
  6. Review `git diff --cached --check`, the staged stat, and the staged diff. Ensure the commit contains one coherent purpose and preserves unrelated user changes.
20
20
  7. Write a Conventional Commit message to a temporary file under `$CODEOPS_TMPDIR` (fallback: the OS temp directory) and commit with `git commit -F`. Never use an inline multiline message.
@@ -233,9 +233,16 @@ decisions remain user-owned.
233
233
  3b. Every addition accepted through scope exploration must also carry its owning `SE-*`
234
234
  back-reference. `Defer`, `Discard`, and `Superseded` entries never produce plan tasks.
235
235
  4. `07-testing-strategy.md` must contain concrete **Specification Test Cases (ST-*)** with input→expected-output pairs, each traced to a requirement / spec doc / AR entry. Expectations come from the SPEC, never from imagined implementation behavior.
236
- 4b. **Confirm the verify command once.** The command that fills every Verify line comes from the
237
- project's AGENTS.md or manifests — state what you detected and have the user confirm it (an AR
238
- entry like any decision). If nothing is detectable, ask — **never invent a command**.
236
+ 4b. **Confirm the verify commands once.** The gate command that fills every `**Verify**` /
237
+ `**Verify (gate)**` line comes from the project's AGENTS.md or manifests — state what you
238
+ detected and have the user confirm it (an AR entry like any decision). If nothing is
239
+ detectable, ask — **never invent a command**. Optionally record a `**Task verify**` scoped
240
+ command for task/commit verification. Run the task command once during authoring, record the
241
+ observed test count as `**Task verify evidence**: <N> tests observed, <date>`, and reject a
242
+ command that runs zero tests. Record the plan's declared spec file(s) so exec-plan can
243
+ pass them with `--include`. With no map and no scoped command, generate exactly today's plan
244
+ (one `**Verify**` line). The canonical gate ladder is
245
+ [../../_shared/test-selection.md](../../_shared/test-selection.md).
239
246
  5. `99-execution-plan.md` must structure every feature phase with the mandatory three-session ordering (Spec Tests → Implementation → Impl Tests & Hardening), carrying each phase's tasks as a **single checkbox list** — the plan's single source of truth for progress; a task line appears exactly once in the document. The full ordering rules are in [templates.md](templates.md).
240
247
  6. If you discover a NEW ambiguity while writing, STOP immediately and add it to the register.
241
248
  In normal mode get the user's decision; with active auto-design resolve an eligible technical
@@ -44,6 +44,10 @@ Run this before finalizing the plan documents. The **Specification-First Testing
44
44
  - [ ] Spec test tasks reference ST-cases from `07-testing-strategy.md`
45
45
  - [ ] Spec test and impl test files use separate naming conventions (`*.spec.test.*` and `*.impl.test.*`)
46
46
  - [ ] A red-phase verification task exists in the execution plan (verify spec tests fail before implementation)
47
+ - [ ] `07-testing-strategy.md` carries the optional `Test slices` table, or records `N/A — full verify only`
48
+ - [ ] Every ST-case maps to a test slice or a validated scoped command
49
+ - [ ] Every slice has a task-tier test and a release/acceptance group or recorded acceptance reason
50
+ - [ ] The full gate command is recorded; a `**Task verify**` command, when present, proved a nonzero test count
47
51
 
48
52
  ## ✅ No Dead Code
49
53
  - [ ] No unused parameters (except interface contracts, overrides, and framework-required signatures)
@@ -312,6 +312,21 @@ With no RD upstream, `01-requirements.md` IS the owning requirements doc:
312
312
  - Adjust targets per project in `01-requirements.md` (an AR-referenced decision) — never
313
313
  silently.
314
314
 
315
+ ## Test Slices & Groups (optional)
316
+
317
+ > Fill this table when the project has a test map (`codeops/test-map.json` or
318
+ > `codeops/test-map/`) or when the plan records a validated `**Task verify**` scoped command.
319
+ > For a map-less project with no scoped command, write `N/A — full verify only` and keep the
320
+ > existing plan shape.
321
+
322
+ | Slice | Tier | Test Files | Sources | Depends On | Gate |
323
+ | ----- | ---- | ---------- | ------- | ---------- | ---- |
324
+ | [slice] | task | [paths] | [paths] | [slice/group] | task |
325
+ | [slice] | release | [paths] | [paths] | [task group] | release |
326
+
327
+ Every slice needs a task-tier test. Every slice needs a release or acceptance group, or a
328
+ recorded manual/external/N/A acceptance reason.
329
+
315
330
  ## 🚨 Specification Test Cases (MANDATORY — NON-NEGOTIABLE)
316
331
 
317
332
  > These test cases are derived EXCLUSIVELY from requirements (`01-requirements.md`),
@@ -478,7 +493,10 @@ task-size criteria in [quality-checklist.md](quality-checklist.md))
478
493
  - [ ] Deliverable 1
479
494
  - [ ] All verification passing
480
495
 
481
- **Verify**: [Project's verify command from AGENTS.md / detected conventions]
496
+ **Test slices**: [optional — slice/tier summary, or `N/A — full verify only`]
497
+ **Task verify**: [optional validated scoped command and observed test count, or omit]
498
+ **Verify (gate)**: [full project verify command from AGENTS.md / detected conventions]
499
+ **Verify**: [legacy alias — same as the gate command when only one line is used]
482
500
 
483
501
  ---
484
502
 
@@ -0,0 +1,169 @@
1
+ ---
2
+ name: organize-tests
3
+ description: Build or refresh a test-impact map for an existing project so local development runs only affected feature-slice test groups while CI keeps a full-suite release gate. Use for organize tests, group tests, test impact map, speed up tests, affected-only testing, test selection, or test-suite health. Detects runners and language adapters, inventories tests, proposes feature slices and tiered groups, runs shadow-mode adoption, updates AGENTS.md and CI additively, and audits drift. Never edits tests or weakens the full gate.
4
+ ---
5
+
6
+ # Organize Tests
7
+
8
+ `organize-tests` adopts opt-in test-impact selection in an existing project. It
9
+ reads the project, proposes feature-slice groups and language adapters, previews
10
+ every write, proves the selection in shadow mode, and only then applies the map.
11
+ The full suite remains the release, acceptance, and CI gate.
12
+
13
+ The skill is additive. It never edits, moves, deletes, or weakens a test. It
14
+ never removes a CI job or bypasses the full gate. A project can stop after any
15
+ preview and keep its current verification flow.
16
+
17
+ ## Modes
18
+
19
+ | Mode | Behavior |
20
+ | --- | --- |
21
+ | default | Analyze, propose, preview, ask, then apply |
22
+ | `--dry-run` | Analysis and proposal only; write nothing |
23
+ | `--check` | Read-only validation and drift report |
24
+ | `--refresh` | Update globs, edges, and timings; preserve manual group edits |
25
+ | `--shadow` | Run the selected and full suites, write evidence, never enable automatically |
26
+ | `--audit` | Report orphans, overlaps, slow groups, never-selected groups, and flaky candidates |
27
+ | `--no-ci` | Skip the CI patch; map and AGENTS block only |
28
+ | `--custom` | Force the custom-command adapter for an unsupported language |
29
+
30
+ ## Protocol
31
+
32
+ ### 1. Preflight
33
+
34
+ Resolve the Git root, layout, existing map, manifests, runner configuration, CI
35
+ files, and `AGENTS.md` managed blocks. Refuse unsafe writes: a symlinked or
36
+ non-regular map, a malformed existing map, or a project with no full command.
37
+ Never overwrite a map the skill does not own.
38
+
39
+ ### 2. Inventory tests
40
+
41
+ Enumerate test units with the project's native discovery. Record the owning
42
+ source path, the test path or target, and the observed test count. The recipes
43
+ are in [references/analysis-protocol.md](references/analysis-protocol.md) and
44
+ [references/language-adapters.md](references/language-adapters.md).
45
+
46
+ ### 3. Propose slices
47
+
48
+ Use feature slices for business applications, pipeline stages for compilers,
49
+ packages for libraries, and workspace packages for monorepos. One test unit owns
50
+ exactly one group. Target 5–15 groups; split a slice that is slow or spans
51
+ unrelated behavior.
52
+
53
+ ### 4. Derive dependencies
54
+
55
+ Use the native graph (Cargo metadata, Go list, Bazel/Nx, Maven/Gradle modules)
56
+ or production imports. `dependsOn` points upstream: a group lists what it needs.
57
+ A change selects its own group and downstream dependents, never upstream
58
+ providers. Never let shared test utilities pull unrelated groups together; when
59
+ uncertain, add an edge or make the group `alwaysRun`.
60
+
61
+ ### 5. Assign tiers
62
+
63
+ `task` is the fast unit, component, or contract tier. `phase` covers API, DB,
64
+ and integration behavior. `release` covers end-to-end and acceptance behavior.
65
+ Every slice needs at least one task-tier group and either a release/acceptance
66
+ group or a per-slice acceptance record:
67
+
68
+ ```json
69
+ { "acceptance": { "mode": "manual", "reason": "approved by the owning team" } }
70
+ ```
71
+
72
+ Valid modes are `manual`, `external`, and `na`. Every record needs a reason.
73
+
74
+ ### 6. Synthesize commands
75
+
76
+ Build argv commands per adapter. Run each candidate once and record the observed
77
+ test count; a command that runs zero tests is invalid. Resolve `{tests}` to the
78
+ group's full test set. For target-based runners (Rust, Go, C/C++), omit `tests`
79
+ when the command performs its own discovery; ownership then comes from `sources`
80
+ plus the command smoke test. Add `policy.prSmoke` groups for the PR gate and a
81
+ `requirements` probe for every `requires` name.
82
+
83
+ ### 7. Preview
84
+
85
+ Show a summary table (slice, tier, tests, sources, dependsOn, duration, command),
86
+ file coverage, the map layout, the AGENTS block diff, and the CI diff. On a
87
+ non-integration branch, ask before writing.
88
+
89
+ ### 8. Shadow
90
+
91
+ Run the selected groups, then the full suite, regardless of the first result.
92
+ Adoption requires zero missed failures. An escaped failure refuses adoption and
93
+ is reported with the group and failure tail. Never enable the map from shadow
94
+ mode. See [references/shadow-mode.md](references/shadow-mode.md).
95
+
96
+ ### 9. Apply with staging
97
+
98
+ Render the map, policy, fragments, optional wrapper, `CODEOPS-TESTING` block,
99
+ CI diff, and package scripts in a staging area under `$CODEOPS_TMPDIR`. Validate
100
+ the map and every diff. Back up existing files, write in a fixed order, and
101
+ restore every prior file on failure. Never leave a partial map or a half-updated
102
+ block. Validate the staged result with `check --strict`.
103
+
104
+ ### 10. Verify
105
+
106
+ Run `check`, one real selection, the full suite once, and `npm pack` or the
107
+ project build as applicable. Record the evidence. When `--metrics` is enabled,
108
+ emit only content-free fields: gate, selected/total, budget status, fallback
109
+ reason, and map hash. Never include test names, paths, output, or user text.
110
+
111
+ ### 11. Refresh/audit
112
+
113
+ `--refresh` updates globs, edges, and timings without discarding manual groups or
114
+ owner fields. `--check` reports drift. `--audit` reports orphans, overlaps,
115
+ slices without a task tier, slow groups, never-selected groups, and flaky
116
+ candidates. The health rules are in
117
+ [references/map-health.md](references/map-health.md).
118
+
119
+ ## AGENTS.md testing block
120
+
121
+ Only `organize-tests` writes the `<!-- CODEOPS-TESTING:START -->` /
122
+ `<!-- CODEOPS-TESTING:END -->` block. `analyze-project` detects it, preserves it
123
+ byte-for-byte, and warns when it is missing or stale; it never renders the block.
124
+ The contract, the optional `Test slices` table (slice, tier, tests, sources,
125
+ gate), and the wrapper guidance are in
126
+ [references/agents-testing-block.md](references/agents-testing-block.md).
127
+
128
+ ## CI patch rules
129
+
130
+ - Add a required full gate for release, acceptance, and package branches.
131
+ - Add an optional affected PR job behind a stable `verify` aggregator.
132
+ - Add nightly only when the project wants it; use the change marker.
133
+ - Add provenance only when the project builds a deployable artifact.
134
+ - Suggest a `.github/CODEOWNERS` entry for `codeops/test-map/**`; on approval,
135
+ add it without touching existing ownership rules.
136
+ - Preserve existing jobs, caching, toolchain setup, and branch filters. Preview
137
+ every diff and never remove a job.
138
+ - Never use `pull_request_target` with fork code or expose secrets to fork PRs.
139
+
140
+ The advisory templates are in
141
+ [references/ci-github-actions.md](references/ci-github-actions.md) and
142
+ [references/project-wrapper.md](references/project-wrapper.md). They are
143
+ previewed like every other change and are never applied automatically.
144
+
145
+ ## Safety rules
146
+
147
+ - Never edit, move, delete, or weaken a test.
148
+ - Never remove a CI job or weaken the full gate.
149
+ - No adoption on shadow misses.
150
+ - Refuse malformed or symlinked maps and paths outside the root.
151
+ - Stage and validate every write; restore prior files on failure.
152
+ - Idempotent refresh; preserve manual group edits and owner fields.
153
+ - No map write without preview and approval on non-integration branches.
154
+
155
+ ## Output contract
156
+
157
+ The preview names the map layout, each slice and tier, the command per group, the
158
+ file coverage, and every file that will change. The shadow report records the
159
+ verdict, selected groups, selected result, full result, escaped groups, and
160
+ `enabled: false`. The apply report lists the written files and the validation
161
+ result. The audit report names every finding with a path or group ID.
162
+
163
+ ---
164
+
165
+ > **Workspace hygiene (non-negotiable):** put every temporary artifact this run creates in
166
+ > `$CODEOPS_TMPDIR` (fallback: the OS temp directory), never in the repository, and delete them
167
+ > all before reporting completion. Never delete user files, versioned artifacts, worktrees, or
168
+ > another session's temporary files. Full rules:
169
+ > [_shared/workspace-hygiene.md](../../_shared/workspace-hygiene.md).