@jaguilar87/gaia 5.0.8 → 5.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +11 -0
  4. package/bin/README.md +6 -1
  5. package/bin/cli/approvals.py +341 -238
  6. package/bin/cli/brief.py +13 -0
  7. package/bin/cli/doctor.py +1 -1
  8. package/dist/gaia-ops/.claude-plugin/plugin.json +1 -1
  9. package/dist/gaia-ops/hooks/adapters/claude_code.py +19 -85
  10. package/dist/gaia-ops/hooks/modules/context/context_injector.py +23 -7
  11. package/dist/gaia-ops/hooks/modules/events/event_writer.py +63 -96
  12. package/dist/gaia-ops/hooks/modules/security/__init__.py +0 -2
  13. package/dist/gaia-ops/hooks/modules/security/approval_cleanup.py +238 -69
  14. package/dist/gaia-ops/hooks/modules/security/approval_grants.py +506 -1103
  15. package/dist/gaia-ops/hooks/modules/security/mutative_verbs.py +24 -1
  16. package/dist/gaia-ops/hooks/modules/session/pending_scanner.py +150 -90
  17. package/dist/gaia-ops/hooks/modules/session/session_manifest.py +257 -28
  18. package/dist/gaia-ops/hooks/post_compact.py +1 -0
  19. package/dist/gaia-ops/hooks/pre_compact.py +1 -0
  20. package/dist/gaia-ops/hooks/user_prompt_submit.py +20 -0
  21. package/dist/gaia-ops/skills/agent-approval-protocol/SKILL.md +27 -7
  22. package/dist/gaia-ops/skills/agent-approval-protocol/reference.md +11 -6
  23. package/dist/gaia-ops/skills/gaia-patterns/reference.md +2 -2
  24. package/dist/gaia-ops/skills/orchestrator-present-approval/SKILL.md +69 -28
  25. package/dist/gaia-ops/skills/orchestrator-present-approval/reference.md +16 -3
  26. package/dist/gaia-ops/skills/orchestrator-present-approval/template.md +10 -5
  27. package/dist/gaia-ops/skills/pending-approvals/SKILL.md +16 -11
  28. package/dist/gaia-ops/skills/subagent-request-approval/SKILL.md +20 -6
  29. package/dist/gaia-ops/skills/subagent-request-approval/reference.md +23 -15
  30. package/dist/gaia-ops/tools/migration/README.md +10 -12
  31. package/dist/gaia-ops/tools/scan/orchestrator.py +194 -10
  32. package/dist/gaia-ops/tools/scan/tests/test_integration.py +1 -2
  33. package/dist/gaia-security/.claude-plugin/plugin.json +1 -1
  34. package/dist/gaia-security/hooks/adapters/claude_code.py +19 -85
  35. package/dist/gaia-security/hooks/modules/context/context_injector.py +23 -7
  36. package/dist/gaia-security/hooks/modules/events/event_writer.py +63 -96
  37. package/dist/gaia-security/hooks/modules/security/__init__.py +0 -2
  38. package/dist/gaia-security/hooks/modules/security/approval_cleanup.py +238 -69
  39. package/dist/gaia-security/hooks/modules/security/approval_grants.py +506 -1103
  40. package/dist/gaia-security/hooks/modules/security/mutative_verbs.py +24 -1
  41. package/dist/gaia-security/hooks/modules/session/pending_scanner.py +150 -90
  42. package/dist/gaia-security/hooks/modules/session/session_manifest.py +257 -28
  43. package/dist/gaia-security/hooks/user_prompt_submit.py +20 -0
  44. package/gaia/approvals/store.py +87 -9
  45. package/gaia/store/schema.sql +38 -1
  46. package/gaia/store/writer.py +400 -0
  47. package/hooks/adapters/claude_code.py +19 -85
  48. package/hooks/elicitation_result.py +20 -75
  49. package/hooks/modules/context/context_injector.py +23 -7
  50. package/hooks/modules/events/event_writer.py +63 -96
  51. package/hooks/modules/security/__init__.py +0 -2
  52. package/hooks/modules/security/approval_cleanup.py +238 -69
  53. package/hooks/modules/security/approval_grants.py +506 -1103
  54. package/hooks/modules/security/mutative_verbs.py +24 -1
  55. package/hooks/modules/session/pending_scanner.py +150 -90
  56. package/hooks/modules/session/session_manifest.py +257 -28
  57. package/hooks/post_compact.py +1 -0
  58. package/hooks/pre_compact.py +1 -0
  59. package/hooks/user_prompt_submit.py +20 -0
  60. package/package.json +1 -1
  61. package/pyproject.toml +1 -1
  62. package/scripts/bootstrap_database.sh +66 -17
  63. package/scripts/migrations/README.md +26 -14
  64. package/scripts/migrations/schema.checksum +2 -2
  65. package/scripts/migrations/v18_to_v19.sql +36 -0
  66. package/scripts/migrations/v19_to_v20.sql +20 -0
  67. package/skills/agent-approval-protocol/SKILL.md +27 -7
  68. package/skills/agent-approval-protocol/reference.md +11 -6
  69. package/skills/gaia-patterns/reference.md +2 -2
  70. package/skills/orchestrator-present-approval/SKILL.md +69 -28
  71. package/skills/orchestrator-present-approval/reference.md +16 -3
  72. package/skills/orchestrator-present-approval/template.md +10 -5
  73. package/skills/pending-approvals/SKILL.md +16 -11
  74. package/skills/subagent-request-approval/SKILL.md +20 -6
  75. package/skills/subagent-request-approval/reference.md +23 -15
  76. package/tools/migration/README.md +10 -12
  77. package/tools/scan/orchestrator.py +194 -10
  78. package/tools/scan/tests/test_integration.py +1 -2
  79. package/bin/cli/plans.py +0 -517
  80. package/dist/gaia-ops/tools/context/deep_merge.py +0 -159
  81. package/dist/gaia-ops/tools/migration/migrate_04_harness_events.py +0 -132
  82. package/dist/gaia-ops/tools/migration/migrate_04_harness_events.sh +0 -23
  83. package/dist/gaia-ops/tools/scan/merge.py +0 -213
  84. package/dist/gaia-ops/tools/scan/tests/test_merge.py +0 -269
  85. package/tools/context/deep_merge.py +0 -159
  86. package/tools/migration/migrate_04_harness_events.py +0 -132
  87. package/tools/migration/migrate_04_harness_events.sh +0 -23
  88. package/tools/scan/merge.py +0 -213
  89. package/tools/scan/tests/test_merge.py +0 -269
@@ -1,269 +0,0 @@
1
- """
2
- Unit tests for context combining logic (T026).
3
-
4
- Tests scanner-owned section replacement, agent-enriched section preservation,
5
- mixed section sub-key merge, unknown section preservation, v1-to-v2 upgrade,
6
- and idempotency.
7
- """
8
-
9
- import copy
10
- import json
11
- from pathlib import Path
12
- from typing import Any, Dict
13
-
14
- import pytest
15
-
16
- from tools.scan.merge import merge_context
17
-
18
-
19
- # ---------------------------------------------------------------------------
20
- # Helpers
21
- # ---------------------------------------------------------------------------
22
-
23
-
24
- def _section_owners() -> Dict[str, str]:
25
- """Return a realistic section_owners map from ScannerRegistry."""
26
- return {
27
- "project_identity": "stack",
28
- "stack": "stack",
29
- "git": "git",
30
- "infrastructure": "infrastructure",
31
- "orchestration": "orchestration",
32
- "environment.tools": "tools",
33
- "environment.tool_preferences": "tools",
34
- "environment.runtimes": "environment",
35
- "environment.os": "environment",
36
- "environment.env_files": "environment",
37
- }
38
-
39
-
40
- # ---------------------------------------------------------------------------
41
- # Test data helpers
42
- # ---------------------------------------------------------------------------
43
-
44
-
45
- def _make_existing_context() -> Dict[str, Any]:
46
- """Create a realistic existing project-context with scanner + agent data."""
47
- return {
48
- "metadata": {
49
- "version": "2.0",
50
- "last_updated": "2026-01-01T00:00:00Z",
51
- "project_name": "test-project",
52
- "scan_config": {
53
- "staleness_hours": 24,
54
- "last_scan": "2026-01-01T00:00:00Z",
55
- "scanner_version": "0.1.0",
56
- },
57
- },
58
- "project_identity": {
59
- "_source": "scanner:stack",
60
- "name": "old-name",
61
- "type": "application",
62
- },
63
- "stack": {
64
- "_source": "scanner:stack",
65
- "languages": [{"name": "python", "manifest": "pyproject.toml", "primary": True}],
66
- "frameworks": [],
67
- "build_tools": [],
68
- },
69
- "git": {
70
- "_source": "scanner:git",
71
- "platform": "github",
72
- "remotes": [],
73
- "default_branch": "main",
74
- },
75
- "environment": {
76
- "_source": "scanner:environment",
77
- "os": {"platform": "linux", "architecture": "x64"},
78
- "runtimes": [{"name": "python3", "version": "3.11.0"}],
79
- "env_files": [],
80
- "tools": [{"name": "git", "path": "/usr/bin/git"}],
81
- "tool_preferences": {"file_viewer": "bat"},
82
- },
83
- "operational_guidelines": {
84
- "_source": "agent:developer",
85
- "deployment_strategy": "blue-green",
86
- "rollback_procedure": "manual",
87
- },
88
- "my_custom_notes": {
89
- "author": "user",
90
- "notes": "User-maintained section",
91
- },
92
- }
93
-
94
-
95
- def _make_scan_results() -> Dict[str, Any]:
96
- """Create scan results from a new scan run."""
97
- return {
98
- "project_identity": {
99
- "_source": "scanner:stack",
100
- "name": "new-name",
101
- "type": "monorepo",
102
- "description": "Updated description",
103
- },
104
- "stack": {
105
- "_source": "scanner:stack",
106
- "languages": [
107
- {"name": "typescript", "manifest": "package.json", "primary": True},
108
- {"name": "python", "manifest": "pyproject.toml", "primary": False},
109
- ],
110
- "frameworks": [{"name": "react", "language": "typescript"}],
111
- "build_tools": [{"name": "npm", "detected_by": "lock_file"}],
112
- },
113
- "git": {
114
- "_source": "scanner:git",
115
- "platform": "github",
116
- "remotes": [{"name": "origin", "url": "git@github.com:o/r.git"}],
117
- "default_branch": "main",
118
- },
119
- "environment": {
120
- "_source": "scanner:environment",
121
- "os": {"platform": "linux", "architecture": "x64", "wsl": True},
122
- "runtimes": [{"name": "python3", "version": "3.12.0"}],
123
- "env_files": [{"name": ".env", "path": ".env"}],
124
- },
125
- }
126
-
127
-
128
- # ---------------------------------------------------------------------------
129
- # Rule 1: Scanner-owned section fully replaced
130
- # ---------------------------------------------------------------------------
131
-
132
-
133
- class TestScannerOwnedSectionReplacement:
134
- """Test that scanner-owned sections are fully replaced with new data."""
135
-
136
- def test_project_identity_replaced(self) -> None:
137
- existing = _make_existing_context()
138
- scan = _make_scan_results()
139
- result = merge_context(existing, scan, _section_owners())
140
- assert result["project_identity"]["name"] == "new-name"
141
- assert result["project_identity"]["type"] == "monorepo"
142
-
143
- def test_stack_section_replaced(self) -> None:
144
- existing = _make_existing_context()
145
- scan = _make_scan_results()
146
- result = merge_context(existing, scan, _section_owners())
147
- lang_names = [l["name"] for l in result["stack"]["languages"]]
148
- assert "typescript" in lang_names
149
- assert len(result["stack"]["frameworks"]) == 1
150
-
151
- def test_git_section_replaced(self) -> None:
152
- existing = _make_existing_context()
153
- scan = _make_scan_results()
154
- result = merge_context(existing, scan, _section_owners())
155
- assert len(result["git"]["remotes"]) == 1
156
-
157
-
158
- # ---------------------------------------------------------------------------
159
- # Rule 2: Agent-enriched sections preserved
160
- # ---------------------------------------------------------------------------
161
-
162
-
163
- class TestAgentEnrichedPreservation:
164
- """Test that agent-enriched sections are preserved byte-identical."""
165
-
166
- def test_operational_guidelines_preserved(self) -> None:
167
- existing = _make_existing_context()
168
- scan = _make_scan_results()
169
- result = merge_context(existing, scan, _section_owners())
170
- assert result["operational_guidelines"]["deployment_strategy"] == "blue-green"
171
- assert result["operational_guidelines"]["rollback_procedure"] == "manual"
172
-
173
-
174
- # ---------------------------------------------------------------------------
175
- # Rule 4: Mixed section (environment) sub-key merge
176
- # ---------------------------------------------------------------------------
177
-
178
-
179
- class TestMixedSectionMerge:
180
- """Test that mixed sections merge scanner fields and keep agent fields."""
181
-
182
- def test_environment_scanner_fields_refreshed(self) -> None:
183
- existing = _make_existing_context()
184
- scan = _make_scan_results()
185
- result = merge_context(existing, scan, _section_owners())
186
- # Scanner-owned sub-keys should be updated
187
- assert result["environment"]["os"].get("wsl") is True
188
- runtimes = result["environment"]["runtimes"]
189
- py = [r for r in runtimes if r["name"] == "python3"][0]
190
- assert py["version"] == "3.12.0"
191
-
192
- def test_environment_agent_fields_kept(self) -> None:
193
- existing = _make_existing_context()
194
- scan = _make_scan_results()
195
- result = merge_context(existing, scan, _section_owners())
196
- # tools and tool_preferences came from tool scanner (not in this scan)
197
- # They should be preserved from existing
198
- assert "tools" in result["environment"] or "tool_preferences" in result["environment"]
199
-
200
-
201
- # ---------------------------------------------------------------------------
202
- # Rule 5: Unknown/user-custom sections preserved
203
- # ---------------------------------------------------------------------------
204
-
205
-
206
- class TestUserCustomPreservation:
207
- """Test that user-custom sections survive combining."""
208
-
209
- def test_custom_section_preserved(self) -> None:
210
- existing = _make_existing_context()
211
- scan = _make_scan_results()
212
- result = merge_context(existing, scan, _section_owners())
213
- assert "my_custom_notes" in result
214
- assert result["my_custom_notes"]["author"] == "user"
215
-
216
-
217
- # ---------------------------------------------------------------------------
218
- # v1-to-v2 upgrade
219
- # ---------------------------------------------------------------------------
220
-
221
-
222
- class TestV1ToV2Upgrade:
223
- """Test upgrading from v1 project-context (no scan_config)."""
224
-
225
- def test_v1_context_upgraded(self, sample_project_context_v1: Dict[str, Any]) -> None:
226
- scan = _make_scan_results()
227
- result = merge_context(sample_project_context_v1, scan, _section_owners())
228
- # Should have scan_config after upgrade
229
- assert "metadata" in result
230
- # Agent-enriched data from v1 should be preserved
231
- if "operational_guidelines" in sample_project_context_v1:
232
- assert "operational_guidelines" in result
233
-
234
- def test_v1_agent_data_not_lost(self, sample_project_context_v1: Dict[str, Any]) -> None:
235
- scan = _make_scan_results()
236
- result = merge_context(sample_project_context_v1, scan, _section_owners())
237
- # User-custom sections from v1 are preserved as-is (Rule 4).
238
- # project_details is no longer produced by the scanner (no backward compat),
239
- # but if it existed in the v1 context it is preserved as a user-custom section.
240
- if "project_details" in sample_project_context_v1:
241
- assert "project_details" in result
242
-
243
-
244
- # ---------------------------------------------------------------------------
245
- # Idempotency
246
- # ---------------------------------------------------------------------------
247
-
248
-
249
- class TestIdempotency:
250
- """Test that running combine twice produces same result (except timestamps)."""
251
-
252
- def test_idempotent_combine(self) -> None:
253
- existing = _make_existing_context()
254
- scan = _make_scan_results()
255
-
256
- result1 = merge_context(existing, scan, _section_owners())
257
- result2 = merge_context(result1, scan, _section_owners())
258
-
259
- # Strip timestamps for comparison
260
- def strip_timestamps(d: Dict) -> Dict:
261
- d = copy.deepcopy(d)
262
- if "metadata" in d:
263
- meta = d["metadata"]
264
- meta.pop("last_updated", None)
265
- if "scan_config" in meta:
266
- meta["scan_config"].pop("last_scan", None)
267
- return d
268
-
269
- assert strip_timestamps(result1) == strip_timestamps(result2)