lifeloop-cli 0.3.0

Provider-neutral lifecycle abstraction and normalizer for AI harnesses
Documentation
# Harness Concept Boundary Note

## Status

Boundary note. Pre-freeze, advisory.

## Date

2026-05-08

## Purpose

Different AI harnesses expose overlapping but inequivalent concepts: skills,
plugins, slash commands, agents/subagents, hooks, tools and tool permissions,
MCP servers, and memory or rule files. When CCD touched harness lifecycle, the
temptation was to normalize each of those concepts into a single cross-harness
abstraction. That temptation produced kernel-shaped feature creep.

This note is the boundary decision for Lifeloop. It names each concept,
classifies it as **lifecycle integration surface** or **out of scope**, and
fixes a single rule for resolving future ambiguity.

The deliverable is small on purpose. A normalized abstraction proposed beyond
this note must point at the rule below and show how it stays inside it.

## The Rule

> Lifeloop normalizes lifecycle reach. It does not normalize meaning.

In practice:

- If a concept changes **when** or **how** a lifecycle moment is observed,
  delivered, or correlated, Lifeloop may carry the lifecycle-relevant
  metadata about it.
- If a concept defines **what** that moment means, what payload to send, or
  how a client should react, it stays out of Lifeloop core.
- Lifecycle-relevant metadata is bounded to capability flags, integration
  modes, payload placements, hook timings, and adapter identity. It is **not**
  a runtime model of the concept itself.

A useful test before adding any new normalized type: would a non-CCD client
(RLM, Reforge-family, Fixity, a new research client) also need this exact
shape, *and* could that shape collapse without changing what lifecycle moments
are observable? If the answer to either is no, the concept is client-owned.

## Per-Concept Boundary

Each row classifies a harness concept and names the lifecycle-relevant facet
Lifeloop carries (if any). "Out of scope" means the concept never enters
Lifeloop core types — not even as an enum case.

### Hooks

**Status:** in scope. Already owned.

Lifeloop normalizes hook timing into lifecycle event vocabulary
(`session.starting`, `frame.opening`, etc.) and exposes `native_hook` as one of
the integration modes in the adapter manifest. A new harness's hooks are
adopted by mapping each hook to a lifecycle event name and declaring the
integration mode in its manifest. No "Hook" type beyond that.

### Skills (Codex, Claude Code) and Plugins (Claude Code)

**Status:** installation-time surface only. Runtime behavior out of scope.

Skills and plugins are bundles. They may install lifecycle integration assets:
hook registrations, harness configuration entries, slash-command entry
points, and MCP server stubs. Lifeloop owns the lifecycle-relevant metadata
for those assets when they are represented as host integration configuration.
It does not own model instruction source files that may mention those skills
or plugins.

Lifeloop does not own:

- the skill/plugin manifest format, beyond the lifecycle integration assets it
  produces;
- a cross-harness "Skill" or "Plugin" type;
- when or why a skill activates at runtime;
- the semantic content of the instructions a skill carries.

A skill is therefore not a Lifeloop concept. A skill *may produce* a
lifecycle integration asset, and that asset goes through the host integration
asset boundary. If the integration needs prose in `AGENTS.md`, `CLAUDE.md`,
or an equivalent instruction file, Lifeloop may suggest text separately but
must not write or render a managed section for that file.

### Slash Commands

**Status:** integration-mode entry-point only. Out of scope as a normalized
concept.

A slash command can be the user-facing trigger for a `manual_skill` or
`launcher_wrapper` integration mode. Lifeloop already represents that with
opaque integration-mode metadata. There is no normalized "SlashCommand" type
and there will not be one. The fact that a harness exposes a slash command
named `/ccd-start` is not a Lifeloop concern; the fact that a particular
adapter integrates via a manual operator-typed entry point *is*, and that is
already covered by the `manual_skill` integration mode.

### Agents and Subagents

**Status:** out of scope.

Agent dispatch is an internal harness or client concern. Lifeloop's frame
vocabulary (`frame.opening`, `frame.opened`, `frame.ending`, `frame.ended`)
and `parent_receipt_id` already give clients enough to correlate nested
frames when a harness exposes them. No "Agent" or "Subagent" type. RLM-style
recursive frames consume the same frame vocabulary; that is the test that
this boundary holds.

### Tools and Tool Permissions

**Status:** out of scope.

Tool permission policy is enforcement, not lifecycle. The product thesis
explicitly excludes tool abstraction. Tool invocation may produce telemetry
that Lifeloop reads (e.g. activity, context pressure), but Lifeloop does not
expose `tool.invoked` as a lifecycle event in `lifeloop.v0.x`. If a future
spec adds a tool-related event, it must satisfy the rule above: the event
must change *when* a lifecycle moment is observable, not catalog *what* the
tool did.

### MCP and Server Integrations

**Status:** out of scope as a protocol. Installation-time asset rendering may
overlap.

MCP is a model-context-protocol transport. Lifeloop does not proxy MCP calls,
does not define MCP capabilities, and does not normalize "tools an MCP server
exposes." Where MCP overlaps with Lifeloop is at *installation time*: a
harness configuration file may need an entry that registers an MCP server stub
pointing at a Lifeloop-owned receipt ledger or telemetry endpoint. That host
integration asset is in scope. Prose about the MCP server inside an
instruction source file is user-owned and out of scope. The MCP protocol
surface itself is not.

### Memory and Rule Files When Exposed As Harness Features

**Status:** out of scope except read-only diagnostics.

`AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, and equivalent files are the canonical
case. These are user-owned model instruction source files. Lifeloop does not
own the whole file, a managed section inside it, or replacement-body rendering
for it.

Lifeloop may:

- read these files for diagnostics;
- report lifecycle-relevant divergence between instruction files;
- emit suggested changes as a separate advisory artifact.

Lifeloop does not own:

- what the memory says — that is client content (CCD continuity prompts,
  Reforge-family recall rules, project-specific guidance);
- recall, promotion, compaction, or governance — Reforge-family clients;
- runtime evaluation of rule content — harness or client.

For the current source-file ownership decision, see
[`decisions/source-files-are-user-owned.md`](decisions/source-files-are-user-owned.md).
The previous managed-section boundary note is superseded at
[`harness-concepts/source-files.md`](harness-concepts/source-files.md).

## Lifecycle Integration Assets

The in-scope subset has a name to keep it auditable:

> **Lifecycle integration asset:** an installable artifact whose purpose is
> to wire a harness's lifecycle reach to a client. Examples: hook
> registrations, harness settings entries that point at a Lifeloop-owned
> command, and MCP server stubs that register a Lifeloop endpoint.

Lifecycle integration assets share these properties:

- they exist outside Lifeloop's runtime types but Lifeloop renders them;
- they are idempotent under repeated apply;
- they carry adapter metadata, not client semantics;
- removing a lifecycle integration asset must be deterministic.

Issue #4 extracts host integration asset behavior into this surface. The
source-file managed-section design once tracked by #16 has been retracted by
[`decisions/source-files-are-user-owned.md`](decisions/source-files-are-user-owned.md).
The normalized abstraction proposed in this note is **only** "lifecycle
integration asset," with the bounded properties above. It is not a normalized
"Skill," "Plugin," "Tool," or "MemoryFile" model.

## What Lifeloop Will Not Normalize

Stated in firm form so future PRs cannot rationalize a backdoor:

- No universal `Skill`, `Plugin`, `SlashCommand`, `Agent`, `Subagent`,
  `Tool`, or `MCPServer` type in Lifeloop core.
- No runtime model of when a skill, plugin, or agent activates.
- No tool-permission enforcement primitive.
- No memory product semantics (recall, promotion, compaction, governance).
- No prompt content abstraction above payload placement metadata.
- No model-selection, provider-routing, or prompt-cache abstraction.
- No "harness IDE" surface.
- No catalog of what a harness's tools, skills, or plugins can do.

If any of the above appears tempting, the concept belongs to a client
(CCD, RLM, Reforge-family, Fixity, a new research client) or to the harness
itself. Lifeloop's job is to make sure those clients can attach without
each one having to relearn the harness.

## Open Questions

Tracked for future spec work, not blockers for the boundary itself:

- Should a future `lifeloop.v0.x` minor add a *manifest field* describing
  whether an adapter's skill/plugin system can host lifecycle integration
  assets directly (so Lifeloop knows whether `manual_skill` or `native_hook`
  is the right integration mode)? The field would be capability metadata,
  not a "Skill" type. In scope under the rule.
- The `receipt_only` placement may be enough for clients that need
  instruction-file diagnostics without adding a new placement class. If a new
  placement is needed, it goes through the spec-bump and `spec_alignment.rs`
  workflow, not a quiet `placement_class` enum addition.
- MCP-stub installation may need additional host integration asset templates.
  Those templates belong in host assets, not in instruction source files.

## Cross-References

- [Product thesis]product-thesis.md — strategic rationale.
- [Lifecycle contract spec]specs/lifecycle-contract/body.md — normative wire
  contract this note supplements.
- [RLM development-contract declaration]rlm-development-contract.md — the
  second-client pressure that keeps this boundary honest.
- Issue `#4` (host integration asset extraction) is the implementation
  surface this note still governs. The source-file rendering implementation
  was superseded by
  [`decisions/source-files-are-user-owned.md`]decisions/source-files-are-user-owned.md.