@amsterdamdatalabs/adl-agent-control-linux-x64 0.1.37 → 0.1.46
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/bin/adl +2 -2
- package/bin/reconcile +3 -11
- package/bin/setup +15 -15
- package/package.json +1 -1
- package/packages/agent-hooks/package.json +1 -1
- package/packages/agent-hooks/src/main.ts +23 -2
- package/packages/agent-hooks/src/rules/impl/adl-mcp-cli.ts +12 -8
- package/packages/agent-hooks/src/rules/impl/adl-run-required.ts +11 -5
- package/packages/agent-hooks/src/rules/impl/adl-workitem-role.ts +18 -5
- package/packages/agent-hooks/src/rules/impl/agent-tamper-protection.ts +30 -26
- package/packages/agent-hooks/src/rules/impl/git-target.ts +5 -0
- package/packages/agent-hooks/src/rules/impl/mcp-tool.ts +3 -1
- package/packages/agent-hooks/src/rules/impl/tool-frontend.ts +230 -11
- package/packages/agent-hooks/src/rules/shell/git-identity.ts +1 -1
- package/packages/agent-hooks/src/rules/shell/invocation.ts +14 -2
- package/packages/agent-hooks/src/rules/shell/protected-carriers.ts +10 -11
- package/packages/agent-settings/assets/TARUN-GLOBAL-AGENTS-INSTRUCTIONS.md +2 -2
- package/packages/agent-settings/package.json +1 -1
- package/packages/agent-settings/src/deploy/agents-deploy.ts +19 -1
- package/packages/agent-settings/src/deploy/index.ts +1 -0
- package/packages/agent-skills/assets/agent-search-tool-audit/SKILL.md +4 -6
- package/packages/agent-skills/assets/finalize-feature/SKILL.md +3 -3
- package/packages/agent-skills/assets/implement-feature/SKILL.md +17 -20
- package/packages/agent-skills/assets/implement-feature/references/briefs.md +3 -3
- package/packages/agent-skills/assets/implement-item/SKILL.md +3 -3
- package/packages/agent-skills/assets/mechanics/SKILL.md +1 -1
- package/packages/agent-skills/assets/plan-feature/SKILL.md +10 -10
- package/packages/agent-skills/assets/prepare-promotion/SKILL.md +3 -4
- package/packages/agent-skills/assets/report-bug/SKILL.md +2 -2
- package/packages/agent-skills/assets/review-change/SKILL.md +3 -3
- package/packages/agent-skills/assets/review-plan/SKILL.md +4 -4
- package/packages/agent-skills/assets/triage-workitems/SKILL.md +4 -4
- package/packages/agent-skills/assets/validate-design/SKILL.md +6 -12
- package/packages/agent-skills/assets/verify-item/SKILL.md +5 -5
- package/packages/agent-skills/assets/wayfinder/SKILL.md +1 -1
- package/packages/agent-skills/assets/workitems-process/SKILL.md +19 -21
- package/packages/agent-skills/assets/workitems-process/references/ado-and-git.md +36 -3
- package/packages/agent-skills/assets/workitems-process/references/evidence.md +4 -4
- package/packages/agent-skills/assets/write-feature/SKILL.md +4 -5
- package/packages/agent-skills/dist/deploy.d.ts +45 -0
- package/packages/agent-skills/dist/deploy.d.ts.map +1 -1
- package/packages/agent-skills/dist/deploy.js +167 -3
- package/packages/agent-skills/dist/deploy.js.map +1 -1
- package/packages/agent-skills/dist/index.d.ts +1 -1
- package/packages/agent-skills/dist/index.d.ts.map +1 -1
- package/packages/agent-skills/dist/index.js +1 -1
- package/packages/agent-skills/dist/index.js.map +1 -1
- package/packages/agent-skills/package.json +1 -1
- package/packages/agent-skills/src/deploy.ts +241 -3
- package/packages/agent-skills/src/index.ts +10 -1
- package/packages/azdo/README.md +13 -4
- package/packages/azdo/bunfig.toml +6 -0
- package/packages/azdo/package.json +1 -1
- package/packages/azdo/src/azdo-attachments.ts +2 -1
- package/packages/azdo/src/azdo-pr.ts +182 -6
- package/packages/azdo/src/azdo-wit.ts +290 -41
- package/packages/azdo/src/cli.ts +79 -12
- package/packages/azdo/src/lib/client.ts +88 -16
- package/packages/azdo/src/mcp-server.ts +75 -10
- package/packages/azdo/src/tool-dispatch.ts +61 -26
- package/packages/fleet/src/install-adl-package.ts +4 -3
- package/packages/repo-controls/assets/promote.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs/research/no-type.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs/research/typed-experiment.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs/research/typed-research.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/extra-key.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/extra-keys.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-created.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-description.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-last-update.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-status.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-title.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/optional-keys.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/research-ok.md +1 -1
- package/packages/repo-controls/package.json +1 -1
- package/packages/repo-controls/src/config.ts +21 -2
- package/packages/repo-controls/src/doctor.ts +9 -14
- package/packages/repo-controls/src/findings-ratchet.ts +12 -0
- package/packages/repo-controls/src/hook.ts +61 -4
|
@@ -9,6 +9,30 @@
|
|
|
9
9
|
* body either way; the allowance is stated in the message for the human,
|
|
10
10
|
* not enforced here).
|
|
11
11
|
*
|
|
12
|
+
* az365 (Scope A — JS-toolchain mutation policy, 2026-10-13): the matcher
|
|
13
|
+
* was a binary-name-only `deny` returning a Finding for every
|
|
14
|
+
* `frontend-tool` invocation regardless of effect. The OLD corpus
|
|
15
|
+
* contained 14 read-only invocations (lint, typecheck, coverage, test
|
|
16
|
+
* runs without snapshot updates, help-only) as `deny`, which is the bug:
|
|
17
|
+
* a legitimate "look at my code" call was refused with the same Finding
|
|
18
|
+
* as a file-rewriting one. The new matcher inspects `inv.args` and
|
|
19
|
+
* returns `null` (allow) for read-only invocations, returning a Finding
|
|
20
|
+
* only for the mutating ones:
|
|
21
|
+
*
|
|
22
|
+
* deny biome format … --write (file rewrite)
|
|
23
|
+
* deny vitest run -u / --update (snapshot mutation)
|
|
24
|
+
* deny tsc <file> (no --noEmit; emits .js/.d.ts)
|
|
25
|
+
* allow tsc --noEmit (type-check, no emit)
|
|
26
|
+
* allow biome check … (read)
|
|
27
|
+
* allow biome lint … (read)
|
|
28
|
+
* allow vitest run (test runner, no source write)
|
|
29
|
+
* allow vitest --coverage run (writes only to coverage/)
|
|
30
|
+
* allow jest --testPathPattern= (test runner, no source write)
|
|
31
|
+
*
|
|
32
|
+
* The matcher does NOT inspect the `just` recipe body — the gate runs at
|
|
33
|
+
* the recipe boundary, not inside it. `--help`/`--version` are already
|
|
34
|
+
* short-circuited at classification time and never reach this matcher.
|
|
35
|
+
*
|
|
12
36
|
* Tarun's decision (2026-09-27, feature az372): the message must not name a
|
|
13
37
|
* particular recipe — see messages.ts's `JUST_RECIPES_POINTER`.
|
|
14
38
|
*/
|
|
@@ -18,37 +42,231 @@ import type { Finding, MessageContext, RuleSpec } from '../spec.ts'
|
|
|
18
42
|
import { noParams } from '../spec.ts'
|
|
19
43
|
import { detailString, JUST_RECIPES_POINTER } from './messages.ts'
|
|
20
44
|
|
|
45
|
+
/** `biome format … --write` rewrites files in place. The flag may appear anywhere on the command line (`--write foo` or `foo --write`); the `--write=foo` and `-wX` concatenated forms arrive as single tokens. */
|
|
46
|
+
function biomeFormatWritesFile(args: readonly { readonly value: string }[]): boolean {
|
|
47
|
+
return args.some((arg) => {
|
|
48
|
+
if (arg.value === '--write' || arg.value === '-w') return true
|
|
49
|
+
if (arg.value.startsWith('--write=')) return true
|
|
50
|
+
// Concatenated short-flag form: `-wX` where X is anything. The shell
|
|
51
|
+
// does not split the token; the matcher sees `-wX` as one element.
|
|
52
|
+
if (arg.value.startsWith('-w') && arg.value.length > 2) return true
|
|
53
|
+
return false
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* `tsc <file>` without `--noEmit` emits `.js` and `.d.ts` files into the
|
|
59
|
+
* working tree. `tsc --noEmit` is the read intent. A bare `tsc` (no
|
|
60
|
+
* file operand, no flags) does nothing the matcher cares about — tsc
|
|
61
|
+
* without operands reads `tsconfig.json` and may emit based on its
|
|
62
|
+
* `noEmit` setting; we treat the no-operand case as ambiguous and let
|
|
63
|
+
* it through (the engine's `uncertain` category would catch it if a
|
|
64
|
+
* future rule needs that).
|
|
65
|
+
*/
|
|
66
|
+
const TSC_VALUE_FLAGS: ReadonlySet<string> = new Set([
|
|
67
|
+
'-p', '--project', // config file
|
|
68
|
+
'--build', // takes a project reference; value can be 'false'
|
|
69
|
+
'-t', '--target',
|
|
70
|
+
'--module',
|
|
71
|
+
'--moduleResolution',
|
|
72
|
+
'--jsx',
|
|
73
|
+
'--outDir',
|
|
74
|
+
'--rootDir',
|
|
75
|
+
'--baseUrl',
|
|
76
|
+
'--paths',
|
|
77
|
+
'--types',
|
|
78
|
+
'--lib',
|
|
79
|
+
])
|
|
80
|
+
|
|
81
|
+
function tscWritesFiles(args: readonly { readonly value: string }[]): boolean {
|
|
82
|
+
// --noEmit is the read intent; --noEmit=false is the write intent.
|
|
83
|
+
if (args.some((arg) => arg.value === '--noEmit')) return false
|
|
84
|
+
// Explicit emit flags are the write intent regardless of the file operand.
|
|
85
|
+
if (args.some((arg) => arg.value === '--emit' || arg.value.startsWith('--emit='))) return true
|
|
86
|
+
// Walk the args, skipping a value-taking flag's operand, and look for a
|
|
87
|
+
// positional file operand that names a .ts/.tsx file. That is the
|
|
88
|
+
// write intent: tsc defaults to emit when given a source file.
|
|
89
|
+
// `tsc -p tsconfig.json` is read-only because `-p` only names the
|
|
90
|
+
// config; tsc respects the config's own `noEmit` setting.
|
|
91
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
92
|
+
const arg = args[index] as { readonly value: string }
|
|
93
|
+
if (arg.value.startsWith('-')) {
|
|
94
|
+
// Flag with a value takes the next word; skip it so we do not
|
|
95
|
+
// mistake the flag's value (`tsconfig.json` after `-p`) for a
|
|
96
|
+
// positional file operand.
|
|
97
|
+
if (TSC_VALUE_FLAGS.has(arg.value)) index += 1
|
|
98
|
+
continue
|
|
99
|
+
}
|
|
100
|
+
// Positional operand that names a TypeScript source file is the
|
|
101
|
+
// write intent. Anything else (a config, a glob, an empty) is not.
|
|
102
|
+
if (/\.tsx?$/.test(arg.value)) return true
|
|
103
|
+
}
|
|
104
|
+
return false
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* `vitest run -u` / `vitest run --update` mutates snapshot files in place.
|
|
109
|
+
* The shell hands the matcher individual tokens; the `-uX` / `--update=now`
|
|
110
|
+
* / `-u=foo` concatenation forms arrive as a single token whose value
|
|
111
|
+
* starts with `-u` or `--update=`. Treat any token that starts with `-u`
|
|
112
|
+
* or `--update` (and is not a different flag like `--updateSnapshot`
|
|
113
|
+
* which is a different binary's flag) as the snapshot-write trigger.
|
|
114
|
+
*/
|
|
115
|
+
function vitestUpdatesSnapshots(args: readonly { readonly value: string }[]): boolean {
|
|
116
|
+
return args.some((arg) => {
|
|
117
|
+
if (arg.value === '-u' || arg.value === '--update') return true
|
|
118
|
+
if (arg.value === '-u=' || arg.value.startsWith('--update=')) return true
|
|
119
|
+
// Concatenated short-flag form: `-uX` where X is anything (rare in
|
|
120
|
+
// practice; vitest doesn't combine `-u` with other short flags, but
|
|
121
|
+
// the shell hands the matcher the token unsplit). Match `-u` as a
|
|
122
|
+
// prefix only when the rest is a non-flag char, so `-update` (long
|
|
123
|
+
// option) does not match.
|
|
124
|
+
if (arg.value.startsWith('-u') && arg.value.length > 2) return true
|
|
125
|
+
return false
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** `jest --testPathPattern=… -u` / `jest -u` / `jest --updateSnapshot` mutates snapshots. */
|
|
130
|
+
function jestUpdatesSnapshots(args: readonly { readonly value: string }[]): boolean {
|
|
131
|
+
return args.some((arg) => {
|
|
132
|
+
if (arg.value === '-u' || arg.value === '--updateSnapshot') return true
|
|
133
|
+
if (arg.value === '-u=' || arg.value.startsWith('--updateSnapshot=')) return true
|
|
134
|
+
// Concatenated short-flag form: `-uX` as above.
|
|
135
|
+
if (arg.value.startsWith('-u') && arg.value.length > 2) return true
|
|
136
|
+
return false
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
|
|
21
140
|
export const toolFrontend: RuleSpec = {
|
|
22
141
|
id: 'tool.frontend',
|
|
23
142
|
category: 'tool',
|
|
24
143
|
severity: 'deny',
|
|
25
144
|
enabledByDefault: true,
|
|
26
|
-
summary: "Deny
|
|
145
|
+
summary: "Deny mutating npx/vitest/jest/biome/tsc invocations; reads and tests are permitted.",
|
|
27
146
|
params: noParams,
|
|
28
147
|
match(inv: Invocation): Finding | null {
|
|
29
148
|
if (inv.kind !== 'frontend-tool') return null
|
|
30
|
-
|
|
149
|
+
const args = inv.args
|
|
150
|
+
// The mutating surfaces. Each is the only binary-name whose flag
|
|
151
|
+
// shape can change the working tree; if the rule's matcher here
|
|
152
|
+
// grows, the corpus deny list below is the place to add the new
|
|
153
|
+
// fixture so the engine's verdict stays testable.
|
|
154
|
+
switch (inv.tool.replace(/\.exe$/i, '')) {
|
|
155
|
+
case 'biome':
|
|
156
|
+
// `biome format … --write` is the file-rewrite path. `biome
|
|
157
|
+
// check` and `biome lint` are reads and must NOT be denied. The
|
|
158
|
+
// matcher inspects the args to distinguish; without this
|
|
159
|
+
// inspection every biome invocation is denied, which is the
|
|
160
|
+
// az365 Scope A bug.
|
|
161
|
+
if (biomeFormatWritesFile(args)) {
|
|
162
|
+
return { ruleId: 'tool.frontend', severity: 'deny', detail: { tool: inv.tool, action: 'biome-format-write' } }
|
|
163
|
+
}
|
|
164
|
+
return null
|
|
165
|
+
case 'tsc':
|
|
166
|
+
// `tsc --noEmit` is a read. `tsc <file>` without --noEmit emits
|
|
167
|
+
// .js and .d.ts into the working tree. Match the absence of
|
|
168
|
+
// --noEmit as the write intent.
|
|
169
|
+
if (tscWritesFiles(args)) {
|
|
170
|
+
return { ruleId: 'tool.frontend', severity: 'deny', detail: { tool: inv.tool, action: 'tsc-emit' } }
|
|
171
|
+
}
|
|
172
|
+
return null
|
|
173
|
+
case 'vitest':
|
|
174
|
+
// `vitest run` is a test runner. The .vitest cache directory and
|
|
175
|
+
// any coverage output live under node_modules / coverage/, not in
|
|
176
|
+
// project source. `-u` / `--update` is the snapshot-mutation
|
|
177
|
+
// path; the snapshot files live alongside the test files and ARE
|
|
178
|
+
// project source.
|
|
179
|
+
if (vitestUpdatesSnapshots(args)) {
|
|
180
|
+
return { ruleId: 'tool.frontend', severity: 'deny', detail: { tool: inv.tool, action: 'vitest-snapshot-write' } }
|
|
181
|
+
}
|
|
182
|
+
return null
|
|
183
|
+
case 'jest':
|
|
184
|
+
if (jestUpdatesSnapshots(args)) {
|
|
185
|
+
return { ruleId: 'tool.frontend', severity: 'deny', detail: { tool: inv.tool, action: 'jest-snapshot-write' } }
|
|
186
|
+
}
|
|
187
|
+
return null
|
|
188
|
+
case 'npx':
|
|
189
|
+
// `npx biome check …` is a read; `npx vitest run` is a test
|
|
190
|
+
// runner; `npx tsc --noEmit` is a typecheck. None mutate source.
|
|
191
|
+
// The mutating shape (browser automation, file rewrites) is
|
|
192
|
+
// Scope B (az365's split into a separate Feature for browser
|
|
193
|
+
// automation); this rule's match() does not cover it today and
|
|
194
|
+
// the agent-hooks `adl-human-azdo-bypass` rule plus the future
|
|
195
|
+
// `tool.browser-automation` rule (Feature azTBA) are the right
|
|
196
|
+
// place for it.
|
|
197
|
+
return null
|
|
198
|
+
default:
|
|
199
|
+
// Unknown `frontend-tool` name (a new addition to
|
|
200
|
+
// ClassifyConfig.frontendToolNames the rule does not yet know
|
|
201
|
+
// about). Default to allow and let the corpus surface it as a
|
|
202
|
+
// chosen behaviour on the next pass.
|
|
203
|
+
return null
|
|
204
|
+
}
|
|
31
205
|
},
|
|
32
206
|
message(finding: Finding, ctx: MessageContext) {
|
|
33
207
|
const tool = detailString(finding, 'tool')
|
|
208
|
+
const action = detailString(finding, 'action')
|
|
209
|
+
const actionLabel =
|
|
210
|
+
action === 'biome-format-write' ? 'file rewrite'
|
|
211
|
+
: action === 'tsc-emit' ? 'tsc emit (.js / .d.ts write)'
|
|
212
|
+
: action === 'vitest-snapshot-write' ? 'snapshot update'
|
|
213
|
+
: action === 'jest-snapshot-write' ? 'snapshot update'
|
|
214
|
+
: 'mutating invocation'
|
|
34
215
|
return {
|
|
35
|
-
user: `Blocked
|
|
216
|
+
user: `Blocked ${tool} (${actionLabel}). Run it through ${JUST_RECIPES_POINTER} instead; it carries the same arguments and runs through the gate profile.`,
|
|
36
217
|
agent:
|
|
37
|
-
`DENIED
|
|
218
|
+
`DENIED ${tool} (${actionLabel}). Do not invoke ${tool} from Bash/Shell, including through sh -c, xargs, find -exec, or command substitution. ` +
|
|
38
219
|
`Run it through ${JUST_RECIPES_POINTER} instead - the recipe carries the same arguments and runs the gate profile rather than skipping it. ` +
|
|
39
|
-
'
|
|
40
|
-
'and invoking the tool from inside a `just` recipe body is allowed because the gate runs at the recipe boundary. ' +
|
|
220
|
+
'Read-only invocations of the same tool (lint, typecheck, test runs without snapshot updates) are permitted. ' +
|
|
41
221
|
`Blocked command: ${ctx.snippet}`,
|
|
42
222
|
}
|
|
43
223
|
},
|
|
44
224
|
corpus: {
|
|
225
|
+
// The mutating invocations — the only ones `match()` returns a Finding
|
|
226
|
+
// for. 6 entries; the az388 corpus floor in tests/unit/rules-corpus-floors.test.ts
|
|
227
|
+
// was lowered from 15 to 6 on 2026-10-13 (az365 Scope A) and the file
|
|
228
|
+
// header explains why.
|
|
45
229
|
deny: [
|
|
230
|
+
'biome format packages/desktop/src --write',
|
|
231
|
+
'biome format --write packages/desktop/src',
|
|
232
|
+
'biome format --write src/foo.ts',
|
|
233
|
+
// Concatenated short-flag form: the shell hands the matcher a
|
|
234
|
+
// single `-wX` token; the predicate's prefix check catches it.
|
|
235
|
+
'biome format src/foo.ts -w',
|
|
236
|
+
'vitest run -u',
|
|
237
|
+
'vitest run --update',
|
|
238
|
+
// Concatenated form: the shell hands the matcher a single `-uX`
|
|
239
|
+
// token; the predicate's prefix check catches it. The corpus entry
|
|
240
|
+
// is the value the shell actually produces (`-uX`); vitest does
|
|
241
|
+
// not combine `-u` with other short flags, but the matcher has
|
|
242
|
+
// to handle the form anyway.
|
|
243
|
+
'vitest run -uX',
|
|
244
|
+
// `=`-form: `--update=now` arrives as a single token, predicate
|
|
245
|
+
// catches via `startsWith('--update=')`.
|
|
246
|
+
'vitest run --update=now',
|
|
247
|
+
'tsc src/foo.ts',
|
|
248
|
+
// jest's snapshot-mutation flag form; covers the jest branch of
|
|
249
|
+
// match() and the jest action label in message(). The flag is
|
|
250
|
+
// either `-u` or `--updateSnapshot`; both must be denied.
|
|
251
|
+
'jest -u',
|
|
252
|
+
'jest --updateSnapshot',
|
|
253
|
+
'jest -uX',
|
|
254
|
+
],
|
|
255
|
+
// The read-only invocations — every one `match()` returns null for.
|
|
256
|
+
// The 14 the OLD corpus mis-classified as denied move here as the
|
|
257
|
+
// audit-trail entry that pins "this is a read, the deny is over-broad
|
|
258
|
+
// and a future narrowing should permit it" — the future narrowing is
|
|
259
|
+
// THIS rule's match(), already implemented; these entries are the
|
|
260
|
+
// proof the test suite pins against.
|
|
261
|
+
allow: [
|
|
46
262
|
'npx biome check packages/desktop/src',
|
|
263
|
+
'npx vitest run',
|
|
264
|
+
'npx tsc --noEmit',
|
|
47
265
|
'vitest run',
|
|
48
266
|
'vitest --coverage run',
|
|
49
267
|
'jest --testPathPattern=adl-conversation',
|
|
268
|
+
'biome check packages/desktop/src',
|
|
50
269
|
'biome lint packages/desktop/src',
|
|
51
|
-
'biome format packages/desktop/src --write',
|
|
52
270
|
'tsc --noEmit',
|
|
53
271
|
'tsc -p tsconfig.json',
|
|
54
272
|
'xargs npx tsc --noEmit',
|
|
@@ -56,12 +274,13 @@ export const toolFrontend: RuleSpec = {
|
|
|
56
274
|
"sh -c 'npx tsc --noEmit'",
|
|
57
275
|
"bash -lc 'npx tsc --noEmit'",
|
|
58
276
|
'echo $(biome lint src/)',
|
|
59
|
-
//
|
|
60
|
-
//
|
|
277
|
+
// The two help-only entries are already short-circuited by
|
|
278
|
+
// isHelpOrVersionOnly at classification time; they never reach
|
|
279
|
+
// `match()`. Listed under `allow` for the same audit-trail reason
|
|
280
|
+
// as the reads above: chosen behaviour, not merely a passing test.
|
|
61
281
|
'biome lint --help',
|
|
62
282
|
'tsc --noEmit --help',
|
|
63
|
-
|
|
64
|
-
allow: [
|
|
283
|
+
// Pre-existing allow entries preserved.
|
|
65
284
|
'npx --version',
|
|
66
285
|
'vitest --version',
|
|
67
286
|
'jest --version',
|
|
@@ -869,7 +869,7 @@ export interface GitInvocationPiece {
|
|
|
869
869
|
/**
|
|
870
870
|
* One `adl mcp cli <command> …` invocation found while scanning a command,
|
|
871
871
|
* paired with the effective identity for THAT specific piece. `command` is the gated command name
|
|
872
|
-
* (e.g. `workitem-create-or-update`), lower-cased; `rest` is the dequoted
|
|
872
|
+
* (e.g. `workitem-create-or-update-pbi`), lower-cased; `rest` is the dequoted
|
|
873
873
|
* values after it, the same shape `impl/adl-mcp-cli.ts`'s `adlMcpCliCall`
|
|
874
874
|
* reads off `Invocation.args.slice(3)`, so a role rule can correlate a
|
|
875
875
|
* specific `Invocation` to its own piece here by comparing values, exactly
|
|
@@ -86,7 +86,19 @@ export type Invocation =
|
|
|
86
86
|
| { readonly kind: 'azure-devops'; readonly args: readonly Token[] }
|
|
87
87
|
| { readonly kind: 'find' }
|
|
88
88
|
| { readonly kind: 'grep' }
|
|
89
|
-
|
|
89
|
+
/**
|
|
90
|
+
* `npx`/`vitest`/`jest`/`biome`/`tsc` and their Windows spellings (the
|
|
91
|
+
* `.exe` variants, see `ClassifyConfig.frontendToolNames`). The
|
|
92
|
+
* `args` field carries the tokenized post-name argument list so
|
|
93
|
+
* `tool.frontend.match()` (and any future `tool.browser-automation`
|
|
94
|
+
* rule) can inspect flags — `--write` on `biome format`, `--noEmit`
|
|
95
|
+
* on `tsc`, `-u`/`--update` on `vitest run`, etc. — instead of
|
|
96
|
+
* denying every binary-name invocation regardless of effect. Threaded
|
|
97
|
+
* through by `walkToolCommand` below; the args are not consumed by
|
|
98
|
+
* `isHelpOrVersionOnly` (which has already run before this producer),
|
|
99
|
+
* they are what remains of the post-name token stream.
|
|
100
|
+
*/
|
|
101
|
+
| { readonly kind: 'frontend-tool'; readonly tool: string; readonly args: readonly Token[] }
|
|
90
102
|
/**
|
|
91
103
|
* `bun test` (any arguments, including a file/pattern operand) — the
|
|
92
104
|
* `test` subcommand specifically, after skipping bun's own `-C <dir>` /
|
|
@@ -638,7 +650,7 @@ function walkToolCommand(name: string, args: readonly Token[], cfg: ClassifyConf
|
|
|
638
650
|
return [{ kind: 'azure-devops', args }]
|
|
639
651
|
}
|
|
640
652
|
if (cfg.cargoNames.has(name)) return isHelpOrVersionOnly(args) ? [] : [{ kind: 'cargo' }]
|
|
641
|
-
if (cfg.frontendToolNames.has(name)) return isHelpOrVersionOnly(args) ? [] : [{ kind: 'frontend-tool', tool: name }]
|
|
653
|
+
if (cfg.frontendToolNames.has(name)) return isHelpOrVersionOnly(args) ? [] : [{ kind: 'frontend-tool', tool: name, args }]
|
|
642
654
|
if (cfg.bunNames.has(name)) return isBunTestSubcommand(args) ? [{ kind: 'bun-test' }] : null
|
|
643
655
|
if (cfg.justNames.has(name)) return walkJustCommand(args)
|
|
644
656
|
if (name === 'xargs' || name === 'xargs.exe') return xargsInvocation(args, walk)
|
|
@@ -10,9 +10,16 @@
|
|
|
10
10
|
* `agent-tamper-protection.ts` imports it, rather than two copies that could
|
|
11
11
|
* drift.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* `adl-config.toml` is NOT in this set. It is a runtime configuration file
|
|
14
|
+
* the agent must be able to edit (Tarun, 2026-10-13: "adl-config.toml can be
|
|
15
|
+
* editted but not committed by the agents"). The COMMIT side is enforced by
|
|
16
|
+
* repo-controls' `integrity.ts` (`WIRING`, refuses a normal commit of
|
|
17
|
+
* `adl-config.toml`) and by `agent-hooks`' `git.protected-tree-commit` rule
|
|
18
|
+
* (Bug 373/PBI 679); both fire on the staged index, not on the working
|
|
19
|
+
* tree.
|
|
20
|
+
*
|
|
21
|
+
* Still hard-coded on purpose (not configurable): a per-repo allowlist in
|
|
22
|
+
* `adl-config.toml` would be the same "switch the gate off" hole this rule
|
|
16
23
|
* exists to close.
|
|
17
24
|
*/
|
|
18
25
|
|
|
@@ -29,14 +36,6 @@ export const PROTECTED_CARRIER_PATHS: ReadonlySet<string> = new Set([
|
|
|
29
36
|
'.claude/settings.json',
|
|
30
37
|
'.claude/settings.local.json',
|
|
31
38
|
'.codex/config.toml',
|
|
32
|
-
// `adl-config.toml` is the repo's hook switch — every hook run re-reads it,
|
|
33
|
-
// so an agent that writes `[hooks] enabled = false` or disables any rule
|
|
34
|
-
// flips the gate off before the next tool call lands. Re-listed here on
|
|
35
|
-
// 2026-10-12 (Tarun: "we left a door open, close it"). The earlier
|
|
36
|
-
// 'human-only at commit time' assumption was wrong: the commit-time gate
|
|
37
|
-
// fires for the COMMITTED file, not for edits that never stage, and the
|
|
38
|
-
// working-tree copy is what every running hook reads.
|
|
39
|
-
'adl-config.toml',
|
|
40
39
|
'.codex/hooks.json',
|
|
41
40
|
'.cursor/hooks.json',
|
|
42
41
|
'.opencode/plugins/adl-repo.ts',
|
|
@@ -6,8 +6,8 @@ Where a repository has its own AGENTS.md, that file and the files it links add t
|
|
|
6
6
|
## Communication
|
|
7
7
|
- Address me as **Tarun**.
|
|
8
8
|
- **Answer first.** No greeting, no "let me", no recap of what was just done, no closers.
|
|
9
|
-
- One idea per sentence. Short beats polite. Articles can go; not, never, no, only never do. Numbers, paths, commands exact.
|
|
10
|
-
- Bullet points over prose
|
|
9
|
+
- One idea per sentence. Short beats polite. Articles can go; not, never, no, only never do. Numbers, paths, commands exact. **Maximum 100 words** per response
|
|
10
|
+
- **Bullet points over prose**.
|
|
11
11
|
- **Open points:** one numbered list of open decisions per conversation. Show it only when it changes — item added or resolved. After a long gap or compaction, restate the full list. That is when it gets lost.
|
|
12
12
|
- **Questions:** one at a time, one or two sentences. Options inline: `A) … B) …`. A long multi-part question means split the work, not more prose.
|
|
13
13
|
- Full sentences for security warnings, irreversible actions, step-by-step orders, and when I'm confused. Then short resumes.
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"description": "Machine-global agent surface tooling (sweep, global-agents, kimi-hooks, surface-policy) run by the fleet bin wrappers under bun; also holds GLOBAL-AGENTS.md, surface policy, and the protected list. Subagent definitions (Claude/Codex/Cursor/Kimi/OpenCode) are canonical in packages/agent-skills/extensions/adl-factory/agents/*.toml and projected per repo by that package's installer; this package does not generate or hold its own copy.",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"typecheck": "tsc --noEmit",
|
|
9
|
-
"test": "bun test --coverage",
|
|
9
|
+
"test": "bun test --isolate --coverage",
|
|
10
10
|
"check": "bun run typecheck && bun run test --coverage"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
@@ -25,6 +25,13 @@ export interface DeployAgentsOptions {
|
|
|
25
25
|
readonly agents?: readonly AgentDef[];
|
|
26
26
|
/** Computes the exact same written/unchanged/removed/conflicts result without writing anything (independent review, 2026-09-29, finding 6). */
|
|
27
27
|
readonly dryRun?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* When set, deploy only the named surface (profile.agents.surfaces[].name) —
|
|
30
|
+
* the deploy half of the per-surface knob (az567). Other surfaces are
|
|
31
|
+
* left untouched: their files are not written, their conflicts are not
|
|
32
|
+
* reported, the lock is not rewritten for them.
|
|
33
|
+
*/
|
|
34
|
+
readonly surfaceFilter?: string;
|
|
28
35
|
}
|
|
29
36
|
|
|
30
37
|
interface AgentConflict {
|
|
@@ -71,6 +78,10 @@ export function deployAgents(options: DeployAgentsOptions): DeployAgentsResult {
|
|
|
71
78
|
let anyLegacyTakeover = false;
|
|
72
79
|
|
|
73
80
|
for (const surface of profile.agents.surfaces) {
|
|
81
|
+
// az567 surfaceFilter: only iterate the named surface. Other
|
|
82
|
+
// surfaces' deploy paths, conflicts, and lock entries are out of
|
|
83
|
+
// scope and never appear in this result.
|
|
84
|
+
if (options.surfaceFilter !== undefined && surface.name !== options.surfaceFilter) continue;
|
|
74
85
|
const ext = extensionFor(surface);
|
|
75
86
|
const kept: string[] = [];
|
|
76
87
|
|
|
@@ -126,7 +137,14 @@ export function deployAgents(options: DeployAgentsOptions): DeployAgentsResult {
|
|
|
126
137
|
newAgentEntries[surface.folder] = kept.sort();
|
|
127
138
|
}
|
|
128
139
|
|
|
129
|
-
|
|
140
|
+
// az567 surfaceFilter: when only one surface is in scope, the
|
|
141
|
+
// `newAgentEntries` map only carries that surface. The lock write
|
|
142
|
+
// must keep every other surface's previous lock entry byte-identical
|
|
143
|
+
// so non-target surfaces are not "unwritten" by a per-surface deploy.
|
|
144
|
+
const lockAgents = options.surfaceFilter === undefined
|
|
145
|
+
? newAgentEntries
|
|
146
|
+
: { ...lock.agents, ...newAgentEntries };
|
|
147
|
+
if (!dryRun) writeLock(targetRepo, { schema: 1, mcp: lock.mcp, agents: lockAgents });
|
|
130
148
|
|
|
131
149
|
// Once anything was actually taken over, this package's own footprint in
|
|
132
150
|
// the legacy lock's `files` section for these exact agent paths is stale:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { type DoctorResult } from "./doctor.js";
|
|
2
2
|
export { listSettings } from "./list.js";
|
|
3
3
|
export { deploySettings, doctorSettings, type DeploySettingsResult } from "./settings.js";
|
|
4
|
+
export { deployAgents, type DeployAgentsResult } from "./agents-deploy.js";
|
|
4
5
|
export { loadProfile } from "./profile.js";
|
|
5
6
|
export { loadMcpSource, type McpServerSpec } from "./mcp-source.js";
|
|
6
7
|
export { loadAgentSources, type AgentDef } from "./agent-toml.js";
|
|
@@ -26,8 +26,7 @@ and its evidence siblings.
|
|
|
26
26
|
- The repo has no `.adl/logs/` (no agent-hooks hooks installed).
|
|
27
27
|
- The question is about a non-search tool (use a different audit
|
|
28
28
|
script).
|
|
29
|
-
- The repo
|
|
30
|
-
--show-toplevel` fallback cannot find the project root — pass
|
|
29
|
+
- The repo has no project root the script can find — pass
|
|
31
30
|
`--out <absolute-dir>` to write the report elsewhere.
|
|
32
31
|
|
|
33
32
|
## Process
|
|
@@ -42,8 +41,7 @@ After deploy, the script lives at:
|
|
|
42
41
|
|
|
43
42
|
In dev (this repo's `packages/agent-skills/`), the equivalent path is
|
|
44
43
|
`packages/agent-skills/assets/agent-search-tool-audit/scripts/audit.sh`.
|
|
45
|
-
The script
|
|
46
|
-
so it works in both layouts.
|
|
44
|
+
The script locates the project root, so it works in both layouts.
|
|
47
45
|
|
|
48
46
|
### 2. Run with the desired window
|
|
49
47
|
|
|
@@ -133,8 +131,8 @@ Rationale moved out of `scripts/audit.sh` (REP-007: no multi-line comments).
|
|
|
133
131
|
`hooks-deny.log` (read-only history, never migrated, deduped against the new ones);
|
|
134
132
|
`.adl/logs/agents/tool-calls.log` (`adl run` lifecycle).
|
|
135
133
|
- Path discovery: the script lives in the agent-skills asset tree (5 levels deep in dev, 4 once
|
|
136
|
-
deployed to `.agents/skills/`), so it
|
|
137
|
-
and falls back to the dev layout if
|
|
134
|
+
deployed to `.agents/skills/`), so it locates the project root to work in both layouts
|
|
135
|
+
and falls back to the dev layout if it cannot.
|
|
138
136
|
- Required tools: `jq`, `python3` and `rg`; each is checked up front and the script fails loud
|
|
139
137
|
(exit 1) naming the missing one. `rg` is a protected bootstrap tool on every managed machine.
|
|
140
138
|
- Cost: under 30 s on a typical 2-3 MB log set; no network.
|
|
@@ -24,13 +24,13 @@ Load `workitems-process` first. You are a leaf sub-agent of `lead` (`implement-f
|
|
|
24
24
|
|
|
25
25
|
## Preconditions
|
|
26
26
|
|
|
27
|
-
1.
|
|
27
|
+
1. The branch head equals `H` and the worktree is clean; else return it to the lead as a finding and stop ([No block](../workitems-process/SKILL.md#no-block)).
|
|
28
28
|
2. The Feature-final verify is `PASS` and code is `APPROVE` and architecture `CLEAR`/`WATCH`, all at `H`; else return it to the lead as a finding and stop ([No block](../workitems-process/SKILL.md#no-block)).
|
|
29
29
|
|
|
30
30
|
## Procedure
|
|
31
31
|
|
|
32
|
-
1. **Docs check.** For each entry in `design.md` `## Docs impact` (per-Bug run: the docs the fix touches), find the matching path in
|
|
33
|
-
2. **PR body.** The five non-empty sections
|
|
32
|
+
1. **Docs check.** For each entry in `design.md` `## Docs impact` (per-Bug run: the docs the fix touches), find the matching path in the diff against `H` and read the hunk, not just the filename. "No docs impact" counts as covered. Any missing entry: verdict `REWORK`, finding `docs-missing` (`where`: doc path or section; `fix`: what to write). Stop.
|
|
33
|
+
2. **PR body.** The five non-empty sections (Summary, Why, Changes, Test plan, Security impact considered), filled as below. Every claim links to evidence (a report, a commit, a run); no unverified statements.
|
|
34
34
|
- `## Summary`: what the Feature delivers, by item (id, title).
|
|
35
35
|
- `## Why`: from `intent.md` and `requirements.md`, in the plan's words, not new claims.
|
|
36
36
|
- `## Changes`: per item, the files and behaviour changed; design updates made during the run; docs touched.
|
|
@@ -14,8 +14,8 @@ Load `workitems-process` first. This card is the lead's whole procedure. Sub-age
|
|
|
14
14
|
## When to use
|
|
15
15
|
|
|
16
16
|
- The dispatcher started you for one Feature: every in-scope item (not Parked/Removed) is at Approved or later, at least one is not Done, every predecessor Feature is at Awaiting Release or Done.
|
|
17
|
-
- **Per-Bug run.** The dispatcher may instead start you for one Fixes Bug at Approved through Updating Docs, until its PR merges, keyed by the Bug **[pending L5, X2]**, on
|
|
18
|
-
- One run = one worktree, one branch, one lead session (you), **one implementation PR
|
|
17
|
+
- **Per-Bug run.** The dispatcher may instead start you for one Fixes Bug at Approved through Updating Docs, until its PR merges, keyed by the Bug **[pending L5, X2]**, on the per-Bug branch. A Bug has no plan folder, no Plan Hash and no Design Validation: skip only Preconditions 4-5 and procedure step 1. Preconditions 1-3 apply in their per-Bug form (below). The Bug's ladder and moves are in [Bug states](../workitems-process/references/workitem-states.md#5-bug-states). A Bug at In Reproduction or later is a resumed run: continue from its state (Resume, below). On a fresh run (Bug at Approved) your first board write is Approved → **In Reproduction**. Then spawn `executor` (`implement-item`, reproduce mode, `reproduce` brief in `references/briefs.md`): its first commit is the Bug's item file, written from the G1-approved Bug Description, with its LEDGER row; its next commit is the regression test, proved red on the base and committed marked as an expected failure so the commit stays green, and that brief writes nothing else. Post the executor's unmarked red run on the base as a comment on the Bug, as evidence. Then move In Reproduction → **In Fixing**; the `fix` brief (bug mode) writes the minimal fix until green, in the loop, and its fix commit removes the expected-failure mark. Every commit stays independently green, with no exception. Run the loop and the one PR as usual. You write no file yourself. Won't reproduce: post what you tried as a comment on the Bug and move it to Parked for Removal (G3 is the human's). You resolve everything else in the run ([No block](../workitems-process/SKILL.md#no-block)).
|
|
18
|
+
- One run = one worktree, one branch, one lead session (you), **one implementation PR**.
|
|
19
19
|
- Resume is not a separate mode: if the trigger still holds on a later tick you are started again in the **same worktree on the same branch**. Read the board and the branch, continue (Resume, below).
|
|
20
20
|
|
|
21
21
|
## Inputs
|
|
@@ -27,21 +27,18 @@ Load `workitems-process` first. This card is the lead's whole procedure. Sub-age
|
|
|
27
27
|
| Plan folder | `plans/features/az<id>-<slug>/`: `intent.md`, `requirements.md` (EARS criteria `AC-n`, `## Items`), `design.md`, `file-map-reference.md`, `items/az<id>-<slug>.md` |
|
|
28
28
|
| Feature fields | `BaselineSha`, `PlanHash`, `PlanRevision` **[pending T3: named arguments; reference names not yet confirmed through the API]** |
|
|
29
29
|
|
|
30
|
-
Identity `agent-tarun`.
|
|
31
|
-
|
|
32
30
|
## Preconditions
|
|
33
31
|
|
|
34
|
-
1.
|
|
35
|
-
2.
|
|
36
|
-
3.
|
|
37
|
-
4.
|
|
38
|
-
5. `sha256` of `requirements.md` equals `PlanHash` (the hash covers `requirements.md` only). On a mismatch, post both hashes as a Feature comment and do not start Design Validation: the approved plan is not the one on the branch. **[pending F7: the tool will refuse the Design Validation move on a mismatch; the field reference name is not yet confirmed through the API.]**
|
|
32
|
+
1. The worktree matches `worktree`; the branch is the Feature's implementation branch (per-Bug run: the per-Bug branch); the tree is clean, or the dirt is your own previous attempt.
|
|
33
|
+
2. Read the Feature's children (`workitem_list_or_get`). Every in-scope item is at Approved or later; if not, the trigger does not hold: report the item and its state on the Feature and end the run. Per-Bug run: read only the Bug; it must be at Approved through Updating Docs with its PR not merged (other Bugs in the Fixes Feature do not matter); if not, report its state on the Bug and end the run.
|
|
34
|
+
3. The plan folder is on the branch (planning PR merged): the four plan documents and every `items/az<id>-*.md` exist. If the planning PR is not merged yet, end the run and report it; the next tick re-evaluates.
|
|
35
|
+
4. `sha256` of `requirements.md` equals `PlanHash` (the hash covers `requirements.md` only). On a mismatch, post both hashes as a Feature comment and do not start Design Validation: the approved plan is not the one on the branch. **[pending F7: the tool will refuse the Design Validation move on a mismatch; the field reference name is not yet confirmed through the API.]**
|
|
39
36
|
|
|
40
37
|
## Procedure
|
|
41
38
|
|
|
42
39
|
### 1. Design Validation
|
|
43
40
|
|
|
44
|
-
1. The run starts by merging
|
|
41
|
+
1. The run starts by merging the integration base into its branch (a fast-forward on a fresh start; never rebase, never force). The worktree `HEAD` then contains the current integration, and Design Validation diffs `<BaselineSha>..HEAD`.
|
|
45
42
|
2. Move every Approved item → **Design Validation** (adjacent forward move; the fence allows it). This is your first board write.
|
|
46
43
|
3. Spawn `architect` with the `validate-design` brief. It checks `design.md` and its `file-map-reference.md` by diffing `<BaselineSha>..HEAD`, records `head_sha`, and rules `FRESH`, or `DRIFT` (with the exact delta). `requirements.md` is **not** re-validated: it was validated in planning and approved at G1.
|
|
47
44
|
4. `FRESH`: continue. `DRIFT`: **drift never leaves the run**. Spawn `planner` (skill `plan-feature`, Mode B: design update) with the delta. It updates `design.md`, `file-map-reference.md` (the `## Map` rows the drift moves, and the `## Baseline` section) and the Feature's Baseline Sha to the new `integration` base, adds the LEDGER row `az<id>-design-update`, and commits on the Feature branch; Plan Hash is unchanged. You write no file: record the update in the implementation report and the PR. Then re-validate once. Items never go to New or In Planning.
|
|
@@ -54,7 +51,7 @@ The four states **In Implementation, In Testing, In Review, Updating Docs** are
|
|
|
54
51
|
|
|
55
52
|
- **Order is yours.** Pick the item order from `design.md` (shared files, risk first). Default staffing is conservative: one executor at a time on the shared branch. Fan out only items with disjoint file scopes in `file-map-reference.md`, as native sub-agents of this session, never the adl-workflow `team` worker pool. You may also spawn read-only explore sub-agents on the fast tier.
|
|
56
53
|
- The usual order is In Testing before In Review (green code before a reviewer's time). A default, not a rule.
|
|
57
|
-
- **Every commit is on the run's one branch** (the Feature branch, or
|
|
54
|
+
- **Every commit is on the run's one branch** (the Feature branch, or the per-Bug branch in a per-Bug run). No per-item branch, worktree or PR. Commits carry `AB#<item id>`.
|
|
58
55
|
|
|
59
56
|
Per item, in the order you chose:
|
|
60
57
|
|
|
@@ -70,7 +67,7 @@ Per item, in the order you chose:
|
|
|
70
67
|
- **Mid-loop drift** (executor returns `DRIFT`): the item moves In Implementation → **Design Validation** **[pending F1]** ([States at a glance](../workitems-process/SKILL.md#states-at-a-glance)), run step 1.3 to 1.6 for that item, then back to In Implementation.
|
|
71
68
|
- **In Review** = reviewer sub-agents in fresh contexts. No PR reviewer, no votes, no PR threads before the merge ([Human gates](../workitems-process/SKILL.md#human-gates)). Findings go to **item comments and the implementation report**. `REQUEST_CHANGES` or `BLOCK` sends the item back. `WATCH` and medium/low are recorded, not blocking.
|
|
72
69
|
- Sub-agents return a report (format in `references/briefs.md`). **You** post it on the item as a comment and keep it for the implementation report.
|
|
73
|
-
- Push the branch (plain
|
|
70
|
+
- Push the branch (plain push, never force) after each item's executor reports `DONE` so that heads are durable for resume. Verifiers, reviewers, the validate lane and the writer record `head_sha` at their start and finish against it; compare each report's `head_sha` with the branch head, and re-run the lane at the new head when the head moved ([Evidence and reports](../workitems-process/SKILL.md#evidence-and-reports), the head rule).
|
|
74
71
|
- A later commit voids earlier test and review evidence for what it touches. The Feature-final check is the evidence the merge relies on.
|
|
75
72
|
|
|
76
73
|
### 3. Feature-final check
|
|
@@ -78,14 +75,14 @@ Per item, in the order you chose:
|
|
|
78
75
|
When every item has been through Build, Test and Review and the tree is clean and pushed:
|
|
79
76
|
|
|
80
77
|
1. Record the final head `H`.
|
|
81
|
-
2. Spawn `verifier` in **Feature-final mode** (every `AC-n` of `requirements.md`, the full suite; in a per-Bug run, the Bug item file's `## Expected behaviour` (EARS) plus `## Behaviour that must not change`, base = the
|
|
78
|
+
2. Spawn `verifier` in **Feature-final mode** (every `AC-n` of `requirements.md`, the full suite; in a per-Bug run, the Bug item file's `## Expected behaviour` (EARS) plus `## Behaviour that must not change`, base = the integration SHA the per-Bug branch was created from) and both review lanes (code, architecture) over the integration..H range.
|
|
82
79
|
3. All pass at `H` → continue. Any finding → fix loop as above (the same 3-round cap, counted per finding), then repeat this step at the new head. Evidence binds to the head it ran at.
|
|
83
80
|
|
|
84
81
|
### 4. Finalize and raise the PR
|
|
85
82
|
|
|
86
83
|
1. Spawn `writer` (`finalize-feature`): docs-impact check against `design.md`, then the **PR body** (five sections) and the **implementation report** content. It returns text; it pushes and writes nothing. Finding `docs-missing` → an executor writes it, then repeat 3.
|
|
87
|
-
2. PR body: the five sections
|
|
88
|
-
3.
|
|
84
|
+
2. PR body: the five sections (Summary, Why, Changes, Test plan, Security impact considered). Title in commit grammar. The branch is the Feature's implementation branch, or the per-Bug branch in a per-Bug run (the `-plan` suffix marks a planning PR and is never used here).
|
|
85
|
+
3. Push the branch. `pr_create_or_update` (action create, target `integration`) with the body and the report content. **The tool does the wiring [pending P1/P2/P4]** ([PR wiring](../workitems-process/references/ado-and-git.md#6-pr-wiring)); you pass nothing for it. Until built, report which parts you could not observe.
|
|
89
86
|
4. Read the PR and the items back: items at Updating Docs, PR linked. A mismatch is reported in the Feature comment, never patched by hand-linking or hand-moving to Done.
|
|
90
87
|
|
|
91
88
|
### 5. CI stage (the last gate)
|
|
@@ -95,9 +92,9 @@ CI is the gate before the merge (ADR-019). Poll `pr_list_or_get` and `build_list
|
|
|
95
92
|
| Result | You do |
|
|
96
93
|
|---|---|
|
|
97
94
|
| Green, PR completes | Read the items back; they should be **Done** with the PR link (V2 not yet verified on a real PR). Never set Done yourself. End `MERGED` |
|
|
98
|
-
| Red | Spawn `executor` (`fix-ci`): smallest fix, no skip, no `\|\| true`, no loosened test. Push. Then `verifier` and `code-reviewer` re-check the **delta** at the new head. Append the round to the implementation report (`pr_create_or_update` update). Cap **3** rounds; past it, post the reason as a Feature comment and change approach. Expired check: scoped `build_run` (pending H1). Integration moved: merge
|
|
95
|
+
| Red | Spawn `executor` (`fix-ci`): smallest fix, no skip, no `\|\| true`, no loosened test. Push. Then `verifier` and `code-reviewer` re-check the **delta** at the new head. Append the round to the implementation report (`pr_create_or_update` update). Cap **3** rounds; past it, post the reason as a Feature comment and change approach. Expired check: scoped `build_run` (pending H1). Integration moved: merge the integration base into the branch |
|
|
99
96
|
| Cancelled or infrastructure failure with log proof | Re-queue only your own PR's validation on the PR's source branch with `build_run` **[pending H1; refusal = report it, stop]**. A failing test or check is never re-queued to get a lucky green |
|
|
100
|
-
| Merge conflict | Executor merges
|
|
97
|
+
| Merge conflict | Executor merges the integration base into the branch (never a force-push), re-runs checks, then the delta is re-verified and re-reviewed as for red |
|
|
101
98
|
|
|
102
99
|
## Board moves (yours)
|
|
103
100
|
|
|
@@ -119,8 +116,8 @@ Never move an item to Approved, Done or Removed. Never move a Feature. `pr_creat
|
|
|
119
116
|
|
|
120
117
|
The trigger holds until the merge, so you may be restarted in the same worktree on the same branch (session died, run ended with the PR open, merge conflict). Do not restart finished work.
|
|
121
118
|
|
|
122
|
-
1. Read every child's board state and its comments (the earlier sub-agent reports are there) **[pending T1: no comment-read tool yet; without it, work from board states
|
|
123
|
-
2.
|
|
119
|
+
1. Read every child's board state and its comments (the earlier sub-agent reports are there) **[pending T1: no comment-read tool yet; without it, work from board states and the PR, and re-run any evidence whose head is stale]**.
|
|
120
|
+
2. The commit log on the implementation branch with `AB#` ids shows which items have commits. `pr_list_or_get` shows whether the PR exists.
|
|
124
121
|
3. PR open: go to step 5. No PR: for each item resume from its board state (In Testing means run Test at the current head; In Review means run Review; and so on). Evidence at an older head is stale: re-run. Per-Bug run: a Bug at In Reproduction gets the `reproduce` brief again (it keeps the commits already made); a Bug at In Fixing or later resumes from its state in the loop.
|
|
125
122
|
4. Reuse the branch and the PR. Never recreate either.
|
|
126
123
|
|
|
@@ -128,7 +125,7 @@ The trigger holds until the merge, so you may be restarted in the same worktree
|
|
|
128
125
|
|
|
129
126
|
| Verdict | When |
|
|
130
127
|
|---|---|
|
|
131
|
-
| `MERGED` | PR completed
|
|
128
|
+
| `MERGED` | PR completed |
|
|
132
129
|
| `PR_OPEN` | PR raised and CI not finished when the session must end; the next tick resumes at step 5 |
|
|
133
130
|
|
|
134
131
|
## Stop rules
|