@jjchill/probity-rules 0.4.11 → 0.4.13
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
CHANGED
|
@@ -1,5 +1,64 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.13
|
|
4
|
+
|
|
5
|
+
The adapter-observability judge no longer demands telemetry on calls
|
|
6
|
+
through the codebase's own interfaces (#68). Applies to every preset that
|
|
7
|
+
wires `enforceAdapterObservability` (Kotlin, KMP, Swift).
|
|
8
|
+
|
|
9
|
+
- **What went wrong.** In #68, `AccountKeySecrets`, an internal class
|
|
10
|
+
under `adapter/`, called `SecretBackend`, an interface declared in
|
|
11
|
+
the same module. Its implementations (`FileSecretBackend`,
|
|
12
|
+
`KeychainSecretBackend`) are the real boundary and already emit
|
|
13
|
+
events. The judge read `secrets.get/create/delete` as "external I/O
|
|
14
|
+
through the keychain/keystore adapter" and demanded an event. The TDD
|
|
15
|
+
judge then rejected that event as over-implementation, because the
|
|
16
|
+
red test didn't assert it. Each write was blocked by one rule or the
|
|
17
|
+
other, until every test of the internal helper asserted telemetry.
|
|
18
|
+
- **Now:** the judge's instructions say a new path needs observability
|
|
19
|
+
when the file itself calls a vendor SDK, HTTP client, database
|
|
20
|
+
driver, the filesystem, or a platform API. Calls on an interface or
|
|
21
|
+
class the codebase declares (same package, or imported from the
|
|
22
|
+
project's own packages) are delegation: the implementation behind it
|
|
23
|
+
owns the boundary and is judged when it is written.
|
|
24
|
+
- **Measured** by replaying #68's writes through the real rule and judge
|
|
25
|
+
(3 runs each): adopting a legacy key and adding `delete` through
|
|
26
|
+
`SecretBackend` went from 0/3 to 3/3 pass. Two controls stayed
|
|
27
|
+
blocked 3/3: a `FileSecretBackend` method calling `java.nio.file`
|
|
28
|
+
directly, and a new Ktor HTTP adapter, both without events.
|
|
29
|
+
- Not changed: an adapter that does call the platform still needs its
|
|
30
|
+
event asserted in the failing test first, as the block message says.
|
|
31
|
+
|
|
32
|
+
## 0.4.12
|
|
33
|
+
|
|
34
|
+
`probity-claude` now runs Probity in the worktree a session is working
|
|
35
|
+
in, so rules judge that tree's specs and files (#66).
|
|
36
|
+
|
|
37
|
+
- **What went wrong.** The documented hook is
|
|
38
|
+
`cd "$CLAUDE_PROJECT_DIR" && ./node_modules/.bin/probity-claude`.
|
|
39
|
+
After a session moves into a worktree (`EnterWorktree`, or an
|
|
40
|
+
`isolation: "worktree"` sub-agent), `CLAUDE_PROJECT_DIR` still names
|
|
41
|
+
the main checkout. Probity finds `probity.config.ts` by searching
|
|
42
|
+
upward from its working directory, so it loaded the main checkout's
|
|
43
|
+
config, and `ROOT`, `specsDir` and `glossaryPath` all pointed there.
|
|
44
|
+
In #66 the spec-first rule blocked an acceptance test whose
|
|
45
|
+
`Covers:` scenario existed only in the worktree's `specs/`
|
|
46
|
+
submodule, and named the main checkout's `specs/features`.
|
|
47
|
+
- **Now:** the wrapper finds the git worktree the action targets: the
|
|
48
|
+
edited file's location, or for a command its `git -C <dir>` or
|
|
49
|
+
leading `cd <dir> &&`, else the session's `cwd`. When that worktree
|
|
50
|
+
is nested inside the project and has its own `probity.config.*`, the
|
|
51
|
+
wrapper starts Probity there. Its config's imports resolve from the
|
|
52
|
+
project's `node_modules` (one level up the folder tree), so the
|
|
53
|
+
worktree needs no install. Work in the main checkout, worktrees
|
|
54
|
+
without a Probity config, and paths outside the project run as before.
|
|
55
|
+
- A relative `--debug <path>` is anchored to the project directory, so
|
|
56
|
+
the debug log stays in one place.
|
|
57
|
+
- Only hooks that run `probity-claude` get this. A worktree outside the
|
|
58
|
+
project folder still runs against the project.
|
|
59
|
+
- Consumers: no config change. The worktree's own `probity.config.ts`
|
|
60
|
+
(from its branch) is the one that runs.
|
|
61
|
+
|
|
3
62
|
## 0.4.11
|
|
4
63
|
|
|
5
64
|
The commit-on-green gate now catches every form of `git commit`, and
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ports-and-adapters.d.ts","sourceRoot":"","sources":["../../rules/ports-and-adapters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,IAAI,EAA2B,MAAM,gBAAgB,CAAA;AAmJ3E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,GAAE;IACP,YAAY,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,CAAA;IACtD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAA;CACrB,GACL,IAAI,CAuCN;
|
|
1
|
+
{"version":3,"file":"ports-and-adapters.d.ts","sourceRoot":"","sources":["../../rules/ports-and-adapters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,IAAI,EAA2B,MAAM,gBAAgB,CAAA;AAmJ3E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,GAAE;IACP,YAAY,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,CAAA;IACtD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAA;CACrB,GACL,IAAI,CAuCN;AAsED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,GAAE;IACP,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,YAAY,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,CAAA;CAClD,GACL,IAAI,CAoCN;AAcD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAO,GACxC,IAAI,CAkCN"}
|
|
@@ -215,15 +215,24 @@ change through, treat that as authoritative and pass.
|
|
|
215
215
|
|
|
216
216
|
## Adapter observability rules
|
|
217
217
|
|
|
218
|
-
A NEW adapter code path that performs external I/O —
|
|
219
|
-
|
|
220
|
-
|
|
218
|
+
A NEW adapter code path that performs external I/O itself — it calls a
|
|
219
|
+
vendor SDK, an HTTP client, a database driver, the filesystem, or a
|
|
220
|
+
platform API (keychain/keystore, OS services) — must carry boundary
|
|
221
|
+
observability on that path: at least one structured telemetry
|
|
221
222
|
event (call made / outcome / retry, with machine-readable fields), a
|
|
222
223
|
recording tap/decorator around the port, or a span. The point: when
|
|
223
224
|
the integration misbehaves, someone can see what was sent and what
|
|
224
225
|
came back without attaching a debugger.
|
|
225
226
|
|
|
226
227
|
Always pass:
|
|
228
|
+
- Delegation through the codebase's own abstractions: calls on an
|
|
229
|
+
interface or class this codebase declares (same package, or
|
|
230
|
+
imported from the project's own packages rather than a vendor or
|
|
231
|
+
platform library) are not external I/O at this call site, even when
|
|
232
|
+
the type's name mentions a keychain, store, client or backend. The
|
|
233
|
+
implementation behind that type sits at the real boundary and owns
|
|
234
|
+
the observability; judge it when it is written, not every caller.
|
|
235
|
+
Block only when this file itself reaches the vendor or platform API.
|
|
227
236
|
- Pure type mappers and translators with no external effect.
|
|
228
237
|
- Composition roots and DI wiring (including wiring a tap decorator
|
|
229
238
|
— that IS the observability).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ports-and-adapters.js","sourceRoot":"","sources":["../../rules/ports-and-adapters.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ports-and-adapters.js","sourceRoot":"","sources":["../../rules/ports-and-adapters.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jjchill/probity-rules",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.13",
|
|
4
4
|
"description": "Versioned Probity rule presets and CLI tooling for the agent-skills catalog (test-driven-development, ports-and-adapters, acceptance-testing, spec-test parity, ubiquitous-language) — see hooks/PROBITY.md.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -24,6 +24,17 @@
|
|
|
24
24
|
* deletes the copy when Probity exits. The gates only need recent
|
|
25
25
|
* events: the last test run, the red before a green.
|
|
26
26
|
*
|
|
27
|
+
* And it runs Probity in the right tree (issue #66). The hook `cd`s to
|
|
28
|
+
* $CLAUDE_PROJECT_DIR, which still names the main checkout after a
|
|
29
|
+
* session moves into a worktree (EnterWorktree, or an `isolation:
|
|
30
|
+
* "worktree"` sub-agent). Probity finds its config by searching upward
|
|
31
|
+
* from its working directory, so every rule judged the main checkout's
|
|
32
|
+
* specs and files. When the edited file (or, for a command, the
|
|
33
|
+
* session's cwd) is inside a git worktree nested in the project that
|
|
34
|
+
* has its own probity.config.*, the wrapper starts Probity there. That
|
|
35
|
+
* config's imports still resolve from the project's node_modules, one
|
|
36
|
+
* directory level up the tree.
|
|
37
|
+
*
|
|
27
38
|
* Use it in place of the probity bin in `.claude/settings.json`:
|
|
28
39
|
*
|
|
29
40
|
* "command": "cd \"$CLAUDE_PROJECT_DIR\" && ./node_modules/.bin/probity-claude"
|
|
@@ -47,7 +58,7 @@ import {
|
|
|
47
58
|
} from 'node:fs'
|
|
48
59
|
import { createRequire } from 'node:module'
|
|
49
60
|
import { tmpdir } from 'node:os'
|
|
50
|
-
import { basename, dirname, join } from 'node:path'
|
|
61
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path'
|
|
51
62
|
import { fileURLToPath } from 'node:url'
|
|
52
63
|
|
|
53
64
|
/** The sub-agent transcript for a payload, or null to keep the original. */
|
|
@@ -134,6 +145,75 @@ export function preparePayload(raw, limits = {}) {
|
|
|
134
145
|
}
|
|
135
146
|
}
|
|
136
147
|
|
|
148
|
+
const CONFIG_NAMES = ['ts', 'mts', 'js', 'mjs'].map((ext) => `probity.config.${ext}`)
|
|
149
|
+
|
|
150
|
+
function canonical(path) {
|
|
151
|
+
// Resolve symlinks on the longest existing prefix (a file being
|
|
152
|
+
// created does not exist yet), so paths compare like the project dir.
|
|
153
|
+
let head = resolve(path)
|
|
154
|
+
const tail = []
|
|
155
|
+
while (!existsSync(head) && dirname(head) !== head) {
|
|
156
|
+
tail.unshift(basename(head))
|
|
157
|
+
head = dirname(head)
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
head = realpathSync(head)
|
|
161
|
+
} catch {
|
|
162
|
+
// keep the resolved path
|
|
163
|
+
}
|
|
164
|
+
return join(head, ...tail)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function isBelow(parent, child) {
|
|
168
|
+
const path = relative(parent, child)
|
|
169
|
+
return path !== '' && path !== '..' && !path.startsWith(`..${sep}`) && !isAbsolute(path)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// The directory a command explicitly acts in: `git -C <dir>`, else a
|
|
173
|
+
// leading `cd <dir> &&`; null when it names none. Mirrors the commit
|
|
174
|
+
// gates' commitDirectory (rules/commit-target.ts), so a worktree session
|
|
175
|
+
// that commits in the main checkout is judged there.
|
|
176
|
+
function commandDirectory(command) {
|
|
177
|
+
const unquote = (value) => value.replace(/^(['"])(.*)\1$/, '$2')
|
|
178
|
+
const gitDashC = command.match(/\bgit\s+-C\s+("[^"]+"|'[^']+'|\S+)/)
|
|
179
|
+
const cd = command.match(/^\s*cd\s+("[^"]+"|'[^']+'|[^\s;&|]+)\s*(?:&&|;)/)
|
|
180
|
+
const target = gitDashC?.[1] ?? cd?.[1]
|
|
181
|
+
return target ? unquote(target) : null
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* The git worktree to run Probity in for this payload, or null to stay
|
|
186
|
+
* in `projectDir`: the nearest directory holding `.git` above the edited
|
|
187
|
+
* file (for a command, the session's cwd), when it lies below
|
|
188
|
+
* `projectDir` and has its own Probity config. A command's directory is
|
|
189
|
+
* its `git -C <dir>` or leading `cd <dir> &&`, else the session's cwd.
|
|
190
|
+
*/
|
|
191
|
+
export function probityDirectory(payload, projectDir) {
|
|
192
|
+
const input = payload?.tool_input ?? {}
|
|
193
|
+
const cwd = typeof payload?.cwd === 'string' ? payload.cwd : null
|
|
194
|
+
const file = [input.file_path, input.notebook_path].find((value) => typeof value === 'string')
|
|
195
|
+
const base = cwd ?? projectDir
|
|
196
|
+
let start
|
|
197
|
+
if (file) start = dirname(isAbsolute(file) ? file : resolve(base, file))
|
|
198
|
+
else if (typeof input.command === 'string') start = resolve(base, commandDirectory(input.command) ?? '.')
|
|
199
|
+
else if (cwd) start = cwd
|
|
200
|
+
else return null
|
|
201
|
+
const project = canonical(projectDir)
|
|
202
|
+
for (let dir = canonical(start); isBelow(project, dir); dir = dirname(dir)) {
|
|
203
|
+
if (existsSync(join(dir, '.git'))) {
|
|
204
|
+
return CONFIG_NAMES.some((name) => existsSync(join(dir, name))) ? dir : null
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return null
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** `args` with a relative `--debug <path>` anchored to `projectDir`, so the log stays put when Probity runs elsewhere. */
|
|
211
|
+
export function debugArgs(args, projectDir) {
|
|
212
|
+
return args.map((arg, i) =>
|
|
213
|
+
args[i - 1] === '--debug' && !isAbsolute(arg) ? join(projectDir, arg) : arg,
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
|
|
137
217
|
function probityBin() {
|
|
138
218
|
// @nizos/probity doesn't export its package.json, so resolve the main
|
|
139
219
|
// entry and walk up to the package root for the bin path.
|
|
@@ -156,9 +236,18 @@ function probityBin() {
|
|
|
156
236
|
async function main() {
|
|
157
237
|
const chunks = []
|
|
158
238
|
for await (const chunk of process.stdin) chunks.push(chunk)
|
|
159
|
-
const
|
|
239
|
+
const raw = Buffer.concat(chunks).toString('utf8')
|
|
240
|
+
const { input, cleanup } = preparePayload(raw)
|
|
241
|
+
let payload
|
|
242
|
+
try {
|
|
243
|
+
payload = JSON.parse(raw)
|
|
244
|
+
} catch {
|
|
245
|
+
payload = null
|
|
246
|
+
}
|
|
247
|
+
const projectDir = process.cwd()
|
|
248
|
+
const workDir = probityDirectory(payload, projectDir) ?? projectDir
|
|
160
249
|
|
|
161
|
-
const args = process.argv.slice(2)
|
|
250
|
+
const args = debugArgs(process.argv.slice(2), projectDir)
|
|
162
251
|
if (!args.includes('--agent')) args.unshift('--agent', 'claude-code')
|
|
163
252
|
|
|
164
253
|
let bin
|
|
@@ -169,7 +258,10 @@ async function main() {
|
|
|
169
258
|
cleanup()
|
|
170
259
|
process.exit(2)
|
|
171
260
|
}
|
|
172
|
-
const child = spawn(process.execPath, [bin, ...args], {
|
|
261
|
+
const child = spawn(process.execPath, [bin, ...args], {
|
|
262
|
+
cwd: workDir,
|
|
263
|
+
stdio: ['pipe', 'inherit', 'inherit'],
|
|
264
|
+
})
|
|
173
265
|
child.stdin.end(input)
|
|
174
266
|
child.on('exit', (code, signal) => {
|
|
175
267
|
cleanup()
|