Skip to content

feat: cogos-harness plugin — portable CC membrane v1 - #16

Merged
chazmaniandinkle merged 2 commits into
mainfrom
feat/cogos-harness-plugin
Aug 3, 2026
Merged

chazmaniandinkle merged 2 commits into
mainfrom
feat/cogos-harness-plugin

Conversation

@chazmaniandinkle

Copy link
Copy Markdown
Contributor

Summary

First increment of graduating the hand-wired CogOS Claude Code membrane into a versioned marketplace artifact, per the harness-membrane audit's "first increment" recommendation.

  • New cogos-harness plugin — the first plugin in this marketplace of the hooks/MCP-server kind, not just skills. Ships:
    • MCP server: cogos-kernel, HTTP connection to a locally-running CogOS kernel (http://127.0.0.1:6931/mcp).
    • Hooks (all fail-open, all degrade to silence with no kernel / no cog workspace): user-scope-session-start.py + user-scope-session-end.py (presence dispatch), seat-identity-heal.py (SessionStart role self-heal), compaction-handoff.py (post-compaction bypass path), user-scope-proprioception.py (ambient branch/clock/model/context-window header on UserPromptSubmit), kernel-vitals-probe.py (detached kernel-health/log/release/PR collector the proprioception hook reads from, never called synchronously).
    • Skills: btw, consolidate, handoff.
  • Marketplace hygiene fixes (audit gaps chore: add CONTRIBUTING.md #1 and chore: add CHANGELOG.md #2, same branch): marketplace.json name corrected from cogos-skills to plugins (matches the repo, and the convention oscine's own marketplace already follows: marketplace name = repo basename); added the missing $schema key; added cogos-architecture's missing .claude-plugin/plugin.json (it was declared in marketplace.json with no manifest — installing it specifically would have misbehaved).

What's excluded, and why

  • dispatch-model-probe.py — the audit named it a "by-reference companion" of the proprioception hook, but its only call site there has been commented out (dead code, kept only for easy revival). It also reads the local Claude Code OAuth credentials file directly and pings api.anthropic.com with them — appropriate for one operator's own incident-response tooling, not for default behavior anyone who installs this plugin gets. Excluded outright, not ported.
  • Plan-usage tracking (use S24%·W18%-style segment) — depended on a statusline side-channel cache that isn't part of this plugin and isn't itself portable membrane. Dropped rather than partially ported. The self-contained context-window (ctx N%) segment is kept.
  • Book/reading-surface sync — the source kernel-vitals-probe.py also read a specific private repo's comment ledger to surface an unrelated personal writing project's review queue (repo constant, thread-state diffing, a book ✎N line segment). That entire code path is removed from the shipped version, not disabled — the audit didn't flag this coupling explicitly, it surfaced during the port.
  • pull-context-dispatch — one of the four skills named in the audit's proposed increment, but it's already published in this marketplace under cogos-workflow (landed same-day, ahead of this branch). Not duplicated here to avoid two copies drifting; cogos-harness + cogos-workflow compose for the full skill set.

What stays user-scope (not absorbed into this plugin)

CLAUDE.md operating principles, the memory corpus, settings.json risk-posture flags (defaultMode, permission allow-lists, the various skip*PermissionPrompt flags), the default-agent/model-tiering setting, and node bootstrap (installing/starting the cogos binary itself) — all documented in the plugin's own README under "What stays outside this plugin". This plugin configures a client against an already-running kernel; it does not install or start one.

Known limitation

The bundled MCP server's port isn't env-parameterized — Claude Code plugin .mcp.json doesn't support arbitrary ${ENV_VAR} substitution in server URLs (only the CLAUDE_PLUGIN_*/CLAUDE_PROJECT_DIR placeholders resolve there), so a non-default kernel port means hand-editing the installed plugin's .mcp.json. Documented in the plugin README rather than worked around in this PR.

Scrub check (public-artifact, mandatory)

Ran against every new/changed file (git diff --cached --name-only):

  • Absolute /Users/... paths: none found.
  • Personal identifiers (names, emails, machine names — slowbro, chaz, darkstar, eclipse, chazmaniandinkle): none found. (Pre-existing myrgic@users.noreply.github.com org owner address in marketplace.json is untouched and isn't personal.)
  • Secrets/tokens/credentials: none found — the one credentials/OAuth mention is prose in the README explaining why dispatch-model-probe.py was excluded, not a value.
  • Session-ID-shaped strings (UUIDs): none found.
  • Book/family/personal-project references: none in shipped code; the README's exclusion rationale names the category ("an unrelated personal writing project") without naming the project or its repo.
  • Two example session-IDs in the handoff skill (slowbro-laptop-*) were genericized to <hostname>-* during the port.
  • Four hardcoded /Users/slowbro/... example paths in a since-superseded draft of the pull-context-dispatch skill were the reason that skill isn't duplicated here at all (see above) — moot once excluded.

Dogfood plan (follow-up, not this PR)

Tap myrgic/plugins and install cogos-harness@plugins in a real seat; confirm the installed copy serves hooks in place of the hand-wired ~/.claude/hooks/ files it was cut from. Tracked as follow-up work, intentionally out of scope here per the audit's own "first increment" framing (prove the shape first).

Test plan

  • python3 -m py_compile on all six hooks — clean.
  • claude plugin validate ./plugins/cogos-harness --strict — passes.
  • claude plugin validate ./plugins/cogos-architecture --strict — passes (new manifest).
  • claude plugin validate . --strict (marketplace root) — passes.
  • Scrub grep sweep above — clean.
  • Install on a real seat and confirm hooks fire from the plugin cache (dogfood follow-up, not this PR).

Adds cogos-harness, the first plugin in this marketplace of the
hooks/MCP-server kind rather than skills-only: SessionStart/SessionEnd
presence hooks, a compaction bypass path, ambient kernel-vitals +
context-window proprioception, the cogos-kernel MCP server, and the
btw/consolidate/handoff skills. Every hook degrades to a silent no-op
wherever the kernel or a cog workspace is absent.

Also fixes marketplace.json's name (cogos-skills -> plugins, matching
the repo and the convention other marketplaces here already follow)
and missing $schema, and adds the cogos-architecture plugin.json that
package was missing.
F1: kernel-vitals-probe.py's disk watermark write fabricated a
~/workspaces/cog/.cog tree on any machine that had none, which then
made every sibling hook believe a cog workspace existed and
self-suppress. Gate the write on an already-existing workspace instead
of mkdir(parents=True) into a third party's home directory.

F2: plugin.json's description still advertised plan-usage
proprioception, which was dropped from the code; align it with
marketplace.json's already-correct wording.

F3: .mcp.json now resolves the kernel port via
${COGOS_KERNEL_PORT:-6931} — env-var substitution in MCP server URLs
does work in Claude Code — so drop the README's stale "Known
limitation" section that claimed otherwise.

F4: compaction-handoff.py unconditionally emitted a
<compaction_handoff> block promising a RECORD even when transcript,
in-flight tasks, and uncommitted work were all empty (e.g. a fresh
substrate-less HOME). Exit quietly instead of injecting an empty
promise to distrust the summary.

F5: unify the three independent kernel-endpoint knobs
(COGOS_KERNEL_URL, COGOS_KERNEL_PORT, and .mcp.json's hardcoded URL)
onto one precedence: COGOS_KERNEL_URL wins when set, COGOS_KERNEL_PORT
is the localhost-only default, applied consistently in both
kernel-vitals-probe.py and seat-identity-heal.py. README documents the
remaining .mcp.json gap (still port-only) honestly rather than as a
blocked-on-upstream limitation.
@chazmaniandinkle

Copy link
Copy Markdown
Contributor Author

Dogfood verification at 7eeb2fa (clean isolated HOME, marketplace add from local clone, plugin install cogos-harness@plugins, hooks executed from the installed cache path with CLAUDE_PLUGIN_ROOT/CLAUDE_PLUGIN_DATA set as the harness would):

  • All 6 hooks exit 0 from the installed layout.
  • No fabricated ~/workspaces tree after any hook run — F1 fix holds from the installed artifact, not just the working tree.
  • All writes confined to the plugin data dir (.kernel-vitals.json, .proprioception-sessions.json).
  • user-scope-proprioception.py emits a well-formed hookSpecificOutput with the <cogos_proprioception> block from the installed plugin — the audit's first-increment success criterion, demonstrated pre-merge.

Note: kernel-present path was exercised (this machine's kernel is live on :6931, which is machine-scoped, not HOME-scoped); the kernel-absent degradation paths were separately verified in the review pass.

@chazmaniandinkle
chazmaniandinkle merged commit b95f6a3 into main Aug 3, 2026
@chazmaniandinkle
chazmaniandinkle deleted the feat/cogos-harness-plugin branch August 3, 2026 17:28
chazmaniandinkle added a commit that referenced this pull request Aug 3, 2026
…loses #17 (#18)

* fix: kernel-gated session lifecycle (auto-register, heartbeat, end)

Presence was workspace-gated: a seat with no cog workspace never
auto-registered on the kernel session registry, even though the kernel
was reachable and willing (#17, same predicate-confusion family as F1
in #16, inverted direction).

Makes all three lifecycle touchpoints kernel-gated instead:

- user-scope-session-start.py: after the existing workspace-handler
  delegation (kept intact and tried first), a direct-registration
  fallback fires only when no workspace handler ran AND a short kernel
  probe succeeds. POSTs /v1/sessions/register with session_id (from
  hook stdin), workspace=cwd, role=$COGOS_SEAT_ROLE or "claude-code",
  hostname, extras.source=plugin-startup.
- user-scope-proprioception.py: when the already-cached kernel-vitals
  read shows the kernel reachable, also POSTs the session heartbeat
  (/v1/sessions/{id}/heartbeat) -- no new probe, at most once per
  turn, silent no-op when the kernel is absent.
- user-scope-session-end.py: mirrors session-start -- direct
  /v1/sessions/{id}/end fallback, same probe gate, only when the
  workspace handler didn't run.

All three resolve the kernel via the same COGOS_KERNEL_URL /
COGOS_KERNEL_PORT precedence as seat-identity-heal.py and
kernel-vitals-probe.py. Every new network call is short-timeout and
fails open silently, matching the existing hook contract.

Verified against the live kernel: register -> heartbeat (last_seen
advances) -> end, in a clean isolated HOME with no cog workspace, plus
delegation-path-untouched and dead-kernel-port cases (all hooks exit 0
silently, no hang).

Bumps cogos-harness 0.1.0 -> 0.2.0 in plugin.json and marketplace.json.

* fix: gate the presence fallback on the registry, not handler existence

Adversarial review of the #17 fix found that handler-existence was
standing in for registration, which is the same predicate confusion #17
is about. The canonical 51-presence-started.py / 51-presence-ended.py
handlers emit presence.started / presence.ended events on the kernel
BUS; neither touches the session registry. Three consequences, all
reproduced against a live kernel:

1. On any machine WITH a cog workspace -- the environment #17 was
   reported from -- delegation ran, handler_invoked suppressed the
   fallback, and the seat still never reached the registry. #17
   unfixed in its own reproduction environment.

2. A workspace shipping 51-presence-ended.py but not
   51-presence-started.py registered via the start fallback and then had
   the end fallback suppressed by the bus-only end handler: a seat left
   active=true, ended=false, forever.

3. /v1/sessions/register is a full-row replace, not a field merge (the
   fact seat-identity-heal.py exists to repair). The unconditional
   fallback fired on resume/compact SessionStart too, overwriting a
   durable seat role with role=claude-code,
   extras.source=plugin-startup.

All three collapse into one predicate. Both hooks now ask the registry
itself -- GET /v1/sessions/presence, which lists live not-yet-ended
sessions -- and act only on what it reports: register when the session
is genuinely absent, end when it is genuinely still live. That call
replaces the GET /health probe rather than adding to it, so the gate is
one round trip, not two. Delegation stays preferred and still runs
first; it just no longer decides.

Also cuts the proprioception heartbeat timeout 1.0s -> 0.25s. That call
is the only network work on the UserPromptSubmit critical path, and its
reachability gate is a cache that can report a hung kernel as reachable
for up to the 90s vitals TTL. Measured against a listener that accepts
and never answers: 1.14s per turn before, 0.39s after.

README updated where it described the old gate.

* fix: align registration vocabulary with fleet conventions

extras.source passes through the harness SessionStart source value
(startup|resume|clear|compact) instead of inventing plugin-startup;
provenance moves to extras.via. End reason uses the established
session_end_hook instead of plugin-shutdown. Verified live: register
row carries {source: startup, via: cogos-harness}; end row carries
end_reason: session_end_hook.
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