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.
- package/CHANGELOG.md +19 -0
- package/README.md +49 -2
- package/_shared/discovery.md +2 -1
- package/_shared/spec-first-ordering.md +6 -2
- package/_shared/test-selection.md +130 -0
- package/bin/codeops-test.mjs +855 -0
- package/bin/lib/test-branch-policy.d.mts +51 -0
- package/bin/lib/test-branch-policy.mjs +185 -0
- package/bin/lib/test-map-validate.d.mts +42 -0
- package/bin/lib/test-map-validate.mjs +618 -0
- package/bin/lib/test-map.d.mts +114 -0
- package/bin/lib/test-map.mjs +384 -0
- package/bin/lib/test-provenance.d.mts +73 -0
- package/bin/lib/test-provenance.mjs +188 -0
- package/bin/lib/test-runner.d.mts +72 -0
- package/bin/lib/test-runner.mjs +350 -0
- package/bin/lib/test-selection-changes.d.mts +76 -0
- package/bin/lib/test-selection-changes.mjs +272 -0
- package/bin/lib/test-selection-explain.mjs +75 -0
- package/bin/lib/test-selection-glob.d.mts +32 -0
- package/bin/lib/test-selection-glob.mjs +206 -0
- package/bin/lib/test-selection.d.mts +80 -0
- package/bin/lib/test-selection.mjs +418 -0
- package/package.json +4 -2
- package/schemas/test-map.schema.json +164 -0
- package/scripts/__pycache__/codeops_plan.cpython-314.pyc +0 -0
- package/scripts/__pycache__/install_agents.cpython-314.pyc +0 -0
- package/scripts/discovery-toggle.mjs +236 -0
- package/skills/analyze-project/SKILL.md +9 -2
- package/skills/exec-plan/SKILL.md +16 -8
- package/skills/exec-plan/execution-protocol.md +15 -2
- package/skills/git-commit/SKILL.md +1 -1
- package/skills/make-plan/SKILL.md +10 -3
- package/skills/make-plan/quality-checklist.md +4 -0
- package/skills/make-plan/templates.md +19 -1
- package/skills/organize-tests/SKILL.md +169 -0
- package/skills/organize-tests/references/agents-testing-block.md +80 -0
- package/skills/organize-tests/references/analysis-protocol.md +94 -0
- package/skills/organize-tests/references/ci-github-actions.md +178 -0
- package/skills/organize-tests/references/language-adapters.md +54 -0
- package/skills/organize-tests/references/map-health.md +47 -0
- package/skills/organize-tests/references/project-wrapper.md +72 -0
- package/skills/organize-tests/references/shadow-mode.md +60 -0
- package/skills/setup-codeops/SKILL.md +30 -3
- package/standards/coding-standards-full.md +28 -0
- package/standards/coding-standards.md +29 -0
- package/scripts/__pycache__/codeops_plan.cpython-312.pyc +0 -0
- 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
|
|
181
|
-
|
|
182
|
-
(
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
|
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
|
|
106
|
-
|
|
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
|
|
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
|
|
237
|
-
project's AGENTS.md or manifests — state what you
|
|
238
|
-
entry like any decision). If nothing is
|
|
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
|
-
**
|
|
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).
|