@jaguilar87/gaia 5.0.2 → 5.0.5

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 (154) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/ARCHITECTURE.md +0 -1
  4. package/CHANGELOG.md +110 -0
  5. package/INSTALL.md +0 -2
  6. package/README.md +1 -6
  7. package/bin/README.md +0 -1
  8. package/bin/cli/_install_helpers.py +1 -1
  9. package/bin/cli/approvals.py +23 -21
  10. package/bin/cli/cleanup.py +0 -1
  11. package/bin/cli/doctor.py +1 -1
  12. package/bin/cli/memory.py +2 -0
  13. package/bin/cli/update.py +1 -1
  14. package/bin/pre-publish-validate.js +48 -5
  15. package/config/README.md +22 -44
  16. package/config/surface-routing.json +0 -2
  17. package/dist/gaia-ops/.claude-plugin/plugin.json +1 -1
  18. package/dist/gaia-ops/config/README.md +22 -44
  19. package/dist/gaia-ops/config/surface-routing.json +0 -2
  20. package/dist/gaia-ops/hooks/modules/agents/contract_validator.py +18 -0
  21. package/dist/gaia-ops/hooks/modules/agents/handoff_persister.py +214 -2
  22. package/dist/gaia-ops/hooks/modules/agents/response_contract.py +26 -0
  23. package/dist/gaia-ops/hooks/modules/agents/transcript_reader.py +15 -0
  24. package/dist/gaia-ops/hooks/modules/security/__init__.py +0 -5
  25. package/dist/gaia-ops/hooks/modules/security/approval_grants.py +124 -19
  26. package/dist/gaia-ops/hooks/modules/security/mutative_verbs.py +99 -7
  27. package/dist/gaia-ops/hooks/modules/tools/bash_validator.py +127 -24
  28. package/dist/gaia-ops/hooks/modules/validation/commit_validator.py +90 -55
  29. package/dist/gaia-ops/skills/README.md +1 -1
  30. package/dist/gaia-ops/skills/agent-contract-handoff/SKILL.md +3 -0
  31. package/dist/gaia-ops/skills/agent-response/SKILL.md +4 -2
  32. package/dist/gaia-ops/skills/gaia-patterns/SKILL.md +1 -1
  33. package/dist/gaia-ops/skills/gaia-patterns/reference.md +2 -3
  34. package/dist/gaia-ops/skills/gaia-release/SKILL.md +60 -24
  35. package/dist/gaia-ops/skills/gaia-release/reference.md +35 -11
  36. package/dist/gaia-ops/skills/git-conventions/SKILL.md +6 -2
  37. package/dist/gaia-ops/skills/orchestrator-present-approval/SKILL.md +30 -7
  38. package/dist/gaia-ops/skills/orchestrator-present-approval/reference.md +32 -15
  39. package/dist/gaia-ops/skills/readme-writing/SKILL.md +1 -1
  40. package/dist/gaia-ops/skills/readme-writing/reference.md +0 -1
  41. package/dist/gaia-ops/skills/security-tiers/SKILL.md +5 -1
  42. package/dist/gaia-ops/skills/security-tiers/reference.md +3 -1
  43. package/dist/gaia-ops/skills/subagent-request-approval/SKILL.md +43 -6
  44. package/dist/gaia-ops/skills/subagent-request-approval/reference.md +66 -16
  45. package/dist/gaia-ops/tools/context/README.md +1 -1
  46. package/dist/gaia-ops/tools/gaia_simulator/extractor.py +0 -1
  47. package/dist/gaia-ops/tools/scan/ui.py +20 -4
  48. package/dist/gaia-ops/tools/scan/verify.py +3 -3
  49. package/dist/gaia-ops/tools/validation/README.md +15 -24
  50. package/dist/gaia-security/.claude-plugin/plugin.json +1 -1
  51. package/dist/gaia-security/hooks/modules/agents/contract_validator.py +18 -0
  52. package/dist/gaia-security/hooks/modules/agents/handoff_persister.py +214 -2
  53. package/dist/gaia-security/hooks/modules/agents/response_contract.py +26 -0
  54. package/dist/gaia-security/hooks/modules/agents/transcript_reader.py +15 -0
  55. package/dist/gaia-security/hooks/modules/security/__init__.py +0 -5
  56. package/dist/gaia-security/hooks/modules/security/approval_grants.py +124 -19
  57. package/dist/gaia-security/hooks/modules/security/mutative_verbs.py +99 -7
  58. package/dist/gaia-security/hooks/modules/tools/bash_validator.py +127 -24
  59. package/dist/gaia-security/hooks/modules/validation/commit_validator.py +90 -55
  60. package/gaia/state/transitions.py +4 -4
  61. package/gaia/store/writer.py +56 -0
  62. package/hooks/modules/README.md +2 -4
  63. package/hooks/modules/agents/contract_validator.py +18 -0
  64. package/hooks/modules/agents/handoff_persister.py +214 -2
  65. package/hooks/modules/agents/response_contract.py +26 -0
  66. package/hooks/modules/agents/transcript_reader.py +15 -0
  67. package/hooks/modules/security/__init__.py +0 -5
  68. package/hooks/modules/security/approval_grants.py +124 -19
  69. package/hooks/modules/security/mutative_verbs.py +99 -7
  70. package/hooks/modules/tools/bash_validator.py +127 -24
  71. package/hooks/modules/validation/commit_validator.py +90 -55
  72. package/index.js +2 -12
  73. package/package.json +4 -6
  74. package/pyproject.toml +3 -3
  75. package/scripts/bootstrap_database.sh +88 -439
  76. package/scripts/check_schema_drift.py +208 -0
  77. package/scripts/migrations/README.md +78 -28
  78. package/scripts/migrations/schema.checksum +8 -0
  79. package/scripts/release-prepare.mjs +199 -0
  80. package/skills/README.md +1 -1
  81. package/skills/agent-contract-handoff/SKILL.md +3 -0
  82. package/skills/agent-response/SKILL.md +4 -2
  83. package/skills/gaia-patterns/SKILL.md +1 -1
  84. package/skills/gaia-patterns/reference.md +2 -3
  85. package/skills/gaia-release/SKILL.md +60 -24
  86. package/skills/gaia-release/reference.md +35 -11
  87. package/skills/git-conventions/SKILL.md +6 -2
  88. package/skills/orchestrator-present-approval/SKILL.md +30 -7
  89. package/skills/orchestrator-present-approval/reference.md +32 -15
  90. package/skills/readme-writing/SKILL.md +1 -1
  91. package/skills/readme-writing/reference.md +0 -1
  92. package/skills/security-tiers/SKILL.md +5 -1
  93. package/skills/security-tiers/reference.md +3 -1
  94. package/skills/subagent-request-approval/SKILL.md +43 -6
  95. package/skills/subagent-request-approval/reference.md +66 -16
  96. package/tools/context/README.md +1 -1
  97. package/tools/gaia_simulator/extractor.py +0 -1
  98. package/tools/scan/ui.py +20 -4
  99. package/tools/scan/verify.py +3 -3
  100. package/tools/validation/README.md +15 -24
  101. package/commands/README.md +0 -64
  102. package/commands/gaia.md +0 -37
  103. package/commands/scan-project.md +0 -74
  104. package/config/crons-schema.md +0 -81
  105. package/config/git_standards.json +0 -72
  106. package/dist/gaia-ops/commands/gaia.md +0 -37
  107. package/dist/gaia-ops/config/crons-schema.md +0 -81
  108. package/dist/gaia-ops/config/git_standards.json +0 -72
  109. package/dist/gaia-ops/hooks/modules/security/gitops_validator.py +0 -179
  110. package/dist/gaia-ops/tools/agentic-loop/decide-status.py +0 -210
  111. package/dist/gaia-ops/tools/agentic-loop/parse-metric.py +0 -106
  112. package/dist/gaia-ops/tools/agentic-loop/record-iteration.py +0 -223
  113. package/dist/gaia-security/hooks/modules/security/gitops_validator.py +0 -179
  114. package/git-hooks/commit-msg +0 -41
  115. package/hooks/modules/security/gitops_validator.py +0 -179
  116. package/scripts/migrations/v10_to_v11.sql +0 -170
  117. package/scripts/migrations/v10_to_v11_fresh.sql +0 -18
  118. package/scripts/migrations/v11_to_v12.sql +0 -195
  119. package/scripts/migrations/v11_to_v12_fresh.sql +0 -19
  120. package/scripts/migrations/v12_to_v13.sql +0 -48
  121. package/scripts/migrations/v12_to_v13_fresh.sql +0 -17
  122. package/scripts/migrations/v13_to_v14.sql +0 -44
  123. package/scripts/migrations/v13_to_v14_fresh.sql +0 -17
  124. package/scripts/migrations/v14_to_v15.sql +0 -71
  125. package/scripts/migrations/v14_to_v15_fresh.sql +0 -19
  126. package/scripts/migrations/v15_to_v16.sql +0 -57
  127. package/scripts/migrations/v15_to_v16_fresh.sql +0 -18
  128. package/scripts/migrations/v16_to_v17.sql +0 -51
  129. package/scripts/migrations/v16_to_v17_fresh.sql +0 -18
  130. package/scripts/migrations/v17_to_v18.sql +0 -66
  131. package/scripts/migrations/v17_to_v18_fresh.sql +0 -24
  132. package/scripts/migrations/v1_to_v2.sql +0 -97
  133. package/scripts/migrations/v2_to_v3.sql +0 -68
  134. package/scripts/migrations/v2_to_v3_merge.sql +0 -69
  135. package/scripts/migrations/v3_to_v4.sql +0 -67
  136. package/scripts/migrations/v3_to_v4_fresh.sql +0 -20
  137. package/scripts/migrations/v4_to_v5.sql +0 -55
  138. package/scripts/migrations/v4_to_v5_fresh.sql +0 -20
  139. package/scripts/migrations/v5_to_v6.sql +0 -48
  140. package/scripts/migrations/v5_to_v6_fresh.sql +0 -17
  141. package/scripts/migrations/v6_to_v7.sql +0 -26
  142. package/scripts/migrations/v6_to_v7_fresh.sql +0 -13
  143. package/scripts/migrations/v7_to_v8.sql +0 -44
  144. package/scripts/migrations/v7_to_v8_fresh.sql +0 -14
  145. package/scripts/migrations/v8_to_v9.sql +0 -87
  146. package/scripts/migrations/v8_to_v9_fresh.sql +0 -15
  147. package/scripts/migrations/v9_to_v10.sql +0 -109
  148. package/scripts/migrations/v9_to_v10_episodes_workspace.sql +0 -109
  149. package/scripts/migrations/v9_to_v10_fresh.sql +0 -18
  150. package/templates/README.md +0 -70
  151. package/templates/managed-settings.template.json +0 -43
  152. package/tools/agentic-loop/decide-status.py +0 -210
  153. package/tools/agentic-loop/parse-metric.py +0 -106
  154. package/tools/agentic-loop/record-iteration.py +0 -223
@@ -8,7 +8,7 @@
8
8
  {
9
9
  "name": "gaia-ops",
10
10
  "description": "Full DevOps orchestration for Claude Code. Eight specialized agents handle the complete development lifecycle — analysis, planning, execution, and deployment. Gaia-Ops scans your codebase to understand it and injects the right context into each sub-agent. Every command is classified by risk: read-only runs freely, state changes pause for your approval, and irreversible operations are permanently blocked.",
11
- "version": "5.0.2",
11
+ "version": "5.0.5",
12
12
  "category": "devops",
13
13
  "author": {
14
14
  "name": "jaguilar87",
@@ -20,7 +20,7 @@
20
20
  {
21
21
  "name": "gaia-security",
22
22
  "description": "Keeps you in the loop only when it matters. Gaia Security analyzes every command and classifies it into risk tiers: read-only queries run freely, simulations and validations pass through, and state-changing operations (create, delete, apply, push) pause for your explicit approval before executing. Irreversible commands like dropping databases or deleting cloud infrastructure are permanently blocked.",
23
- "version": "5.0.2",
23
+ "version": "5.0.5",
24
24
  "category": "security",
25
25
  "author": {
26
26
  "name": "jaguilar87",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gaia-ops",
3
- "version": "5.0.2",
3
+ "version": "5.0.5",
4
4
  "description": "Security-first orchestrator with specialized agents, hooks, and governance for AI coding",
5
5
  "author": {
6
6
  "name": "jaguilar87",
package/ARCHITECTURE.md CHANGED
@@ -72,7 +72,6 @@ Order is short-circuit -- first match wins:
72
72
  | If mutative + no active grant -> generate nonce, block
73
73
  | If mutative + active grant -> allow (T3)
74
74
  | If not mutative -> safe by elimination (T0)
75
- 6. gitops_validator --> GitOps policy for kubectl/helm/flux
76
75
  ```
77
76
 
78
77
  ### Task/Agent Validation
package/CHANGELOG.md CHANGED
@@ -7,6 +7,116 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [5.0.5] - 2026-06-11
11
+
12
+ ### Repository Hygiene, Python 3.11 Floor, v18 Schema Floor + Drift Guard, Release-Pipeline Hardening
13
+
14
+ Maintenance release focused on shrinking the surface area and hardening the release pipeline. Dead and redundant surfaces are removed, Python 3.9 support is dropped (minimum is now 3.11), the database migration history is collapsed to a v18 floor backed by a new build-time schema-drift guard, git-format rules are inlined into their single consumer, and several release-pipeline bugs that caused CI re-triggers and stale local installs are fixed.
15
+
16
+ #### Added
17
+
18
+ - **Schema-drift guard wired into pre-publish validation** — a new build/pre-publish
19
+ check (`scripts/check_schema_drift.py` + a recorded fingerprint in
20
+ `scripts/migrations/schema.checksum`, wired into pre-publish-validate **Step 5c**)
21
+ fails the build if `gaia/store/schema.sql` changes without a matching schema version
22
+ bump and migration. This makes silent schema drift impossible to ship: any edit to
23
+ the schema must be accompanied by a version bump, or the gate stops the release.
24
+
25
+ #### Changed
26
+
27
+ - **Python minimum is now 3.11 (Python 3.9 dropped)** — `pyproject.toml`
28
+ `requires-python`, the ruff target version, and the CI test matrix all move to 3.11
29
+ as the floor. The dead `scripts/check-py39-compat.py` compatibility checker and its
30
+ npm alias are removed along with the 3.9 support.
31
+
32
+ - **Database migration history collapsed to a v18 floor** — fresh installs now stamp
33
+ schema **v18** directly instead of replaying the full migration chain. Databases below
34
+ the v18 floor are rejected cleanly with guidance to recreate, rather than attempting an
35
+ unsupported incremental upgrade. The per-step `vN_to_vN+1` migration SQL and their
36
+ obsolete version-specific tests are removed.
37
+
38
+ - **Git commit-format rules inlined into their single consumer** — the commit-format
39
+ rules previously in `config/git_standards.json` are inlined directly into
40
+ `commit_validator.py` (its only reader), and the standalone config file is removed.
41
+ Forbidden-footer enforcement is consolidated into `bash_validator` (the duplicate dead
42
+ check is removed); runtime AI-attribution footer stripping continues to live in
43
+ `bash_validator`. The git-conventions skill is updated to match.
44
+
45
+ - **Release pipeline hardened** — `publish.yml` now runs on Python 3.11 and appends
46
+ `[skip ci]` to the dist commit-back, stopping the CI re-trigger / version-churn loop
47
+ that re-ran the pipeline on every published dist commit. `gaia:install-local` now
48
+ rebuilds plugins before packing so a local install can never carry a stale `dist/`.
49
+ `ci.yml` drops the obsolete `settings.json` build assertions, and the gaia-release
50
+ skill drift is corrected.
51
+
52
+ - **pytest tmp uses the default OS tmpdir** — the in-repo pytest `basetemp` that
53
+ polluted the working tree (and broke scanner project-identity isolation) is removed;
54
+ tests now use the default OS temporary directory. `config/README.md` is rewritten to
55
+ match the actual directory contents.
56
+
57
+ #### Removed
58
+
59
+ - **Dead and redundant surfaces and artifacts** — removed `evidence/`, `docs/`,
60
+ `git-hooks/` (the redundant `commit-msg` sed copy; runtime footer stripping stays in
61
+ `bash_validator`), `tools/agentic-loop/`, `tools/review/`, `logs/`, the `commands/`
62
+ slash-command surface (including `/gaia`), the `templates/` managed-settings surface,
63
+ and `config/crons-schema.md`. These were either unused, duplicated by an active code
64
+ path, or superseded surfaces with no remaining consumer.
65
+
66
+ ## [5.0.4] - 2026-06-06
67
+
68
+ ### COMMAND_SET Batch Approval, Consent-Reducing Approval Verbs, Contract Advisory Field, Version Source Sync
69
+
70
+ Patch release superseding 5.0.3 (which was never published to npm due to a pyproject.toml version drift that failed pre-publish validation). This release adds the version source sync fix on top of all 5.0.3 changes: COMMAND_SET batch-approval wired end-to-end, consent-reducing approval verbs reclassified out of T3, advisory contract field added, redundant `gitops_validator` removed, and all version sources (package.json, pyproject.toml, .claude-plugin/plugin.json, .claude-plugin/marketplace.json, CHANGELOG.md) aligned. Full suite green (4555 passed).
71
+
72
+ #### Added
73
+
74
+ - **COMMAND_SET batch approval, end-to-end** — a payload carrying a `command_set`
75
+ of more than one mutative command now activates into ONE `COMMAND_SET` grant
76
+ covering the whole batch instead of being degraded to a single command. The
77
+ create side (`activate_db_pending_by_prefix` Step 3b in `approval_grants.py`,
78
+ fed by `_intake_command_set_pending` in `handoff_persister.py` and persisted via
79
+ `gaia/store/writer.py`) was previously orphaned; it is now wired to the
80
+ byte-for-byte consume path in `bash_validator`. The batch is consumed
81
+ item-by-item under a single consent.
82
+
83
+ - **Advisory `user_facing_summary` field on the agent contract** — an additive,
84
+ optional field in the `agent_contract_handoff` envelope (`contract_validator.py`,
85
+ `response_contract.py`) carrying a human-readable summary for the orchestrator to
86
+ surface. Purely additive; absence does not affect validation.
87
+
88
+ #### Changed
89
+
90
+ - **Consent-reducing approval verbs are no longer T3** — `gaia approvals
91
+ revoke|reject|reject-all|clean` only revoke or discard grants Gaia itself issued
92
+ (they reduce capability, never reach remote state), so they are reclassified out
93
+ of T3 via `CONSENT_REDUCING_SUBCOMMAND_EXCEPTIONS` in `mutative_verbs.py`. `gaia
94
+ approvals approve` *grants* capability and remains T3.
95
+
96
+ - **`gaia approvals revoke` unified with auto-detect** — `revoke` now auto-detects
97
+ a pending approval (pending → grant) and the separate `revoke-v2` command was
98
+ removed. Behavior is otherwise unchanged.
99
+
100
+ - **Plan-first heuristic** — COMMAND_SET is now treated as a judgment call, not a
101
+ default, when deciding how to present batched mutative work.
102
+
103
+ #### Fixed
104
+
105
+ - **Guard empty/None `transcript_path`** — `transcript_reader.py` now guards against
106
+ an empty or `None` transcript path instead of failing downstream during nonce
107
+ extraction.
108
+
109
+ - **Harden AI-attribution footer stripping** — the attribution-footer stripping in
110
+ `bash_validator.py` is hardened against additional footer shapes.
111
+
112
+ #### Removed
113
+
114
+ - **Redundant `gitops_validator`** — `hooks/modules/security/gitops_validator.py` and
115
+ its test are removed; its responsibilities are covered by the unified bash
116
+ validation path. All references (security `__init__`, `bash_validator` import/call,
117
+ simulator extractor, surface-routing config, architecture docs, and skill/README
118
+ references) are cleaned up.
119
+
10
120
  ## [5.0.2] - 2026-06-03
11
121
 
12
122
  ### Approval-Flow Hardening, mkdir Reclassification, Jira Skill
package/INSTALL.md CHANGED
@@ -172,7 +172,6 @@ your-project/
172
172
  │ ├── hooks/ (symlink) → Security validations
173
173
  │ ├── commands/ (symlink) → Slash commands
174
174
  │ ├── config/ (symlink) → Configuration (contracts, rules)
175
- │ ├── templates/ (symlink) → Installation templates
176
175
  │ ├── logs/ ← Audit logs
177
176
  │ ├── approvals/ ← Pending T3 approval files
178
177
  │ ├── plugin-registry.json ← installed[].name = "gaia-ops"
@@ -204,7 +203,6 @@ Once installed, you have access to **complete documentation** in each directory:
204
203
  ├── config/README.md Contracts, git standards, surface routing
205
204
  ├── hooks/README.md 8 hook scripts (4 primary + 4 event handlers)
206
205
  ├── tools/ Context, memory, validation, review
207
- ├── templates/README.md Installation templates
208
206
  └── bin/README.md CLI utilities
209
207
  ```
210
208
 
package/README.md CHANGED
@@ -21,7 +21,7 @@ UserPromptSubmit -> routing -> PreToolUse -> agent -> PostToolUse -> S
21
21
  injection
22
22
  ```
23
23
 
24
- That pipeline is the spine. Everything else in this repo is either a component of that pipeline (`hooks/`, `agents/`, `skills/`, `config/`) or infrastructure that supports it (`build/`, `bin/`, `tests/`, `templates/`). Start with the folder that matches the behavior you want to understand, and its README will tell you where it fits in the flow.
24
+ That pipeline is the spine. Everything else in this repo is either a component of that pipeline (`hooks/`, `agents/`, `skills/`, `config/`) or infrastructure that supports it (`build/`, `bin/`, `tests/`). Start with the folder that matches the behavior you want to understand, and its README will tell you where it fits in the flow.
25
25
 
26
26
  ## Overview
27
27
 
@@ -150,10 +150,6 @@ Gaia enforces a 6-layer security pipeline:
150
150
  | Mutative verb detection | `ask` dialog for state-changing ops | User approves via native dialog |
151
151
  | Settings deny rules | 147 deny rules in `settings.local.json` | Self-healing (restored each session) |
152
152
 
153
- ### Enterprise Deployment
154
-
155
- For organization-wide enforcement, deploy `templates/managed-settings.template.json` as a managed settings policy via Claude.ai Admin Console. Managed settings have the highest precedence and cannot be overridden.
156
-
157
153
  ## Project Structure
158
154
 
159
155
  ```
@@ -164,7 +160,6 @@ gaia-dev/
164
160
  ├── config/ # Configuration — routing, contracts, rules, git standards
165
161
  ├── commands/ # Slash commands — /gaia, /scan-project
166
162
  ├── build/ # Plugin manifests — hook + agent registration for Claude Code
167
- ├── templates/ # Installation templates — managed-settings for enterprise
168
163
  ├── bin/ # Single `gaia` CLI; subcommands discovered from bin/cli/
169
164
  ├── tests/ # Test suite — 3-layer pyramid (pytest, LLM eval, e2e)
170
165
  └── tools/ # Context provisioning tools
package/bin/README.md CHANGED
@@ -110,6 +110,5 @@ A single binary; subcommands are discovered, not registered.
110
110
 
111
111
  - [`package.json`](../package.json) -- exposes `bin/gaia`; `scripts.postinstall` / `scripts.preuninstall` wire the lifecycle subcommands
112
112
  - [`INSTALL.md`](../INSTALL.md) -- installation workflow that calls `gaia scan` and `gaia install`
113
- - [`templates/README.md`](../templates/README.md) -- `gaia install` and `gaia scan` consume templates from here
114
113
  - [`hooks/README.md`](../hooks/README.md) -- `gaia doctor` verifies the hook registrations are valid
115
114
  - [`bin/validate-sandbox.sh`](./validate-sandbox.sh) -- end-to-end harness that drives `gaia` subcommands against a fresh tarball install
@@ -345,7 +345,7 @@ def merge_local_hooks(
345
345
  # ---------------------------------------------------------------------------
346
346
 
347
347
  # Directories the package exposes via .claude/<name> symlinks
348
- _SYMLINK_NAMES = ["agents", "tools", "hooks", "commands", "templates", "config", "skills"]
348
+ _SYMLINK_NAMES = ["agents", "tools", "hooks", "commands", "config", "skills"]
349
349
  # Files (not dirs) we link or copy into .claude/
350
350
  _SYMLINK_FILES = ["CHANGELOG.md"]
351
351
 
@@ -440,13 +440,17 @@ def cmd_show(args) -> int:
440
440
  # Subcommand: revoke
441
441
  # ---------------------------------------------------------------------------
442
442
 
443
- def cmd_revoke(args) -> int:
444
- """Revoke an active command_set grant by its approval_id.
443
+ def _revoke_grant(args) -> int:
444
+ """Revoke an active command_set grant by its approval_id (legacy path).
445
445
 
446
446
  Calls ``writer.revoke_approval_grant(approval_id)`` to mark the grant
447
447
  REVOKED in the DB. After revocation, any unconsumed commands in the
448
448
  command_set will require fresh approval.
449
449
 
450
+ This is the legacy ``approval_grants``-table path. It is invoked as the
451
+ fallback by the unified :func:`cmd_revoke` when an id is not found in the
452
+ new ``approvals`` table.
453
+
450
454
  Exits 0 on success, 1 if the grant is not found or already in a terminal
451
455
  state.
452
456
  """
@@ -939,18 +943,14 @@ def cmd_pending(args) -> int:
939
943
  # ---------------------------------------------------------------------------
940
944
 
941
945
  def _resolve_approval_id(raw_id: str) -> str:
942
- """Normalize a raw approval_id input.
946
+ """Normalize a raw approval_id input by trimming surrounding whitespace.
943
947
 
944
- Accepts:
945
- - Full P-{uuid4hex} form: returned as-is.
946
- - Bare hex (no P- prefix): prefixed with 'P-'.
947
- - P-XXXX short form: returned as-is for prefix search downstream.
948
+ The input is passed through unchanged otherwise -- both the full
949
+ ``P-{uuid4hex}`` form and the ``P-XXXX`` short form are returned as-is for
950
+ exact or prefix lookup downstream. (A bare hex string with no ``P-`` prefix
951
+ is also returned untouched; the lookup layer handles that case.)
948
952
  """
949
- stripped = raw_id.strip()
950
- if stripped.upper().startswith("P-"):
951
- return stripped
952
- # Try to detect if it's a bare hex string missing the P- prefix.
953
- return stripped
953
+ return raw_id.strip()
954
954
 
955
955
 
956
956
  def cmd_show_v2(args) -> int:
@@ -1019,14 +1019,16 @@ def cmd_history_single(args) -> int:
1019
1019
 
1020
1020
 
1021
1021
  # ---------------------------------------------------------------------------
1022
- # T3.2: gaia approvals revoke-v2 <id> -- revoke using new approvals table
1022
+ # gaia approvals revoke <id> -- unified revoke (auto-detects pending vs grant)
1023
1023
  # ---------------------------------------------------------------------------
1024
1024
 
1025
- def cmd_revoke_v2(args) -> int:
1026
- """Revoke a pending approval from the new approvals table.
1025
+ def cmd_revoke(args) -> int:
1026
+ """Revoke an approval, auto-detecting which store owns it.
1027
1027
 
1028
- Inserts a REVOKED event and updates status to 'revoked'. Requires
1029
- the approval to be in 'pending' status.
1028
+ First looks the id up in the new ``approvals`` table. If found and
1029
+ ``pending``, inserts a REVOKED event and updates status to 'revoked'.
1030
+ If the id is not present in the new table, falls back to the legacy
1031
+ command_set grant path (:func:`_revoke_grant`).
1030
1032
 
1031
1033
  With ``--yes``, skips the interactive confirmation prompt.
1032
1034
  Exits 0 on success, 1 on error.
@@ -1042,8 +1044,8 @@ def cmd_revoke_v2(args) -> int:
1042
1044
  return 1
1043
1045
 
1044
1046
  if approval is None:
1045
- # Fall back to old revoke command if not found in new table.
1046
- return cmd_revoke(args)
1047
+ # Fall back to legacy grant revoke if not found in new table.
1048
+ return _revoke_grant(args)
1047
1049
 
1048
1050
  current_status = approval.get("status", "?")
1049
1051
  if current_status != "pending":
@@ -1596,7 +1598,7 @@ def register(subparsers) -> None:
1596
1598
  help="Full approval_id (P-{uuid4hex}) of the approval to revoke",
1597
1599
  )
1598
1600
  p_revoke.add_argument("--yes", action="store_true", help="Skip confirmation prompt")
1599
- p_revoke.set_defaults(func=cmd_revoke_v2)
1601
+ p_revoke.set_defaults(func=cmd_revoke)
1600
1602
 
1601
1603
  # approve (T3.3) -- cross-session grant
1602
1604
  p_approve = sub.add_parser(
@@ -1839,7 +1841,7 @@ def _build_standalone_parser() -> argparse.ArgumentParser:
1839
1841
  p_revoke = subparsers.add_parser("revoke", help="Revoke a pending approval")
1840
1842
  p_revoke.add_argument("approval_id", metavar="APPROVAL_ID")
1841
1843
  p_revoke.add_argument("--yes", action="store_true")
1842
- p_revoke.set_defaults(func=cmd_revoke_v2)
1844
+ p_revoke.set_defaults(func=cmd_revoke)
1843
1845
 
1844
1846
  p_history = subparsers.add_parser("history", help="Show approval history")
1845
1847
  p_history.add_argument("approval_id", metavar="APPROVAL_ID", nargs="?")
@@ -333,7 +333,6 @@ SYMLINKS_TO_REMOVE = [
333
333
  ".claude/tools",
334
334
  ".claude/hooks",
335
335
  ".claude/commands",
336
- ".claude/templates",
337
336
  ".claude/config",
338
337
  ".claude/CHANGELOG.md",
339
338
  ".claude/README.en.md",
package/bin/cli/doctor.py CHANGED
@@ -762,7 +762,7 @@ def _extract_check_values(
762
762
  @register_check("Symlinks", order=50)
763
763
  def check_symlinks(project_root: Path) -> dict:
764
764
  """Check .claude/ symlinks resolve to package content."""
765
- names = ["agents", "tools", "hooks", "commands", "templates", "config", "skills", "CHANGELOG.md"]
765
+ names = ["agents", "tools", "hooks", "commands", "config", "skills", "CHANGELOG.md"]
766
766
  critical = {"agents", "hooks", "skills"}
767
767
  valid = 0
768
768
  has_critical_missing = False
package/bin/cli/memory.py CHANGED
@@ -19,6 +19,8 @@ Mutating subcommands operate on the curated ``memory`` table in
19
19
  ~/.claude/projects/.../memory/).
20
20
  """
21
21
 
22
+ from __future__ import annotations
23
+
22
24
  # Repo-root import bootstrap so ``from gaia.store.writer import ...`` resolves
23
25
  # regardless of cwd (the CLI is launched from many places).
24
26
  import sys as _sys
package/bin/cli/update.py CHANGED
@@ -256,7 +256,7 @@ def _run_verification(claude_dir: Path) -> dict:
256
256
  issues.append(f"project-context DB read error: {exc}")
257
257
 
258
258
  # 4. Config files
259
- config_files = ["git_standards.json", "surface-routing.json"]
259
+ config_files = ["surface-routing.json"]
260
260
  for cfg in config_files:
261
261
  path = claude_dir / "config" / cfg
262
262
  ok = path.exists()
@@ -324,12 +324,9 @@ class PrePublishValidator {
324
324
  'hooks',
325
325
  'agents',
326
326
  'skills',
327
- 'commands',
328
327
  'config',
329
- 'templates',
330
328
  'tools',
331
329
  'dist',
332
- 'git-hooks',
333
330
  ];
334
331
 
335
332
  const missing = [];
@@ -385,6 +382,52 @@ class PrePublishValidator {
385
382
  this.log('✓ All critical directories covered by package.json files', 'success');
386
383
  }
387
384
 
385
+ /**
386
+ * Schema-drift guard.
387
+ *
388
+ * Fails the publish if gaia/store/schema.sql changed without a corresponding
389
+ * EXPECTED_SCHEMA_VERSION bump + migration file. Without this guard, schema
390
+ * drift is only caught at runtime by `gaia doctor` (as a warning) -- here we
391
+ * make it a hard, non-zero build failure.
392
+ *
393
+ * The actual logic lives in scripts/check_schema_drift.py (fingerprint of
394
+ * schema.sql pinned to the version in scripts/migrations/schema.checksum).
395
+ * This method just invokes it and surfaces a non-zero exit as a failure.
396
+ */
397
+ validateSchemaDrift() {
398
+ this.log('Step 5c: Schema-drift guard (schema.sql vs EXPECTED_SCHEMA_VERSION)...', 'info');
399
+
400
+ const guardPath = path.join(GAIA_OPS_ROOT, 'scripts', 'check_schema_drift.py');
401
+ if (!fs.existsSync(guardPath)) {
402
+ this.log(`✗ schema-drift guard missing: ${guardPath}`, 'error');
403
+ throw new Error('scripts/check_schema_drift.py not found -- cannot verify schema drift');
404
+ }
405
+
406
+ const pyCmd = findPython();
407
+ if (!pyCmd) {
408
+ // Python is required for the runtime; a publish that cannot run the
409
+ // guard cannot prove the schema is consistent, so fail rather than skip.
410
+ this.log('✗ Python not available -- cannot run schema-drift guard', 'error');
411
+ throw new Error('Python interpreter not found; schema-drift guard cannot run');
412
+ }
413
+
414
+ try {
415
+ const out = this.execute(`${pyCmd} "${guardPath}"`, GAIA_OPS_ROOT, true);
416
+ out.trim().split('\n').filter(Boolean).forEach(line => this.log(` ${line}`, 'info'));
417
+ this.log('✓ No schema drift detected', 'success');
418
+ } catch (error) {
419
+ // execSync throws on non-zero exit; surface the guard's own stderr/stdout.
420
+ const detail = (error.stderr || error.stdout || error.message || '').toString().trim();
421
+ this.log('✗ Schema-drift guard failed:', 'error');
422
+ if (detail) detail.split('\n').forEach(line => console.error(` ${line}`));
423
+ throw new Error(
424
+ 'Schema drift detected: gaia/store/schema.sql changed without a ' +
425
+ 'version bump + migration. Bump EXPECTED_SCHEMA_VERSION in ' +
426
+ 'bin/cli/doctor.py and add the migration, then re-run the guard.'
427
+ );
428
+ }
429
+ }
430
+
388
431
  validatePluginManifest() {
389
432
  this.log('Step 6: Validating plugin manifest (.claude-plugin/plugin.json)...', 'info');
390
433
 
@@ -440,8 +483,7 @@ class PrePublishValidator {
440
483
  // Test 1: Validate JSON files
441
484
  this.log('Test 1: Validating JSON configuration files...', 'info');
442
485
  const jsonFiles = [
443
- 'config/clarification_rules.json',
444
- 'config/git_standards.json'
486
+ 'config/clarification_rules.json'
445
487
  ];
446
488
 
447
489
  jsonFiles.forEach(file => {
@@ -654,6 +696,7 @@ class PrePublishValidator {
654
696
  this.validateFiles();
655
697
  }
656
698
  this.validateFilesCoverage();
699
+ this.validateSchemaDrift();
657
700
  this.validatePluginManifest();
658
701
  this.runTests();
659
702
 
package/config/README.md CHANGED
@@ -1,68 +1,46 @@
1
1
  # Config
2
2
 
3
- Configuration lives here, separate from hooks, because these are data files — not code. Hooks are Python scripts that run at runtime; config files are JSON documents that those scripts read to make decisions. Keeping them apart means you can audit and change system behavior (which agents see which context sections, what git commit patterns are allowed, which surfaces route where) without touching executable code. It also makes the config files version-controllable and reviewable on their own terms.
3
+ Configuration lives here, separate from hooks, because these are data files — not code. Hooks are Python scripts that run at runtime; config files are JSON documents that those scripts read to make decisions. Keeping them apart means you can audit and change system behavior (what git commit patterns are allowed, which surfaces route where) without touching executable code. It also makes the config files version-controllable and reviewable on their own terms.
4
4
 
5
- `context-contracts.json` is the seeding source for agent contracts. During `gaia install`, its contents are loaded into the `project_context_contracts` and `agent_contract_permissions` tables in `~/.gaia/gaia.db`. At runtime, the DB is the SSOTthe hook layer reads contracts from the DB, not from this file. Editing `context-contracts.json` without re-running `gaia install` (or manually applying the SQL) has no effect. The cloud extension files in `cloud/` extend these contracts for cloud-specific sections without modifying the base file, so adding a new cloud provider is a new file, not an edit to the core.
5
+ The routing table is consumed at a well-defined point in the request lifecycle: on every user prompt. It is not loaded eagerly at startupit is parsed exactly when `surface_router.py` is invoked. (Git commit standards used to live here too, in `git_standards.json`; they are now inlined as module-level constants in `hooks/modules/validation/commit_validator.py` the single runtime consumer of those rules so this folder no longer owns them.)
6
6
 
7
- The other files — routing and git standards — are each consumed by a specific module and do exactly what their names say. There is no magic here: the files are loaded, parsed, and applied by the module that reads them.
7
+ ## When activated
8
8
 
9
- ## Cuándo se activa
10
-
11
- This component does not activate as a runtime process. Each file is read on-demand by the module that needs it. The table below shows the read point for each file.
12
-
13
- **Cuándo se lee cada archivo:**
14
-
15
- | File | Read by | When |
16
- |------|---------|------|
17
- | `surface-routing.json` | `hooks/user_prompt_submit.py` | Every prompt — determines routing recommendation injected into orchestrator context |
18
- | `context-contracts.json` | `gaia install` / `gaia update` | One-time at install; populates `~/.gaia/gaia.db` tables. Runtime reads come from DB. |
19
- | `git_standards.json` | `hooks/modules/validation/commit_validator.py` | Every `git commit` call intercepted by PreToolUse |
20
- | `cloud/gcp.json` | `tools/context/context_provider.py` | Agent dispatch when `cloud_provider = gcp` in workspace DB record |
21
- | `cloud/aws.json` | `tools/context/context_provider.py` | Agent dispatch when `cloud_provider = aws` in workspace DB record |
22
-
23
- **Base + cloud merge flow:**
9
+ This folder has no single activation event. Each file is read on-demand by the module that owns it.
24
10
 
25
11
  ```
26
- Agent dispatch triggered
12
+ User submits a prompt
27
13
  |
28
- hooks/modules/context/contracts_loader.py reads project_context_contracts from DB
14
+ hooks/user_prompt_submit.py fires
29
15
  |
30
- Detects cloud_provider from workspace record in ~/.gaia/gaia.db
16
+ tools/context/surface_router.py calls load_surface_routing_config()
31
17
  |
32
- Reads cloud/{provider}.json <- cloud extensions (still file-based)
18
+ Reads config/surface-routing.json
33
19
  |
34
- Merges: extends read/write lists per agent (no duplicates)
35
- |
36
- Result: complete contract for this agent on this cloud
37
- |
38
- Agent receives filtered project-context sections
20
+ Returns surface match + recommended agent injected into orchestrator context
39
21
  ```
40
22
 
41
- ## Qué hay aquí
23
+ If `surface-routing.json` is absent, `load_surface_routing_config()` returns a degraded config (`"version": "missing"`) and routing falls back to the `reconnaissance_agent` default.
24
+
25
+ ## What's here
42
26
 
43
27
  ```
44
28
  config/
45
- ├── context-contracts.json # Seeding source for per-agent read/write contracts (applied on install to gaia.db)
46
- ├── surface-routing.json # Intent classification and agent routing signals
47
- ├── git_standards.json # Commit type allowlist, footer rules, Conventional Commits config
48
- ├── cloud/
49
- │ ├── gcp.json # GCP-specific context sections (extends base contracts)
50
- │ └── aws.json # AWS-specific context sections (extends base contracts)
29
+ ├── surface-routing.json # Surface→agent routing table; consumed by tools/context/surface_router.py on every prompt
51
30
  └── README.md
52
31
  ```
53
32
 
54
- ## Convenciones
33
+ ## Conventions
55
34
 
56
- **context-contracts.json schema:** Each entry is keyed by agent name. Each agent has `read` (list of project-context section names the agent receives) and `write` (list of sections the agent can update via an `update_contracts` clause). `core_sections` is a top-level list of sections injected into every agent regardless of per-agent config. This schema is mirrored in the DB tables `project_context_contracts` (one row per agent) and `agent_contract_permissions` (permission grants).
35
+ **surface-routing.json schema:** Top-level keys are `version`, `reconnaissance_agent`, and `surfaces`. Each surface entry has `intent` (human description), `primary_agent` (agent id to dispatch), `adjacent_surfaces` (list of related surface names), `contract_sections` (context sections injected for that surface), `required_checks` (agent checklist), and `signals` with `keywords`, `commands`, and `artifacts` sub-lists. `surface_router.py` scores surfaces by matching task text against all signal lists; the highest-scoring surface wins.
57
36
 
58
- **Adding a new cloud:** Create `cloud/azure.json` following the same schema as `cloud/gcp.json`. Define agent-specific sections for that cloud. No code changes needed `context_provider.py` detects the file automatically by matching `cloud_provider` from project-context.
37
+ **Git commit standards (no longer in this folder):** The Conventional Commits rules — allowed types, subject/body length limits, subject rules — are inlined as module-level constants (`TYPE_ALLOWED`, `SUBJECT_MAX_LENGTH`, `SUBJECT_RULES`, `BODY_MAX_LINE_LENGTH`, `ENFORCEMENT`) in `hooks/modules/validation/commit_validator.py`. Forbidden-footer detection lives, hardcoded, in `bash_validator`. To add a new commit type, edit `TYPE_ALLOWED` in `commit_validator.py`.
59
38
 
60
- **surface-routing.json format:** Each surface entry has `intent`, `primary_agent`, `adjacent_surfaces`, and `signals` (with `high` and `medium` confidence keyword lists). High-confidence signals are checked first; medium signals act as tie-breakers.
39
+ **Adding a new surface:** Add an entry to `surfaces` in `surface-routing.json` with all required sub-keys. Update L1 routing tests and the `gaia_system` signals if the new surface involves Gaia components.
61
40
 
62
- ## Ver también
41
+ ## See also
63
42
 
64
- - [`~/.gaia/gaia.db`](../gaia/store/schema.sql) — `project_context_contracts` + `agent_contract_permissions` tables (runtime SSOT for contracts)
65
- - [`hooks/user_prompt_submit.py`](../hooks/user_prompt_submit.py) — reads `surface-routing.json` on every prompt
66
- - [`hooks/modules/validation/`](../hooks/modules/validation/) — reads `git_standards.json` on commit validation
67
- - [`tools/context/`](../tools/context/) — reads contracts (from DB) at agent dispatch time
68
- - [`agents/README.md`](../agents/README.md) — agent names that must match context-contracts.json keys
43
+ - [`tools/context/surface_router.py`](../tools/context/surface_router.py) — loads and scores `surface-routing.json`; the routing pillar source of truth
44
+ - [`hooks/user_prompt_submit.py`](../hooks/user_prompt_submit.py) — calls `classify_surfaces` from `surface_router` on every prompt
45
+ - [`hooks/modules/validation/commit_validator.py`](../hooks/modules/validation/commit_validator.py) — enforces Conventional Commits on every `git commit`; standards inlined as module-level constants
46
+ - [`skills/git-conventions/`](../skills/git-conventions/) — the agent-facing skill teaching the commit conventions
@@ -17,7 +17,6 @@
17
17
  "infrastructure",
18
18
  "orchestration",
19
19
  "cluster_details",
20
- "monitoring_observability",
21
20
  "application_services",
22
21
  "infrastructure_topology",
23
22
  "architecture_overview"
@@ -364,7 +363,6 @@
364
363
  "hooks/",
365
364
  "skills/",
366
365
  "agents/",
367
- "templates/",
368
366
  "claude.md",
369
367
  "project-context.json"
370
368
  ]
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gaia-ops",
3
- "version": "5.0.2",
3
+ "version": "5.0.5",
4
4
  "description": "Full DevOps orchestration for Claude Code. Eight specialized agents handle the complete development lifecycle \u2014 analysis, planning, execution, and deployment. Gaia-Ops scans your codebase to understand it and injects the right context into each sub-agent. Every command is classified by risk: read-only runs freely, state changes pause for your approval, and irreversible operations are permanently blocked.",
5
5
  "author": {
6
6
  "name": "jaguilar87",