@codyswann/lisa 4.22.12 → 4.22.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/all/copy-overwrite/scripts/lib/gate-failure-diagnosis.mjs +4 -2
- package/all/copy-overwrite/scripts/lib/process-tree-runner.mjs +62 -11
- package/all/copy-overwrite/scripts/lisa-environment-prepare.mjs +10 -1
- package/all/copy-overwrite/scripts/lisa-floor-collisions.mjs +1 -0
- package/all/copy-overwrite/scripts/lisa-hooks/block-no-verify.sh +43 -2
- package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +19 -5
- package/all/copy-overwrite/scripts/lisa-reconcile-policy.mjs +13 -1
- package/all/copy-overwrite/scripts/lisa-schema-validate.mjs +2 -2
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +24 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.d.ts +5 -0
- package/dist/core/nightly-e2e-guard-behavior-certificate.d.ts.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +8 -2
- package/dist/core/nightly-e2e-guard-behavior-certificate.js.map +1 -1
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +33 -32
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/opencode/plugin-templates/block-no-verify.sh +43 -2
- package/dist/opencode/plugin-templates/lisa-block-direct-issue-create.ts +4 -3
- package/dist/opencode/plugin-templates/lisa-block-no-verify.ts +11 -2
- package/dist/opencode/plugin-templates/parity-safety-net.sh +19 -5
- package/expo/create-only/.github/workflows/nightly-e2e-report.yml +1 -0
- package/package.json +3 -3
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-github-build-intake/SKILL.md +7 -8
- package/plugins/lisa/.codex-plugin/skills/lisa-github-prd-intake/SKILL.md +10 -11
- package/plugins/lisa/.codex-plugin/skills/lisa-github-sync/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-github-write-prd/SKILL.md +14 -12
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-build-intake/SKILL.md +7 -8
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-journey/scripts/parse-plan.py +54 -12
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-build-intake/SKILL.md +9 -10
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-evidence/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-sync/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-repair-intake/SKILL.md +2 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +4 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-tracker-sync/SKILL.md +2 -2
- package/plugins/lisa/hooks/block-no-verify.sh +43 -2
- package/plugins/lisa/hooks/parity-safety-net.sh +19 -5
- package/plugins/lisa/hooks/threshold-ratchet-families.mjs +10 -1
- package/plugins/lisa/rules/reference/config-resolution.md +0 -4
- package/plugins/lisa/rules/reference/leaf-only-lifecycle.md +1 -1
- package/plugins/lisa/scripts/resolve-lifecycle-role.mjs +15 -3
- package/plugins/lisa/skills/lisa-github-build-intake/SKILL.md +7 -8
- package/plugins/lisa/skills/lisa-github-prd-intake/SKILL.md +10 -11
- package/plugins/lisa/skills/lisa-github-sync/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-github-write-prd/SKILL.md +14 -12
- package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +7 -8
- package/plugins/lisa/skills/lisa-jira-journey/scripts/parse-plan.py +54 -12
- package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +9 -10
- package/plugins/lisa/skills/lisa-linear-evidence/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-linear-sync/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-repair-intake/SKILL.md +2 -1
- package/plugins/lisa/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +4 -3
- package/plugins/lisa/skills/lisa-tracker-sync/SKILL.md +2 -2
- package/plugins/lisa-agy/hooks/parity-safety-net.sh +19 -5
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/scripts/resolve-lifecycle-role.mjs +15 -3
- package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +7 -8
- package/plugins/lisa-agy/skills/lisa-github-prd-intake/SKILL.md +10 -11
- package/plugins/lisa-agy/skills/lisa-github-sync/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-github-write-prd/SKILL.md +14 -12
- package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +7 -8
- package/plugins/lisa-agy/skills/lisa-jira-journey/scripts/parse-plan.py +54 -12
- package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +9 -10
- package/plugins/lisa-agy/skills/lisa-linear-evidence/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-linear-sync/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-repair-intake/SKILL.md +2 -1
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +4 -3
- package/plugins/lisa-agy/skills/lisa-tracker-sync/SKILL.md +2 -2
- 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/block-no-verify.sh +43 -2
- package/plugins/lisa-copilot/hooks/parity-safety-net.sh +19 -5
- package/plugins/lisa-copilot/hooks/threshold-ratchet-families.mjs +10 -1
- package/plugins/lisa-copilot/rules/reference/config-resolution.md +0 -4
- package/plugins/lisa-copilot/rules/reference/leaf-only-lifecycle.md +1 -1
- package/plugins/lisa-copilot/scripts/resolve-lifecycle-role.mjs +15 -3
- package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +7 -8
- package/plugins/lisa-copilot/skills/lisa-github-prd-intake/SKILL.md +10 -11
- package/plugins/lisa-copilot/skills/lisa-github-sync/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-github-write-prd/SKILL.md +14 -12
- package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +7 -8
- package/plugins/lisa-copilot/skills/lisa-jira-journey/scripts/parse-plan.py +54 -12
- package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +9 -10
- package/plugins/lisa-copilot/skills/lisa-linear-evidence/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-linear-sync/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-repair-intake/SKILL.md +2 -1
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +4 -3
- package/plugins/lisa-copilot/skills/lisa-tracker-sync/SKILL.md +2 -2
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/block-no-verify.sh +43 -2
- package/plugins/lisa-cursor/hooks/parity-safety-net.sh +19 -5
- package/plugins/lisa-cursor/hooks/threshold-ratchet-families.mjs +10 -1
- package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +0 -4
- package/plugins/lisa-cursor/rules/leaf-only-lifecycle-reference.mdc +1 -1
- package/plugins/lisa-cursor/scripts/resolve-lifecycle-role.mjs +15 -3
- package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +7 -8
- package/plugins/lisa-cursor/skills/lisa-github-prd-intake/SKILL.md +10 -11
- package/plugins/lisa-cursor/skills/lisa-github-sync/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-github-write-prd/SKILL.md +14 -12
- package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +7 -8
- package/plugins/lisa-cursor/skills/lisa-jira-journey/scripts/parse-plan.py +54 -12
- package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +9 -10
- package/plugins/lisa-cursor/skills/lisa-linear-evidence/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-linear-sync/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-repair-intake/SKILL.md +2 -1
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +4 -3
- package/plugins/lisa-cursor/skills/lisa-tracker-sync/SKILL.md +2 -2
- 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/block-no-verify.sh +43 -2
- package/plugins/src/base/hooks/parity-safety-net.sh +19 -5
- package/plugins/src/base/hooks/threshold-ratchet-families.mjs +10 -1
- package/plugins/src/base/rules/reference/config-resolution.md +0 -4
- package/plugins/src/base/rules/reference/leaf-only-lifecycle.md +1 -1
- package/plugins/src/base/scripts/resolve-lifecycle-role.mjs +15 -3
- package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +7 -8
- package/plugins/src/base/skills/lisa-github-prd-intake/SKILL.md +10 -11
- package/plugins/src/base/skills/lisa-github-sync/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-github-write-prd/SKILL.md +14 -12
- package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +7 -8
- package/plugins/src/base/skills/lisa-jira-journey/scripts/parse-plan.py +54 -12
- package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +9 -10
- package/plugins/src/base/skills/lisa-linear-evidence/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-linear-sync/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-repair-intake/SKILL.md +2 -1
- package/plugins/src/base/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +4 -3
- package/plugins/src/base/skills/lisa-tracker-sync/SKILL.md +2 -2
- package/rails/copy-overwrite/scripts/threshold-ratchet-families.mjs +10 -1
- package/typescript/copy-overwrite/scripts/check-nightly-e2e-health.mjs +10 -13
- package/typescript/copy-overwrite/scripts/check-skipped-required-checks.mjs +34 -3
- package/typescript/copy-overwrite/scripts/lisa-mutation.mjs +17 -1
- package/typescript/copy-overwrite/scripts/threshold-ratchet-families.mjs +10 -1
|
@@ -21,20 +21,19 @@ This skill is the destination of the `lisa-tracker-build-intake` shim when `trac
|
|
|
21
21
|
Build-queue **workflow state** names are read from `.lisa.config.json` `linear.workflow.*`, falling back to defaults documented in the `config-resolution` rule. Bash pattern:
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
# Read role with default fallback. Local overrides global per-key.
|
|
25
24
|
read_role() {
|
|
26
25
|
# Single resolver — see config-resolution "The single resolver".
|
|
27
26
|
# Exit 0 + empty output means an OPTIONAL role is unset: skip that transition,
|
|
28
|
-
# never substitute
|
|
29
|
-
#
|
|
30
|
-
node "${CLAUDE_PLUGIN_ROOT}/scripts/resolve-lifecycle-role.mjs" \
|
|
31
|
-
--role "$1" --vendor linear --intent "${
|
|
27
|
+
# never substitute a default. Any non-zero status is a resolver failure and
|
|
28
|
+
# remains visible to the caller.
|
|
29
|
+
node "${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-plugins/lisa}}/scripts/resolve-lifecycle-role.mjs" \
|
|
30
|
+
--role "$1" --vendor linear --intent "${2:-read}"
|
|
32
31
|
}
|
|
33
32
|
|
|
34
|
-
READY=$(read_role ready
|
|
35
|
-
CLAIMED=$(read_role claimed
|
|
36
|
-
REVIEW=$(read_role review
|
|
37
|
-
BLOCKED=$(read_role blocked
|
|
33
|
+
READY=$(read_role ready read) || exit $?
|
|
34
|
+
CLAIMED=$(read_role claimed write) || exit $?
|
|
35
|
+
REVIEW=$(read_role review write) || exit $?
|
|
36
|
+
BLOCKED=$(read_role blocked read) || exit $?
|
|
38
37
|
```
|
|
39
38
|
|
|
40
39
|
For env-keyed `done`, resolve the env first, then look up `done[<env>]`:
|
|
@@ -69,7 +68,7 @@ else
|
|
|
69
68
|
fi
|
|
70
69
|
```
|
|
71
70
|
|
|
72
|
-
In prose below, the role names refer to the
|
|
71
|
+
In prose below, the role names refer to the configured **states**: e.g. "the `ready` state" means whatever `linear.workflow.ready` resolves to. Required roles must be configured; optional roles carry no default.
|
|
73
72
|
|
|
74
73
|
## Why native states, not labels
|
|
75
74
|
|
|
@@ -109,6 +109,6 @@ Return:
|
|
|
109
109
|
## Rules
|
|
110
110
|
|
|
111
111
|
- Never modify the Issue description as part of evidence posting — comments only. Description edits go through `lisa-linear-write-issue`.
|
|
112
|
-
- Never skip the
|
|
112
|
+
- Never skip the transition when `review` is configured. An empty `$REVIEW` is the one sanctioned skip (Phase 5): post the evidence comment, leave the Issue in `$CLAIMED`, and report the skip. Whenever `$REVIEW` is non-empty, the configured native state transition is mandatory.
|
|
113
113
|
- If `lisa-linear-access operation: save-comment` fails, retry once. If it fails again, surface the error — don't pretend the comment was posted.
|
|
114
114
|
- Do not delete prior comments. The history is the audit trail.
|
|
@@ -152,7 +152,7 @@ When the caller passes `--rollup`, this skill **derives a parent/container's wor
|
|
|
152
152
|
|
|
153
153
|
## Rules
|
|
154
154
|
|
|
155
|
-
- Never transition a leaf Issue's workflow `state` from this skill at
|
|
155
|
+
- Never transition a leaf Issue's workflow `state` from this skill except for the narrowly defined Phase 4b backstop that reopens a leaf auto-closed at an intermediate environment. `--rollup` remains the only other write path (parent derivation, per the `leaf-only-lifecycle` rule); every normal milestone SUGGESTS a transition in its comment and nothing more. The state is the lifecycle lane, so an unscoped write here would move the item in the build queue.
|
|
156
156
|
- Never resolve a role by searching the team's states. Only a name the project configured may be written; a `type`- or position-derived match may inform a read and must never supply a write target (`config-resolution`, R2).
|
|
157
157
|
- Rollup derives a *parent's* workflow state from its children and never rolls a parent into the human-owned ready lane (never `$READY`). It cites the `leaf-only-lifecycle` rule by slug rather than restating the state machine.
|
|
158
158
|
- Never post empty or minimal comments — if a milestone has no meaningful content, skip the post.
|
|
@@ -728,7 +728,8 @@ node "${CLAUDE_PLUGIN_ROOT}/scripts/rollup-blocker-classification.mjs" \
|
|
|
728
728
|
report already asks a person to decide.
|
|
729
729
|
|
|
730
730
|
3. **If the derived state differs from the parent's current state, apply it** via the vendor's
|
|
731
|
-
lifecycle write
|
|
731
|
+
lifecycle write: a JIRA transition; a GitHub label swap keeping exactly one `status:*`; or a
|
|
732
|
+
Linear `save-issue` update that writes the resolved workflow `stateId`, never a lifecycle label.
|
|
732
733
|
removing any conflicting stale build lifecycle role — **including a stale `ready`** the parent
|
|
733
734
|
should never carry. Post an idempotent `[lisa-repair-intake]` rollup note naming the derived
|
|
734
735
|
state and the child tally (honor the backoff window + fingerprint). Include that exact rendered
|
|
@@ -131,6 +131,7 @@ export function probe(name, exec = boundedChildOutput) {
|
|
|
131
131
|
return { present: true, version: extractVersion(out) };
|
|
132
132
|
} catch (err) {
|
|
133
133
|
if (err.code === "ENOENT") return { present: false, version: null };
|
|
134
|
+
if (err.code === "ETIMEDOUT") throw err;
|
|
134
135
|
const output = `${err.stdout ?? ""}${err.stderr ?? ""}`;
|
|
135
136
|
return { present: true, version: extractVersion(output) };
|
|
136
137
|
}
|
|
@@ -808,9 +809,9 @@ export function installUserSessionHook(repoRoot, options = {}) {
|
|
|
808
809
|
const dir = join(home, ".claude");
|
|
809
810
|
const settingsPath = join(dir, "settings.json");
|
|
810
811
|
// Settings stores shell source, so the absolute path still needs quoting.
|
|
811
|
-
//
|
|
812
|
-
//
|
|
813
|
-
const command = `bash ${
|
|
812
|
+
// Single quotes suppress parameter expansion and command substitution. An
|
|
813
|
+
// embedded quote closes, escapes, and reopens the quoted shell word.
|
|
814
|
+
const command = `bash '${script.replaceAll("'", `'\\''`)}'`;
|
|
814
815
|
|
|
815
816
|
let settings = {};
|
|
816
817
|
if (exists(settingsPath)) {
|
|
@@ -37,11 +37,11 @@ Two consequences the vendor arms must not restate differently:
|
|
|
37
37
|
Resolve roles through the shared resolver rather than an inlined helper:
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
node "${CLAUDE_PLUGIN_ROOT}/scripts/resolve-lifecycle-role.mjs" \
|
|
40
|
+
node "${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-plugins/lisa}}/scripts/resolve-lifecycle-role.mjs" \
|
|
41
41
|
--role <role> --vendor <jira|linear|github> --intent <read|write> [--env <env>]
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
Exit `0` with a value means configured; exit `0` with **empty** output means an optional role is unset — skip the transition; exit `2` means a required role is unset or a write was refused a fallback value.
|
|
44
|
+
Exit `0` with a value means configured; exit `0` with **empty** output means an optional role is unset — skip the transition; exit `2` means a required role is unset or a write was refused a fallback value. Any other exit is a resolver failure, never an unset role.
|
|
45
45
|
|
|
46
46
|
If `$ARGUMENTS` is empty, all vendor skills auto-detect a ticket reference from the active plan file (most recently modified `.md` in `plans/`).
|
|
47
47
|
|
|
@@ -82,7 +82,7 @@ export const FAMILIES = [
|
|
|
82
82
|
},
|
|
83
83
|
{
|
|
84
84
|
id: "lighthouse",
|
|
85
|
-
match: /(^|\/)lighthouserc-config\.json$/,
|
|
85
|
+
match: /(^|\/)(?:lighthouserc-config|lighthouserc|\.lighthouserc)\.json$/,
|
|
86
86
|
kind: "lighthouse",
|
|
87
87
|
},
|
|
88
88
|
];
|
|
@@ -101,6 +101,8 @@ export const LIGHTHOUSE_ASSERTION_DIRECTIONS = Object.freeze({
|
|
|
101
101
|
maxLength: "max",
|
|
102
102
|
});
|
|
103
103
|
|
|
104
|
+
const LIGHTHOUSE_LEVELS = Object.freeze({ off: 0, warn: 1, error: 2 });
|
|
105
|
+
|
|
104
106
|
/**
|
|
105
107
|
* Extract numeric Lighthouse assertions with a direction chosen per key.
|
|
106
108
|
*
|
|
@@ -136,6 +138,13 @@ export function extractLighthouseAssertions(conf) {
|
|
|
136
138
|
return out;
|
|
137
139
|
}
|
|
138
140
|
for (const [audit, spec] of Object.entries(assertions)) {
|
|
141
|
+
const level = Array.isArray(spec) ? spec[0] : spec;
|
|
142
|
+
if (typeof level === "string" && Object.hasOwn(LIGHTHOUSE_LEVELS, level)) {
|
|
143
|
+
out.set(`${audit}.$level`, {
|
|
144
|
+
value: LIGHTHOUSE_LEVELS[level],
|
|
145
|
+
direction: "min",
|
|
146
|
+
});
|
|
147
|
+
}
|
|
139
148
|
const options =
|
|
140
149
|
Array.isArray(spec) &&
|
|
141
150
|
spec.length >= 2 &&
|
|
@@ -2002,7 +2002,7 @@ function bypassParagraph(claim, context) {
|
|
|
2002
2002
|
function bypassLabelLines(context) {
|
|
2003
2003
|
const label = context.bypassLabel;
|
|
2004
2004
|
const measured = context.bypassLabelState;
|
|
2005
|
-
const state = measured?.state ?? BYPASS_LABEL_STATE.
|
|
2005
|
+
const state = measured?.state ?? BYPASS_LABEL_STATE.notMeasured;
|
|
2006
2006
|
if (state === BYPASS_LABEL_STATE.absent) {
|
|
2007
2007
|
return [
|
|
2008
2008
|
"> [!CAUTION]",
|
|
@@ -3377,7 +3377,8 @@ export function resolveSettings(env) {
|
|
|
3377
3377
|
freshnessHours: limits.freshnessHours,
|
|
3378
3378
|
bootstrapUntil: env.NIGHTLY_BOOTSTRAP_UNTIL || "",
|
|
3379
3379
|
bootstrapMaxDays: limits.bootstrapMaxDays,
|
|
3380
|
-
bypassLabel:
|
|
3380
|
+
bypassLabel:
|
|
3381
|
+
String(env.NIGHTLY_BYPASS_LABEL ?? "").trim() || DEFAULT_BYPASS_LABEL,
|
|
3381
3382
|
bypassMaxHours: limits.bypassMaxHours,
|
|
3382
3383
|
// An ADDITIONAL project rule, never a replacement — see `evaluateBypass`.
|
|
3383
3384
|
extraBypassReasonPattern: env.NIGHTLY_BYPASS_REASON_PATTERN || "",
|
|
@@ -3550,17 +3551,13 @@ export async function runReport(env, wait) {
|
|
|
3550
3551
|
const bypassLabelState =
|
|
3551
3552
|
requiredness.state === REQUIREDNESS.required
|
|
3552
3553
|
? await fetchBypassLabelState(settings.api, settings.bypassLabel, wait)
|
|
3553
|
-
:
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
"not measured — this gate is not required on this branch, so no waiver recipe is printed and a missing label waives nothing"
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
state: BYPASS_LABEL_STATE.unknown,
|
|
3561
|
-
detail:
|
|
3562
|
-
"not measured — whether this gate is required is unknown, so label state cannot establish whether a waiver recipe applies",
|
|
3563
|
-
});
|
|
3554
|
+
: Object.freeze({
|
|
3555
|
+
state: BYPASS_LABEL_STATE.notMeasured,
|
|
3556
|
+
detail:
|
|
3557
|
+
requiredness.state === REQUIREDNESS.notRequired
|
|
3558
|
+
? "not measured — this gate is not required on this branch, so no waiver recipe is printed and a missing label waives nothing"
|
|
3559
|
+
: "not measured — whether this gate is required could not be read, so the labels API was not queried",
|
|
3560
|
+
});
|
|
3564
3561
|
const context = {
|
|
3565
3562
|
branch: settings.branch,
|
|
3566
3563
|
label: settings.issueLabel,
|
|
@@ -188,7 +188,8 @@
|
|
|
188
188
|
* content problem and never says the word "permission".
|
|
189
189
|
*
|
|
190
190
|
* A refusal is not a finding: it fails, and under `"enforcement": "warn"` it is
|
|
191
|
-
* loud and exits 0
|
|
191
|
+
* normally loud and exits 0. `--require-review-evidence` is the deliberate
|
|
192
|
+
* exception: a refusal then blocks because the caller required proven evidence.
|
|
192
193
|
*
|
|
193
194
|
* ## `--fail-on-vacuous` — the supported exit code
|
|
194
195
|
*
|
|
@@ -1541,18 +1542,48 @@ export function fetchChecksForCommit(sha, repo) {
|
|
|
1541
1542
|
);
|
|
1542
1543
|
const runs = ghApiPaginatedArray(
|
|
1543
1544
|
`repos/${slug}/commits/${sha}/check-runs?per_page=100`,
|
|
1544
|
-
'[.check_runs[] | {name: .name, conclusion: .conclusion, description: (.output.title // "")}]'
|
|
1545
|
+
'[.check_runs[] | {id: .id, name: .name, conclusion: .conclusion, description: (.output.title // ""), completed_at: .completed_at, started_at: .started_at}]'
|
|
1545
1546
|
);
|
|
1546
1547
|
return mergeCheckRows(
|
|
1547
1548
|
statuses.map(row =>
|
|
1548
1549
|
normalizeCheckRow(row.name, row.state, row.description)
|
|
1549
1550
|
),
|
|
1550
|
-
runs.map(row =>
|
|
1551
|
+
newestCheckRuns(runs).map(row =>
|
|
1551
1552
|
normalizeCheckRow(row.name, row.conclusion, row.description)
|
|
1552
1553
|
)
|
|
1553
1554
|
);
|
|
1554
1555
|
}
|
|
1555
1556
|
|
|
1557
|
+
/**
|
|
1558
|
+
* Keep the newest check run for each name across every check suite on a SHA.
|
|
1559
|
+
* @param {ReadonlyArray<object>} runs Raw check-run rows with timestamps and ids.
|
|
1560
|
+
* @returns {object[]} One newest row per name.
|
|
1561
|
+
*/
|
|
1562
|
+
export function newestCheckRuns(runs) {
|
|
1563
|
+
const newest = new Map();
|
|
1564
|
+
const rank = row => {
|
|
1565
|
+
const timestamp = Date.parse(row.completed_at ?? row.started_at ?? "");
|
|
1566
|
+
return [Number.isFinite(timestamp) ? timestamp : 0, Number(row.id) || 0];
|
|
1567
|
+
};
|
|
1568
|
+
for (const row of runs) {
|
|
1569
|
+
const name = String(row?.name ?? "");
|
|
1570
|
+
const previous = newest.get(name);
|
|
1571
|
+
if (!previous) {
|
|
1572
|
+
newest.set(name, row);
|
|
1573
|
+
continue;
|
|
1574
|
+
}
|
|
1575
|
+
const [rowTime, rowId] = rank(row);
|
|
1576
|
+
const [previousTime, previousId] = rank(previous);
|
|
1577
|
+
if (
|
|
1578
|
+
rowTime > previousTime ||
|
|
1579
|
+
(rowTime === previousTime && rowId > previousId)
|
|
1580
|
+
) {
|
|
1581
|
+
newest.set(name, row);
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
return [...newest.values()];
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1556
1587
|
/**
|
|
1557
1588
|
* Collapse GitHub's two reporter APIs into one authoritative row per name.
|
|
1558
1589
|
*
|
|
@@ -184,6 +184,7 @@ export const OUTCOMES = Object.freeze({
|
|
|
184
184
|
dryRunTimeout: "mutation-gate: dry-run-timeout",
|
|
185
185
|
scoreBelowBreak: "mutation-gate: score-below-break",
|
|
186
186
|
runFailed: "mutation-gate: run-failed",
|
|
187
|
+
invalidMutatePattern: "mutation-gate: invalid-mutate-pattern",
|
|
187
188
|
childDeadline: "mutation-gate: child-deadline",
|
|
188
189
|
sandboxReclaimed: "mutation-gate: sandbox-reclaimed",
|
|
189
190
|
timeoutAccounting: "mutation-gate: timeout-accounting",
|
|
@@ -297,6 +298,11 @@ export const globToRegExp = glob => {
|
|
|
297
298
|
} else if (char === "," && braceDepth > 0) source += "|";
|
|
298
299
|
else source += char.replaceAll(/[.+^${}()|[\]\\]/gu, "\\$&");
|
|
299
300
|
}
|
|
301
|
+
if (braceDepth !== 0) {
|
|
302
|
+
throw new Error(
|
|
303
|
+
`mutate pattern "${glob}" has ${braceDepth} unclosed "{" — fix the brace alternation in your Stryker config.`
|
|
304
|
+
);
|
|
305
|
+
}
|
|
300
306
|
return new RegExp(`${source}$`, "u");
|
|
301
307
|
};
|
|
302
308
|
|
|
@@ -2013,7 +2019,17 @@ export const runGate = (cwd = process.cwd(), argv = []) => {
|
|
|
2013
2019
|
}
|
|
2014
2020
|
|
|
2015
2021
|
const declaration = resolveMutateDeclaration(cwd);
|
|
2016
|
-
|
|
2022
|
+
let patterns;
|
|
2023
|
+
try {
|
|
2024
|
+
patterns = compileMutatePatterns(declaration.mutate);
|
|
2025
|
+
} catch (error) {
|
|
2026
|
+
console.error(
|
|
2027
|
+
`❌ ${OUTCOMES.invalidMutatePattern}\n` +
|
|
2028
|
+
` ${error instanceof Error ? error.message : String(error)}\n` +
|
|
2029
|
+
` Fix the \`mutate\` patterns in ${declaration.source} before re-running.`
|
|
2030
|
+
);
|
|
2031
|
+
return 1;
|
|
2032
|
+
}
|
|
2017
2033
|
|
|
2018
2034
|
if (countMutateTargetsInRepo(cwd, patterns) === 0) {
|
|
2019
2035
|
console.error(
|
|
@@ -82,7 +82,7 @@ export const FAMILIES = [
|
|
|
82
82
|
},
|
|
83
83
|
{
|
|
84
84
|
id: "lighthouse",
|
|
85
|
-
match: /(^|\/)lighthouserc-config\.json$/,
|
|
85
|
+
match: /(^|\/)(?:lighthouserc-config|lighthouserc|\.lighthouserc)\.json$/,
|
|
86
86
|
kind: "lighthouse",
|
|
87
87
|
},
|
|
88
88
|
];
|
|
@@ -101,6 +101,8 @@ export const LIGHTHOUSE_ASSERTION_DIRECTIONS = Object.freeze({
|
|
|
101
101
|
maxLength: "max",
|
|
102
102
|
});
|
|
103
103
|
|
|
104
|
+
const LIGHTHOUSE_LEVELS = Object.freeze({ off: 0, warn: 1, error: 2 });
|
|
105
|
+
|
|
104
106
|
/**
|
|
105
107
|
* Extract numeric Lighthouse assertions with a direction chosen per key.
|
|
106
108
|
*
|
|
@@ -136,6 +138,13 @@ export function extractLighthouseAssertions(conf) {
|
|
|
136
138
|
return out;
|
|
137
139
|
}
|
|
138
140
|
for (const [audit, spec] of Object.entries(assertions)) {
|
|
141
|
+
const level = Array.isArray(spec) ? spec[0] : spec;
|
|
142
|
+
if (typeof level === "string" && Object.hasOwn(LIGHTHOUSE_LEVELS, level)) {
|
|
143
|
+
out.set(`${audit}.$level`, {
|
|
144
|
+
value: LIGHTHOUSE_LEVELS[level],
|
|
145
|
+
direction: "min",
|
|
146
|
+
});
|
|
147
|
+
}
|
|
139
148
|
const options =
|
|
140
149
|
Array.isArray(spec) &&
|
|
141
150
|
spec.length >= 2 &&
|