Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
47 changes: 28 additions & 19 deletions plugins/codex/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ verification, decision capture, and resumable checkpoints.

- **Orient from memory.** The `bm-orient` skill reads active tasks, open
decisions, and recent Codex checkpoints before substantial work.
- **Checkpoint work.** `PreCompact` records a private request and the `Stop`
hook asks the active Codex turn to run `bm-checkpoint` once after compaction.
- **Checkpoint work after compaction.** The post-compaction `SessionStart`
context asks the resumed Codex turn to run `bm-checkpoint`.
The resulting `codex_session` or `coding_session` note is agent-authored from
the compacted working context, with repository and pull-request evidence.
- **Capture decisions.** The `bm-decide` skill records durable engineering
Expand All @@ -33,19 +33,24 @@ verification, decision capture, and resumable checkpoints.
| --- | --- |
| `.codex-plugin/plugin.json` | Codex plugin manifest |
| `.mcp.json` | Basic Memory MCP server configuration |
| `hooks/hooks.json` | SessionStart, PreCompact, and Stop hook registration |
| `hooks/hooks.json` | SessionStart, PreCompact, and rollout-compatibility Stop registration |
| `hooks/session_start.py` | uv script: runs `basic-memory hook session-start --harness codex` |
| `hooks/pre_compact.py` | uv script: runs `basic-memory hook pre-compact --harness codex` |
| `hooks/stop.py` | uv script: runs `basic-memory hook stop --harness codex` |
| `hooks/stop.py` | uv script: preserves the previous checkpoint handshake during the runtime-pin rollout |
| `skills/` | Codex-native Basic Memory workflows |
| `schemas/` | Seed schemas for Codex sessions, decisions, and tasks |

The hook scripts carry no logic: the brief, checkpoint coordination, and
The hook scripts carry no logic: the brief, checkpoint prompting, and
lifecycle-event capture all live in the pinned Basic Memory revision behind
`bm hook`. Each is a self-contained PEP 723 script pinned to a Basic Memory Git
ref. All refs are updated together with
`just set-codex-hook-version <sha-or-tag>`.

The Stop shim is a temporary rollout bridge. While these scripts remain pinned
to the last durable merged runtime, it preserves that runtime's checkpoint
handshake. The dependency-only follow-up that pins the post-compaction
`SessionStart` implementation also removes the Stop registration.

## Requirements

- **[uv](https://docs.astral.sh/uv/)** — required: the hooks are PEP 723
Expand Down Expand Up @@ -81,8 +86,7 @@ be moved into the plugin directory.

Configuration can live at user level in `~/.codex/basic-memory.json` or at
project level in `.codex/basic-memory.json`. User-level settings are the base;
the nearest project file overrides only the keys it declares. `redactKeys` and
`redactPaths` are the privacy exception: their user and project lists accumulate.
the nearest project file overrides only the keys it declares.
The setup skill asks which scope to use and recommends user-level configuration
by default.

Expand All @@ -101,32 +105,37 @@ Run the setup skill, or create `~/.codex/basic-memory.json` for shared defaults:
"secondaryProjects": [],
"teamProjects": {},
"focus": "code/dev",
"rememberFolder": "codex-remember",
"rememberFolder": "codex/remember",
"recallTimeframe": "7d",
"checkpointOnCompact": true,
"captureEvents": true,
"redactKeys": [],
"redactPaths": [],
"placementConventions": "Put decisions in decisions/ and work checkpoints in codex/<repo-dir>/."
"placementConventions": "Put decisions in codex/decisions/ and work checkpoints in codex/<repo-dir>/."
}
}
```

Codex event capture is on by default. Set the JSON boolean `false` at user or
project level to opt out; malformed values fail closed. Captured, redacted
project level to opt out; malformed values fail closed. Captured
lifecycle-event envelopes land in a local inbox under your Basic Memory home.
The lifecycle trace stays local: `basic-memory hook flush` only moves valid
envelopes into the local retention archive and never creates graph notes. Add
`redactKeys` and `redactPaths` arrays to extend the built-in redaction floor.
envelopes into the local retention archive and never creates graph notes.

Checkpoint prompting is on by default. Set `checkpointOnCompact` to the JSON
boolean `false` to opt out. Codex ignores PreCompact stdout; after compaction,
Codex runs SessionStart with the `compact` trigger. When the setting is enabled,
that context asks the resumed agent to run `bm-checkpoint` from its compacted
working context.

Codex ignores PreCompact stdout, so PreCompact cannot ask the model to write a
note directly. It leaves a private request for the Stop hook. Stop then blocks
the turn once with a request to run `bm-checkpoint`; the active model writes an
agent-authored checkpoint from its compacted context, and the next Stop is a
no-op to prevent loops.
`sessionProfile` only selects whether the skill writes a `codex_session` or
`coding_session` note.

When `captureFolder` is omitted, Codex resolves the Git top-level directory and
writes to `codex/<repo-dir>`. An explicit folder still wins.

Decision notes default to `codex/decisions`, and lightweight `bm-remember`
captures default to `codex/remember`. Project placement conventions and an
explicit `rememberFolder` can override those destinations.

For a coding profile, keep both the profile and checkout-specific repository
identifier in the project file without duplicating the shared settings:

Expand Down
33 changes: 2 additions & 31 deletions plugins/codex/skills/bm-checkpoint/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,18 @@ description: Save a deliberate Codex work checkpoint to Basic Memory with change

Create a durable handoff note for current Codex work. Use this when the user asks
to checkpoint, wrap up, hand off, remember the state of the work, or when the
Stop hook requests the deliberate handoff after compaction.
post-compaction SessionStart context requests the deliberate handoff.

## Gather

Read `~/.codex/basic-memory.json`, then the nearest project
`.codex/basic-memory.json`; project keys override user keys except that
`redactKeys` and `redactPaths` accumulate so project config cannot weaken user
privacy:
`.codex/basic-memory.json`; project keys override user keys:

- `primaryProject`, default omitted
- `captureFolder`, default `codex/<git top-level directory name>`
- `placementConventions`, optional
- `sessionProfile`, default `general`
- `repository`, required when `sessionProfile` is `coding`
- `redactKeys`, optional additional secret field names
- `redactPaths`, optional additional private directory prefixes

Apply the `bm-writing` skill before drafting the note.

Expand All @@ -47,31 +43,6 @@ Gather repo evidence:

Do not claim a test passed unless you ran it or the user supplied the result.

## Privacy Gate

Apply this gate to deliberate checkpoints and Stop-requested checkpoints alike.
It is mandatory before any `write_note` call:

1. Merge user and project `redactKeys` and `redactPaths` by accumulation. Include
the built-in secret-key families (`secret`, `token`, `password`, `credential`,
`auth`, API/access/private keys) and private roots (`~/.ssh`, `~/.aws`, and
`~/.gnupg`). Never copy the redaction rules themselves into the note.
2. Expand `~` for comparison and normalize both slash styles. A denied path
matches its exact directory or a descendant, not a sibling that merely shares
its text prefix.
3. Scrub **every string** passed to `write_note`: title, directory, tags,
frontmatter, body, observations, relations, changed-file paths, command
output, repository evidence, and pull-request evidence. Replace a whole path
value under a denied root, or an embedded denied-path token, with
`[REDACTED_PATH]`. Do not omit schema-required path fields; use the marker.
4. When gathered structured evidence has a key matching a built-in or configured
`redactKeys` entry, replace its entire value with `[REDACTED]`. Never reproduce
credentials, tokens, or private-key material from config, environment, tool
output, or compacted conversation context.
5. Review the final note arguments once more before writing. If any value cannot
be confidently scrubbed, fail closed: skip the checkpoint and report that
privacy policy blocked the write. Do not fall back to an unredacted note.

## Write

A checkpoint is a durable handoff, not a status dump or commit-by-commit
Expand Down
2 changes: 1 addition & 1 deletion plugins/codex/skills/bm-decide/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ choice with rationale and consequences, not a casual preference.
`.codex/basic-memory.json`; project keys override user keys:
- write to `primaryProject` when set
- follow `placementConventions` for the directory when they are specific
- otherwise use `decisions`
- otherwise use `codex/decisions`

Apply the `bm-writing` skill before drafting the note.

Expand Down
2 changes: 1 addition & 1 deletion plugins/codex/skills/bm-remember/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ a small fact that should survive the current thread.
1. Read `~/.codex/basic-memory.json`, then the nearest project
`.codex/basic-memory.json`; project keys override user keys:
- `primaryProject`, default omitted
- `rememberFolder`, default `codex-remember`
- `rememberFolder`, default `codex/remember`

Apply the `bm-writing` skill before drafting the note. Match its depth to this
lightweight capture; do not pad a small fact into an essay.
Expand Down
43 changes: 19 additions & 24 deletions plugins/codex/skills/bm-setup/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ repo, default project, current directory, or previous local state.

- config level: user-level `~/.codex/basic-memory.json` or project-level
`.codex/basic-memory.json`. Ask explicitly and recommend user level by default.
Project settings override user settings key by key, except `redactKeys` and
`redactPaths`, which accumulate so project config cannot weaken user privacy.
Project settings override user settings key by key.
- storage mode: cloud, local, or mixed. Prefer the user's stated mode over any
CLI default.
- `focus`: code/dev, research, writing, planning, or mixed.
Expand All @@ -41,14 +40,14 @@ repo, default project, current directory, or previous local state.
- `teamProjects`: optional share targets for `bm-share`.
- `captureFolder`: default `codex/<repo-dir>`, derived from the Git top-level
directory. Ask only when the user wants an explicit override.
- `rememberFolder`: default `codex-remember`.
- `rememberFolder`: default `codex/remember`.
- `placementConventions`: a short note about where decisions, tasks, and research
notes should land.
- `captureEvents`: whether to record redacted lifecycle-event envelopes in the
notes should land. Default decisions to `codex/decisions` so Codex-authored
memory stays under one tree.
- `checkpointOnCompact`: whether post-compaction SessionStart asks Codex to run
`bm-checkpoint`. Default to `true`; an explicit JSON boolean `false` opts out.
- `captureEvents`: whether to record lifecycle-event envelopes in the
local hook inbox. Default to `true`; an explicit JSON boolean `false` opts out.
- `redactKeys` and `redactPaths`: optional additions to the built-in redaction
floor. Ask for these only when event capture is enabled or the user has
repo-specific privacy requirements.

For the `coding` session profile, verify the current directory is inside a Git
repository. Resolve a stable `repository` identifier such as `owner/name` from
Expand All @@ -57,8 +56,8 @@ confirmation. Do not guess when the remote is missing or ambiguous. Explain that
coding checkpoints store structured repository, branch, SHA, working-directory,
and optional pull-request metadata in Basic Memory.

Explain the capture tradeoff before asking: enabled capture adds a local,
redacted event trail that stays queued until `bm hook flush` archives it locally.
Explain the capture tradeoff before asking: enabled capture adds a local event
trail that stays queued until `bm hook flush` archives it locally.
It never creates knowledge-graph notes or writes to team projects. The default is enabled; an explicit JSON boolean
`false` disables it, and malformed values fail closed.

Expand All @@ -84,25 +83,20 @@ project-level file:
"teamProjects": {},
"focus": "<focus>",
"sessionProfile": "<general-or-coding>",
"rememberFolder": "codex-remember",
"rememberFolder": "codex/remember",
"recallTimeframe": "7d",
"checkpointOnCompact": true,
"captureEvents": true,
"redactKeys": [],
"redactPaths": [],
"placementConventions": "<short convention>"
"placementConventions": "Put decisions in codex/decisions/ and work checkpoints in codex/<repo-dir>/."
}
}
```

Omit `captureFolder` to use `codex/<repo-dir>`; persist it only for an explicit
override. Preserve unrelated keys if the chosen file already exists. Include
`projectMode` when the user chose cloud, local, or mixed routing. Always persist
`captureEvents` as a JSON boolean. Empty `redactKeys` and `redactPaths` lists may
be omitted; when present, they must be JSON arrays of strings. `redactKeys`
extends payload-key redaction, while `redactPaths` also protects
working-directory and path-bearing checkpoint content. User and project
redaction lists accumulate. These files are intentionally Codex-specific; do
not write `.claude/settings.json`.
`checkpointOnCompact` and `captureEvents` as JSON booleans. These files are
intentionally Codex-specific; do not write `.claude/settings.json`.

For a user-level coding setup, omit `sessionProfile` from the shared user file and
keep both the coding profile and confirmed repository identifier in the project
Expand Down Expand Up @@ -164,10 +158,11 @@ Before closing, prove the mapping works:
- Run `basic-memory hook status --harness codex --project-dir <repo-root>` (using
`bm` or `uvx basic-memory` if needed). Confirm that it finds this repo's
settings, reports the selected project, session profile, repository, and
intended capture state.
intended checkpoint-prompt and capture states.
Its inbox counts are shared across harnesses.
- If any check errors, fix the project ref or hook launcher before finishing.

Finish with the project mapping, schemas seeded or skipped, capture/redaction
choices, shared inbox status, and the verification result. Tell the user that
plugin hooks need to be reviewed and trusted in Codex before they run.
Finish with the project mapping, schemas seeded or skipped, checkpoint prompt,
capture choice, shared inbox status, and the verification result.
Tell the user that plugin hooks need to be reviewed and trusted in Codex before
they run.
18 changes: 8 additions & 10 deletions plugins/codex/skills/bm-status/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,20 @@ Gather a concise diagnostic. Do not over-investigate.

2. Plugin config:
- read `~/.codex/basic-memory.json`, then the nearest project
`.codex/basic-memory.json`; project keys override user keys, while
`redactKeys` and `redactPaths` accumulate
`.codex/basic-memory.json`; project keys override user keys
- report the resolved `primaryProject`, `secondaryProjects`, `teamProjects`,
`captureFolder`, `rememberFolder`, `recallTimeframe`, `focus`,
`sessionProfile`, `repository`, `captureEvents`, `redactKeys`, and
`redactPaths`
`sessionProfile`, `repository`, `checkpointOnCompact`, and `captureEvents`
- resolve omitted Codex defaults as `rememberFolder=codex/remember`,
and `checkpointOnCompact=true`

3. Core hook health:
- with the first available launcher, run
`basic-memory hook status --harness codex --project-dir <repo-root>`
- report the shared inbox path, pending envelopes, archived envelopes,
pending checkpoint requests, last
- report the shared inbox path, pending envelopes, archived envelopes, last
flush, settings state, resolved primary project, capture state, capture
folder, Basic Memory version, and uv version from that command
folder, checkpoint-prompt state, Basic Memory version, and uv version from
that command
- inbox counts are global across supported harnesses; do not attribute a
backlog solely to Codex
- treat the command's settings resolution as canonical for hook behavior; if
Expand Down Expand Up @@ -69,13 +69,11 @@ Basic Memory for Codex
- Recall timeframe: <recallTimeframe>
- Session profile: <general | coding>
- Repository: <owner/name or none>
- Checkpoint on compact: <enabled | disabled>
- Event capture: <enabled | disabled>
- Redact keys: <configured count or none>
- Redact paths: <configured count or none>
- Shared hook inbox: <path or unavailable>
- Shared pending envelopes: <count or unavailable>
- Shared archived envelopes: <count or unavailable>
- Pending checkpoint requests: <count or unavailable>
- Last flush: <timestamp, never, or unavailable>
- Hook runtime: basic-memory <version>; uv <version or missing>
- Recent checkpoints: <count across coding_session and codex_session>
Expand Down
15 changes: 6 additions & 9 deletions scripts/validate_codex_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,21 @@
)
REQUIRED_SKILL_TEXT: dict[str, tuple[str, ...]] = {
"bm-setup": (
"checkpointOnCompact",
"captureEvents",
"user-level",
"project-level",
"codex/<repo-dir>",
"redactKeys",
"redactPaths",
"sessionProfile",
"coding-session.md",
"hook status --harness codex",
),
"bm-status": (
"~/.codex/basic-memory.json",
"checkpointOnCompact",
"hook status --harness codex",
"pending envelopes",
"archived envelopes",
"Pending checkpoint requests",
"last flush",
"type=codex_session",
"type=coding_session",
Expand All @@ -53,10 +52,6 @@
"bm-checkpoint": (
"Apply the `bm-writing` skill",
"A checkpoint is a durable handoff, not a status dump",
"## Privacy Gate",
"`redactKeys` and `redactPaths` accumulate",
"[REDACTED_PATH]",
"skip the checkpoint",
"username: <current username>",
"hostname: <current hostname>",
"type: coding_session",
Expand All @@ -66,6 +61,8 @@
"- relates_to [[Exact existing note title]]",
"Never write `[relates_to]`",
),
"bm-decide": ("codex/decisions",),
"bm-remember": ("codex/remember",),
"bm-writing": (
"intentionally user-customizable",
"problem -> approach -> current state and impact",
Expand Down Expand Up @@ -204,8 +201,8 @@ def validate_plugin(plugin_dir: Path) -> None:
readme = (plugin_dir / "README.md").read_text(encoding="utf-8")
for required_text in (
"lifecycle trace stays local",
"Stop hook",
"agent-authored checkpoint",
"post-compaction `SessionStart`",
"note is agent-authored",
):
if required_text not in readme:
raise SystemExit(f"README.md: missing schema ownership text {required_text!r}")
Expand Down
Loading
Loading