Skip to content

feat(hermes-tag): add additive governance and continuity kernel - #91111

Open
andrexibiza wants to merge 2 commits into
NousResearch:mainfrom
andrexibiza:campaign/hermes-tag-kernel
Open

feat(hermes-tag): add additive governance and continuity kernel#91111
andrexibiza wants to merge 2 commits into
NousResearch:mainfrom
andrexibiza:campaign/hermes-tag-kernel

Conversation

@andrexibiza

@andrexibiza andrexibiza commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds the additive gateway.hermes_tag kernel that supplies the horizontal authority model required by the Slack Feature Parity / @Hermes Tag campaign without modifying existing gateway, tool, provider, or Slack effect paths.

The kernel separates identity, scope, policy, approval, capability lease, atomic budget, receipt, continuity, fact, and task-local runtime authority into independently testable modules. It defaults to denial, binds every consequential decision to canonical scope and argument digests, and keeps runtime integration as a separate stacked publication rather than growing gateway/run.py or the Slack adapter.

Architecture

  • canonical principals, external identities, Slack/session surfaces, scopes, actions, decisions, approvals, leases, facts, and continuity envelopes;
  • stable external-identity binding with explicit guest admission boundaries;
  • deterministic capability registry and fail-closed scope validation;
  • deny-by-default policy evaluation with deny precedence and exact approval consumption;
  • signed, scoped, argument-bound, expiring, one-shot capability leases;
  • SQLite-backed atomic budget reservations and append-only receipt chaining;
  • replay-resistant continuity and bounded context/fact projection;
  • task-local ContextVar authority propagation for later gateway/tool/provider seams;
  • obligation registry for precondition and completion evidence;
  • service and middleware surfaces for future admission wiring.

Scope boundary

This PR intentionally does not claim runtime delivery. It introduces no production consumer outside gateway.hermes_tag, performs no Slack API call, does not intercept gateway turns, and does not grant an existing tool/process/provider authority. The next stacked slice will wire one real turn-admission consumer; later slices will bind effect paths and Slack-native behavior.

That separation is load-bearing: a library package plus green tests is implementation evidence, not a released @Hermes Tag capability.

Tests

The branch includes adversarial coverage for:

  • canonical model and digest stability;
  • identity collision and guest behavior;
  • scope containment and incomplete-scope rejection;
  • continuity replay/cycle handling;
  • deny precedence, approval exactness, expiry, and one-shot consumption;
  • atomic concurrent budget reservation;
  • lease tamper, expiry, scope, argument, and replay rejection;
  • receipt-chain verification;
  • obligations and completion evidence;
  • task-local authority isolation.

Topology

Part of #79772
Part of #80338
Implementation successor to the historical Hermes Tag packet evidence recorded by #91036.
Independent of #90307 at runtime; #91036 remains the campaign publication authority.

@alt-glitch alt-glitch added type/feature New feature or request comp/gateway Gateway runner, session dispatch, delivery needs-decision Awaiting maintainer decision before any implementation P3 Low — cosmetic, nice to have labels Aug 20, 2026

@andrexibiza andrexibiza left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head 251db038ef61e78f0c269aee22eccd7a626cc48e as the HT-01 substrate, not as a runtime-delivery claim. The additive boundary is real: all 28 changed files stay under gateway/hermes_tag/ + focused tests, and exact-head CI 32419325252, Docker 32419324150, and Nix 32419324118 are green. The canonical model/digest work, tenant-qualified identities, deny precedence, exact-intent leases, one-shot lease reservation, profile-local SQLite authority, replay fences, and failure-only shadow boundary are materially coherent.

I found two authority-boundary blockers before this becomes the contract HT-02 consumes.

1. bind_authority() can pair a new admission with stale outer decision/lease authority

gateway/hermes_tag/runtime.py sets _CURRENT_ADMISSION unconditionally, but it sets _CURRENT_DECISION and _CURRENT_LEASE only when the incoming RuntimeAuthority fields are non-None:

admission_token = _CURRENT_ADMISSION.set(authority.admission)
if authority.decision is not None:
    decision_token = _CURRENT_DECISION.set(authority.decision)
if authority.lease is not None:
    lease_token = _CURRENT_LEASE.set(authority.lease)

That means a nested bind_authority(RuntimeAuthority(admission=B)) inside an outer authority for A leaves A's decision and lease visible while B is the current admission. capture_authority() then constructs an apparently valid RuntimeAuthority(admission=B, decision=A, lease=A) because the dataclass has no cross-object invariant either. This is exactly the other side of task-local isolation: ContextVar separation prevents cross-task bleed, but partial rebinding permits cross-scope bleed inside one task/context.

Required fix: bind all three ContextVars on every bind_authority() entry, including explicitly setting missing decision/lease to None, then reset all three on exit. Also make RuntimeAuthority (or the binding boundary) reject inconsistent tuples: admission principal/scope/continuity must match the decision and lease; decision↔lease IDs/digests must match when both are present. Add the adversarial witness A(admission+decision+lease) -> nested B(admission only) -> current_decision/current_lease are None -> exit -> A restored, plus mismatched A/B tuple rejection. HT-02 should never have to remember that invariant itself.

2. The public approval facade bypasses the approval capability/policy model it defines

The registry correctly declares approval.grant as HIGH risk with identity.authenticate, intent.exact, expiry.require, and receipt.append obligations. But HermesTagKernel.grant_approval() directly delegates to ApprovalStore.grant(). That store accepts caller-supplied principal_id, approver_id, intent digest, and scope digest; the only approver check is the SQLite FK proving the named principal exists. No approval.grant policy decision, authenticated approver evidence, role/authority check, or lease is consumed. The current high-risk test even demonstrates self-grant by passing the same principal as subject and approver.

So the kernel currently has two incompatible authorities: the capability registry says approval grants are governed effects, while the high-level grant API mints the exact token that unlocks HIGH/CRITICAL actions without traversing that governance path. Once HT-02 exposes an approval interaction, calling the obvious facade would make identity.authenticate metadata decorative rather than authoritative.

Required fix: make the externally consumable approval-grant path itself authority-bearing. For example, require an authenticated approver admission + policy-authorized approval.grant decision/lease and verify its pre-effect obligations before writing the grant; keep any raw store mutation private/internal. Self-approval can still be a deliberate policy choice if wanted, but it must be an explicit matched rule/authority, not a property of knowing an existing principal ID. Add a negative witness showing an unprivileged/self-nominated caller cannot mint a grant merely by supplying valid IDs/digests.

Topology / merge order

This PR is complementary implementation authority, not a replacement for #91036. #79772 remains the campaign meta-owner; #80338 remains the flagship acceptance matrix; #91036 remains the executable publication/reconciliation owner; #90307 remains the external publication-contract dependency for #91036. #91111 should not promote any capability to released: its own no-runtime-consumer boundary is correct. Once this implementation is accepted, #91036 does need reconciliation so its gap/candidate_* state reflects the new current repository candidate rather than the pre-#91111 packet-only topology.

Current main is e30388e409a95b30d6acc4c1f6c0cd8d0c66393c; this head is 8 commits behind from merge base 4a5b6dd4512a10c3c18da3e5b9e5c7fb681cbfbb. Those intervening changes are currently confined to Desktop Hermes Bots/tests + contributor metadata, so I do not see a semantic collision with this package, but the landing head still needs a current-main rebase and fresh exact-head receipts after these fixes.

The historical packet provenance in the PR/README is appropriately described as reconstruction evidence rather than byte-for-byte recovered implementation; I found no external contributor lineage here that should be collapsed as duplicate work.

andrexibiza added a commit to andrexibiza/hermes-agent that referenced this pull request Aug 20, 2026
Run the exact Hermes Tag package and focused tests without changing the upstream PR diff.

Signed-off-by: Andrex Ibiza, MBA <andrexibiza@gmail.com>
andrexibiza added a commit to andrexibiza/hermes-agent that referenced this pull request Aug 20, 2026
Make the fork-only verifier report its exact-commit result through a queryable commit status.

Signed-off-by: Andrex Ibiza, MBA <andrexibiza@gmail.com>
andrexibiza added a commit to andrexibiza/hermes-agent that referenced this pull request Aug 20, 2026
Run compile, Ruff, and the exact Hermes Tag test suite against the published PR branch, then attach the receipt to that exact head.

Signed-off-by: Andrex Ibiza, MBA <andrexibiza@gmail.com>
andrexibiza added a commit to andrexibiza/hermes-agent that referenced this pull request Aug 20, 2026
Temporary fork-only exact-head verification runner. Remove after the receipt is captured.
andrexibiza added a commit to andrexibiza/hermes-agent that referenced this pull request Aug 20, 2026
Keep the focused fork verifier available for the cleanup merge and any exact-head rerun needed during repair.

Signed-off-by: Andrex Ibiza, MBA <andrexibiza@gmail.com>
Add the profile-local Hermes Tag identity, continuity, policy, capability, lease, budget, obligation, provenance, and receipt substrate on current main.

Close the two authority-boundary defects identified on NousResearch#91111:
- task-local admission, decision, and lease state now binds and restores atomically, with cross-scope/principal/continuity tuples rejected;
- approval creation is now a governed approval.grant effect requiring authenticated durable identity, explicit policy allow, an exact argument-bound lease, obligation evidence, one-shot completion, and rollback on failure.

Hide raw approval mutation from the public package facade and add adversarial regressions for nested context reuse, self-approval, target substitution, and exact one-time approval consumption.

Part of NousResearch#79772
Part of NousResearch#80338

Signed-off-by: Andrex Ibiza, MBA <andrexibiza@gmail.com>
@andrexibiza
andrexibiza force-pushed the campaign/hermes-tag-kernel branch from 6babece to 385e5b0 Compare August 20, 2026 22:37
andrexibiza added a commit to andrexibiza/hermes-agent that referenced this pull request Aug 20, 2026
Temporarily patch the stale rollback tests through the new authenticated, policy-authorized, leased approval path, verify them, and push the implementation receipt to the PR branch.

Signed-off-by: Andrex Ibiza, MBA <andrexibiza@gmail.com>
andrexibiza added a commit to andrexibiza/hermes-agent that referenced this pull request Aug 20, 2026
Temporary fork-only repair workflow. Remove after the target branch is green.

Copy link
Copy Markdown
Contributor Author

Implemented the authority-boundary fixes at exact head aaf1bf9316c438593e0cdfc1179742b13d17d45c.

What changed:

  • RuntimeAuthority now rejects cross-principal, cross-scope, cross-continuity, non-ALLOW, and decision↔lease linkage mismatches.
  • bind_authority() now replaces all three ContextVars on entry, including explicit None, and restores the complete outer tuple on exit.
  • The individual admission/decision binders also clear stale derived authority, so the same bug cannot reappear through a different context-manager combination.
  • grant_approval() no longer accepts a caller-nominated approver_id and no longer directly exposes raw approval storage. It now requires a durable authenticated TurnAdmission, an explicitly policy-allowed approval.grant intent, a signed exact-argument lease, verified pre-effect obligations, one-shot lease completion, and compensation if completion fails.
  • Approval issuance uses a dedicated policy evaluation path to avoid recursive self-approval from the generic HIGH-risk floor while retaining default deny, deny precedence, role/principal selectors, and explicit REQUIRE_APPROVAL rules.
  • ApprovalStore was removed from the package/kernel public facade.
  • Added adversarial witnesses for nested A→B authority clearing/restoration, mismatched tuple rejection, unprivileged self-nomination denial, target substitution rejection, exact approval consumption, and governed rollback paths.
  • Rebuilt directly on current main ee000768cef4dc9399f32c88b507104ce15400dd.

Exact-head checks:

  • CI 32426102683 — success
  • Docker Build, Test, and Publish 32426102310 — success
  • Nix flake check 32426102250 — success

Copy link
Copy Markdown
Contributor Author

Exact-head verification: aaf1bf9316c438593e0cdfc1179742b13d17d45c is green in CI 32426102683, Docker 32426102310, and Nix 32426102250. There are 0 unresolved formal review threads. This verifies the current PR head; it does not expand the PR's stated scope.

Copy link
Copy Markdown
Contributor Author

@teknium1 architecture/maintainer review requested on exact head aaf1bf9316c438593e0cdfc1179742b13d17d45c.

Current GitHub truth: open, non-draft, mergeable. Exact-head CI 32426102683, Docker 32426102310, and Nix 32426102250 are all successful.

The key review question is not whether the kernel's unit surface is green; it is whether this additive authority model is the one future consumers must consume. This PR itself grants no Slack/tool/provider effect authority and must not be credited as released @Hermes Tag capability. The next runtime slice should consume the kernel's identity/scope/policy/approval/lease/receipt contract at a real admission/effect seam rather than rebuilding parallel authority in gateway/run.py or Slack/provider code.

#91036 remains campaign publication authority; #90307 remains the executable parity-contract validator. Please review this as the horizontal kernel boundary, not as feature-delivery closure.

Copy link
Copy Markdown
Contributor Author

@teknium1 — architecture review requested on exact head aaf1bf9316c438593e0cdfc1179742b13d17d45c.

Direct receipts are CI 32426102683, Docker 32426102310, and Nix 32426102250, all successful; GitHub currently reports the PR mergeable and there are no unresolved inline threads.

The acceptance question is deliberately narrower than Slack delivery: does this package establish one coherent identity → scope → policy → approval → lease → budget → receipt → continuity authority kernel without granting an ambient effect path or duplicating gateway/Slack ownership?

The next runtime slice must consume these exact contracts at real pre_gateway_dispatch / pre_tool_call seams. This PR alone remains implementation evidence, not released @Hermes Tag capability.

Copy link
Copy Markdown
Contributor Author

Next-consumer contract published in #91913

The authority-continuity manifest records this PR as the active Hermes Tag implementation owner but keeps its status explicitly kernel-only.

The next required publication is now machine-readable:

pre_gateway_dispatch admission consumer
  → same immutable authority decision in task-local context
  → real pre_tool_call effect consumer
  → typed allow / deny / approval-required receipt

Shadow evaluation is valid runtime evidence but cannot claim effect enforcement. Concurrent task isolation, stale authority generation, approval replay, lease replay, and argument-digest mismatch remain fail-closed acceptance requirements.

Contract PR: #91913
Exact head: 170a3a0e67034abd7d6a2c69a16c292b4781720f.

@Enough1122

Copy link
Copy Markdown
Contributor

AI code review — automated review for reference, author can ignore or act on any point.

As an isolated substrate this is coherently built: the invariant list in the README is explicit and mostly testable (fail-closed on unknown capabilities, deny-overrides-allow, one-time approvals, atomic budget reservation, single-use HMAC leases bound to the full authority tuple, hash-chained receipts, signing material by reference), and the test suite exercises authority boundaries rather than just happy paths. The honesty of the provenance boundary section — stating plainly this is a reconstruction and that runtime insertion remains a separate contract — is appreciated.

That said, three structural points deserve attention before this hardens:

  1. Unwired-by-design code has a shelf life. No existing file is modified; nothing calls verify_effect() or the middleware, so today this is ~5k lines of inert library whose real contract only materializes at HT-02. Between now and then, every refactor of gateway seams it eventually touches can silently drift the assumptions here (the tests validate the subsystem in isolation, not against the live seams). Consider gating the package behind an explicit experimental marker, or landing it with the first minimal shadow-admission consumer so drift is caught by CI rather than by HT-02 review.
  2. Namespace implies coupling it doesn't have. gateway/hermes_tag/ sits inside the gateway package while importing nothing from it; a top-level package (or plugin-style layout) would keep the gateway waist narrow and make the behavior-neutrality claim structurally true.
  3. Ledger lifecycle: the profile-local SQLite store needs a stated schema-version/migration story and a retention policy for receipts/facts — append-only hash chains grow unboundedly, and nothing in the diff prunes.

Nit: omniscience.py (provenance-required fact retrieval with sensitivity filtering) undersells its actual, narrower behavior — a name like fact_store.py would age better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery needs-decision Awaiting maintainer decision before any implementation P3 Low — cosmetic, nice to have type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants