@codyswann/lisa 4.60.7 → 4.60.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 (80) hide show
  1. package/all/copy-overwrite/scripts/check-orphaned-branches.mjs +31 -18
  2. package/all/copy-overwrite/scripts/lisa-gates.mjs +17 -5
  3. package/all/copy-overwrite/scripts/lisa-hooks/block-instruction-file-edits.sh +26 -35
  4. package/all/copy-overwrite/scripts/lisa-reconcile-policy.mjs +31 -8
  5. package/all/copy-overwrite/scripts/lisa-run-gates.mjs +8 -1
  6. package/all/create-only/.agents/rules/README.md +13 -6
  7. package/dist/core/host-rules-pointer.js +2 -2
  8. package/dist/core/host-rules-pointer.js.map +1 -1
  9. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  10. package/dist/core/lisa-owned-hash-ledger.js +10 -0
  11. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  12. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  13. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  14. package/dist/core/upstream-evidence-manifest.js +11 -9
  15. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  16. package/dist/opencode/plugin-templates/lisa-block-instruction-file-edits.ts +25 -22
  17. package/package.json +4 -4
  18. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  19. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  20. package/plugins/lisa/hooks/block-instruction-file-edits.sh +26 -35
  21. package/plugins/lisa/rules/reference/config-resolution.md +1 -1
  22. package/plugins/lisa/rules/reference/project-learnings.md +28 -3
  23. package/plugins/lisa-agy/hooks/block-instruction-file-edits.sh +26 -35
  24. package/plugins/lisa-agy/plugin.json +1 -1
  25. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  26. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  27. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  28. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  30. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa-copilot/hooks/block-instruction-file-edits.sh +26 -35
  32. package/plugins/lisa-copilot/rules/reference/config-resolution.md +1 -1
  33. package/plugins/lisa-copilot/rules/reference/project-learnings.md +28 -3
  34. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  35. package/plugins/lisa-cursor/hooks/block-instruction-file-edits.sh +26 -35
  36. package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +1 -1
  37. package/plugins/lisa-cursor/rules/project-learnings-reference.mdc +28 -3
  38. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  40. package/plugins/lisa-expo-agy/plugin.json +1 -1
  41. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  45. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  46. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  50. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  51. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  55. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  56. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  60. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  61. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  65. package/plugins/lisa-rails-agy/plugin.json +1 -1
  66. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  67. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  70. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  71. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  72. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  73. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  75. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  76. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  77. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  78. package/plugins/src/base/hooks/block-instruction-file-edits.sh +26 -35
  79. package/plugins/src/base/rules/reference/config-resolution.md +1 -1
  80. package/plugins/src/base/rules/reference/project-learnings.md +28 -3
@@ -200,25 +200,36 @@ export function defaultBranch(remote = "origin") {
200
200
  */
201
201
  export function branchesWithPullRequests() {
202
202
  const json = run("gh", [
203
- "pr",
204
- "list",
205
- "--state",
206
- "all",
207
- "--limit",
208
- "1000",
209
- "--json",
210
- "headRefName",
203
+ "api",
204
+ "graphql",
205
+ "--paginate",
206
+ "--slurp",
207
+ "-F",
208
+ "owner={owner}",
209
+ "-F",
210
+ "name={repo}",
211
+ "-f",
212
+ "query=query($owner:String!,$name:String!,$endCursor:String){repository(owner:$owner,name:$name){pullRequests(first:100,after:$endCursor){nodes{headRefName}pageInfo{hasNextPage endCursor}}}}",
211
213
  ]);
212
214
  if (json === undefined) return undefined;
213
215
  try {
214
- const rows = JSON.parse(json);
215
- return new Set(
216
- Array.isArray(rows)
217
- ? rows
218
- .map(row => row?.headRefName)
219
- .filter(name => typeof name === "string")
220
- : []
221
- );
216
+ const pages = JSON.parse(json);
217
+ if (!Array.isArray(pages) || pages.length === 0) return undefined;
218
+ const rows = [];
219
+ for (const [index, page] of pages.entries()) {
220
+ const { nodes, pageInfo } = page?.data?.repository?.pullRequests ?? {};
221
+ if (
222
+ !Array.isArray(nodes) ||
223
+ nodes.some(row => typeof row?.headRefName !== "string") ||
224
+ pageInfo?.hasNextPage !== index < pages.length - 1 ||
225
+ (pageInfo.hasNextPage &&
226
+ (typeof pageInfo.endCursor !== "string" || !pageInfo.endCursor))
227
+ ) {
228
+ return undefined;
229
+ }
230
+ rows.push(...nodes);
231
+ }
232
+ return new Set(rows.map(row => row.headRefName));
222
233
  } catch {
223
234
  // probe-direction: fail-closed — unparseable output is not an empty pull-
224
235
  // request set. `main` turns undefined into EXIT.UNAVAILABLE, so a branch is
@@ -249,8 +260,10 @@ export function branchesAhead(remote = "origin", base = "main") {
249
260
  "--count",
250
261
  `${remote}/${base}..${remote}/${branch}`,
251
262
  ]);
252
- const ahead = Number.parseInt(count ?? "", 10);
253
- if (Number.isFinite(ahead) && ahead > 0) rows.push({ ahead, branch });
263
+ if (count === undefined || !/^\d+$/.test(count)) return undefined;
264
+ const ahead = Number(count);
265
+ if (!Number.isSafeInteger(ahead)) return undefined;
266
+ if (ahead > 0) rows.push({ ahead, branch });
254
267
  }
255
268
  return rows;
256
269
  }
@@ -1619,6 +1619,9 @@ const NO_FALLBACK_PROVER = "(none — the fallback announces the absent adapter)
1619
1619
 
1620
1620
  /**
1621
1621
  * What each façade job runs when nothing resolves, and under which step names.
1622
+ * `unconditional` is opt-in: only fallbacks that run or fail for missing
1623
+ * prerequisites may own a taskless declaration. Optional service scans and
1624
+ * legacy audit paths that swallow execution failures do not qualify.
1622
1625
  *
1623
1626
  * Keyed by job id so the gate comes from `QUALITY_JOB_GATES` rather than being
1624
1627
  * written twice. `steps` are the exact step names carrying
@@ -1710,6 +1713,7 @@ const QUALITY_FALLBACKS = Object.freeze({
1710
1713
  steps: ["🗑️ Run dead code detection (knip)"],
1711
1714
  },
1712
1715
  learnings_budget: {
1716
+ unconditional: true,
1713
1717
  // The built-in runs Lisa's own in-tree checker on the Lisa source repo and
1714
1718
  // the published CLI everywhere else, AT THE PROJECT'S OWN VERSION — read
1715
1719
  // from its `@codyswann/lisa` dependency range, because the literal that
@@ -1810,6 +1814,7 @@ const QUALITY_FALLBACKS = Object.freeze({
1810
1814
  ],
1811
1815
  },
1812
1816
  floor_collisions: {
1817
+ unconditional: true,
1813
1818
  // Two candidate paths, and Lisa's own repository ships the script as a
1814
1819
  // template rather than installing it — so the built-in chooses between
1815
1820
  // them at run time and a single named task cannot reproduce the choice.
@@ -1936,6 +1941,7 @@ function qualityInvocations() {
1936
1941
  artifact,
1937
1942
  job,
1938
1943
  command: entry.command,
1944
+ unconditional: entry.unconditional === true,
1939
1945
  steps: Object.freeze([...entry.steps]),
1940
1946
  seedRun:
1941
1947
  entry.seedRun === undefined ? null : Object.freeze([...entry.seedRun]),
@@ -1974,12 +1980,14 @@ function prePushInvocation(gate, command, seedRun) {
1974
1980
  * @param {string} gate Registry gate id.
1975
1981
  * @param {string} command What the built-in else-branch runs.
1976
1982
  * @param {string[]|null} seedRun Candidate task names, or null for the registry default.
1983
+ * @param {boolean} [unconditional] The fallback fails when its prover cannot run.
1977
1984
  * @returns {object} A frozen inventory entry.
1978
1985
  */
1979
- function preCommitInvocation(gate, command, seedRun) {
1986
+ function preCommitInvocation(gate, command, seedRun, unconditional = false) {
1980
1987
  return Object.freeze({
1981
1988
  gate,
1982
1989
  moment: COMMIT,
1990
+ unconditional,
1983
1991
  surface: "pre-commit-hook",
1984
1992
  artifact: PRE_COMMIT_HOOK,
1985
1993
  job: null,
@@ -2066,6 +2074,7 @@ function onEditInvocation(gate, artifact, command) {
2066
2074
  function preToolRefusalInvocation(gate, artifact, command) {
2067
2075
  return Object.freeze({
2068
2076
  gate,
2077
+ unconditional: true,
2069
2078
  moment: PRE_TOOL,
2070
2079
  hookEvent: "PreToolUse",
2071
2080
  surface: "pre-tool-refusal-hook",
@@ -2284,8 +2293,9 @@ export const HARDCODED_INVOCATIONS = Object.freeze([
2284
2293
  "gitleaks protect --staged --redact -v",
2285
2294
  // Nothing a project could name reproduces this: the built-in also builds
2286
2295
  // a combined ignore file out of .gitleaksignore and .gitleaksignore.local
2287
- // before scanning, and degrades to a warning when gitleaks is absent.
2288
- []
2296
+ // before scanning, and blocks when gitleaks is absent.
2297
+ [],
2298
+ true
2289
2299
  ),
2290
2300
  // ONE invocation, THREE properties — `lint-staged` runs oxlint/eslint,
2291
2301
  // prettier and `ast-grep scan` in a single pass, which is why the hook
@@ -5414,7 +5424,9 @@ export function resolveMoment({
5414
5424
  // registry's explicit statement that the default task is descriptive and
5415
5425
  // is not shipped. When the matching hard-coded invocation exists, keep the
5416
5426
  // declaration visible (so contexts and requiredness still derive from it)
5417
- // but let that invocation take its documented fallback path. An explicit
5427
+ // but only delegate to an explicitly unconditional prover. A fallback that
5428
+ // skips for missing credentials or files cannot carry a required property.
5429
+ // An explicit
5418
5430
  // `run:` still replaces the fallback, and an unexpectedly present default
5419
5431
  // script still runs, so this cannot hide a prover the project supplied.
5420
5432
  const facadeBuiltIn =
@@ -5431,7 +5443,7 @@ export function resolveMoment({
5431
5443
  invocation.gate === id &&
5432
5444
  invocation.moment === moment &&
5433
5445
  invocation.facade === CONSULTS_THEN_FALLS_BACK &&
5434
- invocation.command !== NO_FALLBACK_PROVER
5446
+ invocation.unconditional === true
5435
5447
  );
5436
5448
 
5437
5449
  resolved.push({
@@ -24,11 +24,9 @@
24
24
  # Reads (`cat AGENTS.md`, `rg pattern AGENTS.md`) never fire.
25
25
  #
26
26
  # Exemptions (allowed):
27
- # - `LISA_ALLOW_INSTRUCTION_FILE_WRITE` set — the operator's explicit override,
28
- # named in the refusal so a human who really wants the edit can take it;
29
- # - payloads carrying a `<!-- LISA_` marker: Lisa's own marker-bounded regions
30
- # (the agy project-learnings bridge, cross-pollinate's rule section) replace
31
- # in place on re-run and cannot grow the file;
27
+ # - `LISA_ALLOW_INSTRUCTION_FILE_WRITE` set in the operator's runtime config.
28
+ # This cooperative override does not authenticate conversation approval;
29
+ # - Edit/MultiEdit replacing only bounded `<!-- LISA_` regions on both sides;
32
30
  # - paths under `node_modules/` or `dist/` — vendored copies, not the host's.
33
31
  set -euo pipefail
34
32
 
@@ -58,8 +56,8 @@ fi
58
56
 
59
57
  command -v jq >/dev/null 2>&1 || exit 0
60
58
 
61
- # The operator's override. Checked before anything else so it is always the
62
- # cheapest way out of a refusal a human deliberately wants to overrule.
59
+ # Existing operator-configured override. This hook sees tool payloads, not the
60
+ # conversation; the environment variable is not proof of who approved an edit.
63
61
  if [ -n "${LISA_ALLOW_INSTRUCTION_FILE_WRITE:-}" ]; then
64
62
  exit 0
65
63
  fi
@@ -129,34 +127,27 @@ refuse() {
129
127
  cat >&2 <<EOF
130
128
  BLOCKED: refusing to write \`$target\`.
131
129
 
132
- WHY: this is a session-instruction file, not a place to record what you just
133
- learned. Every line in it is loaded into the context of every agent, in every
134
- future session, in this project, forever. It is human-curated on purpose. An
135
- agent appending its own findings is how these files grow into hundreds of lines
136
- of stale, ticket-specific trivia that every later session pays for and half of
137
- which was only ever true once.
138
-
139
- WHERE IT GOES INSTEAD — take the first one that fits:
140
-
141
- 1. Every agent genuinely needs this in every session, and only in this project.
142
- That is a project rule — but do not write it yourself. Capture it with
143
- \`/lisa:persist-learning\` so it lands in the learnings ledger with provenance
144
- and a confidence score. The gardener (\`/lisa:learnings:audit\`) then proposes
145
- promotion into the host-rules directory \`.agents/rules/\` as a human-gated ticket.
146
-
147
- 2. It changes how an existing skill should behave. Edit that skill's SKILL.md.
148
- Knowledge belongs next to the procedure it modifies, not in a global preamble.
149
-
150
- 3. It is true beyond this project. Propose it upstream to Lisa via
151
- \`/lisa:cross-pollinate\`, or open an issue on CodySwannGT/lisa. A local copy of
152
- a general rule silently drifts the moment upstream changes.
153
-
154
- 4. None of the above — it is background someone may want to look up, not a
155
- standing instruction. Write it into the project documentation (\`wiki/\` or
156
- \`docs/\`).
157
-
158
- If a human explicitly asked for this edit, they can re-run with
159
- \`LISA_ALLOW_INSTRUCTION_FILE_WRITE=1\` set, which bypasses this guard.
130
+ WHY: this session-instruction file is loaded into future agent sessions.
131
+ Appending incidental findings makes every later session carry those notes.
132
+ This hook checks write patterns; it cannot read or authenticate conversation approval.
133
+
134
+ If an operator already requested a standing-rule edit, that authorization is
135
+ enough for the requested change. Use the relevant topic file in \`.agents/rules/\`
136
+ when no other destination was specified. All six supported agents read that
137
+ directory through the existing \`AGENTS.md\` pointer. Edit it directly; do not
138
+ require learning capture, another ticket, or repeated approval for the same ruling.
139
+
140
+ If the operator explicitly named this guarded file, preserve that target and
141
+ use the runtime's authorized edit path, or report this remaining restriction.
142
+ Do not silently move the edit, switch tools to evade this refusal, or set an
143
+ override yourself. The existing operator-configured
144
+ \`LISA_ALLOW_INSTRUCTION_FILE_WRITE=1\` escape hatch is not proof of consent.
145
+
146
+ For an agent's own finding, first decide whether it is worth maintaining.
147
+ Decline one-off trivia and unnecessary rules. Durable project knowledge can
148
+ use \`/lisa:persist-learning\`; procedural knowledge belongs with its SKILL.md;
149
+ background belongs in existing project documentation. Use
150
+ \`/lisa:cross-pollinate\` only for a material, reusable upstream improvement.
160
151
  EOF
161
152
  exit 2
162
153
  }
@@ -690,7 +690,6 @@ export function reconcileContexts({
690
690
  const liveContexts = live ?? [];
691
691
  const declaredNames = new Set(declared ?? []);
692
692
  const byName = (a, b) => a.localeCompare(b);
693
- const awaitedDeclarations = awaited ?? [];
694
693
 
695
694
  // The unit of declaration is a (ruleset, context) PAIR, not a name. A name
696
695
  // set cannot hold `build` required by both `base` and `release`, so a live
@@ -707,6 +706,10 @@ export function reconcileContexts({
707
706
  // chain is not a source of declared policy.
708
707
  ...(Object.hasOwn(pins, name) ? { integration_id: pins[name] } : {}),
709
708
  }));
709
+ const awaitedDeclarations = effectiveAwaitedRecords(
710
+ awaited ?? [],
711
+ supplied ?? reconstituted
712
+ );
710
713
  const byContext = new Map();
711
714
  for (const record of supplied ?? reconstituted) {
712
715
  if (!declaredNames.has(record?.context)) continue;
@@ -807,6 +810,23 @@ export function reconcileContexts({
807
810
  };
808
811
  }
809
812
 
813
+ /**
814
+ * Explicit ruleset records override awaited declarations of the same pair.
815
+ * @param {object[]} awaited External check declarations.
816
+ * @param {object[]} records Explicit required-check records.
817
+ * @returns {object[]} Awaited records that still govern a pair.
818
+ */
819
+ function effectiveAwaitedRecords(awaited, records) {
820
+ return awaited.filter(
821
+ record =>
822
+ !records.some(
823
+ explicit =>
824
+ explicit.context === record.context &&
825
+ explicit.ruleset === record.ruleset
826
+ )
827
+ );
828
+ }
829
+
810
830
  /**
811
831
  * Compare the declared `policy` block against observed repository state.
812
832
  *
@@ -1552,13 +1572,16 @@ export function reconcile({
1552
1572
  // comparison record, that record is what the repair reads its pin off — so
1553
1573
  // omitting it here would have the repair write the context unpinned and
1554
1574
  // silently discard the one app the project said may post it.
1555
- const awaitedDeclarations = awaited.map(context => ({
1556
- context,
1557
- ...(resolvedAwaitedHome === null ? {} : { ruleset: resolvedAwaitedHome }),
1558
- ...(Object.hasOwn(declaredAwaitedPins, context)
1559
- ? { integration_id: declaredAwaitedPins[context] }
1560
- : {}),
1561
- }));
1575
+ const awaitedDeclarations = effectiveAwaitedRecords(
1576
+ awaited.map(context => ({
1577
+ context,
1578
+ ...(resolvedAwaitedHome === null ? {} : { ruleset: resolvedAwaitedHome }),
1579
+ ...(Object.hasOwn(declaredAwaitedPins, context)
1580
+ ? { integration_id: declaredAwaitedPins[context] }
1581
+ : {}),
1582
+ })),
1583
+ configured.records
1584
+ );
1562
1585
  const contexts = reconcileContexts({
1563
1586
  declared,
1564
1587
  live: live.contexts,
@@ -293,6 +293,9 @@ function skipReason(gate) {
293
293
  if (gate.mode === "await") {
294
294
  return `awaits "${gate.awaits}"; no signal exists locally`;
295
295
  }
296
+ if (gate.mode === "builtin") {
297
+ return "delegated to the built-in facade; no local task ran or verdict was proved";
298
+ }
296
299
  return null;
297
300
  }
298
301
 
@@ -1086,9 +1089,13 @@ const UNPROVED_STATES = new Set([
1086
1089
  export function provenFloor({ gates, moment, wired = [], result = null }) {
1087
1090
  const declared = coveredFloor({ gates, moment, wired });
1088
1091
  if (!result) return [];
1092
+ // Delegation leaves the built-in responsible for running the check; adding
1093
+ // it to the coverage file would tell that same hook to stand down.
1089
1094
  const unproved = new Set(
1090
1095
  (result.results ?? [])
1091
- .filter(entry => UNPROVED_STATES.has(entry.state))
1096
+ .filter(
1097
+ entry => entry.mode === "builtin" || UNPROVED_STATES.has(entry.state)
1098
+ )
1092
1099
  .map(entry => entry.id)
1093
1100
  );
1094
1101
  return declared.filter(id => !unproved.has(id));
@@ -7,8 +7,11 @@ Antigravity (`agy`) all reach it through the Lisa-managed pointer block in
7
7
 
8
8
  ## Ownership
9
9
 
10
- This directory is **human-authored only** — the humans' decree surface. Lisa
11
- never writes rule bodies into it, and never edits or deletes a file in it.
10
+ The host controls this directory. Lisa's installer never overwrites its rules.
11
+ An agent may make a specific edit an operator has requested or approved,
12
+ including simplifying or removing a rule. Honor that existing authorization;
13
+ do not send the same decision through learning capture, another ticket, or a
14
+ second approval. Preserve unrelated rules and the operator's intended meaning.
12
15
 
13
16
  - Lisa's own rules live in its per-agent plugins and arrive by their own route.
14
17
  Do not copy them here.
@@ -17,10 +20,8 @@ never writes rule bodies into it, and never edits or deletes a file in it.
17
20
  executable contract, and only the gardener (`/lisa:learnings:audit`) ever
18
21
  proposes a promotion out of it — as a human-gated tracker ticket, never a
19
22
  silent agent rewrite.
20
- - Rules already sitting here are first-run gardener candidates like any other
21
- knowledge: expect prose that restates what a lint rule or hook already
22
- enforces to earn a promote-and-delete ticket, so this directory shrinks
23
- instead of growing.
23
+ - An incidental finding is not permission to add or change a standing rule.
24
+ Decline one-off trivia and rules whose maintenance costs exceed their value.
24
25
 
25
26
  ## What belongs here
26
27
 
@@ -41,5 +42,11 @@ with a heading and a handful of imperative rules. Keep each file short: this
41
42
  directory is read in full whenever an agent consults it, so length is a real
42
43
  cost paid on every consultation.
43
44
 
45
+ For an approved standing ruling, update the relevant topic file or create one
46
+ here using ordinary editing tools. The existing pointer in `AGENTS.md` makes it
47
+ available to all six supported agents; this directory is not Claude-specific.
48
+ If the operator explicitly named another file, honor that target and any active
49
+ runtime restrictions rather than silently moving the edit here.
50
+
44
51
  This README is a Lisa-seeded starter and is never overwritten — edit or delete
45
52
  it freely once the directory has real content.
@@ -8,7 +8,7 @@
8
8
  * block in `AGENTS.md` (Claude via the `@AGENTS.md` import in `CLAUDE.md`) — and
9
9
  * no agent loads host rules twice.
10
10
  *
11
- * Pointer only. Lisa never writes rule bodies into `AGENTS.md` or into
11
+ * Pointer only. Lisa's installer never writes rule bodies into `AGENTS.md` or into
12
12
  * `.agents/rules/`; that is what distinguishes this block from the legacy
13
13
  * `LISA_RULES_START..END` bake that PR #1150 removed.
14
14
  *
@@ -80,7 +80,7 @@ export function buildHostRulesPointer(legacyRulesFile) {
80
80
  LISA_HOST_RULES_START_MARKER,
81
81
  "## Host Rules",
82
82
  "",
83
- `Read every file under \`${HOST_RULES_DIR}/\` for this project's standing rules.`,
83
+ `Read every file under \`${HOST_RULES_DIR}/\`. Make operator-requested rule edits directly; no learning-promotion step is needed.`,
84
84
  ...transitionSentence(legacyRulesFile),
85
85
  LISA_HOST_RULES_END_MARKER,
86
86
  ].join("\n");
@@ -1 +1 @@
1
- {"version":3,"file":"host-rules-pointer.js","sourceRoot":"","sources":["../../src/core/host-rules-pointer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,8DAA8D;AAC9D,MAAM,CAAC,MAAM,4BAA4B,GAAG,gCAAgC,CAAC;AAC7E,8DAA8D;AAC9D,MAAM,CAAC,MAAM,0BAA0B,GAAG,8BAA8B,CAAC;AAEzE;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,mBAAmB,CACjC,IAAY,EACZ,WAAmB,EACnB,SAAiB,EACjB,WAAmB;IAEnB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,QAAQ,KAAK,CAAC,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,CAAC,CAAC;IAC7B,MAAM,iBAAiB,GACrB,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,QAAQ,GAAG,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC9E,MAAM,eAAe,GACnB,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACtE,IACE,QAAQ,KAAK,MAAM;QACnB,CAAC,QAAQ,IAAI,MAAM,GAAG,QAAQ,CAAC;QAC/B,iBAAiB;QACjB,eAAe,EACf,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,IAAI,WAAW,KAAK,EAAE,EAAE,CAAC;YACvB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;QACtD,OAAO,GAAG,IAAI,GAAG,SAAS,GAAG,WAAW,IAAI,CAAC;IAC/C,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,GAAG,MAAM,GAAG,WAAW,GAAG,KAAK,EAAE,CAAC;IAC/C,+EAA+E;IAC/E,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC;AACxD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,eAAwB;IAC5D,OAAO;QACL,4BAA4B;QAC5B,eAAe;QACf,EAAE;QACF,2BAA2B,cAAc,wCAAwC;QACjF,GAAG,kBAAkB,CAAC,eAAe,CAAC;QACtC,0BAA0B;KAC3B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,OAAO,mBAAmB,CACxB,IAAI,EACJ,4BAA4B,EAC5B,0BAA0B,EAC1B,EAAE,CACH,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,SAAS,kBAAkB,CAAC,eAAmC;IAC7D,IAAI,eAAe,KAAK,SAAS,EAAE,CAAC;QAClC,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO;QACL,eAAe,eAAe,yCAAyC;QACvE,0CAA0C;KAC3C,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"host-rules-pointer.js","sourceRoot":"","sources":["../../src/core/host-rules-pointer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,8DAA8D;AAC9D,MAAM,CAAC,MAAM,4BAA4B,GAAG,gCAAgC,CAAC;AAC7E,8DAA8D;AAC9D,MAAM,CAAC,MAAM,0BAA0B,GAAG,8BAA8B,CAAC;AAEzE;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,mBAAmB,CACjC,IAAY,EACZ,WAAmB,EACnB,SAAiB,EACjB,WAAmB;IAEnB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,QAAQ,KAAK,CAAC,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,CAAC,CAAC;IAC7B,MAAM,iBAAiB,GACrB,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,QAAQ,GAAG,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC9E,MAAM,eAAe,GACnB,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACtE,IACE,QAAQ,KAAK,MAAM;QACnB,CAAC,QAAQ,IAAI,MAAM,GAAG,QAAQ,CAAC;QAC/B,iBAAiB;QACjB,eAAe,EACf,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,IAAI,WAAW,KAAK,EAAE,EAAE,CAAC;YACvB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;QACtD,OAAO,GAAG,IAAI,GAAG,SAAS,GAAG,WAAW,IAAI,CAAC;IAC/C,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,GAAG,MAAM,GAAG,WAAW,GAAG,KAAK,EAAE,CAAC;IAC/C,+EAA+E;IAC/E,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC;AACxD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,eAAwB;IAC5D,OAAO;QACL,4BAA4B;QAC5B,eAAe;QACf,EAAE;QACF,2BAA2B,cAAc,yFAAyF;QAClI,GAAG,kBAAkB,CAAC,eAAe,CAAC;QACtC,0BAA0B;KAC3B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,OAAO,mBAAmB,CACxB,IAAI,EACJ,4BAA4B,EAC5B,0BAA0B,EAC1B,EAAE,CACH,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,SAAS,kBAAkB,CAAC,eAAmC;IAC7D,IAAI,eAAe,KAAK,SAAS,EAAE,CAAC;QAClC,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO;QACL,eAAe,eAAe,yCAAyC;QACvE,0CAA0C;KAC3C,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"lisa-owned-hash-ledger.d.ts","sourceRoot":"","sources":["../../src/core/lisa-owned-hash-ledger.ts"],"names":[],"mappings":"AACA,6EAA6E;AAE7E;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAC3C,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAq0CjC,CAAC;AAEH;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,+BAA+B,EAAE,QAAQ,CACpD,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAwuCjC,CAAC"}
1
+ {"version":3,"file":"lisa-owned-hash-ledger.d.ts","sourceRoot":"","sources":["../../src/core/lisa-owned-hash-ledger.ts"],"names":[],"mappings":"AACA,6EAA6E;AAE7E;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAC3C,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CA00CjC,CAAC;AAEH;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,+BAA+B,EAAE,QAAQ,CACpD,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CA6uCjC,CAAC"}
@@ -240,6 +240,7 @@ export const LISA_OWNED_HASH_LEDGER = Object.freeze({
240
240
  "e8f70dce15f9dce2b1758fa657d9a279cecc9f8c2a7b69a3bfca86ec37eb2e3f",
241
241
  ]),
242
242
  "scripts/check-orphaned-branches.mjs": Object.freeze([
243
+ "1f450d2492ca0bcdba440f6b403c1deee0f35e749c54d2ed5c5ddc696d5ab896",
243
244
  "340618eddcf85909fc1d8274683b3ba75ecff94e7db9e34c3dcb03a6100092d2",
244
245
  "42a107a86032a7f72e9d05a74bd9026c20589b87a15148a00802c1c0c0f65c9d",
245
246
  "436fb5d8c3b2a686d4e4c350858cee9b4fd7bb6e3d1f6b37496fc345a45618cd",
@@ -606,6 +607,7 @@ export const LISA_OWNED_HASH_LEDGER = Object.freeze({
606
607
  "10b4c60390981d152aa544c014e115cdb5cef28de837d6314f5a4b0df9638e8d",
607
608
  "12c09f699414a5fb7577e221ec4291d5897cdf613cf4d8fe410f089b1b34f123",
608
609
  "13ac69bf2bbae76bc702506bcef5c8fb2b26befaf1358627f4726c1a8ac5905a",
610
+ "16fa33bfbedb9980526130be5d1ce09159ebe7df918f723b2a9b66164917944b",
609
611
  "18496d11521f4f4e66961db6778827d735d0495b92ce2cbbc9f8a49181181c84",
610
612
  "186efce62ea9b94db91226587cd5e1d41ecf2cf9d63e2ba56a012dd5a41f362b",
611
613
  "18ebb837fdf67b2b5b7bcd8776aaa3993667227a1b00fd9088e69952ab64438e",
@@ -784,6 +786,7 @@ export const LISA_OWNED_HASH_LEDGER = Object.freeze({
784
786
  "3e709e1ec8a5843c00684bc477ad32ddab2c5fdb11f71d5aeec0c49609eaf025",
785
787
  "48fe34021b0e91170edf8901d92765cdadf1ade1d22dfc69144559c114f184b6",
786
788
  "6f52076e3488821d638b6dd45b5faca527d92b181cc12e6e5033859e0786c8a7",
789
+ "73ac5af581d16992dd9cc273b4e2f6b55bd6a2629abe23b7658563cf433e082c",
787
790
  "850cfc23852eb752114ae1e938a1c7bcf34bc50c85d3aaca0c02311cc8c0ef70",
788
791
  "8996e0bcbf1db085a5d99734e797c91f5025997bd967bc374efff8348dac14c2",
789
792
  "be1e77fb0ddb246eddae71ebac25e972c3c9e8079514e0dc78d0b73df919464c",
@@ -1035,6 +1038,7 @@ export const LISA_OWNED_HASH_LEDGER = Object.freeze({
1035
1038
  "07542e977ed41f094d5a51cae512e4a3246fabe10d0f83676a176abc81d91624",
1036
1039
  "1e8053bb51a01fda0e2489de231ea252473991138da150dbdff72c5292e1ce53",
1037
1040
  "2512f61074d10216dee0aca748caaebad373e6bba2598bc089f30f977c7a6a83",
1041
+ "263860fba7fadae3c1096973eaf4f25dca1683ef2ce64279dea6845ac90faf6a",
1038
1042
  "269241ef59e1a29b7511a096005ea08938408578bf40a4e1acb97d8a632251ad",
1039
1043
  "395897d1dc3f0ac92d7c892eaa9b9ddf12078181bb9f5b228965478190b09a91",
1040
1044
  "3c062d0b79961a9a93a822507e893dcdaa71d65d735557727d20464c2489d812",
@@ -1081,6 +1085,7 @@ export const LISA_OWNED_HASH_LEDGER = Object.freeze({
1081
1085
  "6e419a489d0605cc0743af207ff4ff3428d8bcdfbabb7207d164593b9644dfa5",
1082
1086
  "79055f89eb9c5bf1739867c12dc2d9e30320f86a2a3dd470300ba706c3c9d85e",
1083
1087
  "7d91ef0e06c53d9d09ddc0b03a22ddd5933e4001ab759c14a964a463df58e635",
1088
+ "7e04168dffa0caa5d8dfb127a9be462a81f4512e86a57455df5708154bfb19b0",
1084
1089
  "853437a0be104fde1eb962f0c5c70b14ea3f4fa8b6973f8d9f6e704a9d3e3611",
1085
1090
  "86a84591a81d8b4e92d2d3f4b7bf447230f1bb3a3b6fc9f0604fcff685026f73",
1086
1091
  "94453bdae8ab3dd2eba0883c4b278027396090e0bdc8e2f102414abc6415912a",
@@ -1586,6 +1591,7 @@ export const LISA_OWNED_HASH_HISTORY_DERIVED = Object.freeze({
1586
1591
  "e8f70dce15f9dce2b1758fa657d9a279cecc9f8c2a7b69a3bfca86ec37eb2e3f",
1587
1592
  ]),
1588
1593
  "scripts/check-orphaned-branches.mjs": Object.freeze([
1594
+ "1f450d2492ca0bcdba440f6b403c1deee0f35e749c54d2ed5c5ddc696d5ab896",
1589
1595
  "340618eddcf85909fc1d8274683b3ba75ecff94e7db9e34c3dcb03a6100092d2",
1590
1596
  "42a107a86032a7f72e9d05a74bd9026c20589b87a15148a00802c1c0c0f65c9d",
1591
1597
  "436fb5d8c3b2a686d4e4c350858cee9b4fd7bb6e3d1f6b37496fc345a45618cd",
@@ -1941,6 +1947,7 @@ export const LISA_OWNED_HASH_HISTORY_DERIVED = Object.freeze({
1941
1947
  "08a66b2ca128d38735810786fb2be73ed5c6dd71560992ff95ad0a2170428d90",
1942
1948
  "091c1198c239f5d49b584a8a296ea9f70ba67b083b36d8742e5a81ff7439b09e",
1943
1949
  "0ceb0133be299a3c89bdf736008ee2c5c8ad027c1a4656b9d0fb7c9438a7ed07",
1950
+ "16fa33bfbedb9980526130be5d1ce09159ebe7df918f723b2a9b66164917944b",
1944
1951
  "18ebb837fdf67b2b5b7bcd8776aaa3993667227a1b00fd9088e69952ab64438e",
1945
1952
  "1a456052254f7c7c8b7c0509fb1e3d8c572891056bc0ef4dd4fbde29b0a54011",
1946
1953
  "1ac3180f7a8f777f6be0d90043d4396ec96589168b43fc7128eeec750aa9aef9",
@@ -2082,6 +2089,7 @@ export const LISA_OWNED_HASH_HISTORY_DERIVED = Object.freeze({
2082
2089
  "3e709e1ec8a5843c00684bc477ad32ddab2c5fdb11f71d5aeec0c49609eaf025",
2083
2090
  "48fe34021b0e91170edf8901d92765cdadf1ade1d22dfc69144559c114f184b6",
2084
2091
  "6f52076e3488821d638b6dd45b5faca527d92b181cc12e6e5033859e0786c8a7",
2092
+ "73ac5af581d16992dd9cc273b4e2f6b55bd6a2629abe23b7658563cf433e082c",
2085
2093
  "850cfc23852eb752114ae1e938a1c7bcf34bc50c85d3aaca0c02311cc8c0ef70",
2086
2094
  "8996e0bcbf1db085a5d99734e797c91f5025997bd967bc374efff8348dac14c2",
2087
2095
  "be1e77fb0ddb246eddae71ebac25e972c3c9e8079514e0dc78d0b73df919464c",
@@ -2320,6 +2328,7 @@ export const LISA_OWNED_HASH_HISTORY_DERIVED = Object.freeze({
2320
2328
  ]),
2321
2329
  "scripts/lisa-reconcile-policy.mjs": Object.freeze([
2322
2330
  "2512f61074d10216dee0aca748caaebad373e6bba2598bc089f30f977c7a6a83",
2331
+ "263860fba7fadae3c1096973eaf4f25dca1683ef2ce64279dea6845ac90faf6a",
2323
2332
  "269241ef59e1a29b7511a096005ea08938408578bf40a4e1acb97d8a632251ad",
2324
2333
  "395897d1dc3f0ac92d7c892eaa9b9ddf12078181bb9f5b228965478190b09a91",
2325
2334
  "3c062d0b79961a9a93a822507e893dcdaa71d65d735557727d20464c2489d812",
@@ -2361,6 +2370,7 @@ export const LISA_OWNED_HASH_HISTORY_DERIVED = Object.freeze({
2361
2370
  "6e419a489d0605cc0743af207ff4ff3428d8bcdfbabb7207d164593b9644dfa5",
2362
2371
  "79055f89eb9c5bf1739867c12dc2d9e30320f86a2a3dd470300ba706c3c9d85e",
2363
2372
  "7d91ef0e06c53d9d09ddc0b03a22ddd5933e4001ab759c14a964a463df58e635",
2373
+ "7e04168dffa0caa5d8dfb127a9be462a81f4512e86a57455df5708154bfb19b0",
2364
2374
  "853437a0be104fde1eb962f0c5c70b14ea3f4fa8b6973f8d9f6e704a9d3e3611",
2365
2375
  "86a84591a81d8b4e92d2d3f4b7bf447230f1bb3a3b6fc9f0604fcff685026f73",
2366
2376
  "94453bdae8ab3dd2eba0883c4b278027396090e0bdc8e2f102414abc6415912a",