Skip to content

Commit eb61d77

Browse files
authored
Merge pull request #57 from chiruu12/fix/framework-hardening
Framework hardening and stabilization (phases 0-6)
2 parents 3bd0ded + 550386a commit eb61d77

195 files changed

Lines changed: 25087 additions & 1971 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 93 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ on:
55
branches: [main, dev]
66
pull_request:
77
branches: [main, dev]
8+
schedule:
9+
# Weekly soak: wake-source leak stress (Phase 6 slice 6.3)
10+
- cron: "0 6 * * 0"
811

912
concurrency:
1013
group: ${{ github.workflow }}-${{ github.ref }}
@@ -13,6 +16,7 @@ concurrency:
1316
jobs:
1417
lint:
1518
name: Lint & Type Check
19+
if: github.event_name != 'schedule'
1620
runs-on: ubuntu-latest
1721
steps:
1822
- uses: actions/checkout@v4
@@ -26,7 +30,6 @@ jobs:
2630
python-version: "3.12"
2731

2832
- name: Install dependencies
29-
# The dev dependency-group is installed by `uv sync` automatically.
3033
run: uv sync --extra api
3134

3235
- name: Ruff lint
@@ -38,8 +41,30 @@ jobs:
3841
- name: Mypy
3942
run: uv run mypy src/
4043

41-
test:
42-
name: Test (Python ${{ matrix.python-version }})
44+
test-fast:
45+
name: Test (fast — PR gate)
46+
if: github.event_name == 'pull_request'
47+
runs-on: ubuntu-latest
48+
steps:
49+
- uses: actions/checkout@v4
50+
51+
- uses: astral-sh/setup-uv@v4
52+
with:
53+
version: "latest"
54+
55+
- uses: actions/setup-python@v5
56+
with:
57+
python-version: "3.12"
58+
59+
- name: Install dependencies
60+
run: uv sync --extra api
61+
62+
- name: Run tests (excluding adversarial)
63+
run: uv run pytest tests/ -v --ignore=tests/adversarial/ -x
64+
65+
test-full:
66+
name: Test (full — merge gate, Python ${{ matrix.python-version }})
67+
if: github.event_name == 'push'
4368
runs-on: ubuntu-latest
4469
strategy:
4570
fail-fast: false
@@ -57,7 +82,6 @@ jobs:
5782
python-version: ${{ matrix.python-version }}
5883

5984
- name: Install dependencies
60-
# The dev dependency-group is installed by `uv sync` automatically.
6185
run: uv sync --extra api
6286

6387
- name: Run tests
@@ -68,10 +92,32 @@ jobs:
6892
# Coverage gating on one matrix leg only; ratchet the floor up as
6993
# coverage rises -- never lower it.
7094
if: matrix.python-version == '3.12'
71-
run: uv run pytest tests/ -v --cov=hive --cov-report=term --cov-fail-under=73
95+
run: uv run pytest tests/ -v --cov=hive --cov-report=term --cov-fail-under=77
96+
97+
resilience:
98+
name: Adversarial / resilience gate
99+
if: github.event_name != 'schedule'
100+
runs-on: ubuntu-latest
101+
steps:
102+
- uses: actions/checkout@v4
103+
104+
- uses: astral-sh/setup-uv@v4
105+
with:
106+
version: "latest"
107+
108+
- uses: actions/setup-python@v5
109+
with:
110+
python-version: "3.12"
111+
112+
- name: Install dependencies
113+
run: uv sync --extra api
114+
115+
- name: Adversarial / resilience gate
116+
run: uv run pytest tests/adversarial/ -v --tb=short
72117

73118
docs:
74119
name: Docs build
120+
if: github.event_name != 'schedule'
75121
runs-on: ubuntu-latest
76122
steps:
77123
- uses: actions/checkout@v4
@@ -86,8 +132,9 @@ jobs:
86132
- name: Build docs (strict)
87133
run: uv run mkdocs build --strict
88134

89-
build:
90-
name: Build wheel
135+
build-clean:
136+
name: Build (clean checkout)
137+
if: github.event_name == 'push'
91138
runs-on: ubuntu-latest
92139
steps:
93140
- uses: actions/checkout@v4
@@ -96,11 +143,48 @@ jobs:
96143
with:
97144
version: "latest"
98145

99-
- name: Build
100-
run: uv build
146+
- uses: actions/setup-python@v5
147+
with:
148+
python-version: "3.12"
149+
150+
- name: Build from git archive
151+
run: |
152+
rm -rf /tmp/hive-ci-build
153+
mkdir -p /tmp/hive-ci-build
154+
git archive HEAD | tar -x -C /tmp/hive-ci-build
155+
cd /tmp/hive-ci-build && uv build
101156
102157
- name: Verify wheel contents
103158
run: |
159+
cd /tmp/hive-ci-build
104160
python3 -m zipfile -l dist/*.whl | grep -q "hive/models.yaml"
105161
python3 -m zipfile -l dist/*.whl | grep -q "hive/profiles/"
106162
echo "Wheel contents verified"
163+
164+
soak:
165+
name: Scheduled soak
166+
if: github.event_name == 'schedule'
167+
runs-on: ubuntu-latest
168+
steps:
169+
- uses: actions/checkout@v4
170+
171+
- uses: astral-sh/setup-uv@v4
172+
with:
173+
version: "latest"
174+
175+
- uses: actions/setup-python@v5
176+
with:
177+
python-version: "3.12"
178+
179+
- name: Install dependencies
180+
run: uv sync --extra api
181+
182+
- name: Wake-source stress (50x)
183+
run: |
184+
for i in $(seq 1 50); do
185+
echo "=== iteration $i/50 ==="
186+
uv run pytest tests/adversarial/test_resource_exhaustion.py::TestWakeSourceCleanup::test_wake_source_does_not_leak_tasks -q --tb=short
187+
done
188+
189+
- name: Full suite (ordering sensitivity)
190+
run: uv run pytest tests/ -v --tb=short

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,3 +233,9 @@ launch/
233233
poker-engine/
234234
hive-arena/
235235
hum/
236+
237+
# macOS
238+
.DS_Store
239+
240+
# Local agent-tool state
241+
.commandcode/

ARCHITECTURE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ src/hive/
1717
│ ├── profile.py # AgentProfile — YAML-driven configuration
1818
│ ├── state.py # AgentState, AgentStatus enum
1919
│ ├── existence.py # ExistenceLoop — autonomous goal generation
20-
│ ├── goal_strategy.py # GoalStrategy protocol, GoalContext, Goal
20+
│ ├── goal_strategy.py # GoalStrategy protocol, GoalContext, GeneratedGoal
2121
│ ├── suffering.py # SufferingState, StressorRegistry, stressor types
2222
│ ├── identity.py # IdentityManager — narrative and opinions
2323
│ ├── delegation.py # DelegationEngine — inter-agent task routing

CLAUDE.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ src/hive/
105105
| Custom A2A pattern | Subclass `A2APattern`, `PatternRegistry.default().register(name, instance)` | `src/hive/interactions/registry.py` |
106106
| Custom goal strategy | Implement `GoalStrategy` protocol, pass to `HiveDaemon` | `src/hive/agents/goal_strategy.py` |
107107
| Daemon hooks | `daemon.hooks.on("event", callback)` | `src/hive/daemon/hooks.py` |
108+
| Phase guard | `PhaseGuard` protocol, `daemon.hooks.register_guard(phase, guard)` | `src/hive/daemon/gates.py` |
109+
| Wake source | `WakeSource` protocol, `daemon.add_wake_source(...)` | `src/hive/daemon/wakeup.py` |
110+
| Swarm policy | `SwarmPolicy` protocol, `HiveDaemon(swarm_policy=...)` | `src/hive/agents/swarm_policy.py` |
108111
| Agent profile | YAML in `profiles/` | `src/hive/agents/profile.py` |
109112
| Plugin toolkit | Drop in `.hive/plugins/` | `src/hive/runtime/plugin_loader.py` |
110113
| Custom STT provider | Implement `STTProvider` protocol | `src/hive/stt/base.py` |

EXTENDING.md

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -213,30 +213,29 @@ async def test_brainstorm_pattern(tmp_path):
213213
## 5. Custom Goal Strategy
214214

215215
Implement the `GoalStrategy` protocol to control how agents generate goals when idle.
216+
Every call must return a `GeneratedGoal` with `cost_usd` and `tokens` set from any LLM
217+
work, even when `objective` is `None`.
216218

217219
```python
218-
from hive import GoalStrategy, GoalContext, Goal
219-
from uuid import uuid4
220+
from hive import GoalStrategy, GoalContext, GeneratedGoal
221+
from pathlib import Path
220222

221223
class PrioritizedGoalStrategy:
222224
"""Generate goals based on suffering and nudges."""
223225

224-
async def generate_goal(self, context: GoalContext) -> Goal | None:
226+
async def generate_goal(self, context: GoalContext) -> GeneratedGoal:
225227
# Prioritize user nudges
226228
if context.nudges:
227-
return Goal(
228-
goal_id=f"goal-{uuid4().hex[:8]}",
229+
return GeneratedGoal(
229230
objective=f"Address user request: {context.nudges[0]}",
230-
reasoning="User nudge takes priority",
231231
)
232232
# High suffering → self-care
233233
if context.suffering.cumulative_load > 0.7:
234-
return Goal(
235-
goal_id=f"goal-{uuid4().hex[:8]}",
234+
return GeneratedGoal(
236235
objective="Focus on resolving active stressors",
237-
reasoning=f"Suffering load at {context.suffering.cumulative_load:.0%}",
238236
)
239-
return None # Fall through to default behavior
237+
# LLM call with no actionable goal — still report spend
238+
return GeneratedGoal(objective=None, cost_usd=0.0, tokens=0)
240239

241240
# Pass to daemon
242241
from hive import HiveDaemon
@@ -246,7 +245,7 @@ daemon = HiveDaemon(hive_dir=Path(".hive"), goal_strategy=PrioritizedGoalStrateg
246245
```python
247246
# Test
248247
import pytest
249-
from hive import GoalContext, GoalStrategy, SufferingState
248+
from hive import GoalContext, GeneratedGoal, SufferingState
250249
from hive.agents.profile import AgentProfile
251250

252251
@pytest.mark.asyncio
@@ -261,9 +260,9 @@ async def test_prioritized_strategy():
261260
nudges=["Please write docs"],
262261
recent_goals=[],
263262
)
264-
goal = await strategy.generate_goal(ctx)
265-
assert goal is not None
266-
assert "write docs" in goal.objective.lower()
263+
result = await strategy.generate_goal(ctx)
264+
assert result.objective is not None
265+
assert "write docs" in result.objective.lower()
267266
```
268267

269268
## 6. Daemon Hooks
@@ -357,7 +356,18 @@ def test_profile_loads():
357356
358357
## 8. Plugin System
359358
360-
Drop a Python file containing a `Toolkit` subclass in `.hive/plugins/`. It's auto-discovered and loaded every 10 daemon cycles.
359+
Drop a Python file containing a `Toolkit` subclass in `.hive/plugins/`. Enable
360+
plugin loading in `.hive/config.yaml` first (disabled by default for security --
361+
plugins execute arbitrary Python with full process privileges, and agents can
362+
write new plugin files that hot-load every 10 daemon cycles):
363+
364+
```yaml
365+
plugins:
366+
enabled: true
367+
allowlist: [] # optional: restrict to named files/stems
368+
```
369+
370+
Once enabled, plugins are auto-discovered and loaded every 10 daemon cycles.
361371

362372
```python
363373
# .hive/plugins/calculator.py

docs/contributing.md

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,30 @@ git checkout -b ci/improvement # CI/CD changes
1313

1414
Open a PR to `main`, wait for CI to pass, then merge.
1515

16+
## Merge gate (CI)
17+
18+
Every PR runs the **fast gate**; pushes to `main`/`dev` also run the **full merge gate**. See [framework stabilization index](plans/framework-stabilization-index.md) for measured baselines.
19+
20+
### PR fast gate (required)
21+
22+
```bash
23+
uv run ruff check src/ tests/
24+
uv run ruff format --check src/ tests/
25+
uv run mypy src/
26+
uv run pytest tests/ -v --ignore=tests/adversarial/
27+
uv run pytest tests/adversarial/ -v --tb=short
28+
uv run mkdocs build --strict
29+
```
30+
31+
### Merge gate (push to main / dev)
32+
33+
```bash
34+
uv run pytest tests/ -v --cov=hive --cov-report=term --cov-fail-under=77
35+
git archive HEAD | tar -x -C /tmp/hive-ci-build && cd /tmp/hive-ci-build && uv build
36+
```
37+
38+
A weekly scheduled **soak** job repeats the wake-source leak test 50× and runs the full suite for ordering sensitivity.
39+
1640
## Development Setup
1741

1842
```bash
@@ -67,7 +91,6 @@ src/hive/
6791
## Pull Request Guidelines
6892

6993
- One logical change per PR
70-
- Tests must pass: `uv run pytest`
71-
- Lint must pass: `uv run ruff check src tests`
94+
- All [merge gate](#merge-gate-ci) commands must pass locally before opening a PR
7295
- Include a clear description of what changed and why
7396
- Reference any related issues

0 commit comments

Comments
 (0)