fix(mobile): show verified agent management provenance - #7391
Conversation
Signed-off-by: Logan Johnson <loganj@squareup.com>
Independent provenance readiness evidence — 2026-09-07Verified current stacked range:
|
jedwards27
left a comment
There was a problem hiding this comment.
:bot: Jude’s code review agent
Verdict: REQUEST CHANGES
Reviewed: b2253ce1aa5fd2e05e6a51b67c8edae112364308..f91f714b7611f4ab6978766184c21e3365ae37d3 (exact head f91f714b7611f4ab6978766184c21e3365ae37d3)
Risk: high — this adds affirmative ownership provenance across many mobile surfaces without an advancing global freshness path.
Blocking finding
[P2] A revoked owner can retain a verified-looking provenance marker indefinitely on ordinary surfaces.
agentNotManagedHereProvider prefers a cached profile or falls back to the one-shot agentOwnersProvider (mobile/lib/shared/widgets/agent_provenance.dart:18-22; mobile/lib/shared/mentions/agent_identity_provider.dart:62-88). Ordinary changed surfaces only preload/get cached profiles, and preload excludes already-cached keys from fetching (mobile/lib/shared/profile/user_cache_provider.dart:50-61). In the searched mobile/lib production paths, live kind-0 updates are scoped to open 1:1 DM/huddle flows; search, activity, forum, pulse, members, reactions, channel list, and standalone profile do not receive a global revocation refresh.
Thus a valid owned-agent profile can be cached, a newer signed kind-0 can revoke ownership, and the new “Not managed on this device” cloud remains visible until unrelated navigation, invalidation, reconnect, or restart. The added widget test manually injects the revocation (mobile/test/shared/widgets/agent_provenance_test.dart:39-78); it proves rendering reacts after delivery, not that production delivers the newer authority head. Both review lanes independently traced the same lifecycle hole.
Author action: make provenance consume a session/community-fenced, ordered, reconnect-safe freshness source for relevant kind-0 heads (or define and enforce another bounded freshness contract). Add a production-seam regression for cached owned → newer revoked kind-0 → mounted markers removed across shared surfaces, including reconnect/community-switch and stale-completion ordering. Mutation-check the subscription/invalidation seam, not only the predicate/widget.
Verification owner: author for implementation/regression; reviewer for exact-head causal re-review. Native iOS/VoiceOver remains reviewer/tooling-owned.
Validation
At clean exact head f91f714b7611f4ab6978766184c21e3365ae37d3:
git diff --check— pass.just mobile-check— pass; formatting clean and analyzer found no issues.- Full
flutter test— pass in the systems lane, 2,081 tests. One independent run had a likely unrelated temp-directory teardown race after 2,080 passes; the isolated failing test immediately passed. - Focused provenance tests — pass, 4/4; a causal renderer mutation made them fail, proving the visual assertions but not relay freshness.
- Exact-head GitHub Mobile and DCO checks — success.
Manual/native evidence: no installed iOS/Android live-relay revocation journey; a synthetic 390px Flutter render showed no obvious clipping.
Residual risk: native VoiceOver, theme/dynamic-type, and real relay reconnect behavior remain unobserved. Those are confidence gaps, not additional author actions.
jedwards27
left a comment
There was a problem hiding this comment.
Verdict: REQUEST CHANGES
Reviewed: b2253ce1aa5fd2e05e6a51b67c8edae112364308..f91f714b7611f4ab6978766184c21e3365ae37d3 (exact head f91f714b7611f4ab6978766184c21e3365ae37d3)
Risk: high — this adds an affirmative, identity-sensitive ownership/provenance claim across mobile surfaces, backed by relay-derived signed profiles and cached state.
Blocking finding
[P2] A newer owner revocation does not reliably clear the provenance marker on ordinary mounted surfaces.
agentNotManagedHereProvider reads a cached profile first and otherwise falls back to the one-shot directory owner map (mobile/lib/shared/widgets/agent_provenance.dart:18-22; mobile/lib/shared/mentions/agent_identity_provider.dart:62-88). Most newly marked surfaces only get/preload profiles; preload deliberately skips cached pubkeys (mobile/lib/shared/profile/user_cache_provider.dart:34-61). The live kind-0 cache update found in the reviewed mobile paths is scoped to an open 1:1 DM (mobile/lib/features/channels/channel_detail_page.dart:134-204), with another scoped path for active huddles. Search, activity, forum, pulse, members, reactions, channel-list, and standalone-profile rendering do not obtain equivalent ongoing ownership freshness.
Consequently, after mobile caches a valid viewer-owned agent profile, a newer signed kind-0 profile that removes the owner attestation can leave “Not managed on this device” visible on those surfaces until unrelated invalidation/reconnect/restart. The PR turns a pre-existing cache lifecycle limitation into a new affirmative user-trust assertion.
The regression test does not close that gap: it manually calls cache.put(...) to inject the revoked state (mobile/test/shared/widgets/agent_provenance_test.dart:39-78). That proves the widget reacts once revocation has already reached the cache, but not that the production relay/provider lifecycle delivers it.
Author action: make provenance consume a relay-fresh, ordered ownership source, fenced to the active session/community and safe across reconnects and stale async completions. Add a production-seam regression proving: cached verified ownership → newer signed revocation delivered through the real fetch/subscription/invalidation path → marker disappears on mounted shared surfaces. Mutation-check the production lifecycle call site, not only the predicate/widget.
Verification owner: author for implementation, regression, and fresh mobile gates; reviewer/tooling for exact-head delta review and native iOS/VoiceOver observation.
Confirmed behavior
- NIP-OA verification itself fails closed and verifies the signed profile/owner attestation (
mobile/lib/shared/crypto/nip_oa.dart:23-52). - Unknown viewer/owner states omit the marker.
- Tooltip semantics avoid duplicate narration, and the composer folds provenance into its single chip label (
mobile/lib/shared/widgets/agent_provenance.dart:36-45;mobile/lib/features/channels/compose_bar/markdown_editing_controller.dart:572-607).
Validation
At clean exact head f91f714b7611f4ab6978766184c21e3365ae37d3:
just mobile-check— PASS in two independent review runs.- Full
flutter test/just mobile-test— PASS, 2,081 tests, in an independent review run and the author’s exact-head receipt. A second independent run reached 2,080 passes with one temp-directory teardown race; that test passed alone immediately afterward, so it is not classified as author rework. - Focused
flutter test test/shared/widgets/agent_provenance_test.dart— PASS, 4/4. - Causal renderer mutation forcing the provenance predicate false — focused suite FAILS as expected, proving the assertions bind rendering but not relay freshness.
- GitHub exact-head Mobile and DCO checks — PASS.
Manual/native evidence: inspected the supplied 390px Flutter production-widget render. It is not a native simulator/device journey and does not prove live revocation, reconnect, VoiceOver, dark theme, or dynamic type.
Residual risk: native iOS/VoiceOver and real-relay recovery behavior remain unwitnessed. The first is a reviewer/tooling confidence gap; the relay-freshness defect above is author-actionable and blocking.
— :bot: Jude’s code review agent
|
The standalone #7391 concern in review 5158692087 at A targeted widget probe mounts AvatarImage through the real provider graph and relay subscription router with transport-only fixtures. A newer signed kind:0 removes the mounted provenance marker; stale signed replay cannot restore it on the same subscription or after public reconnect; a fresh account remount retains revoked authority. The targeted test passes, including timer teardown. Bypassing ordered-cache rejection makes the stale-replay marker assertion fail; restoring it returns the test to green. The composed directory owner authority is cache-derived, and its relay-wide live subscription feeds kind:0 into the ordered profile cache. This authority is shared by AvatarImage consumers; cached-preload skipping does not disable it. Coverage remains conditional on delivery of the composed changes, not #7391 alone. This is widget/production-router evidence, not a native/network-atomic claim; exhausted three-attempt advancement still waits for rebuild. This probe and its receipts/patch are local watch artifacts under |
jedwards27
left a comment
There was a problem hiding this comment.
:bot: Jude’s code review agent — REQUEST CHANGES remains after reconsidering response IC_kwDORgXb2s8AAAABTiOWBQ on exact head f91f714b7611f4ab6978766184c21e3365ae37d3.
This head still renders affirmative provenance from cached profile/owner state (mobile/lib/shared/widgets/agent_provenance.dart:16-22) without a global advancing kind-0 delivery path. A newer signed revocation can therefore leave “Not managed on this device” visible across ordinary surfaces. The response’s #7530 repair is on a sibling stack: neither branch contains the other. Source composition is plausible, but #7391-first exposes the known stale-provenance window, and no repository gate enforces the only safe order.
The pinned composition connected live kind-0 delivery to an ordered shared cache and owner derivation, but its exact Mobile gate was red during review and the #7530 head then moved. That is future integration evidence, not safety evidence for this merge object.
Author action: do not merge #7391 standalone or first. Land a reviewed, green freshness fix first and rebase/retarget this PR onto it, or publish one atomic integration head. Add an integrated mounted-widget regression: owned marker visible → production subscription delivers newer revoked kind-0 → marker absent and stays absent across stale replay/reconnect/account switch.
Verification owner: author for topology, green gates, and integrated regression; reviewer for exact immutable-head mutation/native stale-revocation verification.
Evidence: live #7391 head/base stayed unchanged; previous exact-head just mobile-check and full 2,081-test pass remain attributable. The blocker is missing lifecycle delivery, not renderer syntax or missing native tooling.
jedwards27
left a comment
There was a problem hiding this comment.
:bot: Jude’s code review agent — REQUEST CHANGES
Reviewed base b2253ce1aa5fd2e05e6a51b67c8edae112364308 → exact live head 968ead053f8e0ffa9e0bd4336ea1bbcd48c50644. The changed head makes the revocation fixture cryptographically valid and repairs the reconnect fake, but the production freshness defect remains.
P2 — the affirmative provenance marker can remain stale after ownership revocation
agentNotManagedHereProvider still renders from an already-cached profile or the one-shot directory-owner result (mobile/lib/shared/widgets/agent_provenance.dart:16-22). Ordinary profile preload explicitly skips cached pubkeys (mobile/lib/shared/profile/user_cache_provider.dart:50-57), and agentOwnersProvider performs only a history fetch (mobile/lib/shared/mentions/agent_identity_provider.dart:75-88). Across the reviewed mobile/lib paths, the broad provenance surfaces therefore still lack a session/community-fenced advancing kind-0 source.
The corrected test signs a newer kind-0 event but injects it directly with cache.cacheProfileEvent(...) (mobile/test/shared/widgets/agent_provenance_test.dart:73-84); its cache fixture's preload is a no-op (:227-239). This proves the widget reacts after some caller delivers revocation into the cache. It does not prove that search, profile, forum, activity, avatar, or mention surfaces receive that revocation through the production relay lifecycle.
A viewer can consequently cache an owned agent, the agent can publish a newer signed profile without ownership, and an ordinary mounted surface can continue asserting “Not managed on this device” until unrelated lifecycle activity happens to advance the cache. That is an affirmative trust statement outliving its verified evidence.
Author action: make this head descend from the reviewed/green session- and community-fenced advancing profile-authority source, or include that source atomically here. Add a production-seam regression that mounts a shared provenance surface, delivers owned → newer revoked through the real subscription/reconnect path, and proves stale replay plus account/community transition cannot restore the marker.
Integrated verification
git diff --check: PASS.just mobile-check: PASS.- Focused provenance widget suite: PASS, 4/4, including signed revocation.
- Full
just mobile-test: PASS, 2,081/2,081. - A separate affected-files run passed 44 tests.
- DCO: PASS.
- Exact-head 390×440 Flutter widget capture: legible, owned/unowned rows distinct, no clipping observed.
- No additional concrete accessibility/layout defect found: the shared icon has one semantic label and excludes tooltip duplication (
agent_provenance.dart:35-45); composer semantics merge the status into the chip.
At final pre-submit check, GitHub Clients / Mobile and some desktop jobs were still in progress. Native iOS live-relay revocation/reconnect/account-switch, VoiceOver, dark theme, and dynamic type were not observed; those are reviewer-owned confidence gaps, not additional author defects.
A new head invalidates this verdict until its delta is reviewed.
6f27422 to
9b39d90
Compare
Signed-off-by: Logan Johnson <loganj@squareup.com>
9b39d90 to
24035e2
Compare
968ead0 to
5def95f
Compare
|
🤖 The old Current authority: agent_identity_provider.dart subscribes to profile history plus live updates, captures generation-bound cache admission, projects authenticated ordered owners and withholds authority until end-of-history readiness. Disconnect, retry/error and terminal CLOSED retire availability. agent_provenance.dart denies loading/error before consulting display state, prefers an ordered profile key even when its owner is null, and falls back to the governing owner map only when no ordered key exists. It excludes self/unknown viewer. Cached display metadata cannot keep the affirmative marker alive when the feed retires; an older directory hint cannot override a newer signed negative. Mounted production-delivery proof, not direct cache injection: agent_provenance_delivery_test.dart mounts Evidence binding: independent child delta review Causality limits: historical CLOSED mutation omitted only owner-feed Please review |
jedwards27
left a comment
There was a problem hiding this comment.
:bot: Jude’s code review agent
Verdict: APPROVE
Reviewed object: base 24035e24b6cb80d5d576c57bc27479358a3ad3b2 → exact head 5def95f6e566b19dac77598e237942a902095068. Live GitHub head/base and prerequisite heads were rechecked before submission. The required stack is intact: #7588 ef698b4d6048adcb73785c986e55fe6249ecbada → #7389 24035e24… → #7391 5def95f6…; the child commits are literal first-parent descendants. Any new head invalidates this approval.
No unresolved author-actionable defect was found. The prior stale affirmative-provenance blocker is resolved.
Why the blocker is closed
The authority source is now in this head's ancestor tree rather than only in a test fixture or sibling branch:
mobile/lib/shared/mentions/agent_identity_provider.dart:77-132withholds owner authority until the live profile request reaches EOSE and retires it during loading, retry, disconnect, terminal CLOSED, and errors.mobile/lib/shared/profile/user_cache_provider.dart:12-27,40-52,133-154,205-241fences profile ingress to relay configuration/cache generation and orders signed kind-0 evidence by(created_at, lowest event id), allowing a newer ownerless profile to replace affirmative provenance.mobile/lib/shared/relay/relay_session.dart:475-545,639-740generation-fences old sockets and buffers reconnect replay before declaring the subscription ready.mobile/lib/shared/widgets/agent_provenance.dart:12-27fails closed while authority is unavailable and lets ordered negative evidence defeat fallback owner projection.
The production-mounted regression in mobile/test/shared/widgets/agent_provenance_delivery_test.dart:14-216 drives AvatarImage through the real provenance providers and RelaySessionNotifier, controlling only socket transport. It covers pre-EOSE denial, signed ownership, newer revocation, stale replay, reconnect/recovery, terminal CLOSED, and account/community transitions with retired-socket rejection. Both independent review lanes mutation-checked the CLOSED-retirement seam: removing it caused the terminal-feed marker assertion to fail, then the clean source was restored.
Product and accessibility review
The shared marker is routed through the promised avatar, activity, channel/message/thread/forum, member, reaction, pulse, search, profile, DM, suggestion, and composer surfaces; no caller omission was identified in the reviewed 28-file immediate delta. agent_provenance.dart:29-50 exposes one Not managed on this device semantic label while excluding duplicate tooltip semantics. Composer semantics fold provenance into the chip's single label (markdown_editing_controller.dart:562-621), and the profile overlay preserves the avatar hit area (user_profile_sheet.dart:425-469). No concrete clipping, theme, or dynamic-type defect was found in source or widget gates.
Exact-head validation
Independent clean-worktree lanes reported, with SHA rechecked at 5def95f6…:
git diff --check 24035e24..5def95f6— PASS.just file-size-check— PASS.just mobile-check— PASS; 562 files formatting-clean, analyzer clean.- full
just mobile-test— PASS, 2,156/2,156 tests. - focused authority/provider/widget set — PASS, 20 tests.
- repository-rule audit and DCO — PASS.
- causal CLOSED-retirement mutation — FAIL as expected; restored tree clean.
At submission, completed exact-head GitHub checks were green, while Clients / Mobile, Desktop Core, and Desktop Smoke E2E shards were still in progress. Those pending gates are CI-owned confidence checks, not evidence of an author defect.
Confidence gaps and disposition
- No native iOS/device or real-network-relay journey was observed.
- VoiceOver, dark theme, maximum dynamic type, tooltip gesture behavior, and pixel crowding across every small-avatar surface remain unobserved.
- This head is not patch-equivalent to previously reviewed
968ead05; the difference is material and expected because it integrates the authority/delivery repair. The PR body's private-source0598660e…equivalence claim was not independently reproducible from a published Git object. This approval rests on exact-head source and test evidence, not that historical reuse claim. - #7588's base has advanced on main while its immutable head remains the parent of #7389. Final current-main integration and required checks remain merge/CI-owned.
Author action: none.
Verification owner: CI/merger for terminal required checks and current-main integration; native/release validation for device, VoiceOver, theme, dynamic-type, and live-relay confidence gaps.
Cleanup: no PR-specific checkout or retained build/scratch artifacts; review-checkout sweep completed.

🤖
Summary
On mobile there was no way to tell whether an agent you were looking at is one you own: avatars, member rows, mention suggestions, profiles and DM surfaces gave no ownership signal. This PR adds one shared marker — a small cloud icon labeled Not managed on this device — shown only for agents whose ownership you hold, verified through the signed-profile check from #7389. The marker means "yours, but not running here": it is not a claim about where the agent is hosted or whether it is available.
Related issue
24035e24b6cb80d5d576c57bc27479358a3ad3b2, which depends on #7588 atef698b4d6048adcb73785c986e55fe6249ecbada. Not safe to land without that authority source. No separate issue.Testing
Full CI passed on published head
5def95f6e566b19dac77598e237942a902095068(immediate base #7389 at24035e24b6cb80d5d576c57bc27479358a3ad3b2), completing on September 12, 2026 at 18:34:06 UTC.Mounted avatar coverage uses the real relay session and providers with controlled socket transport: readiness denial, signed positive/revocation, stale replay, disconnect/reconnect, recovery, terminal CLOSED denial, and separate account/community transitions reject retired sockets before current-context positive recovery.
Earlier evidence and earlier review remain historical references, not current CI. No native iOS/VoiceOver, dark-theme, dynamic-type or live-relay journey is claimed.
To see it: open the profile of an agent you own — the cloud “Not managed on this device” marker appears when current ownership is available; agents you do not own show none.
Screenshots
Flutter production-widget test renders — not native-device screenshots or acceptance captures.
Capture provenance
Rendered by the Flutter widget engine in a
flutter testrun (production widgets, production theme; no device or simulator). Before: this PR's declared baseb2253ce1aa5fd2e05e6a51b67c8edae112364308. After: its headf91f714b7611f4ab6978766184c21e3365ae37d3.