Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3646245
Record Codex additionalContext POC evidence
usulpro Jul 5, 2026
f2a145d
docs: record Codex-to-Claude-Code platform handoff for mode-reminder POC
usulpro Jul 5, 2026
38a2cb2
Record Claude Code additionalContext POC evidence and close Phase 3
usulpro Jul 5, 2026
a6810d0
docs(skill): document 'unbind epic' trigger and unbind-session contra…
usulpro Jul 5, 2026
98e3e78
Close Phase 4 after clean cross-doc audit of the unbind contract
usulpro Jul 5, 2026
64b6c81
feat(skill): per-turn mode reminder injection and unbind-session script
usulpro Jul 5, 2026
02d6fae
test: end-to-end unit tests for mode reminder injection and unbind-se…
usulpro Jul 5, 2026
6ce18f9
docs(refs): document mode reminder and unbind lifecycle in hooks-and-…
usulpro Jul 5, 2026
e39708a
Close Phase 6 after green final verification of the reminder/unbind f…
usulpro Jul 5, 2026
7e05b06
Record epic implementation exit: unbound-silence risk mitigated by te…
usulpro Jul 5, 2026
96871b4
docs: lint epic
usulpro Jul 6, 2026
0f52391
update epics
usulpro Jul 6, 2026
119a0c8
docs(epic): accept epic-centric mode model and rebuild Phase 7
usulpro Jul 7, 2026
387ec7b
Add runtime-backed epic mode command
usulpro Jul 7, 2026
38effd4
Clarify epic-loop runtime and source separation
usulpro Jul 7, 2026
ad1be44
Rework epic bindings around runtime driver
usulpro Jul 7, 2026
72bb3f6
Emit compact epic mode markers
usulpro Jul 7, 2026
41623de
Auto-bind resumed epic member sessions
usulpro Jul 7, 2026
104b034
Record phase seven verification evidence
usulpro Jul 7, 2026
6aa9ad0
Close phase seven on source verification
usulpro Jul 7, 2026
46325b8
Record mode reminder implementation exit
usulpro Jul 7, 2026
5d37a5f
fix: repair epic runtime compatibility in doctor
usulpro Jul 7, 2026
b0a6cc7
chore(epic): update rules
usulpro Jul 7, 2026
abbc61b
Shape skill validation phases
usulpro Jul 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions .epic-loop/epics/mode-reminder/decision-log.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,29 @@

## Active Decisions

- The per-turn mode reminder will use `hookSpecificOutput.additionalContext` on `UserPromptSubmit`, the same field name and shape on both Claude Code and Codex CLI. Confirmed via `developers.openai.com/codex/hooks` that Codex supports this field identically to Claude Code. **Not yet confirmed by a real POC on either platform** — Phase 3 of this epic exists specifically to verify this against actual running agents, not just docs.
- Codex's TUI may render `additionalContext` as a visible developer message (per `openai/codex` issues #16486, #16933), unlike Claude Code where it behaves more like an invisible system-reminder. Decision: accept this difference as-is; a visible reminder is a transparency plus, not a defect to mask.
- The per-turn mode reminder will use `hookSpecificOutput.additionalContext` on `UserPromptSubmit`, the same field name and shape on both Claude Code and Codex CLI. Confirmed via `developers.openai.com/codex/hooks` that Codex supports this field identically to Claude Code. Real Codex CLI POC completed on 2026-07-06: an interactive `codex --no-alt-screen --dangerously-bypass-hook-trust --enable hooks -C /projects/my-projects/epic-loop ...` run with a temporary trusted-project `UserPromptSubmit` hook injected `POC-CODEX-ADDITIONAL-CONTEXT-TRUSTED-1783273589-8273`; Codex visibly rendered `UserPromptSubmit hook (completed)` and `hook context: ...8273`, and the model returned the exact token. Real Claude Code POC also completed on 2026-07-06 (see the Claude Code POC decision below); the mechanism is now proven natively on both platforms.
- Claude Code POC evidence (2026-07-06, Claude Code 2.1.201): a scratch-project `.claude/settings.json` `UserPromptSubmit` hook echoing `{"hookSpecificOutput":{"hookEventName":"UserPromptSubmit","additionalContext":"POC-CLAUDE-ADDCTX-1783274622"}}` fired on a real headless turn (`claude -p`, nested run with `CLAUDECODE`/`CLAUDE_CODE_ENTRYPOINT`/`CLAUDE_CODE_SSE_PORT` unset); the model replied `TOKEN_SEEN=yes TOKEN_VALUE=POC-CLAUDE-ADDCTX-1783274622`. The session transcript JSONL represents the injection as a dedicated attachment record `{"attachment":{"type":"hook_additional_context","content":["POC-CLAUDE-ADDCTX-1783274622"],"hookName":"UserPromptSubmit","hookEvent":"UserPromptSubmit"}}` preceding the assistant reply. No trust/permission gating was hit on the headless path — the project-local hook fired without extra flags. Known limitation: interactive TUI rendering was not observed (nested interactive TUI is not runnable from the verifying environment); the transcript attachment record is the accepted primary evidence.
- Codex's TUI renders `additionalContext` visibly as hook context in the transcript/TUI. Decision: accept this difference as-is; a visible reminder is a transparency plus, not a defect to mask. The POC also showed that `codex exec --json --ephemeral` did not fire the project-local hook in the untrusted scratch setup, so the proven path is the interactive CLI/TUI path in a trusted project.
- The new detach script is named `unbind-session.mjs`, for naming consistency with the existing `bind-session.mjs`.
- The unbind trigger should not be a single rigid memorized phrase. The agent should recognize user intent to work outside the epic (e.g. "do this right now", "let's work without the epic for a bit") and call `unbind-session.mjs` proactively, while a short canonical phrase is still designed as a reliable fallback and is made explicit in `SKILL.md`'s frontmatter description. Exact phrase to be decided in Phase 4.
- The unbind trigger should not be a single rigid memorized phrase. The agent should recognize user intent to work outside the epic (e.g. "do this right now", "let's work without the epic for a bit") and call `unbind-session.mjs` proactively, while a short canonical phrase is still designed as a reliable fallback and is made explicit in `SKILL.md`'s frontmatter description.
- The canonical unbind trigger phrase is **`unbind epic`** (decided 2026-07-06, Phase 4): it mirrors the `bind-session.mjs`/`unbind-session.mjs` naming pair, is ultra-short, and lists naturally among the frontmatter `description` triggers. `SKILL.md` now documents it in the frontmatter (verbatim) and in a `## Hooks` subsection: intent-based proactive unbind with the phrase as the always-reliable fallback, `--current`/`--session-id` command shapes, optional `--reason`, no `--slug`/`--mode`, no-op for unbound sessions, rebind via the normal resume flow. Live evidence: after `self-update`, the running Claude Code session reloaded the updated skill description containing the new trigger.
- Rebinding/resuming after an unbind reuses the existing resume flow; no new reattach mechanism is assumed necessary unless Phase 1 design work finds a gap.
- Phase 3 runs Codex's POC first, then Claude Code's POC, with an explicit stop-and-switch task in between. Each platform must run its own POC from a real session on that platform — not simulated from the other platform — so the evidence is genuine per-platform proof, not a cross-platform guess.
- Phase 2 design proposal accepted (`docs/mode-reminder-design.md`): the mode reminder is built by a new `buildModeReminder(payload, binding)` in `lib/hooks.mjs` (not `loop.mjs`), sourced solely from `binding.mode` in the already-loaded `session-bindings.json` — no extra file reads. It fires only for bound `shaping`/`review` sessions on `UserPromptSubmit`; implementation-mode sessions are excluded because the existing `Stop`-driven loop machinery already carries role/mode context every turn.
- `handleHook`'s only control-flow change is a fallback: `maybeBuildImplementationContinuation(...) ?? buildModeReminder(...)`. The two builders are mutually exclusive by construction (one requires `binding.mode === "implementation"`, the other requires it not to be), so no branching logic was added.
- Reminder text is fixed to one line per mode (see `docs/mode-reminder-design.md` section 1), prefixed `[epic-loop]`, naming the epic slug and pointing at the relevant `SKILL.md` rule section. Wording may be tuned after Phase 3 POC evidence, but the mechanism and source do not change.
- `unbind-session.mjs` takes `--current`/`--session-id` (matching `bind-session.mjs`'s detection semantics) plus an optional `--reason`, but deliberately has no `--slug`/`--mode` — it deactivates whatever epic/mode the resolved session id is currently actively bound to, since a session can only ever hold one active binding.
- Unbinding sets `active: false`, `deactivated_at`, and a new additive `deactivated_reason` field on the session's `session-bindings.json` entry (mirroring the shape `bindSession` already writes when a different session supersedes one), and clears the `active_sessions["<slug>:<mode>"]` pointer if it still points at this session id. No schema change for any other reader. A no-op (exit 0) if the session is already unbound or was never bound.
- **Epic-centric mode model accepted (2026-07-07, supersedes the same-day per-binding-mode plan; full design in `docs/epic-mode-model.md`):** an epic is always in exactly one lifecycle mode, held solely in `runtime-state.json` `mode` (`shaping | implementation | review`). Session bindings become mode-less **epic membership**: any number of sessions may be active members of one epic, all receive the same mode marker, and a mode change by any session propagates to every member's next `UserPromptSubmit` without rebinding. Parallel work on different epics is preserved unchanged (one active binding per session, distinct epics never interact). "Multiple sessions on the same epic in different modes" is no longer a supported state.
- `active_sessions` is deleted from the `session-bindings.json` schema (2026-07-07): it only enforced one-session-per-epic/mode, which membership abolishes for shaping/review. This removes the stale-pointer bug class by construction — the earlier "clear stale `active_sessions` on rebind" fix task is superseded, not implemented. Readers tolerate the old leftover shape; the state is gitignored runtime data, no migration script.
- Implementation keeps one exclusive **driver** session (2026-07-07): recorded as `implementation_loop.driver_session_id` in the epic's `runtime-state.json`. Only the driver's `Stop` events continue the loop; a `UserPromptSubmit` from a non-driver member does not interrupt the loop; unbinding the driver sets the loop to `idle` with a clear reason. Entering implementation = membership + set epic mode + designate driver + start loop.
- The human-facing `Current mode:` line in `state-of-epic.md` is dropped entirely (2026-07-07): removed from the `init-epic` template, and `readEpicStateSummary` in `lib/loop.mjs` stops regex-parsing prose for the mode (switches to `runtime-state.json`). Rationale: machines parsing human prose was the ambiguity source; the mode is machine state, `state-of-epic.md` stays purely narrative. New `set-epic-mode.mjs` maintains transitions that today go stale (observed on this very epic: runtime said `implementation` while actually shaping).
- Reminder sourcing change (2026-07-07, supersedes the Phase 2 "no extra file read" decision): `buildModeReminder` reads the epic's `runtime-state.json` `mode` instead of `binding.mode`. Cost: one extra small JSON read per prompt — accepted as the price of live mode propagation to all members; missing/corrupt runtime-state → silent skip, never a crash.
- Compact marker text accepted (2026-07-07): `[epic-loop] epic=<slug> mode=<mode> — follow epic-loop skill mode rules`. A bare marker without an action hint was rejected (risks being read as background noise when `SKILL.md` is not yet loaded); mode-specific instructions stay in `SKILL.md`, triggered via the frontmatter description mentioning the marker pattern. Constraint: the frontmatter description is currently 895 chars against Claude Code's 1024-char limit, so the marker mention must fit in ~120 chars.
- Auto-bind on resume, race guard (2026-07-07): resume by slug/path binds the current session as a member (no mode flag; reminders follow the epic mode automatically; no automatic driver designation for implementation epics). The current-session capture file is last-writer-wins per project with a 15-minute TTL and is written on every hook event of every session, so a parallel session can overwrite it between the user's prompt and the bind call. For auto-bind the capture is accepted only when fresh AND `hook_event_name === "UserPromptSubmit"`; on Codex the mtime transcript fallback makes the race worse, so the same guard applies there. If no acceptable capture exists (e.g. hooks installed but not yet trusted in the running thread), auto-bind is skipped with a one-line notice and resume orientation continues — the general "no hooks installed" path is out of scope because `doctor` gates the whole flow on hook setup before any epic work.
- Implementation **lock marker** for non-driver members (2026-07-07, closes the last open question of the epic-centric model): when the epic mode is `implementation`, non-driver member sessions receive `[epic-loop] epic=<slug> mode=implementation — loop running in another session; read-only, do not edit epic artifacts` on every `UserPromptSubmit`; the driver receives nothing (loop continuations already carry role context). The marker is a deliberate advisory locker protecting the loop's artifact writes (manager compaction, log appends, tracker renders) from concurrent shaping edits; it is not a mechanical write barrier, which is accepted for the agent-session threat model.
- Follow-up work is tracked as **Phase 7 — Epic-Centric Mode Model And Compact Reminder** (2026-07-07), ordered: mode source → membership/driver model → marker → auto-bind → live multi-session verification. The umbrella "design and fix binding lifecycle" follow-up task was removed as duplicative: its design content is this decision set plus `docs/epic-mode-model.md`.

## Historical Decisions

- None recorded yet.
- Superseded same day by the epic-centric model (2026-07-07): the first follow-up plan kept per-binding modes and single-active-session-per-epic/mode semantics — Phase 7 was initially "Fix Session Binding Lifecycle And Compact Reminder" with a "clear stale `active_sessions` pointers on rebind" fix task, "last bind wins" takeover for two sessions in the same mode on the same epic, and `state-of-epic.md`'s `Current mode:` line kept as human-facing information. Preserved in the superseded-marked "Accepted Plan (2026-07-07)" section of `docs/shaping-binding-gap.md`.
Loading
Loading