| name | wrap |
| description | Close a DinoStack work session by safely consolidating context, memory, learnings, deferred work, and handoff state. |
Codex resource resolution
Before executing this skill, resolve the physical directory containing this loaded SKILL.md
(follow the installed skill-directory symlink) and bind it as AE_SKILL_ROOT. Set
AE_CORE_SKILL_ROOT to resources beneath that physical root and validate its
.dinostack-skill.json marker has magic=DINOSTACK_CODEX_SKILL, adapter=codex,
name=agentic-engineering, and schema_version=1. Resolve every logical resource through
the adjacent RESOURCE-MAP.json; reject missing, escaping, symlink-loop, or wrong-type targets.
Derive AE_REPO_DIR from the validated core marker plus its mapped bin resource and require the
repository signature (content/SKILL.md, .codex, and the dispatch helper); never fall back to
the process working directory. Bind AE_PROJECT_DIR to the absolute invoked project root before
changing directories (git rev-parse --show-toplevel when inside a repository, otherwise the
verified invocation directory). Project .claude/**, .agentic/**, .gitignore, QA, settings,
compression, and migration state resolve only beneath AE_PROJECT_DIR, never beneath
AE_REPO_DIR. Evaluate
$AE_REPO_DIR/bin/agentic-codex-dispatch runtime-bindings "<absolute-invocation-directory>"
before any operational step. Require its AE_REPO_DIR and AE_PROJECT_DIR values to match the
independently validated paths above, then consume the same JSON object to bind
AE_CODEX_CONFIG_DIR, AE_SHARED_CONFIG_DIR, and AE_ACTIVATION_CONFIG; fail closed on any
mismatch. Map canonical filesystem tools to Codex filesystem reads, rg --files, rg, shell, and
apply_patch; ask one bounded direct question only after default derivation.
Derive AE_SESSION_ID by passing hook JSON to
$AE_REPO_DIR/bin/agentic-codex-session-id. Native workflows are invoked with $ syntax.
Other DinoStack workflows remain manual command resources loaded with
$AE_REPO_DIR/bin/agentic-codex-dispatch command <name>; do not claim bare slash registration.
Codex spawn_agent accepts only task_name, message, and fork_turns. Put Tier and model intent
in the task brief or resolve it through role routing before the spawn; never pass Claude-only spawn
fields. When isolation is required, the conductor creates the worktree manually before spawning.
Codex spawn contract. Delegate with spawn_agent only. Before any spawn that needs an
isolated checkout, run the following from the invoked project root ($AE_PROJECT_DIR):
git fetch origin.
- Resolve
BASE_BRANCH with
$AE_REPO_DIR/bin/agentic-codex-dispatch base-branch "$AE_PROJECT_DIR". This applies the
canonical precedence: exactly one dedicated unfenced whole-line BASE_BRANCH: declaration in
project AGENTS.md (with an optional Markdown list prefix and optional Declaration: prefix),
then local develop, then local development. Multiple matching declarations are rejected as
ambiguous. If none exists, the helper fails closed; ask the operator whether to use main
(recommended, falling back to master) or establish a develop-based workflow, exactly as
required by the base-branch resolution protocol.
- Choose a unique branch and absolute worktree path beneath
$AE_PROJECT_DIR/.agentic/worktrees/.
- Run
git worktree add "$AE_PROJECT_DIR/.agentic/worktrees/<branch>" -b "<branch>" "origin/$BASE_BRANCH".
- Load the named role instructions with
$AE_REPO_DIR/bin/agentic-codex-dispatch agent <role>.
- Call
spawn_agent with supported inputs (task_name, message, and fork_turns). Begin the
message with Work only in the pre-created worktree <absolute-path> and include the loaded role
instructions plus the execution contract. The spawned agent must use shell commands in that
worktree and must not edit the conductor checkout.
Codex spawns are asynchronous. The conductor remains responsive, uses the collaboration status and
wait operations to collect completion, and applies the existing review gates to the returned diff.
Claude hook payload fields and Claude Task behavior do not apply on Codex.
$wrap — On-Demand Session Context Enrichment
Run the Activation preflight from $AE_CORE_SKILL_ROOT/METHODOLOGY.md before proceeding. If inactive, no-op and exit.
Use when you want a richer project-local handoff than the current Codex Stop continuity file provides - for example, before handing off complex in-progress work to a future session.
The current Codex Stop hook writes raw continuity only to ~/.codex/projects/[hash]/context.md after a Stop event. It does not write project-local $AE_PROJECT_DIR/.agentic/context.d/<session_id>.md, does not write $AE_PROJECT_DIR/.agentic/context.md, and does not recompose a project-local rollup; migration is deferred to context-writer-migration. $wrap continues to write the richer project-local $AE_PROJECT_DIR/.agentic/_wrap.md handoff and populate AGENTS.md with durable decisions, conventions, stack details, and gotchas.
Relationship to wrap-ticket. $wrap is the on-demand richer session-summarization tool that targets AGENTS.md, MEMORY.md, and $AE_PROJECT_DIR/.agentic/_wrap.md across an entire session and uses Skeptic review. The per-ticket Phase 11b wrap-ticket agent (see $AE_REPO_DIR/content/agents/wrap-ticket.md) is a constrained automated subset that fires on every PR opened by $implement-ticket — it appends to MEMORY.md, decisions.md, and $AE_PROJECT_DIR/.agentic/_wrap.md only, never touches AGENTS.md, and runs without Skeptic. They write to overlapping files (MEMORY.md, _wrap.md) but at non-overlapping cadences (per-ticket vs per-session); both follow append-discipline so the concurrent-write hazard is bounded. wrap-ticket and $wrap MUST NOT run concurrently — both acquire $AE_PROJECT_DIR/.agentic/wrap/lock. If $wrap is invoked while wrap-ticket holds the lock, $wrap waits per the standard lock-wait protocol below; if wrap-ticket is invoked while $wrap holds the lock, wrap-ticket skips with skipped_reason: "wrap-lock-contention" and proceeds without learnings capture (Phase 11b is non-blocking).
Deferred background enrichment (daemon)
Manual $wrap is synchronous: there is no in-session auto-enrichment protocol. Background completion of forgotten wraps is performed by the deferred-wrap daemon (Claude-only, opt-in via the deferred_wrap_daemon toggle in $AE_PROJECT_DIR/.agentic/config.json), which headlessly resumes each cleanly-ended session and runs the non-interactive manual workflow 'ds-wrap-deferred' via $AE_REPO_DIR/bin/agentic-codex-dispatch command ds-wrap-deferred command. The daemon is the sole consumer of the per-session marker staged in Step 0a; see $AE_REPO_DIR/content/references/conductor-operating-rules.md for the daemon drain protocol.
Your job (main agent)
Pre-flight scaffold-accuracy check (runs BEFORE Step 0). manual workflow 'ds-init-project' via $AE_REPO_DIR/bin/agentic-codex-dispatch command ds-init-project is the canonical scaffolding spec; $wrap uses it as the reference for "what this project should look like." Check for drift and auto-migrate the critical items inline:
Sentinel short-circuit (evaluate before item 1). On a steady-state project this pre-flight repeats the same ~15 filesystem probes on every $wrap invocation for no reason. Cache the result:
- Watched-paths signature. The signature covers exactly the paths this pre-flight inspects: the CLAUDE.md/AGENTS.md set (root + every track directory), the
$AE_PROJECT_DIR/.claude/context.md / $AE_PROJECT_DIR/.claude/memory.md / $AE_PROJECT_DIR/.claude/memory/ session-state paths and their $AE_PROJECT_DIR/.agentic/ counterparts, the legacy config paths $AE_PROJECT_DIR/.claude/{qa,deploy,findings,tracking,learnings}.md and their $AE_PROJECT_DIR/.agentic/ counterparts, the stub targets ($AE_PROJECT_DIR/.agentic/tracking.md, $AE_PROJECT_DIR/.agentic/deploy.md, $AE_PROJECT_DIR/.agentic/learnings.md), the docs dirs (docs/overview/, docs/technical/, docs/planning/, docs/research/), $AE_PROJECT_DIR/.claude/settings.json, $AE_PROJECT_DIR/.claude/settings.local.json, and $AE_PROJECT_DIR/.gitignore. For each watched path, record path:exists:mtime (mtime as epoch seconds, or the literal string absent when the path does not exist); sort the resulting lines lexicographically by path, join with newlines, and take the sha256 of the result — this is the signature.
- Sentinel path:
$AE_PROJECT_DIR/.agentic/wrap/.scaffold-verified (atomic tmp + rename; mkdir -p $AE_PROJECT_DIR/.agentic/wrap immediately before the write — the lock-acquisition step's own mkdir -p has not run yet at this point in the flow, so a first-ever clean pass needs its own directory guarantee). This is machine-local runtime state, not committed — it is covered by the $AE_PROJECT_DIR/.agentic/* gitignore umbrella.
- At the start of this pre-flight: if the sentinel exists and its stored signature is byte-equal to the freshly-recomputed signature above, SKIP items 1-3 and 5-6 below (the filesystem/migration probes) and proceed straight to Step 0a. Otherwise (sentinel absent, unreadable, or signature mismatch) run items 1-3 and 5-6 in full as described below.
- Scope exclusion: item 4's release-signal-gated
$AE_PROJECT_DIR/.agentic/deploy.md stub check is never covered by the sentinel. That check is gated on release signals detected from session content, not on any filesystem path in the watched-paths signature above — a first-release session that touches only non-watched paths could byte-match the signature and, if this check were also skipped, never get the stub created. So run item 4's release-signal check (and only that check) on EVERY $wrap invocation regardless of whether the sentinel short-circuit fires for items 1-3/5-6. This is cheap — it is judged from session context already being surveyed, not a filesystem probe — so it does not reintroduce the cost this sentinel exists to avoid.
- At the end of this pre-flight (only reached when items 1-3/5-6 actually ran in full, i.e. the sentinel did not short-circuit them): write the sentinel with the freshly-recomputed signature ONLY when the pass reached a fully clean steady state — no CLAUDE.md → AGENTS.md migration performed (item 1), no
$AE_PROJECT_DIR/.claude/ → $AE_PROJECT_DIR/.agentic/ migration performed (item 2), no legacy config migration performed (item 3), no stub created by item 4's non-release-signal checks, no silent auto-fix applied (item 5), AND no "drift that cannot be auto-fixed" recorded (item 6). If the pass did ANY work or recorded ANY unfixable drift, do NOT write the sentinel — the next $wrap run must re-run the full pre-flight and re-log the drift so it keeps surfacing under "Watch Out For" until resolved. This conservative rule guarantees the short-circuit only ever fires on a genuinely clean, unchanged scaffold and never suppresses a real drift bullet.
-
CLAUDE.md → AGENTS.md migration (per-file, recursive through tracks). For each CLAUDE.md in the project (root + every track directory) where a sibling AGENTS.md does not already exist:
cp <dir>/CLAUDE.md <dir>/AGENTS.md to preserve content.
- Root directory: overwrite
<dir>/CLAUDE.md with two import lines, @AGENTS.md then @MEMORY.md, so Claude Code transparently loads both the migrated file and the durable-facts store. Apply the dangling-import guard: if root MEMORY.md does not exist, seed it with the manual workflow 'ds-init-project' via $AE_REPO_DIR/bin/agentic-codex-dispatch command ds-init-project Step 8 stub before writing the import (consistent with this preflight's existing silent-stub-creation pattern in item 4); never overwrite an existing MEMORY.md.
- Track directories: overwrite
<dir>/CLAUDE.md with the single line @AGENTS.md only - tracks do not have their own MEMORY.md, so no @MEMORY.md import is added.
- Skip directories where
AGENTS.md already exists (leave CLAUDE.md untouched).
-
$AE_PROJECT_DIR/.claude/ → $AE_PROJECT_DIR/.agentic/ session state migration. If $AE_PROJECT_DIR/.claude/context.md exists and $AE_PROJECT_DIR/.agentic/context.md does not:
mkdir -p $AE_PROJECT_DIR/.agentic
mv $AE_PROJECT_DIR/.claude/context.md $AE_PROJECT_DIR/.agentic/context.md
- Same for
$AE_PROJECT_DIR/.claude/memory.md and $AE_PROJECT_DIR/.claude/memory/ (the auto-memory dir).
- Redo symlinks in
$AE_SHARED_CONFIG_DIR/projects/[hash]/ to point at the new $AE_PROJECT_DIR/.agentic/ paths.
-
Legacy config migration ($AE_PROJECT_DIR/.claude/<name>.md → $AE_PROJECT_DIR/.agentic/<name>.md) — for each of qa.md, deploy.md, findings.md, tracking.md, learnings.md:
- Both paths exist on disk: do NOT migrate. Log a drift warning in the wrap run output (e.g. "Drift (both $AE_PROJECT_DIR/.claude/findings.md and $AE_PROJECT_DIR/.agentic/findings.md exist - skipping auto-migration; resolve manually via manual workflow 'ds-init-project' via
$AE_REPO_DIR/bin/agentic-codex-dispatch command ds-init-project)"), and add a bullet under the _wrap.md "Watch Out For" section naming the conflicting files. Skip to the next name.
- Only legacy
$AE_PROJECT_DIR/.claude/<name>.md exists: first, run git status --porcelain to check working-tree cleanliness. If there are staged or unstaged changes, do NOT migrate - log a drift note ("Skipped migration of legacy $AE_PROJECT_DIR/.claude/.md: working tree dirty. Commit or stash, then re-run $wrap or manual workflow 'ds-init-project' via $AE_REPO_DIR/bin/agentic-codex-dispatch command ds-init-project.") and add a Watch Out For bullet. If the working tree is clean, migrate: git mv $AE_PROJECT_DIR/.claude/<name>.md $AE_PROJECT_DIR/.agentic/<name>.md. Log the move to the wrap run output only.
- Only
$AE_PROJECT_DIR/.agentic/<name>.md exists: no action.
- Neither exists: no action at this step - the missing-stub creation below handles creation.
-
Missing-stub creation. If any of $AE_PROJECT_DIR/.agentic/tracking.md, $AE_PROJECT_DIR/.agentic/deploy.md (only when release signals detected), or $AE_PROJECT_DIR/.agentic/learnings.md is missing (checked via resolver: $AE_PROJECT_DIR/.agentic/<name>.md preferred, legacy $AE_PROJECT_DIR/.claude/<name>.md fallback), create a stub at $AE_PROJECT_DIR/.agentic/<name>.md per the template in manual workflow 'ds-init-project' via $AE_REPO_DIR/bin/agentic-codex-dispatch command ds-init-project Steps 6a-6d. For $AE_PROJECT_DIR/.agentic/learnings.md, use the template from manual workflow 'ds-init-project' via $AE_REPO_DIR/bin/agentic-codex-dispatch command ds-init-project Step 8 (unconditional — always create). The $AE_PROJECT_DIR/.agentic/deploy.md release-signal check runs every $wrap, even when the sentinel short-circuit skips the rest of this item and items 1-3/5-6 — see the "Scope exclusion" note above.
-
Silent auto-fix for remaining drift. $wrap is silent and hands-off. For any drift $wrap can fix without user input, fix it inline:
- Create
docs/overview/, docs/technical/, docs/planning/, docs/research/ (with .gitkeep) if missing.
- Create
$AE_PROJECT_DIR/.claude/settings.json ({}) if missing.
- Create
$AE_PROJECT_DIR/.claude/settings.local.json with autoMemoryDirectory set to $AE_PROJECT_DIR/.agentic/memory if missing or if the key is not yet present (merge rule: never overwrite an existing value). Scope note: autoMemoryDirectory: $AE_PROJECT_DIR/.agentic/memory is intentional - it routes Claude Code's native auto-memory writes to a local gitignored scratch area. The canonical conductor-managed, human-reviewed durable-facts store remains <cwd>/MEMORY.md (see the Memory path (memory.md) note below).
- Create
$AE_PROJECT_DIR/.gitignore entries for $AE_PROJECT_DIR/.claude/settings.local.json and the $AE_PROJECT_DIR/.agentic/ runtime-artifact block (per manual workflow 'ds-init-project' via $AE_REPO_DIR/bin/agentic-codex-dispatch command ds-init-project Step 9) if missing.
- Pre-AGENTS.md layout detection (DO NOT auto-split inline). If root
AGENTS.md is absent AND root CLAUDE.md exists with more than the @AGENTS.md and/or @MEMORY.md import pointer lines, do NOT attempt the Worker+Skeptic three-way split inline — that migration requires user confirmation of the proposed split, and $wrap's silent contract cannot provide one. Instead, add a "Watch Out For" entry in _wrap.md: Pre-AGENTS.md layout detected (CLAUDE.md has real content, no root AGENTS.md). Run manual workflow 'ds-init-project' via $AE_REPO_DIR/bin/agentic-codex-dispatch command ds-init-project to run the Worker+Skeptic split and migrate.
-
Drift that cannot be auto-fixed. If any drift requires user input (e.g. Linear workspace slug, Jira base URL, confirmation of release commands, selection among multiple detected web UIs), do NOT prompt during $wrap. Instead, record a bullet under "Watch Out For" in the _wrap.md output noting which scaffolding items are still incomplete. The user can address these later by running manual workflow 'ds-init-project' via $AE_REPO_DIR/bin/agentic-codex-dispatch command ds-init-project interactively. Specific drift kinds that always require user input and must be listed here:
- CLAUDE.md split — the pre-AGENTS.md migration requires the user to review and accept the three-way split (AGENTS.md / residual CLAUDE.md / MEMORY.md). $wrap cannot perform this silently; it points at manual workflow 'ds-init-project' via
$AE_REPO_DIR/bin/agentic-codex-dispatch command ds-init-project.
- Linear workspace slug or QA assignee UUID not yet set when
## Linear is present.
- Jira
JIRA_BASE_URL, TICKET_PREFIX, or transition name not yet set when ## Tracker is present.
- Release command / rollback procedure confirmation when
$AE_PROJECT_DIR/.agentic/deploy.md has TODO placeholders.
- Choice among multiple detected web UIs for
$AE_PROJECT_DIR/.agentic/qa.md in a multi-track project.
All steps are silent on success. Log each migration action taken (e.g. "Migrated admin/CLAUDE.md to admin/AGENTS.md + pointer") to the wrap run output only, not as user prompts. After completing items 1-6, apply the sentinel write rule from the "Sentinel short-circuit" note above (write $AE_PROJECT_DIR/.agentic/wrap/.scaffold-verified with the fresh signature only on a fully clean pass; otherwise leave it unwritten). After preflight completes (whether via the sentinel short-circuit or the full run), proceed to Step 0a.
Pre-flight check — no active Workers. Before doing anything else, check whether any background Workers or subagents are currently running. If any are, stop and tell the user: "Cannot run $wrap while background tasks are active. Please wait for them to finish (or stop them) first." Do not proceed until confirmed.
Pre-flight lock acquisition. $wrap writes several shared project-local files (_wrap.md, memory.md, AGENTS.md, compression-state.json, rolling snapshots). It does not write $AE_PROJECT_DIR/.agentic/context.md. The current Codex Stop hook writes only ~/.codex/projects/[hash]/context.md; automatic project-local rollup integration is deferred to context-writer-migration. Concurrent $wrap runs in the same project would clobber the files $wrap does own. Acquire a project-local lock before proceeding:
-
Ensure $AE_PROJECT_DIR/.agentic/wrap/ exists (mkdir -p $AE_PROJECT_DIR/.agentic/wrap).
-
Attempt acquisition in the FOREGROUND via the acquire helper, single attempt, no wait: agentic-wrap-acquire-lock "$cwd" --role=agent --no-wait --session-id="$AE_SESSION_ID". This replaces the old hand-rolled mkdir $AE_PROJECT_DIR/.agentic/wrap/lock plus a separate owner-file write: the helper publishes the lock directory and BOTH owner artifacts (the legacy owner 2-line body and the schema-validated owner.json descriptor) atomically and fail-closed, inside one process - so the window in which the lock directory exists without an owner is eliminated, not merely narrowed - and the descriptor comes from one code path shared with the daemon rather than shell string-formatting. Run it synchronously (foreground): it is a single mkdir plus two rename calls with no poll loop and no sleep, so this is not the long-foreground-poll failure mode, and it must be synchronous because the conductor needs the result before deciding what to do next. Branch on exit code:
- 0 - lock acquired. Proceed.
- 5 - busy (lock already held by someone else). Go to step 3 below.
- 1 - fatal (lib failed to load, or an invalid
--role). Surface the WARNING line verbatim, then abort.
- any other exit code (including 127, the shell's command-not-found code) - surface it verbatim and abort. Never fall through to "proceed" on an unrecognised code.
If the command is not found on PATH at all, abort with a message naming the install step - do NOT fall back to a manual mkdir, which would produce a lock directory with no owner.json and forfeit the daemon-side live-lock protection this whole design depends on. Word the message harness-neutrally, since this file is shared across adapters: "agentic-wrap-acquire-lock not found on PATH - re-run your harness's DinoStack install script (<repo>/.claude/install.sh for Claude Code, the equivalent script under your adapter directory otherwise) to wire bin/ onto PATH."
-
On busy: do not read the owner file, do not classify it, and do not abort. There is no separate unreadable-owner or stale-owner branch to evaluate here - both are progress conditions the helper itself handles, not give-up conditions for the conductor to decide. Start the helper with Codex exec_command; if exec_command yields a session ID, keep the conductor responsive and poll that session with write_stdin until completion; if it returns a terminal result, branch on that result immediately, letting it wait: agentic-wrap-acquire-lock "$cwd" --role=agent --session-id="$AE_SESSION_ID". It polls in-process (jittered ~5s interval) and exits once the lock is acquired or after 20 minutes; non-blocking, so the conductor stays available until notified of completion. On the completion notification, branch on exactly three outcomes:
- 0 - acquired; the lock is now held by $wrap. Proceed normally.
- 2 - timeout. Report the helper's final
timeout ... line verbatim (it already carries role, pid, start time, and an rm -rf recovery command). Then abort.
- 1 - unexpected failure. Surface the WARNING line from stdout, then abort.
- any other exit code - surface it verbatim, then abort.
Exit codes 3 (unreadable owner) and 4 (stale-needs-manual) no longer exist, deliberately: an unreadable owner and an old-looking timestamp are progress conditions, not give-up conditions. A lock whose owner looks old may be held by a $wrap that is legitimately still working - the timestamp alone cannot distinguish that from an abandoned lock, so the waiter keeps waiting and only tells the operator how to unwedge it manually (the HINT's rm -rf command) if the operator judges it stuck.
-
Liveness is the authoritative signal, not the timestamp. The helper determines whether the lock's owner is a live process wherever the owner record carries a genuine, checkable PID; the timestamp is used only where the owner record is PID-blind (e.g. a legacy 2-line body from an interactive $wrap, whose recorded PID is a shell that has already exited by the time the file lands), and even there it is advisory, not decisive. The conductor does not run its own ps -p check - it relies entirely on the helper's exit code.
-
--session-id is the liveness signal for an interactive hold, and it is why an abandoned lock is no longer immortal. A --role=agent descriptor carries pid: null by construction, so there is no process to liveness-check; without a session id the helper's verdict for such a lock is live FOREVER. Pass --session-id="$AE_SESSION_ID" at BOTH acquisition sites above, keeping the flag on the SAME LINE as the invocation. Use AE_SESSION_ID only. It is the Codex session binding derived by passing hook JSON to $AE_REPO_DIR/bin/agentic-codex-session-id. The flag is soft: an unset variable expands to an empty value, the helper publishes session_id: null, acquisition still succeeds, and the lock falls back to a pid-blind age rule - degraded, never stealing a live hold. If session-ID derivation yields no identifier, the documented empty-value fallback applies.
Self-heal on acquire. Before each attempt the helper clears a lock it can PROVE abandoned - a role:agent hold whose session stopped heartbeating past 30 minutes, or any PID-blind hold older than 4 hours - and prints cleared-abandoned-lock <path> when it does. This is the only case in which the helper removes a lock, and it is announced, never silent. An old-but-live hold is still waited on exactly as before. The default config (deferred_wrap_daemon: false) never launches the daemon that owns the other clear path, so without this the operator was the only recovery mechanism.
The 30-minute mark is advisory only: it changes the message the waiter prints (adding the rm -rf HINT to the "waiting"/"timeout" line past that age), never the decision to keep waiting.
Lock release is mandatory on every exit path. The lock dir MUST be removed (run agentic-wrap-release-lock — the PATH-wired helper that releases $AE_PROJECT_DIR/.agentic/wrap/lock) before $wrap returns control to the user, on ALL of:
- successful completion at Step 6;
- escalation to the user at Step 3 (format re-invocation limit or contested finding);
- compression failure or escalation at Part E;
- any user-abort path (e.g. drift requiring input, Skeptic scope bail).
If $wrap aborts before the lock is acquired (e.g. at the active-Workers check above, or because a live or stale lock was detected and the command aborted without acquiring), no lock was acquired and no release is needed.
Pre-flight path check: Confirm $AE_PROJECT_DIR/.agentic/ exists or can be created. The $wrap skill now writes project-local under $AE_PROJECT_DIR/.agentic/ instead of the legacy $AE_SHARED_CONFIG_DIR/projects/[hash]/ hashed directories. No disambiguation needed - one canonical location per project.
Deferred-enrichment data model
This section is the source of truth for the Claude-only deferred-wrap marker schemas. The Claude Stop hook $AE_REPO_DIR/hooks/stop-context.js, the OpenCode plugin, and the deferred-wrap daemon consume those schemas. The current Codex Stop hook writes only ~/.codex/projects/[hash]/context.md and does not stage these project-local markers; migration is deferred to context-writer-migration.
1. $AE_PROJECT_DIR/.agentic/wrap/pending-<session_id>.json (the per-session enrichment marker). One marker per session, keyed by session_id in the filename so concurrent sessions never collide. Staged when a session has substantive un-wrapped work, so the daemon (or the next session in that project) completes enrichment idempotently. Schema:
{
"schema_version": 3,
"session_id": "<uuid of the session that staged the marker>",
"staged_at": "<ISO8601 UTC, immutable, FIFO key>",
"status": "pending | ready | in_progress | done | gave_up",
"claimed_by": "<pid/uuid of the claimant currently running enrichment, or null>",
"claimed_kind": "session | daemon | null",
"claimed_at": "<ISO8601 UTC of last claim, or null>",
"attempts": "<int, 0..3>",
"project_root": "<absolute cwd>",
"last_error": "<short string or null>"
}
status lifecycle: pending (staged on a Stop turn, not yet finalized) -> ready (finalized by a genuine terminal SessionEnd; the SOLE pending -> ready transition - there is NO stale-sweep) -> in_progress (claimed, enrichment running) -> done (completed; marker RETAINED as a wrapped_at-stamped tombstone that suppresses same-session re-staging and is reaped by the janitor after ttl) | gave_up (attempts >= 3; marker retained with a manual-$wrap notice). Only a ready marker is daemon-claimable; an open/idle session leaves its marker pending and is never auto-resumed.
- Dropped vs schema_version 1/2:
branch and head_sha (the daemon enriches in the main project dir, so git-state reflects the live tree; enrichment is conversation-driven, not snapshot-driven).
claimed_kind records who holds the claim: session (a manual $wrap Step 0a) or daemon (the background wrap daemon). Daemon-startup reclaim acts ONLY on claimed_kind: "daemon" markers (MAJOR-C).
staged_at is immutable and is the FIFO ordering key the daemon uses to drain ready markers oldest-first. claimed_at plus a staleness window are a wastefulness reducer, not a correctness invariant - they make a double-claim rare, never impossible; idempotency is what makes a double-run safe.
attempts increments at claim time, before enrichment begins, so a crash mid-enrichment still counts toward the give-up budget.
2. $AE_PROJECT_DIR/.agentic/wrap/last-wrap (the wrap-recency sentinel). This project-local sentinel is written only after a successful $wrap Part A write. Claude and OpenCode marker consumers use it for staging suppression. The current Codex Stop hook writes only ~/.codex/projects/[hash]/context.md and does not consume this sentinel; migration is deferred to context-writer-migration.
3. $AE_PROJECT_DIR/.agentic/wrap/deferred-activity.jsonl (the spillover log). No longer produced. Spillover existed only because a held wrap/lock made a per-turn writer SKIP its context.md write; per-turn writers now write session-private shards and are never skipped, so nothing is deferred. The drain below is RETAINED so records written before that change are not orphaned, and it folds them into _wrap.md's curated ## Recent Focus region. Historical record schema, for readers of an existing file:
{"schema_version": 1, "ts": "<ISO8601 UTC>", "session_id": "<uuid>", "recent_focus": ["<msg>"], "paths_referenced": ["<path>"], "uncommitted": ["<status code + path>"], "tools_used": ["<tool>"]}
Pinned header prefix (NORMATIVE). Exactly one byte-exact prefix is the contract between writer and matcher:
# Session Context\n*Written by $wrap
This is what $AE_REPO_DIR/hooks/stop-context.js and .opencode/plugins/session-context.ts test via startsWith, and what every $wrap Output-1 / merge write must emit as its first two lines. The on-disk header date is a UTC calendar date (date -u +%Y-%m-%d); the header STRING does NOT contain the "UTC" literal - it stays *Written by $wrap on YYYY-MM-DD. ... exactly as the Output-1 template (Step 1) reads. The matcher only tests the pinned prefix (which stops before the date), so the date format and the absence of the "UTC" literal are both compatible. The Part A merge rule (the "(merged context)" header rewrite) appends after the date and is outside the pinned prefix - it stays. The rolling-session-label merge (Part A) is preserved unchanged.
Step 0a - Stage the deferred-wrap safety-net (runs BEFORE Step 0).
$wrap is synchronous: it runs the body inline and returns control only after Step 6 completes. Step 0a stages a per-session marker that is consumed by the deferred-wrap DAEMON, not by any in-session pipeline - so that if THIS session is later force-killed or ends without finishing a manual $wrap, the daemon can complete enrichment headlessly. Staging is GATED: it runs ONLY on the Claude host with the daemon enabled and not inside a daemon run.
Claude-host + opt-in + non-daemon guard (MAJOR-1). Wrap both the toggle read and the marker staging in this guard. Off-Claude (no $AE_PROJECT_DIR/.agentic/wrap/claude-host sentinel - the sentinel is written only by the Claude SessionStart hook and $AE_PROJECT_DIR/.claude/install.sh), toggle off, or inside a daemon run (AGENTIC_WRAP_DAEMON=1) -> stage NOTHING, and $wrap runs byte-identical to the classic synchronous wrap (no marker, no daemon involvement, exactly today's pre-feature behavior):
# Claude-host + opt-in + non-daemon guard. Off-Claude (no $AE_PROJECT_DIR/.agentic/wrap/claude-host sentinel),
# toggle off, or inside a daemon run -> stage nothing, $wrap runs byte-identical to today.
if [ -f "$cwd/.agentic/wrap/claude-host" ] && [ "$AGENTIC_WRAP_DAEMON" != "1" ] && <deferred_wrap_daemon toggle is true in $AE_PROJECT_DIR/.agentic/config.json>; then
<stage the per-session pending-<session_id>.json marker (per the schema below)>
fi
When the guard passes, stage $AE_PROJECT_DIR/.agentic/wrap/pending-<session_id>.json (atomic tmp + rename) per the per-session schema_version 3 marker in the Deferred-enrichment data model section above, with:
schema_version: 3, session_id: <this session_id>, staged_at: <now, ISO8601 UTC>, status: "pending", claimed_by: null, claimed_kind: null, claimed_at: null, attempts: 0, project_root: <absolute cwd>, last_error: null.
The marker is keyed by session_id in its filename, so per-session markers never collide. If this session's own marker already exists with status pending, ready, or in_progress, do NOT overwrite it (MAJOR-3 ready-non-stageable). A marker with status: gave_up, or done WITHOUT a wrapped_at stamp, is not a completion tombstone; staging may proceed. A done marker WITH wrapped_at is a completion tombstone for THIS session and suppresses re-staging (the last-wrap-rollover guard). With the guard false (off-Claude, toggle-off, or under the daemon guard), $wrap stages no marker at all and behaves exactly as the classic synchronous wrap.
Step 0a does NOT write $AE_PROJECT_DIR/.agentic/wrap/last-wrap. last-wrap is written only after a successful Part A _wrap.md write (see Part A). Writing it here would suppress this very session's own recovery marker on the next Stop-hook fire.
Tell the user: "Writing enriched session context — I'll let you know when it's done."
Step 0-pre - Fast zero-substance short-circuit (runs AFTER Step 0a, BEFORE Step 0).
Step 0 (below) unconditionally reads the root + track AGENTS.md files in full, reads $AE_PROJECT_DIR/.agentic/compression-state.json and $AE_PROJECT_DIR/.agentic/learnings.md in full, and runs a gh pr list network call - all before Step 0.5 ever gets to decide the session was zero-substance. Step 0-pre detects the unambiguous zero-substance case earlier so those expensive reads and the network call can be skipped entirely. This is purely a hoist of a determination Step 0.5 already makes - it does NOT change the zero-substance criteria, only detects the unambiguous case earlier.
- Run the work-loss safety net now, unconditionally:
git status --porcelain and git stash list. This check NEVER short-circuits away, on any path - it is the safety net against lost uncommitted work (same rationale Step 0 states below: "critical for preventing work loss"). Capture the raw output of both commands. If Step 0-pre falls through to the full Step 0 (below), Step 0 REUSES this captured output - do not re-run either command there.
- From the conversation/tool-call history, determine whether the session performed ANY file-mutating tool calls (Edit, Write, NotebookEdit) or git commits.
- Check whether any specialist agent (
perf-analyst, release-orchestrator, dependency-auditor) ran this session with session-scoped issues to capture - the same check Step 0.5's zero-substance criteria make, evaluated here from context already in hand (no new mechanism).
- Short-circuit directly to the Step 0.5 "Zero-substance procedure" - skipping the remainder of Step 0 (the AGENTS.md/compression-state.json/learnings.md reads) and the
gh pr list call entirely - ONLY when ALL of the following hold unambiguously:
- No file-mutating tool calls and no git commits this session (item 2).
- The
git status --porcelain output from item 1 shows no tracked changes (no M/A/D entries; untracked-only ?? output does not disqualify).
- The
git stash list output from item 1 shows no new stashes.
- No specialist agent ran with session-scoped issues to capture (item 3).
- No stable fact, architectural decision, or convention was established this session that Output 2 (memory entries) would capture - mirroring the zero-substance path's own Output-2 criterion under Step 0.5, evaluated here from conversation context already in hand rather than from Step 0's stable-facts survey. A discussion-only session that reached an architectural decision with zero file edits does NOT qualify for the short-circuit even though it trips every other gate above - this bullet exists specifically to catch that case.
- No meaningful next steps to record - the conductor judges this the same way Step 0.5 already does: if the only meaningful session output is "answered a question," the case qualifies.
- On ANY uncertainty, fall through to the full Step 0 below rather than short-circuiting - this includes any doubt about whether a stable fact or decision surfaced that Output 2 would capture. This is the same escape-hatch spirit as the "Escape hatch for zero-substance path" note under Step 0.5: when in doubt, do NOT take the fast path.
If the short-circuit fires: go straight to the Zero-substance procedure under Step 0.5 below (which still runs Step 5 worktree cleanup and the Step 6 confirmation as normal); do not run Step 0 or the rest of Step 0.5's routing checks.
If the short-circuit does not fire: proceed to Step 0 below, which reuses the git status --porcelain / git stash list output captured in item 1 above.
Step 0 — Compile session data (inline, no subagent needed).
Survey the current conversation and note down:
- The main task and its current state (done? blocked? in progress?)
- All files touched or created this session (from tool call history — be specific: full paths)
- Any errors, gotchas, or near-misses that surfaced
- Specific remaining next steps (file paths, branch names, commands, open PRs — concrete enough to act on without re-reading the chat)
- Tools used during the session
- Stable project facts worth preserving: setup commands that don't change, persistent project-wide gotchas or quirks, architectural decisions made, recurring patterns or conventions established. Distinguish these from temporary state (current task, files touched this session) - stable facts will go into memory.md, temporary state into
_wrap.md only.
- Identify the project root (absolute cwd).
- Note which tracks (subdirectories) had files touched this session — these are candidates for AGENTS.md updates, and their AGENTS.md paths feed the batch below.
Batch the independent shell/existence/network checks into one Bash invocation. Rather than issuing separate round-trips, gather everything below in a SINGLE heredoc script:
- Existence probes (no content reads yet) for:
$AE_PROJECT_DIR/.claude/compression-state.json, $AE_PROJECT_DIR/.agentic/compression-state.json, $AE_PROJECT_DIR/.agentic/learnings.md, the root AGENTS.md, and each [track]/AGENTS.md for tracks that had files touched this session.
- Open-PR overlap query via
gh pr list (exact command below) — on gh unavailable or an error, log "open-PR overlap check skipped (gh unavailable)" and treat the result as an empty set.
REPO_CWD="<absolute cwd>"
CURRENT_BRANCH=$(git -C "$REPO_CWD" branch --show-current)
PROBE_PATHS=(
"$REPO_CWD/.claude/compression-state.json"
"$REPO_CWD/.agentic/compression-state.json"
"$REPO_CWD/.agentic/learnings.md"
"$REPO_CWD/AGENTS.md"
)
for p in "${PROBE_PATHS[@]}"; do
if [ -e "$p" ]; then echo "EXISTS $p"; else echo "MISSING $p"; fi
done
if command -v gh >/dev/null 2>&1; then
gh pr list --state open --base "$CURRENT_BRANCH" --json number,headRefName,files \
--jq '.[] | {n: .number, branch: .headRefName, files: [.files[].path]}' \
2>/dev/null || echo "GH_PR_LIST_FAILED"
else
echo "GH_UNAVAILABLE"
fi
A #-comment left unmodified inside the PROBE_PATHS=( ... ) array literal above is harmless (bash array literals tolerate comment lines between elements) - unlike a #-comment inside a backslash-continued for p in ... \ list, which would orphan the following ; do into a syntax error. Populate PROBE_PATHS with one array element per touched-track AGENTS.md path before running.
Fallback on batch failure. If the batch script itself errors (non-zero exit from the script as a whole, or an environment where heredoc execution is unavailable), fall back to issuing the existence probes and the gh pr list query as individual calls - the pre-optimization behavior. A batch failure must never take down Step 0's data gathering; it only costs the round-trips this batching was meant to save.
Do NOT re-run git status --porcelain or git stash list here — this batch excludes them by design; reuse the output already captured in Step 0-pre (Step 0 only runs at all when Step 0-pre fell through to the full path).
Using the batch results:
-
Check for and read: the root AGENTS.md (if the existence probe found it), and any [track]/AGENTS.md files in subdirectories that had files touched this session (if their existence probes found them). Record their full current content — this will be passed to the Worker as a dedicated field so it can avoid duplicating what is already captured.
-
Migrate $AE_PROJECT_DIR/.claude/compression-state.json → $AE_PROJECT_DIR/.agentic/compression-state.json if the batch probe found $AE_PROJECT_DIR/.claude/compression-state.json AND did NOT find $AE_PROJECT_DIR/.agentic/compression-state.json: mv $AE_PROJECT_DIR/.claude/compression-state.json $AE_PROJECT_DIR/.agentic/compression-state.json. Log the move to the wrap run output only.
-
Read $AE_PROJECT_DIR/.agentic/compression-state.json if the batch probe (or the migration above) confirms it exists. Record its full current content — this will be passed to Part E later to determine whether compression is needed for each target.
-
Read $AE_PROJECT_DIR/.agentic/learnings.md if the batch probe found it exists in the project. Record its full current content — this will be passed to the draft Worker in Step 1 so it does not re-derive facts already captured by learnings-agent.
-
Check for missing AGENTS.md files: For each directory that had files touched this session, use the batch's existence probe above rather than a separate check. Skip generated/artifact directories (node_modules, .next, dist, out, build, .expo, .turbo, coverage, .cache, __pycache__, .git). For each non-generated directory the probe found missing an AGENTS.md, note it as a new AGENTS.md candidate and include it explicitly in the raw data passed to the draft Worker. The Worker will propose content for these new files; the conductor will create them automatically without asking the user.
-
Reuse git status --porcelain and git stash list from Step 0-pre (do not re-run) to capture uncommitted changes and stashes. If there are uncommitted tracked files (M, A, D - not ??), list them explicitly. This is critical for preventing work loss across sessions - if the user asked to commit and files were missed, this is the safety net.
-
Note specialist agent outputs — if perf-analyst, release-orchestrator, or dependency-auditor ran this session, capture their key findings: stable facts (confirmed hotspots with measurements, release version and tag, known CVEs) belong in memory.md entries; session-scoped issues (a partial deploy, a perf regression under investigation, an unresolved dependency conflict) belong in Watch Out For.
-
Note Trivial commits — if any commits this session were classified Trivial, include them in "files touched" and "next steps" as normal. Trivial commits produce no Skeptic artifact and no adversarial brief - do not flag their absence as a gap. Only note the commit SHA and what changed.
-
Note task-state summary - if $AE_PROJECT_DIR/.agentic/tasks.jsonl exists, apply the task-state fold ($AE_REPO_DIR/content/references/task-state-file.md) and filter the folded records whose session_id is the current session - not a raw-line filter, which double-counts a task that was taken over: a dispossessed task's own records still carry this session's session_id even though the fold has moved ownership elsewhere. Include in the session wrap summary: final task status counts (N done, N blocked, N failed, N abandoned) over that filtered folded set. Do NOT copy task entries into MEMORY.md - they are already durable in the file.
-
Note loop-state summary — loop state is keyed per ticket, so enumerate every $AE_PROJECT_DIR/.agentic/loop-state-*.json plus the legacy $AE_PROJECT_DIR/.agentic/loop-state.json, and consider only those whose session_id == $AE_SESSION_ID. The session_id filter is required, not optional: it needed no gate in a one-file world, but a keyed checkout routinely holds another session's healthy in-flight loop, and reporting that as this session's incomplete loop is a false alarm on every wrap. For each surviving candidate: if status=active, note in the wrap summary that an incomplete loop was active when $wrap ran (the conductor should investigate before ending the session); if status=interrupted, note a pending resume is available (the next $implement-ticket invocation on that ticket will offer to resume). Name the candidate by its loop_key (falling back to ticket_id, then to the filename) so the note identifies which ticket. The wrap command does NOT delete or modify any loop-state file, keyed or legacy - that is the user's choice (resume vs fresh-start). Do NOT copy loop state details into MEMORY.md or _wrap.md beyond the one-line status note.
-
Enumerate open PRs targeting the conductor's current branch — from the batch's gh pr list output above (do not re-run the query). $wrap writes AGENTS.md and memory.md additions onto the conductor's current branch (typically main). If those additions cite file paths or feature keys that live on branches with open PRs not yet merged, the doc additions will land on the target branch describing files/keys that do not yet exist there.
Record the resulting {pr_number, head_branch, modified_files[]} set as the open-PR overlap set. If the batch logged GH_UNAVAILABLE or GH_PR_LIST_FAILED, log "open-PR overlap check skipped (gh unavailable)" to the wrap run output and pass an empty set forward — the deferral logic becomes a no-op rather than blocking the run. The set is supplied to the draft Worker as a dedicated field (see Step 1) so it can flag deferral candidates; the conductor enforces deferral at write time in Step 4.
This raw data is what the draft Worker will format. The Worker is a fresh agent with no session memory, so if you don't supply the details here, they won't appear in the output.
Step 0.5 - Route to light, zero-substance, or standard path.
Inspect what Outputs 2 and 3 would contain based on the raw data already compiled in Step 0. Do not spawn anything yet.
Reached two ways. Normally this step runs after full Step 0 compiles its raw data. It can also be reached directly from Step 0-pre's fast short-circuit, which evaluates the same criteria below from conversation context already in hand, before Step 0's expensive reads ever run. The criteria themselves are defined here and only here - Step 0-pre does not redefine them or replace them with a weaker mechanical proxy; it falls through to full Step 0 on any doubt rather than guessing. Do not read Step 0-pre's earlier evaluation as license to skip re-checking these criteria in spirit - if anything about the session's substance is unclear, the standard or light path is the safe default.
Zero-substance path - triggers when ALL of the following hold:
- Output 2 (memory entries) would be "None"
- Output 3 (AGENTS.md updates) would be "None" for every file AND no new AGENTS.md candidates exist
- No specialist agent (
perf-analyst, release-orchestrator, dependency-auditor) ran with session-scoped issues to capture
- The session had effectively no file activity worth preserving in
_wrap.md: no uncommitted tracked changes, no new stashes, no files touched beyond reads, no meaningful next steps to record. The conductor should judge - if the only meaningful session output is "answered a question", it is zero-substance.
Zero-substance procedure:
- Do NOT write
_wrap.md. The current Codex Stop hook already wrote the session's raw continuity to ~/.codex/projects/[hash]/context.md. A zero-substance $wrap would only create an empty project-local handoff; automatic project-local rollup integration remains deferred to context-writer-migration.
- Skip Steps 1-3 entirely (no Worker, no Skeptic)
- Skip Step 4 Parts A, B, C entirely
- Skip Part D and Part D.5 (no session activity to extract skill-candidate or feedback signals from)
- Skip Part E (nothing changed, nothing to compress)
- Skip Part F (no session activity means no ticket-referencing commits to detect)
- Skip Part G (no session activity means no knowledge-file changes to commit)
- Still run Step 5 (worktree cleanup) - that is always useful
- Step 6 confirmation must say: "zero-substance path - nothing new to capture this session; ran worktree cleanup only"
Light path - triggers when the zero-substance conditions do NOT all hold BUT ALL of the following hold:
- Output 2 (memory entries) would be "None" - STRICT: even a single memory entry routes to standard path
- Output 3 (AGENTS.md updates) would be "None" for every file AND no new AGENTS.md candidates exist
- No specialist agent ran with session-scoped issues to capture
Light path procedure (replaces Steps 1-3; preserves parts of Step 4):
- Main agent drafts
_wrap.md inline from the Step 0 raw data, following the Output 1 structure exactly. No Worker, no Skeptic.
- Skip Step 1 (draft Worker) and Steps 2-3 (Skeptic + sign-off validation).
- Proceed to Step 4 Part A with the inline draft.
- Skip Part B (memory.md - input is None), Part C (AGENTS.md - input is None).
- Run Part D (skill-candidate wrap-time signal) and Part D.5 (session-feedback capture signal) - the light path still ran a session worth extracting from.
- Skip Part E entirely (nothing changed, nothing to compress).
- Run Step 5 (worktree cleanup) as normal.
- Run Step 6 as normal, including Part F (tracker status reconciliation) - a light-path session can still have committed ticket-referencing work even with no memory/AGENTS.md updates to review.
- Step 6 confirmation must say: "light path (no stable facts or AGENTS.md updates to review this session)".
Escape hatch for light path: If, while drafting _wrap.md inline, the main agent notices something it wants the Skeptic to review - ambiguous next-step wording, uncertainty about whether a fact is stable or temporary, unfamiliar territory in the raw data - it must abandon the light path and fall back to the standard path. The light path is for cases where there is genuinely nothing worth an adversarial pass.
Escape hatch for zero-substance path: If the conductor has ANY uncertainty about whether the session is truly zero-substance - for example, the user asked a question whose answer feels architecturally significant, or an implicit decision was made without writing anything down - it must abandon the zero-substance path and use the light or standard path instead. When in doubt, do not use the zero-substance path.
Standard path - triggers when neither of the above applies (i.e. at least one of Outputs 2/3 has real content, OR a specialist agent ran with session-scoped issues). Proceed to Step 1 unchanged.
Step 1 — Spawn a draft Worker (background, general-purpose):
You are a Worker agent. Format the raw session data below into three outputs. Replace all placeholders with real content from the data provided. If a section genuinely has nothing to say, write the word "None" — never leave brackets or template text.
Raw session data:
[paste your Step 0 notes here verbatim — this covers the task, files touched, errors, next steps, tools used, and stable facts. Do NOT embed existing AGENTS.md file contents here; those go in the dedicated field below.]
Existing learnings:
[Paste the full current content of $AE_PROJECT_DIR/.agentic/learnings.md read in Step 0, clearly labeled. If the file was not found, write "None." The Worker must check whether a proposed memory entry is already captured here as a structured learning before proposing it.]
Existing AGENTS.md file contents:
[For each AGENTS.md file read in Step 0, paste its full current content here, clearly labeled with its absolute path, e.g.:
File: /Users/alice/myapp/AGENTS.md
Content:
File: /Users/alice/myapp/backend/AGENTS.md
Content:
If no AGENTS.md files were found, write "None."]
Open-PR overlap set:
[paste the {pr_number, head_branch, modified_files[]} entries captured in Step 0, or "None" if no open PRs target the conductor's current branch / the check was skipped. The conductor will use this to defer doc additions whose cited paths overlap an open PR — but you should still flag candidates so the conductor's deferral pass has hints.]
Output 1 — _wrap.md draft (the curated context)
Produce this exact structure. Include only temporary session state here (current task, files touched, recent errors, next steps). Do not include stable project facts in this file - those belong in Output 2.
# Session Context
*Written by $wrap on YYYY-MM-DD. Managed only by `$wrap`. Not committed to git.*
*Project: [absolute cwd]*
## Recent Focus
[1–3 sentences: what was being worked on when $wrap was invoked]
## Current Task / Next Steps
[Specific next steps: file paths, branch names, open PRs, exact commands. Concrete enough to act on without reading the chat history.]
## Key File Paths
[Files touched or created this session that the next session will care about]
## Uncommitted Changes
[Output of `git status --porcelain` for tracked files only (M/A/D/R, not ??). If working tree is clean, write "(working tree clean)". If there are uncommitted files, list each with its status prefix. This section is a safety net - if the user asked to commit all changes and files appear here, they were missed.]
## Stashes
[Output of `git stash list`, or "(no stashes)" if empty. Stashes may contain work from previous sessions that was never committed.]
## Watch Out For
[Session-specific issues, errors, or near-misses from this session only. Stable/recurring project quirks do not belong here - those go in memory.md. Or: None.]
## Tools Used
[Comma-separated list of unique tools used this session]
Output 2 — memory.md entries
Review the raw session data for stable project facts: setup commands that don't change, persistent project-wide gotchas or quirks, architectural decisions, recurring patterns, project conventions. For each stable fact, produce one entry in this format:
- **YYYY-MM-DD:** [what was decided and why this approach was chosen - alternatives considered may be noted as supporting context, in one to two sentences]
Use today's date for all entries. If there are no stable facts to record, write "None."
Before proposing a memory entry, check the Existing learnings field above. If the same fact is already captured as a structured learning entry (same pattern, same gotcha, same architectural decision), skip it. Do not duplicate content between $AE_PROJECT_DIR/.agentic/learnings.md and $AE_PROJECT_DIR/.agentic/memory.md — they serve different purposes (structured fix-patterns vs. session-synthesized stable facts), but the underlying fact should only be recorded once.
Stable = true every session, not just this one. Temporary = only relevant right now (current task, files touched this session).
For architectural and technology decisions especially: the entry must clearly state why the chosen approach was selected on its own merits. Alternatives considered and their rejection reasons are useful supporting context but are secondary - the positive reasoning for the choice is the primary requirement. A future session asking "should we reconsider X?" should find the answer in the entry without re-researching it.
Deferral hint: If an entry's substance depends on file paths, feature keys, or symbols that appear in the Open-PR overlap set above (i.e. the fact only becomes true once an unmerged PR lands), append the marker [defer-pr: <pr_number>] to the end of the entry text. The conductor uses this hint plus its own path cross-reference to route the entry to $AE_PROJECT_DIR/.agentic/memory-pending.md instead of $AE_PROJECT_DIR/.agentic/memory.md.
Output 3 — AGENTS.md updates
For each AGENTS.md file whose current content was provided in the "Existing AGENTS.md file contents" field above, produce proposed additions only - not a full rewrite. Use that existing content as your baseline: do not propose content already present there.
Format each proposed update as:
File: [full path to AGENTS.md]
Section: [section name, e.g. "## Decisions", "## Conventions", "## Stack", "## Key Conventions"]
Add:
- [bullet point to add]
- [another bullet if needed]
If a section doesn't exist in the target file yet but should be added, indicate:
File: [full path to AGENTS.md]
New section: [section name]
Content:
[section content]
If content in an existing entry should be corrected or superseded, indicate:
File: [full path to AGENTS.md]
Section: [section name]
Update: [existing text] → [replacement text]
Rules:
- Only propose content that was actually established or learned in this session. Do not hallucinate or infer.
- Do not duplicate content already present in the existing AGENTS.md (check against the "Existing AGENTS.md file contents" field provided above).
- Do not contradict existing content without flagging it as an Update.
- For root AGENTS.md: focus on
## Decisions (resolved architecture decisions as brief bullets) and ## Conventions (patterns and rules the project follows).
- For subdir AGENTS.md: focus on
## Stack, ## Key Conventions, and any new relevant categories (Commands, Schema, Flows, Gotchas) that emerged this session.
- Quality directive: lean and curated. No verbose rationale paragraphs, no outdated entries, no conflicting information. Brief, actionable bullets only.
- If nothing new for a particular file, write "None" for that file.
- If no AGENTS.md files were found in the project, write "None."
- Deferral hint: if a proposed addition cites a file path, directory, or feature key that appears in the Open-PR overlap set above (i.e. the addition describes something that only exists on an unmerged branch), append the marker
[defer-pr: <pr_number>] to the end of each affected bullet or section content. The conductor uses this hint plus its own path cross-reference to route the addition to $AE_PROJECT_DIR/.agentic/agents-md-pending.md instead of applying it now.
New AGENTS.md files: For any touched directory explicitly noted as a "new AGENTS.md candidate" in the raw session data (i.e. the directory had files touched but has no existing AGENTS.md), propose creating a new file. Use this format:
File: [full path to new AGENTS.md]
New file: true
Content:
# [Directory name]
[One sentence description of what this directory contains, based on the session data.]
## Stack
[Key technologies from package.json or inferred from file types touched - bullet list]
## Key Conventions
[Conventions observed from the session - bullet list. If none observed, omit this section.]
## Gotchas
[Any gotchas or sharp edges encountered - bullet list. If none, omit this section.]
This is automatic - do not ask the user. Populate sections from session context and any package.json content included in the session data.
Return all three outputs clearly labeled. Do not write to disk.
Step 2 — When the draft Worker returns, spawn a fresh Skeptic (background, general-purpose, never resumed).
Scope constraint: the Skeptic reviews only the accuracy and completeness of the context file and the AGENTS.md updates. Its findings must only trigger context file or AGENTS.md rewrites - never code changes, bug fixes, or any development work. If the Skeptic notes that the context file describes pending work that is already complete (or vice versa), the fix is to update the wording to reflect reality accurately.
Provide this adversarial brief, the Global-context input set (## Global-context inputs block per $AE_REPO_DIR/content/references/skeptic-protocol.md Section 4.5 - fields 1-3 are n/a - internal scaffolding artifact review (no code diff, no architect plan/Brief/qa_criteria applies); field 4 (per-consumer impact table) is n/a - internal scaffolding artifact (not a shared-utility surface, no per-consumer impact table applies); field 5 is the existing AGENTS.md file paths from Step 0; field 6 is the draft _wrap.md/AGENTS.md-update content), then the draft and the existing AGENTS.md file contents from Step 0. Omit any section below whose corresponding Output is "None" - always keep the Output 1 (_wrap.md accuracy) review as the baseline pass; drop the memory-review language if Output 2 is "None"; drop the AGENTS.md-review paragraph if Output 3 is "None". The full brief below is the "all outputs present" case:
"Is this context file accurate and actionable? Check each section: Does Recent Focus correctly describe what was actually happening — or is it vague, generic, or wrong? Are the Next Steps specific enough to act on without reading the chat history (file paths, commands, branch names)? Are Key File Paths complete — is anything relevant omitted? Does Watch Out For capture real gotchas, or is it empty when it shouldn't be? Is any section still template text rather than real content?"
"Also review the proposed AGENTS.md updates (Output 3): Is each proposed addition actually derived from this session's work - or is it generic, hallucinated, or already present in the existing file content provided? Is any content going to the wrong file (project-wide content should go to root; track-specific content should go to the track subdir)? Are updates lean - brief bullets only, no verbose rationale? Does any proposed addition contradict or duplicate existing entries in the same file?"
Require this statement before sign-off: "Active search: I have applied the adversarial brief and actively searched for Critical and Major findings."
Step 3 — Validate sign-off format.
A valid sign-off requires the mandatory elements defined in $AE_REPO_DIR/content/references/skeptic-protocol.md Section 11 (the six always-required lines: Reviewed:, Findings:, Active search:, the sign-off phrase, Manifest check:, Test-CI-wiring check:; the conditional spec-deviation and PR-SHA-range elements do not apply to this internal review). If any element is missing, spawn a new Skeptic with format instructions (not a new re-route round). Limit: 3 format re-invocations, then escalate to the user.
If Critical or Major findings remain: spawn a new draft Worker with the original draft and findings, get a revised draft, then spawn a fresh Skeptic (Step 2). Repeat until sign-off. If the same finding is contested across 2+ re-routes without resolution, escalate to the user.
Step 4 — Write to disk (main agent, inline — do NOT delegate to a subagent).
Background subagents cannot reliably get Write/Edit permissions. The main agent must perform all writes directly. Invoking $wrap implies permission to write these files.
Mandatory Skeptic on hand-authored output. If the conductor authored any of the final outputs inline — for example, after a draft Worker hallucination, after a re-route loop hit its limit, after a light-path escape hatch fell back to the standard path mid-flight, or any other case where the conductor bypassed the Worker → Skeptic chain in Steps 1–3 — the conductor MUST spawn a fresh Skeptic on the on-disk files BEFORE releasing the lock in Step 6. The conductor's escape hatch from Worker iteration does NOT exempt the outputs from Skeptic review; that loophole is closed. The Skeptic in this case reads the on-disk files directly ($AE_PROJECT_DIR/.agentic/_wrap.md, $AE_PROJECT_DIR/.agentic/memory.md, any AGENTS.md files updated, plus any deferred-write files at $AE_PROJECT_DIR/.agentic/memory-pending.md and $AE_PROJECT_DIR/.agentic/agents-md-pending.md) and applies the same adversarial brief from Step 2 (with the same scope constraint — the Skeptic's findings only trigger doc rewrites, never code changes). If the Skeptic raises Critical or Major findings, the conductor revises the on-disk files inline and re-spawns a fresh Skeptic until sign-off, subject to the same 3-re-route limit; on cap exhaustion, escalate to the user with the open findings.
Project directory: [absolute cwd]
Output path (curated context): $AE_PROJECT_DIR/.agentic/_wrap.md. Project-local, gate-free, created if absent. This is a retarget, not a rewrite - $wrap executes the identical Part A algorithm; only the file it reads and writes changed from context.md to _wrap.md.
The project-local derived-rollup contract belongs to harnesses that implement the project-local context writer. The current Codex Stop hook instead writes only ~/.codex/projects/[hash]/context.md and does not recompose $AE_PROJECT_DIR/.agentic/context.md. $wrap writes $AE_PROJECT_DIR/.agentic/_wrap.md; integrating that handoff into an automatic Codex project-local rollup is deferred to context-writer-migration.
Why: the current Codex Stop hook writes continuity only to ~/.codex/projects/[hash]/context.md and does not read or recompose project-local $AE_PROJECT_DIR/.agentic/context.md. $wrap owns its richer project-local $AE_PROJECT_DIR/.agentic/_wrap.md handoff under the wrap lock. Connecting that handoff to an automatic Codex project-local context writer remains deferred to context-writer-migration. Create $AE_PROJECT_DIR/.agentic/ if it does not exist.
Memory path (memory.md): $AE_PROJECT_DIR/.agentic/memory.md. Same directory as _wrap.md. $AE_PROJECT_DIR/.agentic/memory.md is /wrap-internal rolling scratch (written exclusively by $wrap). It is gitignored and is NOT the canonical durable-facts store. The canonical durable-facts store is <cwd>/MEMORY.md, loaded at session start via the @MEMORY.md import in the project root CLAUDE.md (added by manual workflow 'ds-init-project' via $AE_REPO_DIR/bin/agentic-codex-dispatch command ds-init-project).
Migration note: Earlier versions of this skill wrote to $AE_SHARED_CONFIG_DIR/projects/[hash]/{context,memory}.md. If those files exist for the current project but the project-local files do not, copy them once into $AE_PROJECT_DIR/.agentic/ before merging. Symlinks at the old hashed location pointing at the new project paths are acceptable - they preserve any platform mechanism that auto-loads from the legacy path while keeping writes gate-free.
Part A — Write _wrap.md (the curated context)
The pinned header prefix, the spillover-drain procedure, the $AE_PROJECT_DIR/.agentic/wrap/last-wrap write contract, and the _wrap.md rolling-session-label merge algorithm are defined in $AE_REPO_DIR/content/references/wrap-context-format.md (the shared normative home cited by both $wrap and manual workflow 'ds-wrap-deferred' via $AE_REPO_DIR/bin/agentic-codex-dispatch command ds-wrap-deferred). This Part A is the $wrap-specific wrapper around that shared algorithm; the algorithm itself is NOT restated here.
Inside the Part A _wrap.md write window (the whole-flow wrap/lock acquired at pre-flight is held throughout - see "Pre-flight lock acquisition" and the Step 6 release; Part A introduces no new lock window), run, in this exact order:
-
Atomic spillover drain - the 3-step rename-first procedure in $AE_REPO_DIR/content/references/wrap-context-format.md §"Spillover-drain procedure": rename $AE_PROJECT_DIR/.agentic/wrap/deferred-activity.jsonl -> $AE_PROJECT_DIR/.agentic/wrap/deferred-activity.jsonl.draining.<pid>, fold its records into _wrap.md's curated ## Recent Focus region (each record carries its own session_id, preserving cross-session provenance), then unlink the renamed copy. Apply the Recent-Focus dedup rule from that reference (key the folded draft by session_id+staged_at; skip a re-folded duplicate) so a duplicate enrichment of the same marker is idempotent.
-
Rolling-session-label merge write of $AE_PROJECT_DIR/.agentic/_wrap.md - the algorithm in $AE_REPO_DIR/content/references/wrap-context-format.md §"_wrap.md rolling-session-label merge algorithm" (file-absent / non-/ds-wrap / merge branches, the duplicate-claim dedup, the 1-to-10 label rolling window, and the per-section merge rules). The merged write always begins with the pinned header prefix # Session Context\n*Written by $wrap (the matcher contract); no site parses the header date.
-
Write $AE_PROJECT_DIR/.agentic/wrap/last-wrap = this session's session_id (atomic) - per $AE_REPO_DIR/content/references/wrap-context-format.md §"$AE_PROJECT_DIR/.agentic/wrap/last-wrap write contract".
The net behavior of Part A is unchanged by this extraction: the cited reference is semantically identical to the algorithm $wrap formerly inlined here (identical aside from the Result/Return label), pinned by the golden-file parity test ($AE_REPO_DIR/hooks/tests/test-wrap-context-format-golden.js).
Part B — Write memory.md
Skip Part B entirely if the memory entries input above is "None".
Open-PR deferral pass (run BEFORE the read/merge steps below). For each proposed memory entry, cross-reference the file paths, directory paths, and feature keys cited in the entry against the Open-PR overlap set captured in Step 0. An entry is post-merge-deferred if any cited path or key appears in the modified_files[] list of any open PR, OR the Worker tagged the entry with [defer-pr: <pr_number>]. Strip the marker from the entry text and route the entry to $AE_PROJECT_DIR/.agentic/memory-pending.md (append-only; create the file if missing) under a heading ## Pending PR #<pr_number> (<head_branch>). Non-deferred entries continue to the steps below. The pending file is plain markdown — a follow-up doc PR after the source PRs merge can move entries from $AE_PROJECT_DIR/.agentic/memory-pending.md into $AE_PROJECT_DIR/.agentic/memory.md. Rationale: docs land on the conductor's branch (typically main) before source PRs merge; without deferral, memory.md describes paths or keys that do not yet exist on the target branch.
-
Use the Read tool to attempt to read the file at the memory.md path.
-
If the file does not exist: write all non-deferred entries directly as a markdown list. Return: "Wrote fresh memory to [path] (N entries written, M deferred to memory-pending.md)."
-
If the file exists: read its content. For each non-deferred entry, check whether the same fact is already captured — not just as an exact string match, but semantically (same architectural decision, same gotcha, same command). Also check $AE_PROJECT_DIR/.agentic/learnings.md (read in Step 0): if the same fact is captured as a structured learning entry, skip the new memory entry. If an existing entry covers the same fact, skip the new entry. If the new entry supersedes an existing one (same topic but updated or corrected), replace the existing entry in place with the new one. Otherwise append the new entry. Write the merged result. Return: "Updated memory at [path] (N entries added, M entries superseded, K deferred to memory-pending.md)."
Part C — Write AGENTS.md updates
Skip Part C entirely if the AGENTS.md updates input above is "None" or all files within it are marked "None".
Open-PR deferral pass (run BEFORE iterating files). For each proposed Add:, New section:, New file: true, and Update: block, cross-reference the file paths, directory paths, and feature keys cited in the proposed content against the Open-PR overlap set captured in Step 0. A block is post-merge-deferred if any cited path or key appears in the modified_files[] list of any open PR, OR the Worker tagged the block with [defer-pr: <pr_number>]. Strip the marker from the block content and route the deferred block to $AE_PROJECT_DIR/.agentic/agents-md-pending.md (append-only; create the file if missing) under a heading ## Pending PR #<pr_number> (<head_branch>) — <target AGENTS.md path>. Non-deferred blocks continue through the per-file write below. A follow-up doc PR after the source PRs merge can move entries from $AE_PROJECT_DIR/.agentic/agents-md-pending.md into the actual AGENTS.md files. Rationale: docs land on the conductor's branch (typically main) before source PRs merge; without deferral, AGENTS.md describes paths or keys that do not yet exist on the target branch — exactly the failure mode that historically produced Critical findings during $wrap Skeptic review.
For each file with non-deferred updates:
-
Use the Read tool to attempt to read the current file content.
-
If the file does not exist (Read returns a file-not-found error): create a minimal stub appropriate for the file type, then continue to steps 3-6 to apply the proposed updates into it.
- Subdirectory AGENTS.md (any path that is not the project root's AGENTS.md - i.e. the file is not at
[cwd]/AGENTS.md): create a stub with # [directory name] as the H1 (derive from the parent directory of the file path), a ## Stack section header, and a ## Key Conventions section header.
- Root AGENTS.md (the file is at
[cwd]/AGENTS.md): create a stub with # [project name] as the H1 (derive from the cwd directory name), a ## Decisions section header, and a ## Conventions section header.
If the draft Worker proposed a complete New file: true block with content, use that content as the starting file instead of the minimal stub.
After creating the stub or new file, proceed with steps 3-6 to apply the proposed updates into it. Return: "Created and updated AGENTS.md at [path] (N additions)."
-
For each Add: update: locate the target section. Append the new bullet(s) at the end of that section, before the next ## heading (or at end of file if it's the last section). Do not duplicate any bullet already present (check semantically, not just string match).
-
For each New section: update: insert the new section after the last existing section in the file, maintaining the document's natural flow (decisions and conventions before gotchas; stack and key conventions before less-common categories). Do not blindly append without regard to the existing structure.
-
For each Update: update: find the existing text and replace it with the replacement text.
-
Write the updated file to disk.
Return: "Updated AGENTS.md at [path] (N additions, M updates)" for each file written, or "Skipped [path] (nothing to add)" if all proposed additions were already present.
Part D — Skill-candidate wrap-time signal
Skip Part D on the zero-substance path (already skipped Steps 1-3; no session activity to extract from). Run Part D on the light path and the standard path. This step runs INSIDE the wrap/lock window already held from pre-flight. Soft-fail: any error in this step is silently swallowed; Part D failure NEVER breaks or delays the wrap.
Gate: Read $AE_PROJECT_DIR/.agentic/config.json. If skill_candidate_detection is explicitly false, skip Part D entirely. Default (key absent or config missing) is true - proceed.
Extraction (inline LLM reasoning over the session already reflected on in Step 0):
Emit a JSON array of 0-5 entries identifying DISTINCT domains where you or the user repeatedly did manual work, or worked around the same friction, this session - the kind of recurring manual workflow that might warrant a reusable skill/command/preset/lint-rule. Exclude one-off actions. Output [] if nothing qualifies.
Each entry shape:
domain (required): short lowercase-hyphenated slug naming the recurring workflow (e.g. adapter-rebuild, skeptic-context-block). Reuse an obvious existing slug if the same workflow has appeared before; exact-match merging is the helper's job, not yours.
exampleNote (required): one sentence describing the concrete instance observed this session.
suggestedArtifact (optional): one of command|named-agent|preset|lint-rule.
Do NOT include count, firstSeen, lastSeen, or any tally fields - those are helper-assigned.
Write and invoke (Bash):
Write the extracted array to a temp file and call the deep-cluster helper. Use $AE_SESSION_ID as the session id; if it is unset or empty, skip the invocation entirely (soft no-op).
CLUSTER_TMP=$(mktemp /tmp/wrap-clusters-XXXXXX.json)
cat > "$CLUSTER_TMP" << 'EOF'
[...the extracted array...]
EOF
if [ -n "$AE_SESSION_ID" ]; then
node $AE_REPO_DIR/hooks/lib/skill-candidate-deep-cluster.js "$REPO_CWD" "$AE_SESSION_ID" "$CLUSTER_TMP" 2>/dev/null || true
fi
rm -f "$CLUSTER_TMP" 2>/dev/null || true
Where $REPO_CWD is the absolute cwd of the project (the same value identified in Step 0). Any failure (non-zero exit, missing node, missing helper) is silently swallowed via || true; the wrap continues normally.
Part D.5 — Session-feedback capture signal
Skip Part D.5 on the zero-substance path (already skipped Steps 1-3; no session activity to extract feedback signals from) — same skip condition as Part D. Run Part D.5 on the light path and the standard path, immediately after Part D and still INSIDE the wrap/lock window already held from pre-flight. Soft-fail: the whole step is wrapped swallow-all — any error anywhere in Part D.5 is silently swallowed; Part D.5 failure NEVER breaks or delays the wrap. Unlike Part D, Part D.5 has no config gate — it is always-on regardless of skill_candidate_detection or any other toggle.
Deterministic evidence gathering. Each of the four signals below is individually guarded: a missing file, a missing or broken agentic-feedback binary, or a read error on any ONE signal must never break or stall the wrap, and must never prevent the remaining signals from being checked. Gather candidates from whichever signals are available; skip any that error or are absent.
- Tool-friction signal. If
$AE_PROJECT_DIR/.agentic/events.jsonl exists, read only its last ~500 lines (bounded read — never read the whole file). Filter to lines where event == "tool_failure_workaround" AND data.session_uuid == $AE_SESSION_ID. Each match is a candidate: category = tool-friction, evidence "tool_failure_workaround: <tool> (<domain_tag>) - <note>".