Skip to content

Commit 5ab75c1

Browse files
committed
release: publish csep 1.3.4
1 parent 4ef3e55 commit 5ab75c1

10 files changed

Lines changed: 248 additions & 11 deletions

File tree

‎README.md‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ Codex Self-Evolution Plugin,简称 `csep`。它在 `SessionStart` 时把稳定
5353
- 每次 `Stop` 都会归档 session,但 reflection 不是每次都跑;只有计数或高信号命中后才 fork worker。
5454
- reflection worker 写完必须留下 `receipt.json`,父进程会校验路径、hash 和 skill 命名空间。
5555
- memory 写在当前 repo bucket;生成 skill 只允许落到 `csep-reflect-*`,不碰用户已有 skill。
56-
- 核心包只用 Python stdlib,尽量减少安装、升级和 hook 执行时的变量。
56+
- 核心包没有 Python runtime dependencies,只用 stdlib;运行完整插件链路仍需要 Codex CLI、`uv`、hook/plugin 能力和 reflection provider 环境。
5757

5858
## 安装和启用
5959

@@ -88,6 +88,12 @@ csep status | python3 -m json.tool
8888

8989
重点看 `plugin_hooks.manifest_exists`、`plugin_hooks.session_start_declared`、`plugin_hooks.stop_declared` 是否为 `true`。之后每次 `SessionStart` 会注入 memory,每次 `Stop` 会归档 transcript;reflection 只在触发条件满足时后台运行。
9090

91+
发布或排障前可以跑本地 runtime smoke。它使用临时 state dir,不会污染默认 recall DB:
92+
93+
```bash
94+
scripts/smoke-runtime.sh
95+
```
96+
9197
### 首次回填历史
9298

9399
`Stop` hook 会自动归档以后发生的 Codex session;如果是新安装,而本机已经有 `~/.codex/sessions` 历史对话,先做一次 bootstrap,避免第一次 `csep recall` 搜不到旧上下文:

‎README_en.md‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ Every `Stop` archives the session, but reflection only runs when counters or hig
5353
- Every `Stop` archives, but not every `Stop` reflects.
5454
- Reflection writes must leave a `receipt.json`; the parent process validates paths, hashes, and skill namespaces.
5555
- Memory writes stay in the current repo bucket; generated skills must use the `csep-reflect-*` prefix.
56-
- The core package uses only the Python standard library to keep install and hook behavior predictable.
56+
- The core package has zero Python runtime dependencies and uses only the standard library. The full plugin flow still depends on Codex CLI, `uv`, hook/plugin support, and reflection provider configuration.
5757

5858
## Install And Enable
5959

@@ -88,6 +88,12 @@ csep status | python3 -m json.tool
8888

8989
Look for `plugin_hooks.manifest_exists`, `plugin_hooks.session_start_declared`, and `plugin_hooks.stop_declared` to be `true`. After that, every `SessionStart` injects memory, every `Stop` archives the transcript, and reflection runs in the background only when triggered.
9090

91+
Before a release or when debugging runtime wiring, run the local smoke harness. It uses a temporary state dir and does not pollute the default recall DB:
92+
93+
```bash
94+
scripts/smoke-runtime.sh
95+
```
96+
9197
### First-Time History Bootstrap
9298

9399
The `Stop` hook archives future Codex sessions automatically. On a fresh install, existing `~/.codex/sessions` history still needs a one-time bootstrap so the first `csep recall` can find old context:

‎docs/getting-started.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ stop_hook_archive = true
8383
retention_days = 14
8484
```
8585

86+
默认的 `sandbox = "danger-full-access"` 和 `approval_policy = "never"` 是为了让后台 reflection 能无阻塞完成写入和 receipt 落盘。它的信任边界不在 child 自述,而在父进程的 receipt validation:路径、hash、job identity、memory root 和 `csep-reflect-*` skill namespace 都会被校验。低信任或共享环境可以调低这两个值,但后台 reflection 可能因此需要人工确认或无法完成。
87+
8688
## 3. 检查 Codex Plugin Hooks
8789

8890
`csep setup` 会写入或更新 `~/.codex/config.toml`:
@@ -253,6 +255,12 @@ scripts/install.sh
253255
csep status | python3 -m json.tool
254256
```
255257

258+
发布或排障前可以额外跑 runtime smoke。脚本使用临时 state dir,不会污染默认 recall DB:
259+
260+
```bash
261+
scripts/smoke-runtime.sh
262+
```
263+
256264
### `session_reflection.latest.status` 是 `skipped`
257265

258266
这是正常状态之一。常见原因:

‎docs/session-recall.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ csep recall sync-claude --root ~/.claude/projects --limit-files 50
107107

108108
## Plugin Skill
109109

110-
Recall 的操作手册应该作为 CSEP 插件自带 skill 暴露给 Codex harness,而不是通过 `SessionStart` 注入完整 recall contract。
110+
Recall 的完整操作手册应该作为 CSEP 插件自带 skill 暴露给 Codex harness,而不是通过 `SessionStart` 注入完整 recall contract。`SessionStart` 只保留短的 recall policy/pointer,提醒模型按需调用 skill 或 `csep recall`。
111111

112112
插件结构:
113113

@@ -130,7 +130,7 @@ plugin_bundle/
130130
- `no_match` 后换更短、更确定的 needle。
131131
- 命中后引用原文证据,由模型自己总结和判断。
132132

133-
`SessionStart` 不再注入完整 recall 操作手册。最多只保留短提示,或者完全依赖 plugin skill。
133+
`SessionStart` 不再注入完整 recall 操作手册;当前实现只注入短 recall policy,用来区分 stable background 和“按需拉取更多历史”的操作提示。具体工作流仍以 plugin skill 为准。
134134

135135
## 输出模型
136136

‎docs/session-reflection.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,20 @@ skill_generation_mode = "one_shot_active"
143143
active_job_stale_seconds = 1800
144144
```
145145

146+
## 执行模型和安全边界
147+
148+
默认配置里的 `sandbox = "danger-full-access"` 和 `approval_policy = "never"` 是有意选择的高自治模式:reflection child 需要在后台完成读 transcript、写 memory、生成 skill、落 receipt 等动作,不能在 Codex 退出路径上等待人工确认。
149+
150+
这个默认值不表示 child 输出天然可信。CSEP 的安全边界放在父进程:
151+
152+
- child 只能通过 receipt 声明自己写了什么。
153+
- 父进程用 `validate_receipt()` 校验 job / parent / child identity、写入路径、文件 hash、skill namespace 和 `SKILL.md` 结构。
154+
- 合法 memory 写入只允许落在当前 repo bucket 的 `MEMORY.md` 或 `memory/refs/**/*.md`。
155+
- 合法 skill 只允许落到 `~/.codex/skills/csep-reflect-*` 命名空间。
156+
- validation 失败的 job 不会被视为成功沉淀。
157+
158+
如果你在共享机器、低信任 provider、或更严格的合规环境运行,可以把 sandbox / approval policy 调低,但要预期后台 reflection 可能需要人工确认或无法完成。
159+
146160
用户可以换模型。CSEP 会把 job 使用的模型记录到 job/fork response 中,但当前不做模型 allowlist 拦截。
147161

148162
## 排障文件

‎docs/status-2026-05-16.md‎

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,45 @@
44

55
本文记录 `csep` 发布后的项目状态、真实本机运行状态,以及三条主线的下一步优化点。
66

7-
最新交接更新:2026-05-17 12:01 CST。下面 `1.3.2` / `1.3.1` / `1.3.0` / `1.2.0` / `1.1.0` 段落保留为历史状态;当前真实状态以最新发布节为准。
7+
最新交接更新:2026-05-17 12:01 CST。下面 `1.3.3` / `1.3.2` / `1.3.1` / `1.3.0` / `1.2.0` / `1.1.0` 段落保留为历史状态;当前真实状态以最新发布节为准。
8+
9+
## 2026-05-17 1.3.4 发布状态
10+
11+
### 发布和远端
12+
13+
- PyPI 已发布:`csep 1.3.4`
14+
- PyPI 页面:https://pypi.org/project/csep/1.3.4/
15+
- `1.3.4` 包含 wheel 和 sdist:
16+
- `csep-1.3.4-py3-none-any.whl`
17+
- `csep-1.3.4.tar.gz`
18+
- 发布前验证:
19+
- `csep config validate` -> ok
20+
- `scripts/smoke-runtime.sh` -> passed
21+
- `make test PYTHON=/Users/haha/hermes-agent/venv/bin/python3.11` -> `261 passed`
22+
- `uv run --extra dev python -m build` 成功
23+
- `uv run --extra dev twine check dist/csep-1.3.4*` passed
24+
- `uv run --extra dev twine upload dist/csep-1.3.4*` 成功
25+
- `git diff --check` clean
26+
- 构建仍会出现既有 warning:
27+
- `pyproject.toml` license table / license classifier 的 setuptools deprecation warning
28+
- `MANIFEST.in` 中 `tests/fixtures/*.json` 当前无匹配文件
29+
30+
### 1.3.4 主变更
31+
32+
- README / README_en 将 `zero runtime dependencies` 收口为 `zero Python runtime dependencies`,避免误导成不依赖 Codex / uv / app-server。
33+
- `docs/session-recall.md` 明确 `SessionStart` 注入的是短 recall policy/pointer,不是完整 recall 操作手册。
34+
- `docs/session-reflection.md` 和 getting started 补充默认 `danger-full-access` + `approval_policy = "never"` 的执行模型、风险前提和父进程 validation 边界。
35+
- 新增 `scripts/smoke-runtime.sh`,使用临时 state dir 验证:
36+
- `csep status` 版本矩阵
37+
- `SessionStart` hook 输出
38+
- Codex transcript archive + recall
39+
- Claude Code sync + recall
40+
- `tests/test_install_script.py` 覆盖 runtime smoke 脚本入口和关键路径。
41+
42+
### 下一步建议
43+
44+
- 后续 release gate 可以固定先跑 `scripts/smoke-runtime.sh`,再跑全量 pytest。
45+
- 仍可继续考虑一条更接近真实 Codex app-server 的半自动 e2e harness,但这不再阻塞当前发布。
846

947
## 2026-05-17 1.3.3 发布状态
1048

‎pyproject.toml‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "csep"
7-
version = "1.3.3"
8-
description = "Self-evolution loop for OpenAI Codex: session reflection, session recall, and reflection-generated skills with zero runtime dependencies."
7+
version = "1.3.4"
8+
description = "Self-evolution loop for OpenAI Codex: session reflection, session recall, and reflection-generated skills with zero Python runtime dependencies."
99
readme = "README.md"
1010
requires-python = ">=3.11"
1111
license = { file = "LICENSE" }
@@ -22,9 +22,9 @@ keywords = [
2222
"plugin",
2323
"self-evolution",
2424
]
25-
# Keep runtime deps empty — the plugin only uses stdlib. If you ever add a real
26-
# dep, also update the "zero runtime dependencies" wording in the description
27-
# above and in README.
25+
# Keep Python runtime deps empty — the plugin only uses stdlib. If you ever add
26+
# a real dep, also update the "zero Python runtime dependencies" wording in the
27+
# description above and in README.
2828
dependencies = []
2929

3030
# Classifiers drive PyPI search + the project page sidebar. Keep the list

‎scripts/smoke-runtime.sh‎

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5+
CSEP_BIN="${CSEP_BIN:-csep}"
6+
PYTHON="${PYTHON:-python3.11}"
7+
STATE_DIR="${CSEP_SMOKE_STATE_DIR:-}"
8+
CLEANUP_STATE=0
9+
10+
if [ -z "$STATE_DIR" ]; then
11+
STATE_DIR="$(mktemp -d /tmp/csep-runtime-smoke.XXXXXX)"
12+
CLEANUP_STATE=1
13+
fi
14+
15+
cleanup() {
16+
if [ "$CLEANUP_STATE" = "1" ]; then
17+
rm -rf "$STATE_DIR"
18+
fi
19+
}
20+
trap cleanup EXIT
21+
22+
log() {
23+
printf '==> %s\n' "$*"
24+
}
25+
26+
require_command() {
27+
if ! command -v "$1" >/dev/null 2>&1; then
28+
printf 'missing required command: %s\n' "$1" >&2
29+
exit 127
30+
fi
31+
}
32+
33+
assert_json() {
34+
"$PYTHON" - "$@" <<'PY'
35+
import json
36+
import sys
37+
from pathlib import Path
38+
39+
path = Path(sys.argv[1])
40+
kind = sys.argv[2]
41+
data = json.loads(path.read_text(encoding="utf-8"))
42+
43+
if kind == "session-start":
44+
output = data.get("hookSpecificOutput") or {}
45+
if output.get("hookEventName") != "SessionStart":
46+
raise SystemExit(f"invalid SessionStart output: {data}")
47+
if not isinstance(output.get("additionalContext"), str):
48+
raise SystemExit("SessionStart missing additionalContext")
49+
elif kind == "archive":
50+
if data.get("status") != "archived":
51+
raise SystemExit(f"archive did not complete: {data}")
52+
elif kind == "recall":
53+
if data.get("status") != "matched" or int(data.get("count") or 0) < 1:
54+
raise SystemExit(f"recall did not match: {data}")
55+
elif kind == "claude-sync":
56+
if data.get("status") != "completed" or data.get("source") != "claude_code":
57+
raise SystemExit(f"Claude sync did not complete: {data}")
58+
if int(data.get("processed_successfully") or 0) < 1:
59+
raise SystemExit(f"Claude sync archived no sessions: {data}")
60+
elif kind == "status":
61+
tool = (data.get("tools") or {}).get("csep") or {}
62+
missing = [
63+
key for key in ("installed_version", "runtime_version", "source_version", "pypi_latest_version")
64+
if not tool.get(key)
65+
]
66+
if missing:
67+
raise SystemExit(f"status missing CSEP version fields {missing}: {tool}")
68+
else:
69+
raise SystemExit(f"unknown assertion kind: {kind}")
70+
PY
71+
}
72+
73+
require_command "$CSEP_BIN"
74+
require_command "$PYTHON"
75+
mkdir -p "$STATE_DIR"
76+
77+
log "using state dir: $STATE_DIR"
78+
log "checking versions"
79+
"$CSEP_BIN" --version
80+
81+
if [ "${CSEP_SMOKE_SKIP_CONFIG:-0}" != "1" ]; then
82+
log "validating config"
83+
"$CSEP_BIN" config validate >/dev/null
84+
fi
85+
86+
log "checking status version matrix"
87+
"$CSEP_BIN" status > "$STATE_DIR/status.json"
88+
assert_json "$STATE_DIR/status.json" status
89+
90+
log "checking SessionStart hook output"
91+
printf '{"cwd":"%s","session_id":"runtime-smoke-start"}\n' "$ROOT" \
92+
| "$CSEP_BIN" session-start --from-stdin --state-dir "$STATE_DIR" \
93+
> "$STATE_DIR/session-start.json"
94+
assert_json "$STATE_DIR/session-start.json" session-start
95+
96+
log "creating smoke transcripts"
97+
"$PYTHON" - "$STATE_DIR" "$ROOT" <<'PY'
98+
import json
99+
import sys
100+
from pathlib import Path
101+
102+
state = Path(sys.argv[1])
103+
root = Path(sys.argv[2])
104+
105+
codex_transcript = state / "codex-smoke.jsonl"
106+
codex_transcript.write_text(
107+
"\n".join([
108+
json.dumps({"type": "session_meta", "payload": {"id": "runtime-smoke-codex", "cwd": str(root)}}),
109+
json.dumps({"role": "user", "content": "csep runtime smoke archive needle"}),
110+
]) + "\n",
111+
encoding="utf-8",
112+
)
113+
(state / "codex-payload.json").write_text(
114+
json.dumps({
115+
"session_id": "runtime-smoke-codex",
116+
"cwd": str(root),
117+
"transcript_path": str(codex_transcript),
118+
}),
119+
encoding="utf-8",
120+
)
121+
122+
claude_project = state / "claude" / "projects" / "-runtime-smoke"
123+
claude_project.mkdir(parents=True, exist_ok=True)
124+
(claude_project / "claude-smoke.jsonl").write_text(
125+
json.dumps({
126+
"type": "user",
127+
"sessionId": "runtime-smoke-claude",
128+
"uuid": "runtime-smoke-claude-u1",
129+
"cwd": str(root),
130+
"timestamp": "2026-05-17T00:00:00.000Z",
131+
"message": {"role": "user", "content": "csep runtime smoke claude needle"},
132+
}) + "\n",
133+
encoding="utf-8",
134+
)
135+
PY
136+
137+
log "checking archive + recall"
138+
"$CSEP_BIN" session-archive --from-hook-payload "$STATE_DIR/codex-payload.json" --state-dir "$STATE_DIR" \
139+
> "$STATE_DIR/archive.json"
140+
assert_json "$STATE_DIR/archive.json" archive
141+
"$CSEP_BIN" recall "runtime smoke archive needle" --cwd "$ROOT" --state-dir "$STATE_DIR" --format json \
142+
> "$STATE_DIR/recall-codex.json"
143+
assert_json "$STATE_DIR/recall-codex.json" recall
144+
145+
log "checking Claude Code sync + recall"
146+
"$CSEP_BIN" recall sync-claude --root "$STATE_DIR/claude/projects" --state-dir "$STATE_DIR" --limit-files 1 --format json \
147+
> "$STATE_DIR/sync-claude.json"
148+
assert_json "$STATE_DIR/sync-claude.json" claude-sync
149+
"$CSEP_BIN" recall "runtime smoke claude needle" --cwd "$ROOT" --state-dir "$STATE_DIR" --format json \
150+
> "$STATE_DIR/recall-claude.json"
151+
assert_json "$STATE_DIR/recall-claude.json" recall
152+
153+
log "runtime smoke passed"
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
__all__ = ["__version__"]
22

3-
__version__ = "1.3.3"
3+
__version__ = "1.3.4"

‎tests/test_install_script.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,15 @@ def test_setup_script_delegates_to_uvx_setup():
169169
text = (ROOT / "scripts" / "setup.sh").read_text(encoding="utf-8")
170170
assert "uvx csep setup" in text
171171
assert "git clone" not in text
172+
173+
174+
def test_runtime_smoke_script_covers_release_gate_paths():
175+
path = ROOT / "scripts" / "smoke-runtime.sh"
176+
text = path.read_text(encoding="utf-8")
177+
178+
assert path.stat().st_mode & 0o111
179+
assert "session-start --from-stdin" in text
180+
assert "session-archive --from-hook-payload" in text
181+
assert "recall sync-claude" in text
182+
assert "runtime smoke archive needle" in text
183+
assert "runtime smoke claude needle" in text

0 commit comments

Comments
 (0)