Repository navigation
P3-M0 Spike · strip + cache + budget prototypes(3 plugins · fail-closed) - #1
Merged
Merged
Conversation
P3-M0 首轮 fork 侧 spike · Gate 0 CONDITIONAL PASS + Codex 批准。 本 commit 与 Hermes_AI 侧 claude/p3-m0-contracts 契约互链。 双仓 baseline SHA(冻结): - fork(本方): spike/p3-m0 @ base main @ 326bdfb - Hermes_AI: claude/p3-m0-contracts @ base aa5c3c4(PR #27 merge commit) - fixture SHA-256(跨仓一致): 0cd6ac14035292103ecf574aa6434cf3fcc4d0bdcbe9f21f32a0ba7e9ae26212 3 minimal prototypes(遵 AGENTS.md · 不改 core files · 只加 plugin dirs): plugins/spike_p3_m0_strip/ - hook: transform_llm_output(agent/turn_finalizer.py:561) - 覆盖出口 ① 普通最终响应 · 其他 4 出口 fixture 冻结不实装 - fail-open on sanitizer exception · 保留 raw canonical transcript - 使用现有 24 lifecycle hook · 无 core hook 变更 plugins/spike_p3_m0_cache/ - seam: run_llm_execution_middleware(hermes_cli/middleware.py:187 · byte-identical fresh) - fail-closed 无 tenant · fail-closed 非严格集合 request/response - 只缓存 non-streaming + tool-free + finish=stop + tenant/permission_scope 完整 - key schema v1(sha256 · 12 字段 · 含 profile_version + collection_version) - usage 拆分 origin/billable=0/saved · origin 不进 session totals - 内存 LRU cap=256 · 非 persistent · 非 cross-session - module-level API 供 fork core middleware 接线(接线由独立后续 PR) plugins/spike_p3_m0_budget/ - hooks: pre_api_request reserve + post_api_request settle(T2 · profile.yaml) - 明令不用 HERMES_DAILY_TOKEN_BUDGET 冒充 per-tenant budget - fail-open 无 tenant · fail-close BudgetExceeded 超预算 - cache hit(response.cache_meta.hit=True)· settle 不 charge - retry / fallback · reservation 每 api_request_id 独立 plugins/spike_p3_m0_fixtures/ - citation_cases.py 副本 · SHA-256 与 Hermes_AI 侧完全一致 - 17 case: 三态 + Unicode + 字节切分 + tool_result + history replay - SHA-256 校验测试锁死跨仓一致性 Test results(在 fresh-ci venv + `HERMES_HOME=temp` + `SPIKE_*_ENABLED=1`): - scripts/run_tests.sh plugins/spike_p3_m0_{strip,cache,budget} - 3 files, 49 tests passed, 0 failed, 0.6s 明确不做(M0 边界): - 生产接线 fork core middleware(需独立 PR · spike 只暴露 module API) - streaming/interim/interrupt/history-replay/tool-result 各出口实装 - CAS · 跨进程 budget 同步 · persistent cache - fork core files 改动 - M1 rebrand · Provider 正式接线 · MCP · Flutter - 转 Ready / merge 后续:双 Draft PR 互链后停 · 交 Codex 复核。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FiKVo54oX4Uv2qksAHjYsZ
…are · fail-closed Second-round Codex REQUEST CHANGES on fork PR #1 · all 9 blockers fixed: 1. Budget enforcement moved from pre_api_request hook (production swallows hook exceptions) into llm_execution middleware seam. cache plugin now registers as orchestrator: cache lookup → hit (no reserve, top-level usage=0) / miss → budget.reserve → next_call → success settle actual / exception release + re-raise. 2. Removed illegitimate config sources · no SPIKE_* env · no fork profile.yaml read · single source of truth = hermes_cli.config.load_config() reading plugins.spike_p3_m0_cache.{enabled,tenant}. 3. Strict tenant fail-closed · all 4 fields required (tenant_id, principal_id, permission_scope_version, profile_version) · non-empty · str type · whitelist chars · len ≤ 128 · rejects default fills like "system"/"0". 4. Cache response usage split · top-level usage = billable_usage = 0 · origin_usage lives only in cache_meta. 5. UTC daily bucket keyed by (tenant_id, YYYY-MM-DD) · threading.RLock · verified by 40-thread barrier concurrent reserve/settle test. 6. Strip regex backward-compat with Hermes_AI ops/audit.py::strip_sources baseline · only strips explicit [source|来源:...] / independent-line source:/来源: · rejects unconditional bare [N] stripping · fail-CLOSED on sanitizer exception returns FAIL_CLOSED_PLACEHOLDER (NOT original text). 7. Fixture SHA-256 cross-repo gate · new SHA 47155c24dfd58019d1c7abdf3870e30099da29f56fe9535d4054d16b50168666 · 8 marker_types · 24 cases · byte-split enumeration for streaming S1 verification. 8. Real PluginManager discovery tests · new shared helper spike_p3_m0_fixtures/pluginmgr_helper.py · fresh PluginManager() + tmp HERMES_HOME + monkeypatched global · discover_and_load(force=True) · 3 spike test suites all invoke via run_llm_execution_middleware (cache/budget) or invoke_hook("transform_llm_output", ...) (strip) · validates has_middleware/has_hook · does not pollute global manager · disabled-plugin negative-case coverage. 9. Documentation fixes across docs/fork/ · orchestration order added · observer-only contract for budget hook · 4-state budget clarified · M0 final-only scope for strip (2/3/4/5 fixture-frozen · skeleton). Test results: - plugins/spike_p3_m0_cache/tests: 19 passed - plugins/spike_p3_m0_budget/tests: 23 passed - plugins/spike_p3_m0_strip/tests: 63 passed - total: 105 passed in 10.72s Zero non-plugin file changes · opt-in via plugins.enabled · discovery manifest scan side-effect free. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FiKVo54oX4Uv2qksAHjYsZ
…et · fail-CLOSED · Windows CRLF Second Codex re-review on fork PR #1 · all 7 blockers addressed: §I Windows CRLF fixture SHA — .gitattributes now pins the shared strip fixture to LF; SHA gate reads canonical LF bytes so Windows autocrlf checkouts pass. Adds test_fixture_sha256_crlf_input_normalizes to assert the CRLF → LF normalization keeps the frozen SHA (47155c24…) intact. §II cache + budget control-flow decoupled — cache eligibility no longer short-circuits budget enforcement. New order in cache_and_budget_middleware: disabled → next_call budget module missing → fail-CLOSED (never call provider) unsupported api_mode → fail-CLOSED (M0 narrows to chat_completions) non-dict request → fail-CLOSED tenant context invalid → fail-CLOSED (next_call == 0) budget config invalid → fail-CLOSED (no silent None) missing api_request_id → fail-CLOSED cacheable hit → return cached, no reserve cacheable miss OR non-cacheable → reserve → next_call → success settle / exception release All fail-CLOSED paths return a non-empty assistant message with finish_reason=content_filter (Codex §V: safe, displayable, no retry). §III Strict config typing + plugin dep gate — _is_enabled is now `v is True` (rejects "false", 1, [], {}, YAML-string coercion). BudgetConfigError never becomes None; orchestrator fail-CLOSES. Removed the loose sys.modules fallback in _budget_module so cache cannot silently satisfy the dependency when the budget plugin is disabled — new test_cache_alone_without_budget_fail_closed proves it. §IV Budget ledger strict idempotency — new BudgetSettleError raised on settle without a live reservation (unknown / double / post-release); the old "settle unknown accumulates used" contract was a laundering hole and is removed. release() returns bool for idempotency. New tests: - test_concurrent_reserve_precise_success_and_exceeded_counts: 40 threads at barrier, budget = 15 × per-req → exactly 15 succeed, 25 raise BudgetExceeded, no overspend - test_utc_day_rollover_isolates_bucket: monkeypatch _today_utc to simulate Day2 checkout; yesterday's used cannot poison today. §V Production adjacency — _estimate_needed_tokens no longer silently returns 0. It prefers ctx.approx_input_tokens (>0), else estimates from messages content chars / 4 (documented M0 approximation, with an M1+ pending contract for a real tokenizer). New tests prove: streaming + over-budget → next_call == 0 tools + over-budget → next_call == 0 non-cacheable req → still reserves+settles provider exception → releases reservation, re-raises char-count fallback → catches unbudgeted large prompts §VI Strip differential + hook order risk + sys.modules snapshot — New plugins/spike_p3_m0_fixtures/hermes_ai_baseline.py vendors Hermes_AI hermes_devices/ops/audit.py::strip_sources verbatim with a byte-check test locking the pattern strings. New tests/test_strip_differential_vs_hermes_ai.py (27 tests) locks the two intentional UX divergences (\s? leading, .rstrip() vs .strip()) and proves coverage inclusion + negative parity. pluginmgr_helper adds snapshot_plugin_modules / addfinalizer cleanup; new leak test compares names + object ids after teardown. sanitize.py docstring pins the UX diffs explicitly. §VII delivered via scripts/run_tests.sh; full CI-faithful regression run reported in PR body. Test results (scripts/run_tests.sh, 8 workers, deterministic env): - test_strip_differential_vs_hermes_ai.py — 27 passed, 0.5s - test_strip_final.py — 65 passed, 5.3s - test_budget_reserve.py — 28 passed, 5.3s - test_cache_middleware.py — 35 passed, 6.4s Total: 155 passed / 0 failed in 6.4s Non-plugin footprint: only .gitattributes gained one line for the LF rule on the shared fixture. All other changes stay inside opt-in plugin directories. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FiKVo54oX4Uv2qksAHjYsZ
…t]?` newline swallow
Codex third-round §7 identified a real bug in the fork's UX-enhanced strip
regex: the leading `\s?` matches `\n` (Python `\s` includes newlines), so
`"Line A\n[source: k]Next word"` collapsed into `"Line ANext word"` — the
newline separating two lines was silently eaten by the marker match.
Fix:
- sanitize.py `_INLINE_SOURCE_RE`: `\s?` → `[ \t]?` (horizontal whitespace only)
- Docstring updated to reflect the tighter constraint and add the newline
safety proof to the diff table.
- Differential test pattern-lock:
`test_fork_inline_pattern_is_baseline_plus_leading_horizontal_space`
now asserts `[ \t]?` rather than the previous `\s?`.
New negative-proof tests (8) locking Codex §7 requirements:
1. test_leading_space_does_not_eat_newline_before_marker
— `"Line A\n[source: k]Next"` → `"Line A\nNext"` (no line concatenation)
2. test_leading_space_does_not_eat_double_newline_before_marker
— paragraph separator preserved
3. test_leading_space_only_absorbs_single_space_or_tab
— parametrized: `" "` and `"\t"` absorbed; `"\n"` and `"\r"` preserved
4. test_whitespace_diff_does_not_widen_source_deletion_range
— non-source `[normal]` bracket preserved next to `[source: k]`
5. test_trailing_whitespace_rstrip_scope_matches_baseline_content
— trailing spaces / newlines both stripped, content byte-identical
6. test_leading_whitespace_only_diff_between_baseline_and_fork
— leading indent preserved by fork, stripped by baseline; content matches
7. test_middle_line_source_marker_preserves_surrounding_newlines
— `"Line 1\nLine 2 [source: k]\nLine 3"` → both `\n` preserved
8. test_marker_at_start_of_line_preserves_prev_line_terminator
— with and without leading horizontal whitespace variants
Also switched two docstrings to raw-string form (r"...") to silence
Python 3.13 DeprecationWarning on `\s` / `\[` escapes.
Test results (scripts/run_tests.sh, 8 workers):
test_strip_differential_vs_hermes_ai.py — 35 passed (was 27, +8 §7 tests)
test_strip_final.py — 65 passed
test_budget_reserve.py — 28 passed
test_cache_middleware.py — 35 passed
Total: 163 passed / 0 failed in 3.6s
The `\s?` → `[ \t]?` change is UX-neutral for existing fixture cases
(no fixture case had `\n` immediately before a source marker) but removes
the latent line-concatenation bug against real streaming/multiline
provider output.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FiKVo54oX4Uv2qksAHjYsZ
bri12afdsarker96-lgtm
marked this pull request as ready for review
August 10, 2026 14:16
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
P3-M0 最终合入报告
a5ec1785b902e560c3cb2d3964dbdc795c2478ea0170c9bc70c9309e670495e1466eae3a1e4b7029main@326bdfb47155c24dfd58019d1c7abdf3870e30099da29f56fe9535d4054d16b50168666验证
后续
P3-M1 基线分支:
feature/p3-m1-baseline@a5ec1785。