@codyswann/lisa 2.262.0 → 2.263.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/all/copy-contents/gitignore +2 -0
  2. package/dist/cli/ui-config-write.d.ts.map +1 -1
  3. package/dist/cli/ui-config-write.js +24 -1
  4. package/dist/cli/ui-config-write.js.map +1 -1
  5. package/dist/core/learnings-lock.d.ts +7 -0
  6. package/dist/core/learnings-lock.d.ts.map +1 -1
  7. package/dist/core/learnings-lock.js +49 -8
  8. package/dist/core/learnings-lock.js.map +1 -1
  9. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  10. package/dist/core/upstream-evidence-manifest.js +17 -3
  11. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  12. package/dist/health/contract.d.ts +56 -0
  13. package/dist/health/contract.d.ts.map +1 -0
  14. package/dist/health/contract.js +153 -0
  15. package/dist/health/contract.js.map +1 -0
  16. package/dist/health/directory-sync.d.ts +17 -0
  17. package/dist/health/directory-sync.d.ts.map +1 -0
  18. package/dist/health/directory-sync.js +69 -0
  19. package/dist/health/directory-sync.js.map +1 -0
  20. package/dist/health/index.d.ts +3 -0
  21. package/dist/health/index.d.ts.map +1 -0
  22. package/dist/health/index.js +3 -0
  23. package/dist/health/index.js.map +1 -0
  24. package/dist/health/storage.d.ts +39 -0
  25. package/dist/health/storage.d.ts.map +1 -0
  26. package/dist/health/storage.js +262 -0
  27. package/dist/health/storage.js.map +1 -0
  28. package/dist/health/strict-validation.d.ts +56 -0
  29. package/dist/health/strict-validation.d.ts.map +1 -0
  30. package/dist/health/strict-validation.js +144 -0
  31. package/dist/health/strict-validation.js.map +1 -0
  32. package/dist/sync/config-sync.d.ts.map +1 -1
  33. package/dist/sync/config-sync.js +18 -6
  34. package/dist/sync/config-sync.js.map +1 -1
  35. package/dist/sync/registry.d.ts +2 -0
  36. package/dist/sync/registry.d.ts.map +1 -1
  37. package/dist/sync/registry.js +7 -0
  38. package/dist/sync/registry.js.map +1 -1
  39. package/package.json +4 -2
  40. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  42. package/plugins/lisa/rules/reference/config-resolution.md +1 -0
  43. package/plugins/lisa-agy/plugin.json +1 -1
  44. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  46. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  47. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-copilot/rules/reference/config-resolution.md +1 -0
  51. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +1 -0
  53. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  55. package/plugins/lisa-expo-agy/plugin.json +1 -1
  56. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  60. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  61. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  65. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  66. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  67. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  70. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  71. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  72. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  73. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  75. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  76. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  77. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  79. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  80. package/plugins/lisa-rails-agy/plugin.json +1 -1
  81. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  82. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  83. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  84. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  85. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  86. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  87. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  88. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  89. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  90. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  91. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  92. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  93. package/plugins/src/base/rules/reference/config-resolution.md +1 -0
  94. package/scripts/verify-health-contract-built.mjs +143 -0
  95. package/ui/README.md +8 -3
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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-typescript",
3
- "version": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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": "2.262.0",
3
+ "version": "2.263.0",
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"
@@ -181,6 +181,7 @@ fi
181
181
  | `source` | no | — | Default PRD source for batch skills (`/lisa:intake`) and arg-less single-PRD skills. One of `"notion"`, `"confluence"`, `"linear"`, `"github"`, `"jira"`. Explicit URLs/keys passed to a skill always win over `source`; this is a default, not a lock. |
182
182
  | `projectRulesFile` | no | `.claude/rules/PROJECT_RULES.md` | Safe repo-relative Markdown path for hand-authored project rules. Independent of the learnings ledger — relocating the rules file never moves the ledger. |
183
183
  | `learnings.file` | no | `.lisa/PROJECT_LEARNINGS.md` | Safe repo-relative Markdown path for the machine-managed learnings ledger, overriding the `.lisa/` default. Rejected if it resolves inside any auto-loaded rules tree (`.claude/rules`, `.cursor/rules`, `.github/instructions`, `.agents/rules`) — the ledger must stay out of eager context. |
184
+ | `health.schedule` | no | `off` | Health cadence contract: `off`, `daily`, or `weekly`. `lisa sync` populates the default with `_lisaSync.populated` provenance and rejects values outside this closed vocabulary. Runtime results live only at the gitignored `.lisa/health/latest.json`, never in either config file. |
184
185
  | `usage` | no | — | Optional token/cost pricing metadata consumed by the `usage-accounting` rule. Missing pricing never blocks a lifecycle flow; Lisa records token counts with `estimated_cost: null` when no trustworthy price source is configured. |
185
186
  | `wiki` | no | — | Wiki location for the `wiki-knowledge-source` rule. Omit for a local in-repo wiki (`wiki/`). See **Wiki source** below. |
186
187
 
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+ /** Empirical proof for the shipped Health v1 contract and real built CLI. */
3
+ import assert from "node:assert/strict";
4
+ import { execFileSync } from "node:child_process";
5
+ import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
6
+ import { tmpdir } from "node:os";
7
+ import path from "node:path";
8
+
9
+ let assertions = 0;
10
+ const check = (condition, message) => {
11
+ assertions += 1;
12
+ assert.ok(condition, message);
13
+ };
14
+ const equal = (actual, expected, message) => {
15
+ assertions += 1;
16
+ assert.deepEqual(actual, expected, message);
17
+ };
18
+
19
+ const root = process.cwd();
20
+ const project = await mkdtemp(path.join(tmpdir(), "lisa-health-built-"));
21
+ try {
22
+ await writeFile(
23
+ path.join(project, ".lisa.config.json"),
24
+ `${JSON.stringify({ tracker: "github", github: { org: "acme", repo: "app" } }, null, 2)}\n`
25
+ );
26
+ execFileSync(
27
+ process.execPath,
28
+ [path.join(root, "dist/index.js"), "sync", project, "--json"],
29
+ {
30
+ cwd: root,
31
+ env: { ...process.env, LISA_SKIP_UPDATE_CHECK: "1" },
32
+ stdio: ["ignore", "pipe", "pipe"],
33
+ }
34
+ );
35
+ const config = JSON.parse(
36
+ await readFile(path.join(project, ".lisa.config.json"), "utf8")
37
+ );
38
+ equal(
39
+ config.health,
40
+ { schedule: "off" },
41
+ "sync populates only health.schedule"
42
+ );
43
+ equal(
44
+ config._lisaSync.populated["health.schedule"],
45
+ "off",
46
+ "sync records literal health.schedule provenance"
47
+ );
48
+ check(
49
+ config.health.latest === undefined,
50
+ "runtime health state stays out of config"
51
+ );
52
+ console.log("[EVIDENCE: health-v1-built-sync]");
53
+
54
+ const health = await import("@codyswann/lisa/health");
55
+ const deterministicFindings = [
56
+ {
57
+ check: "config.sync",
58
+ layer: "deterministic",
59
+ status: "warn",
60
+ reason: "A configuration advisory needs operator review.",
61
+ },
62
+ ];
63
+ const deterministic = health.validateHealthResult({
64
+ schemaVersion: 1,
65
+ runId: "health-run-new",
66
+ mode: "deterministic",
67
+ startedAt: "2026-07-20T12:00:00.000Z",
68
+ completedAt: "2026-07-20T12:01:00.000Z",
69
+ findings: deterministicFindings,
70
+ summary: health.summarizeHealthFindings(deterministicFindings),
71
+ });
72
+ equal(deterministic.summary.counts, { pass: 0, warn: 1, fail: 0 });
73
+ equal(deterministic.summary.verdict, "in band");
74
+ const fullFindings = [
75
+ {
76
+ check: "config.sync",
77
+ layer: "deterministic",
78
+ status: "pass",
79
+ reason: "Configuration is synchronized.",
80
+ },
81
+ {
82
+ check: "agent.review",
83
+ layer: "agentic",
84
+ status: "fail",
85
+ reason: "The agent review found actionable drift.",
86
+ },
87
+ ];
88
+ const full = health.validateHealthResult({
89
+ schemaVersion: 1,
90
+ runId: "health-run-full",
91
+ mode: "full",
92
+ startedAt: "2026-07-20T12:02:00.000Z",
93
+ completedAt: "2026-07-20T12:03:00.000Z",
94
+ findings: fullFindings,
95
+ summary: health.summarizeHealthFindings(fullFindings),
96
+ });
97
+ equal(full.summary.counts, { pass: 1, warn: 0, fail: 1 });
98
+ equal(full.summary.verdict, "drift detected");
99
+ check(
100
+ Object.isFrozen(full) && Object.isFrozen(full.findings),
101
+ "result is frozen"
102
+ );
103
+ console.log("[EVIDENCE: health-v1-built-contract]");
104
+
105
+ const neverRunProject = await mkdtemp(
106
+ path.join(tmpdir(), "lisa-health-never-")
107
+ );
108
+ try {
109
+ equal(await health.readLatestHealthResult(neverRunProject), {
110
+ status: "never-run",
111
+ });
112
+ } finally {
113
+ await rm(neverRunProject, { recursive: true, force: true });
114
+ }
115
+ equal(
116
+ (await health.writeLatestHealthResult(project, deterministic)).status,
117
+ "written"
118
+ );
119
+ equal(
120
+ (await health.writeLatestHealthResult(project, full)).status,
121
+ "written"
122
+ );
123
+ equal(
124
+ (await health.writeLatestHealthResult(project, deterministic)).status,
125
+ "unchanged"
126
+ );
127
+ const available = await health.readLatestHealthResult(project);
128
+ equal(available.status, "available");
129
+ equal(available.lastRun, full.completedAt);
130
+
131
+ await writeFile(path.join(project, ".lisa/health/latest.json"), "{malformed");
132
+ equal((await health.readLatestHealthResult(project)).status, "unreadable");
133
+ await writeFile(
134
+ path.join(project, ".lisa/health/latest.json"),
135
+ JSON.stringify({ ...full, schemaVersion: 2 })
136
+ );
137
+ equal((await health.readLatestHealthResult(project)).status, "unreadable");
138
+ console.log("[EVIDENCE: health-v1-built-storage]");
139
+ check(assertions > 0, "proof must execute assertions");
140
+ console.log(`health-v1 assertions=${assertions}`);
141
+ } finally {
142
+ await rm(project, { recursive: true, force: true });
143
+ }
package/ui/README.md CHANGED
@@ -126,9 +126,14 @@ product features, secrets, or business logic is never proposed.
126
126
  When the engine lands, `starter.*` should join the `lisa sync` registry so
127
127
  the section above governs it like every other setting.
128
128
 
129
- ## Health (planned documented, not wired)
130
-
131
- The **Health** section answers two questions with different lifecycles:
129
+ ## Health (v1 contract available; consumers planned)
130
+
131
+ The **Health** section answers two questions with different lifecycles. Health
132
+ v1 now defines its shared result and persistence contract at
133
+ `@codyswann/lisa/health`: completed results are validated and atomically stored
134
+ at the gitignored `.lisa/health/latest.json`; `health.schedule` is the only v1
135
+ configuration key and accepts `off`, `daily`, or `weekly`. The button, probes,
136
+ console readback, and scheduler remain downstream work.
132
137
 
133
138
  **Is Lisa on the latest version?** — always-on status. Lisa's CLI already
134
139
  checks npm on every invocation; the console surfaces the result permanently