Skip to content
This repository was archived by the owner on May 26, 2026. It is now read-only.

feat(kora): KR-IDENTITY-MULTI-TENANT-PROOF — Marvin plugin validates Option C#203

Merged
rafe-walker merged 1 commit into
feature/phase2-upgradesfrom
feat/kora-KR-IDENTITY-MULTI-TENANT-PROOF-AND-DAEMON-AUDIT-UPDATE-MEGABUCKET
May 24, 2026
Merged

feat(kora): KR-IDENTITY-MULTI-TENANT-PROOF — Marvin plugin validates Option C#203
rafe-walker merged 1 commit into
feature/phase2-upgradesfrom
feat/kora-KR-IDENTITY-MULTI-TENANT-PROOF-AND-DAEMON-AUDIT-UPDATE-MEGABUCKET

Conversation

@rafe-walker

Copy link
Copy Markdown
Owner

Summary

Build Marvin as a runnable bundled plugin to validate the KR-PLUGIN-IDENTITY Option C architecture from #199 actually works for a non-Kora identity. Proves the pip-installable Kora bundle vision before any external IsoKron user tries it.

Companion docs PR: https://github.com/rafe-walker/kora-docs/pull/new/docs/kora-KR-IDENTITY-MULTI-TENANT-PROOF-AND-DAEMON-AUDIT-UPDATE-MEGABUCKET (MARVIN_DEMO_TRANSCRIPT.md + daemon audit doc update).

What landed

`plugins/marvin/` — new Hermes bundled plugin (~85 LOC Python + 2 markdown identity files):

  • `plugin.yaml` — manifest declaring the `pre_agent_identity_set` hook
  • `init.py` — `register(ctx)` wires the provider via `ctx.register_identity_provider`
  • `MARVIN.md` — Paranoid Android persona (SOUL.md analog)
  • `marvin_system_prompt.md` — reasoning-engine prompt; explicitly NOT Kora

Live demo transcript (captured this session)

```
SCENARIO 1 — Kora-only (Marvin NOT installed)
Resolved identity: agent='Kora' plugin='kora_hermes'

SCENARIO 2 — Marvin-only (Kora DISABLED)
Resolved identity: agent='Marvin' plugin='marvin' ← THE proof

SCENARIO 3a — BOTH; Marvin first in plugins.enabled
Resolved identity (first-non-None wins): agent='Marvin'

SCENARIO 3b — BOTH; Kora first in plugins.enabled
Resolved identity (first-non-None wins): agent='Kora'

SCENARIO 0 — NO plugin loaded
Resolved identity: None → engine file-read fallback
```

All 4 scenarios pass per spec. The Marvin-only scenario is the critical one — it's what an external IsoKron user installing `pip install marvin-runtime` would see.

Test plan

  • 11 new tests in `tests/plugins/test_marvin_multi_tenant_proof.py` covering the 4 scenarios + manifest pin + identity-files-exist pin + register-wires-correctly pin + would-engine-init-consume-Marvin's-spec pin + no-provider fallback pin
  • 544/544 focused regression set green (all identity tests + listener tests + plugin tests)
  • 60 broader-sweep failures verified pre-existing on baseline (4657 baseline → 4721 with my +64 new tests; same failures before/after; test-isolation issue unrelated to this work)

Gaps surfaced (documented in MARVIN_DEMO_TRANSCRIPT.md §6)

None architecturally blocking. Listed in priority of operator-attention:

  1. SOUL.md content carried but not yet engine-consumed — IdentitySpec.soul_md_content flows through but only system_prompt_content reaches the Anthropic SDK. agent/prompt_builder.py:load_soul_md still reads filesystem directly. Future refactor unifies; not pressing.
  2. No end-to-end test constructing a real engine — structural pin from feat(kora): KR-PLUGIN-IDENTITY-OPTION-C-AND-DAEMON-PHASE2 — full identity-as-plugin + 8-listener migration #199 + 11 unit tests + live demo cover the surface; constructing a real engine would require Anthropic creds in test env.
  3. Hot-reload still not supported — matches existing identity-loading contract; not a regression.
  4. No actual `pip install` publish path validated yet — mechanically the same surface as today's bundled-plugin discovery; cheap to validate when operator dispatches the bundle bucket.

Operator activation (for an external IsoKron user)

```bash

Drop the plugin under HERMES_HOME/plugins/

cp -r plugins/marvin ~/.hermes/plugins/

Enable in config

cat >> ~/.hermes/config.yaml <<EOF
plugins:
enabled:
- marvin

…Option C

Build Marvin as a runnable bundled plugin to validate the KR-PLUGIN-IDENTITY Option C architecture from #199 actually works for a non-Kora identity. Proves the pip-installable Kora bundle vision before any external IsoKron user tries it.

plugins/marvin/ — new bundled plugin (Hermes plugin SDK discovery via plugins/<name>/ convention)
  - plugin.yaml — manifest declaring the pre_agent_identity_set hook
  - __init__.py — register(ctx) → ctx.register_identity_provider(marvin_identity_provider). Reads MARVIN.md + marvin_system_prompt.md eagerly at import time.
  - MARVIN.md — Paranoid Android persona (SOUL.md analog for Marvin's identity)
  - marvin_system_prompt.md — reasoning-engine prompt; explicitly says "you are not Kora"

11 tests in tests/plugins/test_marvin_multi_tenant_proof.py validate end-to-end:
  Scenario 1 — Kora-only: bare Hermes without Marvin → engine uses Kora's identity (pre-#199 behavior preserved; no regression)
  Scenario 2 — Marvin-only: Hermes + Marvin plugin (NO Kora) → engine uses Marvin's identity. THIS is the architecture validation — proves Option C supports a non-Kora identity via the same hook surface, with NO code in Kora's fork changing.
  Scenario 3a — Both plugins, Marvin first in plugins.enabled → Marvin wins (first-non-None-wins by FIFO)
  Scenario 3b — Both plugins, Kora first → Kora wins (same code path, opposite outcome from registration order; operator controls via config.yaml ordering)
  Plus: manifest pin, identity-files-exist pin, register-wires-correctly pin, would-engine-init-consume-marvin's-spec pin, no-provider fallback pin

Live demo captured in companion kora-docs PR (MARVIN_DEMO_TRANSCRIPT.md) — all 4 scenarios reproducible.

Tests: 11/11 Marvin tests + 544/544 focused regression set green. 60 broader-sweep failures verified pre-existing on baseline (4657 baseline → 4721 with my +64 new tests; same 60 failures present pre-PR; test-isolation issue unrelated to this work).

Gaps surfaced (documented in MARVIN_DEMO_TRANSCRIPT.md §6):
  - SOUL.md content carried but not yet engine-consumed (future refactor; not pressing)
  - No end-to-end test constructing a real engine (requires Anthropic creds; structural pin from #199 covers source-level wiring)
  - Hot-reload not supported (matches existing identity-loading contract; not a regression)
  - No actual pip-install publish path validated yet (mechanically the same surface; cheap to validate when operator dispatches the bundle bucket)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@rafe-walker rafe-walker merged commit 06544c8 into feature/phase2-upgrades May 24, 2026
2 of 4 checks passed
@rafe-walker rafe-walker deleted the feat/kora-KR-IDENTITY-MULTI-TENANT-PROOF-AND-DAEMON-AUDIT-UPDATE-MEGABUCKET branch May 24, 2026 09:30
rafe-walker added a commit that referenced this pull request May 24, 2026
…n pip POC + structural FATAL flag (#204)

Two deliverables closing #203's §6.4 gap (no pip-install path validated) + #200's deferred follow-up (FATAL contract was documentation-only). Per Joshua's amended feedback-local-first-upstream-after: structure only, no PyPI publish, no upstream PR.

Deliverable A — Pip-packaging foundation

plugins/marvin/ restructured to relocatable src/ layout:

  plugins/marvin/
  ├── pyproject.toml          # NEW — setuptools build + hermes_agent.plugins entry point
  ├── README.md               # NEW — operator-facing install doc
  ├── plugin.yaml             # KEPT — Hermes bundled-plugin discovery
  ├── __init__.py             # REPLACED — sys.path-adjusting compat shim
  └── src/marvin/
      ├── __init__.py         # canonical module (relocated)
      └── data/
          ├── MARVIN.md       # relocated
          └── marvin_system_prompt.md

pyproject.toml declares "[project.entry-points.hermes_agent.plugins] marvin = marvin:register" — the exact entry-point group Hermes's _scan_entry_points already reads. Package_data includes the markdown files so the wheel ships relocatable identity assets.

The in-tree compat shim at plugins/marvin/__init__.py prepends plugins/marvin/src to sys.path + re-exports register/marvin_identity_provider from the canonical module. Existing 11 multi-tenant tests from #203 continue to pass with only one updated assertion (data files moved to src/marvin/data/).

Live dry-run install validated this session:
  $ uv build --wheel → marvin_runtime-0.1.0a1-py3-none-any.whl
  $ pip install <wheel> into fresh /tmp/marvin-dry-install venv
  $ importlib.metadata.entry_points() discovers ('marvin', 'marvin:register')
  $ marvin.__file__ = /tmp/.../site-packages/marvin/__init__.py (NOT in-tree)
  $ provider call → IdentitySpec(agent_name='Marvin', soul_chars=1306, system_chars=1486)
  $ register(stub_ctx) → wires identity provider via stub PluginContext ✓

CI-runnable regression guard: tests/plugins/test_marvin_pip_install_dry_run.py — 6 tests covering pyproject structure pins, wheel content pins, entry-point declaration pin, METADATA Requires-Python pin. ~0.5s per run via uv build (or python -m build if uv unavailable).

Companion kora-docs deliverables (separate PR):
  - kora_docs/14_research/kora_pip_packaging_2026-05-24/AUDIT.md — concrete shopping list for the future 4-package Kora restructure (kora-runtime + kora-cli + kora-cockpit + kora-promote-loops). 7-9 CC-days estimate across 4 sequential phases. 4 open questions for operator (Hermes pip-installability, IsoKron client packaging, schema migrations, versioning cadence).
  - kora_docs/14_research/plugin_identity_option_c_2026-05-24/MARVIN_DEMO_TRANSCRIPT.md §7 addendum — gap §6.4 closed; full wheel build + dry-run install transcript captured.

Deliverable B — Structural FATAL flag

Replaces #200's documentation-driven FATAL contract with structural enforcement:

agent/background_daemon_registry.py:
  - New field BackgroundDaemonEntry.fatal_on_startup_failure: bool = False
  - Field docstring documents the semantics + the looked-up-by-name path for the Path B thin-shim shape

kora_cli/plugins.py:
  - PluginContext.register_background_daemon accepts the new fatal_on_startup_failure kwarg + threads it into the BackgroundDaemonEntry construction

kora_cli/daemon.py:
  - New method DaemonCoordinator._is_startup_failure_fatal(listener_name):
    - Looks up the listener's BackgroundDaemonEntry by name
    - Returns entry.fatal_on_startup_failure if found
    - Returns True if not found (preserves pre-flag behavior for Kora-only HTTP service mounts: web/mcp/webhooks)
    - Returns True on lookup failure (defensive — never silently degrade on infrastructure error)
  - run() loop checks the flag at the listener-startup-raise site:
    - True → abort daemon boot (existing behavior)
    - False → log + continue starting subsequent listeners (NEW: lenient default for non-critical daemons whose own try/except didn't catch an unexpected exception)

kora_cli/listeners/reasoning_engine_listener.py:
  - _hermes_entry now passes fatal_on_startup_failure=True explicitly
  - Module docstring updated to reflect structural-not-documentation contract

Tests: 18 in tests/kora_cli/test_daemon_fatal_on_startup_failure.py covering:
  - Dataclass field defaults + frozen invariant
  - PluginContext kwarg passthrough
  - Lookup behavior (fatal entry / non-fatal entry / no entry / unknown name)
  - End-to-end coordinator behavior (fatal raises abort; non-fatal raises log + continue past)
  - Production pin: reasoning_engine has fatal=True; 7 phase-1/2/3 listeners (snapshot/heartbeat_probes/slack_client/purelymail_client/alert_notifier/cost_telemetry/mcp_consumption) default to fatal=False

Per the [[feedback-local-first-upstream-after]] amendment: this Hermes extension lives in the fork only this dispatch. When operator approves the next upstream-PR batch, this becomes upstream candidate #4 (joining the 3 already-deferred branches from #196).

Tests: 473/473 focused regression set green (Marvin tests + pip dry-run tests + FATAL flag tests + listener tests + identity tests + plugin tests).

Co-authored-by: CC#3 Kora Runtime <kora-pm@stormhavenenterprises.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant