@codyswann/lisa 4.10.7 → 4.10.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/all/copy-overwrite/scripts/lisa-gates.mjs +82 -1
  2. package/all/copy-overwrite/scripts/lisa-hooks/block-no-verify.sh +2 -2
  3. package/dist/cli/doctor-declared-contexts.d.ts.map +1 -1
  4. package/dist/cli/doctor-declared-contexts.js +16 -0
  5. package/dist/cli/doctor-declared-contexts.js.map +1 -1
  6. package/dist/cli/gate-report-drift-section.d.ts.map +1 -1
  7. package/dist/cli/gate-report-drift-section.js +6 -0
  8. package/dist/cli/gate-report-drift-section.js.map +1 -1
  9. package/dist/cli/gate-report-joins.d.ts +0 -15
  10. package/dist/cli/gate-report-joins.d.ts.map +1 -1
  11. package/dist/cli/gate-report-joins.js +2 -1
  12. package/dist/cli/gate-report-joins.js.map +1 -1
  13. package/dist/cli/gate-report-registry.d.ts +8 -0
  14. package/dist/cli/gate-report-registry.d.ts.map +1 -1
  15. package/dist/cli/gate-report-registry.js.map +1 -1
  16. package/dist/cli/gate-report-types.d.ts +2 -1
  17. package/dist/cli/gate-report-types.d.ts.map +1 -1
  18. package/dist/cli/gate-report-types.js.map +1 -1
  19. package/dist/codex/scripts/block-no-verify.sh +1 -1
  20. package/dist/core/gate-context-owners.d.ts +122 -0
  21. package/dist/core/gate-context-owners.d.ts.map +1 -0
  22. package/dist/core/gate-context-owners.js +174 -0
  23. package/dist/core/gate-context-owners.js.map +1 -0
  24. package/dist/core/gate-declaration-drift.d.ts +56 -79
  25. package/dist/core/gate-declaration-drift.d.ts.map +1 -1
  26. package/dist/core/gate-declaration-drift.js +73 -115
  27. package/dist/core/gate-declaration-drift.js.map +1 -1
  28. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  29. package/dist/core/lisa-owned-hash-ledger.js +6 -0
  30. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  31. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  32. package/dist/core/upstream-evidence-manifest.js +10 -6
  33. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  34. package/dist/health/declared-checks-inspection.d.ts +0 -22
  35. package/dist/health/declared-checks-inspection.d.ts.map +1 -1
  36. package/dist/health/declared-checks-inspection.js +44 -5
  37. package/dist/health/declared-checks-inspection.js.map +1 -1
  38. package/dist/opencode/plugin-templates/block-no-verify.sh +2 -2
  39. package/package.json +1 -1
  40. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  42. package/plugins/lisa/hooks/block-no-verify.agy.sh +1 -1
  43. package/plugins/lisa/hooks/block-no-verify.sh +2 -2
  44. package/plugins/lisa-agy/hooks/block-no-verify.agy.sh +1 -1
  45. package/plugins/lisa-agy/plugin.json +1 -1
  46. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  48. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  49. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-copilot/hooks/block-no-verify.sh +2 -2
  53. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-cursor/hooks/block-no-verify.sh +2 -2
  55. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  57. package/plugins/lisa-expo-agy/plugin.json +1 -1
  58. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  62. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  63. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  67. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  68. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  71. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  72. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  73. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  75. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  76. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  77. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  78. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  79. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  80. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  81. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  82. package/plugins/lisa-rails-agy/plugin.json +1 -1
  83. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  84. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  85. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  86. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  87. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  88. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  89. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  90. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  91. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  92. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  93. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  94. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  95. package/plugins/src/base/hooks/block-no-verify.agy.sh +1 -1
  96. package/plugins/src/base/hooks/block-no-verify.sh +2 -2
  97. package/scripts/lisa-github-rulesets.sh +138 -0
  98. package/typescript/copy-overwrite/scripts/check-nightly-e2e-health.mjs +19 -14
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "4.10.7",
3
+ "version": "4.10.9",
4
4
  "description": "TypeScript-specific hooks for formatting, linting, and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "4.10.7",
3
+ "version": "4.10.9",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "4.10.7",
3
+ "version": "4.10.9",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "4.10.7",
3
+ "version": "4.10.9",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.10.7",
3
+ "version": "4.10.9",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.10.7",
3
+ "version": "4.10.9",
4
4
  "description": "Distributable LLM Wiki kernel — ingest, query, lint, and maintain a git-native markdown knowledge base across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.10.7",
3
+ "version": "4.10.9",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.10.7",
3
+ "version": "4.10.9",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.10.7",
3
+ "version": "4.10.9",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -356,7 +356,7 @@ for i, token in enumerate(normalized_tokens):
356
356
  key_match = re.match(r"git_config_key_\d+=(.*)$", lowered, re.DOTALL)
357
357
  if key_match and key_match.group(1).strip().strip("'\"") == "core.hookspath":
358
358
  sys.exit(1)
359
- if lowered.startswith("git_config_parameters="):
359
+ if re.match(r"git_config_parameters\+?=", lowered):
360
360
  parameters = token.split("=", 1)[1]
361
361
  try:
362
362
  configured = shlex.split(parameters, posix=True)
@@ -23,7 +23,7 @@
23
23
  # Add a name here in the same commit that closes a vector. A hardening that
24
24
  # forgets to is invisible to refresh, and shows up as an unexplained diff at
25
25
  # review time instead of a named capability.
26
- # lisa-guard-capabilities: no-verify-abbrev, husky-env, hookspath-allowlist, config-env, git-config-key, git-config-parameters, herestring-aware, no-verify-short
26
+ # lisa-guard-capabilities: no-verify-abbrev, husky-env, hookspath-allowlist, config-env, git-config-key, git-config-parameters, git-config-parameters-append, herestring-aware, no-verify-short
27
27
  #
28
28
  # Shell-token matching avoids false positives from issue bodies, heredocs, and
29
29
  # commit-message prose while still catching quoted real argv values such as
@@ -449,7 +449,7 @@ for i, token in enumerate(normalized_tokens):
449
449
  # `git -c` propagates command-scope config through GIT_CONFIG_PARAMETERS.
450
450
  # Parsing the value as Git's shell-quoted parameter list distinguishes the
451
451
  # key from an unrelated value that merely contains the same text.
452
- if lowered.startswith("git_config_parameters="):
452
+ if re.match(r"git_config_parameters\+?=", lowered):
453
453
  parameters = token.split("=", 1)[1]
454
454
  try:
455
455
  configured = shlex.split(parameters, posix=True)
@@ -901,6 +901,139 @@ collect_templates() {
901
901
  echo "${templates[@]}"
902
902
  }
903
903
 
904
+ ##############################################################################
905
+ # Retired Required Contexts
906
+ ##############################################################################
907
+
908
+ # Report required contexts that NOTHING WILL EVER POST, across EVERY ruleset the
909
+ # repository has — including the ones this script does not manage.
910
+ #
911
+ # This exists because the rest of the script cannot see the failure. Everything
912
+ # above is scoped per MANAGED ruleset name: it makes Lisa's four templates match,
913
+ # and it never looks at a ruleset somebody hand-made. #3067 is what that costs.
914
+ # 4.x renamed the quality job `🔎 AST Grep Scan` to `🔎 Structural Rules`. A
915
+ # repository whose hand-made ruleset still required the old name was left with a
916
+ # required check that can never report — and a required check that never reports
917
+ # does NOT fail a pull request. GitHub holds it at "Expected — Waiting for status
918
+ # to be reported", indefinitely: `mergeable: MERGEABLE`, `mergeStateStatus:
919
+ # BLOCKED`, every other check green, no red tick, no log to open, and nothing
920
+ # anywhere naming the cause. `--dry-run` on that repository correctly added the
921
+ # NEW name to the ruleset it manages and never mentioned the OLD name surviving
922
+ # in the one it does not. Running it to completion left the repository blocked.
923
+ #
924
+ # REPORT ONLY, and deliberately so. Lisa does not own a hand-made ruleset, and
925
+ # silently editing somebody else's branch protection is a bigger decision than
926
+ # this script carries — it would be Lisa deleting a requirement a human wrote,
927
+ # with no record of asking. What this owes the operator is the name and the
928
+ # reason, which is precisely what nothing gave them.
929
+ #
930
+ # The evidence is `previousLabels` in the shipped registry — Lisa's own record
931
+ # that Lisa renamed the job, which is why the claim is provable rather than
932
+ # inferred. A context that is simply absent from the derived set proves nothing:
933
+ # a repository may legitimately require a status posted by a third-party app or
934
+ # by a job its own CI defines, and a sweep that flagged every externally
935
+ # produced context would be noise an operator learns to ignore.
936
+ report_retired_contexts() {
937
+ local repo="$1"
938
+ local rulesets="$2"
939
+ local registry="$LISA_ROOT/all/copy-overwrite/scripts/lisa-gates.mjs"
940
+ local retired
941
+
942
+ # An unread retirement list is NOT "no retired contexts". Saying nothing here
943
+ # would report a repository clean on the strength of a read that never
944
+ # happened, which is the same defect one layer up.
945
+ if [[ ! -f "$registry" ]]; then
946
+ log_warning "Could not read $registry — retired required contexts were NOT checked. This is not a clean result."
947
+ return 0
948
+ fi
949
+ if ! retired=$(node "$registry" retired-contexts 2>/dev/null); then
950
+ log_warning "Could not derive the retired-context list — retired required contexts were NOT checked. This is not a clean result."
951
+ return 0
952
+ fi
953
+
954
+ # Likewise for the other side of the comparison: zero rulesets read and a
955
+ # repository with nothing required look identical from here.
956
+ local -a ids=()
957
+ if [[ -n "$rulesets" ]]; then
958
+ while IFS= read -r id; do
959
+ [[ -n "$id" ]] && ids+=("$id")
960
+ done < <(echo "$rulesets" | jq -r '.[].id // empty')
961
+ fi
962
+ if [[ ${#ids[@]} -eq 0 ]]; then
963
+ log_warning "No rulesets were readable — retired required contexts were NOT checked. This is not a clean result."
964
+ return 0
965
+ fi
966
+
967
+ # The LIST endpoint answers with summaries: id, name, enforcement — and no
968
+ # `rules`. A sweep run over the list would therefore find nothing on every
969
+ # repository, always, and report it as clean. Each ruleset's required checks
970
+ # only exist on its detail payload, so each one is fetched, and a detail this
971
+ # run could not read is stated rather than skipped.
972
+ local details="[]"
973
+ local id detail
974
+ for id in "${ids[@]}"; do
975
+ if ! detail=$(gh api -X GET "repos/$repo/rulesets/$id" 2>/dev/null); then
976
+ log_warning "Ruleset $id could not be read — retired required contexts were NOT checked for it. This is not a clean result."
977
+ continue
978
+ fi
979
+ details=$(jq -c -n --argjson all "$details" --argjson one "$detail" '$all + [$one]')
980
+ done
981
+
982
+ # Matched on the retired LABEL as the final context segment, never on the
983
+ # whole context string. A check run's reported name is the `/`-joined chain
984
+ # of the JOB names reaching it, and the depth varies with nesting: the same
985
+ # gate posts "<quality workflow> / <label>" on the pull-request path and
986
+ # "Release / <quality workflow> / <label>" on the release path. The registry
987
+ # renders one default chain, so comparing whole strings would find the
988
+ # pull-request spelling and walk straight past the release one — omitting a
989
+ # retired required context, which is the exact defect #3067 exists to
990
+ # detect, surviving inside the detector.
991
+ #
992
+ # Matching the leaf is not a loosening. A retired label is a name Lisa's own
993
+ # registry records that Lisa renamed away, so NO chain posts it any more —
994
+ # a ruleset requiring it under any prefix is red-walled just the same. The
995
+ # chain the ruleset actually pins is carried through into the replacement,
996
+ # so the operator is told to require the new name under the same caller
997
+ # chain they already wrote, not under the default one.
998
+ local hits
999
+ hits=$(jq -r -n --argjson live "$details" --argjson retired "$retired" '
1000
+ ($retired | map({key: .label, value: .}) | from_entries) as $dead
1001
+ | [ $live[]
1002
+ | .name as $ruleset
1003
+ | (.rules // [])[]
1004
+ | select(.type == "required_status_checks")
1005
+ | (.parameters.required_status_checks // [])[]
1006
+ | .context as $context
1007
+ | ($context | split(" / ")) as $chain
1008
+ | ($chain | last) as $label
1009
+ | select($dead[$label] != null)
1010
+ | (($chain[0:-1] + [$dead[$label].replacementLabel]) | join(" / ")) as $replacement
1011
+ | "\($ruleset)\t\($context)\t\($replacement)\t\($dead[$label].gate)"
1012
+ ] | unique | .[]')
1013
+
1014
+ if [[ -z "$hits" ]]; then
1015
+ log_verbose "No ruleset requires a context Lisa retired."
1016
+ return 0
1017
+ fi
1018
+
1019
+ echo ""
1020
+ log_warning "RETIRED REQUIRED CONTEXTS — these can NEVER report, and every pull request in this repository is waiting on them:"
1021
+ while IFS=$'\t' read -r ruleset context replacement gate; do
1022
+ [[ -n "$context" ]] || continue
1023
+ echo " ruleset '$ruleset' requires: $context"
1024
+ echo " Lisa renamed the '$gate' job; it posts \"$replacement\" now."
1025
+ echo " Not a failing check — GitHub waits on it forever, so there is no red tick and no log."
1026
+ done <<< "$hits"
1027
+ echo ""
1028
+ echo " Lisa does NOT edit a ruleset it does not manage, so nothing above was changed."
1029
+ echo " A rename is a three-step sequence and only this order is safe at every point:"
1030
+ echo " 1. remove the OLD context from every ruleset naming it"
1031
+ echo " 2. merge the change that makes the job post the NEW name"
1032
+ echo " 3. add the NEW context as required"
1033
+ echo " Doing (3) before (2) creates the same permanent wait in the other direction."
1034
+ echo ""
1035
+ }
1036
+
904
1037
  main() {
905
1038
  # Parse arguments
906
1039
  while [[ $# -gt 0 ]]; do
@@ -1038,6 +1171,11 @@ main() {
1038
1171
  fi
1039
1172
  done
1040
1173
 
1174
+ # Runs whatever the apply did, dry-run included. The retired name lives in a
1175
+ # ruleset the apply above never touched, so an apply that "succeeded" is
1176
+ # exactly the run that most needs to say this.
1177
+ report_retired_contexts "$repo" "$existing_rulesets"
1178
+
1041
1179
  echo ""
1042
1180
  if [[ "$DRY_RUN" == "true" ]]; then
1043
1181
  log_info "Dry run complete. ${#templates[@]} ruleset(s) would be applied."
@@ -3541,21 +3541,26 @@ export async function runReport(env, wait) {
3541
3541
  settings.gateContext,
3542
3542
  wait
3543
3543
  );
3544
- // Measured only where a waiver recipe is actually printed. On a branch this
3545
- // gate does not guard, the issue already says "you do not need a waiver" and
3546
- // never names the label — so a missing label there gates nothing, and
3547
- // reporting it would be a defect against a repository that has none. Skipping
3548
- // the CALL rather than suppressing the RENDER is deliberate: it makes
3549
- // "not reported when the gate is unrequired" a property of the wire, which a
3550
- // renderer edit cannot quietly undo.
3544
+ // Measured only after branch rules prove this gate is required. On a branch
3545
+ // this gate does not guard, the issue already says "you do not need a waiver".
3546
+ // When requiredness itself is unknown, querying the label would turn a label
3547
+ // 404 into a confident defect even though the rules API never proved the
3548
+ // waiver recipe applies. Skipping the CALL rather than suppressing the RENDER
3549
+ // keeps both cases fail-closed at the wire boundary.
3551
3550
  const bypassLabelState =
3552
- requiredness.state === REQUIREDNESS.notRequired
3553
- ? Object.freeze({
3554
- state: BYPASS_LABEL_STATE.notMeasured,
3555
- detail:
3556
- "not measured — this gate is not required on this branch, so no waiver recipe is printed and a missing label waives nothing",
3557
- })
3558
- : await fetchBypassLabelState(settings.api, settings.bypassLabel, wait);
3551
+ requiredness.state === REQUIREDNESS.required
3552
+ ? await fetchBypassLabelState(settings.api, settings.bypassLabel, wait)
3553
+ : requiredness.state === REQUIREDNESS.notRequired
3554
+ ? Object.freeze({
3555
+ state: BYPASS_LABEL_STATE.notMeasured,
3556
+ detail:
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
+ : Object.freeze({
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
+ });
3559
3564
  const context = {
3560
3565
  branch: settings.branch,
3561
3566
  label: settings.issueLabel,