Land Imagination planning lane

This commit is contained in:
Meta Vens 2026-05-01 09:15:57 +01:00
commit 5f2ecf6d80
21 changed files with 812 additions and 69 deletions

View file

@ -86,6 +86,14 @@
"status": "blocked",
"title": "Implementation"
},
{
"id": "imagination",
"note": "Planning material ready: 2 captures, 1 backlog items, 1 roadmap streams, 1 objective drafts.",
"ownerRole": "epiphany-imagination",
"recommendedAction": "roleLaunch",
"status": "ready",
"title": "Imagination / Planning"
},
{
"id": "modeling",
"note": "Checkpoint missing: Re-gather source context before editing.",
@ -237,6 +245,12 @@
}
},
"roleResults": {
"imagination": {
"bindingId": "planning-synthesis-worker",
"note": "No authoritative Epiphany state exists for this thread.",
"roleId": "imagination",
"status": "missingState"
},
"modeling": {
"bindingId": "modeling-checkpoint-worker",
"note": "No authoritative Epiphany state exists for this thread.",

View file

@ -59,6 +59,8 @@ async function smokeViewport(browser, viewport, screenshotPath) {
await page.getByText("Artifact Bundles").waitFor();
await page.getByRole("button", { name: "Prepare Checkpoint" }).waitFor();
await page.getByRole("button", { name: "Adopt Draft" }).waitFor();
await page.getByRole("button", { name: "Launch Imagination" }).waitFor();
await page.getByRole("button", { name: "Read Imagination" }).waitFor();
await page.getByRole("button", { name: "Launch Modeling" }).waitFor();
await page.getByRole("button", { name: "Read Modeling" }).waitFor();
await page.getByRole("button", { name: "Launch Verification" }).waitFor();
@ -73,6 +75,8 @@ async function smokeViewport(browser, viewport, screenshotPath) {
await page.getByText("coordinatorPlan sample completed.").waitFor();
await page.getByRole("button", { name: "Prepare Checkpoint" }).click();
await page.getByText("prepareCheckpoint sample completed.").waitFor();
await page.getByRole("button", { name: "Read Imagination" }).click();
await page.getByText("readImaginationResult sample completed.").waitFor();
await page.getByRole("button", { name: "Read Modeling" }).click();
await page.getByText("readModelingResult sample completed.").waitFor();
await page.getByRole("button", { name: "Read Reorient" }).click();

View file

@ -111,7 +111,10 @@ fn run_operator_action(
"statusSnapshot" => run_status_snapshot(&repo_root, request),
"coordinatorPlan" => run_coordinator_plan(&repo_root, request),
"inspectUnity" => run_unity_inspection(&repo_root, request),
"launchModeling"
"launchImagination"
| "readImaginationResult"
| "acceptImagination"
| "launchModeling"
| "readModelingResult"
| "acceptModeling"
| "launchVerification"

View file

@ -15,7 +15,7 @@ import { useEffect, useMemo, useState } from "react";
import { loadOperatorSnapshot, runOperatorAction } from "./operatorApi";
import type { ArtifactBundle, OperatorAction, OperatorActionResult, OperatorSnapshot, StatusRequest } from "./types";
const roleOrder = ["implementation", "modeling", "verification", "reorientation"];
const roleOrder = ["implementation", "imagination", "modeling", "verification", "reorientation"];
const actionButtons: Array<{
action: OperatorAction;
label: string;
@ -23,6 +23,7 @@ const actionButtons: Array<{
title: string;
requiresThread?: boolean;
requiresReadyState?: boolean;
requiresImaginationPatch?: boolean;
requiresModelingPatch?: boolean;
requiresVerificationResult?: boolean;
requiresReorientResult?: boolean;
@ -78,6 +79,33 @@ const actionButtons: Array<{
requiresContinueImplementation: true,
icon: "play",
},
{
action: "launchImagination",
label: "Launch Imagination",
runningLabel: "Launching",
title: "Launch the fixed imagination/planning worker for this thread",
requiresThread: true,
requiresReadyState: true,
icon: "play",
},
{
action: "readImaginationResult",
label: "Read Imagination",
runningLabel: "Reading",
title: "Read the latest imagination/planning finding",
requiresThread: true,
icon: "eye",
},
{
action: "acceptImagination",
label: "Accept Imagination",
runningLabel: "Accepting",
title: "Accept a reviewed planning-only patch into Epiphany state",
requiresThread: true,
requiresReadyState: true,
requiresImaginationPatch: true,
icon: "accept",
},
{
action: "launchModeling",
label: "Launch Modeling",
@ -290,6 +318,10 @@ export function App() {
Boolean(latestArtifact?.implementationAudit) && latestArtifact?.implementationAudit?.workspaceChanged === false;
const readyState = scene.stateStatus === "ready";
const currentThreadId = request.threadId;
const imaginationFinding = roleResults?.imagination?.finding;
const canAcceptImagination =
text(roleResults?.imagination?.status).toLowerCase() === "completed" &&
Boolean(imaginationFinding?.statePatch?.planning);
const modelingFinding = roleResults?.modeling?.finding;
const canAcceptModeling =
text(roleResults?.modeling?.status).toLowerCase() === "completed" && Boolean(modelingFinding?.statePatch);
@ -356,6 +388,7 @@ export function App() {
{actionButtons.map((button) => {
const needsThread = button.requiresThread && !currentThreadId;
const needsState = button.requiresReadyState && !readyState;
const needsImagination = button.requiresImaginationPatch && !canAcceptImagination;
const needsModeling = button.requiresModelingPatch && !canAcceptModeling;
const needsVerification = button.requiresVerificationResult && !canAcceptVerification;
const needsReorient = button.requiresReorientResult && !canAcceptReorient;
@ -366,6 +399,7 @@ export function App() {
runningAction !== null ||
needsThread ||
needsState ||
needsImagination ||
needsModeling ||
needsVerification ||
needsReorient ||
@ -373,9 +407,11 @@ export function App() {
needsImplementation ||
needsNoDiffReview;
const title = needsThread
? "Prepare a checkpoint or enter a persisted thread id first"
? "Prepare a checkpoint or enter a persisted thread id first"
: needsState
? "Prepare Epiphany state before launching this lane"
: needsImagination
? "Read a completed imagination result with a planning patch before accepting it"
: needsModeling
? "Read a completed modeling result with a state patch before accepting it"
: needsVerification
@ -657,6 +693,7 @@ export function App() {
<div>
<SectionHeader title="Findings" icon={<FileText size={18} />} />
<div className="stack">
<Finding title="Imagination / Planning" result={roleResults.imagination} />
<Finding title="Modeling / Checkpoint" result={roleResults.modeling} />
<Finding title="Verification / Review" result={roleResults.verification} />
<Finding title="Reorientation" result={reorientResult} findingKey="finding" />

View file

@ -12,6 +12,9 @@ export type OperatorAction =
| "inspectUnity"
| "prepareCheckpoint"
| "continueImplementation"
| "launchImagination"
| "readImaginationResult"
| "acceptImagination"
| "launchModeling"
| "readModelingResult"
| "acceptModeling"

View file

@ -34,10 +34,10 @@ The spine exists in more than twenty-five live paths:
- pre-compaction checkpoint intervention: app-server `TokenCount` handling now checks the same pressure mapper, and for loaded Epiphany threads crossing `shouldPrepareCompaction` it steers the active turn once with a bounded CRRC checkpoint directive to bank working context before compaction/reorientation. The per-turn dedupe bit and the pending compact handoff live in listener `ThreadState`; successful steering marks the active turn for a forced compact action at clean turn completion, while aborted turns clear the handoff. The intervention uses existing turn steering and turn-boundary automation rather than a new scheduler in [bespoke_event_handling.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/bespoke_event_handling.rs:1467), [codex_message_processor.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs:15405), and [thread_state.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/thread_state.rs:144).
- reorientation policy: app-server declares experimental read-only `thread/epiphany/reorient`, reuses the same live/stored thread view plus mapped pressure/freshness/watcher signals, and derives a bounded `resume`/`regather` verdict with checkpoint-path/frontier reasons without mutating, compacting, scheduling, notifying, or continuing work in [common.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/common.rs:365), [v2.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/v2.rs:4514), [codex_message_processor.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs:4394), and [codex_message_processor.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs:11424).
- CRRC coordinator recommendation: app-server declares experimental read-only `thread/epiphany/crrc`, reuses the same pressure/freshness/watcher/reorientation mapping plus the fixed `reorient-worker` binding and result read-back helper, recognizes already accepted reorientation findings so an accepted result is not recommended for acceptance again, and returns one bounded recommendation such as continue, prepare checkpoint, launch worker, wait, review, accept, or regather manually without mutating, launching, accepting, compacting, notifying, scheduling, or silently continuing work in [common.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/common.rs:370), [v2.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/v2.rs:4661), and [codex_message_processor.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs:4546).
- role ownership projection: app-server declares experimental read-only `thread/epiphany/roles`, reuses the same state/jobs/pressure/reorient/result/CRRC signals as the coordinator loop, and projects implementation, modeling/checkpoint, verification/review, and reorientation lanes with owner roles, status, linked jobs, authority scopes, and recommended scene actions without launching, scheduling, accepting, compacting, notifying, or mutating state in [common.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/common.rs:350), [v2.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/v2.rs:4161), and [codex_message_processor.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs:4310).
- role specialist launch/read-back/acceptance: app-server declares experimental `thread/epiphany/roleLaunch`, read-only `thread/epiphany/roleResult`, and write-gated `thread/epiphany/roleAccept`. `roleLaunch` accepts only fixed modeling/checkpoint and verification/review role ids, builds bounded specialist payloads from authoritative Epiphany state, routes them through the existing `epiphany_launch_job` job-control seam, and emits the normal `jobLaunch` state update for `jobBindings` only. `roleResult` resolves the fixed or requested binding through the current `agent_jobs` backend and projects structured output as a reviewable finding with verdict, summary, next safe move, inspected files, frontier/evidence ids, optional modeling `statePatch`, raw result, and errors without mutation, promotion, scheduling, or hidden continuation. `roleAccept` currently accepts only completed modeling/checkpoint findings with a valid modeling-safe `statePatch`, applies it through the existing Epiphany state validator, appends audit observation/evidence, and emits `stateUpdated` with source `roleAccept`. Implementation stays owned by the main coding agent, verification output remains review-only, and reorientation stays on the CRRC-specific `reorientLaunch` / `reorientResult` / `reorientAccept` path.
- role ownership projection: app-server declares experimental read-only `thread/epiphany/roles`, reuses the same state/jobs/pressure/reorient/result/CRRC signals as the coordinator loop, and projects implementation, Imagination/planning, modeling/checkpoint, verification/review, and reorientation lanes with owner roles, status, linked jobs, authority scopes, and recommended scene actions without launching, scheduling, accepting, compacting, notifying, or mutating state in [common.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/common.rs:350), [v2.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/v2.rs:4161), and [codex_message_processor.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs:4310).
- role specialist launch/read-back/acceptance: app-server declares experimental `thread/epiphany/roleLaunch`, read-only `thread/epiphany/roleResult`, and write-gated `thread/epiphany/roleAccept`. `roleLaunch` accepts fixed Imagination/planning, modeling/checkpoint, and verification/review role ids, builds bounded specialist payloads from authoritative Epiphany state, routes them through the existing `epiphany_launch_job` job-control seam, and emits the normal `jobLaunch` state update for `jobBindings` only. `roleResult` resolves the fixed or requested binding through the current `agent_jobs` backend and projects structured output as a reviewable finding with verdict, summary, next safe move, inspected files, frontier/evidence ids, optional `statePatch`, raw result, and errors without mutation, promotion, scheduling, or hidden continuation. `roleAccept` accepts completed Imagination findings only when they contain a valid planning-only patch with draft Objective Draft review gates, accepts completed modeling/checkpoint findings only with a modeling-safe `statePatch`, applies accepted patches through the existing Epiphany state validator, appends audit observation/evidence, and emits `stateUpdated` with source `roleAccept`. Implementation stays owned by the main coding agent, verification output remains review-only, and reorientation stays on the CRRC-specific `reorientLaunch` / `reorientResult` / `reorientAccept` path.
- fixed-lane coordinator: app-server declares experimental read-only `thread/epiphany/coordinator`, composes role, pressure, reorient, CRRC, role result, and reorient result signals, and returns one current action without mutation. Review acceptance and implementation clearance are intentionally different: an accepted verification finding clears implementation only when the verifier verdict is `pass`; accepted `needs-evidence`, `needs-review`, or `fail` findings route back toward modeling/checkpoint strengthening before code work resumes. `regatherManually` is now fallback, not a supervisor-puppeteering excuse: completed unaccepted modeling findings stop at `reviewModelingResult`, modeling acceptance precedes verification, and fixed lane actions are considered before manual regather.
- MVP operator view: `tools/epiphany_mvp_status.py` launches app-server over stdio, starts or reads one thread, gathers scene, pressure, reorient, jobs, roles, modeling/verification role results, reorient result, and CRRC responses, and renders them as text or JSON so the loop can be dogfooded without reading Rust.
- MVP operator view: `tools/epiphany_mvp_status.py` launches app-server over stdio, starts or reads one thread, gathers scene, pressure, reorient, jobs, roles, Imagination/modeling/verification role results, reorient result, and CRRC responses, and renders them as text or JSON so the loop can be dogfooded without reading Rust.
- MVP dogfood runner: `tools/epiphany_mvp_dogfood.py` launches app-server over stdio, drives a bounded state/role/CRRC/reorientation loop, and writes operator-safe JSON/text artifacts plus sealed transcript diagnostics under `.epiphany-dogfood/mvp-loop` so a human can audit the run through projections and compare it against a vanilla Codex reference.
- Agent function/API telemetry: `tools/epiphany_agent_telemetry.py` mechanically parses sealed AppServerClient JSONL transcripts into operator-safe telemetry with method counts, request shape, job/status/path fields, and visible function/tool names while sealing text, turn items, raw results, and direct agent messages.
- reorientation launch: app-server now declares experimental `thread/epiphany/reorientLaunch`, requires a loaded thread plus durable investigation checkpoint, consumes the live reorientation verdict, and launches one fixed `reorient-worker` binding through the existing job-control seam with explicit resume-versus-regather scope, checkpoint-derived payload, and structured-output expectations instead of pretending CRRC suddenly grew a scheduler in [common.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/common.rs:370), [v2.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/v2.rs:4575), and [codex_message_processor.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs:4519).
@ -1429,7 +1429,7 @@ These are deliberately not shipped yet:
- no broad automatic Compact-Rehydrate-Reorient-Continue coordinator; current code preserves and rehydrates valid Epiphany snapshots across resume/rollback/compaction, exposes context pressure, carries a durable investigation checkpoint packet, can steer active loaded turns once at the compaction-prep threshold to bank working context, returns a read-only reorientation verdict, can explicitly launch one bounded reorientation worker, read that worker's result back through a reviewable surface, explicitly accept completed findings into typed state, and can auto-run only the narrow coordinator-approved compact/reorient-worker launch actions at turn completion. Semantic launch/acceptance policy and next-step execution remain explicit rather than ambient scheduler behavior.
- no automatic watcher-driven graph or semantic invalidation.
- no code-intelligence graph.
- no broad specialist-agent scheduler or marketplace; only fixed manual modeling/checkpoint and verification/review role templates exist.
- no broad specialist-agent scheduler or marketplace; only fixed manual Imagination/planning, modeling/checkpoint, and verification/review role templates exist.
- no Epiphany-owned long-running job executor beyond the current runtime `agent_jobs` seam.
- no GUI source of truth; the current Tauri/React shell reflects and steers existing app-server surfaces.
- no durable retrieval-summary write from `thread/epiphany/retrieve`.
@ -1450,7 +1450,7 @@ stored Epiphany state
-> reflected as a read-only resume/regather verdict from checkpoint + freshness + watcher + pressure
-> reflected as a read-only CRRC recommendation over verdict + worker/result state
-> reflected as read-only MVP role ownership over implementation, modeling/checkpoint, verification/review, and reorientation lanes
-> fixed modeling/checkpoint and verification/review role workers can be launched explicitly
-> fixed Imagination/planning, modeling/checkpoint, and verification/review role workers can be launched explicitly
-> fixed role worker results can be read back for review without mutation
-> reviewed modeling/checkpoint statePatch findings can be accepted explicitly to grow graph/checkpoint state
-> completed unaccepted modeling findings stop for review before verification
@ -1464,7 +1464,7 @@ stored Epiphany state
-> successful writes emit thread/epiphany/stateUpdated
```
The current remaining missing organ is not the red pen, not the first chewing motion, not the first state-change bell, not the first client-shaped scene, not the first job/progress reflection, not the first live bound-job notification, not the first freshness lens, not the first watcher-backed invalidation read, not the first targeted graph/evidence context read, not the first bounded graph traversal read, not the first planning projection, not the first context-pressure gauge, not the first durable investigation checkpoint packet, not the first bounded reorientation verdict, not the first thin launcher seam, not the first explicit launch/interrupt authority surface over that seam, not the first runtime consumer that can act on the reorientation verdict without becoming a hidden tyrant, not the first read-back surface for that worker's findings, not the first explicit acceptance write for completed findings, not the first read-only CRRC coordinator recommendation, not the first role ownership board, not the first fixed role specialist launch/read-back templates, not the first review-gated modeling statePatch acceptance path, and no longer the first dogfood-facing operator view over the landed loop. Those exist. Proposal now has the first bits of map memory, focus, and selection hygiene: it can auto-select a bounded evidence-backed observation cluster when ids are omitted, requires accepting recent evidence behind selected observations, prioritizes stronger selected observations for proposal wording, reuses existing architecture nodes by concrete code-ref/path/id checks before creating new path nodes, can rescue unanchored graph nodes through strict unique semantic overlap, follows graph links and incident edges into the frontier, and reports match-kind-aware churn pressure from the actual proposal shape. Distillation now also has the first source-output-aware teeth for tool/model summaries. Promotion now notices risky deltas instead of just checking the shape of the envelope, treats graph expansion as risky even if the patch tries to mumble "low pressure" while dragging a new wall into the blueprint, token-matches strong verifier kinds so `contest` cannot sneak past as `test`, and validates investigation checkpoints against known evidence before the packet can be inked. Successful update/promote writes now emit `thread/epiphany/stateUpdated` with `source: "update"` or `source: "promote"`, event-level revision, and typed changed fields so clients no longer need to poll blindly, infer the cause from timing, or diff the whole state to know which top-level sections moved. Runtime `agent_job_progress` background events now also emit `thread/epiphany/jobsUpdated` with changed bound-job payloads so clients no longer need to poll `thread/epiphany/jobs` just to watch one real owner move, the bound jobs now reflect launcher id, authority scope, and backend kind/job id instead of treating raw runtime pointers as the architecture, `jobLaunch` / `jobInterrupt` can create or tear down those bindings without pretending reflection is authority, `reorientLaunch` can launch one fixed `reorient-worker` job from the checkpoint verdict without turning the verdict itself into a writer, `reorientResult` can read the worker's structured output back as a reviewable finding without promoting it, `reorientAccept` can explicitly bank a completed finding into typed observation/evidence plus optional scratch/checkpoint state, `roleLaunch` can launch fixed modeling/checkpoint and verification/review specialists over that same bounded seam, `roleResult` can read their structured outputs back without mutation, `roleAccept` can apply a reviewed modeling graph/checkpoint patch through the normal state validator, and `crrc` can recommend the next explicit action over that loop without doing the action itself. The scene, jobs, roles, role-result, role-accept, freshness, context, graphQuery, planning, pressure, reorient, crrc, reorient-result, and MVP status surfaces give clients compact or bounded views without making the GUI/client authoritative. The remaining missing organs are explicit planning adoption actions, Imagination synthesis, a second backend if `agent_jobs` stops being enough, automatic scheduling if dogfood proves it is needed, and richer product ergonomics after the MVP loop has been tested by a human.
The current remaining missing organ is not the red pen, not the first chewing motion, not the first state-change bell, not the first client-shaped scene, not the first job/progress reflection, not the first live bound-job notification, not the first freshness lens, not the first watcher-backed invalidation read, not the first targeted graph/evidence context read, not the first bounded graph traversal read, not the first planning projection, not the first context-pressure gauge, not the first durable investigation checkpoint packet, not the first bounded reorientation verdict, not the first thin launcher seam, not the first explicit launch/interrupt authority surface over that seam, not the first runtime consumer that can act on the reorientation verdict without becoming a hidden tyrant, not the first read-back surface for that worker's findings, not the first explicit acceptance write for completed findings, not the first read-only CRRC coordinator recommendation, not the first role ownership board, not the first fixed role specialist launch/read-back templates, not the first review-gated modeling statePatch acceptance path, not the first planning synthesis lane, and no longer the first dogfood-facing operator view over the landed loop. Those exist. Proposal now has the first bits of map memory, focus, and selection hygiene: it can auto-select a bounded evidence-backed observation cluster when ids are omitted, requires accepting recent evidence behind selected observations, prioritizes stronger selected observations for proposal wording, reuses existing architecture nodes by concrete code-ref/path/id checks before creating new path nodes, can rescue unanchored graph nodes through strict unique semantic overlap, follows graph links and incident edges into the frontier, and reports match-kind-aware churn pressure from the actual proposal shape. Distillation now also has the first source-output-aware teeth for tool/model summaries. Promotion now notices risky deltas instead of just checking the shape of the envelope, treats graph expansion as risky even if the patch tries to mumble "low pressure" while dragging a new wall into the blueprint, token-matches strong verifier kinds so `contest` cannot sneak past as `test`, and validates investigation checkpoints against known evidence before the packet can be inked. Successful update/promote writes now emit `thread/epiphany/stateUpdated` with `source: "update"` or `source: "promote"`, event-level revision, and typed changed fields so clients no longer need to poll blindly, infer the cause from timing, or diff the whole state to know which top-level sections moved. Runtime `agent_job_progress` background events now also emit `thread/epiphany/jobsUpdated` with changed bound-job payloads so clients no longer need to poll `thread/epiphany/jobs` just to watch one real owner move, the bound jobs now reflect launcher id, authority scope, and backend kind/job id instead of treating raw runtime pointers as the architecture, `jobLaunch` / `jobInterrupt` can create or tear down those bindings without pretending reflection is authority, `reorientLaunch` can launch one fixed `reorient-worker` job from the checkpoint verdict without turning the verdict itself into a writer, `reorientResult` can read the worker's structured output back as a reviewable finding without promoting it, `reorientAccept` can explicitly bank a completed finding into typed observation/evidence plus optional scratch/checkpoint state, `roleLaunch` can launch fixed Imagination/planning, modeling/checkpoint, and verification/review specialists over that same bounded seam, `roleResult` can read their structured outputs back without mutation, `roleAccept` can apply reviewed Imagination planning patches and reviewed modeling graph/checkpoint patches through the normal state validator, and `crrc` can recommend the next explicit action over that loop without doing the action itself. The scene, jobs, roles, role-result, role-accept, freshness, context, graphQuery, planning, pressure, reorient, crrc, reorient-result, and MVP status surfaces give clients compact or bounded views without making the GUI/client authoritative. The remaining missing organs are GitHub Issues import into planning captures, a second backend if `agent_jobs` stops being enough, automatic scheduling if dogfood proves it is needed, and richer product ergonomics after the MVP loop has been tested by a human.
```text
model/tool observations
@ -1478,9 +1478,9 @@ model/tool observations
The typed write path exists, the first deterministic observation/evidence distillation path exists, source-output-aware distillation now summarizes noisy tool/model output into typed evidence with final result/error/finished lines ahead of generic warnings, the first deterministic map/churn proposal path exists, proposal-quality hardening now auto-selects bounded observation clusters when ids are omitted, requires accepting recent evidence, reuses existing graph nodes by observed code refs, prioritizes stronger selected observations, rescues unanchored graph nodes by strict semantic overlap, focuses linked frontier context, reports match-kind-aware map-delta pressure, and the first verifier-backed promotion gate exists. The first map/churn safety layer also exists: promotion can reject state replacement patches that are not tied to explicit observations/evidence, that contain structurally broken subgoal, invariant, graph, frontier, checkpoint, investigation-checkpoint, or churn fields, that try to promote risky churn without a warning and stronger verifier evidence, that claim a graph expansion is low-pressure to dodge the warning gate, or that rely on substring verifier-kind accidents. The first live state notification layer also exists: accepted writes publish updated typed state plus event-level revision and typed changed fields; rejected promotions stay quiet.
In natural language: current Epiphany can preserve a map, show a map, project a compact scene from the map, project derived job/progress slots from the map and retrieval summary, reflect a thin launcher seam over bound work, explicitly launch or interrupt that bound work through a bounded authority surface, emit live bound-job progress notifications from runtime `agent_job_progress` events, project a read-only freshness lens over retrieval and graph staleness, layer watcher-backed invalidation telemetry onto that lens for loaded threads, project a targeted graph/evidence context shard from the map, traverse bounded graph neighborhoods and path/symbol matches from the map, project planning captures/backlog/roadmap/objective drafts without adopting them, project context pressure from token telemetry, bank an in-flight investigation packet in durable typed state, reflect that packet back into prompt/scene/context, turn those signals into a read-only resume-versus-regather verdict, recommend the next explicit CRRC action over the verdict and worker/result state, print that recommendation in a dogfood operator view, explicitly launch one bounded `reorient-worker` job from that verdict, read its result back for review, explicitly accept a completed finding into typed state, retrieve evidence for the map, draft one explicit observation/evidence patch, derive a bounded graph/frontier/churn candidate from verified evidence-backed observations with code refs, choose a small coherent observation set when the caller does not provide one, favor the strongest selected observation when drafting that candidate, reject or accept a verified candidate, apply explicit map/evidence/churn/planning edits, notify clients after successful writes, and sanity-check those edits before they hit the ledger. It still does not invalidate stale graph regions automatically, execute Epiphany-owned long-running jobs beyond the borrowed runtime `agent_jobs` backend, adopt objective drafts without a human gate, or coordinate broad specialist ownership. Teeth are installed; chewing is supervised; the first bell rings; the first wall board exists; the first shift board exists; the first freshness clipboard exists; the first door sensor now exists too; the first binder lens exists; the first graph-walk lens exists; the first planning wall exists; the first pressure gauge exists; the first ember packet exists; the first verdict slip now exists too; the first bounded launch lever and stop switch now exist too; the first explicit wakeup-hand now exists too; the first coordinator clipboard now exists too; the first operator readout exists too. The next slices are about giving the human a planning cockpit and making Imagination useful without letting it seize the red pen.
In natural language: current Epiphany can preserve a map, show a map, project a compact scene from the map, project derived job/progress slots from the map and retrieval summary, reflect a thin launcher seam over bound work, explicitly launch or interrupt that bound work through a bounded authority surface, emit live bound-job progress notifications from runtime `agent_job_progress` events, project a read-only freshness lens over retrieval and graph staleness, layer watcher-backed invalidation telemetry onto that lens for loaded threads, project a targeted graph/evidence context shard from the map, traverse bounded graph neighborhoods and path/symbol matches from the map, project planning captures/backlog/roadmap/objective drafts without adopting them, synthesize planning material through a review-gated Imagination lane, project context pressure from token telemetry, bank an in-flight investigation packet in durable typed state, reflect that packet back into prompt/scene/context, turn those signals into a read-only resume-versus-regather verdict, recommend the next explicit CRRC action over the verdict and worker/result state, print that recommendation in a dogfood operator view, explicitly launch one bounded `reorient-worker` job from that verdict, read its result back for review, explicitly accept a completed finding into typed state, retrieve evidence for the map, draft one explicit observation/evidence patch, derive a bounded graph/frontier/churn candidate from verified evidence-backed observations with code refs, choose a small coherent observation set when the caller does not provide one, favor the strongest selected observation when drafting that candidate, reject or accept a verified candidate, apply explicit map/evidence/churn/planning edits, notify clients after successful writes, and sanity-check those edits before they hit the ledger. It still does not invalidate stale graph regions automatically, execute Epiphany-owned long-running jobs beyond the borrowed runtime `agent_jobs` backend, import GitHub Issues into captures, adopt objective drafts without a human gate, or coordinate broad specialist ownership. Teeth are installed; chewing is supervised; the first bell rings; the first wall board exists; the first shift board exists; the first freshness clipboard exists; the first door sensor now exists too; the first binder lens exists; the first graph-walk lens exists; the first planning wall exists; the first pressure gauge exists; the first ember packet exists; the first verdict slip now exists too; the first bounded launch lever and stop switch now exist too; the first explicit wakeup-hand now exists too; the first coordinator clipboard now exists too; the first operator readout exists too; Imagination now has a fenced workbench. The next slice is feeding that bench real GitHub issue material without letting it seize the red pen.
The role board, fixed specialist templates, and first modeling accept path now exist too. Current Epiphany can project implementation, modeling/checkpoint, verification/review, and reorientation ownership through `thread/epiphany/roles`, explicitly launch fixed modeling/verification workers, read their findings back, apply reviewed modeling graph/checkpoint patches through `thread/epiphany/roleAccept`, and keep semantic acceptance review-gated. What remains is dogfooding the operator loop until real product friction, not imagined architecture guilt, tells us the next organ.
The role board, fixed specialist templates, and first role accept paths now exist too. Current Epiphany can project implementation, Imagination/planning, modeling/checkpoint, verification/review, and reorientation ownership through `thread/epiphany/roles`, explicitly launch fixed Imagination/modeling/verification workers, read their findings back, apply reviewed Imagination planning patches and reviewed modeling graph/checkpoint patches through `thread/epiphany/roleAccept`, and keep semantic acceptance review-gated. What remains is dogfooding the operator loop until real product friction, not imagined architecture guilt, tells us the next organ.
## Verification Hooks
@ -1511,7 +1511,7 @@ Current tests cover the landed flows at useful seams:
- live app-server smoke for the first bounded reorient-launch runtime consumer plus CRRC coordinator recommendations in `tools/epiphany_phase6_reorient_launch_smoke.py`.
- live app-server smoke for the first MVP operator status view in `tools/epiphany_mvp_status_smoke.py`.
- focused app-server mapper coverage for `thread/epiphany/roles`, plus MVP status smoke coverage that consumes the roles surface.
- focused app-server/protocol coverage for `thread/epiphany/roleLaunch`, `thread/epiphany/roleResult`, and `thread/epiphany/roleAccept`, plus live role smoke coverage that launches modeling and verification workers, reads completed results, accepts a reviewed modeling graph patch, and proves the durable graph grows before verification sees the expanded frontier.
- focused app-server/protocol coverage for `thread/epiphany/roleLaunch`, `thread/epiphany/roleResult`, and `thread/epiphany/roleAccept`, plus live role smoke coverage that launches Imagination, modeling, and verification workers, reads completed results, accepts a reviewed Imagination planning patch and a reviewed modeling graph patch, proves durable planning grows without objective adoption, and proves the durable graph grows before verification sees the expanded frontier.
- live app-server smoke for the first Phase 6 job-control boundary in `tools/epiphany_phase6_job_control_smoke.py`.
- live app-server smoke for explicit Phase 6 role launch/read-back in `tools/epiphany_phase6_role_smoke.py`.

View file

@ -28,14 +28,14 @@ Do not trust this file for the exact live HEAD. Always check git.
- Do not copy exact branch or HEAD from this note. Run `git status --short --branch` and `git log --oneline -5`.
- Phase 1 through Phase 5 are complete enough.
- Phase 6 has read-only `thread/epiphany/scene`, `thread/epiphany/jobs`, `thread/epiphany/roles`, `thread/epiphany/freshness`, `thread/epiphany/context`, `thread/epiphany/graphQuery`, `thread/epiphany/planning`, `thread/epiphany/pressure`, `thread/epiphany/reorient`, `thread/epiphany/crrc`, `thread/epiphany/coordinator`, `thread/epiphany/reorientResult`, and `thread/epiphany/roleResult`; durable `jobBindings` now act as a thin Epiphany-owned launcher seam with launcher id, authority scope, and backend kind/job id, `thread/epiphany/jobLaunch` / `thread/epiphany/jobInterrupt` / `thread/epiphany/roleLaunch` now provide explicit bounded authority over that seam, jobs can overlay it onto live runtime `agent_jobs` progress for loaded threads, and `thread/epiphany/jobsUpdated` now translates live `agent_job_progress:{json}` background events into changed launcher-bound notifications without polling or scheduling. Freshness carries watcher-backed invalidation inputs, graphQuery traverses authoritative typed graph neighborhoods and path/symbol matches without mutation, planning projects typed captures/backlog/roadmap/objective drafts without adopting work, roles project implementation/modeling/verification/reorientation ownership from existing signals without becoming a scheduler, `roleLaunch` can launch only fixed modeling/checkpoint or verification/review specialists, `roleResult` reads those outputs back as reviewable findings without mutation, `thread/epiphany/roleAccept` can apply a reviewed modeling/checkpoint `statePatch` through the existing state validator, reorient turns checkpoint plus freshness/pressure/watcher signals into a read-only resume-versus-regather verdict, `thread/epiphany/reorientLaunch` is the explicit runtime consumer over that verdict, `thread/epiphany/reorientResult` reads the launched worker output back as a reviewable finding without mutation or promotion, `thread/epiphany/reorientAccept` explicitly banks completed findings into accepted observation/evidence plus optional scratch/checkpoint state, `thread/epiphany/crrc` recommends the next explicit CRRC action without launching, accepting, compacting, scheduling, or mutating, and `thread/epiphany/coordinator` composes those signals into a fixed-lane MVP action recommendation without becoming a writer.
- `tools/epiphany_mvp_status.py` is the first dogfood operator view. It starts or reads a thread through app-server and prints scene, planning, pressure, reorient, jobs, roles, modeling/verification role result read-backs, reorient result, and CRRC recommendation as text or JSON.
- Phase 6 has read-only `thread/epiphany/scene`, `thread/epiphany/jobs`, `thread/epiphany/roles`, `thread/epiphany/freshness`, `thread/epiphany/context`, `thread/epiphany/graphQuery`, `thread/epiphany/planning`, `thread/epiphany/pressure`, `thread/epiphany/reorient`, `thread/epiphany/crrc`, `thread/epiphany/coordinator`, `thread/epiphany/reorientResult`, and `thread/epiphany/roleResult`; durable `jobBindings` now act as a thin Epiphany-owned launcher seam with launcher id, authority scope, and backend kind/job id, `thread/epiphany/jobLaunch` / `thread/epiphany/jobInterrupt` / `thread/epiphany/roleLaunch` now provide explicit bounded authority over that seam, jobs can overlay it onto live runtime `agent_jobs` progress for loaded threads, and `thread/epiphany/jobsUpdated` now translates live `agent_job_progress:{json}` background events into changed launcher-bound notifications without polling or scheduling. Freshness carries watcher-backed invalidation inputs, graphQuery traverses authoritative typed graph neighborhoods and path/symbol matches without mutation, planning projects typed captures/backlog/roadmap/objective drafts without adopting work, roles project implementation/imagination/modeling/verification/reorientation ownership from existing signals without becoming a scheduler, `roleLaunch` can launch fixed Imagination/planning, modeling/checkpoint, or verification/review specialists, `roleResult` reads those outputs back as reviewable findings without mutation, `thread/epiphany/roleAccept` can apply reviewed Imagination planning-only `statePatch` findings and reviewed modeling/checkpoint `statePatch` findings through the existing state validator, reorient turns checkpoint plus freshness/pressure/watcher signals into a read-only resume-versus-regather verdict, `thread/epiphany/reorientLaunch` is the explicit runtime consumer over that verdict, `thread/epiphany/reorientResult` reads the launched worker output back as a reviewable finding without mutation or promotion, `thread/epiphany/reorientAccept` explicitly banks completed findings into accepted observation/evidence plus optional scratch/checkpoint state, `thread/epiphany/crrc` recommends the next explicit CRRC action without launching, accepting, compacting, scheduling, or mutating, and `thread/epiphany/coordinator` composes those signals into a fixed-lane MVP action recommendation without becoming a writer.
- `tools/epiphany_mvp_status.py` is the first dogfood operator view. It starts or reads a thread through app-server and prints scene, planning, pressure, reorient, jobs, roles, Imagination/modeling/verification role result read-backs, reorient result, and CRRC recommendation as text or JSON.
- `tools/epiphany_mvp_coordinator.py` is the first auditable fixed-lane coordinator runner. It starts or reads a thread through app-server, follows the harness-native coordinator action, can auto-launch modeling, verification, or reorient-worker jobs, keeps semantic findings review-gated by default, and writes summary, steps, rendered snapshots, transcript, stderr, and final next-action artifacts under `.epiphany-dogfood/coordinator` or a caller-provided artifact directory.
- Native CRRC automation is now landed only at turn-complete safe boundaries. It may submit `Op::Compact` for coordinator-approved `compactRehydrateReorient` or for a successful pre-compaction checkpoint intervention's pending compact handoff, and it may launch the fixed `reorient-worker` for coordinator-approved `launchReorientWorker`. It does not auto-launch modeling/verification, accept findings, promote evidence, edit implementation code, or keep going after reviewable semantic output.
- Native CRRC automation is now landed only at turn-complete safe boundaries. It may submit `Op::Compact` for coordinator-approved `compactRehydrateReorient` or for a successful pre-compaction checkpoint intervention's pending compact handoff, and it may launch the fixed `reorient-worker` for coordinator-approved `launchReorientWorker`. It does not auto-launch Imagination/modeling/verification, accept findings, promote evidence, edit implementation code, or keep going after reviewable semantic output.
- Pre-compaction checkpoint intervention is now landed. On token-count events for loaded Epiphany threads, when current context usage reaches 80% of the active auto-compact/context limit, the harness steers the active turn once with a CRRC checkpoint directive so the agent banks working context before compaction/reorientation. Pressure ignores cumulative token spend; cumulative-only telemetry reports unknown instead of yelling. A successful steer now latches a turn-scoped compact handoff that is consumed at clean turn completion, preventing the brake from decaying into another implementation turn. This is still bounded steering plus compaction handoff, not automatic semantic acceptance, a broad scheduler, or implementation continuation.
- `tools/epiphany_mvp_dogfood.py` is the first auditable dogfood runner. It drives a bounded state/role/CRRC/reorientation loop and writes local artifacts under `.epiphany-dogfood/mvp-loop`, including sealed app-server transcript diagnostics, rendered snapshots, operator-safe final status, vanilla-reference prompt/output, and comparison notes. The runner now writes truthful vanilla/comparison artifacts whether the optional vanilla reference is skipped, fails, or completes.
- `tools/epiphany_mvp_live_specialist.py` is the first auditable live-specialist runner. It launches a real role specialist through `thread/epiphany/roleLaunch`, lets the spawned worker report through `report_agent_job_result`, reads it back through `thread/epiphany/roleResult`, and writes local artifacts under `.epiphany-dogfood/live-specialist`.
- The first MVP GUI shell now exists under `apps/epiphany-gui`. It is a Tauri v2 + React operator console over the existing status bridge, dogfood artifacts, and GUI action artifacts, not a new source of truth. It can prepare a durable Epiphany checkpoint for a resumable operator thread, run status snapshots and coordinator-plan artifacts, launch/read the fixed modeling and verification lanes, launch/read the fixed reorient-worker, and explicitly accept completed reorient findings after review.
- The first MVP GUI shell now exists under `apps/epiphany-gui`. It is a Tauri v2 + React operator console over the existing status bridge, dogfood artifacts, and GUI action artifacts, not a new source of truth. It can prepare a durable Epiphany checkpoint for a resumable operator thread, run status snapshots and coordinator-plan artifacts, launch/read/accept the fixed Imagination planning lane, launch/read the fixed modeling and verification lanes, launch/read the fixed reorient-worker, and explicitly accept completed reorient findings after review.
- Internal `agent_job:` workers now get the reporting tool independent of the user-facing CSV spawn feature, and ephemeral worker sessions can initialize the sqlite state runtime on demand so specialist reports land in the shared backend.
- Durable in-flight investigation checkpointing is now landed in authoritative typed state, writable through `thread/epiphany/update` or accepted `thread/epiphany/promote`, rendered into the prompt, and reflected through scene/context.
- The prompt doctrine pass is landed. Shared Epiphany prompts now carry distilled memory/evidence discipline. Rendered state intro/doctrine text lives in `epiphany-core/src/prompts/`, and lane/control prompt text lives in `vendor/codex/codex-rs/app-server/src/prompts/epiphany_specialists.toml`: modeling is the Body, implementation is the Hands and GUI-launched main coding lane, verification is the Soul, reorientation is Life, coordinator remains the read-only Self, and CRRC owns the pre-compaction intervention template.
@ -50,8 +50,8 @@ Do not trust this file for the exact live HEAD. Always check git.
- Aetheria dogfood exposed and then landed the first editor/runtime bridge. An implementation worker launched legacy `D:\Unity\Editor\Unity.exe -version` (Unity 5.5.0f3) even though Aetheria pins Unity `6000.1.10f1`; the stray process was killed. `tools/epiphany_unity_bridge.py` now resolves the project-pinned Unity editor, refuses wrong/missing versions, owns `-batchmode`, `-quit`, and `-projectPath`, and writes inspection/command/log artifacts. `tools/epiphany_gui_action.py` points implementation workers at that bridge, and the GUI has an Inspect Unity action.
- The current Aetheria runtime truth is blocked but legible: the project pins Unity `6000.1.10f1`, this machine currently has Hub editor `6000.4.2f1`, and the bridge wrote `.epiphany-gui/runtime/unity-inspect-1777549218802064800-23832` proving the exact editor is missing. Treat that artifact as the evidence gap until the pinned editor exists.
- The GUI parses `implementation-result.json` into artifact metadata, surfaces the latest implementation diff/no-diff outcome, and pauses immediate `Continue Implementation` repeats when the newest artifact is a no-diff implementation audit.
- The planning loop is now runtime-backed and operator-visible. Typed captures, backlog items, roadmap streams, Objective Drafts, and GitHub issue source refs live in `EpiphanyThreadState`, validate through revision-gated `thread/epiphany/update`, render into prompts when present, project read-only through `thread/epiphany/planning`, render in the GUI Planning panel, and can be explicitly adopted through the artifacted `adoptObjectiveDraft` GUI action. Chat is deliberation, not an objective pipe; ideas and GitHub Issues remain planning state until explicit human adoption.
- Next real product move: add the Imagination planning synthesis lane so existing planning material can produce reviewable Objective Drafts, then add GitHub Issues import into captures before returning to EpiphanyGraph/Rider bridge work.
- The planning loop is now runtime-backed and operator-visible. Typed captures, backlog items, roadmap streams, Objective Drafts, and GitHub issue source refs live in `EpiphanyThreadState`, validate through revision-gated `thread/epiphany/update`, render into prompts when present, project read-only through `thread/epiphany/planning`, render in the GUI Planning panel, can be synthesized by the fixed Imagination/planning lane, and can be explicitly adopted through the artifacted `adoptObjectiveDraft` GUI action. Chat is deliberation, not an objective pipe; ideas and GitHub Issues remain planning state until explicit human adoption.
- Next real product move: import GitHub Issues into planning captures so real external backlog material can feed Imagination before returning to EpiphanyGraph/Rider bridge work.
- The repo is an Epiphany fork of Codex, not a Codex preset.
- `vendor/codex` is tracked directly, not a submodule.
- `epiphany-core` owns the heavy Epiphany organs where practical.
@ -94,11 +94,11 @@ The current spine:
- verifier-backed promotion through `thread/epiphany/promote`
- successful-write notification through `thread/epiphany/stateUpdated`
- explicit launch/interrupt authority through `thread/epiphany/jobLaunch` and `thread/epiphany/jobInterrupt`
- explicit modeling/checkpoint and verification/review specialist launch through `thread/epiphany/roleLaunch`
- read-only modeling/checkpoint and verification/review specialist result read-back through `thread/epiphany/roleResult`
- review-gated modeling/checkpoint patch acceptance through `thread/epiphany/roleAccept`
- explicit Imagination/planning, modeling/checkpoint, and verification/review specialist launch through `thread/epiphany/roleLaunch`
- read-only Imagination/planning, modeling/checkpoint, and verification/review specialist result read-back through `thread/epiphany/roleResult`
- review-gated Imagination planning-only patch acceptance and modeling/checkpoint patch acceptance through `thread/epiphany/roleAccept`
- config-backed Eyes/research prompt text and active anti-Greenspun checks in base, implementation, verification, and coordinator prompts; a full launchable research lane is still a coordinator/protocol extension, not yet a landed roleLaunch lane
- typed planning state for captures, backlog, roadmap streams, Objective Drafts, explicit adoption boundaries, GitHub Issues source refs, GUI Planning view, and artifacted Objective Draft adoption; the Imagination synthesis lane is not landed yet
- typed planning state for captures, backlog, roadmap streams, Objective Drafts, explicit adoption boundaries, GitHub Issues source refs, GUI Planning view, artifacted Objective Draft adoption, and the fixed Imagination synthesis lane
- bounded reorient-guided worker launch through `thread/epiphany/reorientLaunch`
- read-only reorient-worker result read-back through `thread/epiphany/reorientResult`
- explicit reorient-worker finding acceptance through `thread/epiphany/reorientAccept`
@ -138,7 +138,7 @@ The current spine:
- live MVP coordinator smoke through `tools/epiphany_mvp_coordinator_smoke.py`
- live job-control app-server smoke through `tools/epiphany_phase6_job_control_smoke.py`
- live specialist MVP pass through `tools/epiphany_mvp_live_specialist.py`
- Tauri v2 + React GUI operator shell under `apps/epiphany-gui`, including visual smoke, durable checkpoint preparation, fixed lane launch/read-back controls, explicit review-gated reorient acceptance, and bounded artifact-writing buttons
- Tauri v2 + React GUI operator shell under `apps/epiphany-gui`, including visual smoke, durable checkpoint preparation, fixed lane launch/read-back controls, explicit Imagination planning acceptance, explicit review-gated reorient acceptance, and bounded artifact-writing buttons
The exact current control flow is documented in
`notes/epiphany-current-algorithmic-map.md`.
@ -160,7 +160,7 @@ The exact current control flow is documented in
- `thread/epiphany/coordinator` is read-only.
- `thread/epiphany/roles` is read-only.
- `thread/epiphany/roleResult` is read-only.
- `thread/epiphany/roleAccept` is a narrow write surface for completed modeling/checkpoint `statePatch` findings only; it must not accept verification output, broad authority fields, job bindings, or arbitrary implementation work.
- `thread/epiphany/roleAccept` is a narrow write surface for completed Imagination/planning and modeling/checkpoint `statePatch` findings only; it must not accept verification output, broad authority fields, job bindings, arbitrary implementation work, or implicit objective adoption.
- `thread/epiphany/reorientResult` is read-only.
- Durable typed state writes go through `thread/epiphany/update`, accepted `thread/epiphany/promote`, `thread/epiphany/reorientAccept`, `thread/epiphany/roleAccept`, or the bounded `thread/epiphany/jobLaunch`, `thread/epiphany/jobInterrupt`, `thread/epiphany/reorientLaunch`, and `thread/epiphany/roleLaunch` authority surfaces when they mutate typed state or `jobBindings`.
- `thread/epiphany/index` writes the retrieval catalog, not durable Epiphany understanding.
@ -263,7 +263,7 @@ For GUI Objective Draft adoption behavior, run:
& 'C:\Users\Meta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\python.exe' '.\tools\epiphany_gui_planning_smoke.py'
```
For explicit modeling/checkpoint and verification/review role launch/read-back,
For explicit Imagination/planning, modeling/checkpoint, and verification/review role launch/read-back/acceptance,
run:
```powershell

View file

@ -62,3 +62,4 @@
{"ts": "2026-05-01T05:23:34Z", "type": "design", "status": "ok", "note": "The planner/future-shape role is named Imagination. It may synthesize captures, backlog, roadmap streams, objective drafts, and unresolved planning questions alongside Eyes research, but its outputs remain planning artifacts until explicit human adoption turns a draft into the active objective."}
{"ts": "2026-05-01T06:26:49Z", "type": "implementation", "status": "ok", "note": "The first planning substrate runtime slice landed: EpiphanyThreadState now carries typed captures, backlog items, roadmap streams, Objective Drafts, and GitHub source refs; planning validates through revision-gated updates, renders into prompts, exposes read-only thread/epiphany/planning with generated schema types, appears in scene actions, and is covered by tools/epiphany_phase6_planning_smoke.py."}
{"ts": "2026-05-01T06:56:54Z", "type": "implementation", "status": "ok", "note": "The GUI planning operator slice landed: tools/epiphany_mvp_status.py now collects/render planning state, apps/epiphany-gui renders planning summaries/captures/backlog/roadmap/objective drafts with a selected draft control, and tools/epiphany_gui_action.py exposes adoptObjectiveDraft to explicitly update objective/subgoal/planning/scratch/checkpoint/evidence with patch/update artifacts. tools/epiphany_gui_planning_smoke.py proves draft adoption marks the draft adopted, source backlog active, and activeObjective updated."}
{"ts": "2026-05-01T09:11:50+01:00", "type": "implementation", "status": "ok", "note": "The Imagination planning lane landed: ThreadEpiphanyRoleId now includes imagination, app-server fixed roleLaunch/roleResult/roleAccept supports the planning-synthesis-worker, roleAccept accepts only review-gated planning patches without objective adoption, status/GUI surfaces can launch/read/accept the lane, schemas include the new role id, and tools/epiphany_phase6_role_smoke.py proves Imagination acceptance grows planning state while modeling and verification still complete."}

View file

@ -66,7 +66,7 @@ active_subgoals:
- Use `tools/epiphany_agent_telemetry.py` and generated `agent-function-telemetry.json` files to inspect behavior shape without opening sealed transcripts.
- Treat repeated no-diff implementation turns as an MVP blocker, not success. A bounded implementation pass must leave a reviewable workspace diff or an explicit reviewable failure artifact before dogfood can be called healthy.
- Build the Rider and Unity bridges before the next Aetheria implementation dogfood run: the Aetheria-side resident Unity editor package, named Unity bridge operation planner, and GUI Environment panel are now landed; next build the EpiphanyGraph-backed GUI graph dashboard, Rider context bridge, and Rider plugin MVP.
- Preserve the newly landed GUI Planning view and explicit Objective Draft adoption action; Imagination planning synthesis and GitHub Issues import remain the next planning organs.
- Preserve the newly landed GUI Planning view, explicit Objective Draft adoption action, and Imagination planning synthesis lane; GitHub Issues import remains the next planning organ.
- Treat the target workflow as three integrated surfaces: Rider is the human code/diff view, Epiphany GUI is the agent dashboard and persisted-state graph view, and Unity is the pinned runtime/test/scene environment.
- Use `notes/epiphany-rider-unity-integration-plan.md` as the source-grounded plan for Rider-as-IDE and Unity-as-editor/runtime integration seams.
@ -87,7 +87,7 @@ accepted_design:
- The harness now has an in-flight investigation checkpoint packet in durable typed state; automatic CRRC still needs runtime logic that reads the packet and decides whether to resume or force reorientation and re-gathering after compaction.
- GUI/client work must reflect or steer typed state rather than manufacture a second source of truth.
- The MVP GUI target is a local Tauri v2 + React operator console over existing app-server APIs. Tauri owns windowing and local lifecycle; React renders the operator surface; app-server and typed Epiphany state remain authoritative.
- `apps/epiphany-gui` is the first GUI shell: a Tauri v2 + React app that invokes the existing MVP status bridge, renders coordinator/CRRC/pressure/reorient/role/job/finding/planning status, lists dogfood and GUI action artifact bundles, prepares a durable Epiphany checkpoint for a resumable operator thread, and exposes bounded buttons for status snapshots, coordinator plans, fixed modeling/verification/reorient launch and read-back, explicit Objective Draft adoption, and explicit review-gated reorient acceptance.
- `apps/epiphany-gui` is the first GUI shell: a Tauri v2 + React app that invokes the existing MVP status bridge, renders coordinator/CRRC/pressure/reorient/role/job/finding/planning status, lists dogfood and GUI action artifact bundles, prepares a durable Epiphany checkpoint for a resumable operator thread, and exposes bounded buttons for status snapshots, coordinator plans, fixed Imagination/modeling/verification/reorient launch and read-back, explicit Imagination planning acceptance, explicit Objective Draft adoption, and explicit review-gated reorient acceptance.
- Durable `job_bindings` now form a thin Epiphany-owned launcher seam: bindings can carry launcher identity, authority scope, backend kind, and backend job id while still supporting the older runtime-agent-job pointer as a compatibility alias.
- `thread/epiphany/jobs` is a read-only job/progress reflection: it derives indexing, remap, verification, and specialist slots from authoritative state and retrieval summaries, and can overlay durable launcher bindings onto live runtime `agent_jobs` snapshots through the current backend adapter when a real owner exists; it is not a scheduler, notification stream, or durable runtime job table.
- `thread/epiphany/jobsUpdated` is the first live bound-job progress notification: it translates runtime `agent_job_progress:{json}` background events into changed launcher-bound payloads by resolving durable `job_bindings` against live `agent_jobs` snapshots through the current backend adapter, without polling in a loop, mutating state, or scheduling work.
@ -105,9 +105,9 @@ accepted_design:
- Limited native CRRC automation is now wired only at turn-complete safe boundaries: it can submit a compact operation when the read-only coordinator recommends `compactRehydrateReorient` or when a successful pre-compaction checkpoint intervention left a pending compact handoff for the completed turn, and it can launch the fixed `reorient-worker` when the coordinator recommends `launchReorientWorker`. It does not auto-launch modeling or verification, auto-accept findings, promote evidence, edit implementation code, or continue after reviewable semantic results.
- Pre-compaction checkpoint intervention is now wired at token-count boundaries: when a loaded Epiphany thread crosses the existing `shouldPrepareCompaction` pressure threshold during an active turn, the harness steers the active agent once for that turn with a bounded CRRC directive to bank scratch/checkpoint/map/evidence before compaction/reorientation. A successful steer marks a turn-scoped pending compact handoff consumed at clean turn completion; this is still steering plus compaction handoff, not semantic acceptance, broad scheduling, or automatic implementation continuation.
- `tools/epiphany_mvp_status.py` is the first dogfood operator view: it starts or reads a thread through app-server and renders scene, planning, pressure, reorient, jobs, role result read-backs, reorient result, CRRC recommendation, and MVP role lanes as text or JSON without becoming a state store.
- `thread/epiphany/roles` is the first harness-native role ownership surface: it projects implementation, modeling/checkpoint, verification/review, and reorientation lanes from authoritative typed state, bound jobs, pressure/reorient/CRRC, and worker result status without launching, scheduling, accepting, compacting, or mutating state.
- `thread/epiphany/roleLaunch` and `thread/epiphany/roleResult` are the first explicit role-scoped specialist templates: modeling/checkpoint and verification/review can be launched through fixed bindings over the existing job-control seam and read back as reviewable findings, while implementation stays owned by the main coding agent and reorientation stays on the CRRC-specific launch/result path.
- `thread/epiphany/roleAccept` is the first review-gated role finding acceptance path: it currently accepts only completed modeling/checkpoint findings with a valid `statePatch`, applies only modeling-safe fields such as graphs/frontier/checkpoints/scratch/investigation checkpoint plus audit observation/evidence, and rejects verification or broad authority fields.
- `thread/epiphany/roles` is the first harness-native role ownership surface: it projects implementation, Imagination/planning, modeling/checkpoint, verification/review, and reorientation lanes from authoritative typed state, bound jobs, pressure/reorient/CRRC, and worker result status without launching, scheduling, accepting, compacting, or mutating state.
- `thread/epiphany/roleLaunch` and `thread/epiphany/roleResult` are the first explicit role-scoped specialist templates: Imagination/planning, modeling/checkpoint, and verification/review can be launched through fixed bindings over the existing job-control seam and read back as reviewable findings, while implementation stays owned by the main coding agent and reorientation stays on the CRRC-specific launch/result path.
- `thread/epiphany/roleAccept` is the first review-gated role finding acceptance path: it accepts completed Imagination/planning findings only when they return a valid planning-only `statePatch` with draft Objective Draft review gates, accepts completed modeling/checkpoint findings only with a modeling-safe `statePatch`, appends audit observation/evidence, and rejects verification, implementation, reorientation, job binding, objective adoption, or broad authority fields.
- `thread/epiphany/reorientLaunch` is the first bounded runtime consumer over the reorientation verdict: it launches one fixed `reorient-worker` job through the existing job-control seam with explicit resume-versus-regather scope, authority, and checkpoint-derived payload, without making `thread/epiphany/reorient` a writer or turning CRRC into an ambient scheduler.
- `thread/epiphany/reorientResult` is the first bounded read-back surface for that worker: it reads the fixed or requested reorient-worker binding through the current `agent_jobs` backend and projects completed output as a reviewable finding without mutation, promotion, scheduling, or hidden continuation.
- `thread/epiphany/reorientAccept` is the first explicit acceptance write for completed reorient-worker findings: it appends accepted observation/evidence records, can optionally update scratch and investigation checkpoint state, and emits `stateUpdated` with source `reorientAccept` without accepting pending work or launching follow-up work.
@ -138,6 +138,7 @@ accepted_design:
- The first runtime planning slice is landed inside `EpiphanyThreadState`: typed captures, backlog items, roadmap streams, Objective Drafts, and GitHub source refs are revision-gated through `thread/epiphany/update`, rendered into prompts when present, and reflected read-only through `thread/epiphany/planning`.
- The first GUI planning operator slice is landed: status collection includes `thread/epiphany/planning`, the Tauri/React dashboard renders planning summaries, captures, backlog, roadmap streams, and Objective Drafts, and the `adoptObjectiveDraft` GUI action requires an explicit selected draft before updating objective, subgoal, planning status, scratch, checkpoint, observation, evidence, and adoption artifacts.
- The planning/future-shape role is Imagination: it can synthesize captures, backlog shape, roadmap streams, and Objective Drafts while Eyes scouts outside reality, but its output remains planning state until explicit human adoption.
- The Imagination lane is now a fixed roleLaunch/roleResult/roleAccept specialist over the existing job-control seam: it may launch as `planning-synthesis-worker`, return a reviewable planning-only `statePatch`, and update captures/backlog/roadmap/objective drafts after explicit acceptance without adopting an objective or touching implementation/modeling state.
- Unity integration must be editor-resident, not text-archeology-only: scene files, prefabs, materials, shaders, ScriptableObjects, asset GUIDs, prefab overrides, and serialized component fields should be inspected through a Unity Editor package using Unity APIs, with narrow mutation preview/apply commands and artifact receipts.
- The first resident Unity editor package now lives in the Aetheria branch `codex/epiphany-unity-editor-bridge`: `Assets/Editor/Epiphany/EpiphanyEditorBridge.cs` exposes editor-resident inspect, compilation, scene, prefab, serialized-object, and reference-search probes with JSON/Markdown artifacts. Epiphany's Unity bridge detects that package and plans named probe/test commands through `GameCult.Epiphany.Unity.EpiphanyEditorBridge.RunProbe`, while runtime execution remains blocked until the exact project-pinned Unity editor is present.
- The GUI Environment panel now reflects the latest Unity bridge runtime audit, including project-pinned editor status, resident package presence, execute method, installed/candidate editor paths, search roots, and artifact bundle details; Rider is shown as a pending environment lane until its bridge exists.
@ -170,6 +171,7 @@ landed_baseline:
- Planning substrate design: `notes/epiphany-planning-substrate.md` formalizes conversation capture, inbox, backlog, roadmap streams, objective drafts, explicit adoption, the Imagination planning role, and GitHub Issues import.
- Phase 6 planning substrate runtime slice: typed planning state in `EpiphanyThreadState`, prompt rendering, revision-gated update validation, read-only `thread/epiphany/planning`, scene action reflection, generated schemas, and live planning smoke coverage.
- Phase 6 GUI planning operator slice: MVP status and Tauri/React GUI now render planning state, and `tools/epiphany_gui_action.py` exposes explicit selected Objective Draft adoption with artifacted state patch/update proof.
- Phase 6 Imagination role lane: `thread/epiphany/roles` now exposes Imagination/planning, `roleLaunch` can launch the fixed `planning-synthesis-worker`, `roleResult` projects its finding, `roleAccept` applies planning-only patches with review-gated Objective Drafts, and the GUI/status tools can launch, read, and accept that lane.
guardrails:
- Use `tools/epiphany_phase5_smoke.py` before modifying update, distill, propose, promote, read-projection, or notification behavior.
- Use `tools/epiphany_phase6_scene_smoke.py` before modifying scene projection behavior.
@ -188,7 +190,7 @@ landed_baseline:
- Use `tools/epiphany_phase6_reorient_launch_smoke.py` before modifying the read-only CRRC coordinator recommendation surface.
- Use `tools/epiphany_mvp_status_smoke.py` before modifying the MVP operator status view.
- Use `tools/epiphany_mvp_coordinator_smoke.py` before modifying the fixed-lane MVP coordinator endpoint or runner.
- Use `tools/epiphany_phase6_role_smoke.py` before modifying explicit role launch/read-back behavior.
- Use `tools/epiphany_phase6_role_smoke.py` before modifying explicit role launch/read-back/acceptance behavior.
- Use `tools/epiphany_unity_bridge_smoke.py` before modifying the Unity editor/runtime bridge.
- Use `tools/epiphany_mvp_live_specialist.py` before claiming live specialist execution still works end to end.
- Use `npm run build`, `npm run smoke:visual`, `cargo fmt --check`, `cargo check`, and `npm run tauri -- build --debug --no-bundle` under `apps/epiphany-gui` before claiming GUI operator changes are usable.
@ -210,5 +212,5 @@ rejected_paths:
reason: persistent state that accumulates every local truth becomes memory cruft and weakens rehydration
current_status:
summary: Phase 1 durable state through Phase 6 GUI/coordinator/specialist/CRRC MVP surfaces are landed. The Aetheria supervised run found and fixed real harness wounds in coordinator policy, no-diff audits, stale job/result projection, and reorient acceptance. The Unity incident proved the next blocker is environment access, so `notes/epiphany-rider-unity-integration-plan.md` governs the Rider/Epiphany GUI/Unity workflow. Planning is now an operator-visible loop: typed captures, backlog items, roadmap streams, Objective Drafts, and GitHub source refs live in Epiphany state, render into prompts, validate through revision-gated updates, project through `thread/epiphany/planning`, render in the GUI, and can be explicitly adopted into the active objective through an artifacted GUI action.
next_action: Add the Imagination planning synthesis lane so captures/backlog/roadmap material can produce reviewable Objective Drafts, then import GitHub Issues into planning captures. After the planning loop can synthesize and ingest work, return to the EpiphanyGraph dashboard, Rider context bridge, and Rider plugin MVP before another Aetheria implementation dogfood run.
summary: Phase 1 durable state through Phase 6 GUI/coordinator/specialist/CRRC MVP surfaces are landed. The Aetheria supervised run found and fixed real harness wounds in coordinator policy, no-diff audits, stale job/result projection, and reorient acceptance. The Unity incident proved the next blocker is environment access, so `notes/epiphany-rider-unity-integration-plan.md` governs the Rider/Epiphany GUI/Unity workflow. Planning is now an operator-visible loop: typed captures, backlog items, roadmap streams, Objective Drafts, and GitHub source refs live in Epiphany state, render into prompts, validate through revision-gated updates, project through `thread/epiphany/planning`, render in the GUI, can be synthesized by the fixed Imagination/planning lane, and can be explicitly adopted into the active objective through an artifacted GUI action.
next_action: Import GitHub Issues into planning captures so real external backlog material can feed Imagination. After the planning loop can ingest issues, return to the EpiphanyGraph dashboard, Rider context bridge, and Rider plugin MVP before another Aetheria implementation dogfood run.

View file

@ -1136,8 +1136,12 @@ def run_action(args: argparse.Namespace) -> dict[str, Any]:
"Adopted reviewed Objective Draft "
f"{args.planning_draft_id} as the active objective."
)
elif args.action in {"launchModeling", "launchVerification"}:
role_id = "modeling" if args.action == "launchModeling" else "verification"
elif args.action in {"launchImagination", "launchModeling", "launchVerification"}:
role_id = {
"launchImagination": "imagination",
"launchModeling": "modeling",
"launchVerification": "verification",
}[args.action]
payload: dict[str, Any] = {
"threadId": thread_id,
"roleId": role_id,
@ -1159,12 +1163,32 @@ def run_action(args: argparse.Namespace) -> dict[str, Any]:
else:
response = launch
summary = f"Launched {role_id} role worker without waiting."
elif args.action in {"readModelingResult", "readVerificationResult"}:
role_id = "modeling" if args.action == "readModelingResult" else "verification"
elif args.action in {
"readImaginationResult",
"readModelingResult",
"readVerificationResult",
}:
role_id = {
"readImaginationResult": "imagination",
"readModelingResult": "modeling",
"readVerificationResult": "verification",
}[args.action]
response = client.send(
"thread/epiphany/roleResult", {"threadId": thread_id, "roleId": role_id}
)
summary = f"Read {role_id} role result."
elif args.action == "acceptImagination":
if revision is None:
raise ValueError("acceptImagination requires ready Epiphany state with a revision")
response = client.send(
"thread/epiphany/roleAccept",
{
"threadId": thread_id,
"roleId": "imagination",
"expectedRevision": revision,
},
)
summary = "Accepted reviewed imagination planning patch."
elif args.action == "acceptModeling":
if revision is None:
raise ValueError("acceptModeling requires ready Epiphany state with a revision")
@ -1381,6 +1405,9 @@ def main() -> int:
"--action",
required=True,
choices=[
"launchImagination",
"readImaginationResult",
"acceptImagination",
"launchModeling",
"readModelingResult",
"acceptModeling",

View file

@ -213,7 +213,12 @@ def main() -> int:
parser.add_argument("--app-server", type=Path, default=DEFAULT_APP_SERVER)
parser.add_argument("--artifact-dir", type=Path, default=DEFAULT_ARTIFACT_DIR)
parser.add_argument("--codex-home", type=Path, default=DEFAULT_CODEX_HOME)
parser.add_argument("--roles", nargs="+", choices=["modeling", "verification"], default=list(DEFAULT_ROLES))
parser.add_argument(
"--roles",
nargs="+",
choices=["imagination", "modeling", "verification"],
default=list(DEFAULT_ROLES),
)
parser.add_argument("--max-runtime-seconds", type=int, default=180)
parser.add_argument("--timeout-seconds", type=int, default=240)
parser.add_argument("--poll-seconds", type=float, default=5.0)

View file

@ -117,6 +117,9 @@ def collect_status(
roles = client.send("thread/epiphany/roles", {"threadId": thread_id})
planning = client.send("thread/epiphany/planning", {"threadId": thread_id})
role_results = {
"imagination": client.send(
"thread/epiphany/roleResult", {"threadId": thread_id, "roleId": "imagination"}
),
"modeling": client.send(
"thread/epiphany/roleResult", {"threadId": thread_id, "roleId": "modeling"}
),
@ -243,10 +246,15 @@ def render_status(status: dict[str, Any]) -> str:
"Role Findings",
]
)
for role_id in ("modeling", "verification"):
role_labels = {
"imagination": "Imagination / Planning",
"modeling": "Modeling / Checkpoint",
"verification": "Verification / Review",
}
for role_id in ("imagination", "modeling", "verification"):
role_result = role_results.get(role_id) or {}
finding = role_result.get("finding")
label = "Modeling / Checkpoint" if role_id == "modeling" else "Verification / Review"
label = role_labels[role_id]
lines.append(
f"- {label}: {maybe(role_result.get('status'))} for "
f"{maybe(role_result.get('bindingId'))}"

View file

@ -69,8 +69,9 @@ def run_smoke(args: argparse.Namespace) -> dict[str, object]:
)
lane_ids = [lane["id"] for lane in status["roles"]["roles"]]
require(
lane_ids == ["implementation", "modeling", "verification", "reorientation"],
"roles surface should expose the four MVP role lanes",
lane_ids
== ["implementation", "imagination", "modeling", "verification", "reorientation"],
"roles surface should expose the five MVP role lanes",
)
require(
status["roles"]["note"].startswith("Role ownership is derived read-only"),
@ -91,6 +92,7 @@ def run_smoke(args: argparse.Namespace) -> dict[str, object]:
)
require(
"Role Findings" in rendered
and status["roleResults"]["imagination"]["status"] == "missingState"
and status["roleResults"]["modeling"]["status"] == "missingState"
and status["roleResults"]["verification"]["status"] == "missingState",
"rendered status should expose fixed role result read-back status",

View file

@ -27,6 +27,7 @@ DEFAULT_RESULT = ROOT / ".epiphany-smoke" / "phase6-role-smoke-result.json"
DEFAULT_TRANSCRIPT = ROOT / ".epiphany-smoke" / "phase6-role-smoke-transcript.jsonl"
DEFAULT_STDERR = ROOT / ".epiphany-smoke" / "phase6-role-smoke-server.stderr.log"
IMAGINATION_BINDING_ID = "planning-synthesis-worker"
MODELING_BINDING_ID = "modeling-checkpoint-worker"
VERIFICATION_BINDING_ID = "verification-review-worker"
@ -51,7 +52,88 @@ def complete_role_backend_job(
"frontierNodeIds": [GRAPH_NODE_ID],
"evidenceIds": evidence_ids or ["ev-checkpoint"],
}
if role_id == "modeling":
if role_id == "imagination":
result["statePatch"] = {
"planning": {
"workspace_root": str(ROOT),
"captures": [
{
"id": "capture-imagination-smoke",
"title": "Imagination role smoke source",
"body": "Planning synthesis should create reviewable objective drafts without adopting them.",
"confidence": "medium",
"status": "triaged",
"speaker": "smoke",
"tags": ["imagination", "planning"],
"source": {
"kind": "chat",
"uri": "codex://threads/imagination-smoke",
},
}
],
"backlog_items": [
{
"id": "backlog-imagination-smoke",
"title": "Accept a planning-only role patch",
"kind": "test",
"summary": "Prove Imagination can grow planning state through review-gated roleAccept.",
"status": "ready",
"horizon": "near",
"priority": {
"value": "medium",
"rationale": "The planning lane needs a durable smoke guard.",
},
"confidence": "medium",
"product_area": "epiphany-control-plane",
"lane_hints": ["imagination", "soul"],
"acceptance_sketch": [
"roleAccept applies planning without adopting the draft."
],
"source_refs": [
{
"kind": "chat",
"uri": "codex://threads/imagination-smoke",
}
],
}
],
"roadmap_streams": [
{
"id": "stream-imagination-smoke",
"title": "Planning synthesis smoke",
"purpose": "Keep planning proposals review-gated.",
"status": "active",
"item_ids": ["backlog-imagination-smoke"],
"near_term_focus": "backlog-imagination-smoke",
}
],
"objective_drafts": [
{
"id": "draft-imagination-smoke",
"title": "Review Imagination planning patch",
"summary": "Accept a planning-only patch and leave adoption to a later human action.",
"source_item_ids": ["backlog-imagination-smoke"],
"scope": {
"includes": ["planning state update"],
"excludes": ["objective adoption", "implementation edits"],
},
"acceptance_criteria": [
"The role accept response changes planning, observations, and evidence only.",
"The draft remains in draft status after acceptance.",
],
"evidence_required": ["phase6 role smoke"],
"lane_plan": {
"imagination": "synthesize a bounded future shape",
"soul": "verify the patch remains review-gated",
},
"risks": ["accidental objective adoption"],
"review_gates": ["human adoption review"],
"status": "draft",
}
],
}
}
elif role_id == "modeling":
patch = reorient_patch()
graphs = patch["graphs"]
graphs["architecture"]["nodes"].append(
@ -259,13 +341,105 @@ def run_smoke(args: argparse.Namespace) -> dict[str, Any]:
start_index=update_notification_start,
)
imagination_launch = launch_role(
client,
thread_id,
role_id="imagination",
expected_revision=1,
)
require(
imagination_launch["revision"] == 2,
"imagination launch should advance revision",
)
assert_role_job(
imagination_launch["job"],
binding_id=IMAGINATION_BINDING_ID,
owner_role="epiphany-imagination",
authority_scope="epiphany.role.imagination",
scope="role-scoped planning synthesis",
)
imagination_payload = complete_role_backend_job(
codex_home,
imagination_launch["job"]["backendJobId"],
binding_id=IMAGINATION_BINDING_ID,
role_id="imagination",
verdict="draft-ready",
)
imagination_result_start = len(client.notifications)
imagination_result = client.send(
"thread/epiphany/roleResult",
{"threadId": thread_id, "roleId": "imagination"},
)
assert imagination_result is not None
assert_role_result(
imagination_result,
role_id="imagination",
binding_id=IMAGINATION_BINDING_ID,
payload=imagination_payload,
expected_revision=2,
)
client.require_no_notification(
"thread/epiphany/stateUpdated",
start_index=imagination_result_start,
)
imagination_roles = client.send("thread/epiphany/roles", {"threadId": thread_id})
assert imagination_roles is not None
imagination_lane = next(
role for role in imagination_roles["roles"] if role["id"] == "imagination"
)
require(
imagination_lane["recommendedAction"] == "roleResult",
"roles should point imagination at roleResult while the binding exists",
)
assert_role_job(
job_by_id(imagination_lane["jobs"], IMAGINATION_BINDING_ID),
binding_id=IMAGINATION_BINDING_ID,
owner_role="epiphany-imagination",
authority_scope="epiphany.role.imagination",
scope="role-scoped planning synthesis",
)
imagination_accept_start = len(client.notifications)
imagination_accept = client.send(
"thread/epiphany/roleAccept",
{
"threadId": thread_id,
"roleId": "imagination",
"expectedRevision": 2,
},
)
assert imagination_accept is not None
require(
imagination_accept["changedFields"] == ["observations", "evidence", "planning"],
"imagination accept should apply only planning plus audit records",
)
require(
imagination_accept["appliedPatch"]["planning"]["objective_drafts"][0]["status"]
== "draft",
"imagination accept should leave Objective Drafts review-gated",
)
imagination_accept_notification = client.wait_for_notification(
"thread/epiphany/stateUpdated",
start_index=imagination_accept_start,
timeout=15.0,
)
require(
imagination_accept_notification["params"]["source"] == "roleAccept",
"imagination accept should emit roleAccept state update source",
)
require(
imagination_accept_notification["params"]["revision"] == 3,
"imagination accept should advance durable revision",
)
modeling_launch = launch_role(
client,
thread_id,
role_id="modeling",
expected_revision=1,
expected_revision=3,
)
require(modeling_launch["revision"] == 2, "modeling launch should advance revision")
require(modeling_launch["revision"] == 4, "modeling launch should advance revision")
assert_role_job(
modeling_launch["job"],
binding_id=MODELING_BINDING_ID,
@ -291,7 +465,7 @@ def run_smoke(args: argparse.Namespace) -> dict[str, Any]:
role_id="modeling",
binding_id=MODELING_BINDING_ID,
payload=modeling_payload,
expected_revision=2,
expected_revision=4,
)
client.require_no_notification(
"thread/epiphany/stateUpdated",
@ -321,7 +495,7 @@ def run_smoke(args: argparse.Namespace) -> dict[str, Any]:
{
"threadId": thread_id,
"roleId": "modeling",
"expectedRevision": 2,
"expectedRevision": 4,
},
)
assert modeling_accept is not None
@ -352,7 +526,7 @@ def run_smoke(args: argparse.Namespace) -> dict[str, Any]:
"modeling accept should emit roleAccept state update source",
)
require(
accept_notification["params"]["revision"] == 3,
accept_notification["params"]["revision"] == 5,
"modeling accept should advance durable revision",
)
@ -360,10 +534,10 @@ def run_smoke(args: argparse.Namespace) -> dict[str, Any]:
client,
thread_id,
role_id="verification",
expected_revision=3,
expected_revision=5,
)
require(
verification_launch["revision"] == 4,
verification_launch["revision"] == 6,
"verification launch should advance revision",
)
assert_role_job(
@ -392,7 +566,7 @@ def run_smoke(args: argparse.Namespace) -> dict[str, Any]:
role_id="verification",
binding_id=VERIFICATION_BINDING_ID,
payload=verification_payload,
expected_revision=4,
expected_revision=6,
)
client.require_no_notification(
"thread/epiphany/stateUpdated",
@ -402,9 +576,14 @@ def run_smoke(args: argparse.Namespace) -> dict[str, Any]:
final_read = client.send("thread/read", {"threadId": thread_id, "includeTurns": False})
assert final_read is not None
require(
final_read["thread"]["epiphanyState"]["revision"] == 4,
final_read["thread"]["epiphanyState"]["revision"] == 6,
"role result read-back should not mutate durable state",
)
require(
final_read["thread"]["epiphanyState"]["planning"]["objective_drafts"][0]["id"]
== "draft-imagination-smoke",
"accepted imagination result should grow durable planning state",
)
graph_ids = [
node["id"]
for node in final_read["thread"]["epiphanyState"]["graphs"]["architecture"]["nodes"]
@ -418,6 +597,10 @@ def run_smoke(args: argparse.Namespace) -> dict[str, Any]:
"threadId": thread_id,
"codexHome": str(codex_home),
"workspace": str(workspace),
"imaginationRevision": imagination_launch["revision"],
"imaginationResultStatus": imagination_result["status"],
"imaginationAcceptRevision": imagination_accept["revision"],
"imaginationAcceptedDraft": "draft-imagination-smoke",
"modelingRevision": modeling_launch["revision"],
"modelingResultStatus": modeling_result["status"],
"modelingNextSafeMove": modeling_result["finding"]["nextSafeMove"],
@ -439,7 +622,7 @@ def run_smoke(args: argparse.Namespace) -> dict[str, Any]:
def main() -> int:
parser = argparse.ArgumentParser(
description="Live-smoke explicit Phase 6 modeling/verification role launch and read-back."
description="Live-smoke explicit Phase 6 imagination/modeling/verification role launch and read-back."
)
parser.add_argument("--app-server", type=Path, default=DEFAULT_APP_SERVER)
parser.add_argument("--codex-home", type=Path, default=DEFAULT_CODEX_HOME)

View file

@ -4494,6 +4494,7 @@
"ThreadEpiphanyRoleId": {
"enum": [
"implementation",
"imagination",
"modeling",
"verification",
"reorientation"

View file

@ -16882,6 +16882,7 @@
"ThreadEpiphanyRoleId": {
"enum": [
"implementation",
"imagination",
"modeling",
"verification",
"reorientation"

View file

@ -14769,6 +14769,7 @@
"ThreadEpiphanyRoleId": {
"enum": [
"implementation",
"imagination",
"modeling",
"verification",
"reorientation"

View file

@ -2,4 +2,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type ThreadEpiphanyRoleId = "implementation" | "modeling" | "verification" | "reorientation";
export type ThreadEpiphanyRoleId = "implementation" | "imagination" | "modeling" | "verification" | "reorientation";

View file

@ -4197,6 +4197,7 @@ pub enum ThreadEpiphanyRolesSource {
#[ts(export_to = "v2/")]
pub enum ThreadEpiphanyRoleId {
Implementation,
Imagination,
Modeling,
Verification,
Reorientation,

View file

@ -5107,6 +5107,28 @@ impl CodexMessageProcessor {
};
let mut patch = match role_id {
ThreadEpiphanyRoleId::Imagination => {
let patch = match parse_role_finding_state_patch(&finding) {
Ok(patch) => patch,
Err(message) => {
self.send_invalid_request_error(request_id, message).await;
return;
}
};
let patch_errors = imagination_role_accept_patch_errors(&patch);
if !patch_errors.is_empty() {
self.send_invalid_request_error(
request_id,
format!(
"imagination role state patch is not acceptable: {}",
patch_errors.join("; ")
),
)
.await;
return;
}
patch
}
ThreadEpiphanyRoleId::Modeling => {
let patch = match parse_role_finding_state_patch(&finding) {
Ok(patch) => patch,
@ -5141,6 +5163,7 @@ impl CodexMessageProcessor {
};
let accepted_kind = match role_id {
ThreadEpiphanyRoleId::Imagination => "planning_synthesis",
ThreadEpiphanyRoleId::Modeling => "modeling_result",
ThreadEpiphanyRoleId::Verification => "verification_result",
ThreadEpiphanyRoleId::Implementation | ThreadEpiphanyRoleId::Reorientation => {
@ -13085,6 +13108,8 @@ fn map_epiphany_scene(state: Option<&EpiphanyThreadState>, loaded: bool) -> Thre
}
const EPIPHANY_SCENE_RECORD_LIMIT: usize = 5;
const EPIPHANY_IMAGINATION_ROLE_BINDING_ID: &str = "planning-synthesis-worker";
const EPIPHANY_IMAGINATION_OWNER_ROLE: &str = "epiphany-imagination";
const EPIPHANY_MODELING_ROLE_BINDING_ID: &str = "modeling-checkpoint-worker";
const EPIPHANY_MODELING_OWNER_ROLE: &str = "epiphany-modeler";
const EPIPHANY_VERIFICATION_ROLE_BINDING_ID: &str = "verification-review-worker";
@ -13139,6 +13164,7 @@ fn epiphany_scene_available_actions(
fn epiphany_role_binding_id(role_id: ThreadEpiphanyRoleId) -> Result<&'static str, String> {
match role_id {
ThreadEpiphanyRoleId::Imagination => Ok(EPIPHANY_IMAGINATION_ROLE_BINDING_ID),
ThreadEpiphanyRoleId::Modeling => Ok(EPIPHANY_MODELING_ROLE_BINDING_ID),
ThreadEpiphanyRoleId::Verification => Ok(EPIPHANY_VERIFICATION_ROLE_BINDING_ID),
ThreadEpiphanyRoleId::Implementation => Err(
@ -13154,6 +13180,7 @@ fn epiphany_role_binding_id(role_id: ThreadEpiphanyRoleId) -> Result<&'static st
fn epiphany_role_owner(role_id: ThreadEpiphanyRoleId) -> Result<&'static str, String> {
match role_id {
ThreadEpiphanyRoleId::Imagination => Ok(EPIPHANY_IMAGINATION_OWNER_ROLE),
ThreadEpiphanyRoleId::Modeling => Ok(EPIPHANY_MODELING_OWNER_ROLE),
ThreadEpiphanyRoleId::Verification => Ok(EPIPHANY_VERIFICATION_OWNER_ROLE),
ThreadEpiphanyRoleId::Implementation | ThreadEpiphanyRoleId::Reorientation => {
@ -13165,6 +13192,7 @@ fn epiphany_role_owner(role_id: ThreadEpiphanyRoleId) -> Result<&'static str, St
fn epiphany_role_label(role_id: ThreadEpiphanyRoleId) -> &'static str {
match role_id {
ThreadEpiphanyRoleId::Implementation => "implementation",
ThreadEpiphanyRoleId::Imagination => "imagination",
ThreadEpiphanyRoleId::Modeling => "modeling",
ThreadEpiphanyRoleId::Verification => "verification",
ThreadEpiphanyRoleId::Reorientation => "reorientation",
@ -13186,6 +13214,7 @@ struct EpiphanySpecialistPromptConfig {
#[derive(Debug, serde::Deserialize)]
struct EpiphanyRolePromptConfig {
imagination: String,
modeling: String,
verification: String,
#[allow(dead_code)]
@ -13234,6 +13263,12 @@ fn build_epiphany_role_launch_request(
let linked_subgoal_ids = epiphany_active_subgoal_ids(Some(state));
let linked_graph_node_ids = epiphany_active_graph_node_ids(Some(state));
let (scope, authority_scope, instruction, output_schema_json) = match role_id {
ThreadEpiphanyRoleId::Imagination => (
"role-scoped planning synthesis",
"epiphany.role.imagination",
build_epiphany_role_launch_instruction(role_id),
epiphany_role_launch_output_schema(role_id),
),
ThreadEpiphanyRoleId::Modeling => (
"role-scoped modeling/checkpoint maintenance",
"epiphany.role.modeling",
@ -13270,6 +13305,7 @@ fn build_epiphany_role_launch_request(
"recentObservations": state.observations.iter().take(8).collect::<Vec<_>>(),
"graphFrontier": &state.graph_frontier,
"graphCheckpoint": &state.graph_checkpoint,
"planning": &state.planning,
"churn": &state.churn,
});
@ -13292,6 +13328,7 @@ fn build_epiphany_role_launch_request(
fn build_epiphany_role_launch_instruction(role_id: ThreadEpiphanyRoleId) -> String {
let prompts = &epiphany_specialist_prompt_config().roles;
match role_id {
ThreadEpiphanyRoleId::Imagination => prompts.imagination.clone(),
ThreadEpiphanyRoleId::Modeling => prompts.modeling.clone(),
ThreadEpiphanyRoleId::Verification => prompts.verification.clone(),
ThreadEpiphanyRoleId::Implementation | ThreadEpiphanyRoleId::Reorientation => {
@ -13302,6 +13339,9 @@ fn build_epiphany_role_launch_instruction(role_id: ThreadEpiphanyRoleId) -> Stri
fn epiphany_role_launch_output_schema(role_id: ThreadEpiphanyRoleId) -> serde_json::Value {
let verdict_enum = match role_id {
ThreadEpiphanyRoleId::Imagination => {
vec!["draft-ready", "planning-update-needed", "blocked"]
}
ThreadEpiphanyRoleId::Modeling => {
vec![
"checkpoint-ready",
@ -13359,7 +13399,44 @@ fn epiphany_role_launch_output_schema(role_id: ThreadEpiphanyRoleId) -> serde_js
"nextSafeMove",
"filesInspected",
];
if role_id == ThreadEpiphanyRoleId::Modeling {
if role_id == ThreadEpiphanyRoleId::Imagination {
if let Some(map) = properties.as_object_mut() {
map.insert(
"statePatch".to_string(),
serde_json::json!({
"type": "object",
"description": "Required reviewable thread/epiphany/update patch for Imagination. Use only planning plus optional observations/evidence. planning is a full replacement object and must include at least one objective_drafts entry with status draft.",
"required": ["planning"],
"properties": {
"planning": {
"type": "object",
"required": ["objective_drafts"],
"properties": {
"objective_drafts": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": ["id", "title", "summary", "acceptance_criteria", "status"],
"properties": {
"status": {
"type": "string",
"enum": ["draft"]
}
},
"additionalProperties": true
}
}
},
"additionalProperties": true
}
},
"additionalProperties": true
}),
);
}
required.push("statePatch");
} else if role_id == ThreadEpiphanyRoleId::Modeling {
if let Some(map) = properties.as_object_mut() {
map.insert(
"statePatch".to_string(),
@ -13631,13 +13708,18 @@ fn map_epiphany_role_finding(
.get("statePatch")
.cloned()
.and_then(|patch| serde_json::from_value(patch).ok());
let item_error = if role_id == ThreadEpiphanyRoleId::Modeling {
merge_epiphany_item_error(
let item_error = match role_id {
ThreadEpiphanyRoleId::Imagination => merge_epiphany_item_error(
item_error,
imagination_role_state_patch_error(&raw_result, state_patch.as_ref()),
),
ThreadEpiphanyRoleId::Modeling => merge_epiphany_item_error(
item_error,
modeling_role_state_patch_error(&raw_result, state_patch.as_ref()),
)
} else {
item_error
),
ThreadEpiphanyRoleId::Implementation
| ThreadEpiphanyRoleId::Verification
| ThreadEpiphanyRoleId::Reorientation => item_error,
};
ThreadEpiphanyRoleFinding {
role_id,
@ -13695,6 +13777,32 @@ fn modeling_role_state_patch_error(
}
}
fn imagination_role_state_patch_error(
raw_result: &serde_json::Value,
state_patch: Option<&ThreadEpiphanyUpdatePatch>,
) -> Option<String> {
let Some(value) = raw_result.get("statePatch") else {
return Some(
"imagination result is not reviewable: missing required statePatch".to_string(),
);
};
let Some(patch) = state_patch else {
return Some(format!(
"imagination result is not reviewable: invalid statePatch ({})",
serde_json::from_value::<ThreadEpiphanyUpdatePatch>(value.clone()).unwrap_err()
));
};
let errors = imagination_role_accept_patch_errors(patch);
if errors.is_empty() {
None
} else {
Some(format!(
"imagination result is not reviewable: {}",
errors.join("; ")
))
}
}
fn epiphany_modeling_finding_has_reviewable_state_patch(
finding: &ThreadEpiphanyRoleFinding,
) -> bool {
@ -13705,6 +13813,17 @@ fn epiphany_modeling_finding_has_reviewable_state_patch(
.is_some_and(|patch| modeling_role_accept_patch_errors(patch).is_empty())
}
#[cfg(test)]
fn epiphany_imagination_finding_has_reviewable_state_patch(
finding: &ThreadEpiphanyRoleFinding,
) -> bool {
finding.role_id == ThreadEpiphanyRoleId::Imagination
&& finding
.state_patch
.as_ref()
.is_some_and(|patch| imagination_role_accept_patch_errors(patch).is_empty())
}
fn json_string_field(value: &serde_json::Value, key: &str) -> Option<String> {
value
.get(key)
@ -14671,6 +14790,7 @@ fn epiphany_role_finding_accepted_index(
finding: &ThreadEpiphanyRoleFinding,
) -> Option<usize> {
let accepted_kind = match finding.role_id {
ThreadEpiphanyRoleId::Imagination => "planning_synthesis",
ThreadEpiphanyRoleId::Modeling => "modeling_result",
ThreadEpiphanyRoleId::Verification => "verification_result",
ThreadEpiphanyRoleId::Implementation | ThreadEpiphanyRoleId::Reorientation => {
@ -14714,6 +14834,34 @@ fn map_epiphany_roles(
) -> Vec<ThreadEpiphanyRoleLane> {
let state_present = state.is_some();
let checkpoint = state.and_then(|state| state.investigation_checkpoint.as_ref());
let imagination_jobs = jobs
.iter()
.filter(|job| job.id == EPIPHANY_IMAGINATION_ROLE_BINDING_ID)
.cloned()
.collect::<Vec<_>>();
let imagination_bound_job = imagination_jobs
.iter()
.find(|job| job.id == EPIPHANY_IMAGINATION_ROLE_BINDING_ID);
let imagination_has_bound_job = imagination_bound_job.is_some();
let imagination_status = imagination_bound_job
.as_ref()
.map(|job| map_epiphany_job_status_to_role_status(job.status))
.unwrap_or_else(|| map_epiphany_imagination_role_status(state));
let imagination_note = imagination_bound_job
.as_ref()
.and_then(|job| {
job.blocking_reason
.clone()
.or_else(|| job.progress_note.clone())
})
.unwrap_or_else(|| render_epiphany_imagination_role_note(state));
let imagination_recommended_action = if imagination_has_bound_job {
Some(ThreadEpiphanySceneAction::RoleResult)
} else if state_present {
Some(ThreadEpiphanySceneAction::RoleLaunch)
} else {
Some(ThreadEpiphanySceneAction::Update)
};
let modeling_jobs = jobs
.iter()
.filter(|job| {
@ -14809,6 +14957,21 @@ fn map_epiphany_roles(
recommendation.recommended_scene_action
},
},
ThreadEpiphanyRoleLane {
id: ThreadEpiphanyRoleId::Imagination,
title: "Imagination / Planning".to_string(),
owner_role: EPIPHANY_IMAGINATION_OWNER_ROLE.to_string(),
status: imagination_status,
note: imagination_note,
jobs: imagination_jobs,
authority_scopes: vec![
"thread/epiphany/roleLaunch".to_string(),
"thread/epiphany/roleResult".to_string(),
"thread/epiphany/roleAccept".to_string(),
"thread/epiphany/update".to_string(),
],
recommended_action: imagination_recommended_action,
},
ThreadEpiphanyRoleLane {
id: ThreadEpiphanyRoleId::Modeling,
title: "Modeling / Checkpoint".to_string(),
@ -14861,6 +15024,37 @@ fn map_epiphany_roles(
]
}
fn map_epiphany_imagination_role_status(
state: Option<&EpiphanyThreadState>,
) -> ThreadEpiphanyRoleStatus {
let Some(state) = state else {
return ThreadEpiphanyRoleStatus::Blocked;
};
if state.planning.is_empty() {
ThreadEpiphanyRoleStatus::Needed
} else {
ThreadEpiphanyRoleStatus::Ready
}
}
fn render_epiphany_imagination_role_note(state: Option<&EpiphanyThreadState>) -> String {
let Some(state) = state else {
return "No authoritative Epiphany state exists for planning synthesis.".to_string();
};
let planning = &state.planning;
if planning.is_empty() {
return "Planning substrate is empty; capture or import backlog material before synthesis."
.to_string();
}
format!(
"Planning material ready: {} captures, {} backlog items, {} roadmap streams, {} objective drafts.",
planning.captures.len(),
planning.backlog_items.len(),
planning.roadmap_streams.len(),
planning.objective_drafts.len()
)
}
fn map_epiphany_modeling_role_status(
checkpoint_present: bool,
graph_remap_job: Option<&ThreadEpiphanyJob>,
@ -15118,12 +15312,86 @@ fn parse_role_finding_state_patch(
finding: &ThreadEpiphanyRoleFinding,
) -> Result<ThreadEpiphanyUpdatePatch, String> {
let Some(value) = finding.raw_result.get("statePatch") else {
return Err(
"completed modeling role finding did not include a reviewable statePatch".to_string(),
);
return Err("completed role finding did not include a reviewable statePatch".to_string());
};
serde_json::from_value(value.clone())
.map_err(|err| format!("completed modeling role finding has invalid statePatch: {err}"))
.map_err(|err| format!("completed role finding has invalid statePatch: {err}"))
}
fn imagination_role_accept_patch_errors(patch: &ThreadEpiphanyUpdatePatch) -> Vec<String> {
let mut errors = Vec::new();
if patch.objective.is_some() {
errors.push(
"objective changes are not allowed through imagination role acceptance".to_string(),
);
}
if patch.active_subgoal_id.is_some() || patch.subgoals.is_some() {
errors.push(
"subgoal changes are not allowed through imagination role acceptance".to_string(),
);
}
if patch.invariants.is_some() {
errors.push(
"invariant changes are not allowed through imagination role acceptance".to_string(),
);
}
if patch.graphs.is_some()
|| patch.graph_frontier.is_some()
|| patch.graph_checkpoint.is_some()
|| patch.investigation_checkpoint.is_some()
{
errors.push(
"graph or checkpoint changes are not allowed through imagination role acceptance"
.to_string(),
);
}
if patch.scratch.is_some() {
errors.push(
"scratch changes are not allowed through imagination role acceptance".to_string(),
);
}
if patch.job_bindings.is_some() {
errors.push(
"job binding changes are not allowed through imagination role acceptance".to_string(),
);
}
if patch.churn.is_some() || patch.mode.is_some() {
errors.push(
"churn or mode changes are not allowed through imagination role acceptance".to_string(),
);
}
let Some(planning) = patch.planning.as_ref() else {
errors.push("statePatch must include planning changes".to_string());
return errors;
};
if planning.objective_drafts.is_empty() {
errors.push("planning patch must include at least one objective draft".to_string());
}
if !planning
.objective_drafts
.iter()
.any(|draft| draft.status.eq_ignore_ascii_case("draft"))
{
errors.push(
"planning patch must include at least one objective draft with status draft"
.to_string(),
);
}
for draft in &planning.objective_drafts {
if draft.acceptance_criteria.is_empty() {
errors.push(format!(
"planning objective draft {:?} must include acceptance criteria",
draft.id
));
}
if draft.review_gates.is_empty() {
errors.push(format!(
"planning objective draft {:?} must include review gates",
draft.id
));
}
}
errors
}
fn modeling_role_accept_patch_errors(patch: &ThreadEpiphanyUpdatePatch) -> Vec<String> {
@ -20175,16 +20443,18 @@ mod tests {
role_ids,
vec![
ThreadEpiphanyRoleId::Implementation,
ThreadEpiphanyRoleId::Imagination,
ThreadEpiphanyRoleId::Modeling,
ThreadEpiphanyRoleId::Verification,
ThreadEpiphanyRoleId::Reorientation,
]
);
assert_eq!(roles[0].status, ThreadEpiphanyRoleStatus::Ready);
assert_eq!(roles[1].status, ThreadEpiphanyRoleStatus::Ready);
assert_eq!(roles[2].status, ThreadEpiphanyRoleStatus::Needed);
assert_eq!(roles[2].jobs[0].owner_role, "epiphany-verifier");
assert_eq!(roles[3].status, ThreadEpiphanyRoleStatus::Ready);
assert_eq!(roles[1].status, ThreadEpiphanyRoleStatus::Needed);
assert_eq!(roles[2].status, ThreadEpiphanyRoleStatus::Ready);
assert_eq!(roles[3].status, ThreadEpiphanyRoleStatus::Needed);
assert_eq!(roles[3].jobs[0].owner_role, "epiphany-verifier");
assert_eq!(roles[4].status, ThreadEpiphanyRoleStatus::Ready);
}
fn base_coordinator_roles() -> Vec<ThreadEpiphanyRoleLane> {
@ -20193,6 +20463,10 @@ mod tests {
ThreadEpiphanyRoleId::Implementation,
ThreadEpiphanyRoleStatus::Ready,
),
coordinator_role(
ThreadEpiphanyRoleId::Imagination,
ThreadEpiphanyRoleStatus::Ready,
),
coordinator_role(
ThreadEpiphanyRoleId::Modeling,
ThreadEpiphanyRoleStatus::Ready,
@ -20943,6 +21217,12 @@ mod tests {
#[test]
fn epiphany_specialist_prompt_config_parses() {
let prompts = epiphany_specialist_prompt_config();
assert!(
prompts
.roles
.imagination
.contains("Imagination of the machine")
);
assert!(prompts.roles.modeling.contains("Body of the machine"));
assert!(prompts.roles.verification.contains("Soul of the machine"));
assert!(prompts.roles.research.contains("Eyes of the machine"));
@ -20975,6 +21255,7 @@ mod tests {
ThreadEpiphanyCoordinatorAction::LaunchVerification,
);
assert!(note.contains("read-only Self"));
assert!(note.contains("Imagination/planning"));
assert!(note.contains("Eyes/research"));
assert!(note.contains("Hands/implementation"));
assert!(note.contains("LaunchVerification"));
@ -21000,6 +21281,41 @@ mod tests {
..Default::default()
};
let imagination = build_epiphany_role_launch_request(
"thr_123",
ThreadEpiphanyRoleId::Imagination,
Some(7),
Some(90),
&state,
)
.expect("imagination should have a fixed launch template");
assert_eq!(imagination.binding_id, EPIPHANY_IMAGINATION_ROLE_BINDING_ID);
assert_eq!(imagination.owner_role, EPIPHANY_IMAGINATION_OWNER_ROLE);
assert_eq!(imagination.authority_scope, "epiphany.role.imagination");
assert_eq!(imagination.input_json["roleId"], "imagination");
assert!(imagination.input_json.get("planning").is_some());
assert!(
imagination
.instruction
.contains("Imagination of the machine")
);
assert!(
imagination
.instruction
.contains("reviewable `thread/epiphany/update` patch")
);
let imagination_schema = imagination.output_schema_json.as_ref().unwrap();
assert!(imagination_schema["properties"].get("statePatch").is_some());
assert_eq!(
imagination_schema["properties"]["statePatch"]["required"][0],
"planning"
);
assert_eq!(
imagination_schema["properties"]["statePatch"]["properties"]["planning"]["properties"]
["objective_drafts"]["minItems"],
1
);
let modeling = build_epiphany_role_launch_request(
"thr_123",
ThreadEpiphanyRoleId::Modeling,
@ -21283,6 +21599,130 @@ mod tests {
));
}
#[test]
fn map_epiphany_role_finding_marks_imagination_without_planning_patch_unreviewable() {
let missing_patch = map_epiphany_role_finding(
ThreadEpiphanyRoleId::Imagination,
serde_json::json!({
"roleId": "imagination",
"verdict": "draft-ready",
"summary": "A plan was described but no durable planning patch was returned.",
"nextSafeMove": "Relaunch imagination with a state patch."
}),
None,
None,
);
assert!(missing_patch.state_patch.is_none());
assert!(
missing_patch
.item_error
.as_deref()
.is_some_and(|error| error.contains("missing required statePatch"))
);
assert!(!epiphany_imagination_finding_has_reviewable_state_patch(
&missing_patch
));
let reviewable = map_epiphany_role_finding(
ThreadEpiphanyRoleId::Imagination,
serde_json::json!({
"roleId": "imagination",
"verdict": "draft-ready",
"summary": "Planning material was synthesized into a draft objective.",
"nextSafeMove": "Review and optionally adopt the draft.",
"statePatch": {
"planning": {
"captures": [
{
"id": "capture-imagination-test",
"title": "Planning source",
"confidence": "medium",
"status": "triaged",
"source": {"kind": "chat", "uri": "codex://threads/test"}
}
],
"backlog_items": [
{
"id": "backlog-imagination-test",
"title": "Synthesized planning seam",
"kind": "feature",
"summary": "Prove Imagination can bank a reviewable objective draft.",
"status": "ready",
"horizon": "near",
"priority": {"value": "medium", "rationale": "Smoke coverage"},
"confidence": "medium",
"product_area": "planning",
"source_refs": [{"kind": "chat", "uri": "codex://threads/test"}]
}
],
"objective_drafts": [
{
"id": "draft-imagination-test",
"title": "Review Imagination patch",
"summary": "Accept a planning-only role patch after review.",
"source_item_ids": ["backlog-imagination-test"],
"scope": {
"includes": ["planning"],
"excludes": ["objective adoption", "implementation"]
},
"acceptance_criteria": ["A planning patch can be accepted without adopting the draft."],
"evidence_required": ["role finding"],
"lane_plan": {"imagination": "synthesize", "soul": "review"},
"risks": ["accidental adoption"],
"review_gates": ["human adoption review"],
"status": "draft"
}
]
}
}
}),
None,
None,
);
assert!(reviewable.item_error.is_none());
assert!(epiphany_imagination_finding_has_reviewable_state_patch(
&reviewable
));
let missing_review_gate = map_epiphany_role_finding(
ThreadEpiphanyRoleId::Imagination,
serde_json::json!({
"roleId": "imagination",
"verdict": "draft-ready",
"summary": "The draft forgot its gate.",
"nextSafeMove": "Add review gates before accepting.",
"statePatch": {
"planning": {
"objective_drafts": [
{
"id": "draft-no-gate",
"title": "Ungated draft",
"summary": "This must not be accepted.",
"scope": {},
"acceptance_criteria": ["It has criteria."],
"lane_plan": {},
"status": "draft"
}
]
}
}
}),
None,
None,
);
assert!(
missing_review_gate
.item_error
.as_deref()
.is_some_and(|error| error.contains("review gates"))
);
assert!(!epiphany_imagination_finding_has_reviewable_state_patch(
&missing_review_gate
));
}
#[test]
fn role_finding_accepted_after_uses_newest_first_evidence_order() {
let modeling = map_epiphany_role_finding(

View file

@ -1,4 +1,14 @@
[roles]
imagination = '''
Act as the Epiphany planning synthesis specialist for one bounded pass.
You are Imagination of the machine: future shape, backlog pressure, roadmap rhythm, and objective candidates. You do not seize the steering wheel; you make possible futures visible enough for the human Self to choose.
Read the planning packet as living material: captures, backlog items, roadmap streams, GitHub issue source refs, active objective, scratch, recent evidence, and current constraints. Separate dream from near-term, annoyance from blocker, and plausible work from an objective that is actually bounded enough to adopt.
Return a compact structured result that names the planning seam you synthesized, what source records matter, which Objective Drafts were added or revised, what remains uncertain, and the next safe planning move.
`statePatch` is part of the Imagination job. Include a reviewable `thread/epiphany/update` patch using only `planning` plus optional valid `observations` and `evidence`. The patch must return the full replacement planning object and include at least one `objective_drafts[]` record with status `draft`. Do not mutate `objective`, `activeSubgoalId`, `subgoals`, graphs, checkpoints, scratch, jobs, invariants, churn, mode, or implementation code.
Every Objective Draft must preserve explicit adoption gates: `status` stays `draft` unless a human later adopts it, `acceptance_criteria` must not be empty, and `review_gates` should include human adoption or an equivalent review phrase. Every new backlog item or capture must keep source refs where known; GitHub issues remain source refs, not internal authority.
Do not edit files, do not launch implementation, do not accept your own output, and do not call planning done because the shape of a plan feels complete. The point is a future the user can inspect without being dragged into it.
'''
modeling = '''
Act as the Epiphany modeling/checkpoint specialist for one bounded pass.
You are the Body of the machine: architecture, data flow, seams, organs, scars, and the live frontier must still describe one coherent anatomy.
@ -97,7 +107,7 @@ Do not continue broad implementation until the seam is re-grounded, and do not c
[coordinator]
note_template = '''
Coordinator is the read-only Self of the Epiphany organism. It does not implement, verify, promote, or accept; it preserves agency by routing Body/modeling, Eyes/research, Hands/implementation, Soul/verification, and Life/reorientation from CRRC {crrc_action}, pressure {pressure_level}, modeling {modeling_result_status}, verification {verification_result_status}, and reorient {reorient_result_status}. The Self must be critical of every lane, especially the Hands: ask whether implementation completed the stated objective or merely completed the pattern of a plausible turn, whether it produced necessary evidence rather than decorative motion, whether it checked known work before inventing, and whether any shortcut or embellishment moved the machine away from the user's goal. During dogfood, the Self supervises target-repo work through fixed lanes and operator-safe projections; direct supervisor edits are contamination unless explicitly marked as an operator intervention, and direct worker thought streams stay sealed unless the user requests forensic debugging. Current recommendation: {coordinator_action}.
Coordinator is the read-only Self of the Epiphany organism. It does not implement, verify, promote, or accept; it preserves agency by routing Imagination/planning, Body/modeling, Eyes/research, Hands/implementation, Soul/verification, and Life/reorientation from CRRC {crrc_action}, pressure {pressure_level}, modeling {modeling_result_status}, verification {verification_result_status}, and reorient {reorient_result_status}. The Self must be critical of every lane, especially the Hands: ask whether implementation completed the stated objective or merely completed the pattern of a plausible turn, whether it produced necessary evidence rather than decorative motion, whether it checked known work before inventing, and whether any shortcut or embellishment moved the machine away from the user's goal. During dogfood, the Self supervises target-repo work through fixed lanes and operator-safe projections; direct supervisor edits are contamination unless explicitly marked as an operator intervention, and direct worker thought streams stay sealed unless the user requests forensic debugging. Current recommendation: {coordinator_action}.
'''
[crrc]