Skip to content

Pin #1127: the User NodeType declaration is never treated as an account - #1157

Merged
rbuergi merged 4 commits into
mainfrom
fix/1127-aisettings-phantom-user-pin
Aug 10, 2026
Merged

rbuergi merged 4 commits into
mainfrom
fix/1127-aisettings-phantom-user-pin

Conversation

@rbuergi

@rbuergi rbuergi commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Fixes #1127

Verdict on the path question

The path User/_Memex/AiSettings was wrong — a path-construction bug, not a missing schema. User is the self-typed NodeType declaration node: UserNodeType.CreateMeshNode() deliberately sets NodeType = "User" on the declaration at path User, so any pathless nodeType:User query returns the declaration alongside real accounts. AiSourcesInstallHook.Users() enumerated users exactly that way and reconciled the declaration's Id ("User") as an account — once per installed package, on every pod boot's repair pass. 27 occurrences = 3 pod boots × ~9 installed packages (Store, Edu, Essentials, Skill, Agent, Publish, DataModelling, Collaboration, AgenticOffice, Feedback), each burst landing within the same second per pod.

What happened to the failed writes

Nothing was lost. Live-portal evidence (read-only):

  • path:User/_Memex/AiSettings → 0 results — every phantom write was aborted by the 42P01, none persisted.
  • rbuergi/_Memex/AiSettings is healthy, lastModifiedBy: system-security today 12:55Z, carrying exactly the per-package agent/skill source rows — the same repair pass reconciled every real account correctly the whole time. The failing writes carried only machine-generated source rows for a nonexistent account; no user-entered settings were involved.

The fix, and what this PR adds

The enumeration fix (drop the declaration id) already landed on main in PR #1074 (540852750) — but with no test, and the regression is silent by construction (the per-user Catch reduces the failing write to a log line). This PR adds the end-to-end regression pin on the monolith mesh, where the phantom write succeeds and leaves queryable evidence:

  • Falsified red: with the filter line removed, the test fails — User/_Memex/AiSettings exists after OnPartitionInstalled ("found 1 item(s)").
  • Green as merged: real accounts receive the package's agent + skill sources; the declaration path stays absent.

Also a Category: Fix What's New entry for the user-visible half of #1127 (recurring 42P01 log errors + the auto-filed incident), which PR #1074's entry did not cover.

Same-bug sweep (negative results)

  • AiSourcesInstallHook is the only every-user fan-out writer. All other nodeType:User consumers are content-filtered lookups (content.email: / content.objectId: — the declaration's content is a NodeTypeDefinition, so they never match) or read-only (DevLogin list, UserIdentityCache, pickers).
  • Post-creation seed handlers (AiSettingsSeedHandler etc., keyed on NodeType User) cannot fire for the declaration: static/declaration nodes are served by StaticNodeQueryProvider at query time and never pass through the CreateNode pipeline.
  • No other code constructs a literal User/... write path; remaining "User/" literals are reads/strips of the legacy User/{id} catalog-mirror shape.

Prod follow-up

None needed in the database (nothing persisted, no real data affected). The 42P01 noise on memex-cloud stops when the pods roll to an image containing PR #1074 (merged this morning); occurrences after the merge are the pre-fix image still running.

Verification

  • dotnet build test/MeshWeaver.Hosting.Monolith.Test -c Release -warnaserror — clean (0 warnings, 0 errors), test file included.
  • Falsification loop: filter removed → RED, filter restored → GREEN (single-test runs, FullyQualifiedName~AiSourcesInstallHookTest).

🤖 Generated with Claude Code

https://claude.ai/code/session_01UYLgoPK1qLtyxZ2ixdFtj5

memex-cloud logged 27x `42P01: relation "user.mesh_nodes" does not exist`,
every one a CreateNode at the literal path User/_Memex/AiSettings. The path
was wrong, not the schema: `User` is the SELF-TYPED NodeType declaration
node (UserNodeType.CreateMeshNode sets NodeType = "User" on the node at
path "User"), so AiSourcesInstallHook's pathless `nodeType:User` user
enumeration returned it alongside the real accounts and reconciled it as a
user - once per installed package, on every pod boot's repair pass
(27 = 3 pod boots x ~9 packages). The per-user Catch reduced each failure
to log noise; on PG nothing ever persisted (path:User/_Memex/AiSettings is
empty on the live portal), and no real user's settings were touched - the
hook reconciled every real account correctly the whole time.

The enumeration fix (drop the declaration id) already landed on main via
PR #1074 (5408527) but shipped with NO test - the regression is silent
by construction, since the Catch swallows the failing write. This adds the
end-to-end pin on the monolith mesh, where the phantom write SUCCEEDS and
leaves queryable evidence: with the filter removed the test goes red
(User/_Memex/AiSettings exists), with it in place real users receive the
package's agent + skill sources and the declaration path stays absent.

Sweep result: no other writer constructs a literal User/... path. All
remaining `nodeType:User` consumers are content-filtered lookups (the
declaration carries a NodeTypeDefinition, so content.email/objectId never
match) or read-only; declaration nodes are served by the static query
provider and never pass through CreateNode, so post-creation seed handlers
cannot fire for them.

Fixes #1127

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UYLgoPK1qLtyxZ2ixdFtj5

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds an end-to-end regression pin for #1127 ensuring AiSourcesInstallHook never treats the self-typed User NodeType declaration as a real account, plus a user-visible “What’s New” entry documenting the fix.

Changes:

  • Adds a monolith integration test that runs the install hook and verifies it only writes AI source rows for real users (and never creates User/_Memex/AiSettings).
  • Adds a Category: Fix What’s New entry describing the eliminated recurring startup/install errors.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
test/MeshWeaver.Hosting.Monolith.Test/AiSourcesInstallHookTest.cs New regression test covering the “User declaration treated as account” failure mode end-to-end.
src/MeshWeaver.Documentation/Data/WhatsNew/2026-08-10-ai-settings-no-longer-written-for-a-phantom-account.md New release note entry documenting the fix and its impact.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@github-actions

github-actions Bot commented Aug 10, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

701 tests  ±0   701 ✅ ±0   14m 9s ⏱️ +14s
 10 suites ±0     0 💤 ±0 
 10 files   ±0     0 ❌ ±0 

Results for commit cc2d39e. ± Comparison against base commit e6c09b1.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 10, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

1 776 tests  ±0   1 584 ✅ ±0   6m 19s ⏱️ +6s
   11 suites ±0     192 💤 ±0 
   11 files   ±0       0 ❌ ±0 

Results for commit cc2d39e. ± Comparison against base commit e6c09b1.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 10, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

1 923 tests   - 95   1 922 ✅  - 94   6m 15s ⏱️ -30s
   11 suites ± 0       1 💤  -  1 
   11 files   ± 0       0 ❌ ± 0 

Results for commit cc2d39e. ± Comparison against base commit e6c09b1.

This pull request removes 305 and adds 210 tests. Note that renamed tests count towards both.
MeshWeaver.Hosting.Monolith.Test.CessionLayoutAreaTest ‑ BusinessRulesDoc_RelativeReference_ResolvesToMotorXL
MeshWeaver.Hosting.Monolith.Test.CessionLayoutAreaTest ‑ Cession_Trace_HubConfiguration
MeshWeaver.Hosting.Monolith.Test.CessionLayoutAreaTest ‑ MotorXL_LayoutArea_ReturnsContent
MeshWeaver.Hosting.Monolith.Test.CessionLayoutAreaTest ‑ MotorXL_Overview_ShouldRender
MeshWeaver.Hosting.Monolith.Test.CessionLayoutAreaTest ‑ MotorXL_PathResolves
MeshWeaver.Hosting.Monolith.Test.CessionLayoutAreaTest ‑ NonExistentPath_Failure
MeshWeaver.Hosting.Monolith.Test.CircuitContextIsolationTest ‑ CircuitContext_DoesNotLeakAnotherUserAcrossAnAsyncLocalHop
MeshWeaver.Hosting.Monolith.Test.CodeEditRecompileTest ‑ CodeEdit_ExplicitRelease_IsUpToDate_RecompilesOnSourceChange
MeshWeaver.Hosting.Monolith.Test.CodeEditRecompileTest ‑ CompileTool_UnchangedSources_ForcesFreshRun_NeverReturnsPreviousRunsSnapshot
MeshWeaver.Hosting.Monolith.Test.CodeEditRecompileTest ‑ FailedCompile_DirectCompileToolRetry_RunsFreshCompileAndCreatesRelease
…
MeshWeaver.Hosting.Monolith.Test.BrokenNodeTypeAccessTest ‑ AccessingInstance_OfNonCompilingNodeType_AnswersTerminalError_NotSilence
MeshWeaver.Hosting.Monolith.Test.ChangeFeedVersionGateTest ‑ ReceivedOwnerWrites_DoNotResubscribe_ButStaleAnnouncementDoes
MeshWeaver.Hosting.Monolith.Test.ClrMdDacUnloadCrashTest ‑ ThreadThatRanAClrMdSnapshot_MustExitSafely_AfterDataTargetDisposal
MeshWeaver.Hosting.Monolith.Test.ColdReactivationPatchReadYourWriteTest ‑ UpdateAgainstDisposedHub_ReactivatesAndPersists
MeshWeaver.Hosting.Monolith.Test.CompileActivityNoPhantomPathTest ‑ CompiledNodeType_LastCompilationActivityPath_PointsAtAnExistingActivityNode
MeshWeaver.Hosting.Monolith.Test.CompileActivityNoPhantomPathTest ‑ SubscribingAbsentCompileActivity_InTightLoop_StaysResponsive_NoStorm
MeshWeaver.Hosting.Monolith.Test.CompileLeafStabilityTest ‑ CompileAndGetConfigurations_FreshNodeTypes_AllEmitWithinBound
MeshWeaver.Hosting.Monolith.Test.CompileLeafStabilityTest ‑ Kickoff_FreshNodeTypes_AllSettle_NoStall
MeshWeaver.Hosting.Monolith.Test.CompileSingleDriverConsistencyTest ‑ ConcurrentRequests_OnFreshNodeType_BothSucceedConsistently
MeshWeaver.Hosting.Monolith.Test.CompileSingleDriverConsistencyTest ‑ FreshCompile_TerminalStateIsComplete
…

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 10, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

1 289 tests  +96   1 288 ✅ +95   6m 29s ⏱️ +30s
   11 suites ± 0       1 💤 + 1 
   11 files   ± 0       0 ❌ ± 0 

Results for commit cc2d39e. ± Comparison against base commit e6c09b1.

This pull request removes 210 and adds 306 tests. Note that renamed tests count towards both.
MeshWeaver.Hosting.Monolith.Test.BrokenNodeTypeAccessTest ‑ AccessingInstance_OfNonCompilingNodeType_AnswersTerminalError_NotSilence
MeshWeaver.Hosting.Monolith.Test.ChangeFeedVersionGateTest ‑ ReceivedOwnerWrites_DoNotResubscribe_ButStaleAnnouncementDoes
MeshWeaver.Hosting.Monolith.Test.ClrMdDacUnloadCrashTest ‑ ThreadThatRanAClrMdSnapshot_MustExitSafely_AfterDataTargetDisposal
MeshWeaver.Hosting.Monolith.Test.ColdReactivationPatchReadYourWriteTest ‑ UpdateAgainstDisposedHub_ReactivatesAndPersists
MeshWeaver.Hosting.Monolith.Test.CompileActivityNoPhantomPathTest ‑ CompiledNodeType_LastCompilationActivityPath_PointsAtAnExistingActivityNode
MeshWeaver.Hosting.Monolith.Test.CompileActivityNoPhantomPathTest ‑ SubscribingAbsentCompileActivity_InTightLoop_StaysResponsive_NoStorm
MeshWeaver.Hosting.Monolith.Test.CompileLeafStabilityTest ‑ CompileAndGetConfigurations_FreshNodeTypes_AllEmitWithinBound
MeshWeaver.Hosting.Monolith.Test.CompileLeafStabilityTest ‑ Kickoff_FreshNodeTypes_AllSettle_NoStall
MeshWeaver.Hosting.Monolith.Test.CompileSingleDriverConsistencyTest ‑ ConcurrentRequests_OnFreshNodeType_BothSucceedConsistently
MeshWeaver.Hosting.Monolith.Test.CompileSingleDriverConsistencyTest ‑ FreshCompile_TerminalStateIsComplete
…
MeshWeaver.Hosting.Monolith.Test.AiSourcesInstallHookTest ‑ InstallHook_RegistersSourcesForRealUsers_NeverForTheUserTypeDeclaration
MeshWeaver.Hosting.Monolith.Test.CessionLayoutAreaTest ‑ BusinessRulesDoc_RelativeReference_ResolvesToMotorXL
MeshWeaver.Hosting.Monolith.Test.CessionLayoutAreaTest ‑ Cession_Trace_HubConfiguration
MeshWeaver.Hosting.Monolith.Test.CessionLayoutAreaTest ‑ MotorXL_LayoutArea_ReturnsContent
MeshWeaver.Hosting.Monolith.Test.CessionLayoutAreaTest ‑ MotorXL_Overview_ShouldRender
MeshWeaver.Hosting.Monolith.Test.CessionLayoutAreaTest ‑ MotorXL_PathResolves
MeshWeaver.Hosting.Monolith.Test.CessionLayoutAreaTest ‑ NonExistentPath_Failure
MeshWeaver.Hosting.Monolith.Test.CircuitContextIsolationTest ‑ CircuitContext_DoesNotLeakAnotherUserAcrossAnAsyncLocalHop
MeshWeaver.Hosting.Monolith.Test.CodeEditRecompileTest ‑ CodeEdit_ExplicitRelease_IsUpToDate_RecompilesOnSourceChange
MeshWeaver.Hosting.Monolith.Test.CodeEditRecompileTest ‑ CompileTool_UnchangedSources_ForcesFreshRun_NeverReturnsPreviousRunsSnapshot
…

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 10, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

   11 files  ±0     11 suites  ±0   9m 28s ⏱️ +21s
1 984 tests ±0  1 876 ✅ ±0  108 💤 ±0  0 ❌ ±0 
2 163 runs  +1  2 055 ✅ +1  108 💤 ±0  0 ❌ ±0 

Results for commit cc2d39e. ± Comparison against base commit e6c09b1.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 10, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

1 351 tests  ±0   1 346 ✅ ±0   7m 57s ⏱️ - 1m 33s
   11 suites ±0       5 💤 ±0 
   11 files   ±0       0 ❌ ±0 

Results for commit cc2d39e. ± Comparison against base commit e6c09b1.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 10, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

   65 files  ±0     65 suites  ±0   50m 39s ⏱️ -52s
9 024 tests +1  8 717 ✅ +1  307 💤 ±0  0 ❌ ±0 
9 203 runs  +2  8 896 ✅ +2  307 💤 ±0  0 ❌ ±0 

Results for commit cc2d39e. ± Comparison against base commit e6c09b1.

♻️ This comment has been updated with latest results.

…ndex

The assertion read GetQuery(...).FirstAsync() and expected an empty result. That
is the one case a query cannot settle: it is eventually consistent, so an empty
first emission is equally consistent with 'the phantom was never written' and
'the phantom was written and the index has not caught up' — and the second is
precisely the regression this test exists to catch. As written it could only
ever pass, including against the pre-fix code, which makes it a false negative
rather than a pin.

Now reads GetMeshNodeStream(phantomPath), which is authoritative: an absent node
surfaces as a routing NotFound (OnError/DeliveryFailureException) while a present
one arrives as OnNext, so the two outcomes are different signals rather than the
same empty result. Same shape as CompileActivityNoPhantomPathTest, which pins the
identical 'leaf never created under a real ancestor' phantom.

The reviewer's other point — unused usings failing under -warnaserror via CS8019
— does not apply: CS8019 is not enabled here and the project builds clean with
-c Release -warnaserror, verified before and after this change.

Test passes (1/1); MeshWeaver.Hosting.Monolith.Test builds clean under CI flags.
@rbuergi

rbuergi commented Aug 10, 2026

Copy link
Copy Markdown
Contributor Author

Addressed both review points in cc2d39ea2.

The phantom assertion is the substantive one, and the reviewer is right — as written the test could not fail. It read GetQuery(...).FirstAsync() and expected an empty result, but a query index is eventually consistent, so an empty first emission is equally consistent with "the phantom was never written" and "the phantom was written and the index hasn't caught up". The second reading is exactly the regression this test exists to catch, so it would have passed against the pre-fix code too — a false negative rather than a pin.

It now reads GetMeshNodeStream(phantomPath), which is authoritative and, importantly, gives the two outcomes different shapes: an absent node surfaces as a routing NotFound (OnError / DeliveryFailureException), a present one as OnNext. Same pattern as CompileActivityNoPhantomPathTest, which pins the identical "leaf never created under a real ancestor" phantom — including its check that the fault is specifically the routing NotFound, so the assertion can't pass for an unrelated reason.

On the unused usings: that one does not apply. CS8019 is not enabled in this project, and MeshWeaver.Hosting.Monolith.Test builds clean under -c Release -warnaserror both before and after the change — verified rather than assumed. I've left the usings alone rather than churn the file on a build failure that doesn't occur.

Test passes 1/1.

@rbuergi
rbuergi merged commit 2a6e598 into main Aug 10, 2026
18 checks passed
@rbuergi
rbuergi deleted the fix/1127-aisettings-phantom-user-pin branch August 19, 2026 10:33
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.

PostgreSQL schema 'user.mesh_nodes' missing — node creation fails on all pods

2 participants