Skip to content

feat(core): suite-2 uplift read (+40pp) + scope-filter blindness, capture gap, Redis drift - #175

Merged
bejranonda merged 3 commits into
mainfrom
fix/scope-filter-diagnosis-and-redis-drift
Jul 28, 2026
Merged

feat(core): suite-2 uplift read (+40pp) + scope-filter blindness, capture gap, Redis drift#175
bejranonda merged 3 commits into
mainfrom
fix/scope-filter-diagnosis-and-redis-drift

Conversation

@bejranonda

@bejranonda bejranonda commented Jul 28, 2026

Copy link
Copy Markdown
Owner

0. Suite-2 generation-uplift read — +40pp, and where injection pays

Pre-registration committed at f62895b before any arm ran. Five
corpus-dependent tasks; treatment arm injected verbatim from the live KRA path
rather than hand-written — so this measures retrieval and generation together.

Convention applied (n=5)
Control 3/5 (60%)
Treatment (live-KRA) 5/5 (100%)
Paired difference +40pp, 0 regressions

The aggregate is the less interesting half. What separates a flip from a tie is
whether the convention is locally arbitrary:

# Convention C T
1 force-dynamic on env-reading server component flip
2 x-forwarded-host instead of req.url tie
3 mount-gate window.location tie
4 @brain/core/format-relative subpath flip
5 no baked numbers in i18n strings tie
  • Flips are unguessable. Control emitted no static-rendering opt-out of any
    kind (task 1) — the v0.14.0 bug verbatim — and hand-rolled its own
    formatRelative (task 4), exactly the divergence v0.15.0 consolidated away.
  • Ties are good craft. Control reached for x-forwarded-host unprompted,
    mount-gated its window read, and used {count} placeholders without being told.

So injection pays where the convention is arbitrary and ties where it is craft
which predicts where capture effort returns most. Suite 1's four ties had the same
cause; two independent suites now agree on the mechanism.

Caveat that matters most: the five rules were verified retrievable from a
project-scoped session first, to control for #174 below. Roughly half the corpus
is invisible to such a session today, so an uncurated run would score lower.
This measures the Brain with its known retrieval defect controlled for.


What

Three findings from building the second generation-uplift suite. That suite takes
its treatment-arm injection from the live KRA path rather than a hand-written
block — the change you make when you want to measure the product instead of the
mechanism. The first probe returned a null, and chasing it surfaced more than the
benchmark itself would have.

1. Capture gap — partially closed

Probed three documented repo rules. Two were absent from the Brain entirely
force-dynamic (GUIDELINES §10) and the package-boundary rule — despite each
carrying a class-of-bug entry here and real debugging cost. The Oracle said so
outright: "isn't captured in your current knowledge base."

Proven to be capture, not retrieval: taught force-dynamic, re-ran the
identical prompt, and it came back ranked first. KRA ranked it correctly the
moment it existed. Nine rules backfilled from GUIDELINES §9/§10 and AGENTS.md.

This inverts the diagnosis I opened with. I'd recommended work on retrieval
quality (NDCG 0.3075) — but retrieval can only rank what capture put there.

2. scope: "user" is invisible outside its capture project — open, #174

buildKnowledgeWhere and buildRawProjectFilter (the pgvector path kra.ts and
oracle.ts actually use) resolve visibility as ownerProjectId = $activeProject OR (ownerProjectId IS NULL AND ownerUserId = $user). The scope column is
never consulted.
A row with scope: "user" and a non-null ownerProjectId
matches neither arm — and that is exactly what brain_teach_knowledge produces by
default in a project-bound session.

Controlled reproduction, identical prompt:

Path Result
unscoped brain_retrieve_knowledge best-matching item #1 at 0.9009, exact trigger match, 100% success / 11 uses
brain_start_session(projectName:"BrainPlatform") absent — injected a tangential i18n rule instead

Scale: 101 active rows in Default vs 100 in Brain Platform; 117 rows
repo-wide are scope='user' with a non-null project.

Deliberately not fixed here. Both candidate fixes change what every user sees;
that's a design call, written up in #174 rather than taken unilaterally.

3. Redis config drift — doc fixed

.env.example claimed "Production docker-compose provides redis://redis:6379".
It does not — compose only passes ${REDIS_URL:-} through. Consequences on the
reference instance: a healthy deploy-redis-1 has run for weeks with nothing
connected; rate-limit state lives in an in-process Map that resets on every
deploy
, silently clearing daily caps like RATE_LIMIT_MEETING_EXTRACT_PER_DAY;
and the get-then-set race fixed in #173 was therefore a live production
bypass, not a dev-only concern.

Test plan

  • scripts/check-doc-refs.sh passes locally (DOC_REF_CEILING=172).
  • Capture-gap claim verified both ways — Oracle stated the rule was absent,
    and the before/after on an identical prompt moved it from absent to rank 1.
  • Scope-filter claim verified three ways: read both helpers, confirmed
    kra.ts:172 uses the raw one, and reproduced the miss with a controlled
    scoped-vs-unscoped comparison.
  • Row counts from read-only psql against prod (SELECT only, permitted
    autonomously).
  • .env.example claim checked against deploy/docker-compose.yml:147-149.
  • Relying on CI for typecheck / test / build — no code changes in this PR
    (docs + .env.example only), but CI is still the gate.
  • Operator action: set REDIS_URL="redis://redis:6379" in the live .env
    and redeploy. .env is gitignored — no PR can do this.
  • Reviewer: decide the scope: "user" knowledge is invisible outside its capture project (117 rows, ~half the corpus) #174 fix direction (honour scope in the filter vs.
    null out ownerProjectId on user-scope rows).

🤖 Generated with Claude Code

https://claude.ai/code/session_01Rqj5y9fT3XPUqnqKyustp9

…g drift

Three findings from building the second generation-uplift suite. That
suite takes its treatment-arm injection from the LIVE KRA path rather
than a hand-written block; the first probe returned a null, and chasing
it surfaced more than the benchmark would have.

1. Capture gap (partially closed). Two of three probed repo rules were
   absent from the Brain entirely — force-dynamic and the package
   boundary — despite each having a class-of-bug entry and real
   debugging cost. Proven to be capture, not retrieval: teaching
   force-dynamic and re-running the identical prompt ranked it FIRST.
   Nine rules backfilled; the rest of GUIDELINES is unswept.

2. scope-filter blindness (open). buildKnowledgeWhere and
   buildRawProjectFilter resolve visibility purely on ownerProjectId
   and never consult the scope column, so scope:"user" rows carrying a
   non-null ownerProjectId are invisible outside their capture project
   — 117 rows. Measured: the best-matching item in the corpus (0.9009
   similarity, 11 successful uses, exact trigger match) is returned
   first by unscoped retrieval and not at all by the project-scoped
   session path. ~Half the corpus (101 in Default vs 100 in Brain
   Platform) is invisible to a BrainPlatform session. Left open
   deliberately: the fix changes what every user sees.

3. Redis drift (doc fixed). .env.example claimed "Production
   docker-compose provides redis://redis:6379". It does not — compose
   only passes ${REDIS_URL:-} through. So a healthy redis container has
   run for weeks with nothing connected, rate-limit state lives in an
   in-process Map that resets on every deploy, and the get-then-set
   race fixed in #173 was a live production bypass rather than a
   dev-only concern.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rqj5y9fT3XPUqnqKyustp9
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Updated Redis deployment guidance and added a Known Issues entry documenting corpus capture gaps, user-scope visibility behavior, measurements, backfill notes, and corrected Compose configuration details.

Changes

Documentation updates

Layer / File(s) Summary
Redis deployment guidance
.env.example
Explains empty REDIS_URL behavior, Docker Compose pass-through, rate-limit resets, and the recommended server Redis URL.
Corpus visibility investigation
docs/KNOWN_ISSUES.md
Documents corpus capture and scope: "user" visibility findings, measured impact, backfill notes, and corrected Redis documentation.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Possibly related issues

  • Issue 174 — Documents the same scope: "user" visibility defect and root cause.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately summarizes the main changes: suite-2 uplift findings, scope-filter blindness, capture gap, and Redis docs drift.
Description check ✅ Passed The description covers the change, rationale, test plan, and gate expectations, though the template's explicit Gates section is not formatted separately.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/scope-filter-diagnosis-and-redis-drift

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

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/KNOWN_ISSUES.md`:
- Line 403: Update the issue heading and wording in the documented entry to
state that the visibility blind spot affects project-scoped retrieval or
project-scoped sessions, not user-scope retrieval generally. Preserve the
existing evidence and distinction that unscoped retrieval can return these rows,
while keeping the open design-call status unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d73c8c27-316d-409b-b311-27e11d63cd08

📥 Commits

Reviewing files that changed from the base of the PR and between d97c01b and bda82ee.

📒 Files selected for processing (2)
  • .env.example
  • docs/KNOWN_ISSUES.md

Comment thread docs/KNOWN_ISSUES.md
| Issue | Where | Status |
|---|---|---|
| ~~**Hard-won repo rules were never taught to the Brain.**~~ **Partially closed (2026-07-28).** A probe of three documented rules found two absent from the corpus entirely: the `force-dynamic` rule (§10) and the package-boundary rule — each with a class-of-bug entry here and real debugging cost behind it. The Oracle said so outright: *"isn't captured in your current knowledge base."* Only the `#418` mount-gate rule was present. **This is a capture gap, not a retrieval gap** — proven by teaching `force-dynamic` and re-running the *identical* prompt, which then ranked it **first**. KRA ranked it correctly the moment it existed. Nine rules were backfilled from GUIDELINES §9/§10 and AGENTS.md. **The rest of GUIDELINES/KNOWN_ISSUES has not been swept** — the docs remain the de facto memory for pre-Stage-1 lessons. | `docs/GUIDELINES.md`, the Brain corpus | partially done |
| **`scope: "user"` knowledge is invisible outside the project it was captured in — 117 rows affected.** Both `buildKnowledgeWhere` and `buildRawProjectFilter` (the pgvector path that `kra.ts` and `oracle.ts` actually use) resolve visibility as `ownerProjectId = $activeProject OR (ownerProjectId IS NULL AND ownerUserId = $user)`. **The `scope` column is never consulted.** A row with `scope: "user"` and a non-null `ownerProjectId` therefore matches neither arm and is filtered out — even though `scope: "user"` is `brain_teach_knowledge`'s *default* and reads as "follows the user". (`Knowledge.visibility` is a separate Phase-4 field and does not govern this path.) **Measured impact, same prompt both arms:** unscoped retrieval ranked `cmqpqemoh…` — trigger *"Adding a user-facing concept or glossary page to the External Brain webapp"*, 100% success over 11 uses — **first, at 0.9009 similarity**. The project-scoped session path did not return it at all, injecting a tangential i18n rule instead. **Scale:** 101 active items in `Default` vs 100 in `Brain Platform`; 117 rows repo-wide are `scope='user'` with a non-null project. Roughly half the corpus is invisible to a `BrainPlatform` session. **Not fixed here** — the fix is a design call with real blast radius (honour `scope` in the filter, vs. null out `ownerProjectId` on user-scope rows as a data repair) and it changes what every user sees. Note the duplicate-project detector cannot surface this: it looks for *normalized name collisions*, and `Brain Platform` vs `Default` will never collide. | `packages/core/src/scope-filter.ts:64-75,155-168`; `kra.ts:172` | open — needs a design call |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Scope the heading to project-scoped retrieval.

The current wording implies that scope: "user" knowledge is generally invisible outside its capture project, but this entry also says unscoped retrieval returns the row, and the later resolved note documents scope-aware behavior when no project is supplied. Please state explicitly that the blind spot affects project-scoped sessions, so readers do not infer that all user-scope retrieval is broken.

Proposed wording
-| **`scope: "user"` knowledge is invisible outside the project it was captured in — 117 rows affected.**
+| **Project-scoped sessions can hide `scope: "user"` knowledge captured in another project — 117 rows affected.**
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| **`scope: "user"` knowledge is invisible outside the project it was captured in — 117 rows affected.** Both `buildKnowledgeWhere` and `buildRawProjectFilter` (the pgvector path that `kra.ts` and `oracle.ts` actually use) resolve visibility as `ownerProjectId = $activeProject OR (ownerProjectId IS NULL AND ownerUserId = $user)`. **The `scope` column is never consulted.** A row with `scope: "user"` and a non-null `ownerProjectId` therefore matches neither arm and is filtered out — even though `scope: "user"` is `brain_teach_knowledge`'s *default* and reads as "follows the user". (`Knowledge.visibility` is a separate Phase-4 field and does not govern this path.) **Measured impact, same prompt both arms:** unscoped retrieval ranked `cmqpqemoh…` — trigger *"Adding a user-facing concept or glossary page to the External Brain webapp"*, 100% success over 11 uses — **first, at 0.9009 similarity**. The project-scoped session path did not return it at all, injecting a tangential i18n rule instead. **Scale:** 101 active items in `Default` vs 100 in `Brain Platform`; 117 rows repo-wide are `scope='user'` with a non-null project. Roughly half the corpus is invisible to a `BrainPlatform` session. **Not fixed here** — the fix is a design call with real blast radius (honour `scope` in the filter, vs. null out `ownerProjectId` on user-scope rows as a data repair) and it changes what every user sees. Note the duplicate-project detector cannot surface this: it looks for *normalized name collisions*, and `Brain Platform` vs `Default` will never collide. | `packages/core/src/scope-filter.ts:64-75,155-168`; `kra.ts:172` | open — needs a design call |
| **Project-scoped sessions can hide `scope: "user"` knowledge captured in another project — 117 rows affected.** Both `buildKnowledgeWhere` and `buildRawProjectFilter` (the pgvector path that `kra.ts` and `oracle.ts` actually use) resolve visibility as `ownerProjectId = $activeProject OR (ownerProjectId IS NULL AND ownerUserId = $user)`. **The `scope` column is never consulted.** A row with `scope: "user"` and a non-null `ownerProjectId` therefore matches neither arm and is filtered out — even though `scope: "user"` is `brain_teach_knowledge`'s *default* and reads as "follows the user". (`Knowledge.visibility` is a separate Phase-4 field and does not govern this path.) **Measured impact, same prompt both arms:** unscoped retrieval ranked `cmqpqemoh…` — trigger *"Adding a user-facing concept or glossary page to the External Brain webapp"*, 100% success over 11 uses — **first, at 0.9009 similarity**. The project-scoped session path did not return it at all, injecting a tangential i18n rule instead. **Scale:** 101 active items in `Default` vs 100 in `Brain Platform`; 117 rows repo-wide are `scope='user'` with a non-null project. Roughly half the corpus is invisible to a `BrainPlatform` session. **Not fixed here** — the fix is a design call with real blast radius (honour `scope` in the filter, vs. null out `ownerProjectId` on user-scope rows as a data repair) and it changes what every user sees. Note the duplicate-project detector cannot surface this: it looks for *normalized name collisions*, and `Brain Platform` vs `Default` will never collide. | `packages/core/src/scope-filter.ts:64-75,155-168`; `kra.ts:172` | open — needs a design call |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/KNOWN_ISSUES.md` at line 403, Update the issue heading and wording in
the documented entry to state that the visibility blind spot affects
project-scoped retrieval or project-scoped sessions, not user-scope retrieval
generally. Preserve the existing evidence and distinction that unscoped
retrieval can return these rows, while keeping the open design-call status
unchanged.

bejranonda and others added 2 commits July 28, 2026 21:08
… live-KRA)

Committed BEFORE any run, per the honesty guardrail suite 1 set: this
file's first commit is the pre-registration timestamp. Task list,
injection procedure and metric are locked as of this commit.

Suite 1 measured the injection MECHANISM: generic utility tasks with a
hand-written injected block. Four of its six tasks tied because the base
model already knew the edge case. Suite 2 changes both variables — tasks
whose correct answer is only derivable from BrainPlatform convention,
and a treatment block taken verbatim from the LIVE KRA path rather than
authored. That makes it a test of retrieval + generation together, i.e.
the product.

Confound control: KNOWN_ISSUES §0p / #174 established that scope:"user"
knowledge is invisible outside its capture project. All five rules under
test were taught into the canonical Brain Platform project and verified
retrievable from a projectName:"BrainPlatform" session before this file
was written — three of them ranking #1 for their own task query.
Without that control a null could not distinguish "knowledge didn't
help" from "knowledge wasn't visible".

Records two caveats up front rather than discovering them later:
grading is static assertion over emitted source (weaker than suite 1's
executable tests, since these conventions govern code shape), and the
control arm's repo isolation is instruction-enforced rather than
sandboxed — which would inflate control and understate uplift, so any
control pass is checked against its transcript and a contaminated task
is reported void rather than as a tie.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rqj5y9fT3XPUqnqKyustp9
…on pays

Runs the suite pre-registered in f62895b: five corpus-dependent tasks,
treatment arm injected verbatim from the LIVE KRA path. Control 3/5,
treatment 5/5 — +40pp, 0 regressions.

The aggregate is the less interesting half. What separates a flip from a
tie is whether the convention is locally arbitrary:

- FLIPS. Task 1 (force-dynamic) — control emitted no static-rendering
  opt-out of any kind, reproducing the v0.14.0 bug verbatim; the rule
  only exists because deploy/Dockerfile builds with dummy env. Task 4
  (@brain/core/format-relative) — control hand-rolled its own
  formatter, exactly the divergence v0.15.0 consolidated away. Neither
  is derivable from general expertise.
- TIES. Tasks 2, 3 and 5 are conventions that coincide with good
  practice: the control arm reached for x-forwarded-host unprompted,
  mount-gated its window read, and used {count} placeholders without
  being told.

So injection pays where the convention is arbitrary and ties where it
is craft — which predicts where capture effort returns most. Suite 1's
ties had the same cause; two independent suites now agree.

Caveats recorded rather than buried: n=5; grading is static assertion
over emitted source; isolation was instruction-enforced and every
control pass was checked for contamination (none found). Most
importantly, the five rules were verified retrievable from a
project-scoped session first, to control for #174 — roughly half the
corpus is invisible to such a session today, so an uncurated run would
score lower. This measures the Brain with its known retrieval defect
controlled for, not as a user experiences it.

Both arms' raw outputs committed under suite-2/tasks/ for audit.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rqj5y9fT3XPUqnqKyustp9
@bejranonda bejranonda changed the title docs: scope-filter blindness, corpus capture gap, and the Redis config drift feat(core): suite-2 uplift read (+40pp) + scope-filter blindness, capture gap, Redis drift Jul 28, 2026
@bejranonda
bejranonda merged commit 395a244 into main Jul 28, 2026
6 checks passed
@bejranonda
bejranonda deleted the fix/scope-filter-diagnosis-and-redis-drift branch July 28, 2026 21:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant