# Domain Module Aliases (lifeloop-cli 0.1.1) Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Ship lifeloop-cli 0.1.1 as a non-breaking additive release that exposes domain-organized submodules (`adapters`, `capability`, `event`, `manifest`) as re-export aliases over the existing 0.1.0 surface. This makes the *module layout* ccd-guide's slimdown plan assumed available, while preserving lifeloop's principled type design (the `CapabilityKind` + `CapabilityRequest` split, the `LifecycleEventKind` name, etc.).

**What this fixes for ccd-guide and what it does NOT fix:** This release alone does **not** make ccd-guide's slimdown Phase 1 ("flip imports, leave function bodies unchanged") executable. It only addresses the *module-path* half of the divergence. The *type/function-name* half — `EventName` → `LifecycleEventKind`, flat `Capability` → split `CapabilityKind` + `CapabilityRequest`, `negotiate_capabilities` → `negotiate`, `all_manifests` → `manifest_registry` — stays unchanged in lifeloop by deliberate design choice. ccd-guide's Phase 1 must therefore be amended into a "translate, don't flip" task that rewrites consumer call sites to the published 0.1.x types and function names. That amendment is out of scope of this plan and lives in ccd-guide's CCD profile.

**Architecture:** Pure re-export module shim inside `src/lib.rs`. Each new submodule is `pub mod <name> { pub use ... }` — no new types, no behavior change, no breaking renames. Compile-time integration test in `tests/domain_modules.rs` verifies *every* aliased path resolves to the same item as its canonical path. Version bumps to 0.1.1, CHANGELOG records the additive paths.

**Tech Stack:** Rust 2024 edition, cargo, lifeloop-cli (existing crate at workspace root).

**Out of scope (explicit non-goals):**
- Renaming `negotiate` to `negotiate_capabilities` (the published name is fine inside `capability::` namespace).
- Adding a flat `Capability` type that fuses `CapabilityKind` + `CapabilityRequest` (load-bearing distinction; consumer adapts).
- Renaming `LifecycleEventKind` to `EventName` (substrate-precision name wins).
- Renaming `manifest_registry` to `all_manifests` (registry semantics > collection semantics).
- Re-organizing the canonical layout (moving items *out* of `src/lib.rs` top-level) — that's a 0.2.0 conversation, not 0.1.1. Aliases only here.
- Amending ccd-guide's slimdown Phase 1 plan — owned by ccd-guide's profile.

---

## File Structure

| File | Status | Responsibility |
|---|---|---|
| `Cargo.toml` | Modify | Bump `version` from `0.1.0` to `0.1.1` |
| `src/lib.rs` | Modify | Append four `pub mod { pub use ... }` blocks at end of file (after the existing `DispatchEnvelope` definition); no other edits |
| `tests/domain_modules.rs` | Create | Compile-time integration test: every aliased path resolves and equals the canonical path |
| `CHANGELOG.md` | Create | First entry: `0.1.1 - additive domain module aliases`, lists the four new module paths |

The new submodules go inside `src/lib.rs` rather than separate files because they are pure re-exports (each is ~5–15 lines) and keeping them adjacent to the canonical declarations makes drift easier to spot. If a 0.2.0 reorg later moves canonical declarations *into* the submodules, those submodules become their own files.

---

## Canonical 0.1.0 surface (reference for the alias mapping)

This is the source of truth the implementer should grep against if any path below seems off:

| Canonical path (0.1.0) | Where defined |
|---|---|
| `lifeloop::AdapterManifest` | `src/lib.rs:1067` |
| `lifeloop::RegisteredAdapter` | `src/lib.rs:1140` |
| `lifeloop::ConformanceLevel` | `src/lib.rs:1126` |
| `lifeloop::ManifestPlacementClass` | `src/lib.rs:941` |
| `lifeloop::ManifestLifecycleEventSupport` | `src/lib.rs:967` |
| `lifeloop::ManifestPlacementSupport` | `src/lib.rs:978` |
| `lifeloop::ManifestContextPressure` | `src/lib.rs:989` |
| `lifeloop::ManifestReceipts` | `src/lib.rs:998` |
| `lifeloop::ManifestSessionIdentity` | `src/lib.rs:1012` |
| `lifeloop::ManifestSessionRename` | `src/lib.rs:1022` |
| `lifeloop::ManifestApprovalSurface` | `src/lib.rs:1030` |
| `lifeloop::ManifestTelemetrySource` | `src/lib.rs:1037` |
| `lifeloop::ManifestKnownDegradation` | `src/lib.rs:1048` |
| `lifeloop::manifest_registry` | `src/lib.rs:1148` |
| `lifeloop::lookup_manifest` | `src/lib.rs:1179` |
| `lifeloop::codex_manifest` | `src/lib.rs:1205` |
| `lifeloop::claude_manifest` | `src/lib.rs:1380` |
| `lifeloop::hermes_manifest` | `src/lib.rs:1557` |
| `lifeloop::openclaw_manifest` | `src/lib.rs:1562` |
| `lifeloop::gemini_manifest` | `src/lib.rs:1567` |
| `lifeloop::opencode_manifest` | `src/lib.rs:1572` |
| `lifeloop::LifecycleEventKind` | `src/lib.rs:91` |
| `lifeloop::lifecycle_event_kinds` | `src/lib.rs:141` |
| `lifeloop::router::CapabilityKind` | `src/router/negotiation.rs:91` (re-exported at `src/router/mod.rs:62-65`) |
| `lifeloop::router::CapabilityRequest` | re-exported at `src/router/mod.rs:62-65` |
| `lifeloop::router::CapabilityRequirement` | re-exported at `src/router/mod.rs:62-65` |
| `lifeloop::router::DefaultNegotiationStrategy` | re-exported at `src/router/mod.rs:62-65` |
| `lifeloop::router::NegotiatedPlan` | re-exported at `src/router/mod.rs:62-65` |
| `lifeloop::router::PayloadPlacementDecision` | re-exported at `src/router/mod.rs:62-65` |
| `lifeloop::router::PlacementRejection` | re-exported at `src/router/mod.rs:62-65` |
| `lifeloop::router::negotiate` | `src/router/negotiation.rs:375` (re-exported at `src/router/mod.rs:62-65`) |

---

## Task 1: Add `event` submodule alias

**Files:**
- Modify: `src/lib.rs` (append at end of file)

- [ ] **Step 1: Read the current end of `src/lib.rs` to confirm append location**

Run: `tail -20 /Users/emanuele/devel/lifeloop/src/lib.rs`
Expected: ends with the `DispatchEnvelope` struct definition closing brace and possibly a trailing newline. Note the exact last line so the append doesn't accidentally land mid-item.

- [ ] **Step 2: Append the `event` submodule block**

Append exactly this to `src/lib.rs` (one blank line above):

```rust
// ============================================================================
// Domain module aliases (added 0.1.1)
//
// These submodules are pure re-exports of the canonical top-level surface
// they exist so consumers can write `use lifeloop::event::LifecycleEventKind`
// instead of `use lifeloop::LifecycleEventKind`. The canonical paths remain
// authoritative; aliases must stay in lockstep.
// ============================================================================

/// Lifecycle event vocabulary. Re-exports of canonical crate-root items.
pub mod event {
    pub use crate::{LifecycleEventKind, lifecycle_event_kinds};
}
```

- [ ] **Step 3: Verify the crate still builds**

Run: `cargo build -p lifeloop-cli`
Expected: clean build, no warnings about unused re-exports (the aliases are `pub use`, so they're public API and won't warn).

- [ ] **Step 4: Commit**

```bash
git add src/lib.rs
git commit -m "feat(lib): add event domain module alias"
```

---

## Task 2: Add `manifest` submodule alias

**Files:**
- Modify: `src/lib.rs` (append after Task 1's block)

- [ ] **Step 1: Append the `manifest` submodule block**

Append to `src/lib.rs`:

```rust
/// Adapter manifest types and registry. Re-exports of canonical crate-root items.
pub mod manifest {
    pub use crate::{
        AdapterManifest, ConformanceLevel, ManifestApprovalSurface,
        ManifestContextPressure, ManifestKnownDegradation,
        ManifestLifecycleEventSupport, ManifestPlacementClass,
        ManifestPlacementSupport, ManifestReceipts, ManifestSessionIdentity,
        ManifestSessionRename, ManifestTelemetrySource, RegisteredAdapter,
        lookup_manifest, manifest_registry,
    };
}
```

- [ ] **Step 2: Verify the crate still builds**

Run: `cargo build -p lifeloop-cli`
Expected: clean build.

- [ ] **Step 3: Commit**

```bash
git add src/lib.rs
git commit -m "feat(lib): add manifest domain module alias"
```

---

## Task 3: Add `adapters` submodule alias

**Files:**
- Modify: `src/lib.rs` (append after Task 2's block)

- [ ] **Step 1: Append the `adapters` submodule block**

Append to `src/lib.rs`:

```rust
/// Built-in adapter registry and per-harness manifest constructors.
/// Re-exports of canonical crate-root items.
pub mod adapters {
    pub use crate::{
        claude_manifest, codex_manifest, gemini_manifest, hermes_manifest,
        lookup_manifest, manifest_registry, opencode_manifest, openclaw_manifest,
    };
}
```

- [ ] **Step 2: Verify the crate still builds**

Run: `cargo build -p lifeloop-cli`
Expected: clean build.

- [ ] **Step 3: Commit**

```bash
git add src/lib.rs
git commit -m "feat(lib): add adapters domain module alias"
```

---

## Task 4: Add `capability` submodule alias

**Files:**
- Modify: `src/lib.rs` (append after Task 3's block)

- [ ] **Step 1: Append the `capability` submodule block**

Append to `src/lib.rs`:

```rust
/// Capability vocabulary and negotiation entry point. Re-exports the canonical
/// router items so consumers can use `lifeloop::capability::*` without taking
/// a dependency on the `router` module path. The `CapabilityKind` /
/// `CapabilityRequest` split is preserved deliberately — `CapabilityKind` is
/// the identity used as a manifest lookup key, and `CapabilityRequest` adds
/// policy (`desired`, `level`).
pub mod capability {
    pub use crate::router::{
        CapabilityKind, CapabilityRequest, CapabilityRequirement,
        DefaultNegotiationStrategy, NegotiatedPlan, PayloadPlacementDecision,
        PlacementRejection, negotiate,
    };
}
```

- [ ] **Step 2: Verify the crate still builds**

Run: `cargo build -p lifeloop-cli`
Expected: clean build.

- [ ] **Step 3: Commit**

```bash
git add src/lib.rs
git commit -m "feat(lib): add capability domain module alias"
```

---

## Task 5: Compile-time integration test for alias identity

**Files:**
- Create: `tests/domain_modules.rs`

The test asserts that each aliased path resolves to the *same item* as its canonical path. We use type-equality assertions (`fn _assert<T>() where T: Same<U>`) for types and function-pointer equality for functions. Rust does not have a built-in `Same` trait, so we use the standard `assert_eq_size!`-style approach: assign through a binding whose type is the canonical path, and let the compiler check that the alias unifies.

- [ ] **Step 1: Write the failing test**

Create `tests/domain_modules.rs`. Every `pub use` line introduced in Tasks 1-4 must have a corresponding identity check below; if you add a new alias to one of the modules later, add a matching check here.

```rust
//! Verifies the 0.1.1 domain module aliases (`event`, `manifest`, `adapters`,
//! `capability`) resolve to the same items as their canonical crate-root or
//! `router` counterparts. Compile-only checks for types (the `let _: T = u`
//! bindings unify only if `T` and the inferred type of `u` are the same item)
//! plus runtime function-pointer-equality checks for functions.
//!
//! If any alias drifts (e.g. someone replaces a re-export with a wrapper
//! struct), this test fails to compile or to run.

#![allow(dead_code, unused_imports)]

// ============================================================================
// event
// ============================================================================

fn _event_kind_alias_matches() {
    // Use a real variant from src/lib.rs:91-120.
    let canonical_value: lifeloop::LifecycleEventKind =
        lifeloop::LifecycleEventKind::SessionStarting;
    let aliased_value: lifeloop::event::LifecycleEventKind = canonical_value;
    let _: lifeloop::LifecycleEventKind = aliased_value;
}

fn _event_lifecycle_event_kinds_alias_matches() {
    let canonical: fn() -> Vec<lifeloop::LifecycleEventKind> =
        lifeloop::lifecycle_event_kinds;
    let aliased: fn() -> Vec<lifeloop::LifecycleEventKind> =
        lifeloop::event::lifecycle_event_kinds;
    assert_eq!(canonical as usize, aliased as usize);
}

// ============================================================================
// manifest — every `pub use` from Task 2 has a check below
// ============================================================================

fn _manifest_adapter_manifest_alias(x: lifeloop::AdapterManifest) {
    let _: lifeloop::manifest::AdapterManifest = x;
}

fn _manifest_registered_adapter_alias(x: lifeloop::RegisteredAdapter) {
    let _: lifeloop::manifest::RegisteredAdapter = x;
}

fn _manifest_conformance_level_alias(x: lifeloop::ConformanceLevel) {
    let _: lifeloop::manifest::ConformanceLevel = x;
}

fn _manifest_placement_class_alias(x: lifeloop::ManifestPlacementClass) {
    let _: lifeloop::manifest::ManifestPlacementClass = x;
}

fn _manifest_lifecycle_event_support_alias(x: lifeloop::ManifestLifecycleEventSupport) {
    let _: lifeloop::manifest::ManifestLifecycleEventSupport = x;
}

fn _manifest_placement_support_alias(x: lifeloop::ManifestPlacementSupport) {
    let _: lifeloop::manifest::ManifestPlacementSupport = x;
}

fn _manifest_context_pressure_alias(x: lifeloop::ManifestContextPressure) {
    let _: lifeloop::manifest::ManifestContextPressure = x;
}

fn _manifest_receipts_alias(x: lifeloop::ManifestReceipts) {
    let _: lifeloop::manifest::ManifestReceipts = x;
}

fn _manifest_session_identity_alias(x: lifeloop::ManifestSessionIdentity) {
    let _: lifeloop::manifest::ManifestSessionIdentity = x;
}

fn _manifest_session_rename_alias(x: lifeloop::ManifestSessionRename) {
    let _: lifeloop::manifest::ManifestSessionRename = x;
}

fn _manifest_approval_surface_alias(x: lifeloop::ManifestApprovalSurface) {
    let _: lifeloop::manifest::ManifestApprovalSurface = x;
}

fn _manifest_telemetry_source_alias(x: lifeloop::ManifestTelemetrySource) {
    let _: lifeloop::manifest::ManifestTelemetrySource = x;
}

fn _manifest_known_degradation_alias(x: lifeloop::ManifestKnownDegradation) {
    let _: lifeloop::manifest::ManifestKnownDegradation = x;
}

fn _manifest_registry_fn_alias() {
    let canonical: fn() -> Vec<lifeloop::RegisteredAdapter> = lifeloop::manifest_registry;
    let aliased: fn() -> Vec<lifeloop::RegisteredAdapter> = lifeloop::manifest::manifest_registry;
    assert_eq!(canonical as usize, aliased as usize);
}

fn _manifest_lookup_manifest_fn_alias() {
    let canonical: fn(&str) -> Option<lifeloop::RegisteredAdapter> = lifeloop::lookup_manifest;
    let aliased: fn(&str) -> Option<lifeloop::RegisteredAdapter> =
        lifeloop::manifest::lookup_manifest;
    assert_eq!(canonical as usize, aliased as usize);
}

// ============================================================================
// adapters — every `pub use` from Task 3 has a check below
// ============================================================================

fn _adapters_manifest_registry_fn_alias() {
    let canonical: fn() -> Vec<lifeloop::RegisteredAdapter> = lifeloop::manifest_registry;
    let aliased: fn() -> Vec<lifeloop::RegisteredAdapter> = lifeloop::adapters::manifest_registry;
    assert_eq!(canonical as usize, aliased as usize);
}

fn _adapters_lookup_manifest_fn_alias() {
    let canonical: fn(&str) -> Option<lifeloop::RegisteredAdapter> = lifeloop::lookup_manifest;
    let aliased: fn(&str) -> Option<lifeloop::RegisteredAdapter> =
        lifeloop::adapters::lookup_manifest;
    assert_eq!(canonical as usize, aliased as usize);
}

fn _adapters_codex_manifest_fn_alias() {
    let canonical: fn() -> lifeloop::AdapterManifest = lifeloop::codex_manifest;
    let aliased: fn() -> lifeloop::AdapterManifest = lifeloop::adapters::codex_manifest;
    assert_eq!(canonical as usize, aliased as usize);
}

fn _adapters_claude_manifest_fn_alias() {
    let canonical: fn() -> lifeloop::AdapterManifest = lifeloop::claude_manifest;
    let aliased: fn() -> lifeloop::AdapterManifest = lifeloop::adapters::claude_manifest;
    assert_eq!(canonical as usize, aliased as usize);
}

fn _adapters_hermes_manifest_fn_alias() {
    let canonical: fn() -> lifeloop::AdapterManifest = lifeloop::hermes_manifest;
    let aliased: fn() -> lifeloop::AdapterManifest = lifeloop::adapters::hermes_manifest;
    assert_eq!(canonical as usize, aliased as usize);
}

fn _adapters_openclaw_manifest_fn_alias() {
    let canonical: fn() -> lifeloop::AdapterManifest = lifeloop::openclaw_manifest;
    let aliased: fn() -> lifeloop::AdapterManifest = lifeloop::adapters::openclaw_manifest;
    assert_eq!(canonical as usize, aliased as usize);
}

fn _adapters_gemini_manifest_fn_alias() {
    let canonical: fn() -> lifeloop::AdapterManifest = lifeloop::gemini_manifest;
    let aliased: fn() -> lifeloop::AdapterManifest = lifeloop::adapters::gemini_manifest;
    assert_eq!(canonical as usize, aliased as usize);
}

fn _adapters_opencode_manifest_fn_alias() {
    let canonical: fn() -> lifeloop::AdapterManifest = lifeloop::opencode_manifest;
    let aliased: fn() -> lifeloop::AdapterManifest = lifeloop::adapters::opencode_manifest;
    assert_eq!(canonical as usize, aliased as usize);
}

// ============================================================================
// capability — every `pub use` from Task 4 has a check below
// ============================================================================

fn _capability_kind_alias(x: lifeloop::router::CapabilityKind) {
    let _: lifeloop::capability::CapabilityKind = x;
}

fn _capability_request_alias(x: lifeloop::router::CapabilityRequest) {
    let _: lifeloop::capability::CapabilityRequest = x;
}

fn _capability_requirement_alias(x: lifeloop::router::CapabilityRequirement) {
    let _: lifeloop::capability::CapabilityRequirement = x;
}

fn _capability_default_negotiation_strategy_alias(
    x: lifeloop::router::DefaultNegotiationStrategy,
) {
    let _: lifeloop::capability::DefaultNegotiationStrategy = x;
}

fn _capability_negotiated_plan_alias(x: lifeloop::router::NegotiatedPlan) {
    let _: lifeloop::capability::NegotiatedPlan = x;
}

fn _capability_payload_placement_decision_alias(
    x: lifeloop::router::PayloadPlacementDecision,
) {
    let _: lifeloop::capability::PayloadPlacementDecision = x;
}

fn _capability_placement_rejection_alias(x: lifeloop::router::PlacementRejection) {
    let _: lifeloop::capability::PlacementRejection = x;
}

fn _capability_negotiate_fn_alias() {
    // The full signature of `negotiate` is intentionally not duplicated here —
    // function-pointer-equality only requires that the items unify, which the
    // `as usize` cast forces. If `negotiate`'s signature changes, this still
    // compiles (the `as usize` cast erases the signature) but the underlying
    // alias re-export in src/lib.rs would have caught the mismatch first.
    let canonical = lifeloop::router::negotiate as usize;
    let aliased = lifeloop::capability::negotiate as usize;
    assert_eq!(canonical, aliased);
}

// ============================================================================
// runtime entry point — only the function-pointer checks need to actually run;
// the `let _: T = x` checks above are compile-only.
// ============================================================================

#[test]
fn function_aliases_unify_with_canonical_paths() {
    _event_lifecycle_event_kinds_alias_matches();
    _manifest_registry_fn_alias();
    _manifest_lookup_manifest_fn_alias();
    _adapters_manifest_registry_fn_alias();
    _adapters_lookup_manifest_fn_alias();
    _adapters_codex_manifest_fn_alias();
    _adapters_claude_manifest_fn_alias();
    _adapters_hermes_manifest_fn_alias();
    _adapters_openclaw_manifest_fn_alias();
    _adapters_gemini_manifest_fn_alias();
    _adapters_opencode_manifest_fn_alias();
    _capability_negotiate_fn_alias();
}
```

- [ ] **Step 2: Run the test — should pass immediately because Tasks 1-4 are already in place**

Run: `cargo test -p lifeloop-cli --test domain_modules`
Expected: 1 passed (`function_aliases_unify_with_canonical_paths`). The compile-only type-binding checks (`_*_alias` free functions) pass implicitly by virtue of the file compiling at all.

If compilation fails, the alias modules are out of sync. Fix the alias side (`src/lib.rs`) to match the canonical path, not the test — the canonical path is the source of truth and the alias is a contract that must match it.

- [ ] **Step 3: Run the full test suite to confirm no regressions**

Run: `cargo test -p lifeloop-cli`
Expected: all existing tests still pass; `domain_modules` adds one new pass.

- [ ] **Step 4: Commit**

```bash
git add tests/domain_modules.rs
git commit -m "test: lock in domain module alias identity"
```

---

## Task 6: Bump version and add CHANGELOG

**Files:**
- Modify: `Cargo.toml:5` (`version = "0.1.0"` → `version = "0.1.1"`)
- Create: `CHANGELOG.md`

- [ ] **Step 1: Bump version in `Cargo.toml`**

Edit `Cargo.toml`:

```toml
# Before:
version = "0.1.0"

# After:
version = "0.1.1"
```

- [ ] **Step 2: Create `CHANGELOG.md`**

Create `CHANGELOG.md`:

```markdown
# Changelog

All notable changes to `lifeloop-cli` are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.1.1] - 2026-05-10

### Added

- Domain module aliases as pure re-exports over the canonical 0.1.0 surface,
  so consumers can use module-shaped imports without changing the underlying
  type design:
  - `lifeloop::event::{LifecycleEventKind, lifecycle_event_kinds}`
  - `lifeloop::manifest::{AdapterManifest, RegisteredAdapter, ConformanceLevel,
    Manifest*, manifest_registry, lookup_manifest}`
  - `lifeloop::adapters::{manifest_registry, lookup_manifest, codex_manifest,
    claude_manifest, hermes_manifest, openclaw_manifest, gemini_manifest,
    opencode_manifest}`
  - `lifeloop::capability::{CapabilityKind, CapabilityRequest,
    CapabilityRequirement, DefaultNegotiationStrategy, NegotiatedPlan,
    PayloadPlacementDecision, PlacementRejection, negotiate}`

### Notes

- This release is **purely additive**. All 0.1.0 paths remain authoritative
  and unchanged. Aliases are tested for identity in `tests/domain_modules.rs`.
- The `CapabilityKind` / `CapabilityRequest` split is preserved deliberately:
  `CapabilityKind` is identity (used as a manifest lookup key);
  `CapabilityRequest` adds policy (`desired: SupportState`,
  `level: RequirementLevel`).

## [0.1.0] - 2026-05-09

- Initial publish: provider-neutral lifecycle abstraction and normalizer for
  AI harnesses. See `README.md` for the full surface.
```

- [ ] **Step 3: Verify the crate still builds and the version is picked up**

Run: `cargo build -p lifeloop-cli && cargo pkgid -p lifeloop-cli`
Expected: build clean; `cargo pkgid` reports `...#lifeloop-cli@0.1.1` (or `...#0.1.1` depending on cargo version).

- [ ] **Step 4: Commit**

```bash
git add Cargo.toml Cargo.lock CHANGELOG.md
git commit -m "chore(release): bump to 0.1.1 with domain module aliases"
```

(`Cargo.lock` will update for the workspace member version. If lefthook runs `cargo fmt`, let it; the formatter is the last thing before commit per the workflow rule.)

---

## Task 7: Branch + PR

**Files:** none (workflow only)

- [ ] **Step 1: Branch from main BEFORE Task 1**

This step belongs at the top of the plan, not the bottom — branch creation is a precondition for Tasks 1-6, not a follow-up. The implementer should run this before Task 1 Step 1:

```bash
git checkout main && git pull --ff-only origin main
git checkout -b release/0.1.1-domain-module-aliases
```

If on first reading you discover Tasks 1-6 were already committed onto `main` directly (against the workflow rule "branch from main after sync"), **stop and ask the user before doing anything destructive**. Do not run `git reset --hard` to "rewind" main — that's a destructive operation that needs explicit authorization per the global safety rule. The safe recovery is:
1. `git status` and `git log origin/main..main` to confirm what landed.
2. Surface the situation to the user with the commit list.
3. Wait for explicit guidance (typical resolution: create the branch from current `main`, then `git push origin main` so origin catches up — *not* a reset).

- [ ] **Step 2: Push and open MR**

Run:
```bash
git push -u origin release/0.1.1-domain-module-aliases
```

Then open the MR (this repo uses GitLab — `glab mr create` if installed, otherwise the URL printed by `git push`).

MR title: `feat(lib): 0.1.1 domain module aliases`

MR description:
```markdown
## Summary

Additive 0.1.1 release: introduces four pure-re-export submodules
(`event`, `manifest`, `adapters`, `capability`) so consumers can use
module-shaped imports without lifeloop changing canonical paths or type
shapes.

Driven by the ccd-guide slimdown plan (`docs/plans/2026-05-09-ccd-slimdown.md`
on `ccd-723-slimdown-design`) which assumed module-organized imports.

## What is and isn't aliased

Aliased (additive paths exist now):
- `lifeloop::event::*` → canonical `lifeloop::*` (LifecycleEventKind etc.)
- `lifeloop::manifest::*` → canonical `lifeloop::*` (manifest types + registry)
- `lifeloop::adapters::*` → canonical `lifeloop::*` (per-harness manifest fns)
- `lifeloop::capability::*` → canonical `lifeloop::router::*`

NOT aliased (consumer-side adapt):
- No flat `Capability` type — `CapabilityKind` (identity) and
  `CapabilityRequest` (identity + policy) stay split.
- No `EventName` rename — `LifecycleEventKind` is the published name.
- No `negotiate_capabilities` rename — `negotiate` stays inside
  `capability::` namespace.
- No `all_manifests` rename — `manifest_registry` stays.

## Test plan

- [x] `cargo test -p lifeloop-cli --test domain_modules` (new identity test)
- [x] `cargo test -p lifeloop-cli` (full suite, no regressions)
- [x] `cargo build -p lifeloop-cli` (clean build)
```

- [ ] **Step 3: Address CI / review and merge per `cpprrm`**

Once CI is green and any review feedback is addressed, merge per the
`cpprrm` shorthand (no further chat authorization needed for this in-scope PR).
After merge, fast-forward local main:

```bash
git checkout main && git pull --ff-only origin main
```

- [ ] **Step 4: Publish 0.1.1 to crates.io**

Run:
```bash
cargo publish -p lifeloop-cli --dry-run
cargo publish -p lifeloop-cli
```

Expected: dry-run clean, then publish succeeds. ccd-guide can then bump its
`Cargo.toml` to `lifeloop = "0.1.1"` and pick up the aliases.

(Crate-publish step requires explicit go-ahead per general "destructive /
external" rule — confirm with the user before running the non-dry-run
publish, since publish is irreversible.)

---

## Self-Review

**Spec coverage:**
- ✅ Adds the four domain modules ccd-guide's plan assumed (`adapters`, `capability`, `event`, `manifest`).
- ✅ Preserves the principled type design (split `CapabilityKind`/`CapabilityRequest`, `LifecycleEventKind` name, `manifest_registry` name).
- ✅ Non-breaking — every 0.1.0 path remains identical.
- ✅ Compile-time identity test covers *every* `pub use` from Tasks 1-4 (Task 5 has one check per aliased name; if a future change adds an alias without a check, the test serves as a forcing function).
- ✅ Versioned release with CHANGELOG entry.
- ❌ Does **not** make ccd-guide Phase 1 a pure import flip — naming-half remains. Acknowledged explicitly in the goal section; ccd-guide must amend its Phase 1 plan separately.

**Placeholder scan:** none — every step has concrete code, exact paths, exact commands. The test uses real enum variants (`SessionStarting`) confirmed against `src/lib.rs:91-120`.

**Type consistency:** the aliased identifiers in Tasks 1-4 match the canonical-path identifiers used in Task 5's identity test (verified against `src/lib.rs` line numbers in the canonical-surface table). Test function name `function_aliases_unify_with_canonical_paths` matches the `cargo test` invocation expected output in Step 2.

**Two known asymmetries (intentional):**
- `manifest::` exposes `manifest_registry` and `lookup_manifest`, and `adapters::` *also* exposes them. Both module names are reasonable entry points for "give me the registry" depending on whether the consumer is thinking in terms of "manifest data" or "adapters." Pick whichever reads better at the call site.
- `_*_alias` free functions in `tests/domain_modules.rs` are dead code at runtime — they exist only so the compiler checks the type-equality bindings. The `#[allow(dead_code, unused_imports)]` attribute is intentional.
