DUNIN7 · LOOMWORKS · RECORD
record.dunin7.com
Status Current
Path phases/phase-45-delegation-contract-and-approval-cards/phase-45-cr-delegation-contract-and-approval-cards-v0_1.md

Phase 45 — Change Request — Delegation Contract + Approval Cards + OVA Seam

CR identifier. CR-2026-059 Version. 0.1 Date. 2026-05-06 Status. Approved for execution. Author. Claude.ai (CR drafting layer). Operator: Marvin Percival. Executing agent. Claude Code (CC) on DUNIN7-M4. Depends on. Phase 44 complete (tag phase-44-sse-and-proactive-behavior, substrate b474d26, frontend 0f1edee). Phase 43 complete (personal memory contribution, remember_about_me / forget_about_me flows). Phase 42 complete (classify → route → respond pipeline, stub intents, conversation turns). Phase 41 complete (personal engagement, ActorRef(kind="companion")). Phase 40 complete (vocabulary wall, orchestration API). Informed by. Phase 45 scoping note v0.1 (eight decisions settled). Companion as agent investigation v0.1 (§§3–7, 10 — capability tiers, delegation contract, approval card pattern, trust escalation, OVA credential seam, implementation sequence). Personal memory investigation v0.1 (§§3, 7 — personal engagement as storage surface). Phase 44 CR (SSE channel, notification surface, trigger evaluator, event bus). Phase 43 CR (personal memory contribution, remember_about_me flow, cross-promotion instruction). Phase 42 CR (intent taxonomy, stub intents, classifier prompt, router architecture). Product identity standing note v0.1. Current status manifest v0.29.


1. Purpose

Phase 45 closes Arc 2 by giving the Companion the ability to act — not just inform. Phases 41–44 built a Companion that converses, classifies intent, routes to engine operations, reads and writes personal memory, pushes notifications through SSE, evaluates triggers, and observes patterns. What it cannot yet do is propose an engine operation and execute it with the Operator's approval.

Three capabilities ship:

  1. Delegation contract. A set of structured assertions in the personal engagement that define what the Companion is authorized to do. Each delegation specifies a capability identifier, scope (which engagements), constraints, and approval mode (per_action or pre_authorized). The contract is managed through conversation — the Operator grants, queries, and revokes delegations by talking to the Companion.
  1. Approval card pattern. When the Companion proposes an action that requires approval, it creates an approval card — a notification with structured action metadata and [Approve] / [Decline] controls. The Operator approves via the notification surface (tapping the button) or via conversation ("yes, go ahead"). Approval triggers the engine operation. Decline dismisses the card.
  1. Stub intent activation. Three of the four stub intents from Phase 42 become live: request_draft (Companion identifies shape type, checks delegation, creates approval card or executes directly), approve_draft (Companion processes conversational approval of a pending action), and request_revision (Companion proposes re-production through the same delegation check and approval flow). request_redirect remains a stub.

One thing is seam-only: the OVA credential interface. Phase 45 implements authorization as a delegation assertion lookup. The function's contract is designed so that OVA can replace the implementation later without changing call sites.

Full-stack phase. One migration (extend companion_notifications). Two checkpoints (substrate then frontend). Arc 2 complete after this phase.


2. Scoping decisions adopted

All eight decisions from the scoping note v0.1 are adopted without modification.

| Decision | Summary | |----------|---------| | S1 | Capability scope: Tier 1 engine-internal only. The Companion can propose and execute operations the Operator could do through the existing UI (trigger shaping, initiate render, commit assertion). Tiers 2–5 (external) are post-Arc 2. | | S2 | Delegation contract as structured personal memory assertions. Each delegation is a committed assertion with structured JSON content: capability identifier, scope, constraints, approval mode. Delegation assertions are distinguished from regular personal memory by a delegation category or prefix convention. | | S3 | Approval card pattern: notifications with action controls. The companion_notifications table gains columns for approval metadata. Approval cards are delivered through SSE using the existing notification_created event type. The frontend distinguishes them by approval_status. | | S4 | Three stub intents activated (request_draft, approve_draft, request_revision). Router handlers change from returning stub markers to performing real operations with delegation check and approval flow. request_redirect remains a stub. | | S5 | Approval through conversation and notification surface. Two paths, same outcome. Notification surface: [Approve] / [Decline] buttons call REST endpoints. Conversation: "yes, go ahead" classified as approve_draft. Both converge on the same approval-processing function. | | S6 | OVA credential seam: verify_companion_authorization function. Currently implemented as delegation assertion lookup. Interface designed for OVA replacement. ActorRef extended with capability_ref and approval_mode. | | S7 | Trust escalation: substrate support, no proactive suggestion. Pre-authorized delegation is available but Operator-initiated only. The Companion does not suggest "would you like me to just do this?" | | S8 | Migration: extend companion_notifications, no new table. Six new nullable columns for approval card metadata. Informational notifications unaffected. |


3. Pre-flight and baseline

3.1 Baseline

3.2 Pre-flight checklist

Step 0 confirms:

  1. companion_notifications table exists with columns from Phase 44 (person_id, trigger_type, trigger_ref, title, body, engagement_id, status, snoozed_until, timestamps). [CC verifies: model file, column names and types, existing status enum values (pending, delivered, seen, dismissed).]
  2. SSE event bus exists and delivers person-scoped events. [CC verifies: bus module file, publish function signature, that notification_created and notification_updated event types are in use.]
  3. Notification REST endpoints exist (GET /operator/notifications, PATCH .../seen, PATCH .../dismiss). [CC verifies: router file, function names, response schemas.]
  4. SSE endpoint exists at GET /operator/notifications/stream. [CC verifies: streaming response, heartbeat, auth dependency.]
  5. Notification service exists with CRUD functions. [CC verifies: service module file, create/list/mark-seen/dismiss functions.]
  6. Personal engagement exists per person (Phase 41). [CC verifies: personal_engagement_id FK on person table.]
  7. Personal memory loader exists (load_personal_memory). [CC verifies: function file and signature — this is the function that loads committed assertions from the personal engagement.]
  8. remember_about_me intent and router handler exist (Phase 43). [CC verifies: router dispatches remember_about_me, creates held assertion in personal engagement with conversational commit.]
  9. forget_about_me intent and router handler exist (Phase 43). [CC verifies: router dispatches forget_about_me, keyword-matches and retracts personal assertions.]
  10. Stub intents exist in classifier prompt: request_draft, approve_draft, request_revision, request_redirect. [CC verifies: classifier prompt text asset lists all four, router returns stub markers for them.]
  11. ActorRef(kind="companion") exists (Phase 41). [CC verifies: model, kind enum includes companion, fields used for Companion-created events.]
  12. Vocabulary wall test exists (Phase 40). [CC verifies: how new schemas are registered, that approval-related schemas must pass the wall.]
  13. ClassifiedIntent model exists with intent, confidence, extracted_parameters. [CC verifies: model file, that extracted_parameters is a dict suitable for carrying action-specific data from the classifier.]
  14. Prompt assembly function exists (assemble_prompt). [CC verifies: component structure and assembly order (persona → engagement context → personal memory → cross-promotion → observation → intent instruction → conversation history).]
  15. Intent instruction templates exist for stub intents. [CC verifies: template files for request_draft, approve_draft, request_revision — these are the stub templates that will be replaced with live templates.]
  16. Frontend notification drawer exists (Phase 44). [CC verifies: component file, that it renders notification cards from the notification list. Identify where conditional rendering for approval cards inserts.]
  17. Frontend notification badge exists (Phase 44). [CC verifies: badge component, that it reads unseen count from notification state.]
  18. Frontend API client has notification endpoint calls (list, seen, dismiss). [CC verifies: API module, function signatures.]
  19. Frontend SSE connection hook exists. [CC verifies: hook file, that incoming events update notification state.]

Pre-flight divergence stops execution at Step 0 and drives a CR amendment.

3.3 CR archival

Archive this CR to docs/phase-crs/phase-45-cr-delegation-contract-and-approval-cards-v0_1.md at Step 0 before Step 1 begins.


4. Construction decisions this CR closes

D1 — Delegation assertion schema

Delegation assertions live in the personal engagement as committed assertions with structured JSON content. The assertion's content is structured (not natural language) so the router can query delegation state programmatically.

Content schema:


{
  "type": "delegation",
  "capability": "produce_specification",
  "scope": "all",
  "constraints": {},
  "approval_mode": "per_action"
}

Fields:

Delegation CRUD through conversation:

D2 — OVA credential seam: verify_companion_authorization

A function that answers: "is the Companion authorized to perform this action for this person on this engagement?"

Signature:


async def verify_companion_authorization(
    person_id: UUID,
    capability: str,
    engagement_id: UUID,
    db: AsyncSession
) -> AuthorizationResult

AuthorizationResult model:


class AuthorizationResult(BaseModel):
    authorized: bool
    delegation_ref: UUID | None  # assertion ID of the matching delegation
    approval_mode: str  # "per_action" | "pre_authorized" | "denied"
    reason: str  # human-readable explanation

Current implementation: query the personal engagement for committed assertions with type: "delegation" matching the requested capability. Check scope (does the delegation cover this engagement?). Check constraints (does the proposed action satisfy constraints?). Return the result.

OVA seam contract: the function name, signature, and return type are the stable interface. When OVA hardens, the internals change from "query personal memory" to "verify OVA credential against capability claim." Callers (router handlers, approval processor) are not modified.

[CC determines: the module location for this function — alongside the notification service, in the orchestration module, or in a new delegation module. The requirement is that the function is importable from router handlers and the approval endpoint.]

D3 — ActorRef extension

ActorRef(kind="companion") gains two optional fields:

These fields appear on every event the Companion creates through the delegation flow. Events created through the existing reactive flow (Phase 42 add_knowledge, Phase 43 remember_about_me) do not carry these fields.

[CC determines: whether capability_ref and approval_mode are added to the ActorRef model directly or carried in a separate authorization_context field on ActorRef. Direct fields are simpler; a nested object is cleaner for OVA replacement. The requirement is that the fields are present on Companion-created events and accessible for audit.]

D4 — Migration 0061: extend companion_notifications

Six new nullable columns on the companion_notifications table:

All columns nullable. Existing informational notifications are unaffected — rows without approval metadata continue to render as informational cards.

No new indexes. The existing (person_id, status, created_at DESC) index serves approval card queries.

D5 — Approval card creation and processing

Creation flow:

When the router determines an action requires approval (delegation check returns per_action), it creates a companion_notifications row with:

The notification is pushed through SSE using the existing event bus (notification_created event type).

Approval endpoint:

POST /operator/notifications/{id}/approve

Decline endpoint:

POST /operator/notifications/{id}/decline

Both endpoints use the vocabulary wall.

[CC determines: the exact endpoint path prefix — whether these are under /operator/notifications/ alongside the Phase 44 endpoints or under a new prefix. The requirement is that approve/decline are person-scoped and authenticated.]

D6 — Pre-authorization bypass

When the delegation check returns pre_authorized, the Companion skips the approval card and executes the engine operation directly.

Flow:

  1. Router handler calls verify_companion_authorization. Result: authorized=True, approval_mode="pre_authorized".
  2. Router dispatches the engine operation immediately with ActorRef(kind="companion") carrying capability_ref and approval_mode = "pre_authorized".
  3. Router creates an informational notification (not an approval card) reporting the outcome: "I produced the Soil Analysis specification — it's in your Workshop." Delivered through SSE.
  4. The responder generates a conversational confirmation.

No approval card, no [Approve] button, no REST call. The Operator has already granted standing authority.

D7 — Intent activation: request_draft

Classifier update: the request_draft description in the classifier prompt changes from a stub description to an action description. Parameter extraction guidance added:

[CC determines: the parameter extraction field names and the exact classifier prompt wording, consistent with existing parameter extraction patterns (e.g., add_knowledge extracts knowledge_text).]

Intent instruction template: replaces the stub template. The new template guides the responder through the delegation flow outcome:

Router handler:

  1. Extract shape_type, engagement_ref, grammar from extracted_parameters.
  2. Resolve the engagement (from the converse context or extracted parameters).
  3. Identify the corresponding engine capability (e.g., produce_specification).
  4. Call verify_companion_authorization(person_id, capability, engagement_id).
  5. If pre_authorized: dispatch engine operation (D6 flow). Return route result with operation data.
  6. If per_action: create approval card (D5 flow). Return route result with pending approval data.
  7. If denied (no delegation): return route result indicating no authorization. The responder explains the Companion needs delegation authority.

[CC determines: how to map the classifier's shape_type to an engine capability and to the actual engine function call (e.g., triggering shaping, calling the produce function). The requirement is that the router can dispatch the engine operation — identify the function to call and the parameters to pass.]

D8 — Intent activation: approve_draft

Classifier update: the approve_draft description changes from stub to live. This intent fires when the Operator says "yes, go ahead" or "approved" in conversation, after the Companion has proposed an action.

Router handler:

  1. Look up the most recent pending_approval notification for this person. [CC determines: the lookup strategy — most recent by created_at, or matched by conversation context (the Companion's last response proposed a specific action). Most recent is simpler; context-matched is more accurate if multiple approval cards are pending.]
  2. If found: process approval (same logic as the POST .../approve endpoint — update status, dispatch engine operation, record result, push SSE update).
  3. If not found: return a route result indicating nothing is pending. The responder explains there's nothing to approve.

Intent instruction template: replaces the stub template. Guides the responder:

D9 — Intent activation: request_revision

Classifier update: the request_revision description changes from stub to live. Parameter extraction:

Router handler:

  1. Identify the target (shape or render) from extracted_parameters and engagement context.
  2. Determine the engine operation: re-trigger shaping for a shape revision, re-trigger rendering for a render revision. If the target supports incremental re-render (Phase 36), use that path.
  3. Call verify_companion_authorization with the appropriate capability.
  4. Same approval flow as request_draft (D7 steps 5–7).

Intent instruction template: replaces the stub template. Same outcome guidance as request_draft.

[CC determines: how to identify the target — whether the classifier extracts enough information to pinpoint the shape/render, or whether the router queries available shapes/renders and matches by description (similar to request_download in Phase 42).]

D10 — Classifier prompt update

The classifier prompt text asset is updated:

  1. request_draft moves from the stub section to the live section with full action description and parameter extraction guidance.
  2. approve_draft moves from the stub section to the live section.
  3. request_revision moves from the stub section to the live section with parameter extraction guidance.
  4. request_redirect remains in the stub section.

The stub section shrinks from four intents to one. The live section grows from nine intents (Phase 43) to twelve.

[CC determines: the exact prompt wording for each activated intent, consistent with the existing classifier prompt structure and Operator vocabulary.]


5. Migration

One migration: Alembic 0061.

Adds six nullable columns to the companion_notifications table per D4: action_type, action_params, approval_status, approved_at, declined_at, execution_result.

May also add a new value to the trigger_type enum if CC determines a new trigger type value is needed (D5). [CC determines: whether enum alteration is needed or whether an existing value suffices.]

No data backfill. Existing rows have null values for all new columns.


6. Module changes

Phase 45 modifies existing files and creates new files.

| File | Change | |------|--------| | New: src/loomworks/delegation/ module | Delegation assertion schema, query functions, verify_companion_authorization. [CC determines: whether this is a new top-level module or nested under orchestration/.] | | New: delegation assertion schema | Pydantic model for delegation content per D1. | | New: verify_companion_authorization function | OVA seam per D2. | | New: AuthorizationResult model | Per D2. | | ActorRef model | Add capability_ref and approval_mode fields per D3. | | companion_notifications model | Add six new columns per D4. | | Notification schemas | Add approval-specific fields to NotificationResponse (action_type, approval_status, action_params, execution_result). New schemas for approve/decline request and response. | | Notification service | Add approval card creation, approval processing, decline processing functions per D5. | | New: approval endpoints | POST /operator/notifications/{id}/approve and POST /operator/notifications/{id}/decline per D5. In the notifications router. | | Router (src/loomworks/orchestration/router.py) | Replace stub handlers for request_draft, approve_draft, request_revision with live handlers per D7–D9. | | Classifier prompt text asset | Update intent taxonomy: three intents from stub to live, parameter extraction guidance per D10. | | Intent instruction templates | Replace stub templates for request_draft, approve_draft, request_revision with live templates per D7–D9. | | remember_about_me router handler | Extend to recognize delegation instructions and create structured delegation assertions per D1. | | forget_about_me router handler | Extend to match and revoke delegation assertions per D1. | | ask_about_past_input router handler (or new sub-handler) | Extend to query and summarize active delegations per D1. | | Alembic | Migration 0061 per D4. |

Frontend changes:

| File | Change | |------|--------| | Notification card component | Add approval card variant with [Approve] and [Decline] buttons per scoping note §4. Conditional rendering based on approval_status. | | API client | Add approve and decline endpoint calls. | | Notification drawer | Handle approval card state transitions (pending → executing → complete, pending → declined). | | Notification badge | Include pending approval cards in badge count. |

[CC determines: exact file paths and component names following existing frontend conventions.]


7. Test suite

Substrate tests

Delegation assertions (D1):

  1. Delegation assertion created with structured JSON content in personal engagement.
  2. Delegation assertion content includes required fields (type, capability, scope, approval_mode).
  3. Delegation assertion queryable: given a person and capability, return matching delegation.
  4. Delegation query returns null when no matching delegation exists.
  5. Delegation query respects scope: delegation for "all" matches any engagement.
  6. Delegation query respects scope: delegation for specific engagement ID matches only that engagement.
  7. Delegation revocation: retracted assertion no longer returned by delegation query.

OVA seam — verify_companion_authorization (D2):

  1. Returns authorized=True, approval_mode="per_action" when per-action delegation exists.
  2. Returns authorized=True, approval_mode="pre_authorized" when pre-authorized delegation exists.
  3. Returns authorized=False when no delegation exists for the requested capability.
  4. Returns authorized=False when delegation exists but scope does not cover the requested engagement.
  5. Returns authorized=False when delegation exists but constraints do not match the proposed action.
  6. Returns the delegation assertion UUID as delegation_ref when authorized.

ActorRef extension (D3):

  1. ActorRef(kind="companion") accepts capability_ref and approval_mode.
  2. Events created with extended ActorRef persist capability_ref and approval_mode.

Approval card creation and processing (D4, D5):

  1. Approval card creation populates action_type, action_params, approval_status on notification row.
  2. Approval card has approval_status = "pending_approval".
  3. POST /operator/notifications/{id}/approve sets approval_status = "approved", approved_at.
  4. POST /operator/notifications/{id}/approve triggers the engine operation specified in action_params.
  5. POST /operator/notifications/{id}/approve records execution_result with operation outcome.
  6. POST /operator/notifications/{id}/approve pushes notification_updated SSE event.
  7. POST /operator/notifications/{id}/decline sets approval_status = "declined", declined_at.
  8. POST /operator/notifications/{id}/decline does not trigger any engine operation.
  9. POST /operator/notifications/{id}/decline pushes notification_updated SSE event.
  10. Approve endpoint returns 404 for notification belonging to another person.
  11. Approve endpoint returns error for notification that is not pending_approval.
  12. Approve and decline endpoints pass vocabulary wall.

Pre-authorization bypass (D6):

  1. Pre-authorized delegation bypasses approval card, executes engine operation directly.
  2. Pre-authorization creates an informational notification (not approval card) reporting the outcome.
  3. Pre-authorization sends SSE notification with execution result.

request_draft routing (D7):

  1. request_draft classified correctly (no regression from Phase 42 classification tests).
  2. request_draft with per-action delegation creates approval card.
  3. request_draft with pre-authorized delegation executes directly.
  4. request_draft with no delegation returns denial (responder explains Companion needs authorization).
  5. request_draft extracts shape type and engagement from classifier parameters.

approve_draft routing (D8):

  1. approve_draft processes conversational approval of pending approval card.
  2. approve_draft with no pending card returns "nothing to approve."
  3. approve_draft triggers engine operation (same result as REST approval endpoint).

request_revision routing (D9):

  1. request_revision with per-action delegation creates approval card.
  2. request_revision with pre-authorized delegation executes directly.
  3. request_revision identifies target shape or render from classifier parameters.

Backward compatibility:

  1. All Phase 44 tests pass (SSE, notifications, triggers, observation, quiet mode).
  2. All Phase 43 tests pass (personal memory, cross-promotion, classifier).
  3. All Phase 42 tests pass (classify → route → respond pipeline).
  4. Informational notifications unaffected by new columns (null approval fields, same rendering).
  5. Vocabulary wall test passes with all new schemas.

Expected substrate test count: ~30–40 new tests.

Frontend tests

Approval card rendering:

  1. Approval card renders with Approve and Decline buttons when approval_status is present.
  2. Informational notification renders without Approve/Decline buttons (no regression).
  3. Approve button calls POST /operator/notifications/{id}/approve.
  4. Decline button calls POST /operator/notifications/{id}/decline.
  5. After approval: card updates to show executing/complete state.
  6. After decline: card updates to show declined state.

Badge and drawer integration:

  1. Badge count includes pending approval cards.
  2. Resolved approval cards (approved/declined) no longer counted in badge.

Expected frontend test count: ~8–12 new vitest tests.


8. Order of operations

| Step | What | Auto/checkpoint | |------|------|-----------------| | 0 | Pre-flight: verify baseline (substrate 1,701 + 26 skipped, frontend 270 vitest, Alembic 0060). Verify all 19 pre-flight items. Archive this CR to docs/phase-crs/. | Auto | | 1 | Migration 0061: extend companion_notifications with six nullable columns (D4). Verify migration runs, columns exist, existing rows unaffected. | Auto | | 2 | Delegation assertion schema and query functions (D1). Delegation content model, create/query/revoke functions. Tests §7 items 1–7. | Auto | | 3 | OVA seam: verify_companion_authorization function and AuthorizationResult model (D2). Tests §7 items 8–13. | Auto | | 4 | ActorRef extension (D3). Add capability_ref and approval_mode. Tests §7 items 14–15. | Auto | | 5 | Approval card creation and processing (D5). Notification service additions, approve/decline endpoints, SSE integration. Tests §7 items 16–27. | Auto | | 6 | Pre-authorization bypass (D6). Direct execution flow for pre-authorized delegations. Tests §7 items 28–30. | Auto | | 7 | Intent activation: request_draft handler, classifier prompt update, intent instruction template (D7, D10). Tests §7 items 31–35. | Auto | | 8 | Intent activation: approve_draft handler, intent instruction template (D8, D10). Tests §7 items 36–38. | Auto | | 9 | Intent activation: request_revision handler, intent instruction template (D9, D10). Tests §7 items 39–41. | Auto | | 10 | Delegation CRUD through conversation: extend remember_about_me, forget_about_me, delegation query response (D1 CRUD). | Auto | | 11 | Backward compatibility and vocabulary wall. Tests §7 items 42–46. Full substrate test sweep. | Auto | | A | Checkpoint A — Operator confirms substrate work. All substrate tests pass. | Checkpoint | | 12 | Frontend: approval card component with Approve/Decline buttons. API client additions for approve/decline endpoints. Tests §7 items 47–52. | Auto | | 13 | Frontend: notification drawer and badge updates for approval cards. Tests §7 items 53–54. | Auto | | 14 | Frontend verification: npm run lint && npx tsc --noEmit && npm run build && npm run test clean. | Auto | | B | Checkpoint B — Operator confirms. Tag both repos phase-45-delegation-contract-and-approval-cards. Implementation notes. Arc 2 complete. | Checkpoint |


9. Acceptance gate

  1. Delegation contract assertions are stored in personal engagement with structured content.
  2. verify_companion_authorization correctly resolves delegation state (authorized/denied, per-action/pre-authorized, scope check, constraint check).
  3. Approval card appears on notification surface when Companion proposes an action (per-action delegation).
  4. [Approve] button triggers engine operation. [Decline] button dismisses without action.
  5. Pre-authorized delegation bypasses approval and executes directly with informational notification.
  6. request_draft intent is live (no longer stub). Produces approval card or direct execution.
  7. approve_draft intent is live. Processes conversational approval.
  8. request_revision intent is live. Proposes re-production through approval flow.
  9. ActorRef on Companion-created events carries capability_ref and approval_mode.
  10. OVA seam function exists with the interface shape for future OVA replacement.
  11. Vocabulary wall passes with all new schemas.
  12. All substrate tests pass. All frontend tests pass (vitest, lint, tsc, build).

10. What this phase does not build


11. Post-CR state (expected)


12. Relationship to subsequent phases


13. Kickoff prompt for the Claude Code session


Read the Change Request document at the path I supply below. This is
CR-2026-059 v0.1, the Phase 45 Change Request. You are the executing
agent named in the CR.

CR path: ~/Downloads/phase-45-cr-delegation-contract-and-approval-cards-v0_1.md

Phase 45 adds delegation contract, approval cards, and OVA seam.
The Companion becomes an agent with bounded autonomy: it proposes
engine operations, the Operator approves or declines, and a
delegation contract governs what the Companion can do. Three stub
intents activate (request_draft, approve_draft, request_revision).
Full-stack phase. One migration (extend companion_notifications).
Two checkpoints: A after substrate, B after frontend. Arc 2 final
phase.

Code baseline: tag phase-44-sse-and-proactive-behavior (substrate
at b474d26, frontend at 0f1edee). Substrate: 1,701 tests, 26
skipped, Alembic 0060. Frontend: 270 vitest (27 files).
eslint/tsc/build clean.

Run pre-flight (Step 0) per Section 3.2. Nineteen pre-flight items
covering substrate (items 1–15) and frontend (items 16–19).

Per Section 3.3: archive this CR to
docs/phase-crs/phase-45-cr-delegation-contract-and-approval-cards-v0_1.md
in the substrate repo at Step 0.

Per Section 8, fourteen steps with two checkpoints. Steps 1–11
(substrate) then Checkpoint A. Steps 12–14 (frontend) then
Checkpoint B (final, tag both repos, Arc 2 complete).

Pre-flight surprises stop at Step 0 and drive a CR amendment.

Implementation notes at Checkpoint B:
docs/phase-impl-notes/phase-45-implementation-notes-v0_1.md

DUNIN7 — Done In Seven LLC — Miami, Florida Phase 45 CR — v0.1 — 2026-05-06