Skip to content

feat(BLK-013): real bounded CLI runner task on hardened docker sandbox - #159

Merged
Ghenghis merged 1 commit into
feat/hermes3d-7-complete-gui-repo-wiringfrom
claude/blk013-bounded-task-runner
May 10, 2026
Merged

Ghenghis merged 1 commit into
feat/hermes3d-7-complete-gui-repo-wiringfrom
claude/blk013-bounded-task-runner

Conversation

@Ghenghis

Copy link
Copy Markdown
Owner

Summary

Adds POST /api/code-operator/cli-runners/run-bounded-task and the backing
run_bounded_code_cli_task service. The endpoint executes a single
fixed prompt ("list top-level Python symbols in /workspace/{rel}") through
opencode/openhands inside a hardened docker sandbox.

This clears Wave 2026-05-09 hard gate 7 (real CLI runner work on the hardened
sandbox) without unlocking writes, network egress, or any host-secret mount.

Hardened docker invocation (non-negotiable)

docker run --rm --network=none --read-only \
  --tmpfs /tmp:size=64m,noexec,nosuid \
  --memory=512m --cpus=1 --pids-limit=128 \
  --cap-drop=ALL --security-opt=no-new-privileges \
  -v ${PROJECT_ROOT}:/workspace:ro \
  -w /workspace \
  ${sandbox.image} \
  ${runner_bin} --headless --json -t "${BOUNDED_TASK_PROMPT.format(rel=...)}"

Constants live in services/code_history.py:

  • BOUNDED_TASK_PROMPT — single fixed prompt (callers cannot inject)
  • BOUNDED_TASK_TIMEOUT_S = 30
  • BOUNDED_TASK_MAX_STDOUT_BYTES = 8 * 1024

Output contract

  • Stdout: redact_text(stdout) truncated to 8 KiB; full sha256 recorded as evidence.
  • Stderr: returned only as sha256. Never appears in the response body even
    redacted (OpenHands/aider stderr commonly carries provider base-URLs and
    rate-limit headers).
  • Timeout: container reaped, status="timeout", exit_code=-1.
  • Sandbox not ready or runner not detected: status="blocked", no docker call.

Tests (6/6 pass; 52/52 adjacent regression pass)

04_testing/pytest/unit/test_cli_runner_bounded_task.py:

  1. happy path — fake CompletedProcess returns ["foo","bar"]; evidence recorded.
  2. sandbox not ready — returns blocked, no subprocess.run call.
  3. runner not detected — returns blocked, no subprocess.run call.
  4. timeout reaped — subprocess.TimeoutExpired -> status="timeout", exit_code=-1.
  5. docker argv pin — --network=none, --read-only, --tmpfs ... noexec,nosuid,
    --memory=512m, --cpus=1, --pids-limit=128, --cap-drop=ALL,
    --security-opt=no-new-privileges all present in argv.
  6. sandbox isolation — argv carries no -v G:/private, no ~/.aws, no ${HOME},
    no provider API key env-var pass-through, no -e flag at all.

Test plan

  • python -m py_compile on all 3 touched files
  • pytest 04_testing/pytest/unit/test_cli_runner_bounded_task.py -> 6/6 pass
  • pytest 04_testing/pytest/unit/test_code_operator.py -> 52/52 pass
  • FastAPI route enumeration shows /api/code-operator/cli-runners/run-bounded-task
  • No G:/private, .env, *_API_KEY=value, or literal token in any diff hunk
  • pre-push hook passes

MCP locks

Locked at start of task, will be released after merge:

  • 03_implementation/src/hermes3d/services/code_history.py
  • 03_implementation/src/hermes3d/api/routes/code_operator.py
  • 04_testing/pytest/unit/test_cli_runner_bounded_task.py

Hermes evidence chain: PASS
Task ID: BLK-013-bounded-task
hermes_run_gate: lint+pytest (pre-push)

Online research (BLK-013 brief requirement)

🤖 Generated with Claude Code

Adds POST /api/code-operator/cli-runners/run-bounded-task plus the
backing run_bounded_code_cli_task service. The endpoint executes a
single fixed prompt ("list top-level Python symbols in /workspace/{rel}")
through opencode/openhands via docker run with the full hardened
profile: --network=none, --read-only, /tmp tmpfs (noexec,nosuid),
--memory=512m, --cpus=1, --pids-limit=128, --cap-drop=ALL,
--security-opt=no-new-privileges, workspace mounted :ro.

Stderr is returned only as a sha256 in the response body. Stdout is
redacted via gateways.redaction.redact_text and capped at 8 KiB; full
stdout sha256 is recorded as evidence. On subprocess.TimeoutExpired the
container is reaped and the response carries status="timeout",
exit_code=-1.

This clears Wave 2026-05-09 hard gate 7 (real CLI runner work on the
hardened sandbox) without unlocking writes, network egress, or any
host-secret mount: G:/private, ~/.aws, ${HOME}, and provider API key
env vars are explicitly absent from the docker argv (asserted by test).

6/6 new unit tests pass; 52/52 adjacent test_code_operator regression
tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b5ce7ccd-74dd-4a5e-81b4-3af049afc919

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/blk013-bounded-task-runner

Comment @coderabbitai help to get the list of available commands and usage tips.

@Ghenghis
Ghenghis merged commit 0f42dda into feat/hermes3d-7-complete-gui-repo-wiring May 10, 2026
1 check passed
Ghenghis added a commit that referenced this pull request May 10, 2026
… 7/7 gates green) (#160)

Wave 1 of the v0.13 production promotion swarm cleared all 7 hard gates.
This PR flips the resolver default from v0.12 (hermes-agent-fresh) to
v0.13 (hermes-agent-v013-canary). v0.12 remains the opt-in fallback via
HERMES_AGENT_CHECKOUT=G:/Github/hermes-agent-fresh.

Hard gates (all PASS, evidence below)
1. MiniMax accepted=true       — P1-1 (ev_84c2ba93c25d4017)
2. DeepSeek accepted=true      — P1-1 (ev_6fa1536b411822db)
3. Canary runtime smoke passes — P1-2 (8/8 imports, 38-subcommand CLI,
   10 MCP tools, redaction default-ON per upstream PR #21193)
4. v0.12 rollback path proven  — P1-2 (resolver mid-process flip 4/4
   reads correct; production HEAD bit-identical pre/post)
5. No secret leak              — P1-1 + P1-4 (only env-var NAMES + host
   labels in evidence; auth_scheme is static "Authorization: Bearer
   <redacted>"; OWASP A09 review clean)
6. No v0.12 regression         — P1-2 (production checkout bit-identical;
   git status --short empty post-run)
7. BLK-013 fixed by PR #159    — P1-4 (bounded CLI runner endpoint
   shipped with --network=none + --read-only + --cap-drop=ALL +
   sha256-only stderr; 6/6 new tests + 52/52 adjacent regression PASS)

Bonus discovery (P1-3)
- Upstream PR #21561 already merged the Windows TUI signal.SIGPIPE/
  SIGHUP guard on 2026-05-08 (uses hasattr() pattern + SIGBREAK
  fallback). Canary at v2026.5.7 (498bfc7, 2026-05-07) predates this
  fix, so the TUI/PTY surface still requires advancing the canary
  checkout to >= e93bfc6c to gain native Windows TUI support. Core CLI
  + MCP + redaction + provider chat all work TODAY without that
  advance, which is why this promotion is safe.

Patch
- DEFAULT_AGENT_CHECKOUT now points to v0.13 (canary path).
- New V012_FALLBACK_CHECKOUT constant exposes the v0.12 path for
  operators + tests that need explicit fallback semantics.
- CANARY_AGENT_CHECKOUT retained as alias for back-compat.

Tests added/updated (11/11 PASS)
- test_default_is_v013_post_promotion: env unset → v0.13
- test_v012_fallback_via_env: env=hermes-agent-fresh → v0.12
- test_resolver_constants_are_correct: DEFAULT == CANARY == v0.13;
  V012_FALLBACK == hermes-agent-fresh
- test_a5_per_call_env_flip: post-promotion semantic (v0.13 ↔ v0.12)
- test_a4_agent_updates_repo_path_per_call: same semantic via
  agent_updates._repo_path()
- test_v013_default_when_env_unset: route helper + resolver agree
- (4 source-level pin tests retained from PR #155)

Verification
- py_compile: OK
- Focused tests: 11/11 pass
- Pre-push hook: passed

Rollback
- Operators set HERMES_AGENT_CHECKOUT=G:/Github/hermes-agent-fresh in
  the FastAPI process env to revert to v0.12 mid-process (no restart;
  per-call resolver from PR #155).
- Or revert this PR (1 commit) to restore v0.12 as the file-level
  default.
- Production v0.12 checkout at G:/Github/hermes-agent-fresh remains
  byte-identical (HEAD 73bf3ab1, v2026.4.30, status clean) — never
  modified by canary work.

Companion PRs
- PR #155 (8544bbc): per-call env-switch resolver (Wave A4 + A5)
- PR #157 (b8277db): canary smoke results 7/1/0
- PR #158 (5a32fe0): production v0.13 + multi-version action plan
- PR #159 (open): BLK-013 bounded task — provides hard gate 7

Hermes-Agent-Only Wave 1 swarm provenance
- P1-1 Live provider probes: PASS (ev_84c2ba93c25d4017 + ev_6fa1536b411822db)
- P1-2 Canary runtime + production-untouched: PASS (5/5 sub-checks)
- P1-3 Upstream Windows guard: ALREADY MERGED upstream (PR #21561)
- P1-4 BLK-013: PR #159 OPEN (6/6 + 52/52 regression PASS)
- P1-5 Promotion: this PR

References
- Upstream v0.13.0 release: https://github.com/NousResearch/hermes-agent/releases/tag/v2026.5.7
- Upstream redaction default-ON: NousResearch/hermes-agent#21193
- Upstream Windows guard: NousResearch/hermes-agent#21561
- 12-Factor App rule III (config in env): https://12factor.net/config

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ghenghis added a commit that referenced this pull request May 10, 2026
Re-runs all 8 smokes from PR #157 against the now-promoted v0.13
default. PR #160 squash 3158a4e flipped DEFAULT_AGENT_CHECKOUT to
v0.13 canary path; this PR confirms zero regression and upgrades
Smoke 7 (BLK-013 bounded task) from N/A to PASS using the endpoint
shipped in PR #159 with monkeypatched subprocess.run.

Result: 8 PASS / 0 FAIL / 0 N/A. Net change vs PR #157: Smoke 7
N/A -> PASS. Every smoke that PR #157 reported PASS is still PASS.

Pre-flight pins (both PASS)
- services/agent_checkout.py:DEFAULT_AGENT_CHECKOUT == v0.13 canary
- G:/Github/hermes-agent-fresh HEAD == 73bf3ab1 (v0.12 byte-identical)

Smoke results
1. Hermes Agent imports: 8/8 packages OK against canary venv
2. MCP tools: 10 @mcp.tool() decorators at exact PR #157 lines
   (471, 528, 561, 618, 670, 699, 733, 769, 823, 839)
3. MiniMax: build_probe_request well-formed; no key value leak
4. DeepSeek: RuntimeError on missing env; env-var NAME not echoed
5. OpenCode: detected=True via PRIVATE_ENV path
6. OpenHands: detected=True (Finding F1: --version probe slow on
   cold start; non-blocking, documented)
7. BLK-013 bounded task: TestClient + monkeypatched subprocess.run;
   hardened-docker argv pinned (--network=none, --read-only,
   --cap-drop=ALL, --tmpfs noexec, --memory=512m, --cpus=1,
   --pids-limit=128, --security-opt=no-new-privileges); no -v
   G:/private mount; no -e provider env passthrough; stderr only
   surfaces as sha256
8. Rollback to v0.12: 5/5 mid-process env flips correct

Module added: 04_testing/pytest/unit/test_v013_post_promotion_smoke.py
(11 cases: 2 pre-flight pins + 8 smokes + 1 aggregate). Runs in
~20s, no live HTTP, no docker spawn. Handoff doc:
03_implementation/docs/handoffs/HERMES_AGENT_V013_POST_PROMOTION_SMOKE_2026-05-09.md

Constraints honored
- Read-only on canary code (no upstream edits)
- No secret values printed or stored
- Live HTTP probes deferred (PR #157 already banked accepted=true)
- MCP locks held + evidence recorded (ev_4003d541281ee381) for
  task P1-7-V013-POST-PROMOTION-SMOKE

Online research
- Argo Rollouts blue-green smoke gate
  https://argoproj.github.io/rollouts/
- Kubernetes post-flight pattern
  https://testkube.io/glossary/post-flight-testing
- 12-Factor App rule III https://12factor.net/config

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ghenghis added a commit that referenced this pull request May 10, 2026
…162)

* feat(hermes-agent): promote v0.13 to production default (Wave 1 P1-5; 7/7 gates green)

Wave 1 of the v0.13 production promotion swarm cleared all 7 hard gates.
This PR flips the resolver default from v0.12 (hermes-agent-fresh) to
v0.13 (hermes-agent-v013-canary). v0.12 remains the opt-in fallback via
HERMES_AGENT_CHECKOUT=G:/Github/hermes-agent-fresh.

Hard gates (all PASS, evidence below)
1. MiniMax accepted=true       — P1-1 (ev_84c2ba93c25d4017)
2. DeepSeek accepted=true      — P1-1 (ev_6fa1536b411822db)
3. Canary runtime smoke passes — P1-2 (8/8 imports, 38-subcommand CLI,
   10 MCP tools, redaction default-ON per upstream PR #21193)
4. v0.12 rollback path proven  — P1-2 (resolver mid-process flip 4/4
   reads correct; production HEAD bit-identical pre/post)
5. No secret leak              — P1-1 + P1-4 (only env-var NAMES + host
   labels in evidence; auth_scheme is static "Authorization: Bearer
   <redacted>"; OWASP A09 review clean)
6. No v0.12 regression         — P1-2 (production checkout bit-identical;
   git status --short empty post-run)
7. BLK-013 fixed by PR #159    — P1-4 (bounded CLI runner endpoint
   shipped with --network=none + --read-only + --cap-drop=ALL +
   sha256-only stderr; 6/6 new tests + 52/52 adjacent regression PASS)

Bonus discovery (P1-3)
- Upstream PR #21561 already merged the Windows TUI signal.SIGPIPE/
  SIGHUP guard on 2026-05-08 (uses hasattr() pattern + SIGBREAK
  fallback). Canary at v2026.5.7 (498bfc7, 2026-05-07) predates this
  fix, so the TUI/PTY surface still requires advancing the canary
  checkout to >= e93bfc6c to gain native Windows TUI support. Core CLI
  + MCP + redaction + provider chat all work TODAY without that
  advance, which is why this promotion is safe.

Patch
- DEFAULT_AGENT_CHECKOUT now points to v0.13 (canary path).
- New V012_FALLBACK_CHECKOUT constant exposes the v0.12 path for
  operators + tests that need explicit fallback semantics.
- CANARY_AGENT_CHECKOUT retained as alias for back-compat.

Tests added/updated (11/11 PASS)
- test_default_is_v013_post_promotion: env unset → v0.13
- test_v012_fallback_via_env: env=hermes-agent-fresh → v0.12
- test_resolver_constants_are_correct: DEFAULT == CANARY == v0.13;
  V012_FALLBACK == hermes-agent-fresh
- test_a5_per_call_env_flip: post-promotion semantic (v0.13 ↔ v0.12)
- test_a4_agent_updates_repo_path_per_call: same semantic via
  agent_updates._repo_path()
- test_v013_default_when_env_unset: route helper + resolver agree
- (4 source-level pin tests retained from PR #155)

Verification
- py_compile: OK
- Focused tests: 11/11 pass
- Pre-push hook: passed

Rollback
- Operators set HERMES_AGENT_CHECKOUT=G:/Github/hermes-agent-fresh in
  the FastAPI process env to revert to v0.12 mid-process (no restart;
  per-call resolver from PR #155).
- Or revert this PR (1 commit) to restore v0.12 as the file-level
  default.
- Production v0.12 checkout at G:/Github/hermes-agent-fresh remains
  byte-identical (HEAD 73bf3ab1, v2026.4.30, status clean) — never
  modified by canary work.

Companion PRs
- PR #155 (8544bbc): per-call env-switch resolver (Wave A4 + A5)
- PR #157 (b8277db): canary smoke results 7/1/0
- PR #158 (5a32fe0): production v0.13 + multi-version action plan
- PR #159 (open): BLK-013 bounded task — provides hard gate 7

Hermes-Agent-Only Wave 1 swarm provenance
- P1-1 Live provider probes: PASS (ev_84c2ba93c25d4017 + ev_6fa1536b411822db)
- P1-2 Canary runtime + production-untouched: PASS (5/5 sub-checks)
- P1-3 Upstream Windows guard: ALREADY MERGED upstream (PR #21561)
- P1-4 BLK-013: PR #159 OPEN (6/6 + 52/52 regression PASS)
- P1-5 Promotion: this PR

References
- Upstream v0.13.0 release: https://github.com/NousResearch/hermes-agent/releases/tag/v2026.5.7
- Upstream redaction default-ON: NousResearch/hermes-agent#21193
- Upstream Windows guard: NousResearch/hermes-agent#21561
- 12-Factor App rule III (config in env): https://12factor.net/config

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(hermes-agent): version registry for v0.12 + v0.13 (Wave 2 P2-1)

Adds services/agent_version_registry.py — a frozen-dataclass registry
(KNOWN_VERSIONS = (V012, V013)) of known Hermes Agent versions and
their feature flags (redaction_default_on, has_kanban,
has_heartbeat_reclaim, has_zombie_detection,
has_pluggable_providers_dir). Pairs with agent_checkout.py: that module
resolves which path is active right now; this one says what we know
about each version we've shipped. active_version() reads the live
checkout path through hermes_agent_checkout() so per-call env-flips
(PR #155 / Wave A5) propagate without restart, and returns None if the
operator points HERMES_AGENT_CHECKOUT at a custom fork.

Reads agent_checkout module-level constants only; does not edit it.

Pinned by tests:
- KNOWN_VERSIONS = (v0.12@v2026.4.30, v0.13@v2026.5.7) in order.
- v0.13.redaction_default_on=True (NousResearch/hermes-agent#21193,
  Wave A3 verified).
- v0.12.redaction_default_on=False (Wave 2 prior swarm Agent 2).
- active_version() == v0.13 with env unset (post-PR #160 default).
- active_version() == v0.12 when env=hermes-agent-fresh.
- active_version() is None for unknown checkouts.
- FrozenInstanceError on any field mutation (no cross-version leak).

LoC: 89 lines source (67 non-blank/non-comment) + 114 lines tests.
Tests: 13/13 pass; combined with test_agent_checkout_resolver.py 24/24 pass.

References:
- Python @DataClass(frozen=True): https://docs.python.org/3/library/dataclasses.html#frozen-instances
- Django AppConfig metadata pattern: https://docs.djangoproject.com/en/5.1/ref/applications/

Handoff: P2-5 (compat matrix) reads KNOWN_VERSIONS for capability rows;
P2-6 (proof events) reads active_version().label/upstream_tag for the
version_tag field on every event.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ghenghis added a commit that referenced this pull request May 10, 2026
* feat(hermes-agent): promote v0.13 to production default (Wave 1 P1-5; 7/7 gates green)

Wave 1 of the v0.13 production promotion swarm cleared all 7 hard gates.
This PR flips the resolver default from v0.12 (hermes-agent-fresh) to
v0.13 (hermes-agent-v013-canary). v0.12 remains the opt-in fallback via
HERMES_AGENT_CHECKOUT=G:/Github/hermes-agent-fresh.

Hard gates (all PASS, evidence below)
1. MiniMax accepted=true       — P1-1 (ev_84c2ba93c25d4017)
2. DeepSeek accepted=true      — P1-1 (ev_6fa1536b411822db)
3. Canary runtime smoke passes — P1-2 (8/8 imports, 38-subcommand CLI,
   10 MCP tools, redaction default-ON per upstream PR #21193)
4. v0.12 rollback path proven  — P1-2 (resolver mid-process flip 4/4
   reads correct; production HEAD bit-identical pre/post)
5. No secret leak              — P1-1 + P1-4 (only env-var NAMES + host
   labels in evidence; auth_scheme is static "Authorization: Bearer
   <redacted>"; OWASP A09 review clean)
6. No v0.12 regression         — P1-2 (production checkout bit-identical;
   git status --short empty post-run)
7. BLK-013 fixed by PR #159    — P1-4 (bounded CLI runner endpoint
   shipped with --network=none + --read-only + --cap-drop=ALL +
   sha256-only stderr; 6/6 new tests + 52/52 adjacent regression PASS)

Bonus discovery (P1-3)
- Upstream PR #21561 already merged the Windows TUI signal.SIGPIPE/
  SIGHUP guard on 2026-05-08 (uses hasattr() pattern + SIGBREAK
  fallback). Canary at v2026.5.7 (498bfc7, 2026-05-07) predates this
  fix, so the TUI/PTY surface still requires advancing the canary
  checkout to >= e93bfc6c to gain native Windows TUI support. Core CLI
  + MCP + redaction + provider chat all work TODAY without that
  advance, which is why this promotion is safe.

Patch
- DEFAULT_AGENT_CHECKOUT now points to v0.13 (canary path).
- New V012_FALLBACK_CHECKOUT constant exposes the v0.12 path for
  operators + tests that need explicit fallback semantics.
- CANARY_AGENT_CHECKOUT retained as alias for back-compat.

Tests added/updated (11/11 PASS)
- test_default_is_v013_post_promotion: env unset → v0.13
- test_v012_fallback_via_env: env=hermes-agent-fresh → v0.12
- test_resolver_constants_are_correct: DEFAULT == CANARY == v0.13;
  V012_FALLBACK == hermes-agent-fresh
- test_a5_per_call_env_flip: post-promotion semantic (v0.13 ↔ v0.12)
- test_a4_agent_updates_repo_path_per_call: same semantic via
  agent_updates._repo_path()
- test_v013_default_when_env_unset: route helper + resolver agree
- (4 source-level pin tests retained from PR #155)

Verification
- py_compile: OK
- Focused tests: 11/11 pass
- Pre-push hook: passed

Rollback
- Operators set HERMES_AGENT_CHECKOUT=G:/Github/hermes-agent-fresh in
  the FastAPI process env to revert to v0.12 mid-process (no restart;
  per-call resolver from PR #155).
- Or revert this PR (1 commit) to restore v0.12 as the file-level
  default.
- Production v0.12 checkout at G:/Github/hermes-agent-fresh remains
  byte-identical (HEAD 73bf3ab1, v2026.4.30, status clean) — never
  modified by canary work.

Companion PRs
- PR #155 (8544bbc): per-call env-switch resolver (Wave A4 + A5)
- PR #157 (b8277db): canary smoke results 7/1/0
- PR #158 (5a32fe0): production v0.13 + multi-version action plan
- PR #159 (open): BLK-013 bounded task — provides hard gate 7

Hermes-Agent-Only Wave 1 swarm provenance
- P1-1 Live provider probes: PASS (ev_84c2ba93c25d4017 + ev_6fa1536b411822db)
- P1-2 Canary runtime + production-untouched: PASS (5/5 sub-checks)
- P1-3 Upstream Windows guard: ALREADY MERGED upstream (PR #21561)
- P1-4 BLK-013: PR #159 OPEN (6/6 + 52/52 regression PASS)
- P1-5 Promotion: this PR

References
- Upstream v0.13.0 release: https://github.com/NousResearch/hermes-agent/releases/tag/v2026.5.7
- Upstream redaction default-ON: NousResearch/hermes-agent#21193
- Upstream Windows guard: NousResearch/hermes-agent#21561
- 12-Factor App rule III (config in env): https://12factor.net/config

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(v0.12-pin): regression-pin 8 fallback surfaces (Wave 2 P2-3)

Pin v0.12 (G:/Github/hermes-agent-fresh, v2026.4.30) fallback behavior
for the eight surfaces that consume the active Hermes Agent checkout.
After PR #160 promoted v0.13 to default, v0.12 became the rollback path
(operator sets HERMES_AGENT_CHECKOUT=fresh to revert mid-process). If a
future v0.13-only change broke any of these surfaces, rollback would be
half-functional and operators could not safely revert.

Surfaces pinned (one test each):
1. agent_updates._repo_path() — per-call resolver
2. module_runtime.BUILTIN_RUNTIME_PROBES["hermes_agent"]["path"]
   (note: prompt said MODULES, actual constant is BUILTIN_RUNTIME_PROBES)
3. code_history.SOURCE_REPOS[id="nous_hermes_agent"].local_path
4. db/load_modules.SOURCE_OVERRIDES["hermes_agent"]["local_path"]
5. _run_update_checks does not crash under v0.12 (canary-venv-free)
6. Provider config (MiniMax + DeepSeek) — config layer only, no HTTP
7. CLI runner detection (OpenCode + OpenHands) — env-only, no PATH dep
8. agent_config.last_run + proof_events writes round-trip cleanly,
   no v0.13 cross-version contamination at write time

Surfaces 2-4 are module-level constants captured at import; the test
mutates env then importlib.reload(module) so the literals re-resolve.
Pattern documented in test docstrings with references to:
- pytest monkeypatch — https://docs.pytest.org/en/stable/how-to/monkeypatch.html
- importlib.reload — https://docs.python.org/3/library/importlib.html
- nox per-version isolation — https://nox.thea.codes/en/stable/tutorial.html

8/8 pass; 11 existing resolver tests still pass (19/19 combined).
No production source modified — read-only on src/, test-only addition.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ghenghis added a commit that referenced this pull request May 10, 2026
… P2-4) (#165)

Adds 8 focused unit tests that lock down the post-PR #160 (squash 3158a4e)
production v0.13 default behavior so future commits cannot silently regress
Wave 1 gate 3.

Surfaces pinned (one test each):
1. agent_updates._repo_path() -> v0.13 path when env unset.
2. module_runtime.BUILTIN_RUNTIME_PROBES['hermes_agent']['path']
   resolves to v0.13 when env unset at module-import (importlib.reload).
3. code_history.SOURCE_REPOS[id='nous_hermes_agent'].local_path
   resolves to v0.13 when env unset at module-import (importlib.reload).
4. db.load_modules.SOURCE_OVERRIDES['hermes_agent']['local_path']
   resolves to v0.13 when env unset at module-import (importlib.reload).
5. agent_updates._run_update_checks runs against the v0.13 checkout
   without crashing (subprocess mocked; pytest gate is NEVER spawned).
6. MiniMax + DeepSeek probe requests build correctly with v0.13 active
   (no live HTTP; bearer scheme + Accept header asserted).
7. CLI runner detection works (OpenCode 1.4.3-hermes3d + OpenHands CLI
   1.16.0 per PR #157 baseline) under v0.13 default.
8. BLK-013 POST /api/code-operator/cli-runners/run-bounded-task is
   registered (per PR #159) and the docker argv carries --network=none
   (subprocess.run mocked; no real container spawn).

Sister coverage to PR P2-3 (test_v012_fallback_regression_pin.py): same
8 surfaces, opposite env state. Neither file duplicates the existing
test_agent_checkout_resolver.py — those tests cover env-flip semantics;
this file pins the resolved path values across all 4 import-time sites
and the 4 runtime sites that depend on them.

Refs:
- PR #160 squash 3158a4e (Wave 1 promotion, 7/7 gates green)
- PR #159 0f42dda (BLK-013 hardened bounded-task runner)
- PR #157 b8277db (canary smoke baseline; CLI runner versions)
- pip-tools regression-pin pattern (pin a default in lock file +
  re-assert on every compile run); 12-Factor App config rule III.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ghenghis added a commit that referenced this pull request May 10, 2026
… P3-4) (#171)

* feat(hermes-agent): promote v0.13 to production default (Wave 1 P1-5; 7/7 gates green)

Wave 1 of the v0.13 production promotion swarm cleared all 7 hard gates.
This PR flips the resolver default from v0.12 (hermes-agent-fresh) to
v0.13 (hermes-agent-v013-canary). v0.12 remains the opt-in fallback via
HERMES_AGENT_CHECKOUT=G:/Github/hermes-agent-fresh.

Hard gates (all PASS, evidence below)
1. MiniMax accepted=true       — P1-1 (ev_84c2ba93c25d4017)
2. DeepSeek accepted=true      — P1-1 (ev_6fa1536b411822db)
3. Canary runtime smoke passes — P1-2 (8/8 imports, 38-subcommand CLI,
   10 MCP tools, redaction default-ON per upstream PR #21193)
4. v0.12 rollback path proven  — P1-2 (resolver mid-process flip 4/4
   reads correct; production HEAD bit-identical pre/post)
5. No secret leak              — P1-1 + P1-4 (only env-var NAMES + host
   labels in evidence; auth_scheme is static "Authorization: Bearer
   <redacted>"; OWASP A09 review clean)
6. No v0.12 regression         — P1-2 (production checkout bit-identical;
   git status --short empty post-run)
7. BLK-013 fixed by PR #159    — P1-4 (bounded CLI runner endpoint
   shipped with --network=none + --read-only + --cap-drop=ALL +
   sha256-only stderr; 6/6 new tests + 52/52 adjacent regression PASS)

Bonus discovery (P1-3)
- Upstream PR #21561 already merged the Windows TUI signal.SIGPIPE/
  SIGHUP guard on 2026-05-08 (uses hasattr() pattern + SIGBREAK
  fallback). Canary at v2026.5.7 (498bfc7, 2026-05-07) predates this
  fix, so the TUI/PTY surface still requires advancing the canary
  checkout to >= e93bfc6c to gain native Windows TUI support. Core CLI
  + MCP + redaction + provider chat all work TODAY without that
  advance, which is why this promotion is safe.

Patch
- DEFAULT_AGENT_CHECKOUT now points to v0.13 (canary path).
- New V012_FALLBACK_CHECKOUT constant exposes the v0.12 path for
  operators + tests that need explicit fallback semantics.
- CANARY_AGENT_CHECKOUT retained as alias for back-compat.

Tests added/updated (11/11 PASS)
- test_default_is_v013_post_promotion: env unset → v0.13
- test_v012_fallback_via_env: env=hermes-agent-fresh → v0.12
- test_resolver_constants_are_correct: DEFAULT == CANARY == v0.13;
  V012_FALLBACK == hermes-agent-fresh
- test_a5_per_call_env_flip: post-promotion semantic (v0.13 ↔ v0.12)
- test_a4_agent_updates_repo_path_per_call: same semantic via
  agent_updates._repo_path()
- test_v013_default_when_env_unset: route helper + resolver agree
- (4 source-level pin tests retained from PR #155)

Verification
- py_compile: OK
- Focused tests: 11/11 pass
- Pre-push hook: passed

Rollback
- Operators set HERMES_AGENT_CHECKOUT=G:/Github/hermes-agent-fresh in
  the FastAPI process env to revert to v0.12 mid-process (no restart;
  per-call resolver from PR #155).
- Or revert this PR (1 commit) to restore v0.12 as the file-level
  default.
- Production v0.12 checkout at G:/Github/hermes-agent-fresh remains
  byte-identical (HEAD 73bf3ab1, v2026.4.30, status clean) — never
  modified by canary work.

Companion PRs
- PR #155 (8544bbc): per-call env-switch resolver (Wave A4 + A5)
- PR #157 (b8277db): canary smoke results 7/1/0
- PR #158 (5a32fe0): production v0.13 + multi-version action plan
- PR #159 (open): BLK-013 bounded task — provides hard gate 7

Hermes-Agent-Only Wave 1 swarm provenance
- P1-1 Live provider probes: PASS (ev_84c2ba93c25d4017 + ev_6fa1536b411822db)
- P1-2 Canary runtime + production-untouched: PASS (5/5 sub-checks)
- P1-3 Upstream Windows guard: ALREADY MERGED upstream (PR #21561)
- P1-4 BLK-013: PR #159 OPEN (6/6 + 52/52 regression PASS)
- P1-5 Promotion: this PR

References
- Upstream v0.13.0 release: https://github.com/NousResearch/hermes-agent/releases/tag/v2026.5.7
- Upstream redaction default-ON: NousResearch/hermes-agent#21193
- Upstream Windows guard: NousResearch/hermes-agent#21561
- 12-Factor App rule III (config in env): https://12factor.net/config

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(recovery): pin RC v2 saga semantics across v0.12 + v0.13 (Wave 4 P3-4)

Mission: confirm Recovery Controller v2 freeze/thaw + saga compensation
behave identically against the v0.12 fallback checkout
(G:/Github/hermes-agent-fresh) and the v0.13 production default
(G:/Github/hermes-agent-v013-canary). Per the action plan §P3-4 user
requirement: "freeze/snapshot/MCP-lock semantics unchanged".

This is a TEST-ONLY PR. recovery_controller.py source is NOT modified.

New tests (8 logical, 9 collected — test 5 parameterized v012/v013):
1. test_freeze_run_creates_record_under_v013_default — env unset, freeze
   produces RecoveryRun with locked_files + pre_snapshot_ids +
   freeze_event_utc.
2. test_freeze_run_creates_record_under_v012_fallback — env=v0.12, identical
   record shape (full payload-key contract).
3. test_thaw_run_releases_locks_v013 — release exactly once, locked_files
   cleared.
4. test_thaw_run_releases_locks_v012 — parity with v0.13.
5. test_compensate_freeze_failure_under_both_versions — Postgres-style
   rollback: snapshot fails on file 3 of 3; release_mcp_files called once
   for the full set, mark_recovery_outcome retry_failed written, run
   transitions to RETRY_FAILED. Same on v0.12 + v0.13 (parametrized).
6. test_freeze_run_proof_event_records_active_version — auto-skipped until
   P2-6 (proof event version tagging) lands; detection scans
   RecoveryRun.__dataclass_fields__ for version_label / version_tag /
   active_checkout / hermes_agent_version.
7. test_recovery_run_dataclass_field_set_unchanged — pin: field set is
   identical across env-driven importlib.reload of recovery_controller.
8. test_freeze_thaw_round_trip_identical_across_versions — saga step
   ORDER (record_step_failure → lock_mcp_files → snapshot_file ×N →
   release_mcp_files) is byte-identical across versions.

Sources cited (2-source minimum per brief):
* Saga pattern (Garcia-Molina + Salem 1987) — Temporal docs:
  https://temporal.io/blog/saga-pattern-made-easy
* PostgreSQL transaction-rollback semantics:
  https://www.postgresql.org/docs/current/tutorial-transactions.html

Constraints honored:
* recovery_controller.py source NOT modified.
* No filesystem requirement on actual v0.12/v0.13 directories — all v1
  collaborators stubbed (lock_mcp_files, release_mcp_files,
  snapshot_file, record_step_failure, mark_recovery_outcome,
  append_mcp_evidence).
* RC v2 commits 3-5 (autonomous mode, propose/review/apply, UI panel)
  out of scope.

Verification:
* New file alone: pytest 8 passed, 1 skipped (test 6 P2-6 fence).
* Combined (this file + test_v012_fallback_regression_pin.py +
  test_v013_default_regression_pin.py + test_agent_checkout_resolver.py +
  test_recovery_controller_freeze.py): 41 passed, 1 skipped, 1 deselected
  (BLK-013 route test pre-existing failure unrelated to RC v2 — relies on
  PR #159 not in this branch's ancestry).

Hermes evidence chain: PASS
Task ID: P3-4-RC-CROSS-VERSION-2026-05-09
hermes_run_gate: pytest 8/8 + combined 41+/41+

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ghenghis added a commit that referenced this pull request May 10, 2026
* test(v0.13): regression-pin v0.13 production default behavior (Wave 2 P2-4)

Adds 8 focused unit tests that lock down the post-PR #160 (squash 3158a4e)
production v0.13 default behavior so future commits cannot silently regress
Wave 1 gate 3.

Surfaces pinned (one test each):
1. agent_updates._repo_path() -> v0.13 path when env unset.
2. module_runtime.BUILTIN_RUNTIME_PROBES['hermes_agent']['path']
   resolves to v0.13 when env unset at module-import (importlib.reload).
3. code_history.SOURCE_REPOS[id='nous_hermes_agent'].local_path
   resolves to v0.13 when env unset at module-import (importlib.reload).
4. db.load_modules.SOURCE_OVERRIDES['hermes_agent']['local_path']
   resolves to v0.13 when env unset at module-import (importlib.reload).
5. agent_updates._run_update_checks runs against the v0.13 checkout
   without crashing (subprocess mocked; pytest gate is NEVER spawned).
6. MiniMax + DeepSeek probe requests build correctly with v0.13 active
   (no live HTTP; bearer scheme + Accept header asserted).
7. CLI runner detection works (OpenCode 1.4.3-hermes3d + OpenHands CLI
   1.16.0 per PR #157 baseline) under v0.13 default.
8. BLK-013 POST /api/code-operator/cli-runners/run-bounded-task is
   registered (per PR #159) and the docker argv carries --network=none
   (subprocess.run mocked; no real container spawn).

Sister coverage to PR P2-3 (test_v012_fallback_regression_pin.py): same
8 surfaces, opposite env state. Neither file duplicates the existing
test_agent_checkout_resolver.py — those tests cover env-flip semantics;
this file pins the resolved path values across all 4 import-time sites
and the 4 runtime sites that depend on them.

Refs:
- PR #160 squash 3158a4e (Wave 1 promotion, 7/7 gates green)
- PR #159 0f42dda (BLK-013 hardened bounded-task runner)
- PR #157 b8277db (canary smoke baseline; CLI runner versions)
- pip-tools regression-pin pattern (pin a default in lock file +
  re-assert on every compile run); 12-Factor App config rule III.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(v0.12-pin): regression-pin 8 fallback surfaces (Wave 2 P2-3)

Pin v0.12 (G:/Github/hermes-agent-fresh, v2026.4.30) fallback behavior
for the eight surfaces that consume the active Hermes Agent checkout.
After PR #160 promoted v0.13 to default, v0.12 became the rollback path
(operator sets HERMES_AGENT_CHECKOUT=fresh to revert mid-process). If a
future v0.13-only change broke any of these surfaces, rollback would be
half-functional and operators could not safely revert.

Surfaces pinned (one test each):
1. agent_updates._repo_path() — per-call resolver
2. module_runtime.BUILTIN_RUNTIME_PROBES["hermes_agent"]["path"]
   (note: prompt said MODULES, actual constant is BUILTIN_RUNTIME_PROBES)
3. code_history.SOURCE_REPOS[id="nous_hermes_agent"].local_path
4. db/load_modules.SOURCE_OVERRIDES["hermes_agent"]["local_path"]
5. _run_update_checks does not crash under v0.12 (canary-venv-free)
6. Provider config (MiniMax + DeepSeek) — config layer only, no HTTP
7. CLI runner detection (OpenCode + OpenHands) — env-only, no PATH dep
8. agent_config.last_run + proof_events writes round-trip cleanly,
   no v0.13 cross-version contamination at write time

Surfaces 2-4 are module-level constants captured at import; the test
mutates env then importlib.reload(module) so the literals re-resolve.
Pattern documented in test docstrings with references to:
- pytest monkeypatch — https://docs.pytest.org/en/stable/how-to/monkeypatch.html
- importlib.reload — https://docs.python.org/3/library/importlib.html
- nox per-version isolation — https://nox.thea.codes/en/stable/tutorial.html

8/8 pass; 11 existing resolver tests still pass (19/19 combined).
No production source modified — read-only on src/, test-only addition.

* ci(P3-3): multi-version regression gate (Wave 2 P3-3)

Add .github/workflows/hermes-agent-versions.yml — runs the per-call
resolver suite (PR #155) plus both regression-pin suites (PR #163 v0.12
fallback + PR #165 v0.13 default) on ubuntu-24.04 + windows-latest with
Python 3.11 whenever any contract surface changes.

Why
- Wave 1 (PR #160 squash 3158a4e) promoted Hermes Agent v0.13 to the
  production default; v0.12 is now the operator rollback
  (HERMES_AGENT_CHECKOUT=G:/Github/hermes-agent-fresh).
- A future commit could silently break either side. This workflow is
  the Wave 2 gate that fails CI if either version regresses.
- Windows-latest is required because production runs on Windows
  desktops (Tenacity Release ships a Windows TUI guard); Ubuntu-24.04
  is the server-host parity row.

Design notes
- fail-fast: false so one OS does not mask the other.
- No `pip install -e .` — the regression suites are env-mocked + path-
  mocked; verified by reading both pin files end to end (no .exists()
  on G:/Github/hermes-agent-fresh or G:/Github/hermes-agent-v013-canary,
  only Path equality).
- HERMES_AGENT_CHECKOUT is left UNSET in the env block — the resolver
  defaults to v0.13, and the v0.12 suite uses monkeypatch.setenv
  internally (per pytest's recommended pattern).
- shell: bash on the test steps so backslash line continuation works
  on both runners.

Local re-run gate: 27 passed in 2.40s (Windows + Python 3.14, will run
on Python 3.11 in CI). yamllint: 2 cosmetic warnings only, identical
profile to existing ci.yml.

Hermes evidence chain: PASS
Task ID: P3-3-MULTI-VERSION-CI-2026-05-09
hermes_run_gate: yamllint clean + local pytest re-run on same suites

Sources cited (read for this PR):
- GitHub Actions matrix strategy:
  https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs
- pytest skip / xfail multi-OS guidance:
  https://docs.pytest.org/en/stable/how-to/skipping.html
- 12-Factor App config rule III: https://12factor.net/config

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ghenghis added a commit that referenced this pull request May 10, 2026
… of #168] (#173)

* feat(hermes-agent): promote v0.13 to production default (Wave 1 P1-5; 7/7 gates green)

Wave 1 of the v0.13 production promotion swarm cleared all 7 hard gates.
This PR flips the resolver default from v0.12 (hermes-agent-fresh) to
v0.13 (hermes-agent-v013-canary). v0.12 remains the opt-in fallback via
HERMES_AGENT_CHECKOUT=G:/Github/hermes-agent-fresh.

Hard gates (all PASS, evidence below)
1. MiniMax accepted=true       — P1-1 (ev_84c2ba93c25d4017)
2. DeepSeek accepted=true      — P1-1 (ev_6fa1536b411822db)
3. Canary runtime smoke passes — P1-2 (8/8 imports, 38-subcommand CLI,
   10 MCP tools, redaction default-ON per upstream PR #21193)
4. v0.12 rollback path proven  — P1-2 (resolver mid-process flip 4/4
   reads correct; production HEAD bit-identical pre/post)
5. No secret leak              — P1-1 + P1-4 (only env-var NAMES + host
   labels in evidence; auth_scheme is static "Authorization: Bearer
   <redacted>"; OWASP A09 review clean)
6. No v0.12 regression         — P1-2 (production checkout bit-identical;
   git status --short empty post-run)
7. BLK-013 fixed by PR #159    — P1-4 (bounded CLI runner endpoint
   shipped with --network=none + --read-only + --cap-drop=ALL +
   sha256-only stderr; 6/6 new tests + 52/52 adjacent regression PASS)

Bonus discovery (P1-3)
- Upstream PR #21561 already merged the Windows TUI signal.SIGPIPE/
  SIGHUP guard on 2026-05-08 (uses hasattr() pattern + SIGBREAK
  fallback). Canary at v2026.5.7 (498bfc7, 2026-05-07) predates this
  fix, so the TUI/PTY surface still requires advancing the canary
  checkout to >= e93bfc6c to gain native Windows TUI support. Core CLI
  + MCP + redaction + provider chat all work TODAY without that
  advance, which is why this promotion is safe.

Patch
- DEFAULT_AGENT_CHECKOUT now points to v0.13 (canary path).
- New V012_FALLBACK_CHECKOUT constant exposes the v0.12 path for
  operators + tests that need explicit fallback semantics.
- CANARY_AGENT_CHECKOUT retained as alias for back-compat.

Tests added/updated (11/11 PASS)
- test_default_is_v013_post_promotion: env unset → v0.13
- test_v012_fallback_via_env: env=hermes-agent-fresh → v0.12
- test_resolver_constants_are_correct: DEFAULT == CANARY == v0.13;
  V012_FALLBACK == hermes-agent-fresh
- test_a5_per_call_env_flip: post-promotion semantic (v0.13 ↔ v0.12)
- test_a4_agent_updates_repo_path_per_call: same semantic via
  agent_updates._repo_path()
- test_v013_default_when_env_unset: route helper + resolver agree
- (4 source-level pin tests retained from PR #155)

Verification
- py_compile: OK
- Focused tests: 11/11 pass
- Pre-push hook: passed

Rollback
- Operators set HERMES_AGENT_CHECKOUT=G:/Github/hermes-agent-fresh in
  the FastAPI process env to revert to v0.12 mid-process (no restart;
  per-call resolver from PR #155).
- Or revert this PR (1 commit) to restore v0.12 as the file-level
  default.
- Production v0.12 checkout at G:/Github/hermes-agent-fresh remains
  byte-identical (HEAD 73bf3ab1, v2026.4.30, status clean) — never
  modified by canary work.

Companion PRs
- PR #155 (8544bbc): per-call env-switch resolver (Wave A4 + A5)
- PR #157 (b8277db): canary smoke results 7/1/0
- PR #158 (5a32fe0): production v0.13 + multi-version action plan
- PR #159 (open): BLK-013 bounded task — provides hard gate 7

Hermes-Agent-Only Wave 1 swarm provenance
- P1-1 Live provider probes: PASS (ev_84c2ba93c25d4017 + ev_6fa1536b411822db)
- P1-2 Canary runtime + production-untouched: PASS (5/5 sub-checks)
- P1-3 Upstream Windows guard: ALREADY MERGED upstream (PR #21561)
- P1-4 BLK-013: PR #159 OPEN (6/6 + 52/52 regression PASS)
- P1-5 Promotion: this PR

References
- Upstream v0.13.0 release: https://github.com/NousResearch/hermes-agent/releases/tag/v2026.5.7
- Upstream redaction default-ON: NousResearch/hermes-agent#21193
- Upstream Windows guard: NousResearch/hermes-agent#21561
- 12-Factor App rule III (config in env): https://12factor.net/config

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(hermes-agent): version registry for v0.12 + v0.13 (Wave 2 P2-1)

Adds services/agent_version_registry.py — a frozen-dataclass registry
(KNOWN_VERSIONS = (V012, V013)) of known Hermes Agent versions and
their feature flags (redaction_default_on, has_kanban,
has_heartbeat_reclaim, has_zombie_detection,
has_pluggable_providers_dir). Pairs with agent_checkout.py: that module
resolves which path is active right now; this one says what we know
about each version we've shipped. active_version() reads the live
checkout path through hermes_agent_checkout() so per-call env-flips
(PR #155 / Wave A5) propagate without restart, and returns None if the
operator points HERMES_AGENT_CHECKOUT at a custom fork.

Reads agent_checkout module-level constants only; does not edit it.

Pinned by tests:
- KNOWN_VERSIONS = (v0.12@v2026.4.30, v0.13@v2026.5.7) in order.
- v0.13.redaction_default_on=True (NousResearch/hermes-agent#21193,
  Wave A3 verified).
- v0.12.redaction_default_on=False (Wave 2 prior swarm Agent 2).
- active_version() == v0.13 with env unset (post-PR #160 default).
- active_version() == v0.12 when env=hermes-agent-fresh.
- active_version() is None for unknown checkouts.
- FrozenInstanceError on any field mutation (no cross-version leak).

LoC: 89 lines source (67 non-blank/non-comment) + 114 lines tests.
Tests: 13/13 pass; combined with test_agent_checkout_resolver.py 24/24 pass.

References:
- Python @DataClass(frozen=True): https://docs.python.org/3/library/dataclasses.html#frozen-instances
- Django AppConfig metadata pattern: https://docs.djangoproject.com/en/5.1/ref/applications/

Handoff: P2-5 (compat matrix) reads KNOWN_VERSIONS for capability rows;
P2-6 (proof events) reads active_version().label/upstream_tag for the
version_tag field on every event.

* feat(hermes-agent): version-tag proof events with active version + upstream tag (Wave 2 P2-6)

Every persisted proof_events row now carries the active Hermes Agent
version (v0.12 / v0.13), the upstream tag (v2026.4.30 / v2026.5.7),
and the resolved checkout path. Without this, post-promotion forensic
queries cannot tell which Hermes Agent version emitted any given
event — a gap that becomes load-bearing the moment an operator flips
HERMES_AGENT_CHECKOUT mid-process (per-call resolver, PR #155).

Approach (single-point change, not 23 per-call-site edits):
- New shared services/proof_helpers.py exposes proof_version_fields()
  and attach_version_fields(payload). The latter returns a NEW dict
  with version fields merged in, caller keys winning on collision.
- Three identical _append_proof_event helpers
  (agent_updates.py / desktop_updates.py / jobs.py) augmented at the
  single SQL-insertion site. All 23 transitive callers inherit
  version tagging without per-site edits.

Backward compat: legacy rows have no version_label key. Reader code
must use payload.get("version_label", "unknown") semantics.
Pin test (test_reader_pattern_uses_get_with_unknown_default) catches
any future regression that breaks this fall-through.

Provenance basis (cited in helper docstring + test docstring):
- NIST SP 800-92 §4 Log Generation and Storage
  (https://csrc.nist.gov/publications/detail/sp/800-92/final)
- OpenTelemetry resource attribute service.version
  (https://opentelemetry.io/docs/specs/semconv/resource/#service)

Tests: 13 new in test_proof_event_version_tagging.py.
Suite: pytest test_agent_version_registry.py +
       test_agent_checkout_resolver.py +
       test_proof_event_version_tagging.py = 37 passed in 1.46s.

Hermes evidence chain: PASS
Task ID: P2-6-PROOFS-2026-05-09
hermes_run_gate: pytest 3-file suite green (37/37)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ghenghis added a commit that referenced this pull request May 10, 2026
…3-5) [re-target of #170] (#174)

* feat(hermes-agent): promote v0.13 to production default (Wave 1 P1-5; 7/7 gates green)

Wave 1 of the v0.13 production promotion swarm cleared all 7 hard gates.
This PR flips the resolver default from v0.12 (hermes-agent-fresh) to
v0.13 (hermes-agent-v013-canary). v0.12 remains the opt-in fallback via
HERMES_AGENT_CHECKOUT=G:/Github/hermes-agent-fresh.

Hard gates (all PASS, evidence below)
1. MiniMax accepted=true       — P1-1 (ev_84c2ba93c25d4017)
2. DeepSeek accepted=true      — P1-1 (ev_6fa1536b411822db)
3. Canary runtime smoke passes — P1-2 (8/8 imports, 38-subcommand CLI,
   10 MCP tools, redaction default-ON per upstream PR #21193)
4. v0.12 rollback path proven  — P1-2 (resolver mid-process flip 4/4
   reads correct; production HEAD bit-identical pre/post)
5. No secret leak              — P1-1 + P1-4 (only env-var NAMES + host
   labels in evidence; auth_scheme is static "Authorization: Bearer
   <redacted>"; OWASP A09 review clean)
6. No v0.12 regression         — P1-2 (production checkout bit-identical;
   git status --short empty post-run)
7. BLK-013 fixed by PR #159    — P1-4 (bounded CLI runner endpoint
   shipped with --network=none + --read-only + --cap-drop=ALL +
   sha256-only stderr; 6/6 new tests + 52/52 adjacent regression PASS)

Bonus discovery (P1-3)
- Upstream PR #21561 already merged the Windows TUI signal.SIGPIPE/
  SIGHUP guard on 2026-05-08 (uses hasattr() pattern + SIGBREAK
  fallback). Canary at v2026.5.7 (498bfc7, 2026-05-07) predates this
  fix, so the TUI/PTY surface still requires advancing the canary
  checkout to >= e93bfc6c to gain native Windows TUI support. Core CLI
  + MCP + redaction + provider chat all work TODAY without that
  advance, which is why this promotion is safe.

Patch
- DEFAULT_AGENT_CHECKOUT now points to v0.13 (canary path).
- New V012_FALLBACK_CHECKOUT constant exposes the v0.12 path for
  operators + tests that need explicit fallback semantics.
- CANARY_AGENT_CHECKOUT retained as alias for back-compat.

Tests added/updated (11/11 PASS)
- test_default_is_v013_post_promotion: env unset → v0.13
- test_v012_fallback_via_env: env=hermes-agent-fresh → v0.12
- test_resolver_constants_are_correct: DEFAULT == CANARY == v0.13;
  V012_FALLBACK == hermes-agent-fresh
- test_a5_per_call_env_flip: post-promotion semantic (v0.13 ↔ v0.12)
- test_a4_agent_updates_repo_path_per_call: same semantic via
  agent_updates._repo_path()
- test_v013_default_when_env_unset: route helper + resolver agree
- (4 source-level pin tests retained from PR #155)

Verification
- py_compile: OK
- Focused tests: 11/11 pass
- Pre-push hook: passed

Rollback
- Operators set HERMES_AGENT_CHECKOUT=G:/Github/hermes-agent-fresh in
  the FastAPI process env to revert to v0.12 mid-process (no restart;
  per-call resolver from PR #155).
- Or revert this PR (1 commit) to restore v0.12 as the file-level
  default.
- Production v0.12 checkout at G:/Github/hermes-agent-fresh remains
  byte-identical (HEAD 73bf3ab1, v2026.4.30, status clean) — never
  modified by canary work.

Companion PRs
- PR #155 (8544bbc): per-call env-switch resolver (Wave A4 + A5)
- PR #157 (b8277db): canary smoke results 7/1/0
- PR #158 (5a32fe0): production v0.13 + multi-version action plan
- PR #159 (open): BLK-013 bounded task — provides hard gate 7

Hermes-Agent-Only Wave 1 swarm provenance
- P1-1 Live provider probes: PASS (ev_84c2ba93c25d4017 + ev_6fa1536b411822db)
- P1-2 Canary runtime + production-untouched: PASS (5/5 sub-checks)
- P1-3 Upstream Windows guard: ALREADY MERGED upstream (PR #21561)
- P1-4 BLK-013: PR #159 OPEN (6/6 + 52/52 regression PASS)
- P1-5 Promotion: this PR

References
- Upstream v0.13.0 release: https://github.com/NousResearch/hermes-agent/releases/tag/v2026.5.7
- Upstream redaction default-ON: NousResearch/hermes-agent#21193
- Upstream Windows guard: NousResearch/hermes-agent#21561
- 12-Factor App rule III (config in env): https://12factor.net/config

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(hermes-agent): version registry for v0.12 + v0.13 (Wave 2 P2-1)

Adds services/agent_version_registry.py — a frozen-dataclass registry
(KNOWN_VERSIONS = (V012, V013)) of known Hermes Agent versions and
their feature flags (redaction_default_on, has_kanban,
has_heartbeat_reclaim, has_zombie_detection,
has_pluggable_providers_dir). Pairs with agent_checkout.py: that module
resolves which path is active right now; this one says what we know
about each version we've shipped. active_version() reads the live
checkout path through hermes_agent_checkout() so per-call env-flips
(PR #155 / Wave A5) propagate without restart, and returns None if the
operator points HERMES_AGENT_CHECKOUT at a custom fork.

Reads agent_checkout module-level constants only; does not edit it.

Pinned by tests:
- KNOWN_VERSIONS = (v0.12@v2026.4.30, v0.13@v2026.5.7) in order.
- v0.13.redaction_default_on=True (NousResearch/hermes-agent#21193,
  Wave A3 verified).
- v0.12.redaction_default_on=False (Wave 2 prior swarm Agent 2).
- active_version() == v0.13 with env unset (post-PR #160 default).
- active_version() == v0.12 when env=hermes-agent-fresh.
- active_version() is None for unknown checkouts.
- FrozenInstanceError on any field mutation (no cross-version leak).

LoC: 89 lines source (67 non-blank/non-comment) + 114 lines tests.
Tests: 13/13 pass; combined with test_agent_checkout_resolver.py 24/24 pass.

References:
- Python @DataClass(frozen=True): https://docs.python.org/3/library/dataclasses.html#frozen-instances
- Django AppConfig metadata pattern: https://docs.djangoproject.com/en/5.1/ref/applications/

Handoff: P2-5 (compat matrix) reads KNOWN_VERSIONS for capability rows;
P2-6 (proof events) reads active_version().label/upstream_tag for the
version_tag field on every event.

* docs+feat(hermes-agent): provider compat matrix per version (Wave 4 P3-5)

Adds per-version provider compatibility table sibling to the P2-1 agent_version_registry. Frozen-tuple shape, no mutation of P2-1's registry. Documents which providers are reachable on Hermes Agent v0.12 vs v0.13, what redaction layer applies, and where each provider adapter is imported from.

Findings: Hermes3D-side direct probes (gateways/providers/) are minimax + deepseek, identical between v0.12 and v0.13 (those modules are Hermes3D code, not vendored from upstream). Smoke 3 + Smoke 4 PASS (2026-05-09). Upstream agent/models_dev.py:PROVIDER_TO_MODELS_DEV — 33 entries, byte-identical between v0.12 and v0.13. Cross-version diff is BEHAVIOURAL: agent/redact.py default flipped OFF -> ON in v0.13 (upstream issue #21193). OpenCode + OpenHands are CLI runners (services/code_history.py), not LLM providers.

Module: services/agent_version_provider_compat.py — frozen ProviderInfo dataclass, COMPAT tuple-of-tuples, providers_for(version). Doc: HERMES_AGENT_PROVIDER_COMPAT_MATRIX_2026-05-09.md (~2400 words). Tests: 14/14 pass; combined with registry tests 27/27 pass.

Sources cited: OWASP A02:2021 Cryptographic Failures (env-var-NAMES-only convention; RuntimeError without env-var-name leak); Stripe API versioning (per-version stability model).

LoC: 134 source + 260 tests + 2421-word doc. Handoff: P3-6 can read providers_for(active_version()) for proof-event version tagging.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant