@codyswann/lisa 2.240.0 → 2.242.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/sync/registry.d.ts.map +1 -1
- package/dist/sync/registry.js +7 -0
- package/dist/sync/registry.js.map +1 -1
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +10 -1
- package/plugins/lisa/.codex-plugin/hooks.json +9 -0
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-github-build-intake/SKILL.md +2 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-build-intake/SKILL.md +2 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-build-intake/SKILL.md +2 -0
- package/plugins/lisa/hooks/threshold-ratchet-compare.mjs +315 -0
- package/plugins/lisa/hooks/threshold-ratchet-families.mjs +295 -0
- package/plugins/lisa/hooks/threshold-ratchet.mjs +213 -0
- package/plugins/lisa/hooks/threshold-ratchet.sh +22 -0
- package/plugins/lisa/rules/eager/claim-archaeology.md +37 -0
- package/plugins/lisa/rules/reference/claim-archaeology.md +142 -0
- package/plugins/lisa/skills/lisa-github-build-intake/SKILL.md +2 -0
- package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +2 -0
- package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +2 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +2 -0
- package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +2 -0
- package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +2 -0
- 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 +10 -1
- package/plugins/lisa-copilot/hooks/threshold-ratchet-compare.mjs +315 -0
- package/plugins/lisa-copilot/hooks/threshold-ratchet-families.mjs +295 -0
- package/plugins/lisa-copilot/hooks/threshold-ratchet.mjs +213 -0
- package/plugins/lisa-copilot/hooks/threshold-ratchet.sh +22 -0
- package/plugins/lisa-copilot/rules/eager/claim-archaeology.md +37 -0
- package/plugins/lisa-copilot/rules/reference/claim-archaeology.md +142 -0
- package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +2 -0
- package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +2 -0
- package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +2 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/hooks.json +4 -0
- package/plugins/lisa-cursor/hooks/threshold-ratchet-compare.mjs +315 -0
- package/plugins/lisa-cursor/hooks/threshold-ratchet-families.mjs +295 -0
- package/plugins/lisa-cursor/hooks/threshold-ratchet.mjs +213 -0
- package/plugins/lisa-cursor/hooks/threshold-ratchet.sh +22 -0
- package/plugins/lisa-cursor/rules/claim-archaeology-reference.mdc +147 -0
- package/plugins/lisa-cursor/rules/claim-archaeology.mdc +42 -0
- package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +2 -0
- package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +2 -0
- package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +2 -0
- 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/.claude-plugin/plugin.json +9 -0
- package/plugins/src/base/hooks/threshold-ratchet-compare.mjs +315 -0
- package/plugins/src/base/hooks/threshold-ratchet-families.mjs +295 -0
- package/plugins/src/base/hooks/threshold-ratchet.mjs +213 -0
- package/plugins/src/base/hooks/threshold-ratchet.sh +22 -0
- package/plugins/src/base/rules/eager/claim-archaeology.md +37 -0
- package/plugins/src/base/rules/reference/claim-archaeology.md +142 -0
- package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +2 -0
- package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +2 -0
- package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +2 -0
- package/rails/copy-overwrite/lefthook.yml +6 -0
- package/rails/copy-overwrite/scripts/check-threshold-ratchet.mjs +213 -0
- package/rails/copy-overwrite/scripts/threshold-ratchet-compare.mjs +315 -0
- package/rails/copy-overwrite/scripts/threshold-ratchet-families.mjs +295 -0
- package/scripts/build-plugins.sh +20 -0
- package/scripts/lib/per-agent-hook-filter.mjs +7 -0
- package/typescript/copy-contents/.husky/pre-commit +17 -0
- package/typescript/copy-overwrite/scripts/check-threshold-ratchet.mjs +213 -0
- package/typescript/copy-overwrite/scripts/threshold-ratchet-compare.mjs +315 -0
- package/typescript/copy-overwrite/scripts/threshold-ratchet-families.mjs +295 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/sync/registry.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE9D;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,8CAA8C;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,2EAA2E;AAC3E,MAAM,WAAW,aAAa;IAC5B,yDAAyD;IACzD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,QAAQ,CAAC,YAAY,EAAE,SAAS,CAAC;IACjC;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACvD,uEAAuE;IACvE,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IAChD;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,sDAAsD;IACtD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,8DAA8D;AAC9D,MAAM,WAAW,WAAW;IAC1B,uCAAuC;IACvC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,oEAAoE;IACpE,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,4CAA4C;IAC5C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAoCD;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,aAAa,
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/sync/registry.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE9D;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,8CAA8C;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,2EAA2E;AAC3E,MAAM,WAAW,aAAa;IAC5B,yDAAyD;IACzD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,QAAQ,CAAC,YAAY,EAAE,SAAS,CAAC;IACjC;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACvD,uEAAuE;IACvE,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IAChD;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,sDAAsD;IACtD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,8DAA8D;AAC9D,MAAM,WAAW,WAAW;IAC1B,uCAAuC;IACvC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,oEAAoE;IACpE,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,4CAA4C;IAC5C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAoCD;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,aAAa,EAoJjD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,WAAW,EA0C/C,CAAC"}
|
package/dist/sync/registry.js
CHANGED
|
@@ -100,6 +100,13 @@ export const SYNC_REGISTRY = [
|
|
|
100
100
|
defaultValue: { staleAfterHours: 2, maxCandidates: 100 },
|
|
101
101
|
description: "Repair-intake stall window and candidate cap",
|
|
102
102
|
},
|
|
103
|
+
{
|
|
104
|
+
// Semantics documented in one place: the claim-archaeology rule pair
|
|
105
|
+
// (plugins/src/base/rules/{eager,reference}/claim-archaeology.md).
|
|
106
|
+
key: "archaeology",
|
|
107
|
+
defaultValue: { maxSteps: 8 },
|
|
108
|
+
description: "Claim-time archaeology cost budget (max tracker/git queries per claim)",
|
|
109
|
+
},
|
|
103
110
|
{
|
|
104
111
|
key: "monitor",
|
|
105
112
|
defaultValue: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/sync/registry.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/sync/registry.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"form-data": ">=4.0.6"
|
|
103
103
|
},
|
|
104
104
|
"name": "@codyswann/lisa",
|
|
105
|
-
"version": "2.
|
|
105
|
+
"version": "2.242.0",
|
|
106
106
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
107
107
|
"main": "dist/index.js",
|
|
108
108
|
"exports": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.242.0",
|
|
4
4
|
"description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -63,6 +63,15 @@
|
|
|
63
63
|
}
|
|
64
64
|
]
|
|
65
65
|
},
|
|
66
|
+
{
|
|
67
|
+
"matcher": "Edit|Write|NotebookEdit|Bash",
|
|
68
|
+
"hooks": [
|
|
69
|
+
{
|
|
70
|
+
"type": "command",
|
|
71
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/threshold-ratchet.sh"
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
},
|
|
66
75
|
{
|
|
67
76
|
"matcher": "TeamCreate|Agent",
|
|
68
77
|
"hooks": [
|
|
@@ -20,6 +20,15 @@
|
|
|
20
20
|
"command": "${PLUGIN_ROOT}/hooks/shell-write-nudge.sh"
|
|
21
21
|
}
|
|
22
22
|
]
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"matcher": "Edit|Write|NotebookEdit|Bash",
|
|
26
|
+
"hooks": [
|
|
27
|
+
{
|
|
28
|
+
"type": "command",
|
|
29
|
+
"command": "${PLUGIN_ROOT}/hooks/threshold-ratchet.sh"
|
|
30
|
+
}
|
|
31
|
+
]
|
|
23
32
|
}
|
|
24
33
|
],
|
|
25
34
|
"PreToolUse": [
|
|
@@ -256,6 +256,8 @@ A blocker is active if it is open and has no cleared status label. Treat `status
|
|
|
256
256
|
|
|
257
257
|
**On `rejection-reclaim`, reflect before re-implementing** (per `rejection-detection`): read the rejection evidence through the access layer — the issue comments posted after the backward transition (the QA rejection comment) and the review threads on the rejected PR via `lisa-github-read-issue` — assemble ONE candidate learning (rule, why, provenance linking the rejection comment + rejected PR, evidence links, scope hint, triggering issue, fingerprint `sll4-sha1(rule\ntriggering_issue)[:12]`), and route it to the `lisa-persist-learning` skill. If that skill is absent, record the candidate as a comment carrying a **visible prose line plus** the marker (a bare marker renders as an empty bubble) — `Recorded a candidate learning from this rejection (queued for the judgment gate): <one-line candidate rule>.` then `<!-- [lisa-rejection-candidate] key=<issue>-<transition-ts> -->` — and proceed. Dedupe on `<issue>-<backward-transition-timestamp>` — a second re-claim produces no duplicate. Unreadable/absent evidence → no candidate, still implement.
|
|
258
258
|
|
|
259
|
+
**Claim-time archaeology runs second — after rejection detection, still before the relabel below.** Classify this item per the vendor-neutral `claim-archaeology` rule, with the rejection classification above as its input. All shared semantics — ancestry signals, classification, learning-loop exclusion, cost budget, candidate derivation, marker dedupe, and the never-block degrade — live in that one slug; change them there, never here. GitHub wiring only: the typed relations and `closingIssuesReferences` are already in the read bundle; text-similarity searches use `gh search issues` over recently-closed issues; the fallback candidate comment is posted with `gh issue comment`.
|
|
260
|
+
|
|
259
261
|
```bash
|
|
260
262
|
gh issue edit <number> --repo <org>/<repo> --remove-label "$READY" --add-label "$CLAIMED"
|
|
261
263
|
# Assign to the authenticated user ONLY when the issue is currently unassigned (attributable claim;
|
|
@@ -200,6 +200,8 @@ This gate never blocks a legitimate flat Task/Bug: those have no open children a
|
|
|
200
200
|
|
|
201
201
|
**On `rejection-reclaim`, reflect before re-implementing** (per `rejection-detection`): read the rejection evidence through the access layer — the ticket comments posted after the backward transition (the QA rejection comment) via `lisa-atlassian-access operation: read-ticket` / `comment` reads and the review threads on the rejected PR — assemble ONE candidate learning (rule, why, provenance linking the rejection comment + rejected PR, evidence links, scope hint, triggering issue, fingerprint `sll4-sha1(rule\ntriggering_issue)[:12]`), and route it to the `lisa-persist-learning` skill. If that skill is absent, record the candidate via `lisa-atlassian-access operation: comment` as a comment carrying a **visible prose line plus** the marker (a bare marker renders as an empty bubble) — `Recorded a candidate learning from this rejection (queued for the judgment gate): <one-line candidate rule>.` then `<!-- [lisa-rejection-candidate] key=<issue>-<transition-ts> -->` — and proceed. Dedupe on `<issue>-<backward-transition-timestamp>` — a second re-claim produces no duplicate. Unreadable/absent evidence → no candidate, still implement.
|
|
202
202
|
|
|
203
|
+
**Claim-time archaeology runs second — after rejection detection, still before the transition below.** Classify this ticket per the vendor-neutral `claim-archaeology` rule, with the rejection classification above as its input. All shared semantics — ancestry signals, classification, learning-loop exclusion, cost budget, candidate derivation, marker dedupe, and the never-block degrade — live in that one slug; change them there, never here. JIRA wiring only: the typed relations are already in the read bundle; text-similarity searches run through `lisa-atlassian-access operation: search-issues jql:` over recently-closed tickets; the fallback candidate comment is posted via `lisa-atlassian-access operation: comment`.
|
|
204
|
+
|
|
203
205
|
Transition the ticket from `$READY` to `$CLAIMED` by invoking `lisa-atlassian-access` `operation: transition key: <TICKET> to: "$CLAIMED"`.
|
|
204
206
|
- **Assign to the authenticated user when the ticket is unassigned.** A claim must be attributable. If the ticket has no assignee, assign it to the authenticated account — prefer acli `--assignee @me` (resolves server-side to the authenticated user, which avoids the federated-`accountId` mis-assignment), or `write-ticket` with the `accountId` from the `/rest/api/3/myself` identity probe the access skill already documents. Leave an already-assigned ticket's assignee untouched — never reassign work that already has an owner.
|
|
205
207
|
- Post a `[claude-build-intake]` comment via `lisa-atlassian-access` `operation: comment key: <TICKET> body: "Claimed by Claude. Starting build."`
|
|
@@ -190,6 +190,8 @@ This gate never blocks a legitimate flat Task/Bug: those have no open children a
|
|
|
190
190
|
|
|
191
191
|
**On `rejection-reclaim`, reflect before re-implementing** (per `rejection-detection`): read the rejection evidence through the access layer — the Issue comments posted after the backward transition (the QA rejection comment) via `lisa-linear-access operation: list-comments` and the review threads on the rejected PR — assemble ONE candidate learning (rule, why, provenance linking the rejection comment + rejected PR, evidence links, scope hint, triggering issue, fingerprint `sll4-sha1(rule\ntriggering_issue)[:12]`), and route it to the `lisa-persist-learning` skill. If that skill is absent, record the candidate via `lisa-linear-access operation: save-comment` as a comment carrying a **visible prose line plus** the marker (a bare marker renders as an empty bubble) — `Recorded a candidate learning from this rejection (queued for the judgment gate): <one-line candidate rule>.` then `<!-- [lisa-rejection-candidate] key=<issue>-<transition-ts> -->` — and proceed. Dedupe on `<issue>-<backward-transition-timestamp>` — a second re-claim produces no duplicate. Unreadable/absent evidence → no candidate, still implement.
|
|
192
192
|
|
|
193
|
+
**Claim-time archaeology runs second — after rejection detection, still before the relabel below.** Classify this Issue per the vendor-neutral `claim-archaeology` rule, with the rejection classification above as its input. All shared semantics — ancestry signals, classification, learning-loop exclusion, cost budget, candidate derivation, marker dedupe, and the never-block degrade — live in that one slug; change them there, never here. Linear wiring only: the native relations are already in the read bundle; text-similarity searches run through `lisa-linear-access operation: list-issues` filtered to recently-closed Issues; the fallback candidate comment is posted via `lisa-linear-access operation: save-comment`.
|
|
194
|
+
|
|
193
195
|
Update labels via `lisa-linear-access operation: save-issue`: remove `$READY`, add `$CLAIMED`. Resolve label IDs via `list_issue_labels` (create `$CLAIMED` if missing).
|
|
194
196
|
|
|
195
197
|
**Assign to the authenticated user when the Issue is unassigned.** A claim must be attributable. If the Issue has no assignee, set its `assigneeId` to the authenticated viewer (resolve the viewer's id via the Linear MCP identity — e.g. `get_user` for the current actor) through `lisa-linear-access operation: save-issue`. Leave an already-assigned Issue's assignee untouched — never reassign work that already has an owner.
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Threshold ratchet — comparison rules and reporting.
|
|
3
|
+
*
|
|
4
|
+
* Pure comparison layer: given a watched file's baseline and current
|
|
5
|
+
* contents, report every weakening. No filesystem or git access. See
|
|
6
|
+
* threshold-ratchet-families.mjs for extraction and threshold-ratchet.mjs
|
|
7
|
+
* for the CLI.
|
|
8
|
+
*/
|
|
9
|
+
import {
|
|
10
|
+
extractAllowEntries,
|
|
11
|
+
extractExemptionEntries,
|
|
12
|
+
extractK6Constraints,
|
|
13
|
+
extractNumericLeaves,
|
|
14
|
+
extractRubocopThresholds,
|
|
15
|
+
extractStrykerConstraints,
|
|
16
|
+
extractStrykerMutate,
|
|
17
|
+
familyFor,
|
|
18
|
+
parseJson,
|
|
19
|
+
} from "./threshold-ratchet-families.mjs";
|
|
20
|
+
|
|
21
|
+
/** Finding type: a numeric bound or boolean gate moved the weakening way. */
|
|
22
|
+
const TYPE_WEAKENED = "weakened";
|
|
23
|
+
/** Finding type: an exemption entry was added (Tier 3). */
|
|
24
|
+
const TYPE_EXEMPTION_ADDED = "exemption-added";
|
|
25
|
+
/** Family kind for .lisa.config.json (the thresholdRatchet.allow carrier). */
|
|
26
|
+
const KIND_ALLOW_LIST = "allow-list";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @typedef {object} Finding
|
|
30
|
+
* @property {string} file Repo-relative path of the gate file
|
|
31
|
+
* @property {string} key Dotted key path within the file
|
|
32
|
+
* @property {"weakened"|"removed"|"exemption-added"|"file-deleted"|"allow-added"|"unparseable"} type
|
|
33
|
+
* Which ratchet rule the change violated
|
|
34
|
+
* @property {number|string} [base] Baseline value
|
|
35
|
+
* @property {number|string} [current] Current value
|
|
36
|
+
* @property {string} message Operator-readable explanation
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Build the "file could not be parsed" finding.
|
|
41
|
+
* @param {string} relPath Repo-relative path
|
|
42
|
+
* @returns {Finding} The unparseable-file finding
|
|
43
|
+
*/
|
|
44
|
+
function unparseable(relPath) {
|
|
45
|
+
return {
|
|
46
|
+
file: relPath,
|
|
47
|
+
key: "*",
|
|
48
|
+
type: "unparseable",
|
|
49
|
+
message: `${relPath} is no longer valid JSON — a broken gate file disables the gate.`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Compare two constraint maps: report removals and direction violations.
|
|
55
|
+
* @param {string} relPath Repo-relative path the constraints came from
|
|
56
|
+
* @param {Map<string, { value: number, direction: "min"|"max" }>} base
|
|
57
|
+
* Baseline constraints
|
|
58
|
+
* @param {Map<string, { value: number, direction: "min"|"max" }>} current
|
|
59
|
+
* Current constraints
|
|
60
|
+
* @returns {Finding[]} One finding per removed or weakened constraint
|
|
61
|
+
*/
|
|
62
|
+
export function compareConstraints(relPath, base, current) {
|
|
63
|
+
const findings = [];
|
|
64
|
+
for (const [key, baseC] of base) {
|
|
65
|
+
const currentC = current.get(key);
|
|
66
|
+
if (!currentC) {
|
|
67
|
+
findings.push({
|
|
68
|
+
file: relPath,
|
|
69
|
+
key,
|
|
70
|
+
type: "removed",
|
|
71
|
+
base: baseC.value,
|
|
72
|
+
message: `${relPath}: ${key} was removed — the tuned floor would silently fall back to a default.`,
|
|
73
|
+
});
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const weakened =
|
|
77
|
+
baseC.direction === "min"
|
|
78
|
+
? currentC.value < baseC.value
|
|
79
|
+
: currentC.value > baseC.value;
|
|
80
|
+
if (weakened) {
|
|
81
|
+
const verb =
|
|
82
|
+
baseC.direction === "min" ? "may only increase" : "may only decrease";
|
|
83
|
+
findings.push({
|
|
84
|
+
file: relPath,
|
|
85
|
+
key,
|
|
86
|
+
type: TYPE_WEAKENED,
|
|
87
|
+
base: baseC.value,
|
|
88
|
+
current: currentC.value,
|
|
89
|
+
message: `${relPath}: ${key} changed ${baseC.value} → ${currentC.value} (this value ${verb}).`,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return findings;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Compare a stryker.conf.json pair: the break threshold plus mutate-list
|
|
98
|
+
* exemptions (new negations or removed targets shrink the gate).
|
|
99
|
+
* @param {string} relPath Repo-relative path
|
|
100
|
+
* @param {unknown} base Parsed baseline config
|
|
101
|
+
* @param {unknown} current Parsed current config
|
|
102
|
+
* @returns {Finding[]} Break-threshold and mutate-scope findings
|
|
103
|
+
*/
|
|
104
|
+
function compareStryker(relPath, base, current) {
|
|
105
|
+
const findings = compareConstraints(
|
|
106
|
+
relPath,
|
|
107
|
+
extractStrykerConstraints(base),
|
|
108
|
+
extractStrykerConstraints(current)
|
|
109
|
+
);
|
|
110
|
+
const baseMutate = extractStrykerMutate(base);
|
|
111
|
+
const currentMutate = extractStrykerMutate(current);
|
|
112
|
+
for (const negation of currentMutate.negations) {
|
|
113
|
+
if (!baseMutate.negations.has(negation)) {
|
|
114
|
+
findings.push({
|
|
115
|
+
file: relPath,
|
|
116
|
+
key: `mutate ${negation}`,
|
|
117
|
+
type: TYPE_EXEMPTION_ADDED,
|
|
118
|
+
message: `${relPath}: new mutation-testing exclusion "${negation}" — excluding files from a gate is a weakening.`,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
for (const positive of baseMutate.positives) {
|
|
123
|
+
if (!currentMutate.positives.has(positive)) {
|
|
124
|
+
findings.push({
|
|
125
|
+
file: relPath,
|
|
126
|
+
key: `mutate ${positive}`,
|
|
127
|
+
type: TYPE_EXEMPTION_ADDED,
|
|
128
|
+
message: `${relPath}: mutation-testing target "${positive}" was removed — shrinking a gate's coverage is a weakening.`,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return findings;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Compare a k6 thresholds pair: numeric bounds plus abortOnFail downgrades.
|
|
137
|
+
* @param {string} relPath Repo-relative path
|
|
138
|
+
* @param {unknown} base Parsed baseline thresholds
|
|
139
|
+
* @param {unknown} current Parsed current thresholds
|
|
140
|
+
* @returns {Finding[]} Bound and abortOnFail findings
|
|
141
|
+
*/
|
|
142
|
+
function compareK6(relPath, base, current) {
|
|
143
|
+
const baseC = extractK6Constraints(base);
|
|
144
|
+
const currentC = extractK6Constraints(current);
|
|
145
|
+
const findings = compareConstraints(relPath, baseC.numeric, currentC.numeric);
|
|
146
|
+
for (const [key, wasOn] of baseC.booleans) {
|
|
147
|
+
// k6 defaults abortOnFail to false, so DELETING an explicit `true` is as
|
|
148
|
+
// much a weakening as flipping it — anything but a current `true` blocks.
|
|
149
|
+
if (wasOn && currentC.booleans.get(key) !== true) {
|
|
150
|
+
findings.push({
|
|
151
|
+
file: relPath,
|
|
152
|
+
key,
|
|
153
|
+
type: TYPE_WEAKENED,
|
|
154
|
+
base: "true",
|
|
155
|
+
current: "false",
|
|
156
|
+
message: `${relPath}: ${key} turned off — the gate no longer stops the run on failure.`,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return findings;
|
|
161
|
+
}
|
|
162
|
+
|
|
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
|
+
/**
|
|
187
|
+
* Compare .lisa.config.json allow lists: report added exception entries so a
|
|
188
|
+
* change can never grant itself an exception.
|
|
189
|
+
* @param {string} relPath Repo-relative path
|
|
190
|
+
* @param {unknown} base Parsed baseline config
|
|
191
|
+
* @param {unknown} current Parsed current config
|
|
192
|
+
* @returns {Finding[]} One finding per newly added allow entry
|
|
193
|
+
*/
|
|
194
|
+
function compareAllowList(relPath, base, current) {
|
|
195
|
+
const baseKeys = new Set(
|
|
196
|
+
extractAllowEntries(base).map(e => `${e.file} ${e.key}`)
|
|
197
|
+
);
|
|
198
|
+
const findings = [];
|
|
199
|
+
for (const entry of extractAllowEntries(current)) {
|
|
200
|
+
if (!baseKeys.has(`${entry.file} ${entry.key}`)) {
|
|
201
|
+
findings.push({
|
|
202
|
+
file: relPath,
|
|
203
|
+
key: `thresholdRatchet.allow ${entry.file}#${entry.key}`,
|
|
204
|
+
type: "allow-added",
|
|
205
|
+
message: `${relPath}: new threshold exception for ${entry.file} → ${entry.key}. Exceptions are a human decision: land this entry in its own human-approved change first, then make the threshold change.`,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return findings;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Compare one watched file's baseline and current contents and report every
|
|
214
|
+
* weakening. Pure: no filesystem or git access.
|
|
215
|
+
* @param {string} relPath Repo-relative path (forward slashes)
|
|
216
|
+
* @param {string | null} baselineText Baseline contents (null = file is new)
|
|
217
|
+
* @param {string | null} currentText Current contents (null = file deleted)
|
|
218
|
+
* @returns {Finding[]} Every ratchet violation in the change (empty = clean)
|
|
219
|
+
*/
|
|
220
|
+
export function compareFile(relPath, baselineText, currentText) {
|
|
221
|
+
const family = familyFor(relPath);
|
|
222
|
+
if (!family || baselineText === null || baselineText === undefined) return [];
|
|
223
|
+
|
|
224
|
+
if (currentText === null || currentText === undefined) {
|
|
225
|
+
if (family.kind === KIND_ALLOW_LIST) return [];
|
|
226
|
+
return [
|
|
227
|
+
{
|
|
228
|
+
file: relPath,
|
|
229
|
+
key: "*",
|
|
230
|
+
type: "file-deleted",
|
|
231
|
+
message: `${relPath} was deleted — deleting a quality gate is a weakening.`,
|
|
232
|
+
},
|
|
233
|
+
];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (family.kind === "rubocop-yaml") {
|
|
237
|
+
return compareConstraints(
|
|
238
|
+
relPath,
|
|
239
|
+
extractRubocopThresholds(baselineText, family.direction),
|
|
240
|
+
extractRubocopThresholds(currentText, family.direction)
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const base = parseJson(baselineText);
|
|
245
|
+
const current = parseJson(currentText);
|
|
246
|
+
if (base === undefined) return [];
|
|
247
|
+
if (current === undefined) {
|
|
248
|
+
return family.kind === KIND_ALLOW_LIST ? [] : [unparseable(relPath)];
|
|
249
|
+
}
|
|
250
|
+
switch (family.kind) {
|
|
251
|
+
case "json-num":
|
|
252
|
+
return compareConstraints(
|
|
253
|
+
relPath,
|
|
254
|
+
extractNumericLeaves(base, family.direction),
|
|
255
|
+
extractNumericLeaves(current, family.direction)
|
|
256
|
+
);
|
|
257
|
+
case "stryker":
|
|
258
|
+
return compareStryker(relPath, base, current);
|
|
259
|
+
case "k6":
|
|
260
|
+
return compareK6(relPath, base, current);
|
|
261
|
+
case "exemption-list":
|
|
262
|
+
return compareExemptions(relPath, base, current);
|
|
263
|
+
case KIND_ALLOW_LIST:
|
|
264
|
+
return compareAllowList(relPath, base, current);
|
|
265
|
+
default:
|
|
266
|
+
return [];
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Drop findings covered by baseline-side allow entries. `allow-added`
|
|
272
|
+
* findings are never dropped — an exception cannot approve its own creation.
|
|
273
|
+
* @param {Finding[]} findings All findings from the change
|
|
274
|
+
* @param {Array<{ file: string, key: string }>} allowEntries Baseline
|
|
275
|
+
* (already-merged) allow list
|
|
276
|
+
* @returns {{ blocked: Finding[], allowed: Finding[] }} Findings that still
|
|
277
|
+
* block vs. findings covered by a recorded exception
|
|
278
|
+
*/
|
|
279
|
+
export function applyAllowList(findings, allowEntries) {
|
|
280
|
+
const blocked = [];
|
|
281
|
+
const allowed = [];
|
|
282
|
+
for (const finding of findings) {
|
|
283
|
+
const isAllowed =
|
|
284
|
+
finding.type !== "allow-added" &&
|
|
285
|
+
allowEntries.some(
|
|
286
|
+
e =>
|
|
287
|
+
(finding.file === e.file || finding.file.endsWith(`/${e.file}`)) &&
|
|
288
|
+
(e.key === "*" || e.key === finding.key)
|
|
289
|
+
);
|
|
290
|
+
if (isAllowed) allowed.push(finding);
|
|
291
|
+
else blocked.push(finding);
|
|
292
|
+
}
|
|
293
|
+
return { blocked, allowed };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Render the operator-facing block message.
|
|
298
|
+
* @param {Finding[]} findings Blocked findings
|
|
299
|
+
* @returns {string} Multi-line report explaining what weakened and the
|
|
300
|
+
* human-approved exception path
|
|
301
|
+
*/
|
|
302
|
+
export function formatReport(findings) {
|
|
303
|
+
return [
|
|
304
|
+
"⛔ Quality gate weakened — blocked by the threshold ratchet.",
|
|
305
|
+
"",
|
|
306
|
+
...findings.map(f => ` • ${f.message}`),
|
|
307
|
+
"",
|
|
308
|
+
"Quality thresholds are a one-way ratchet: they may tighten but never",
|
|
309
|
+
"loosen. Fix the code so it meets the current gate instead of lowering the",
|
|
310
|
+
"gate. If a human decides an exception is genuinely correct, they record it",
|
|
311
|
+
"in .lisa.config.json under thresholdRatchet.allow (with a reason) in a",
|
|
312
|
+
"separate human-approved change; this check honors exceptions only after",
|
|
313
|
+
"they are merged.",
|
|
314
|
+
].join("\n");
|
|
315
|
+
}
|