@codyswann/lisa 3.3.0 → 3.4.1

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 (121) hide show
  1. package/all/copy-overwrite/scripts/lisa-hooks/block-no-verify.sh +31 -0
  2. package/all/copy-overwrite/scripts/lisa-work-item.mjs +24 -2
  3. package/dist/codex/scripts/block-no-verify.sh +53 -4
  4. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  5. package/dist/core/upstream-evidence-manifest.js +33 -12
  6. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  7. package/package.json +1 -1
  8. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  9. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  10. package/plugins/lisa/.codex-plugin/skills/lisa-implement/SKILL.md +4 -2
  11. package/plugins/lisa/.codex-plugin/skills/lisa-quality-review/SKILL.md +7 -0
  12. package/plugins/lisa/.codex-plugin/skills/lisa-review-local/SKILL.md +27 -0
  13. package/plugins/lisa/.codex-plugin/skills/lisa-tdd-implementation/SKILL.md +1 -0
  14. package/plugins/lisa/.codex-plugin/skills/lisa-track/SKILL.md +1 -1
  15. package/plugins/lisa/.codex-plugin/skills/lisa-tracker-source-artifacts/SKILL.md +2 -0
  16. package/plugins/lisa/hooks/block-no-verify.agy.sh +53 -4
  17. package/plugins/lisa/hooks/block-no-verify.sh +31 -0
  18. package/plugins/lisa/rules/eager/design-source-of-truth.md +38 -0
  19. package/plugins/lisa/rules/eager/tracked-work.md +1 -1
  20. package/plugins/lisa/rules/reference/design-source-of-truth.md +195 -0
  21. package/plugins/lisa/rules/reference/tracked-work.md +4 -2
  22. package/plugins/lisa/scripts/design-source-gate.mjs +530 -0
  23. package/plugins/lisa/skills/lisa-implement/SKILL.md +4 -2
  24. package/plugins/lisa/skills/lisa-quality-review/SKILL.md +7 -0
  25. package/plugins/lisa/skills/lisa-review-local/SKILL.md +27 -0
  26. package/plugins/lisa/skills/lisa-tdd-implementation/SKILL.md +1 -0
  27. package/plugins/lisa/skills/lisa-track/SKILL.md +1 -1
  28. package/plugins/lisa/skills/lisa-tracker-source-artifacts/SKILL.md +2 -0
  29. package/plugins/lisa-agy/hooks/block-no-verify.agy.sh +53 -4
  30. package/plugins/lisa-agy/plugin.json +1 -1
  31. package/plugins/lisa-agy/scripts/design-source-gate.mjs +530 -0
  32. package/plugins/lisa-agy/skills/lisa-implement/SKILL.md +4 -2
  33. package/plugins/lisa-agy/skills/lisa-quality-review/SKILL.md +7 -0
  34. package/plugins/lisa-agy/skills/lisa-review-local/SKILL.md +27 -0
  35. package/plugins/lisa-agy/skills/lisa-tdd-implementation/SKILL.md +1 -0
  36. package/plugins/lisa-agy/skills/lisa-track/SKILL.md +1 -1
  37. package/plugins/lisa-agy/skills/lisa-tracker-source-artifacts/SKILL.md +2 -0
  38. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  40. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  41. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-copilot/hooks/block-no-verify.sh +31 -0
  45. package/plugins/lisa-copilot/rules/eager/design-source-of-truth.md +38 -0
  46. package/plugins/lisa-copilot/rules/eager/tracked-work.md +1 -1
  47. package/plugins/lisa-copilot/rules/reference/design-source-of-truth.md +195 -0
  48. package/plugins/lisa-copilot/rules/reference/tracked-work.md +4 -2
  49. package/plugins/lisa-copilot/scripts/design-source-gate.mjs +530 -0
  50. package/plugins/lisa-copilot/skills/lisa-implement/SKILL.md +4 -2
  51. package/plugins/lisa-copilot/skills/lisa-quality-review/SKILL.md +7 -0
  52. package/plugins/lisa-copilot/skills/lisa-review-local/SKILL.md +27 -0
  53. package/plugins/lisa-copilot/skills/lisa-tdd-implementation/SKILL.md +1 -0
  54. package/plugins/lisa-copilot/skills/lisa-track/SKILL.md +1 -1
  55. package/plugins/lisa-copilot/skills/lisa-tracker-source-artifacts/SKILL.md +2 -0
  56. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-cursor/hooks/block-no-verify.sh +31 -0
  58. package/plugins/lisa-cursor/rules/design-source-of-truth-reference.mdc +200 -0
  59. package/plugins/lisa-cursor/rules/design-source-of-truth.mdc +43 -0
  60. package/plugins/lisa-cursor/rules/tracked-work-reference.mdc +4 -2
  61. package/plugins/lisa-cursor/rules/tracked-work.mdc +1 -1
  62. package/plugins/lisa-cursor/scripts/design-source-gate.mjs +530 -0
  63. package/plugins/lisa-cursor/skills/lisa-implement/SKILL.md +4 -2
  64. package/plugins/lisa-cursor/skills/lisa-quality-review/SKILL.md +7 -0
  65. package/plugins/lisa-cursor/skills/lisa-review-local/SKILL.md +27 -0
  66. package/plugins/lisa-cursor/skills/lisa-tdd-implementation/SKILL.md +1 -0
  67. package/plugins/lisa-cursor/skills/lisa-track/SKILL.md +1 -1
  68. package/plugins/lisa-cursor/skills/lisa-tracker-source-artifacts/SKILL.md +2 -0
  69. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  71. package/plugins/lisa-expo-agy/plugin.json +1 -1
  72. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  73. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  75. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  76. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  77. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  79. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  80. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  81. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  82. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  83. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  84. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  85. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  86. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  87. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  88. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  89. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  90. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  91. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  92. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  93. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  94. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  95. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  96. package/plugins/lisa-rails-agy/plugin.json +1 -1
  97. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  98. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  99. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  100. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  101. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  102. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  103. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  104. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  105. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  106. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  107. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  108. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  109. package/plugins/src/base/hooks/block-no-verify.agy.sh +53 -4
  110. package/plugins/src/base/hooks/block-no-verify.sh +31 -0
  111. package/plugins/src/base/rules/eager/design-source-of-truth.md +38 -0
  112. package/plugins/src/base/rules/eager/tracked-work.md +1 -1
  113. package/plugins/src/base/rules/reference/design-source-of-truth.md +195 -0
  114. package/plugins/src/base/rules/reference/tracked-work.md +4 -2
  115. package/plugins/src/base/scripts/design-source-gate.mjs +530 -0
  116. package/plugins/src/base/skills/lisa-implement/SKILL.md +4 -2
  117. package/plugins/src/base/skills/lisa-quality-review/SKILL.md +7 -0
  118. package/plugins/src/base/skills/lisa-review-local/SKILL.md +27 -0
  119. package/plugins/src/base/skills/lisa-tdd-implementation/SKILL.md +1 -0
  120. package/plugins/src/base/skills/lisa-track/SKILL.md +1 -1
  121. package/plugins/src/base/skills/lisa-tracker-source-artifacts/SKILL.md +2 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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": "3.3.0",
3
+ "version": "3.4.1",
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"
@@ -102,16 +102,65 @@ def disables_verification(token):
102
102
  and NO_VERIFY.startswith(token)
103
103
  )
104
104
 
105
+ # The only relocations that keep a repo's own hooks in play. Anything else —
106
+ # including "" and /dev/null, which are simply the two most obvious members of
107
+ # the blocked set rather than special cases — is refused.
108
+ PERMITTED_HOOKS_PATHS = {".husky", ".githooks"}
109
+
110
+
111
+ def is_permitted_hooks_path(value):
112
+ """Whether a core.hooksPath value relocates hooks rather than disabling them.
113
+
114
+ Args:
115
+ value: The raw core.hooksPath value as it appeared on the command line.
116
+
117
+ Returns:
118
+ True if the path is an established in-repo hooks directory.
119
+ """
120
+ cleaned = value.strip().strip("'\"")
121
+ if cleaned.startswith("./"):
122
+ cleaned = cleaned[2:]
123
+ return cleaned.rstrip("/") in PERMITTED_HOOKS_PATHS
124
+
125
+
105
126
  for i, token in enumerate(normalized_tokens):
106
127
  if disables_verification(token):
107
128
  sys.exit(1)
108
129
  if token == "HUSKY=0" or token.startswith("HUSKY_SKIP_HOOKS="):
109
130
  sys.exit(1)
110
- if token.startswith("core.hooksPath="):
111
- value = token.split("=", 1)[1]
112
- if value in ("", "/dev/null"):
131
+ # Allowlist the destinations, do not denylist the disabling ones: hooks are
132
+ # disabled just as completely by any directory that happens to contain none
133
+ # (`-c core.hooksPath=/tmp/empty`), so the set that DISABLES hooks is
134
+ # unbounded while the set that legitimately relocates them is tiny. Matched
135
+ # case-insensitively because git config names are.
136
+ lowered = token.lower()
137
+ if lowered.startswith("core.hookspath="):
138
+ if not is_permitted_hooks_path(token.split("=", 1)[1]):
139
+ sys.exit(1)
140
+ if lowered == "core.hookspath" and i + 1 < len(normalized_tokens):
141
+ if not is_permitted_hooks_path(normalized_tokens[i + 1]):
142
+ sys.exit(1)
143
+ # `--config-env=core.hooksPath=SOMEVAR` reads the path out of the named env
144
+ # var, so it is not in the command at all and cannot be allowlisted.
145
+ if lowered.startswith("--config-env="):
146
+ spec = token.split("=", 1)[1]
147
+ if spec.split("=", 1)[0].strip().lower() == "core.hookspath":
148
+ sys.exit(1)
149
+ # git also accepts `--config-env <name>=<envvar>` as two tokens. Guarding
150
+ # only the `=` spelling let the trailing `core.hooksPath=.husky` fall
151
+ # through to the allowlist above, which reads `.husky` as a path — but here
152
+ # it names an ENVIRONMENT VARIABLE, which can hold /dev/null. Checked at the
153
+ # `--config-env` token, which the loop reaches first.
154
+ if lowered == "--config-env" and i + 1 < len(normalized_tokens):
155
+ spec = normalized_tokens[i + 1]
156
+ if spec.split("=", 1)[0].strip().strip("'\"").lower() == "core.hookspath":
113
157
  sys.exit(1)
114
- if token == "core.hooksPath" and i + 1 < len(normalized_tokens) and normalized_tokens[i + 1] in ("", "/dev/null"):
158
+ # `GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=core.hooksPath
159
+ # GIT_CONFIG_VALUE_0=/dev/null git ...` sets the same command-scope config
160
+ # via env-var-style assignments. The index is arbitrary below
161
+ # GIT_CONFIG_COUNT, so it is matched as `\d+` rather than pinned to 0.
162
+ key_match = re.match(r"git_config_key_\d+=(.*)$", lowered, re.DOTALL)
163
+ if key_match and key_match.group(1).strip().strip("'\"") == "core.hookspath":
115
164
  sys.exit(1)
116
165
 
117
166
  sys.exit(0)
@@ -186,6 +186,37 @@ for i, token in enumerate(normalized_tokens):
186
186
  spec = token.split("=", 1)[1]
187
187
  if spec.split("=", 1)[0].strip().lower() == "core.hookspath":
188
188
  sys.exit(1)
189
+ # git accepts `--config-env <name>=<envvar>` as TWO tokens as well as one,
190
+ # and guarding only the `=` spelling was worse than missing the separate
191
+ # form outright: the trailing `core.hooksPath=.husky` then fell through to
192
+ # the allowlist above, which reads `.husky` as a PATH and permits it. But
193
+ # here it is an ENVIRONMENT VARIABLE NAME, and `env '.husky=/dev/null' git
194
+ # --config-env core.hooksPath=.husky` really does resolve hooksPath to
195
+ # /dev/null. The allowlist was being used as the bypass.
196
+ #
197
+ # Checked at the `--config-env` token, which the loop reaches first, so the
198
+ # refusal happens before the value token can be mistaken for a path.
199
+ if lowered == "--config-env" and i + 1 < len(normalized_tokens):
200
+ spec = normalized_tokens[i + 1]
201
+ if spec.split("=", 1)[0].strip().strip("'\"").lower() == "core.hookspath":
202
+ sys.exit(1)
203
+ # `GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=core.hooksPath
204
+ # GIT_CONFIG_VALUE_0=/dev/null git commit` sets command-scope config the
205
+ # same way `-c core.hooksPath=...` does — env-var-style assignments ahead of
206
+ # the invocation instead of a flag — so it disables every hook just as
207
+ # completely while matching none of the token shapes above. Upstream missed
208
+ # this until a downstream fork hardened its own copy against it, which is
209
+ # the one direction a guard must never be caught in.
210
+ #
211
+ # The index is matched as `\d+` rather than pinned to 0: git accepts any
212
+ # index below GIT_CONFIG_COUNT, so a single-index check is evaded by typing
213
+ # a 1. Refused outright, like --config-env=, because the path lives in a
214
+ # separate GIT_CONFIG_VALUE_<n> token that can be exported earlier,
215
+ # reordered, or left out entirely — there is nothing here to allowlist
216
+ # against.
217
+ key_match = re.match(r"git_config_key_\d+=(.*)$", lowered, re.DOTALL)
218
+ if key_match and key_match.group(1).strip().strip("'\"") == "core.hookspath":
219
+ sys.exit(1)
189
220
 
190
221
  sys.exit(0)
191
222
  PY
@@ -0,0 +1,38 @@
1
+ # Design Source of Truth (load-bearing)
2
+
3
+ **Figma is the design source of truth, and every UI surface a change touches declares where its design came from.** A changed UI surface that neither cites a Figma node nor carries the designated marker is a contract violation — and so is a surface whose declaration the gate cannot resolve.
4
+
5
+ **One vendor-neutral contract, cited by** `lisa-implement`, `lisa-tdd-implementation`, `lisa-review-local`, `lisa-quality-review`, and `lisa-tracker-source-artifacts` (the `leaf-only-lifecycle` / `repo-scope-split` precedent: one shared slug, never divergent per-skill prose).
6
+
7
+ ## Membership
8
+
9
+ Membership is **surface, not repo name or file extension**: a file is in scope the moment a change makes it render something user-observable — a screen, component, layout, style token, visual state, or markup a user reads. Barrels, pure-logic modules, tests, stories, generated output, and vendored code are out. Projects narrow or widen the default detection through `designSource.include` / `designSource.exclude` in `.lisa.config.json`; they never turn the obligation off.
10
+
11
+ ## The two declarations
12
+
13
+ Exactly one of these, written as an ordinary comment in whatever syntax the file uses:
14
+
15
+ | Form | Means |
16
+ |---|---|
17
+ | `DESIGN-SOURCE: <figma-url>` | The surface is backed by a Figma node — it already existed there, or it was synced back. |
18
+ | `DESIGN-SOURCE: none — not in Figma` | The surface is deliberately not captured at the design source. |
19
+
20
+ The marker's spelling is fixed and load-bearing; a drifted spelling silently disarms the gate. An optional trailing ` — <reason>` records why. Only a `figma.com` URL seals a surface: a link to a screenshot, a Slack thread, or any other mock is **malformed**, not proof.
21
+
22
+ ## Sync-back is the default; the marker is the exception, not the default
23
+
24
+ When the flow has proven Figma access (the `tool-access-gate` probe), reflecting the surface in Figma and citing the node is the expected outcome. Reach for the marker only when the surface genuinely does not belong in the design source — a debug affordance, a dev-only playground, a throwaway internal tool. The gate reports every marked exception so review can challenge it, and reports a reasonless exception separately when Figma access was available.
25
+
26
+ ## Host design-system rules stay authoritative
27
+
28
+ Projects that carry their own design-system rules (`figma-design-system`, `design-system`, `use-the-design-library`, or an equivalent) keep them. This contract never restates component hierarchy, token vocabulary, or reuse policy, and never overrides them. It asks one orthogonal question the host rules do not: is the design source declared?
29
+
30
+ ## The gate fails closed
31
+
32
+ `scripts/design-source-gate.mjs` decides the change deterministically. It fails on an undeclared surface, a malformed annotation, a file that both cites Figma and denies having a source, a changed file it could not read, and a diff it could not compute. **A design-source violation is a blocking review finding, never a warning** — it is not demoted to "optional", "if cheap", or a follow-up. A gate that passes on what it could not read proves nothing, so it never does.
33
+
34
+ ## Bootstrap and degradation
35
+
36
+ Adoption never demands a retroactive backfill: the gate judges **only the surfaces this change touched**. Pre-existing unannotated UI is burndown, recorded and worked down, not this work item's blocker. If a project has no Figma at all, that is not an exemption — every changed surface carries the marker, and the resulting exception list is the honest record of how much of the product lives outside its design source. Behavior obligations for the same surfaces are unchanged and still governed by `bdd-e2e-coverage`.
37
+
38
+ Full contract (marker grammar, gate semantics, host-rule precedence, bootstrap procedure): [reference/design-source-of-truth.md](../reference/design-source-of-truth.md).
@@ -2,6 +2,6 @@
2
2
 
3
3
  Before the first durable project mutation (code, tests, config, docs, committed research/plans/findings, commits, or PRs), establish exactly one live tracker leaf through `lisa-track`. Read-only discussion and orientation are exempt only while they produce no durable artifact.
4
4
 
5
- The mandatory order is: live-validate an explicit ref, or conservatively search and create exactly one valid leaf through `lisa-tracker-write` when no unique match exists; idempotently claim it through `lisa-tracker-claim`; then persist and verify the worktree-local binding with `node scripts/lisa-work-item.mjs bind <ref>`. A tracker that answers no, and any claim or binding failure, blocks durable work; a tracker that cannot be reached does not block a commit — the offline checks still run, the skip is loud on stderr, and the required `Work-Item Traceability` check re-runs the live checks with credentials before anything merges.
5
+ The mandatory order is: live-validate an explicit ref, or conservatively search and create exactly one valid leaf through `lisa-tracker-write` when no unique match exists; idempotently claim it through `lisa-tracker-claim`; then persist and verify the worktree-local binding with `node scripts/lisa-work-item.mjs link <ref>`. A tracker that answers no, and any claim or binding failure, blocks durable work; a tracker that cannot be reached does not block a commit — the offline checks still run, the skip is loud on stderr, and the required `Work-Item Traceability` check re-runs the live checks with credentials before anything merges.
6
6
 
7
7
  Carry that canonical ref through the branch, every ordinary commit's `Work-Item:` trailer, the PR, usage/evidence, and `lisa-tracker-sync`. Hooks and CI never create tickets. Keep the binding through interruptions or blocked outcomes; run `node scripts/lisa-work-item.mjs clear` only after merge/deploy/verification, two-way linkage/evidence, and the tracker item have all reached true terminal completion.