@codyswann/lisa 2.300.7 → 2.300.9
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/dist/cli/doctor-readiness-domain.d.ts.map +1 -1
- package/dist/cli/doctor-readiness-domain.js +23 -14
- package/dist/cli/doctor-readiness-domain.js.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +10 -10
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/hooks/threshold-ratchet-compare.mjs +1 -27
- package/plugins/lisa/hooks/threshold-ratchet-families.mjs +0 -34
- package/plugins/lisa/hooks/threshold-ratchet.mjs +5 -2
- package/plugins/lisa/hooks/threshold-ratchet.sh +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/hooks/threshold-ratchet-compare.mjs +1 -27
- package/plugins/lisa-copilot/hooks/threshold-ratchet-families.mjs +0 -34
- package/plugins/lisa-copilot/hooks/threshold-ratchet.mjs +5 -2
- package/plugins/lisa-copilot/hooks/threshold-ratchet.sh +1 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/threshold-ratchet-compare.mjs +1 -27
- package/plugins/lisa-cursor/hooks/threshold-ratchet-families.mjs +0 -34
- package/plugins/lisa-cursor/hooks/threshold-ratchet.mjs +5 -2
- package/plugins/lisa-cursor/hooks/threshold-ratchet.sh +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/hooks/threshold-ratchet-compare.mjs +1 -27
- package/plugins/src/base/hooks/threshold-ratchet-families.mjs +0 -34
- package/plugins/src/base/hooks/threshold-ratchet.mjs +5 -2
- package/plugins/src/base/hooks/threshold-ratchet.sh +1 -1
- package/rails/copy-overwrite/scripts/check-threshold-ratchet.mjs +5 -2
- package/rails/copy-overwrite/scripts/threshold-ratchet-compare.mjs +1 -27
- package/rails/copy-overwrite/scripts/threshold-ratchet-families.mjs +0 -34
- package/typescript/copy-overwrite/scripts/check-threshold-ratchet.mjs +5 -2
- package/typescript/copy-overwrite/scripts/threshold-ratchet-compare.mjs +1 -27
- package/typescript/copy-overwrite/scripts/threshold-ratchet-families.mjs +0 -34
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor-readiness-domain.d.ts","sourceRoot":"","sources":["../../src/cli/doctor-readiness-domain.ts"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EACL,KAAK,cAAc,EAIpB,MAAM,iCAAiC,CAAC;AAEzC,6EAA6E;AAC7E,eAAO,MAAM,6BAA6B,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"doctor-readiness-domain.d.ts","sourceRoot":"","sources":["../../src/cli/doctor-readiness-domain.ts"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EACL,KAAK,cAAc,EAIpB,MAAM,iCAAiC,CAAC;AAEzC,6EAA6E;AAC7E,eAAO,MAAM,6BAA6B,qBAAqB,CAAC;AAuNhE;;;;;;;;;;GAUG;AACH,wBAAsB,8BAA8B,CAClD,IAAI,EAAE,MAAM,EACZ,eAAe,CAAC,EAAE,SAAS,cAAc,EAAE,GAC1C,OAAO,CAAC,wBAAwB,CAAC,CA2BnC"}
|
|
@@ -35,6 +35,11 @@ export const DOMAIN_OWNERSHIP_DIMENSION_ID = "domain-ownership";
|
|
|
35
35
|
const DOMAIN_BLOCKER_ID = "B1";
|
|
36
36
|
/** Most destructive commands quoted in one finding before summarizing. */
|
|
37
37
|
const MAX_EVIDENCE_COMMANDS = 10;
|
|
38
|
+
/** Shared tail for SKIP reasons that cannot establish domain ownership offline. */
|
|
39
|
+
const DOMAIN_OWNERSHIP_OFFLINE_TAIL = "Whether this repository's business rules, glossary, and danger zones are " +
|
|
40
|
+
"genuinely owned and written down cannot be established by reading files " +
|
|
41
|
+
"offline — that needs the agent-ready domain phase — so domain ownership is " +
|
|
42
|
+
"not established either way.";
|
|
38
43
|
/**
|
|
39
44
|
* Commands that irreversibly destroy stored data. Every entry is a whole
|
|
40
45
|
* operation, not a keyword: `rm -rf` is deliberately absent because build
|
|
@@ -139,27 +144,27 @@ function domainFinding(scan) {
|
|
|
139
144
|
* operator can tell "nothing to report" from "never looked".
|
|
140
145
|
* @param inspected - How many workflow files were read
|
|
141
146
|
* @param runbook - Whether a runbook is checked in
|
|
147
|
+
* @param recoverableDestructiveOps - Ungated destructive commands cleared by a runbook
|
|
142
148
|
* @returns The SKIP reason
|
|
143
149
|
*/
|
|
144
|
-
function skipReason(inspected, runbook) {
|
|
150
|
+
function skipReason(inspected, runbook, recoverableDestructiveOps) {
|
|
145
151
|
if (inspected === 0) {
|
|
146
152
|
return ("No GitHub Actions workflow files were found under `.github/workflows/`, " +
|
|
147
153
|
"so no irreversible data-destroying operations could be assessed from " +
|
|
148
154
|
"workflow declarations (recovery runbook " +
|
|
149
|
-
`${runbook ? "present" : "absent"}).
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
155
|
+
`${runbook ? "present" : "absent"}). ${DOMAIN_OWNERSHIP_OFFLINE_TAIL}`);
|
|
156
|
+
}
|
|
157
|
+
if (runbook && recoverableDestructiveOps > 0) {
|
|
158
|
+
return (`Read ${inspected} workflow file(s) and found ` +
|
|
159
|
+
`${recoverableDestructiveOps} irreversible data-destroying operation(s) ` +
|
|
160
|
+
"that this file alone proves can run unattended and ungated, but the " +
|
|
161
|
+
"repository checks in a recovery runbook, so the no-way-back half of B1 " +
|
|
162
|
+
`is not established (recovery runbook present). ${DOMAIN_OWNERSHIP_OFFLINE_TAIL}`);
|
|
154
163
|
}
|
|
155
164
|
return (`Read ${inspected} workflow file(s) for irreversible data-destroying ` +
|
|
156
165
|
"operations and found none that this file alone proves runs unattended, " +
|
|
157
166
|
`ungated, and with no way back (recovery runbook ` +
|
|
158
|
-
`${runbook ? "present" : "absent"}).
|
|
159
|
-
"repository's business rules, glossary, and danger zones are genuinely " +
|
|
160
|
-
"owned and written down cannot be established by reading files offline — " +
|
|
161
|
-
"that needs the agent-ready domain phase — so domain ownership is not " +
|
|
162
|
-
"established either way.");
|
|
167
|
+
`${runbook ? "present" : "absent"}). ${DOMAIN_OWNERSHIP_OFFLINE_TAIL}`);
|
|
163
168
|
}
|
|
164
169
|
/**
|
|
165
170
|
* Build the SKIP record, carrying every guarded destructive operation as an
|
|
@@ -169,14 +174,18 @@ function skipReason(inspected, runbook) {
|
|
|
169
174
|
* @param scan - The destructive-operation scan
|
|
170
175
|
* @param inspected - How many workflow files were read
|
|
171
176
|
* @param runbook - Whether a runbook is checked in
|
|
177
|
+
* @param recoverableDestructiveOps - Ungated destructive commands cleared by a runbook
|
|
172
178
|
* @returns The SKIP dimension record
|
|
173
179
|
*/
|
|
174
|
-
function skipRecord(guarded, scan, inspected, runbook) {
|
|
180
|
+
function skipRecord(guarded, scan, inspected, runbook, recoverableDestructiveOps = 0) {
|
|
175
181
|
return {
|
|
176
182
|
id: DOMAIN_OWNERSHIP_DIMENSION_ID,
|
|
177
183
|
status: "SKIP",
|
|
178
184
|
findings: [
|
|
179
|
-
{
|
|
185
|
+
{
|
|
186
|
+
reason: skipReason(inspected, runbook, recoverableDestructiveOps),
|
|
187
|
+
skip: true,
|
|
188
|
+
},
|
|
180
189
|
...informationalFindings([
|
|
181
190
|
...guarded.map(entry => `\`${entry.workflow}\` job \`${entry.jobId}\` runs a destructive ` +
|
|
182
191
|
`operation (\`${entry.command}\`) behind a gate, beside its own ` +
|
|
@@ -208,7 +217,7 @@ export async function assessDomainOwnershipDimension(root, parsedWorkflows) {
|
|
|
208
217
|
// B1 for the whole repository — the blocker asks for BOTH halves at once.
|
|
209
218
|
const recoverable = await hasCheckedInRunbook(root);
|
|
210
219
|
if (scan.ungated.length === 0 || recoverable) {
|
|
211
|
-
return skipRecord(recoverable ? [...scan.gated, ...scan.ungated] : scan.gated, scan, workflows.length, recoverable);
|
|
220
|
+
return skipRecord(recoverable ? [...scan.gated, ...scan.ungated] : scan.gated, scan, workflows.length, recoverable, recoverable ? scan.ungated.length : 0);
|
|
212
221
|
}
|
|
213
222
|
return {
|
|
214
223
|
id: DOMAIN_OWNERSHIP_DIMENSION_ID,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor-readiness-domain.js","sourceRoot":"","sources":["../../src/cli/doctor-readiness-domain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,EACL,0BAA0B,EAC1B,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EAEd,YAAY,GAEb,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAErE,OAAO,EAGL,wBAAwB,GAEzB,MAAM,iCAAiC,CAAC;AAEzC,6EAA6E;AAC7E,MAAM,CAAC,MAAM,6BAA6B,GAAG,kBAAkB,CAAC;AAEhE,0EAA0E;AAC1E,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAE/B,0EAA0E;AAC1E,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAEjC;;;;;GAKG;AACH,MAAM,qBAAqB,GAAsB;IAC/C,6BAA6B;IAC7B,6CAA6C;IAC7C,kCAAkC;IAClC,wCAAwC;IACxC,uCAAuC;IACvC,yBAAyB;IACzB,+CAA+C;IAC/C,sBAAsB;IACtB,oBAAoB;IACpB,mBAAmB;IACnB,sBAAsB;IACtB,sCAAsC;IACtC,kCAAkC;IAClC,mCAAmC;IACnC,2EAA2E;CAC5E,CAAC;AAEF,8EAA8E;AAC9E,MAAM,iBAAiB,GAAsB;IAC3C,aAAa;IACb,eAAe;IACf,YAAY;IACZ,cAAc;IACd,qBAAqB;CACtB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,SAAS,YAAY,CACnB,OAAe,EACf,GAAsB,EACtB,IAAwB;IAExB,OAAO,CACL,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC5D,CAAC,cAAc,CAAC,GAAG,OAAO,KAAK,IAAI,CAAC,GAAG,KAAK,GAAG,CAAC,GAAG,EAAE,CAAC,CACvD,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,gBAAgB,CACvB,SAAyB,EACzB,GAAsB,EACtB,IAAwB;IAExB,IAAI,CAAC,0BAA0B,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC;QAC3C,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,CACL,SAAS,GAAG,CAAC,EAAE,8DAA8D;QAC7E,+BAA+B,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,uBAAuB;QAC7E,yEAAyE;QACzE,oCAAoC,CACrC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,cAAc,CAAC,GAAsB;IAC5C,OAAO,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC3B,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CACxE,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,aAAa,CAAC,IAAmB;IACxC,OAAO;QACL,OAAO,EAAE,iBAAiB;QAC1B,kBAAkB,EAChB,yEAAyE;YACzE,wBAAwB;QAC1B,QAAQ,EAAE,gBAAgB,CAAC,IAAI,CAAC,OAAO,EAAE,qBAAqB,CAAC;QAC/D,gBAAgB,EACd,0EAA0E;YAC1E,sEAAsE;YACtE,gDAAgD;QAClD,eAAe,EACb,uEAAuE;YACvE,uEAAuE;YACvE,gEAAgE;QAClE,mBAAmB,EAAE;YACnB,wEAAwE;gBACtE,wDAAwD;SAC3D;KACF,CAAC;AACJ,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"doctor-readiness-domain.js","sourceRoot":"","sources":["../../src/cli/doctor-readiness-domain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,EACL,0BAA0B,EAC1B,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EAEd,YAAY,GAEb,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAErE,OAAO,EAGL,wBAAwB,GAEzB,MAAM,iCAAiC,CAAC;AAEzC,6EAA6E;AAC7E,MAAM,CAAC,MAAM,6BAA6B,GAAG,kBAAkB,CAAC;AAEhE,0EAA0E;AAC1E,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAE/B,0EAA0E;AAC1E,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAEjC,mFAAmF;AACnF,MAAM,6BAA6B,GACjC,2EAA2E;IAC3E,0EAA0E;IAC1E,6EAA6E;IAC7E,6BAA6B,CAAC;AAEhC;;;;;GAKG;AACH,MAAM,qBAAqB,GAAsB;IAC/C,6BAA6B;IAC7B,6CAA6C;IAC7C,kCAAkC;IAClC,wCAAwC;IACxC,uCAAuC;IACvC,yBAAyB;IACzB,+CAA+C;IAC/C,sBAAsB;IACtB,oBAAoB;IACpB,mBAAmB;IACnB,sBAAsB;IACtB,sCAAsC;IACtC,kCAAkC;IAClC,mCAAmC;IACnC,2EAA2E;CAC5E,CAAC;AAEF,8EAA8E;AAC9E,MAAM,iBAAiB,GAAsB;IAC3C,aAAa;IACb,eAAe;IACf,YAAY;IACZ,cAAc;IACd,qBAAqB;CACtB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,SAAS,YAAY,CACnB,OAAe,EACf,GAAsB,EACtB,IAAwB;IAExB,OAAO,CACL,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC5D,CAAC,cAAc,CAAC,GAAG,OAAO,KAAK,IAAI,CAAC,GAAG,KAAK,GAAG,CAAC,GAAG,EAAE,CAAC,CACvD,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,gBAAgB,CACvB,SAAyB,EACzB,GAAsB,EACtB,IAAwB;IAExB,IAAI,CAAC,0BAA0B,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC;QAC3C,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,CACL,SAAS,GAAG,CAAC,EAAE,8DAA8D;QAC7E,+BAA+B,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,uBAAuB;QAC7E,yEAAyE;QACzE,oCAAoC,CACrC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,cAAc,CAAC,GAAsB;IAC5C,OAAO,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC3B,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CACxE,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,aAAa,CAAC,IAAmB;IACxC,OAAO;QACL,OAAO,EAAE,iBAAiB;QAC1B,kBAAkB,EAChB,yEAAyE;YACzE,wBAAwB;QAC1B,QAAQ,EAAE,gBAAgB,CAAC,IAAI,CAAC,OAAO,EAAE,qBAAqB,CAAC;QAC/D,gBAAgB,EACd,0EAA0E;YAC1E,sEAAsE;YACtE,gDAAgD;QAClD,eAAe,EACb,uEAAuE;YACvE,uEAAuE;YACvE,gEAAgE;QAClE,mBAAmB,EAAE;YACnB,wEAAwE;gBACtE,wDAAwD;SAC3D;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,UAAU,CACjB,SAAiB,EACjB,OAAgB,EAChB,yBAAiC;IAEjC,IAAI,SAAS,KAAK,CAAC,EAAE,CAAC;QACpB,OAAO,CACL,0EAA0E;YAC1E,uEAAuE;YACvE,0CAA0C;YAC1C,GAAG,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,MAAM,6BAA6B,EAAE,CACvE,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,IAAI,yBAAyB,GAAG,CAAC,EAAE,CAAC;QAC7C,OAAO,CACL,QAAQ,SAAS,8BAA8B;YAC/C,GAAG,yBAAyB,6CAA6C;YACzE,sEAAsE;YACtE,yEAAyE;YACzE,kDAAkD,6BAA6B,EAAE,CAClF,CAAC;IACJ,CAAC;IACD,OAAO,CACL,QAAQ,SAAS,qDAAqD;QACtE,yEAAyE;QACzE,kDAAkD;QAClD,GAAG,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,MAAM,6BAA6B,EAAE,CACvE,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,UAAU,CACjB,OAAkC,EAClC,IAAmB,EACnB,SAAiB,EACjB,OAAgB,EAChB,yBAAyB,GAAG,CAAC;IAE7B,OAAO;QACL,EAAE,EAAE,6BAA6B;QACjC,MAAM,EAAE,MAAM;QACd,QAAQ,EAAE;YACR;gBACE,MAAM,EAAE,UAAU,CAAC,SAAS,EAAE,OAAO,EAAE,yBAAyB,CAAC;gBACjE,IAAI,EAAE,IAAI;aACX;YACD,GAAG,qBAAqB,CAAC;gBACvB,GAAG,OAAO,CAAC,GAAG,CACZ,KAAK,CAAC,EAAE,CACN,KAAK,KAAK,CAAC,QAAQ,YAAY,KAAK,CAAC,KAAK,wBAAwB;oBAClE,gBAAgB,KAAK,CAAC,OAAO,oCAAoC;oBACjE,mEAAmE;oBACnE,+CAA+C,CAClD;gBACD,GAAG,IAAI,CAAC,UAAU;aACnB,CAAC;SACH;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAClD,IAAY,EACZ,eAA2C;IAE3C,MAAM,SAAS,GACb,eAAe,IAAI,CAAC,MAAM,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5D,MAAM,IAAI,GAAG,YAAY,CAAC,SAAS,EAAE,YAAY,EAAE;QACjD,SAAS,EAAE,cAAc;QACzB,cAAc,EAAE,gBAAgB;KACjC,CAAC,CAAC;IACH,6EAA6E;IAC7E,0EAA0E;IAC1E,MAAM,WAAW,GAAG,MAAM,mBAAmB,CAAC,IAAI,CAAC,CAAC;IACpD,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,WAAW,EAAE,CAAC;QAC7C,OAAO,UAAU,CACf,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,EAC3D,IAAI,EACJ,SAAS,CAAC,MAAM,EAChB,WAAW,EACX,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CACtC,CAAC;IACJ,CAAC;IACD,OAAO;QACL,EAAE,EAAE,6BAA6B;QACjC,4EAA4E;QAC5E,yEAAyE;QACzE,6EAA6E;QAC7E,MAAM,EAAE,MAAM;QACd,QAAQ,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,GAAG,qBAAqB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;KAC3E,CAAC;AACJ,CAAC"}
|
|
@@ -299,10 +299,10 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
|
|
|
299
299
|
"plugins/src/base/hooks/parity-safety-net.sh": "062c69eea85157f1e941ec3626d8912aa9f182af6ccdbe19687588a6074db5ce",
|
|
300
300
|
"plugins/src/base/hooks/setup-jira-cli.sh": "a675f6b17ce7f0d6b9fb7daeba6d2bc59bcb48ef79b034076ea73b2b1e72ee09",
|
|
301
301
|
"plugins/src/base/hooks/shell-write-nudge.sh": "69839af423f8792b1e52c71097316c3264425553031627c0a2f9409a9d5becd0",
|
|
302
|
-
"plugins/src/base/hooks/threshold-ratchet-compare.mjs": "
|
|
303
|
-
"plugins/src/base/hooks/threshold-ratchet-families.mjs": "
|
|
304
|
-
"plugins/src/base/hooks/threshold-ratchet.mjs": "
|
|
305
|
-
"plugins/src/base/hooks/threshold-ratchet.sh": "
|
|
302
|
+
"plugins/src/base/hooks/threshold-ratchet-compare.mjs": "4535a145c5c5cbee13bdf970bc91d390a264043604e607288cdd6bf28de56192",
|
|
303
|
+
"plugins/src/base/hooks/threshold-ratchet-families.mjs": "db14245ed89b483912af287bdee917a7a921dc21bb29b49be37fc799c05c413f",
|
|
304
|
+
"plugins/src/base/hooks/threshold-ratchet.mjs": "04f660131bf50864239eb09cbf4d9555e53921254f6ab45ff2b85c173e160901",
|
|
305
|
+
"plugins/src/base/hooks/threshold-ratchet.sh": "b86f4d0b554e44fd119ad8a8920cd0ba6c9dbe779f7ee219d381cf0629453990",
|
|
306
306
|
"plugins/src/base/hooks/ticket-sync-reminder.sh": "212f80bcdfd9e3ec2254987c5a7186d1d2d38065b330ddcdc631f76df7c4a6c0",
|
|
307
307
|
"plugins/src/base/hooks/track-plan-sessions.sh": "8bc47dcad3aed628df0c9efbe7b6f3df87635b94b0c54c71e0bd52eceace563a",
|
|
308
308
|
"plugins/src/base/rules/eager/automation-runbook-contract.md": "b9b4f4d82b366959e90451a2b425ac116953ac074ab6a192dc3909ec8a2be1d6",
|
|
@@ -884,10 +884,10 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
|
|
|
884
884
|
"rails/copy-overwrite/ast-grep/utils/.gitkeep": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
|
885
885
|
"rails/copy-overwrite/config/initializers/version.rb": "fa664a84f407877b5ddbd3709a2352b30e4dc6fbce538a8503bad6c52448dc86",
|
|
886
886
|
"rails/copy-overwrite/lefthook.yml": "28bb382b9171a04fb92ebafc1f1522b8639f5edc4fcffcc6940e7fba3460fb4e",
|
|
887
|
-
"rails/copy-overwrite/scripts/check-threshold-ratchet.mjs": "
|
|
887
|
+
"rails/copy-overwrite/scripts/check-threshold-ratchet.mjs": "04f660131bf50864239eb09cbf4d9555e53921254f6ab45ff2b85c173e160901",
|
|
888
888
|
"rails/copy-overwrite/scripts/lisa-clean-git-env.sh": "e7121a0ee9e1bf7c01cd2ab55f563dd6d9ab75990739bb6ddf571a513efa10e9",
|
|
889
|
-
"rails/copy-overwrite/scripts/threshold-ratchet-compare.mjs": "
|
|
890
|
-
"rails/copy-overwrite/scripts/threshold-ratchet-families.mjs": "
|
|
889
|
+
"rails/copy-overwrite/scripts/threshold-ratchet-compare.mjs": "4535a145c5c5cbee13bdf970bc91d390a264043604e607288cdd6bf28de56192",
|
|
890
|
+
"rails/copy-overwrite/scripts/threshold-ratchet-families.mjs": "db14245ed89b483912af287bdee917a7a921dc21bb29b49be37fc799c05c413f",
|
|
891
891
|
"rails/copy-overwrite/sgconfig.yml": "0ffe058c791d4d323f98104cf78c763bb0356d88739042060ceb86b6b1cb78d7",
|
|
892
892
|
"rails/create-only/.github/workflows/ci.yml": "599805266a47b5c0039145a11df225d4db6c59d35b2ecc582a44d55ab7f4b1f0",
|
|
893
893
|
"rails/create-only/.github/workflows/claude-code-review-response.yml": "bfbe3127c4941c35886d324d439ae0302fbccd74a11cb4decf05a21195efa0e9",
|
|
@@ -1003,11 +1003,11 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
|
|
|
1003
1003
|
"typescript/copy-overwrite/eslint.ignore.config.json": "54739e51ce09c314fe01d8a3919e6db5d48cbfb63dcba957f8aca6eba6229a9b",
|
|
1004
1004
|
"typescript/copy-overwrite/eslint.slow.config.ts": "f66d04381940936544931d7f20ffd4e43c38b8a907d56ffa101e2236371d8c94",
|
|
1005
1005
|
"typescript/copy-overwrite/knip.json": "6eb93d705a2d645332fbae1dd42cf2d48f85978ebc265451a53790912f277d12",
|
|
1006
|
-
"typescript/copy-overwrite/scripts/check-threshold-ratchet.mjs": "
|
|
1006
|
+
"typescript/copy-overwrite/scripts/check-threshold-ratchet.mjs": "04f660131bf50864239eb09cbf4d9555e53921254f6ab45ff2b85c173e160901",
|
|
1007
1007
|
"typescript/copy-overwrite/scripts/check-verification-coverage.mjs": "3c1d27d0668fc66a18cb014f2b607878f0a88c178a773fbe964b8e46b19e86d6",
|
|
1008
1008
|
"typescript/copy-overwrite/scripts/lisa-mutation.mjs": "7d8d24dea151ef2178807046e97aea7ebfadd9c88eb50347feb7bb4bb190227d",
|
|
1009
|
-
"typescript/copy-overwrite/scripts/threshold-ratchet-compare.mjs": "
|
|
1010
|
-
"typescript/copy-overwrite/scripts/threshold-ratchet-families.mjs": "
|
|
1009
|
+
"typescript/copy-overwrite/scripts/threshold-ratchet-compare.mjs": "4535a145c5c5cbee13bdf970bc91d390a264043604e607288cdd6bf28de56192",
|
|
1010
|
+
"typescript/copy-overwrite/scripts/threshold-ratchet-families.mjs": "db14245ed89b483912af287bdee917a7a921dc21bb29b49be37fc799c05c413f",
|
|
1011
1011
|
"typescript/copy-overwrite/sgconfig.yml": "0ffe058c791d4d323f98104cf78c763bb0356d88739042060ceb86b6b1cb78d7",
|
|
1012
1012
|
"typescript/copy-overwrite/tsconfig.eslint.json": "8a8ae5e201b18887b8eeda5eb569747221695524d4193ea03b4338b97b7fa642",
|
|
1013
1013
|
"typescript/copy-overwrite/tsconfig.json": "8f4257fcd2cfd4b23e83e0fdd4908d41ee8f1ae75bb95d632e653a0ac13d58e4",
|
package/package.json
CHANGED
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
"brace-expansion": ">=5.0.8"
|
|
116
116
|
},
|
|
117
117
|
"name": "@codyswann/lisa",
|
|
118
|
-
"version": "2.300.
|
|
118
|
+
"version": "2.300.9",
|
|
119
119
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
120
120
|
"main": "dist/index.js",
|
|
121
121
|
"exports": {
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import {
|
|
10
10
|
extractAllowEntries,
|
|
11
|
-
extractExemptionEntries,
|
|
12
11
|
extractK6Constraints,
|
|
13
12
|
extractNumericLeaves,
|
|
14
13
|
extractRubocopThresholds,
|
|
@@ -20,7 +19,7 @@ import {
|
|
|
20
19
|
|
|
21
20
|
/** Finding type: a numeric bound or boolean gate moved the weakening way. */
|
|
22
21
|
const TYPE_WEAKENED = "weakened";
|
|
23
|
-
/** Finding type:
|
|
22
|
+
/** Finding type: a gate-shrinking exemption was added (Tier 3). */
|
|
24
23
|
const TYPE_EXEMPTION_ADDED = "exemption-added";
|
|
25
24
|
/** Family kind for .lisa.config.json (the thresholdRatchet.allow carrier). */
|
|
26
25
|
const KIND_ALLOW_LIST = "allow-list";
|
|
@@ -160,29 +159,6 @@ function compareK6(relPath, base, current) {
|
|
|
160
159
|
return findings;
|
|
161
160
|
}
|
|
162
161
|
|
|
163
|
-
/**
|
|
164
|
-
* Compare an exemption-list pair (audit ignore files): report added entries.
|
|
165
|
-
* @param {string} relPath Repo-relative path
|
|
166
|
-
* @param {unknown} base Parsed baseline list
|
|
167
|
-
* @param {unknown} current Parsed current list
|
|
168
|
-
* @returns {Finding[]} One finding per newly added ignore entry
|
|
169
|
-
*/
|
|
170
|
-
function compareExemptions(relPath, base, current) {
|
|
171
|
-
const baseEntries = extractExemptionEntries(base);
|
|
172
|
-
const findings = [];
|
|
173
|
-
for (const entry of extractExemptionEntries(current)) {
|
|
174
|
-
if (!baseEntries.has(entry)) {
|
|
175
|
-
findings.push({
|
|
176
|
-
file: relPath,
|
|
177
|
-
key: entry,
|
|
178
|
-
type: TYPE_EXEMPTION_ADDED,
|
|
179
|
-
message: `${relPath}: new security-audit ignore entry "${entry}" — ignoring a finding weakens the gate and needs a human's sign-off.`,
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
return findings;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
162
|
/**
|
|
187
163
|
* Compare .lisa.config.json allow lists: report added exception entries so a
|
|
188
164
|
* change can never grant itself an exception.
|
|
@@ -258,8 +234,6 @@ export function compareFile(relPath, baselineText, currentText) {
|
|
|
258
234
|
return compareStryker(relPath, base, current);
|
|
259
235
|
case "k6":
|
|
260
236
|
return compareK6(relPath, base, current);
|
|
261
|
-
case "exemption-list":
|
|
262
|
-
return compareExemptions(relPath, base, current);
|
|
263
237
|
case KIND_ALLOW_LIST:
|
|
264
238
|
return compareAllowList(relPath, base, current);
|
|
265
239
|
default:
|
|
@@ -48,11 +48,6 @@ export const FAMILIES = [
|
|
|
48
48
|
match: /(^|\/)\.github\/k6\/thresholds\/[^/]+\.json$/,
|
|
49
49
|
kind: "k6",
|
|
50
50
|
},
|
|
51
|
-
{
|
|
52
|
-
id: "audit-ignore",
|
|
53
|
-
match: /(^|\/)audit\.ignore\.(config|local)\.json$/,
|
|
54
|
-
kind: "exemption-list",
|
|
55
|
-
},
|
|
56
51
|
{
|
|
57
52
|
id: "lisa-config",
|
|
58
53
|
match: /(^|\/)\.lisa\.config\.json$/,
|
|
@@ -251,35 +246,6 @@ export function extractK6Constraints(conf) {
|
|
|
251
246
|
return { numeric, booleans };
|
|
252
247
|
}
|
|
253
248
|
|
|
254
|
-
/**
|
|
255
|
-
* Flatten an exemption file (audit ignore list) into a set of entry tokens.
|
|
256
|
-
* Arrays contribute their string items; objects contribute their keys.
|
|
257
|
-
* @param {unknown} conf Parsed JSON
|
|
258
|
-
* @returns {Set<string>} One token per exemption entry
|
|
259
|
-
*/
|
|
260
|
-
export function extractExemptionEntries(conf) {
|
|
261
|
-
const out = new Set();
|
|
262
|
-
if (Array.isArray(conf)) {
|
|
263
|
-
for (const item of conf) {
|
|
264
|
-
if (typeof item === "string") out.add(item);
|
|
265
|
-
else if (item && typeof item === "object") out.add(JSON.stringify(item));
|
|
266
|
-
}
|
|
267
|
-
} else if (conf && typeof conf === "object") {
|
|
268
|
-
for (const [key, value] of Object.entries(conf)) {
|
|
269
|
-
if (Array.isArray(value)) {
|
|
270
|
-
for (const item of value) {
|
|
271
|
-
out.add(
|
|
272
|
-
`${key}:${typeof item === "string" ? item : JSON.stringify(item)}`
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
} else {
|
|
276
|
-
out.add(key);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
return out;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
249
|
/**
|
|
284
250
|
* Extract thresholdRatchet.allow entries from parsed .lisa.config.json.
|
|
285
251
|
* @param {unknown} config Parsed config (may be undefined)
|
|
@@ -12,8 +12,11 @@
|
|
|
12
12
|
* Tier 1 — designed tunables: vitest/jest/simplecov/e2e thresholds
|
|
13
13
|
* (minimums) and eslint/rubocop thresholds (maximums). Tier 2 — stryker's
|
|
14
14
|
* break score and k6 expression bounds. Tier 3 — exemption additions
|
|
15
|
-
* (
|
|
16
|
-
*
|
|
15
|
+
* (stryker mutate exclusions, thresholdRatchet.allow entries) which weaken
|
|
16
|
+
* a gate without touching a number. Audit ignore lists are deliberately NOT
|
|
17
|
+
* watched: the security-audit-handling ladder authorizes agents to add
|
|
18
|
+
* documented entries autonomously, and doctor readiness (B5) audits that
|
|
19
|
+
* every entry carries a written decision.
|
|
17
20
|
*
|
|
18
21
|
* Human override: `.lisa.config.json` → `thresholdRatchet.allow` entries
|
|
19
22
|
* ({ file, key, reason }). Honored ONLY from the baseline side (HEAD /
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# PostToolUse hook for Edit|Write|NotebookEdit|Bash: the threshold ratchet.
|
|
3
3
|
# Quality thresholds (coverage minimums, complexity maximums, mutation break
|
|
4
|
-
# score, e2e route floors, k6 bounds
|
|
4
|
+
# score, e2e route floors, k6 bounds) may tighten but never
|
|
5
5
|
# weaken. The deterministic comparator lives in threshold-ratchet.mjs and is
|
|
6
6
|
# shared with the pre-commit (husky/lefthook --staged) and CI (--base) layers,
|
|
7
7
|
# so this hook is fast feedback — not the only line of defense.
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import {
|
|
10
10
|
extractAllowEntries,
|
|
11
|
-
extractExemptionEntries,
|
|
12
11
|
extractK6Constraints,
|
|
13
12
|
extractNumericLeaves,
|
|
14
13
|
extractRubocopThresholds,
|
|
@@ -20,7 +19,7 @@ import {
|
|
|
20
19
|
|
|
21
20
|
/** Finding type: a numeric bound or boolean gate moved the weakening way. */
|
|
22
21
|
const TYPE_WEAKENED = "weakened";
|
|
23
|
-
/** Finding type:
|
|
22
|
+
/** Finding type: a gate-shrinking exemption was added (Tier 3). */
|
|
24
23
|
const TYPE_EXEMPTION_ADDED = "exemption-added";
|
|
25
24
|
/** Family kind for .lisa.config.json (the thresholdRatchet.allow carrier). */
|
|
26
25
|
const KIND_ALLOW_LIST = "allow-list";
|
|
@@ -160,29 +159,6 @@ function compareK6(relPath, base, current) {
|
|
|
160
159
|
return findings;
|
|
161
160
|
}
|
|
162
161
|
|
|
163
|
-
/**
|
|
164
|
-
* Compare an exemption-list pair (audit ignore files): report added entries.
|
|
165
|
-
* @param {string} relPath Repo-relative path
|
|
166
|
-
* @param {unknown} base Parsed baseline list
|
|
167
|
-
* @param {unknown} current Parsed current list
|
|
168
|
-
* @returns {Finding[]} One finding per newly added ignore entry
|
|
169
|
-
*/
|
|
170
|
-
function compareExemptions(relPath, base, current) {
|
|
171
|
-
const baseEntries = extractExemptionEntries(base);
|
|
172
|
-
const findings = [];
|
|
173
|
-
for (const entry of extractExemptionEntries(current)) {
|
|
174
|
-
if (!baseEntries.has(entry)) {
|
|
175
|
-
findings.push({
|
|
176
|
-
file: relPath,
|
|
177
|
-
key: entry,
|
|
178
|
-
type: TYPE_EXEMPTION_ADDED,
|
|
179
|
-
message: `${relPath}: new security-audit ignore entry "${entry}" — ignoring a finding weakens the gate and needs a human's sign-off.`,
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
return findings;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
162
|
/**
|
|
187
163
|
* Compare .lisa.config.json allow lists: report added exception entries so a
|
|
188
164
|
* change can never grant itself an exception.
|
|
@@ -258,8 +234,6 @@ export function compareFile(relPath, baselineText, currentText) {
|
|
|
258
234
|
return compareStryker(relPath, base, current);
|
|
259
235
|
case "k6":
|
|
260
236
|
return compareK6(relPath, base, current);
|
|
261
|
-
case "exemption-list":
|
|
262
|
-
return compareExemptions(relPath, base, current);
|
|
263
237
|
case KIND_ALLOW_LIST:
|
|
264
238
|
return compareAllowList(relPath, base, current);
|
|
265
239
|
default:
|
|
@@ -48,11 +48,6 @@ export const FAMILIES = [
|
|
|
48
48
|
match: /(^|\/)\.github\/k6\/thresholds\/[^/]+\.json$/,
|
|
49
49
|
kind: "k6",
|
|
50
50
|
},
|
|
51
|
-
{
|
|
52
|
-
id: "audit-ignore",
|
|
53
|
-
match: /(^|\/)audit\.ignore\.(config|local)\.json$/,
|
|
54
|
-
kind: "exemption-list",
|
|
55
|
-
},
|
|
56
51
|
{
|
|
57
52
|
id: "lisa-config",
|
|
58
53
|
match: /(^|\/)\.lisa\.config\.json$/,
|
|
@@ -251,35 +246,6 @@ export function extractK6Constraints(conf) {
|
|
|
251
246
|
return { numeric, booleans };
|
|
252
247
|
}
|
|
253
248
|
|
|
254
|
-
/**
|
|
255
|
-
* Flatten an exemption file (audit ignore list) into a set of entry tokens.
|
|
256
|
-
* Arrays contribute their string items; objects contribute their keys.
|
|
257
|
-
* @param {unknown} conf Parsed JSON
|
|
258
|
-
* @returns {Set<string>} One token per exemption entry
|
|
259
|
-
*/
|
|
260
|
-
export function extractExemptionEntries(conf) {
|
|
261
|
-
const out = new Set();
|
|
262
|
-
if (Array.isArray(conf)) {
|
|
263
|
-
for (const item of conf) {
|
|
264
|
-
if (typeof item === "string") out.add(item);
|
|
265
|
-
else if (item && typeof item === "object") out.add(JSON.stringify(item));
|
|
266
|
-
}
|
|
267
|
-
} else if (conf && typeof conf === "object") {
|
|
268
|
-
for (const [key, value] of Object.entries(conf)) {
|
|
269
|
-
if (Array.isArray(value)) {
|
|
270
|
-
for (const item of value) {
|
|
271
|
-
out.add(
|
|
272
|
-
`${key}:${typeof item === "string" ? item : JSON.stringify(item)}`
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
} else {
|
|
276
|
-
out.add(key);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
return out;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
249
|
/**
|
|
284
250
|
* Extract thresholdRatchet.allow entries from parsed .lisa.config.json.
|
|
285
251
|
* @param {unknown} config Parsed config (may be undefined)
|
|
@@ -12,8 +12,11 @@
|
|
|
12
12
|
* Tier 1 — designed tunables: vitest/jest/simplecov/e2e thresholds
|
|
13
13
|
* (minimums) and eslint/rubocop thresholds (maximums). Tier 2 — stryker's
|
|
14
14
|
* break score and k6 expression bounds. Tier 3 — exemption additions
|
|
15
|
-
* (
|
|
16
|
-
*
|
|
15
|
+
* (stryker mutate exclusions, thresholdRatchet.allow entries) which weaken
|
|
16
|
+
* a gate without touching a number. Audit ignore lists are deliberately NOT
|
|
17
|
+
* watched: the security-audit-handling ladder authorizes agents to add
|
|
18
|
+
* documented entries autonomously, and doctor readiness (B5) audits that
|
|
19
|
+
* every entry carries a written decision.
|
|
17
20
|
*
|
|
18
21
|
* Human override: `.lisa.config.json` → `thresholdRatchet.allow` entries
|
|
19
22
|
* ({ file, key, reason }). Honored ONLY from the baseline side (HEAD /
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# PostToolUse hook for Edit|Write|NotebookEdit|Bash: the threshold ratchet.
|
|
3
3
|
# Quality thresholds (coverage minimums, complexity maximums, mutation break
|
|
4
|
-
# score, e2e route floors, k6 bounds
|
|
4
|
+
# score, e2e route floors, k6 bounds) may tighten but never
|
|
5
5
|
# weaken. The deterministic comparator lives in threshold-ratchet.mjs and is
|
|
6
6
|
# shared with the pre-commit (husky/lefthook --staged) and CI (--base) layers,
|
|
7
7
|
# so this hook is fast feedback — not the only line of defense.
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import {
|
|
10
10
|
extractAllowEntries,
|
|
11
|
-
extractExemptionEntries,
|
|
12
11
|
extractK6Constraints,
|
|
13
12
|
extractNumericLeaves,
|
|
14
13
|
extractRubocopThresholds,
|
|
@@ -20,7 +19,7 @@ import {
|
|
|
20
19
|
|
|
21
20
|
/** Finding type: a numeric bound or boolean gate moved the weakening way. */
|
|
22
21
|
const TYPE_WEAKENED = "weakened";
|
|
23
|
-
/** Finding type:
|
|
22
|
+
/** Finding type: a gate-shrinking exemption was added (Tier 3). */
|
|
24
23
|
const TYPE_EXEMPTION_ADDED = "exemption-added";
|
|
25
24
|
/** Family kind for .lisa.config.json (the thresholdRatchet.allow carrier). */
|
|
26
25
|
const KIND_ALLOW_LIST = "allow-list";
|
|
@@ -160,29 +159,6 @@ function compareK6(relPath, base, current) {
|
|
|
160
159
|
return findings;
|
|
161
160
|
}
|
|
162
161
|
|
|
163
|
-
/**
|
|
164
|
-
* Compare an exemption-list pair (audit ignore files): report added entries.
|
|
165
|
-
* @param {string} relPath Repo-relative path
|
|
166
|
-
* @param {unknown} base Parsed baseline list
|
|
167
|
-
* @param {unknown} current Parsed current list
|
|
168
|
-
* @returns {Finding[]} One finding per newly added ignore entry
|
|
169
|
-
*/
|
|
170
|
-
function compareExemptions(relPath, base, current) {
|
|
171
|
-
const baseEntries = extractExemptionEntries(base);
|
|
172
|
-
const findings = [];
|
|
173
|
-
for (const entry of extractExemptionEntries(current)) {
|
|
174
|
-
if (!baseEntries.has(entry)) {
|
|
175
|
-
findings.push({
|
|
176
|
-
file: relPath,
|
|
177
|
-
key: entry,
|
|
178
|
-
type: TYPE_EXEMPTION_ADDED,
|
|
179
|
-
message: `${relPath}: new security-audit ignore entry "${entry}" — ignoring a finding weakens the gate and needs a human's sign-off.`,
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
return findings;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
162
|
/**
|
|
187
163
|
* Compare .lisa.config.json allow lists: report added exception entries so a
|
|
188
164
|
* change can never grant itself an exception.
|
|
@@ -258,8 +234,6 @@ export function compareFile(relPath, baselineText, currentText) {
|
|
|
258
234
|
return compareStryker(relPath, base, current);
|
|
259
235
|
case "k6":
|
|
260
236
|
return compareK6(relPath, base, current);
|
|
261
|
-
case "exemption-list":
|
|
262
|
-
return compareExemptions(relPath, base, current);
|
|
263
237
|
case KIND_ALLOW_LIST:
|
|
264
238
|
return compareAllowList(relPath, base, current);
|
|
265
239
|
default:
|
|
@@ -48,11 +48,6 @@ export const FAMILIES = [
|
|
|
48
48
|
match: /(^|\/)\.github\/k6\/thresholds\/[^/]+\.json$/,
|
|
49
49
|
kind: "k6",
|
|
50
50
|
},
|
|
51
|
-
{
|
|
52
|
-
id: "audit-ignore",
|
|
53
|
-
match: /(^|\/)audit\.ignore\.(config|local)\.json$/,
|
|
54
|
-
kind: "exemption-list",
|
|
55
|
-
},
|
|
56
51
|
{
|
|
57
52
|
id: "lisa-config",
|
|
58
53
|
match: /(^|\/)\.lisa\.config\.json$/,
|
|
@@ -251,35 +246,6 @@ export function extractK6Constraints(conf) {
|
|
|
251
246
|
return { numeric, booleans };
|
|
252
247
|
}
|
|
253
248
|
|
|
254
|
-
/**
|
|
255
|
-
* Flatten an exemption file (audit ignore list) into a set of entry tokens.
|
|
256
|
-
* Arrays contribute their string items; objects contribute their keys.
|
|
257
|
-
* @param {unknown} conf Parsed JSON
|
|
258
|
-
* @returns {Set<string>} One token per exemption entry
|
|
259
|
-
*/
|
|
260
|
-
export function extractExemptionEntries(conf) {
|
|
261
|
-
const out = new Set();
|
|
262
|
-
if (Array.isArray(conf)) {
|
|
263
|
-
for (const item of conf) {
|
|
264
|
-
if (typeof item === "string") out.add(item);
|
|
265
|
-
else if (item && typeof item === "object") out.add(JSON.stringify(item));
|
|
266
|
-
}
|
|
267
|
-
} else if (conf && typeof conf === "object") {
|
|
268
|
-
for (const [key, value] of Object.entries(conf)) {
|
|
269
|
-
if (Array.isArray(value)) {
|
|
270
|
-
for (const item of value) {
|
|
271
|
-
out.add(
|
|
272
|
-
`${key}:${typeof item === "string" ? item : JSON.stringify(item)}`
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
} else {
|
|
276
|
-
out.add(key);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
return out;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
249
|
/**
|
|
284
250
|
* Extract thresholdRatchet.allow entries from parsed .lisa.config.json.
|
|
285
251
|
* @param {unknown} config Parsed config (may be undefined)
|
|
@@ -12,8 +12,11 @@
|
|
|
12
12
|
* Tier 1 — designed tunables: vitest/jest/simplecov/e2e thresholds
|
|
13
13
|
* (minimums) and eslint/rubocop thresholds (maximums). Tier 2 — stryker's
|
|
14
14
|
* break score and k6 expression bounds. Tier 3 — exemption additions
|
|
15
|
-
* (
|
|
16
|
-
*
|
|
15
|
+
* (stryker mutate exclusions, thresholdRatchet.allow entries) which weaken
|
|
16
|
+
* a gate without touching a number. Audit ignore lists are deliberately NOT
|
|
17
|
+
* watched: the security-audit-handling ladder authorizes agents to add
|
|
18
|
+
* documented entries autonomously, and doctor readiness (B5) audits that
|
|
19
|
+
* every entry carries a written decision.
|
|
17
20
|
*
|
|
18
21
|
* Human override: `.lisa.config.json` → `thresholdRatchet.allow` entries
|
|
19
22
|
* ({ file, key, reason }). Honored ONLY from the baseline side (HEAD /
|