Repository navigation
Description
Note
This issue was compiled with help from a Claude Code review. Items 1 and 10 were reproduced by actually running them; every other item is labeled with the evidence level defined below.
These issues come from a review of changes made to master (1190dcbb) since 2026-10-27. The review covered:
server/, includingownership.py,artifact_plugins.py, and the Unix socket listenerbuiltin/persistence/builtin/runtime/builtin/inference/- the new
source_management/package
Architecture-level issues are out of scope here; they are tracked in #2085.
Before filing, each item below was checked against #2081–#2085 (including N1–N14 in the #2085 comment) and against the currently open issues and PRs. None of them is reported there. Where an item relates to a closed issue, that is noted in the item.
Items are ordered by severity; none is high severity. Evidence levels:
- Reproduced: confirmed by running it.
- Confirmed: confirmed by reading the code along the call path, without a reproduction test.
- Likely: the code path holds, but triggering it requires specific timing or a specific environment.
1. Blank scope_id on Dream routes returns 500 (medium, reproduced)
- Location:
server/app.py:3953/3969/3997(get_dream_run,create_dream_run,list_dream_runs) - Problem: These three routes declare
scope_id/run_idas barestr, without thePath(min_length=1, max_length=256, pattern=r".*\S.*")that the other routes use. A blank scope reachesvalidate_scope_id, which raisesValueError, and_map_domain_errorhas no mapping for it. - Reproduction: With a SQLite
BuiltinRuntimeand httpx ASGITransport,GET /v1/scopes/%20/dream/abcreturns500 {"code":"internal_error","message":"The Server failed."}. For comparison,GET /v1/scopes/%20/artifacts/atomic-memory/areturns 422. - Suggestion: Add the same
Path(...)constraints to these three routes, plus a regression test.
2. Source manager reads every target on every sync (medium, confirmed)
- Location:
source_management/manager.py:483; see also:367,:460 - Problem:
- Every call to
sync_instance,add, andconnect_instancereads and decodes all targets fromStore.all("target"). sync_instancealso sorts the full result.addalready has thefollow_identityprimary-key table, yet deduplication is still a linear scan.
- Every call to
- Impact:
- Each Runtime's
synchronize()polls every 0.1 seconds (runtime.py:234), and every page it turns triggers another full scan. - As the number of targets grows, JSON decoding will overwhelm the manager, which runs on a single event loop.
- This conflicts with the AGENTS.md requirement to avoid whole-collection scans on incremental operations.
- Each Runtime's
- Suggestion:
- Add
instance_id,identity, andtarget_idcolumns with indexes to the records. - Have
sync_instancepage byinstance_idandtarget_id > cursorwith aLIMIT. - Have
addlook records up throughfollow_identity.
- Add
3. credential() uses a non-blocking lock, so concurrent calls fail outright (medium, confirmed)
- Location:
source_management/deployment.py:121-127,source_management/durable.py:47 - Problem: The
credential()docstring says it serializes credential creation, but theRuntimeOwnerit uses locks withLOCK_EX | LOCK_NB. A caller that does not get the lock immediately receives 409instance-runninginstead of waiting. - Impact:
- When Server startup (
prepare(),factory.py:193) and CLIpowercontext source loadcall it at the same time, one of them fails with a misleading error. - If the Server is the one that fails, startup aborts.
- When Server startup (
- Suggestion: Use a blocking
LOCK_EX(optionally with a timeout) for the configuration lock, separate from the Runtime owner lock.
4. Custom Artifact search SQL has no LIMIT (medium, confirmed)
- Location:
builtin/runtime/custom_artifact_search.py:403-448 - Problem:
- Neither the
_textnor the_vectorquery has aLIMIT;candidate_limitis applied in_rankonly after every row has been returned to Python. - The vector query computes a distance for every row in the scope / build.
- With a non-builtin authorization provider, it also runs
await bound.checkonce per identity (:293).
- Neither the
- Impact: Memory and latency of a single search grow linearly with the number of custom Artifacts in the scope. R9 in [risk] 性能与并发风险合集(R1–R14):启动锁内全库工作、O(N²) 迁移、检索全量计算等 #2084 covered the same pattern for Atomic Memory search, and perf(atomic-memory): related recall peak memory grows linearly under complete enumeration #2448 / perf(atomic-memory): bound related recall candidates in SQL to reduce peak memory and recall cost #2449 are bounding related recall; this path was added by feat(artifact): add managed plugin framework and some example #2246 and falls outside both.
- Suggestion: Add
ORDER BY ... LIMITin SQL usingmax(limit, candidate_limit). If per-item authorization filtering leaves too few results, fetch more in batches.
5. Source Runtime / manager reconnect loops handle only some exceptions (medium, likely)
- Location:
source_management/runtime.py:80, 139, 240,source_management/manager.py:575, 652 - Problem:
run()andserve_channel()catch onlyhttpx.HTTPErrorandManagementError; any other exception escapes the reconnect loop as anExceptionGroup. - Impact:
- Runtime side: When the manager hits an unhandled exception (for example, SQLite raising
OperationalErroron a full disk), it returns a plain-text 500.response.json()inpost()then raisesJSONDecodeErrorand the Runtime process exits. In background mode, no supervisor restarts it. - Manager side: An unexpected exception in
respondsilently ends theserve_channelbackground task created in the lifespan. The process keeps holding the owner lock but never reconnects, and the Server keeps reporting management unavailable.
- Runtime side: When the manager hits an unhandled exception (for example, SQLite raising
- Suggestion:
- Map non-JSON error responses to
ManagementError. - Catch
Exception(but not cancellation) as a fallback in the reconnect loops, log it throughlog_safely, and reconnect. - Add a done callback to the background task so the process fails and exits when the task ends with an exception.
- Map non-JSON error responses to
6. Race between approval and online ownership migration (medium, likely)
- Location:
server/app.py:4856-4881(approve_candidate),server/ownership.py:236-278,server/authz/repository.py:449-457 - Problem:
- For a Candidate with
target=None,approve_candidatecommits the approval first and only then writes the Artifact owner in a separate transaction (keycandidate-artifact-owner:<scope>:<cid>). - That write is neither inside the approval transaction nor routed through the owner recovery outbox.
- Online
POST /v1/access/ownership/applyis blocked only by pending recovery evidence, so it does not cover this window. - R4 in [risk] 性能与并发风险合集(R1–R14):启动锁内全库工作、O(N²) 迁移、检索全量计算等 #2084 listed
create_artifactandpropose_experience. fix(authz): commit local write ownership atomically #2164 made same-database writes and owner establishment atomic for those paths, and feat(authz): recover external owner writes from durable intents #2187 added recovery for external stores. The approval path's owner write is outside both fixes and still runs afterreview.approve()returns.
- For a Candidate with
- Impact:
- With access control enforced, user U approves an experience Candidate that U proposed.
- After the approval transaction commits but before the owner is written, an administrator runs
apply, which writes the owner as the migration's Principal underexperience-owner:.... - U's
establish_artifact_ownerfinds a different owner, raisesAccessConflictError, and the approval returns 409. - The Artifact's owner is permanently the migration's Principal.
- Suggestion: Any one of the following:
- When migration encounters such a Candidate result, use the recorded
proposed_ownerand the same key as the approval path. - Move the post-approval owner write into the approval transaction (
on_commit). - Route this step through the recovery outbox.
- When migration encounters such a Candidate result, use the recorded
7. Migration uses a different owner idempotency key from the original write paths (low, confirmed)
- Location:
server/ownership.py:505-518(_owner_identity) - Problem:
- Migration writes with
bounded_idempotency_key(f"{family}-owner", scope, artifact_id). - The original write paths each use a different key:
base-artifact-owner:<sha>atapp.py:4613,candidate-artifact-owner:atapp.py:4879, andbackground-custom-owner:atbuiltin/runtime/artifact_plugins.py:321. establish_artifact_ownerstreats a write as idempotent only when both the owner and the key match (repository.py:454).
- Migration writes with
- Impact: Suppose the owner write fails after an approval commits (for example, the process restarts), and an administrator then runs
apply --owner user:Uwith the same owner as the original proposer. The subsequent approval retry, andcreate_artifactretries against an external Access store, consistently return 409. This contradicts the documented "no conflict when the owner is the same". - Suggestion: Have migration reuse each family's original key derivation, or have retry paths treat an identical owner as success.
8. All authentication fails when the generated identities.json is unavailable (low, confirmed)
- Location:
source_management/deployment.py:84 - Problem:
DeploymentAuthenticationfalls back to the user-configured authentication provider only when the generated identity file returns Rejected. - Impact:
- When the file is unreadable, corrupted, or over the 1000-entry limit, it raises
AuthenticationUnavailableError, and authentication fails for every user token. - The file is re-read and re-parsed on every request.
- When the file is unreadable, corrupted, or over the 1000-entry limit, it raises
- Suggestion:
- When the generated file is unavailable, fall back to the user-configured provider only and warn through
log_safely. - Cache the parsed result by mtime.
- When the generated file is unavailable, fall back to the user-configured provider only and warn through
9. Inference settings recorded in receipts may differ from the request actually sent (low, likely)
- Location:
builtin/inference/pydantic_ai.py:121-137 - Problem:
_receipt_model_settingsemulates only OpenAI's reasoning rules.- When an Anthropic profile sets
anthropic_disallows_sampling_settings=True, pydantic_ai dropstemperature,top_p, andtop_k. - The thinking check looks only at
supports_thinkingand ignoresthinking_always_enabled.
- Impact: The receipt's
effective_settingsrecords parameters that were never sent. This is the kind of mismatch 32631c1 / bae5d71 set out to remove. - Suggestion: Derive the receipt settings from the request pydantic_ai actually prepares instead of re-implementing the rules.
10. 18 test failures on macOS (low, reproduced)
- Observed:
uv run pytest -n 6on macOS gives 9221 passed, 18 failed, 911 skipped. - Cause:
- 14 failures are in
tests/test_source_management.py,tests/e2e/test_source_collect.py, andtests/e2e/test_source_deployment.py.RuntimeOwner.identity()atsource_management/durable.py:59unconditionally reads/proc/<pid>/stat,/proc/sys/kernel/random/boot_id, and/etc/machine-id, which raisesFileNotFoundErroron macOS. - The documentation says local collect deployment requires Linux, but these tests are not skipped by platform.
- Running a Runtime on macOS shows
deployment-failed: FileNotFoundError; check configuration and private credential filesrather than a clearbackground-unsupported. - The other 4 failures are in
tests/test_ci_installation_roles.py: Python in the newly created virtual environment exits with SIGABRT. This may be a local environment problem and is not yet confirmed.
- 14 failures are in
- Suggestion:
- Mark tests that depend on Linux process identity with
skipif(sys.platform != "linux"). - Have
RuntimeOwner.identity()raise a clear unsupported-platform error on non-Linux platforms.
- Mark tests that depend on Linux process identity with
Other
Operational logs in source_management/runtime.py and source_management/write.py call logger.* directly instead of going through log_safely as AGENTS.md requires.
Reviewed with no issues found
- Inference privacy boundary:
privacy.py,protected_transport.py,context_check.py - Plugin loading and scope authorization
- Unix socket listener: directory and socket permissions, stale socket reclamation, cleanup on shutdown
- Database admission: subprocess isolation
- Source evidence declarations (feat(memory): persist versioned Source evidence and neutral Atomic Memory projection #2004) and historical migrations
- Exact-reference outcome receipt reads (perf(work): read outcome receipts by exact reference #2401)
mysql_compatibility.py and sqlite_compatibility.py were only sampled for consistency between interrupted-DDL recovery and the schema snapshots; they were not fully reviewed.
Environment
- Reviewed:
master, commit1190dcbb - Test environment: macOS (Darwin, arm64), Python 3.13
ruff check,ruff format --check, andty checkall pass
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsTodo
Activity
[-][review] master 近期改动的正确性问题合集:Dream 路由 500、Source 管理全量扫描、凭据锁并发失败、所有权迁移冲突等[/-][+][review] Correctness issues in recent master changes: Dream route 500, full scans in Source management, credential lock failures under concurrency, ownership migration conflicts[/+]ctxloop commentedon Nov 4, 2026
please assign to me
fix(source): reject collect Runtimes on non-Linux platforms (#2480)
fix(server): reject blank Dream scope IDs at the HTTP boundary (#2494)
ctxloop commentedon Nov 6, 2026
Status
Checked on 2026-11-08 against
masterda03e70a. The review baseline is1190dcbb. This comment is edited in place as PRs open and merge.✅ merged · 🔶 partly resolved · 🟡 in review · ⬜ not started
scope_id500e0bc7f8c)89d960e3).credential()non-blocking lockLIMITidentities.jsonfails all authenticationda03e70a)temperature,top_p, andtop_kwhen a profile setsanthropic_disallows_sampling_settings. The thinking check still reads onlysupports_thinkingand ignoresthinking_always_enabled, and the receipt settings are still re-derived rather than taken from the request pydantic_ai prepares.c4b9b540)tests/test_ci_installation_roles.pydid not reproduce on macOS 26.1 arm64 with Python 3.13.12 and are unchanged.log_safelyin Source logs