@codyswann/lisa 4.8.2 → 4.8.4

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.
Files changed (83) hide show
  1. package/all/copy-overwrite/scripts/lisa-gates.mjs +212 -3
  2. package/all/copy-overwrite/scripts/lisa-reconcile-policy.mjs +11 -0
  3. package/all/copy-overwrite/scripts/lisa-run-gates.mjs +232 -67
  4. package/dist/cli/gate-report-cells.d.ts +1 -1
  5. package/dist/cli/gate-report-cells.d.ts.map +1 -1
  6. package/dist/cli/gate-report-cells.js +18 -2
  7. package/dist/cli/gate-report-cells.js.map +1 -1
  8. package/dist/cli/gate-report-joins.d.ts.map +1 -1
  9. package/dist/cli/gate-report-joins.js +36 -4
  10. package/dist/cli/gate-report-joins.js.map +1 -1
  11. package/dist/cli/gate-report-registry.d.ts +30 -0
  12. package/dist/cli/gate-report-registry.d.ts.map +1 -1
  13. package/dist/cli/gate-report-registry.js +25 -0
  14. package/dist/cli/gate-report-registry.js.map +1 -1
  15. package/dist/core/gate-declaration-drift.d.ts +18 -0
  16. package/dist/core/gate-declaration-drift.d.ts.map +1 -1
  17. package/dist/core/gate-declaration-drift.js +30 -1
  18. package/dist/core/gate-declaration-drift.js.map +1 -1
  19. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  20. package/dist/core/lisa-owned-hash-ledger.js +6 -0
  21. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  22. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  23. package/dist/core/upstream-evidence-manifest.js +9 -6
  24. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  25. package/package.json +1 -1
  26. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  28. package/plugins/lisa/.codex-plugin/skills/lisa-doctor/SKILL.md +41 -0
  29. package/plugins/lisa/skills/lisa-doctor/SKILL.md +41 -0
  30. package/plugins/lisa-agy/plugin.json +1 -1
  31. package/plugins/lisa-agy/skills/lisa-doctor/SKILL.md +41 -0
  32. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  34. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  35. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  38. package/plugins/lisa-copilot/skills/lisa-doctor/SKILL.md +41 -0
  39. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  40. package/plugins/lisa-cursor/skills/lisa-doctor/SKILL.md +41 -0
  41. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  43. package/plugins/lisa-expo-agy/plugin.json +1 -1
  44. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  48. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  49. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  53. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  54. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  58. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  59. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  63. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  64. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  67. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  68. package/plugins/lisa-rails-agy/plugin.json +1 -1
  69. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  71. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  72. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  73. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  74. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  75. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  76. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  77. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  78. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  79. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  80. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  81. package/plugins/src/base/skills/lisa-doctor/SKILL.md +41 -0
  82. package/scripts/lisa-github-rulesets.sh +15 -5
  83. package/scripts/lisa-ruleset-payload.mjs +30 -0
package/package.json CHANGED
@@ -148,7 +148,7 @@
148
148
  "zod-validation-error": "^4.0.0"
149
149
  },
150
150
  "name": "@codyswann/lisa",
151
- "version": "4.8.2",
151
+ "version": "4.8.4",
152
152
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
153
153
  "main": "dist/index.js",
154
154
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -569,6 +569,37 @@ additive-only `addRequiredChecks`, which is still read but can never express "st
569
569
  Both surfaces name the ruleset a repair writes to, so `--ruleset` is needed only for a
570
570
  gate-DERIVED context on a repository whose rulesets are ambiguous.
571
571
 
572
+ **2b. A gate proved OUTSIDE the quality facade names its own check-run chain.** A run-mode gate's
573
+ required context is derived by prefixing the gate's label with the chain of jobs that reach the
574
+ facade. When a project proves one property from a workflow of its own instead, that chain describes
575
+ a route that never reaches the prover, and the derived name is one nothing posts — which does not
576
+ turn a pull request red, it holds it at "Expected — Waiting for status to be reported" for ever.
577
+ Declare the real chain on that moment:
578
+
579
+ ```json
580
+ "gates": {
581
+ "e2e-browser": {
582
+ "run": "test:e2e:pr",
583
+ "pull-request": {
584
+ "level": "required",
585
+ "caller_chain": ["🎭 PR Browser Coverage"]
586
+ }
587
+ }
588
+ }
589
+ ```
590
+
591
+ Read the chain off a completed run, never off the YAML: a check run's reported name is the
592
+ `/`-joined chain of the JOB names that reach it, outermost first, and the top workflow's own `name:`
593
+ never appears in it. Take it from
594
+ `gh api repos/OWNER/NAME/commits/SHA/check-runs --paginate --slurp`, or let
595
+ `lisa-gates.mjs verify-contexts --posted=<file>` compare a derivation against what actually posted.
596
+ Either spelling works — `["A", "B"]` or `"A / B"` — and the override replaces the caller's chain for
597
+ that gate only; every other gate keeps deriving exactly what it derived before. It is declared per
598
+ MOMENT, because the same job posts a one-level name on the pull-request path and a two-level one on
599
+ the release path. Declaring it on the whole gate, on an `await`ing moment, at a moment that posts no
600
+ status, or with a blank level is refused by `lisa-gates.mjs validate` — at declaration time, which
601
+ is the last point at which any of those can still fail loudly.
602
+
572
603
  **3. Keep both names during a rename.** `--previous=<old label>` requires the old and the new
573
604
  context simultaneously for one release. Without it the reconciliation reports the still-live old
574
605
  context as EXTRA (and `--prune` would delete it) while in-flight pull requests wait on a context
@@ -588,6 +619,16 @@ That rule construction is itself config-driven now. `all/github-rulesets/base.js
588
619
  `policy.review.required_approving_review_count`, none of which config could express while the
589
620
  template owned them.
590
621
 
622
+ `policy.review.require_extra_approval_for_unattributed_changes` is declarable for a reason worth
623
+ stating, because its default is not "leave it alone". Measured against the live rulesets API on
624
+ 2026-08-25: a `pull_request` rule sent WITHOUT the parameter came back with it `true`; set
625
+ explicitly to `false` it came back `false`; sent again without it, it came back `true`. GitHub
626
+ re-applies its own default on every write that omits the field. So an operator who wants it OFF
627
+ cannot hold it off unless the payload names it, and an operator who wants it ON is recording no
628
+ choice at all — the value is whatever GitHub's default happens to be. Declaring it pins the choice
629
+ and makes it visible to the reconciler; declaring nothing keeps the payload byte-for-byte what it
630
+ was, parameter absent.
631
+
591
632
  ## Secrets configuration
592
633
 
593
634
  Run the secrets health checks through the skill that owns the contract, rather than reimplementing
@@ -569,6 +569,37 @@ additive-only `addRequiredChecks`, which is still read but can never express "st
569
569
  Both surfaces name the ruleset a repair writes to, so `--ruleset` is needed only for a
570
570
  gate-DERIVED context on a repository whose rulesets are ambiguous.
571
571
 
572
+ **2b. A gate proved OUTSIDE the quality facade names its own check-run chain.** A run-mode gate's
573
+ required context is derived by prefixing the gate's label with the chain of jobs that reach the
574
+ facade. When a project proves one property from a workflow of its own instead, that chain describes
575
+ a route that never reaches the prover, and the derived name is one nothing posts — which does not
576
+ turn a pull request red, it holds it at "Expected — Waiting for status to be reported" for ever.
577
+ Declare the real chain on that moment:
578
+
579
+ ```json
580
+ "gates": {
581
+ "e2e-browser": {
582
+ "run": "test:e2e:pr",
583
+ "pull-request": {
584
+ "level": "required",
585
+ "caller_chain": ["🎭 PR Browser Coverage"]
586
+ }
587
+ }
588
+ }
589
+ ```
590
+
591
+ Read the chain off a completed run, never off the YAML: a check run's reported name is the
592
+ `/`-joined chain of the JOB names that reach it, outermost first, and the top workflow's own `name:`
593
+ never appears in it. Take it from
594
+ `gh api repos/OWNER/NAME/commits/SHA/check-runs --paginate --slurp`, or let
595
+ `lisa-gates.mjs verify-contexts --posted=<file>` compare a derivation against what actually posted.
596
+ Either spelling works — `["A", "B"]` or `"A / B"` — and the override replaces the caller's chain for
597
+ that gate only; every other gate keeps deriving exactly what it derived before. It is declared per
598
+ MOMENT, because the same job posts a one-level name on the pull-request path and a two-level one on
599
+ the release path. Declaring it on the whole gate, on an `await`ing moment, at a moment that posts no
600
+ status, or with a blank level is refused by `lisa-gates.mjs validate` — at declaration time, which
601
+ is the last point at which any of those can still fail loudly.
602
+
572
603
  **3. Keep both names during a rename.** `--previous=<old label>` requires the old and the new
573
604
  context simultaneously for one release. Without it the reconciliation reports the still-live old
574
605
  context as EXTRA (and `--prune` would delete it) while in-flight pull requests wait on a context
@@ -588,6 +619,16 @@ That rule construction is itself config-driven now. `all/github-rulesets/base.js
588
619
  `policy.review.required_approving_review_count`, none of which config could express while the
589
620
  template owned them.
590
621
 
622
+ `policy.review.require_extra_approval_for_unattributed_changes` is declarable for a reason worth
623
+ stating, because its default is not "leave it alone". Measured against the live rulesets API on
624
+ 2026-08-25: a `pull_request` rule sent WITHOUT the parameter came back with it `true`; set
625
+ explicitly to `false` it came back `false`; sent again without it, it came back `true`. GitHub
626
+ re-applies its own default on every write that omits the field. So an operator who wants it OFF
627
+ cannot hold it off unless the payload names it, and an operator who wants it ON is recording no
628
+ choice at all — the value is whatever GitHub's default happens to be. Declaring it pins the choice
629
+ and makes it visible to the reconciler; declaring nothing keeps the payload byte-for-byte what it
630
+ was, parameter absent.
631
+
591
632
  ## Secrets configuration
592
633
 
593
634
  Run the secrets health checks through the skill that owns the contract, rather than reimplementing
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -569,6 +569,37 @@ additive-only `addRequiredChecks`, which is still read but can never express "st
569
569
  Both surfaces name the ruleset a repair writes to, so `--ruleset` is needed only for a
570
570
  gate-DERIVED context on a repository whose rulesets are ambiguous.
571
571
 
572
+ **2b. A gate proved OUTSIDE the quality facade names its own check-run chain.** A run-mode gate's
573
+ required context is derived by prefixing the gate's label with the chain of jobs that reach the
574
+ facade. When a project proves one property from a workflow of its own instead, that chain describes
575
+ a route that never reaches the prover, and the derived name is one nothing posts — which does not
576
+ turn a pull request red, it holds it at "Expected — Waiting for status to be reported" for ever.
577
+ Declare the real chain on that moment:
578
+
579
+ ```json
580
+ "gates": {
581
+ "e2e-browser": {
582
+ "run": "test:e2e:pr",
583
+ "pull-request": {
584
+ "level": "required",
585
+ "caller_chain": ["🎭 PR Browser Coverage"]
586
+ }
587
+ }
588
+ }
589
+ ```
590
+
591
+ Read the chain off a completed run, never off the YAML: a check run's reported name is the
592
+ `/`-joined chain of the JOB names that reach it, outermost first, and the top workflow's own `name:`
593
+ never appears in it. Take it from
594
+ `gh api repos/OWNER/NAME/commits/SHA/check-runs --paginate --slurp`, or let
595
+ `lisa-gates.mjs verify-contexts --posted=<file>` compare a derivation against what actually posted.
596
+ Either spelling works — `["A", "B"]` or `"A / B"` — and the override replaces the caller's chain for
597
+ that gate only; every other gate keeps deriving exactly what it derived before. It is declared per
598
+ MOMENT, because the same job posts a one-level name on the pull-request path and a two-level one on
599
+ the release path. Declaring it on the whole gate, on an `await`ing moment, at a moment that posts no
600
+ status, or with a blank level is refused by `lisa-gates.mjs validate` — at declaration time, which
601
+ is the last point at which any of those can still fail loudly.
602
+
572
603
  **3. Keep both names during a rename.** `--previous=<old label>` requires the old and the new
573
604
  context simultaneously for one release. Without it the reconciliation reports the still-live old
574
605
  context as EXTRA (and `--prune` would delete it) while in-flight pull requests wait on a context
@@ -588,6 +619,16 @@ That rule construction is itself config-driven now. `all/github-rulesets/base.js
588
619
  `policy.review.required_approving_review_count`, none of which config could express while the
589
620
  template owned them.
590
621
 
622
+ `policy.review.require_extra_approval_for_unattributed_changes` is declarable for a reason worth
623
+ stating, because its default is not "leave it alone". Measured against the live rulesets API on
624
+ 2026-08-25: a `pull_request` rule sent WITHOUT the parameter came back with it `true`; set
625
+ explicitly to `false` it came back `false`; sent again without it, it came back `true`. GitHub
626
+ re-applies its own default on every write that omits the field. So an operator who wants it OFF
627
+ cannot hold it off unless the payload names it, and an operator who wants it ON is recording no
628
+ choice at all — the value is whatever GitHub's default happens to be. Declaring it pins the choice
629
+ and makes it visible to the reconciler; declaring nothing keeps the payload byte-for-byte what it
630
+ was, parameter absent.
631
+
591
632
  ## Secrets configuration
592
633
 
593
634
  Run the secrets health checks through the skill that owns the contract, rather than reimplementing
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "AWS CDK-specific Lisa plugin.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -569,6 +569,37 @@ additive-only `addRequiredChecks`, which is still read but can never express "st
569
569
  Both surfaces name the ruleset a repair writes to, so `--ruleset` is needed only for a
570
570
  gate-DERIVED context on a repository whose rulesets are ambiguous.
571
571
 
572
+ **2b. A gate proved OUTSIDE the quality facade names its own check-run chain.** A run-mode gate's
573
+ required context is derived by prefixing the gate's label with the chain of jobs that reach the
574
+ facade. When a project proves one property from a workflow of its own instead, that chain describes
575
+ a route that never reaches the prover, and the derived name is one nothing posts — which does not
576
+ turn a pull request red, it holds it at "Expected — Waiting for status to be reported" for ever.
577
+ Declare the real chain on that moment:
578
+
579
+ ```json
580
+ "gates": {
581
+ "e2e-browser": {
582
+ "run": "test:e2e:pr",
583
+ "pull-request": {
584
+ "level": "required",
585
+ "caller_chain": ["🎭 PR Browser Coverage"]
586
+ }
587
+ }
588
+ }
589
+ ```
590
+
591
+ Read the chain off a completed run, never off the YAML: a check run's reported name is the
592
+ `/`-joined chain of the JOB names that reach it, outermost first, and the top workflow's own `name:`
593
+ never appears in it. Take it from
594
+ `gh api repos/OWNER/NAME/commits/SHA/check-runs --paginate --slurp`, or let
595
+ `lisa-gates.mjs verify-contexts --posted=<file>` compare a derivation against what actually posted.
596
+ Either spelling works — `["A", "B"]` or `"A / B"` — and the override replaces the caller's chain for
597
+ that gate only; every other gate keeps deriving exactly what it derived before. It is declared per
598
+ MOMENT, because the same job posts a one-level name on the pull-request path and a two-level one on
599
+ the release path. Declaring it on the whole gate, on an `await`ing moment, at a moment that posts no
600
+ status, or with a blank level is refused by `lisa-gates.mjs validate` — at declaration time, which
601
+ is the last point at which any of those can still fail loudly.
602
+
572
603
  **3. Keep both names during a rename.** `--previous=<old label>` requires the old and the new
573
604
  context simultaneously for one release. Without it the reconciliation reports the still-live old
574
605
  context as EXTRA (and `--prune` would delete it) while in-flight pull requests wait on a context
@@ -588,6 +619,16 @@ That rule construction is itself config-driven now. `all/github-rulesets/base.js
588
619
  `policy.review.required_approving_review_count`, none of which config could express while the
589
620
  template owned them.
590
621
 
622
+ `policy.review.require_extra_approval_for_unattributed_changes` is declarable for a reason worth
623
+ stating, because its default is not "leave it alone". Measured against the live rulesets API on
624
+ 2026-08-25: a `pull_request` rule sent WITHOUT the parameter came back with it `true`; set
625
+ explicitly to `false` it came back `false`; sent again without it, it came back `true`. GitHub
626
+ re-applies its own default on every write that omits the field. So an operator who wants it OFF
627
+ cannot hold it off unless the payload names it, and an operator who wants it ON is recording no
628
+ choice at all — the value is whatever GitHub's default happens to be. Declaring it pins the choice
629
+ and makes it visible to the reconciler; declaring nothing keeps the payload byte-for-byte what it
630
+ was, parameter absent.
631
+
591
632
  ## Secrets configuration
592
633
 
593
634
  Run the secrets health checks through the skill that owns the contract, rather than reimplementing
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -569,6 +569,37 @@ additive-only `addRequiredChecks`, which is still read but can never express "st
569
569
  Both surfaces name the ruleset a repair writes to, so `--ruleset` is needed only for a
570
570
  gate-DERIVED context on a repository whose rulesets are ambiguous.
571
571
 
572
+ **2b. A gate proved OUTSIDE the quality facade names its own check-run chain.** A run-mode gate's
573
+ required context is derived by prefixing the gate's label with the chain of jobs that reach the
574
+ facade. When a project proves one property from a workflow of its own instead, that chain describes
575
+ a route that never reaches the prover, and the derived name is one nothing posts — which does not
576
+ turn a pull request red, it holds it at "Expected — Waiting for status to be reported" for ever.
577
+ Declare the real chain on that moment:
578
+
579
+ ```json
580
+ "gates": {
581
+ "e2e-browser": {
582
+ "run": "test:e2e:pr",
583
+ "pull-request": {
584
+ "level": "required",
585
+ "caller_chain": ["🎭 PR Browser Coverage"]
586
+ }
587
+ }
588
+ }
589
+ ```
590
+
591
+ Read the chain off a completed run, never off the YAML: a check run's reported name is the
592
+ `/`-joined chain of the JOB names that reach it, outermost first, and the top workflow's own `name:`
593
+ never appears in it. Take it from
594
+ `gh api repos/OWNER/NAME/commits/SHA/check-runs --paginate --slurp`, or let
595
+ `lisa-gates.mjs verify-contexts --posted=<file>` compare a derivation against what actually posted.
596
+ Either spelling works — `["A", "B"]` or `"A / B"` — and the override replaces the caller's chain for
597
+ that gate only; every other gate keeps deriving exactly what it derived before. It is declared per
598
+ MOMENT, because the same job posts a one-level name on the pull-request path and a two-level one on
599
+ the release path. Declaring it on the whole gate, on an `await`ing moment, at a moment that posts no
600
+ status, or with a blank level is refused by `lisa-gates.mjs validate` — at declaration time, which
601
+ is the last point at which any of those can still fail loudly.
602
+
572
603
  **3. Keep both names during a rename.** `--previous=<old label>` requires the old and the new
573
604
  context simultaneously for one release. Without it the reconciliation reports the still-live old
574
605
  context as EXTRA (and `--prune` would delete it) while in-flight pull requests wait on a context
@@ -588,6 +619,16 @@ That rule construction is itself config-driven now. `all/github-rulesets/base.js
588
619
  `policy.review.required_approving_review_count`, none of which config could express while the
589
620
  template owned them.
590
621
 
622
+ `policy.review.require_extra_approval_for_unattributed_changes` is declarable for a reason worth
623
+ stating, because its default is not "leave it alone". Measured against the live rulesets API on
624
+ 2026-08-25: a `pull_request` rule sent WITHOUT the parameter came back with it `true`; set
625
+ explicitly to `false` it came back `false`; sent again without it, it came back `true`. GitHub
626
+ re-applies its own default on every write that omits the field. So an operator who wants it OFF
627
+ cannot hold it off unless the payload names it, and an operator who wants it ON is recording no
628
+ choice at all — the value is whatever GitHub's default happens to be. Declaring it pins the choice
629
+ and makes it visible to the reconciler; declaring nothing keeps the payload byte-for-byte what it
630
+ was, parameter absent.
631
+
591
632
  ## Secrets configuration
592
633
 
593
634
  Run the secrets health checks through the skill that owns the contract, rather than reimplementing
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Expo and React Native-specific skills, agents, rules, and MCP servers.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Harper/Fabric-specific Lisa rules for TypeScript component apps.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "NestJS-specific skills and migration write-protection hooks.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Ruby on Rails-specific skills and hooks for RuboCop and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"