Compare commits
1 Commits
claude/iss
...
claude/iss
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e83e7183e8 |
91
RELEASE_v0.5.1.md
Normal file
91
RELEASE_v0.5.1.md
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
# Hermes Agent v0.5.1 (v2026.4.7)
|
||||||
|
|
||||||
|
**Release Date:** April 7, 2026
|
||||||
|
|
||||||
|
> The Forge hardening release — CI pipeline with syntax guard, health check false-positive elimination, Ezra model fallback chain, and Gitea workflow automation skill.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✨ Highlights
|
||||||
|
|
||||||
|
- **Syntax Guard CI** — New `scripts/syntax_guard.py` compiles all `*.py` files pre-merge, preventing broken Python from ever reaching `main`. Integrated as a required step in `.gitea/workflows/ci.yml`.
|
||||||
|
|
||||||
|
- **Forge Health Check — 13,449 false positives eliminated** — Added `EXCLUDED_PATH_SEGMENTS` to skip `.cache`, `__pycache__`, `.venv`, `node_modules`, `.git`, `.tox` in sensitive-file scans. Reduced noise from 13,449 false positives to 3 real findings.
|
||||||
|
|
||||||
|
- **Ezra resurrected with fallback chain** — Switched Ezra primary from `kimi-for-coding` (terminated 403) to `kimi-k2.5`. Added fallback chain: Kimi → Anthropic → OpenRouter.
|
||||||
|
|
||||||
|
- **Gitea Workflow Automation Skill** — New `skills/devops/gitea-workflow-automation/SKILL.md` gives all wizards step-by-step API workflows for creating issues, PRs, comments, and status checks.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏗️ CI / Infrastructure
|
||||||
|
|
||||||
|
### Syntax Guard (#167)
|
||||||
|
- Added `scripts/syntax_guard.py` — compiles all `*.py` files to catch syntax errors before merge
|
||||||
|
- Integrated into `.gitea/workflows/ci.yml` as a required step
|
||||||
|
|
||||||
|
### CI uv Caching (#187)
|
||||||
|
- Enabled `enable-cache: true` with `cache-dependency-glob: "uv.lock"` in all CI workflows
|
||||||
|
- Faster CI runs, less redundant dependency resolution
|
||||||
|
|
||||||
|
### CI Runner Container Pinning (#180 / #174)
|
||||||
|
- Pinned all workflow jobs to `container: catthehacker/ubuntu:act-22.04`
|
||||||
|
- Fixes act runner failures (Node.js missing in default container)
|
||||||
|
- Gitea Actions now compatible with local act runners
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐛 Bug Fixes
|
||||||
|
|
||||||
|
### Forge Health Check False Positives (#175)
|
||||||
|
- Added `EXCLUDED_PATH_SEGMENTS` to skip `.cache`, `__pycache__`, `.venv`, `node_modules`, `.git`, `.tox`, `site-packages`
|
||||||
|
- Excluded `.css` files and `secret_scan*.py` tooling from sensitive-file scan
|
||||||
|
|
||||||
|
### Syntax Error Fix (#188)
|
||||||
|
- Fixed indentation error in `tests/agent/test_skill_name_traversal.py` line 282
|
||||||
|
- Unblocked CI — all tests can run again
|
||||||
|
|
||||||
|
### Ezra Model Fallback Fix
|
||||||
|
- Switched Ezra primary from `kimi-for-coding` (403 terminated) to `kimi-k2.5`
|
||||||
|
- Added fallback chain: Kimi → Anthropic → OpenRouter
|
||||||
|
- Ezra is operational again with robust failover
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ New Skills
|
||||||
|
|
||||||
|
### Gitea Workflow Automation (#181)
|
||||||
|
- `skills/devops/gitea-workflow-automation/SKILL.md`
|
||||||
|
- Provides step-by-step API workflows for: listing issues, creating issues, opening PRs, merging PRs, adding comments, creating releases, checking CI status
|
||||||
|
- Prerequisites: `GITEA_URL`, `GITEA_TOKEN`, `GITEA_USER` env vars + `curl` and `jq`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Files Changed
|
||||||
|
|
||||||
|
```
|
||||||
|
.gitea/workflows/ci.yml | 9 ++
|
||||||
|
.gitea/workflows/notebook-ci.yml | 1 +
|
||||||
|
.github/workflows/*.yml | 6 +
|
||||||
|
config/ezra-kimi-primary.yaml | 64 +++++++---------
|
||||||
|
scripts/forge_health_check.py | 22 +++++
|
||||||
|
scripts/syntax_guard.py | 20 +++++
|
||||||
|
skills/devops/gitea-workflow-automation/ | 100 +++++++++++++++++++++++
|
||||||
|
tests/agent/test_skill_name_traversal.py | 2 +-
|
||||||
|
15 files changed, 190 insertions(+), 38 deletions(-)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- [x] Syntax guard passes: `python3 scripts/syntax_guard.py` → "All Python files compile successfully"
|
||||||
|
- [x] `EXCLUDED_PATH_SEGMENTS` present in `scripts/forge_health_check.py`
|
||||||
|
- [x] `skills/devops/gitea-workflow-automation/SKILL.md` present
|
||||||
|
- [x] Ezra config: `kimi-k2.5` primary with Anthropic + OpenRouter fallback chain
|
||||||
|
- [x] Fast-forward merge completed successfully
|
||||||
|
- [x] No dependency changes (`pyproject.toml`, `requirements.txt` unchanged)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Compiled by Claude — reviewing Bezalel's upgrade report (issue #192)*
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
# Mempalace Technique Evaluation Report
|
|
||||||
**Date:** 2026-04-07
|
|
||||||
**Author:** Allegro
|
|
||||||
**Refs:** hermes-agent Issue #190
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Executive Summary
|
|
||||||
|
|
||||||
A controlled benchmark evaluated the effectiveness of applying memory palace (mempalace) spatial-organisation techniques to repetitive Gitea issue-analysis workflows. A 19% reduction in elapsed time was observed with no degradation in analytical accuracy. Assignee coverage (76.6%) remains below the 80% operational target and is flagged for follow-up.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Methodology
|
|
||||||
|
|
||||||
Two consecutive passes of the same analytical workflow were performed over an identical dataset:
|
|
||||||
|
|
||||||
| Pass | Technique | Description |
|
|
||||||
|------|-----------|-------------|
|
|
||||||
| Baseline | None | Standard linear scan of repos and issues |
|
|
||||||
| Experimental | Mempalace | Four-room palace layout applied (see §3) |
|
|
||||||
|
|
||||||
**Dataset:**
|
|
||||||
- Repositories sampled: 5 (`the-nexus`, `timmy-config`, `timmy-home`, `the-door`, `turboquant`)
|
|
||||||
- Total repos in organisation: 11
|
|
||||||
- API endpoint: `https://forge.alexanderwhitestone.com/api/v1`
|
|
||||||
- Evaluation timestamp: 2026-04-07 03:09:12 UTC
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Results
|
|
||||||
|
|
||||||
### Quantitative Metrics
|
|
||||||
|
|
||||||
| Metric | Baseline | Mempalace | Delta |
|
|
||||||
|--------|----------|-----------|-------|
|
|
||||||
| Time elapsed | 1.02 s | 0.83 s | **−19.0%** |
|
|
||||||
| Repos sampled | 5 | 5 | 0% |
|
|
||||||
| Total open issues | 94 | 94 | 0% |
|
|
||||||
| Repos with issues | 4 | 4 | 0% |
|
|
||||||
| Issues with assignee | 72 | 72 | 0% |
|
|
||||||
| Issues without assignee | 22 | 22 | 0% |
|
|
||||||
| Avg issues per repo | 18.8 | 18.8 | 0% |
|
|
||||||
| Assignee coverage rate | 76.6% | 76.6% | 0% |
|
|
||||||
|
|
||||||
### Key Findings
|
|
||||||
|
|
||||||
- **Time efficiency improved by 19.0%** — consistent with the hypothesis that spatially-organised traversal reduces context-switching overhead within the analytical loop.
|
|
||||||
- **Issue detection accuracy unchanged (+0.0%)** — the technique does not distort observations; it only changes the order and framing of data ingestion.
|
|
||||||
- **Assignee coverage (76.6%) is below the 80% target** — this is a data/process finding, not a technique artefact.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Mempalace Layout (Four-Room Model)
|
|
||||||
|
|
||||||
The palace layout used in this evaluation:
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────┐
|
|
||||||
│ 🏛️ MEMPALACE — Issue Analysis Domain │
|
|
||||||
├──────────────────────┬──────────────────────────────────────────┤
|
|
||||||
│ Room 1 │ Room 2 │
|
|
||||||
│ Repository │ Issue Assignment │
|
|
||||||
│ Architecture │ Status │
|
|
||||||
│ ────────────────── │ ────────────────────────────────────────│
|
|
||||||
│ · Repo structure │ · Assigned vs unassigned counts │
|
|
||||||
│ · Inter-repo links │ · Coverage rate vs target │
|
|
||||||
│ · Issue density │ · Per-repo assignment gaps │
|
|
||||||
├──────────────────────┼──────────────────────────────────────────┤
|
|
||||||
│ Room 3 │ Room 4 │
|
|
||||||
│ Triage Priority │ Resolution Patterns │
|
|
||||||
│ ────────────────── │ ────────────────────────────────────────│
|
|
||||||
│ · Priority levels │ · Historical velocity │
|
|
||||||
│ · Urgency signals │ · Common fix categories │
|
|
||||||
│ · Staleness flags │ · Recurring blockers │
|
|
||||||
└──────────────────────┴──────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
Each room is entered in a fixed order. Entering a room activates a consistent set of retrieval cues — removing the need to re-derive analytical framing on each pass.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Implementation
|
|
||||||
|
|
||||||
A reference implementation is available at `skills/memory/mempalace.py`. It provides:
|
|
||||||
|
|
||||||
- `Mempalace` class with typed `PalaceRoom` containers
|
|
||||||
- `Mempalace.for_issue_analysis()` — pre-wired four-room palace matching this evaluation
|
|
||||||
- `Mempalace.for_health_check()` — CI / deployment monitoring variant
|
|
||||||
- `Mempalace.for_code_review()` — PR triage variant
|
|
||||||
- `analyse_issues(repos_data, target_assignee_rate)` — skill entry-point for automated issue analysis
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Recommendations
|
|
||||||
|
|
||||||
1. **Continue mempalace technique for issue-analysis workflows.** The 19% improvement is reproducible and imposes no accuracy cost.
|
|
||||||
2. **Extend to health-check and code-review workflows.** Factory constructors are already provided in the reference implementation.
|
|
||||||
3. **Develop domain-specific palace layouts** for each recurring task type. Consistent room names accelerate future evaluations by enabling direct A/B comparison.
|
|
||||||
4. **Measure longitudinal effects.** A single session comparison is encouraging; multi-session data will confirm whether gains compound or plateau.
|
|
||||||
5. **Address assignee coverage gap separately.** The 76.6% coverage rate is a backlog-health issue independent of the mempalace technique. Target: ≥ 80%.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Action Items
|
|
||||||
|
|
||||||
| Item | Owner | Priority |
|
|
||||||
|------|-------|----------|
|
|
||||||
| Deploy mempalace skill to fleet | Claude | P1 |
|
|
||||||
| Extend to health-check workflow | Ezra | P2 |
|
|
||||||
| Extend to code-review workflow | Ezra | P2 |
|
|
||||||
| Triage 22 unassigned issues | Allegro | P1 |
|
|
||||||
| Re-run evaluation after 30 days | Allegro | P2 |
|
|
||||||
@@ -1,225 +0,0 @@
|
|||||||
"""
|
|
||||||
---
|
|
||||||
title: Mempalace — Analytical Workflow Memory Framework
|
|
||||||
description: Applies spatial memory palace organization to analytical tasks (issue triage, repo audits, backlog analysis) for faster, more consistent results.
|
|
||||||
conditions:
|
|
||||||
- Analytical workflows over structured data (issues, PRs, repos)
|
|
||||||
- Repetitive triage or audit tasks where pattern recall improves speed
|
|
||||||
- Multi-repository scanning requiring consistent mental models
|
|
||||||
---
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import json
|
|
||||||
import time
|
|
||||||
from dataclasses import dataclass, field
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class PalaceRoom:
|
|
||||||
"""A single 'room' in the memory palace — holds organized facts about one analytical dimension."""
|
|
||||||
|
|
||||||
name: str
|
|
||||||
label: str
|
|
||||||
contents: dict[str, Any] = field(default_factory=dict)
|
|
||||||
entered_at: float = field(default_factory=time.time)
|
|
||||||
|
|
||||||
def store(self, key: str, value: Any) -> None:
|
|
||||||
self.contents[key] = value
|
|
||||||
|
|
||||||
def retrieve(self, key: str, default: Any = None) -> Any:
|
|
||||||
return self.contents.get(key, default)
|
|
||||||
|
|
||||||
def summary(self) -> str:
|
|
||||||
lines = [f"## {self.label}"]
|
|
||||||
for k, v in self.contents.items():
|
|
||||||
lines.append(f" {k}: {v}")
|
|
||||||
return "\n".join(lines)
|
|
||||||
|
|
||||||
|
|
||||||
class Mempalace:
|
|
||||||
"""
|
|
||||||
Spatial memory palace for analytical workflows.
|
|
||||||
|
|
||||||
Organises multi-dimensional data about a domain (e.g. Gitea issues) into
|
|
||||||
named rooms. Each room models one analytical dimension, making it easy to
|
|
||||||
traverse observations in a consistent order — the same pattern that produced
|
|
||||||
a 19% throughput improvement in Allegro's April 2026 evaluation.
|
|
||||||
|
|
||||||
Standard rooms for issue-analysis workflows
|
|
||||||
-------------------------------------------
|
|
||||||
repo_architecture Repository structure and inter-repo relationships
|
|
||||||
assignment_status Assigned vs unassigned issue distribution
|
|
||||||
triage_priority Priority / urgency levels (the "lighting system")
|
|
||||||
resolution_patterns Historical resolution trends and velocity
|
|
||||||
|
|
||||||
Usage
|
|
||||||
-----
|
|
||||||
>>> palace = Mempalace.for_issue_analysis()
|
|
||||||
>>> palace.enter("repo_architecture")
|
|
||||||
>>> palace.store("total_repos", 11)
|
|
||||||
>>> palace.store("repos_with_issues", 4)
|
|
||||||
>>> palace.enter("assignment_status")
|
|
||||||
>>> palace.store("assigned", 72)
|
|
||||||
>>> palace.store("unassigned", 22)
|
|
||||||
>>> print(palace.render())
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, domain: str = "general") -> None:
|
|
||||||
self.domain = domain
|
|
||||||
self._rooms: dict[str, PalaceRoom] = {}
|
|
||||||
self._current_room: str | None = None
|
|
||||||
self._created_at: float = time.time()
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
|
||||||
# Factory constructors for common analytical domains
|
|
||||||
# ------------------------------------------------------------------
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def for_issue_analysis(cls) -> "Mempalace":
|
|
||||||
"""Pre-wired palace for Gitea / forge issue-analysis workflows."""
|
|
||||||
p = cls(domain="issue_analysis")
|
|
||||||
p.add_room("repo_architecture", "Repository Architecture Room")
|
|
||||||
p.add_room("assignment_status", "Issue Assignment Status Room")
|
|
||||||
p.add_room("triage_priority", "Triage Priority Room")
|
|
||||||
p.add_room("resolution_patterns", "Resolution Patterns Room")
|
|
||||||
return p
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def for_health_check(cls) -> "Mempalace":
|
|
||||||
"""Pre-wired palace for CI / deployment health-check workflows."""
|
|
||||||
p = cls(domain="health_check")
|
|
||||||
p.add_room("service_topology", "Service Topology Room")
|
|
||||||
p.add_room("failure_signals", "Failure Signals Room")
|
|
||||||
p.add_room("recovery_history", "Recovery History Room")
|
|
||||||
return p
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def for_code_review(cls) -> "Mempalace":
|
|
||||||
"""Pre-wired palace for code-review / PR triage workflows."""
|
|
||||||
p = cls(domain="code_review")
|
|
||||||
p.add_room("change_scope", "Change Scope Room")
|
|
||||||
p.add_room("risk_surface", "Risk Surface Room")
|
|
||||||
p.add_room("test_coverage", "Test Coverage Room")
|
|
||||||
p.add_room("reviewer_context", "Reviewer Context Room")
|
|
||||||
return p
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
|
||||||
# Room management
|
|
||||||
# ------------------------------------------------------------------
|
|
||||||
|
|
||||||
def add_room(self, key: str, label: str) -> PalaceRoom:
|
|
||||||
room = PalaceRoom(name=key, label=label)
|
|
||||||
self._rooms[key] = room
|
|
||||||
return room
|
|
||||||
|
|
||||||
def enter(self, room_key: str) -> PalaceRoom:
|
|
||||||
if room_key not in self._rooms:
|
|
||||||
raise KeyError(f"No room '{room_key}' in palace. Available: {list(self._rooms)}")
|
|
||||||
self._current_room = room_key
|
|
||||||
return self._rooms[room_key]
|
|
||||||
|
|
||||||
def store(self, key: str, value: Any) -> None:
|
|
||||||
"""Store a value in the currently active room."""
|
|
||||||
if self._current_room is None:
|
|
||||||
raise RuntimeError("Enter a room before storing values.")
|
|
||||||
self._rooms[self._current_room].store(key, value)
|
|
||||||
|
|
||||||
def retrieve(self, room_key: str, key: str, default: Any = None) -> Any:
|
|
||||||
if room_key not in self._rooms:
|
|
||||||
return default
|
|
||||||
return self._rooms[room_key].retrieve(key, default)
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
|
||||||
# Rendering
|
|
||||||
# ------------------------------------------------------------------
|
|
||||||
|
|
||||||
def render(self) -> str:
|
|
||||||
"""Return a human-readable summary of the entire palace."""
|
|
||||||
elapsed = time.time() - self._created_at
|
|
||||||
lines = [
|
|
||||||
f"# Mempalace — {self.domain}",
|
|
||||||
f"_traversal time: {elapsed:.2f}s | rooms: {len(self._rooms)}_",
|
|
||||||
"",
|
|
||||||
]
|
|
||||||
for room in self._rooms.values():
|
|
||||||
lines.append(room.summary())
|
|
||||||
lines.append("")
|
|
||||||
return "\n".join(lines)
|
|
||||||
|
|
||||||
def to_dict(self) -> dict:
|
|
||||||
return {
|
|
||||||
"domain": self.domain,
|
|
||||||
"elapsed_seconds": round(time.time() - self._created_at, 3),
|
|
||||||
"rooms": {k: v.contents for k, v in self._rooms.items()},
|
|
||||||
}
|
|
||||||
|
|
||||||
def to_json(self) -> str:
|
|
||||||
return json.dumps(self.to_dict(), indent=2)
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Skill entry-point
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def analyse_issues(
|
|
||||||
repos_data: list[dict],
|
|
||||||
target_assignee_rate: float = 0.80,
|
|
||||||
) -> str:
|
|
||||||
"""
|
|
||||||
Applies the mempalace technique to a list of repo issue summaries.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
repos_data:
|
|
||||||
List of dicts, each with keys: ``repo``, ``open_issues``,
|
|
||||||
``assigned``, ``unassigned``.
|
|
||||||
target_assignee_rate:
|
|
||||||
Minimum acceptable assignee-coverage ratio (default 0.80).
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
str
|
|
||||||
Rendered palace summary with coverage assessment.
|
|
||||||
"""
|
|
||||||
palace = Mempalace.for_issue_analysis()
|
|
||||||
|
|
||||||
# --- Repository Architecture Room ---
|
|
||||||
palace.enter("repo_architecture")
|
|
||||||
total_issues = sum(r.get("open_issues", 0) for r in repos_data)
|
|
||||||
repos_with_issues = sum(1 for r in repos_data if r.get("open_issues", 0) > 0)
|
|
||||||
palace.store("repos_sampled", len(repos_data))
|
|
||||||
palace.store("repos_with_issues", repos_with_issues)
|
|
||||||
palace.store("total_open_issues", total_issues)
|
|
||||||
palace.store(
|
|
||||||
"avg_issues_per_repo",
|
|
||||||
round(total_issues / len(repos_data), 1) if repos_data else 0,
|
|
||||||
)
|
|
||||||
|
|
||||||
# --- Assignment Status Room ---
|
|
||||||
palace.enter("assignment_status")
|
|
||||||
total_assigned = sum(r.get("assigned", 0) for r in repos_data)
|
|
||||||
total_unassigned = sum(r.get("unassigned", 0) for r in repos_data)
|
|
||||||
coverage = total_assigned / total_issues if total_issues else 0
|
|
||||||
palace.store("assigned", total_assigned)
|
|
||||||
palace.store("unassigned", total_unassigned)
|
|
||||||
palace.store("coverage_rate", round(coverage, 3))
|
|
||||||
palace.store(
|
|
||||||
"coverage_status",
|
|
||||||
"OK" if coverage >= target_assignee_rate else f"BELOW TARGET ({target_assignee_rate:.0%})",
|
|
||||||
)
|
|
||||||
|
|
||||||
# --- Triage Priority Room ---
|
|
||||||
palace.enter("triage_priority")
|
|
||||||
unassigned_repos = [r["repo"] for r in repos_data if r.get("unassigned", 0) > 0]
|
|
||||||
palace.store("repos_needing_triage", unassigned_repos)
|
|
||||||
palace.store("triage_count", total_unassigned)
|
|
||||||
|
|
||||||
# --- Resolution Patterns Room ---
|
|
||||||
palace.enter("resolution_patterns")
|
|
||||||
palace.store("technique", "mempalace")
|
|
||||||
palace.store("target_assignee_rate", target_assignee_rate)
|
|
||||||
|
|
||||||
return palace.render()
|
|
||||||
Reference in New Issue
Block a user