Skip to content

feat(rebuild_log): instrument UserPromptSubmit path (#288 phase-1a) - #358

Merged
robotrocketscience merged 2 commits into
mainfrom
feat/issue-288-rebuild-log-ups
May 2, 2026
Merged

feat(rebuild_log): instrument UserPromptSubmit path (#288 phase-1a)#358
robotrocketscience merged 2 commits into
mainfrom
feat/issue-288-rebuild-log-ups

Conversation

@robotrocketscience

@robotrocketscience robotrocketscience commented May 2, 2026

Copy link
Copy Markdown
Owner

Summary

Phase-1a of #288 wired the per-rebuild diagnostic log only into rebuild_v14, which fires from pre_compact(). The high-frequency rebuild call site is user_prompt_submit(), which calls search_for_prompt()retrieve() directly and never reaches rebuild_v14. Result: no rebuild_logs/<session>.jsonl files appeared anywhere on disk under normal use, so phase-1b (operator-week of captured logs) was unreachable.

Verified empty before this PR: find ~ -path '*/aelfrice/rebuild_logs/*.jsonl' returned nothing despite weeks of UPS hook activity.

Changes

  • context_rebuilder.py — new record_user_prompt_submit_log helper. Synthesises a single RecentTurn from the prompt, marks survivors of content-hash dedup as packed, dropped duplicates as dropped with content_hash_collision_with:<survivor>. Score fields are None (_empty_scores) — BM25 / posterior decomposition is not exposed at this layer; locking the schema in phase-1a means phase-2 ranker work fills the same fields without a log-format migration. Honors the same AELFRICE_REBUILD_LOG=0 env opt-out and [rebuild_log] enabled = false TOML opt-out as the PreCompact path; reuses _append_rebuild_log_record for size-cap behaviour.
  • hook.py — call the helper from user_prompt_submit() after dedup, before format. Path resolution mirrors _rebuild_and_format: <git-common-dir>/aelfrice/rebuild_logs/<session_id>.jsonl. Fail-soft: any path-resolution or write error logs to stderr and never breaks the hook.
  • tests/test_rebuild_log_user_prompt_submit.py — 9 tests covering schema parity with PreCompact, dedup-drop visibility, env opt-out, TOML opt-out, missing-session-id no-op, in-memory store no-op, and lock-level passthrough.
  • docs/rebuild_eval_harness.md — record both call sites; note that the original spec assumed all rebuild call sites went through rebuild_v14, which the UPS path does not.

Verification

  • uv run pytest tests/test_rebuild_log_user_prompt_submit.py — 9 passed
  • uv run pytest tests/test_rebuild_log.py tests/test_hook_user_prompt_submit.py tests/test_hook_pre_compact.py tests/test_audit_rebuild_log.py — 43 passed (no regressions in the existing rebuild_log / UPS / PreCompact / audit suites)
  • Both commits show G (good signature) under git log --format='%h %G? %s'.

Test plan

  • Reviewer confirms schema parity (UPS record can be consumed by scripts/audit_rebuild_log.py from feat(scripts): rebuild_log audit script (#288 phase-1c) #350 with no changes).
  • Reviewer confirms phase-1b operator-week countdown should now restart from the merge of this PR (the previous "calendar gate" was producing no data).
  • CI green.

Closes #288 phase-1a follow-up. Phase-1b (operator-week wait) and phase-1c audit script are unchanged.

Summary by Sourcery

Instrument the high-frequency UserPromptSubmit retrieval path to emit rebuild_log entries consistent with the existing PreCompact logging schema, enabling phase-1b data collection.

New Features:

  • Add a helper to record rebuild_log entries for UserPromptSubmit retrievals using the same schema and opt-outs as the existing rebuild_v14 logging.

Enhancements:

  • Wire the UserPromptSubmit hook to emit per-session rebuild logs in a fail-soft manner without impacting hook behavior on I/O errors.
  • Document both rebuild logging call sites and clarify how the UPS path bypassed rebuild_v14 in the original design.

Tests:

  • Add end-to-end and unit tests ensuring UPS rebuild logs are written when expected, respect env/TOML opt-outs, correctly mark deduplicated candidates, and no-op when logging is disabled or inapplicable.

@sourcery-ai

sourcery-ai Bot commented May 2, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds rebuild_log instrumentation for the high-frequency UserPromptSubmit retrieval path, sharing schema and config with the existing PreCompact/rebuild_v14 logging, and verifies it with focused tests and doc updates.

Sequence diagram for UserPromptSubmit rebuild_log instrumentation

sequenceDiagram
    actor User
    participant GitHook as user_prompt_submit
    participant Search as search_for_prompt
    participant Retrieve as retrieve
    participant HookLog as _emit_user_prompt_submit_rebuild_log
    participant Ctx as record_user_prompt_submit_log
    participant FS as rebuild_logs_dir

    User->>GitHook: invoke user_prompt_submit(prompt)
    GitHook->>Search: search_for_prompt(prompt)
    Search->>Retrieve: retrieve(prompt)
    Retrieve-->>GitHook: hits (pre_dedup)
    GitHook->>GitHook: compute n_returned, n_unique, n_l0, n_l1
    GitHook->>GitHook: hits_pre_dedup = list(hits)
    alt config.collapse_duplicate_hashes
        GitHook->>GitHook: hits = _dedup_by_content_hash(hits)
    end

    GitHook->>HookLog: _emit_user_prompt_submit_rebuild_log(prompt, session_id, hits_pre_dedup, hits_post_dedup=hits)

    HookLog->>HookLog: if not session_id or db_path()==":memory:": return
    HookLog->>HookLog: log_path = _rebuild_log_dir_for_db(db_path()) / session_id.jsonl
    HookLog->>Ctx: record_user_prompt_submit_log(prompt, session_id, hits_pre_dedup, hits_post_dedup, log_path, enabled, stderr)

    Ctx->>Ctx: check enabled, env opt-out, log_path, hits_pre_dedup
    Ctx->>Ctx: surviving_ids, survivor_by_hash from hits_post_dedup
    Ctx->>Ctx: build candidates with packed/dropped and reasons
    Ctx->>Ctx: build pack_summary
    Ctx->>Ctx: synthetic_turn = RecentTurn(role=user, text=prompt, session_id)
    Ctx->>Ctx: record = _build_rebuild_log_record([...synthetic_turn...], candidates, pack_summary)
    Ctx->>FS: _append_rebuild_log_record(log_path, record)

    FS-->>Ctx: append ok
    Ctx-->>HookLog: return
    HookLog-->>GitHook: return (non-fatal on error)
    GitHook->>GitHook: total_chars, body = _format_hits(hits)
    GitHook-->>User: formatted response
Loading

Class diagram for new UPS rebuild_log helpers and related types

classDiagram
    class ContextRebuilder {
        _append_rebuild_log_record(log_path, record, stderr)
        _rebuild_log_disabled_via_env() bool
        _empty_scores() dict
        _belief_lock_level_for_log(belief) int
        _build_rebuild_log_record(recent_turns, session_id, candidates, pack_summary) dict
        record_user_prompt_submit_log(prompt, session_id, hits_pre_dedup, hits_post_dedup, log_path, enabled, stderr) void
    }

    class Hook {
        user_prompt_submit(prompt, config, session_id, stderr) int
        _dedup_by_content_hash(hits) list~Belief~
        _emit_user_prompt_submit_rebuild_log(prompt, session_id, hits_pre_dedup, hits_post_dedup, stderr) void
        db_path() Path
    }

    class RecentTurn {
        +role: str
        +text: str
        +session_id: str
    }

    class Belief {
        +id: str
        +content: str
        +lock_level: int
    }

    class RebuilderConfig {
        +rebuild_log_enabled: bool
    }

    class Filesystem {
        +rebuild_logs_dir: Path
    }

    Hook --> "*" Belief : uses hits_pre_dedup / hits_post_dedup
    Hook --> Hook : _dedup_by_content_hash
    Hook --> ContextRebuilder : calls record_user_prompt_submit_log
    Hook --> RebuilderConfig : load_rebuilder_config
    Hook --> Filesystem : _rebuild_log_dir_for_db, log_path

    ContextRebuilder --> RecentTurn : constructs synthetic_turn
    ContextRebuilder --> Belief : inspects id, content, lock_level
    ContextRebuilder --> Filesystem : _append_rebuild_log_record

    RebuilderConfig --> ContextRebuilder : rebuild_log_enabled
Loading

Flow diagram for record_user_prompt_submit_log decision and record building

flowchart TD
    A["record_user_prompt_submit_log<br/>(prompt, session_id,<br/>hits_pre_dedup, hits_post_dedup,<br/>log_path, enabled, stderr)"] --> B{enabled?}
    B -- no --> Z1[return]
    B -- yes --> C{"env opt-out<br/>_rebuild_log_disabled_via_env()?"}
    C -- yes --> Z2[return]
    C -- no --> D{log_path is None?}
    D -- yes --> Z3[return]
    D -- no --> E{"hits_pre_dedup empty?"}
    E -- yes --> Z4[return]
    E -- no --> F[build surviving_ids from hits_post_dedup]
    F --> G[build survivor_by_hash from hits_post_dedup]
    G --> H[init candidates list and n_dropped_by_dedup]
    H --> I[for each belief in hits_pre_dedup with rank]
    I --> J{belief.id in surviving_ids?}
    J -- yes --> K["decision = packed<br/>reason = None"]
    J -- no --> L["decision = dropped<br/>reason = content_hash_collision*<br/>increment n_dropped_by_dedup"]
    K --> M[append candidate dict with empty scores and lock_level]
    L --> M[append candidate dict with empty scores and lock_level]
    M --> N{more beliefs?}
    N -- yes --> I
    N -- no --> O["compute pack_summary<br/>n_candidates, n_packed,<br/>n_dropped_by_floor=0,<br/>n_dropped_by_dedup,<br/>n_dropped_by_budget=0,<br/>total_chars_packed"]
    O --> P["synthetic_turn = RecentTurn(role=user,<br/>text=prompt, session_id=session_id)"]
    P --> Q["record = _build_rebuild_log_record(<br/>recent_turns=[synthetic_turn],<br/>session_id, candidates, pack_summary)"]
    Q --> R["_append_rebuild_log_record(log_path, record, stderr)"]
    R --> Z5[return]
Loading

File-Level Changes

Change Details Files
Introduce a reusable helper to emit rebuild_log entries for UserPromptSubmit retrievals with schema parity to existing rebuild_v14 logging.
  • Add record_user_prompt_submit_log to construct a synthetic RecentTurn from the user prompt and feed it through _build_rebuild_log_record
  • Compute candidates from pre-dedup hits, marking survivors as packed and duplicates as dropped with content_hash_collision_with: reasons
  • Populate pack_summary fields, including dedup drop counts and total_chars_packed while leaving floor/budget drops at zero pending later ranker wiring
  • Honor existing env and TOML opt-outs and use _append_rebuild_log_record for size-capped writes, no-oping when disabled, no path, or no candidates
src/aelfrice/context_rebuilder.py
Wire UserPromptSubmit to the rebuild_log helper with fail-soft behavior and consistent log file layout.
  • Capture hits_pre_dedup before optional content-hash dedup and call a new _emit_user_prompt_submit_rebuild_log helper after dedup but before formatting
  • Resolve per-session log_path under <db_dir>/rebuild_logs/<session_id>.jsonl using _rebuild_log_dir_for_db and load_rebuilder_config for the enabled flag
  • Skip logging when session_id is missing or DB is :memory:, and swallow/log any import/path/write exceptions to stderr without affecting hook success
src/aelfrice/hook.py
Document and test the new UserPromptSubmit rebuild_log path to ensure schema parity, opt-outs, and behavior in edge cases.
  • Update rebuild_eval_harness.md to describe both rebuild_v14 (PreCompact) and UserPromptSubmit logging call sites and emphasize shared schema/behavior
  • Add tests that run user_prompt_submit end-to-end to assert log creation, no-log conditions (no hits, env/TOML opt-out, missing session_id, in-memory DB), and compatibility with existing audit tooling
  • Unit-test record_user_prompt_submit_log to verify dedup drop marking, lock_level passthrough, disabled/log_path-none no-ops, and the expected pack_summary fields
docs/rebuild_eval_harness.md
tests/test_rebuild_log_user_prompt_submit.py

Assessment against linked issues

Issue Objective Addressed Explanation
#288 Implement Layer 1 per-rebuild diagnostic logging that records, for each rebuild invocation, a JSONL row per session under <project>/.git/aelfrice/rebuild_logs/ with timestamp, session_id, recent turns (or hash), extracted query/entities/intents, top-K candidate beliefs with scores, which candidates were packed vs dropped and why.
#288 Implement Layer 2 fixed-corpus precision harness using a labeled corpus of (query, expected-belief-set) pairs to compute precision@K and recall@K for ranker variants. The PR only extends the existing per-rebuild logging to the UserPromptSubmit path and updates documentation/tests. It does not add any fixed corpus, labeling pipeline, or evaluation harness computing precision@K/recall@K.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented May 2, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@robotrocketscience has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 43 minutes and 49 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7a58e4d8-d09d-439d-9374-a2e83fa10f0b

📥 Commits

Reviewing files that changed from the base of the PR and between c102590 and 565980a.

📒 Files selected for processing (4)
  • docs/rebuild_eval_harness.md
  • src/aelfrice/context_rebuilder.py
  • src/aelfrice/hook.py
  • tests/test_rebuild_log_user_prompt_submit.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-288-rebuild-log-ups

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 43 minutes and 49 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@robotrocketscience robotrocketscience added the attn:review Needs review (PR open, awaiting reviewer) label May 2, 2026
@github-actions github-actions Bot added the attn:merge-conflict PR branch needs rebase label May 2, 2026
@github-actions

github-actions Bot commented May 2, 2026

Copy link
Copy Markdown

This PR is now behind main. Rebase locally so your commit signatures stay intact:

git fetch origin && git checkout 'feat/issue-288-rebuild-log-ups' && git rebase origin/main
# resolve conflicts if any, then
git push --force-with-lease

Auto-rebase was removed because the bot has no signing key; rebasing as the bot strips author signatures and the required_signatures rule on main then blocks the merge. See #341.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Toug:2026-05-02T21:33:48Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:Toug:2026-05-02T21:34:14Z]

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • In record_user_prompt_submit_log, you recompute a SHA1 digest of b.content both when building survivor_by_hash and again in the pre-dedup loop; consider computing it once per belief (or reusing b.content_hash if it’s already a content-based digest) to avoid redundant work and reduce the risk of future divergence between content and the hash source.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `record_user_prompt_submit_log`, you recompute a SHA1 digest of `b.content` both when building `survivor_by_hash` and again in the pre-dedup loop; consider computing it once per belief (or reusing `b.content_hash` if it’s already a content-based digest) to avoid redundant work and reduce the risk of future divergence between `content` and the hash source.

## Individual Comments

### Comment 1
<location path="src/aelfrice/hook.py" line_range="665-685" />
<code_context>
             n_unique = len(unique_hashes)
             n_l0 = sum(1 for h in hits if h.lock_level == LOCK_USER)
             n_l1 = n_returned - n_l0
+            hits_pre_dedup = list(hits)
             # AC6: optional dedup before formatting.
             if config.collapse_duplicate_hashes:
                 hits = _dedup_by_content_hash(hits)
+            # #288 phase-1a extension: emit one rebuild_log row per
+            # UPS retrieval. Without this the high-frequency rebuild
+            # call site produces no log; phase-1b operator-week data
+            # collection depends on it.
+            _emit_user_prompt_submit_rebuild_log(
+                prompt=prompt,
+                session_id=session_id,
</code_context>
<issue_to_address>
**suggestion (performance):** Avoid copying hits when dedup is disabled to reduce overhead on the hot path.

`hits_pre_dedup = list(hits)` runs even when `collapse_duplicate_hashes` is false, adding avoidable overhead on high-throughput paths. Consider only creating this list inside the `if config.collapse_duplicate_hashes:` block (passing `hits_post_dedup=hits` and `hits_pre_dedup=hits` when dedup is disabled), or let `_emit_user_prompt_submit_rebuild_log` treat `hits_post_dedup` as identical to `hits_pre_dedup` when one of them is `None`.

```suggestion
            n_unique = len(unique_hashes)
            n_l0 = sum(1 for h in hits if h.lock_level == LOCK_USER)
            n_l1 = n_returned - n_l0
            # AC6: optional dedup before formatting.
            # Avoid copying hits on the hot path when dedup is disabled.
            hits_pre_dedup = hits
            if config.collapse_duplicate_hashes:
                hits_pre_dedup = list(hits)
                hits = _dedup_by_content_hash(hits)
            # #288 phase-1a extension: emit one rebuild_log row per
            # UPS retrieval. Without this the high-frequency rebuild
            # call site produces no log; phase-1b operator-week data
            # collection depends on it.
            _emit_user_prompt_submit_rebuild_log(
                prompt=prompt,
                session_id=session_id,
                hits_pre_dedup=hits_pre_dedup,
                hits_post_dedup=hits,
                stderr=serr,
            )
            # total_chars measured post-collapse (what is actually injected).
            total_chars = sum(len(h.content) for h in hits)
            body = _format_hits(hits)
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/aelfrice/hook.py
Comment on lines 665 to 685
n_unique = len(unique_hashes)
n_l0 = sum(1 for h in hits if h.lock_level == LOCK_USER)
n_l1 = n_returned - n_l0
hits_pre_dedup = list(hits)
# AC6: optional dedup before formatting.
if config.collapse_duplicate_hashes:
hits = _dedup_by_content_hash(hits)
# #288 phase-1a extension: emit one rebuild_log row per
# UPS retrieval. Without this the high-frequency rebuild
# call site produces no log; phase-1b operator-week data
# collection depends on it.
_emit_user_prompt_submit_rebuild_log(
prompt=prompt,
session_id=session_id,
hits_pre_dedup=hits_pre_dedup,
hits_post_dedup=hits,
stderr=serr,
)
# total_chars measured post-collapse (what is actually injected).
total_chars = sum(len(h.content) for h in hits)
body = _format_hits(hits)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (performance): Avoid copying hits when dedup is disabled to reduce overhead on the hot path.

hits_pre_dedup = list(hits) runs even when collapse_duplicate_hashes is false, adding avoidable overhead on high-throughput paths. Consider only creating this list inside the if config.collapse_duplicate_hashes: block (passing hits_post_dedup=hits and hits_pre_dedup=hits when dedup is disabled), or let _emit_user_prompt_submit_rebuild_log treat hits_post_dedup as identical to hits_pre_dedup when one of them is None.

Suggested change
n_unique = len(unique_hashes)
n_l0 = sum(1 for h in hits if h.lock_level == LOCK_USER)
n_l1 = n_returned - n_l0
hits_pre_dedup = list(hits)
# AC6: optional dedup before formatting.
if config.collapse_duplicate_hashes:
hits = _dedup_by_content_hash(hits)
# #288 phase-1a extension: emit one rebuild_log row per
# UPS retrieval. Without this the high-frequency rebuild
# call site produces no log; phase-1b operator-week data
# collection depends on it.
_emit_user_prompt_submit_rebuild_log(
prompt=prompt,
session_id=session_id,
hits_pre_dedup=hits_pre_dedup,
hits_post_dedup=hits,
stderr=serr,
)
# total_chars measured post-collapse (what is actually injected).
total_chars = sum(len(h.content) for h in hits)
body = _format_hits(hits)
n_unique = len(unique_hashes)
n_l0 = sum(1 for h in hits if h.lock_level == LOCK_USER)
n_l1 = n_returned - n_l0
# AC6: optional dedup before formatting.
# Avoid copying hits on the hot path when dedup is disabled.
hits_pre_dedup = hits
if config.collapse_duplicate_hashes:
hits_pre_dedup = list(hits)
hits = _dedup_by_content_hash(hits)
# #288 phase-1a extension: emit one rebuild_log row per
# UPS retrieval. Without this the high-frequency rebuild
# call site produces no log; phase-1b operator-week data
# collection depends on it.
_emit_user_prompt_submit_rebuild_log(
prompt=prompt,
session_id=session_id,
hits_pre_dedup=hits_pre_dedup,
hits_post_dedup=hits,
stderr=serr,
)
# total_chars measured post-collapse (what is actually injected).
total_chars = sum(len(h.content) for h in hits)
body = _format_hits(hits)

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Gylf:2026-05-02T21:35:39Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Review (Gylf): diff is clean — discretion grep empty, both commits signed, CI green, no text conflict per git merge-tree github/main pr-358 (only #290 phase-3 store/doctor work landed on main, disjoint from this PR's context_rebuilder/hook/rebuild_log changes).

Blocked only on rebase: git merge-base --is-ancestor github/main pr-358 is false. attn:merge-conflict label is correct. Once rebased onto current github/main and force-pushed, this is FF-mergeable.

Releasing review claim.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:Gylf:2026-05-02T21:36:55Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Setr:2026-05-02T21:56:10Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Kulili:2026-05-02T21:56:40Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:Kulili:2026-05-02T21:56:44Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Toug:2026-05-02T21:57:03Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Review (Setr): code LGTM, blocked on rebase.

Substantive review

  • Schema parity with PreCompact path is clean — synthesises a RecentTurn so _build_rebuild_log_record applies unchanged, and _empty_scores() keeps the on-disk schema stable for phase-2 ranker work.
  • Dedup-drop bookkeeping is correct: survivors marked packed, drops carry content_hash_collision_with:<survivor> reason, n_dropped_by_dedup reflects the count. Floor/budget held at zero with the rationale documented (UPS path has no visibility into ranker internals — fair).
  • Fail-soft wrapper in hook.py matches the rest of the rebuild_log code: any error → one stderr line, hook proceeds. Correct posture for a diagnostic side-channel.
  • Opt-outs honored: enabled flag, AELFRICE_REBUILD_LOG=0 env, missing session_id, :memory: DB, empty hits_pre_dedup all no-op.
  • 9 tests cover schema parity, dedup-drop, env+TOML opt-out, missing session_id, in-memory, lock passthrough.
  • Discretion grep clean.
  • CI green (one-shot CANCELLED runs were superseded by SUCCESS).

Blocker (not author's diff — branch state)

  • git merge-base --is-ancestor github/main github/feat/issue-288-rebuild-log-ups → REBASE-NEEDED. mergeStateStatus=BLOCKED. attn:merge-conflict already labeled.
  • Once rebased onto current main and pushed, this is a fast-forward merge. Releasing the review claim so whichever session is next on the queue can FF-push after the rebase.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:Setr:2026-05-02T21:57:41Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Review notes (Toug):

  • CI: all required checks green. Signed commits verified (%G? = G on both).
  • Discretion grep: clean.
  • Code: fail-soft wrapper in hook.py is correct (catches anything, traces to stderr, never propagates). record_user_prompt_submit_log in context_rebuilder.py reuses _build_rebuild_log_record / _empty_scores so the on-disk schema stays identical to the PreCompact path. Synthetic RecentTurn is a reasonable adapter. Pre-dedup snapshot is taken before _dedup_by_content_hash mutates hits, so candidate set is correct. Holding n_dropped_by_floor / n_dropped_by_budget at 0 with the inline rationale is the right call for phase-1a.
  • Tests: 309 lines covering enable/disable, env opt-out, empty pre-dedup, dedup-reason synthesis, fail-soft.

Blocker: branch is not fast-forward against github/maingit merge-base --is-ancestor github/main github/feat/issue-288-rebuild-log-ups returns false. attn:merge-conflict label is accurate even though GH calls the PR MERGEABLE (3-way merge would work, but repo policy is FF push).

Action requested: rebase onto current github/main and force-push. After that the PR is ready to merge.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:Toug:2026-05-02T21:57:59Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Setr:2026-05-02T22:06:48Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Gylf:2026-05-02T22:07:00Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Setr review pass — diff is clean (discretion grep empty), CI green on the latest staging-gate retry, but branch is non-FF against main (behind by v1.6.0 release + ~10 commits). attn:merge-conflict is correctly set; releasing review claim until rebase lands.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:Setr:2026-05-02T22:07:56Z]

@robotrocketscience robotrocketscience removed the attn:review Needs review (PR open, awaiting reviewer) label May 2, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:Gylf:2026-05-02T22:08:41Z]

Phase-1a wired the per-rebuild diagnostic log only into rebuild_v14,
which fires from PreCompact. The high-frequency rebuild call site is
user_prompt_submit, which calls search_for_prompt -> retrieve()
directly and never reaches rebuild_v14. Result: rebuild_logs/ stayed
empty under normal session load and phase-1b operator-week data
collection could not begin.

Add record_user_prompt_submit_log in context_rebuilder.py: synthesise
a single RecentTurn from the prompt so the existing schema helpers
apply unchanged, mark survivors of content-hash dedup as 'packed' and
the dropped duplicates as 'dropped' with reason
'content_hash_collision_with:<survivor>'. Score fields are None per
_empty_scores -- the BM25 / posterior decomposition is not exposed at
this layer, locking the schema in phase-1a means phase-2 ranker work
fills the same fields without a log-format migration.

Wire from hook.user_prompt_submit() after dedup. Same 5MB cap, same
AELFRICE_REBUILD_LOG=0 / [rebuild_log] enabled=false opt-outs as the
PreCompact path. Fail-soft: any path-resolution or write error logs
to stderr and never breaks the hook.
Update 'Where the write hook lives' to list both rebuild_v14 and
user_prompt_submit, and call out that the original spec assumed all
rebuild call sites went through rebuild_v14 -- which the
UserPromptSubmit path does not. Records that bypass turned phase-1a
into a no-op under normal session load and is what motivated the UPS
wiring.
@robotrocketscience
robotrocketscience force-pushed the feat/issue-288-rebuild-log-ups branch from 8db9421 to 565980a Compare May 2, 2026 22:16
@robotrocketscience robotrocketscience added attn:review Needs review (PR open, awaiting reviewer) and removed attn:merge-conflict PR branch needs rebase labels May 2, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

Rebased onto current main (was ~12 commits behind). Force-pushed (8db9421...565980a). Both commits re-signed (G), local pytest on touched file (9/9), discretion grep clean. Awaiting review by another session.

— Setr

@robotrocketscience robotrocketscience added the author-Setr PR coordination mutex label May 2, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Gylf:2026-05-02T22:18:46Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Kulili:2026-05-02T22:18:49Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Toug:2026-05-02T22:18:53Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:Kulili:2026-05-02T22:18:54Z]

@robotrocketscience
robotrocketscience merged commit 565980a into main May 2, 2026
16 checks passed
@robotrocketscience
robotrocketscience deleted the feat/issue-288-rebuild-log-ups branch May 2, 2026 22:19
@robotrocketscience

Copy link
Copy Markdown
Owner Author

Merged via FF push (565980a). Schema parity verified, fail-soft wrapper good, opt-outs honored, all CI green. Reviewer: Gylf.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:Gylf:2026-05-02T22:19:34Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:Toug:2026-05-02T22:20:10Z]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

attn:review Needs review (PR open, awaiting reviewer) author-Setr PR coordination mutex

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rebuild redesign: eval harness — per-rebuild log + fixed-corpus precision

1 participant