Files
weichen 439865616a
ci / vet-and-test (pull_request) Failing after 44s
Share retrieval inside runs and open project listing/conversations to members (EXT-13/15)
Why: A member's agent cannot see published project knowledge inside a run
(runCtx.Retrieve and hostcaps.search are owner-only), and members can
neither see shared projects nor open conversations in them - both are P0
prerequisites for RDM G1 (EXT-13/EXT-15, proposal
2026-10-09-ext-g1-b1-b3-exec-backend).
Evidence: Red tests first - TestRunContextRetrieveShared (undefined
SetSharedKnowledgeAllow), TestInprocHostKnowledgeSearchShared (0 shared
hits), TestSharedProjectListAndConversation (empty list) all failed before
and pass after; go test ./... 32/32 packages ok.
Impact: runmgr gains SetSharedKnowledgeAllow and routes Retrieve through
RetrieveShared when the predicate is registered (nil = unchanged); hostcaps
search consults the same EXT-8 slot; GET /api/projects appends
adapter-authorized shared projects (projectView with shared flag, deduped);
POST /api/projects/{id}/conversations falls back to
CreateConversationInProject when the own-project path misses and the
adapter authorizes; store gains ListAllProjects (candidates only, callers
must filter through the adapter) and CreateConversationInProject.
Revocation takes effect immediately on both paths; absence of an adapter
is byte-for-byte the previous behavior. No pkg/extension surface change.
Refs: docs/proposals/2026-10-09-ext-g1-b1-b3-exec-backend.md (EXT-13, EXT-15)
2026-10-09 15:44:48 +00:00

73 lines
2.5 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package runmgr
// EXT-13 红测(提案 2026-10-09-ext-g1-b1-b3 §1):回合内检索(knowledge_search
// 工具与 @knowledge 注入的同一入口 runCtx.Retrieve)在适配器放行后吃到共享命中;
// 未放行/未注册谓词时保持本人两路、零泄露。
import (
"context"
"testing"
core "agent-system-core"
)
type sharedFakeRetriever struct {
own []core.RetrievalHit
shared []core.RetrievalHit
gotAlt bool
}
func (f *sharedFakeRetriever) Retrieve(ctx context.Context, owner core.ID, q core.RetrievalQuery) ([]core.RetrievalHit, error) {
return f.own, nil
}
func (f *sharedFakeRetriever) RetrieveShared(ctx context.Context, owner core.ID, q core.RetrievalQuery, allow func(docOwner, projectID core.ID) bool) ([]core.RetrievalHit, error) {
f.gotAlt = true
out := append([]core.RetrievalHit{}, f.own...) // 对齐 rag.Pipeline:本人两路如常
for _, h := range f.shared {
if allow(h.Doc.Owner, h.Doc.ProjectID) {
out = append(out, h)
}
}
return out, nil
}
func TestRunContextRetrieveShared(t *testing.T) {
m := New(nil, nil, nil, nil, nil, nil, nil, nil, nil)
fr := &sharedFakeRetriever{
own: []core.RetrievalHit{{Doc: core.KnowledgeDoc{Ref: "own-1", Chunk: "本人命中"}}},
shared: []core.RetrievalHit{{Doc: core.KnowledgeDoc{Ref: "sh-1", Chunk: "共享命中", Owner: "alice", ProjectID: "p-1"}}},
}
m.SetRetriever(fr)
rc := &runCtx{mgr: m, run: core.Run{Owner: "bob"}}
// 未注册共享谓词:本人两路如常,不触碰共享通道。
hits, err := rc.Retrieve(core.RetrievalQuery{Text: "命中"})
if err != nil || len(hits) != 1 || hits[0].Doc.Ref != "own-1" {
t.Fatalf("no predicate: hits=%+v err=%v", hits, err)
}
if fr.gotAlt {
t.Fatal("no predicate: must not call RetrieveShared(未注册=现状)")
}
// 注册谓词(放行 bob×p-1)→ 本人 + 共享。
m.SetSharedKnowledgeAllow(func(owner string, projectID core.ID) bool {
return owner == "bob" && string(projectID) == "p-1"
})
fr.gotAlt = false
hits, err = rc.Retrieve(core.RetrievalQuery{Text: "命中"})
if err != nil || len(hits) != 2 {
t.Fatalf("with predicate: hits=%+v err=%v, want own+shared", hits, err)
}
if !fr.gotAlt {
t.Fatal("with predicate: RetrieveShared not consulted(EXT-13)")
}
// 谓词拒绝 → 只剩本人。
m.SetSharedKnowledgeAllow(func(owner string, projectID core.ID) bool { return false })
hits, err = rc.Retrieve(core.RetrievalQuery{Text: "命中"})
if err != nil || len(hits) != 1 || hits[0].Doc.Ref != "own-1" {
t.Fatalf("denied: hits=%+v err=%v, want own only", hits, err)
}
}