@codyswann/lisa 4.29.2 → 4.29.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  2. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  3. package/dist/core/upstream-evidence-manifest.js +15 -4
  4. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  5. package/package.json +3 -3
  6. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  7. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  8. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/SKILL.md +55 -0
  9. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/aws-bootstrap.mjs +202 -16
  10. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +589 -100
  11. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/owner.mjs +96 -0
  12. package/plugins/lisa/skills/lisa-secrets-access/SKILL.md +55 -0
  13. package/plugins/lisa/skills/lisa-secrets-access/scripts/aws-bootstrap.mjs +202 -16
  14. package/plugins/lisa/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +589 -100
  15. package/plugins/lisa/skills/lisa-secrets-access/scripts/owner.mjs +96 -0
  16. package/plugins/lisa-agy/plugin.json +1 -1
  17. package/plugins/lisa-agy/skills/lisa-secrets-access/SKILL.md +55 -0
  18. package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/aws-bootstrap.mjs +202 -16
  19. package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +589 -100
  20. package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/owner.mjs +96 -0
  21. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  22. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  23. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  24. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  25. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  26. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa-copilot/skills/lisa-secrets-access/SKILL.md +55 -0
  28. package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/aws-bootstrap.mjs +202 -16
  29. package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +589 -100
  30. package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/owner.mjs +96 -0
  31. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-cursor/skills/lisa-secrets-access/SKILL.md +55 -0
  33. package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/aws-bootstrap.mjs +202 -16
  34. package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +589 -100
  35. package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/owner.mjs +96 -0
  36. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  38. package/plugins/lisa-expo-agy/plugin.json +1 -1
  39. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  40. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  43. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  44. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  48. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  49. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  53. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  54. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  58. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  59. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  63. package/plugins/lisa-rails-agy/plugin.json +1 -1
  64. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  67. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  68. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  69. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  71. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  72. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  73. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  74. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  75. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  76. package/plugins/src/base/skills/lisa-secrets-access/SKILL.md +55 -0
  77. package/plugins/src/base/skills/lisa-secrets-access/scripts/aws-bootstrap.mjs +202 -16
  78. package/plugins/src/base/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +589 -100
  79. package/plugins/src/base/skills/lisa-secrets-access/scripts/owner.mjs +96 -0
  80. package/scripts/two-channel-couplings.json +1 -1
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Which project a credential written to this machine belongs to.
3
+ *
4
+ * Everything this skill writes outside `~/.config/<namespace>` lands in a
5
+ * SHARED per-user location: `~/.aws/config`, `~/.aws/credentials`, `~/.bashrc`,
6
+ * `~/.profile`. Those paths have no tenant in them, so two Lisa projects on one
7
+ * workstation wrote the same identifiers and the second silently replaced the
8
+ * first. Measured with two synthetic bundles declaring the same stage names:
9
+ * two profiles survived where four were written, the surviving `agent-dev`
10
+ * named the second tenant's account, and every shell on the machine sourced the
11
+ * second tenant's values — including the first tenant's sessions. Both runs
12
+ * exited 0.
13
+ *
14
+ * Nothing about the surviving profile is wrong. It is a real, working profile
15
+ * that belongs to someone else, which is why no property check on the profile
16
+ * detects it — only a comparison against the intended owner fires. So the owner
17
+ * has to be carried to the write point rather than inferred there.
18
+ *
19
+ * The owner is the **tenant namespace**, not the repository. That is deliberate
20
+ * and it is the existing model, not a new one: `~/.config/<namespace>` is
21
+ * already the unit of sharing, so two repositories of one tenant SHOULD see the
22
+ * same credentials and two tenants must not. A namespace is also already
23
+ * resolved and validated by the time any writer runs, so nothing has to be
24
+ * guessed at the point of writing.
25
+ *
26
+ * Its own module because both writers need it and neither should import the
27
+ * other: `aws-bootstrap` renders profile names, `materialize-secrets` writes
28
+ * the shell block, and this file imports nothing.
29
+ * @module owner
30
+ */
31
+
32
+ /**
33
+ * The one shape an owner may take.
34
+ *
35
+ * Identical to the namespace rule in `surfaces.mjs`, because it IS the
36
+ * namespace — the same value, re-checked where it is about to become part of an
37
+ * ini section header and a shell filename. Anything with a bracket, newline, or
38
+ * path separator would either corrupt `~/.aws/config` or redirect a write.
39
+ */
40
+ const OWNER_SHAPE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
41
+
42
+ /**
43
+ * Require an owner, and refuse to write anything without one.
44
+ *
45
+ * Failing closed is the whole point. A writer that fell back to an unowned name
46
+ * when resolution came up empty would recreate the exact collision this exists
47
+ * to remove, and it would do it on the machines least able to notice — the ones
48
+ * where nobody declared a tenant.
49
+ * @param {unknown} owner Candidate owner.
50
+ * @param {string} what What is about to be written, for the message.
51
+ * @returns {string} The owner, trimmed, when usable.
52
+ */
53
+ export function assertOwner(owner, what) {
54
+ const trimmed = typeof owner === "string" ? owner.trim() : "";
55
+ if (!trimmed) {
56
+ throw new Error(
57
+ `cannot determine which project these credentials belong to, so ` +
58
+ `${what} was not written.\n` +
59
+ `Name the tenant explicitly — 'lisa environment local --tenant=<name>' ` +
60
+ `— or set "secrets".namespace in .lisa.config.json.\n` +
61
+ `Unowned names collide across projects on a shared machine, and the ` +
62
+ `collision is silent: the surviving credential is valid, it just ` +
63
+ `belongs to someone else.`
64
+ );
65
+ }
66
+ if (!OWNER_SHAPE.test(trimmed) || trimmed === "..") {
67
+ throw new Error(
68
+ `owner "${trimmed}" is not one safe name segment, so ${what} was not ` +
69
+ `written. Use letters, digits, dot, dash or underscore.`
70
+ );
71
+ }
72
+ return trimmed;
73
+ }
74
+
75
+ /**
76
+ * The marker fragment that records who owns a managed block.
77
+ *
78
+ * Written inside the existing marker text rather than as a new marker, so the
79
+ * family recognisers keep matching blocks from every past version. A block with
80
+ * no `owner=` predates ownership and is handled as legacy, never as ours.
81
+ * @param {string} owner Validated owner.
82
+ * @returns {string} The fragment to embed in a marker.
83
+ */
84
+ export function ownerTag(owner) {
85
+ return `owner=${owner}`;
86
+ }
87
+
88
+ /**
89
+ * Read the owner out of one marker line.
90
+ * @param {string} marker The matched marker text.
91
+ * @returns {string|null} The owner, or null when the block predates ownership.
92
+ */
93
+ export function ownerOf(marker) {
94
+ const found = /\bowner=([A-Za-z0-9][A-Za-z0-9._-]*)/.exec(marker);
95
+ return found === null ? null : found[1];
96
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Expo and React Native-specific skills, agents, rules, and MCP servers.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Harper/Fabric-specific Lisa rules for TypeScript component apps.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "NestJS-specific skills and migration write-protection hooks.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Ruby on Rails-specific skills and hooks for RuboCop and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "4.29.2",
3
+ "version": "4.29.4",
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.29.2",
3
+ "version": "4.29.4",
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.29.2",
3
+ "version": "4.29.4",
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.29.2",
3
+ "version": "4.29.4",
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.29.2",
3
+ "version": "4.29.4",
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.29.2",
3
+ "version": "4.29.4",
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.29.2",
3
+ "version": "4.29.4",
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.29.2",
3
+ "version": "4.29.4",
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.29.2",
3
+ "version": "4.29.4",
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.29.2",
3
+ "version": "4.29.4",
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"
@@ -73,6 +73,61 @@ ${XDG_CONFIG_HOME:-$HOME/.config}/<secrets.namespace>/ # dir 0700
73
73
  - Each temporary file is created **beside its destination**, because a rename is only atomic within one filesystem.
74
74
  - The writer and the parser for `secrets.env` live in one module (`envfile.mjs`) on purpose. A shell sources the file, and the resolver reads it back; if the quoting and the parsing drift apart, every value containing a quote is corrupted silently.
75
75
 
76
+ ## Everything written outside that directory is owned
77
+
78
+ Materialization also writes to paths with **no tenant in them** — `~/.aws/config`, `~/.aws/credentials`, `~/.bashrc`, `~/.profile`. Two projects on one machine therefore wrote the same identifiers, and the second silently replaced the first. Measured with two bundles declaring the same stage names: two profiles survived where four were written, the surviving `agent-dev` named the second project's account, and every shell on the machine sourced the second project's values. Both runs exited 0.
79
+
80
+ Nothing about the surviving profile is malformed — it is a real, working profile that belongs to someone else — so no property check on the profile can detect it. Only a comparison against the intended owner fires.
81
+
82
+ **The owner is `secrets.namespace`.** It already scopes `~/.config/<namespace>`, so two repositories of one tenant share and two tenants do not. Nothing is written without one.
83
+
84
+ | Identifier | How it is protected |
85
+ | --- | --- |
86
+ | `~/.aws` profile names | Prefixed: `<namespace>-<stage>` (`<namespace>-lisa-bootstrap` for the source profile). Distinct names make a wrong resolution impossible, matching the convention `lisa-setup-remote-aws` writes. |
87
+ | `~/.aws` managed block | Tagged `owner=<namespace>`. Only this owner's block is replaced; another project's is left untouched. |
88
+ | Shell profile block | Tagged the same way, but it **cannot** be namespaced — it exports into every shell and there is no name a consumer selects. So it is claimed only when unowned or already ours; a block held by another project stops the run and names it. `LISA_SECRETS_CLAIM_SHELL_PROFILE=1` takes it over deliberately. |
89
+
90
+ **Blocks written before ownership existed** are attributed rather than assumed, because leaving an orphan behind is its own defect — an orphaned shell block is still sourced, and the last assignment wins.
91
+
92
+ - A legacy **shell** block names `<config root>/<tenant>/secrets.env` in its body, so it states its own owner: ours is replaced in place, another tenant's stops the run.
93
+ - A legacy **`~/.aws`** block carries no such tell, so it is attributed by the accounts in its role ARNs. Matching this bundle's accounts means it is our own past output and is replaced; anything else is reported by name on every run and removed only under `LISA_SECRETS_PRUNE_LEGACY_PROFILES=1`.
94
+
95
+ A stage may declare `expectedAccountId`. This writer never contacts AWS, so it cannot compare against a live `sts:GetCallerIdentity` the way `lisa-setup-remote-aws` does — but a declaration contradicting the account in its own role ARN is caught before anything is written.
96
+
97
+ ### Emit vs resolve — the bare names still RESOLVE, and are never EMITTED
98
+
99
+ Two different guarantees, with two different expiry conditions. Conflating them is how a temporary shim becomes permanent by accident.
100
+
101
+ | | Guarantee | Shape | Expires |
102
+ | --- | --- | --- | --- |
103
+ | **Emit** | what this writer produces as canonical output | `<namespace>-<stage>`, assuming from `<namespace>-lisa-bootstrap` | never |
104
+ | **Resolve** | names that must keep working for *readers* mid-migration | the above, **plus** the deprecated bare `<stage>` family | when no caller resolves the bare names |
105
+
106
+ Generators outside this repository emit the bare `<stage>` family and the bare `lisa-bootstrap` source profile independently, and scripts and documentation in caller repositories name them directly. Nothing co-ordinates a rename across those repositories, so emitting only the owned names would leave writer and reader disagreeing: a bare `[profile <stage>]` whose `source_profile = lisa-bootstrap` would point at a section that no longer exists, and every call through it would fail to resolve.
107
+
108
+ So the bare names are written as **deprecated aliases beside** the canonical output — never as canonical output — from one bundle in one pass, inside the same owner-tagged block:
109
+
110
+ ```ini
111
+ [profile <namespace>-<stage>] # EMITTED — canonical output
112
+ source_profile = <namespace>-lisa-bootstrap
113
+
114
+ [profile <stage>] # RESOLVE-ONLY, DEPRECATED — an alias
115
+ source_profile = lisa-bootstrap # same role, external id and region
116
+ ```
117
+
118
+ Nothing in the canonical body ever names the bare source profile, so canonical output does not depend on the deprecated half resolving. `emitSourceProfileFor()` is the only thing that names a source profile in canonical output; `RESOLVE_ONLY_LEGACY_SOURCE_PROFILE` is used by the resolve path alone — not to attribute a block either, since ownership comes from the `owner=` marker and a pre-ownership block is attributed by the accounts in its role ARNs.
119
+
120
+ **The resolve window is not a fix.** The bare family is a *single shared slot* on a machine that may serve several projects — the exact collision the owned names remove. On the resolve path that collision is unfixed:
121
+
122
+ - claimed when nothing else holds them, or when they are already this project's;
123
+ - refused **as a set** when another project or an operator's own section holds any part of them — a half-written family whose source profile belongs to someone else resolves into that project's account;
124
+ - reported by name on every run when refused, because a caller that has not migrated still uses those names and would otherwise get another project's account while reporting success;
125
+ - `LISA_SECRETS_CLAIM_LEGACY_PROFILES=1` takes them deliberately.
126
+
127
+ The emitted names are always written and always correct; refusing an alias never withholds them. The bare names are correct for at most one project per machine.
128
+
129
+ **Removal condition, stated against the resolve path** — the only half that expires. Delete the resolve-only half once nothing outside this repository *resolves* the bare family, i.e. no caller names `<stage>` or `lisa-bootstrap` when selecting a profile. A caller proves it by selecting only `<namespace>-<stage>`; `LISA_SECRETS_NO_LEGACY_PROFILES=1` opts one out ahead of the removal and takes the isolation immediately. The emit path is unaffected by that deletion, because it never used the deprecated name.
130
+
76
131
  ## Configuration
77
132
 
78
133
  ```json