Skip to content

fix: block main derived bleed via legacy fallback in buildDerivedCandidates#710

Closed
jlin53882 wants to merge 5 commits intoCortexReach:masterfrom
jlin53882:fix/522-legacy-fallback-ownership
Closed

fix: block main derived bleed via legacy fallback in buildDerivedCandidates#710
jlin53882 wants to merge 5 commits intoCortexReach:masterfrom
jlin53882:fix/522-legacy-fallback-ownership

Conversation

@jlin53882
Copy link
Copy Markdown
Contributor

Summary

Layer 1 of 2 — blocks the read-path leak without changing write behavior.

Problem (reviewer feedback): buildDerivedCandidates() falls back to legacyRows when itemCandidates is empty. The combined-legacy rows written by buildLegacyCombinedPayload carry metadata.derived but have no itemKind, so isOwnedByAgent accepts owner==='main' for any sub-agent — causing context bleed.

Fix: Before the legacy flatMap, filter out rows where derived content exists AND owner is 'main'.

Files changed:

  • src/reflection-store.tsbuildDerivedCandidates now filters owner==='main' derived legacy rows in fallback
  • test/buildDerivedCandidates-legacy-fallback.test.mjs — 7 regression cases covering all fallback paths

Testing

7 test cases covering:

  • A. main derived legacy → sub-agent invisible (core fix)
  • B. main derived legacy → main visible
  • C. agent-x derived legacy → agent-x visible
  • D. agent-x derived legacy → agent-y invisible
  • E. pure legacy invariant (no derived) → sub-agent visible (unaffected)
  • F. item-derived candidates present → legacy fallback skipped
  • G. derived + empty owner → invisible (defensive)

Review reference

#522 (review)

jlin53882 and others added 5 commits April 25, 2026 01:17
…tate

Resolves reviewer Must-Fix #1 from PR CortexReach#522:

- _registeredApis.add(api) was called BEFORE _initPluginState.
  If _initPluginState threw, the api was already in the WeakSet,
  permanently blocking any retry from the same api instance.

- Fix: wrap _initPluginState in try-catch; add api to WeakSet only
  after successful init. If init fails, api stays out of WeakSet,
  allowing subsequent register() calls with the same api to retry.

Fixes CortexReach#448
- Issue 1: split 'if' from comment line + add missing fallback block
- Issue 2: test now imports from ../src/reflection-store.ts (no local copy)
- Issue 3: normalize all CR to LF (UTF-8 + LF)

Fixes reviewer Must-Fix from PR CortexReach#522
…idates

Layer 1 of 2 — blocks the read-path leak without changing write behavior.

Problem: buildDerivedCandidates() falls back to legacyRows when
itemCandidates is empty. combined-legacy rows (written by
buildLegacyCombinedPayload) carry metadata.derived but have no itemKind,
so isOwnedByAgent accepts owner==='main' for any sub-agent — causing
context bleed.

Fix: before the legacy flatMap, filter out rows where:
  - derived content exists (hasDerivedContent)
  - AND owner is 'main' (owner === 'main' → reject)
  - AND owner is empty (empty owner with derived → reject)
  - AND owner does not match querying agentId

Pure legacy invariants (no derived) are unaffected.

Added: buildDerivedCandidates-legacy-fallback.test.mjs with 7 cases
covering all fallback paths including the main→sub-agent bleed (case A).

Review: CortexReach#522 (review)
@jlin53882 jlin53882 closed this Apr 28, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b02f15c080

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/reflection-store.ts
if (!hasDerivedContent) return true; // 無 derived → 正常 legacy invariant
const owner = typeof metadata.agentId === "string" ? metadata.agentId.trim() : "";
if (!owner) return false; // 有 derived 但無 owner → 不可見
if (owner === "main") return false; // ★ main 的 derived 不外流
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve main-owned legacy derived rows for main agent

In buildDerivedCandidates, the new legacy fallback filter unconditionally drops rows where owner === "main", so the main agent also loses access to its own legacy-derived reflections when itemCandidates is empty (the exact migration/backfill path this fallback is for). This regresses behavior for older memory-reflection records: a main-owned legacy row now contributes zero derived candidates even when agentId is "main".

Useful? React with 👍 / 👎.

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