feat(desktop): connect chat to relay - #6
Merged
Merged
Conversation
Collaborator
Author
tlongwell-block
added a commit
that referenced
this pull request
Mar 10, 2026
* origin/main: Add desktop Home feed (#12) Add desktop Playwright e2e harness (#11) Update desktop icon and persist window state (#9) feat: add channel creation flow (#8) Improve message markdown display and formatting (#7) feat(desktop): connect chat to relay (#6) docs(readme): clarify desktop setup (#4) feat: add desktop app (#3) # Conflicts: # crates/sprout-test-client/tests/e2e_rest_api.rs
tlongwell-block
added a commit
that referenced
this pull request
May 14, 2026
Adds a Settings → Agent Provider panel to the desktop GUI for configuring the `sprout-agent` provider, model, API key, and behavior knobs. Settings are encrypted at rest with NIP-44 self-encryption (the user's own nostr key) and injected into the sprout-agent child's env at spawn time. The panel also gains automatic provider-URL detection based on the API key format the user pastes. ## Backend (`desktop/src-tauri/src/commands/agent_provider_settings/`) - `mod.rs` — IPC types + plaintext `StoredSettings` (Drop zeroizes `api_key`; no Debug derive on input/stored). v2 envelope binds the plaintext to its owner pubkey for rollback / envelope-swap protection. - `storage.rs` — envelope read/write, NIP-44 encrypt/decrypt with `Zeroizing<String>` for plaintext, atomic-rename writes, file-size cap, `normalize_origin` (rejects non-loopback `http://`, userinfo, query, fragment), `validate_input` (provider whitelist, control-char rejection, size caps for key/model/base_url/system_prompt, positive- int knobs). `validate_stored` mirrors the same rules on the decrypted blob at spawn time — fails closed on a rolled-back pre-validation envelope so a redirected `http://api.example.com/v1` cannot escape. - `commands.rs` — `get_*`, `save_*`, `delete_*`, `get_*_env_presence` Tauri commands. The save command trims whitespace + zeroes the input api_key before validation can early-return. - `spawn.rs` — `LoadForSpawn` enum + `EnvPairs` newtype whose Drop zeroizes every value buffer. `apply_to_command` hands each env pair to `Command::env` by reference, zeroizing the local buffer after. Spawn policy: Ok → strip OWNED_AGENT_ENV_VARS + ACP-level vars then inject; None → no-op; IdentityMismatch / Error → fail closed (strip inherited owned vars, inject nothing). - `tests.rs` — round-trip envelope I/O, identity-rotation, save-time validation (oversized prompt, zero timeouts, tiny history bytes, unknown provider, control chars, oversized fields, api-key whitespace trim, owner_pubkey v2), `apply_to_command` × `LoadForSpawn` matrix (Ok/None/IdentityMismatch-fails-closed/Error-fails-closed, openai dialect), `stored_to_env_pairs` for each dialect, R7 `validate_stored` coverage (non-loopback http, control chars in key/model/base_url, userinfo/query in base_url, unknown provider, oversized prompt, empty-key + loopback local accepted). ## Runtime integration (`desktop/src-tauri/src/managed_agents/runtime.rs`) - `build_agent_command` calls `agent_provider_settings::apply_to_command` exactly when the harness is `sprout-agent`. ACP-level vars (SPROUT_AGENT_PROVIDER etc.) are stripped from inherited parent env before injection so a stale shell `ANTHROPIC_API_KEY` never shadows saved settings. `respond-to` gate env (`SPROUT_ACP_RESPOND_TO[_ALLOWLIST]`) threads through with the new `owner_hex: Option<&str>` parameter (origin/main merge). ## Frontend - `lib/detectProvider.ts` — pure key-format detector. Recognizes Anthropic, OpenAI (legacy/proj/svcacct via fixed infix), OpenRouter, Groq, xAI, Cerebras, Together, Perplexity, Fireworks (medium), bare sk- → DeepSeek (low + ambiguity-aware), plus localhost/127.0.0.1 patterns for Ollama / vLLM / llama.cpp. Includes ADMIN_ONLY_PROVIDER_ID sentinel for `sk-ant-admin01-` which we explicitly refuse to save. Key format wins over a prefilled default base URL; an explicit non- default base URL wins back for medium-confidence keys (e.g. Fireworks + api.openai.com host). Fixture strings construct the OpenAI infix via concat so GitHub's secret scanner doesn't regex-match an inline OpenAI-shaped service-account/project key (`detectProvider.test.mjs`, `settings-agent-provider.spec.ts`). - `lib/providerCatalog.ts` — declarative catalog (id, label, dialect, isLocal, default model + base URL, key-shape hint). Drives the picker, the auto-fill on detection, the local-provider placeholder enforcement, and the per-provider model field default. - `lib/agentProviderFormState.ts` — FormState shape + reducers. `applyProviderSwitch` is the single source of truth for what gets reset on a provider change (model when empty or still previous default; baseUrl when new provider has a default OR user hasn't edited it; clears previous default host for null-default providers; drops apiKey on switch TO a local provider). Used by both the manual picker and the auto-detect effect so the policy can't drift. - `lib/agentProviderSettingsApi.ts` + `hooks/useAgentProviderSettings.ts` — typed IPC wrappers + React-Query hooks (load / save / delete / envPresence). - `ui/AgentProviderSettingsCard.tsx` — the panel itself. Empty state with shell-env hint, identity-rotation banner, load-error banner, detected-provider badge, reveal/hide toggle, advanced section, inline provider-change warning, confirm-clear dialog. On save success the plaintext is wiped from form state + reveal toggles off, independent of any React-Query refresh effect (covers the structural- sharing identical-redacted-view edge case). - `ui/AgentProviderAdvancedFields.tsx`, `AgentProviderBanners.tsx`, `AgentProviderClearDialog.tsx` — split components. ## Per-agent dialog (sprout-agent special case) - `agents/ui/CreateAgentDialogSections.tsx` — Model + System prompt inputs are hidden for sprout-agent paths (those are owned globally). A note line points users to Settings → Agent Provider. - `agents/ui/EditAgentDialog.tsx` — passes `selectedProviderId="custom"` to the shared runtime fields so the agent-command input stays editable for existing rows; the system-prompt hide still resolves via `isSproutAgentPath`'s `agentCommand` arm. - `agents/ui/ManagedAgentRow.tsx` — "Model managed by Sprout settings" link is a span with role="button" + stopPropagation (was a nested `<button>` inside the row button — both invalid HTML and double- triggering). - `agents/lib/resolveAcpProviderId.ts` — TS / Rust alignment for inline-args resolution (Rust `known_acp_provider` strips args; TS now matches). ## Tests - Rust: 328 tests passing (R7 added 7; full agent_provider_settings suite at 44/44). - TS / node-test: 55 cases for the form-state reducer + provider detector. - Playwright integration: 12 settings-agent-provider scenarios (empty state + detection + save round-trip, identity-rotation banner, provider-change-warning, key-format-beats-prefilled-baseUrl, load- error banner, clear flow + Escape cancel, rotation banner a11y, local-provider switch with saved key, manual switch reset, detected- provider model reset, post-save key-input clear). ## just ci summary - Rust: 321/321 + 7 new validate_stored tests - Mobile: 336/336 - Desktop / web: format + biome + file-size + ts-check all green - Playwright integration agents + settings-agent-provider: 19/19 green ## Codex review history - Reviews #1, #2, #3, #5, #6 surfaced and fixed: non-loopback HTTP at save AND spawn, identity-mismatch fails closed, local-provider key leak prevention, inline-args resolver alignment, Zeroize on api_key before early-return, no Debug derive, control-char / length caps + trim, owner_pubkey v2 envelope, local-provider switch unblock, EditAgentDialog command field, model reset on detection switch, nested-button fix, `applyProviderSwitch` reducer extraction. - Review #7 (P2-UI + P2-Rust): clear form.apiKey + revealKey on save success; validate decrypted settings at spawn time. - Review #8: 9/10, no blocking findings. Signed-off-by: Tyler Longwell <109685178+tlongwell-block@users.noreply.github.com> Co-authored-by: Dawn <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@sprout-oss.stage.blox.sqprod.co>
tlongwell-block
pushed a commit
that referenced
this pull request
May 22, 2026
Adds crates/sprout-relay/src/api/git/cas_publish.rs — the pure async function that turns a post-receive-pack workspace into a durable manifest CAS. Composes Dawn's GitStore primitives (put_pack, put_manifest, get_pointer, put_pointer) and Dawn's Manifest schema (canonical_bytes, validate, Inv_Closed at compose time) into the spec's step 2-7 sequence: read pointer (e, d_before) §step 3 fetch + verify m_before §step 3 + A1 detectability snapshot refs + symref-HEAD from disk (HEAD inherits parent on detach) pack new objects via pack-objects --revs §step 1-2 put_pack(bytes) -> packs/<sha256> §step 2 (A1) compose m_after (parent packs + new pack, parent = digest only) §step 5 m_after.validate() (Sami/Max/Perci #2-#4) put_manifest(canonical_bytes) -> manifests/<sha256> §step 6 put_pointer(IfMatch(e) | IfNoneMatchStar) §step 7 (CAS) Won -> CasSuccess { manifest, manifest_key } Lost -> CasError::Conflict { winner_manifest, winner_manifest_key } (→ HTTP 409, with winner for disk reconcile) The function returns *before* a Response is constructed — it is called from finalize_push, which is the unique site that builds a push 2xx, so the structural seam still enforces Theorem 1 (success-after-CAS). ## Review fixes folded in Sami's review (#1–#6) + Perci's #1 + Max's pre-CAS-validation blocker are all addressed in this commit: - **parent = bare 64-hex digest, not full key** (Perci #1, Max). Pointer body is `<digest>`; `Manifest.parent` stores the same digest, matching `Inv_RefDerivedFromParent` literally. `read_parent` strips the `manifests/` prefix before assigning. Dawn's new `MalformedParent` validator catches any drift at the write seam. - **Pre-CAS validation** (Sami #2, Max). `m_after.validate()?` runs between `compose_after` and `put_manifest`. Unsafe refnames, malformed oids, empty HEAD — all surface as `CasError::ManifestInvalid(...)` (4xx-class) before any S3 write, *not* as "valid CAS, un-clone-able output." Typed variant (not reused `ManifestReadFailed`) so logs / status mapping distinguish "client input rejected" from "stored parent failed A1" (Max + Dawn). - **Detached-HEAD fallback** (Sami #3). `snapshot_workspace_state` returns empty `head` on detached HEAD; `cas_publish` falls back to `parent.head` if non-empty. `validate()` rejects the first-push-+- detached case (Sami #4 — no parent to inherit from, manifest is un-clone-able). - **Conflict carries the winner** (Sami #5 + Dawn). `Conflict { winner_manifest, winner_manifest_key }` lets `finalize_push` invoke Eva's `reconcile_to_manifest` mechanically from the error arm, without a second pointer GET in the caller. `warn!()` at the `LostRace` site logs (pointer, expected etag, attempted manifest) for debugging concurrent-push patterns. Boxed for `clippy::result_large_err`. - **Empty-pack comment** (Sami #6). Clarified `capture_pack` returns `None` in both the delete-all (`refs_after.is_empty()`) and refs-only (`pack-objects` empty stdout) cases. - **`pointer_key` consolidated** in `manifest.rs` (Sami #1, Dawn, Max — Sami's "single source of truth" argument). `cas_publish` imports it; the duplicate definition is gone. - **`validate-invocation` test added** in `cas_publish.rs` (Sami's recommendation). Pins that a future refactor dropping the `validate?` call between `compose_after` and `put_manifest` is caught by unit test, not by every subsequent un-clone-able read. ## What this deliberately does NOT do (each with citation) - No retry on LostRace. Per Sami's TLA-action guidance: the receive-pack output is derived against a now-superseded parent; reusing it would violate Inv_RefDerivedFromParent. Client re-pushes, which re-hydrates + re-runs receive-pack against the advanced pointer — the only safe retry, which git already performs. Spec §Push step 7: 'GOTO 3 (retry) or respond non-ff' — both arms safe; we take non-ff. - No kind:30618 emission. That is derived after CAS — finalize_push calls Sami's build_ref_state_event over m_after.refs / m_after.head on Ok. Spec §Implementation Correspondence: 'kind:30618 is derived after CAS, never the commit.' - No advisory lock. Spec §Push 'no advisory lock in v1' — writer serialization is the CAS. A mutex would hide the contention Inv_NoFork proves safe. ## Tests 10 unit tests pin digest_from_key (manifest/<...> prefix invariant), compose_after (Inv_Closed coverage, sort, dedupe, refs-only-no-new-pack, first-push, parent-is-digest-not-key), validate invocation (unsafe refname + first-push-empty-HEAD both rejected pre-CAS). 244 relay tests green; clippy --tests -D warnings clean. The integration into finalize_push lands separately — Eva owns the AppState::git_store wiring + main.rs startup probe gate. This module is callable today: cas_publish(&store, repo_path, owner, repo, &refs_before) -> Result<CasSuccess, CasError>. Refs: - docs/git-on-object-storage.md §Push step 2-7, §Implementation Correspondence, §Mechanized Verification (Inv_NoFork, Inv_RefEffectApplied, Inv_RefDerivedFromParent, Inv_Closed). Also makes transport::harden_git_env pub(crate) for reuse by cas_publish's two subprocess sites (for-each-ref, pack-objects). Co-authored-by: Tyler Longwell <tyler@block.xyz>
tlongwell-block
pushed a commit
that referenced
this pull request
May 22, 2026
Adds crates/sprout-relay/src/api/git/cas_publish.rs — the pure async function that turns a post-receive-pack workspace into a durable manifest CAS. Composes Dawn's GitStore primitives (put_pack, put_manifest, get_pointer, put_pointer) and Dawn's Manifest schema (canonical_bytes, validate, Inv_Closed at compose time) into the spec's step 2-7 sequence: read pointer (e, d_before) §step 3 fetch + verify m_before §step 3 + A1 detectability snapshot refs + symref-HEAD from disk (HEAD inherits parent on detach) pack new objects via pack-objects --revs §step 1-2 put_pack(bytes) -> packs/<sha256> §step 2 (A1) compose m_after (parent packs + new pack, parent = digest only) §step 5 m_after.validate() (Sami/Max/Perci #2-#4) put_manifest(canonical_bytes) -> manifests/<sha256> §step 6 put_pointer(IfMatch(e) | IfNoneMatchStar) §step 7 (CAS) Won -> CasSuccess { manifest, manifest_key } Lost -> CasError::Conflict { winner_manifest, winner_manifest_key } (→ HTTP 409, with winner for disk reconcile) The function returns *before* a Response is constructed — it is called from finalize_push, which is the unique site that builds a push 2xx, so the structural seam still enforces Theorem 1 (success-after-CAS). ## Review fixes folded in Sami's review (#1–#6) + Perci's #1 + Max's pre-CAS-validation blocker are all addressed in this commit: - **parent = bare 64-hex digest, not full key** (Perci #1, Max). Pointer body is `<digest>`; `Manifest.parent` stores the same digest, matching `Inv_RefDerivedFromParent` literally. `read_parent` strips the `manifests/` prefix before assigning. Dawn's new `MalformedParent` validator catches any drift at the write seam. - **Pre-CAS validation** (Sami #2, Max). `m_after.validate()?` runs between `compose_after` and `put_manifest`. Unsafe refnames, malformed oids, empty HEAD — all surface as `CasError::ManifestInvalid(...)` (4xx-class) before any S3 write, *not* as "valid CAS, un-clone-able output." Typed variant (not reused `ManifestReadFailed`) so logs / status mapping distinguish "client input rejected" from "stored parent failed A1" (Max + Dawn). - **Detached-HEAD fallback** (Sami #3). `snapshot_workspace_state` returns empty `head` on detached HEAD; `cas_publish` falls back to `parent.head` if non-empty. `validate()` rejects the first-push-+- detached case (Sami #4 — no parent to inherit from, manifest is un-clone-able). - **Conflict carries the winner** (Sami #5 + Dawn). `Conflict { winner_manifest, winner_manifest_key }` lets `finalize_push` invoke Eva's `reconcile_to_manifest` mechanically from the error arm, without a second pointer GET in the caller. `warn!()` at the `LostRace` site logs (pointer, expected etag, attempted manifest) for debugging concurrent-push patterns. Boxed for `clippy::result_large_err`. - **Empty-pack comment** (Sami #6). Clarified `capture_pack` returns `None` in both the delete-all (`refs_after.is_empty()`) and refs-only (`pack-objects` empty stdout) cases. - **`pointer_key` consolidated** in `manifest.rs` (Sami #1, Dawn, Max — Sami's "single source of truth" argument). `cas_publish` imports it; the duplicate definition is gone. - **`validate-invocation` test added** in `cas_publish.rs` (Sami's recommendation). Pins that a future refactor dropping the `validate?` call between `compose_after` and `put_manifest` is caught by unit test, not by every subsequent un-clone-able read. ## What this deliberately does NOT do (each with citation) - No retry on LostRace. Per Sami's TLA-action guidance: the receive-pack output is derived against a now-superseded parent; reusing it would violate Inv_RefDerivedFromParent. Client re-pushes, which re-hydrates + re-runs receive-pack against the advanced pointer — the only safe retry, which git already performs. Spec §Push step 7: 'GOTO 3 (retry) or respond non-ff' — both arms safe; we take non-ff. - No kind:30618 emission. That is derived after CAS — finalize_push calls Sami's build_ref_state_event over m_after.refs / m_after.head on Ok. Spec §Implementation Correspondence: 'kind:30618 is derived after CAS, never the commit.' - No advisory lock. Spec §Push 'no advisory lock in v1' — writer serialization is the CAS. A mutex would hide the contention Inv_NoFork proves safe. ## Tests 10 unit tests pin digest_from_key (manifest/<...> prefix invariant), compose_after (Inv_Closed coverage, sort, dedupe, refs-only-no-new-pack, first-push, parent-is-digest-not-key), validate invocation (unsafe refname + first-push-empty-HEAD both rejected pre-CAS). 244 relay tests green; clippy --tests -D warnings clean. The integration into finalize_push lands separately — Eva owns the AppState::git_store wiring + main.rs startup probe gate. This module is callable today: cas_publish(&store, repo_path, owner, repo, &refs_before) -> Result<CasSuccess, CasError>. Refs: - docs/git-on-object-storage.md §Push step 2-7, §Implementation Correspondence, §Mechanized Verification (Inv_NoFork, Inv_RefEffectApplied, Inv_RefDerivedFromParent, Inv_Closed). Also makes transport::harden_git_env pub(crate) for reuse by cas_publish's two subprocess sites (for-each-ref, pack-objects). Co-authored-by: Tyler Longwell <tyler@block.xyz>
tlongwell-block
added a commit
that referenced
this pull request
May 22, 2026
Adds crates/sprout-relay/src/api/git/cas_publish.rs — the pure async function that turns a post-receive-pack workspace into a durable manifest CAS. Composes Dawn's GitStore primitives (put_pack, put_manifest, get_pointer, put_pointer) and Dawn's Manifest schema (canonical_bytes, validate, Inv_Closed at compose time) into the spec's step 2-7 sequence: read pointer (e, d_before) §step 3 fetch + verify m_before §step 3 + A1 detectability snapshot refs + symref-HEAD from disk (HEAD inherits parent on detach) pack new objects via pack-objects --revs §step 1-2 put_pack(bytes) -> packs/<sha256> §step 2 (A1) compose m_after (parent packs + new pack, parent = digest only) §step 5 m_after.validate() (Sami/Max/Perci #2-#4) put_manifest(canonical_bytes) -> manifests/<sha256> §step 6 put_pointer(IfMatch(e) | IfNoneMatchStar) §step 7 (CAS) Won -> CasSuccess { manifest, manifest_key } Lost -> CasError::Conflict { winner_manifest, winner_manifest_key } (→ HTTP 409, with winner for disk reconcile) The function returns *before* a Response is constructed — it is called from finalize_push, which is the unique site that builds a push 2xx, so the structural seam still enforces Theorem 1 (success-after-CAS). ## Review fixes folded in Sami's review (#1–#6) + Perci's #1 + Max's pre-CAS-validation blocker are all addressed in this commit: - **parent = bare 64-hex digest, not full key** (Perci #1, Max). Pointer body is `<digest>`; `Manifest.parent` stores the same digest, matching `Inv_RefDerivedFromParent` literally. `read_parent` strips the `manifests/` prefix before assigning. Dawn's new `MalformedParent` validator catches any drift at the write seam. - **Pre-CAS validation** (Sami #2, Max). `m_after.validate()?` runs between `compose_after` and `put_manifest`. Unsafe refnames, malformed oids, empty HEAD — all surface as `CasError::ManifestInvalid(...)` (4xx-class) before any S3 write, *not* as "valid CAS, un-clone-able output." Typed variant (not reused `ManifestReadFailed`) so logs / status mapping distinguish "client input rejected" from "stored parent failed A1" (Max + Dawn). - **Detached-HEAD fallback** (Sami #3). `snapshot_workspace_state` returns empty `head` on detached HEAD; `cas_publish` falls back to `parent.head` if non-empty. `validate()` rejects the first-push-+- detached case (Sami #4 — no parent to inherit from, manifest is un-clone-able). - **Conflict carries the winner** (Sami #5 + Dawn). `Conflict { winner_manifest, winner_manifest_key }` lets `finalize_push` invoke Eva's `reconcile_to_manifest` mechanically from the error arm, without a second pointer GET in the caller. `warn!()` at the `LostRace` site logs (pointer, expected etag, attempted manifest) for debugging concurrent-push patterns. Boxed for `clippy::result_large_err`. - **Empty-pack comment** (Sami #6). Clarified `capture_pack` returns `None` in both the delete-all (`refs_after.is_empty()`) and refs-only (`pack-objects` empty stdout) cases. - **`pointer_key` consolidated** in `manifest.rs` (Sami #1, Dawn, Max — Sami's "single source of truth" argument). `cas_publish` imports it; the duplicate definition is gone. - **`validate-invocation` test added** in `cas_publish.rs` (Sami's recommendation). Pins that a future refactor dropping the `validate?` call between `compose_after` and `put_manifest` is caught by unit test, not by every subsequent un-clone-able read. ## What this deliberately does NOT do (each with citation) - No retry on LostRace. Per Sami's TLA-action guidance: the receive-pack output is derived against a now-superseded parent; reusing it would violate Inv_RefDerivedFromParent. Client re-pushes, which re-hydrates + re-runs receive-pack against the advanced pointer — the only safe retry, which git already performs. Spec §Push step 7: 'GOTO 3 (retry) or respond non-ff' — both arms safe; we take non-ff. - No kind:30618 emission. That is derived after CAS — finalize_push calls Sami's build_ref_state_event over m_after.refs / m_after.head on Ok. Spec §Implementation Correspondence: 'kind:30618 is derived after CAS, never the commit.' - No advisory lock. Spec §Push 'no advisory lock in v1' — writer serialization is the CAS. A mutex would hide the contention Inv_NoFork proves safe. ## Tests 10 unit tests pin digest_from_key (manifest/<...> prefix invariant), compose_after (Inv_Closed coverage, sort, dedupe, refs-only-no-new-pack, first-push, parent-is-digest-not-key), validate invocation (unsafe refname + first-push-empty-HEAD both rejected pre-CAS). 244 relay tests green; clippy --tests -D warnings clean. The integration into finalize_push lands separately — Eva owns the AppState::git_store wiring + main.rs startup probe gate. This module is callable today: cas_publish(&store, repo_path, owner, repo, &refs_before) -> Result<CasSuccess, CasError>. Refs: - docs/git-on-object-storage.md §Push step 2-7, §Implementation Correspondence, §Mechanized Verification (Inv_NoFork, Inv_RefEffectApplied, Inv_RefDerivedFromParent, Inv_Closed). Also makes transport::harden_git_env pub(crate) for reuse by cas_publish's two subprocess sites (for-each-ref, pack-objects). Co-authored-by: Tyler Longwell <tyler@block.xyz> Co-authored-by: Quinn <quinn@users.noreply.sprout> Signed-off-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com>
DevYonghunT
added a commit
to DevYonghunT/buzz
that referenced
this pull request
Aug 4, 2026
완료 기준 7개 중 부분 충족이던 셋(block#3 renamed, block#6 upgrade 경로, block#7 UI 실행 증거)을 충족으로 올리고 Phase 3을 완료로 표시한다. 세션 D 시점의 판정은 각 행에 그대로 남겼다 — 표를 고치고 근거를 나중에 맞추는 순서가 아니다. WORKSPACE_CATALOG.md의 두 곳을 정정했다. §7의 renamed 「구현 상태」는 이제 미리보기와 ledger 둘 다이고, 검증 표의 「데스크톱 | pnpm test | 설정 카드 렌더」는 애초에 없던 테스트를 가리키고 있었으므로 실제 Playwright 스펙으로 바꿨다. §5에는 도출식이 버전을 빼는 것을 무엇이 고정하는지 적었다. renamed의 필드 추가에는 §4의 리더-우선 순서가 적용되지 않는다는 것도 명시했다. 그 순서는 relay에 저장돼 구버전이 읽는 provenance의 steps 어휘를 위한 것이고, Ledger는 apply command의 반환값으로만 살아 생산자와 소비자가 같은 빌드 안에 있다. steps 쪽 규칙은 그대로다. E1 계획서에 세 번째 계획 이탈을 보탰다 — Task 5의 featureGate는 구현 중에 뒤집혔고(매니페스트에 없는 ID는 fail-open이라 무음 no-op) 실제 게이트는 SettingsView의 역할 검사다. 앞서 둘만 적어 목록이 불완전했다. BASELINE에는 게이트 18줄과 재주입 결과표를 남겼다. 셋 중 둘만 단독 방어선이었고, upgrade 테스트의 캔버스 단언은 아니었다 — no_change가 캔버스 단계 앞에서 반환하기 때문이다. 그 사실과, 새 Tauri command를 더하는 세션은 mock 핸들러도 함께 더해야 한다는 것을 기록했다. Phase 3 밖에 남은 것 넷은 그대로다: generation 증가 경로, 선점의 약한 형태와 위임 실행 요청, CLI 적용 경로, 나머지 8개 업무방 콘텐츠. Signed-off-by: Dev_YongT <devyongt@gmail.com>
tkrnkym
pushed a commit
to tkrnkym/nuxx
that referenced
this pull request
Aug 4, 2026
Creating one was a text field and a toast. That is enough to name a community and nothing like enough to have one: the person doing it has just become its owner, and the two decisions that follow — who can get in, and who else is in it — are exactly the ones that are painful to discover later. The third door of "add a community" now opens a three-step setup flow that asks them while the reader is still thinking about the community rather than a week after they gave up looking for the setting. The draft starts invite-only, which is the safe default for something that does not exist yet; an open door on an empty community cannot be reconsidered after the fact. Inviting nobody is allowed, because a community of one is a legitimate thing to start and blocking on "invite somebody" would be a demand rather than an offer. The community it creates lands in the rail, since one that was created and then did not appear reads as the creation having failed. The flow is also what the eight `--nuxx-hosted-community-*` tokens were for. They were copied over with nothing reading them, which is the exact mistake AGENTS.md warns about — so this adds their Tailwind mapping and their consumer together, plus an e2e that asserts the computed colours, because a missing mapping degrades silently to inherited ones rather than failing. The light palette is deliberate and stays put under every theme: this is the one screen a person sees before they have a community, so it cannot follow a theme they have not chosen. It is a plain overlay rather than the `Dialog` primitive. That one calls `showModal()`, and a full-window flow inside the browser's top layer cannot be scrolled past on a short viewport — the same top-layer behaviour as gotcha block#6. Also drops `--nuxx-gradient-top` / `--nuxx-gradient-bottom`, two aliases with no reader at all. Signed-off-by: Claude <noreply@anthropic.com>
heintonny
added a commit
to heintonny/buzz
that referenced
this pull request
Aug 6, 2026
Wizard plan Phase 2: a provider-backed create now walks Identity → Where → Harness → Sign-in → Model → Review; a local create keeps the one-screen fast path untouched. A provider create is a chain of dependent questions — the host's catalog depends on the connection, the harness on the catalog, the auth story and models on the harness — and the wizard makes that ordering structural instead of guarded. The step model (createWizardSteps.ts) is pure and reuses the existing validity signals as gates: config-complete + host-answered opens Harness, a picked harness opens the rest, and the current step clamps back automatically when an edit invalidates a later answer (changing the host walks you back from Model to Where). The submit button exists only on the Review step; Enter mid-wizard means Next, never Create. This structurally fixes the two open provider-mode UX bugs: the AI/model section cannot render before the host answered (block#6 — it previously offered THIS computer's models for an unpicked host), and Advanced now lives on the Review step instead of overlapping the SSH controls at the bottom of the dialog (block#5). The Sign-in step consumes the per-harness auth states (probed yes/no with hint, manual side-task) and never gates — deploy-then-login stays legitimate. WhereToRunSection renders in slices (where/harness/hidden) so the wizard uses it as two step bodies while keeping the ONE component — and its in-flight host requests, probed names, and error state — mounted across steps. Its model-probe writes now re-assert the harness fields of the request they answer: draftRef can lag a frame behind the catalog write, and a fast host (or the e2e mocks) answered before React re-rendered, silently clobbering the catalog. The file-size ratchet drove the extraction of the AI-configuration section and the Advanced disclosure into their own components. E2E: the mock bridge now mocks check_backend_provider / discover_provider_harnesses / probe_provider_models, and create-wizard-screenshots.spec.ts walks every step against a mocked SSH provider (five step screenshots) and pins the unreachable-model and local-fast-path properties. where-to-run-config.spec.ts is repaired for the Radix run-target dropdown (selectOption never worked against it) and names the agent first, as the wizard requires. Known, pre-existing on this branch (unchanged here): 8 smoke tests against the redesigned instance-edit dialog (edit-agent-run-on, parts of agent-numeric-tuning/readiness) assert the old upstream layout and fail with or without this change — they need their own update pass before the upstream PR. Signed-off-by: Hein Tonny Køien <hein.tonny@koien.no>
BradGroux
pushed a commit
to BradGroux/buzz
that referenced
this pull request
Aug 23, 2026
TheSentinel454
added a commit
that referenced
this pull request
Aug 26, 2026
## Why Finish the replaceable-event slice of [tracker #2](TheSentinel454#2) and [domain issue #6](TheSentinel454#6) without disturbing the runtime/store boundary established by #6660 and #6668. PR #6700 has merged; this PR now targets current main containing its replacement lock and transaction observability. ## What - Move `Db::replace_addressable_event`, its SQL, lock/transaction instrumentation, and focused addressable/parameterized replacement tests from `lib.rs` to `replaceable.rs` - Preserve the transaction-required parameterized API, replacement ordering, rollback semantics, mention indexing, and exactly one datastore span per public operation - Keep implementation and focused PostgreSQL tests co-located; follow-up review removed the dedicated replaceable ownership source guard as low value ## Stack - Exact base: main at f249710 - Exact head: codex/issue-6-finish-replaceable-store at da01840 - Tracker: TheSentinel454#2 - Domain: TheSentinel454#6 - Test/span acceptance: TheSentinel454#17 and TheSentinel454#19 ## Non-goals - No SQL, schema, retry, timeout, lock ordering, transaction boundary, or client-visible behavior changes - No store traits, domain-handle redesign, `PgExecutor` migration, raw pool accessor, new crate, or directory reorganization - No changes to, retargeting of, or merge action on PR #6700 ## Risk Assessment Low. This is a mechanical ownership move with unchanged signatures and SQL. The primary review risk is losing or nesting instrumentation, covered by exact moved PostgreSQL tests and the cumulative production-diff audit. ## Blox Verification Author workstation: `buzz-tornquist-issue-2-store-stack` (`2046520`), exact head `2de5444e14606ce6912d1bb932bc88bffa379a77`. - `cargo fmt --all --check` — passed - `cargo clippy -p buzz-db -p buzz-relay --all-targets -- -D warnings` — passed - `cargo test -p buzz-db --quiet` — 108 passed, 200 ignored; both integration source guards passed - Native PostgreSQL: `cargo test -p buzz-db replaceable::tests:: -- --ignored --test-threads=1` — 11 passed - `cargo test -p buzz-relay --lib api::mesh_demo::tests::demo_join_forwarded_arm_round_trips_echo -- --exact --test-threads=1` — passed - Full serial relay suite was also exercised: 908 tests passed but the existing load-sensitive mesh demo test returned 504 under suite load, matching PR #6700's documented baseline; it passed in isolation and this diff does not touch that subsystem Independent exact-head Blox review: `buzz-tornquist-pr-6777-review` (`2047422`) at `2de5444e14606ce6912d1bb932bc88bffa379a77`. - Review findings — none (critical, important, or minor) - `cargo fmt --all --check` and the ownership guard — passed - `cargo clippy -p buzz-db -p buzz-relay --all-targets -- -D warnings` — passed - `cargo test -p buzz-db --quiet` — 108 passed, 200 ignored; source guards passed - Native PostgreSQL moved suite — 11 passed - `cargo test -p buzz-relay --lib` — 909 passed, 48 ignored Generated with Codex ## Superseded pre-comment restack verification PR #6700 merged before publication completed. This layer was restacked onto current main through the exact parent named above; the final cumulative tip is 2ddcc8a. Cumulative author gates passed: formatting and diff checks; buzz-db and buzz-relay all-target clippy with -D warnings; DB lib 111 passed / 200 ignored; ownership 22/22; observability 1/1; the full isolated PostgreSQL domain matrix; and relay lib 910 passed / 49 ignored. - Workstation: `buzz-tornquist-pr-6777-final-review` (`2057617`), fresh shallow checkout - Base: `f24971033178926153b49d320bd876d15d9cb2bf` - Head: `ffbeaaf00810aa359ab85818ff4820f92263e45f` - Findings: none Reviewed `base..head` for the replaceable-store extraction. The addressable replacement SQL, stale/duplicate outcomes, advisory-lock ordering, transaction rollback/commit boundaries, mention-index atomicity, and public `Db` signature are retained in `replaceable.rs`. The PR #6700 transaction timer remains around the same logical transaction through `TransactionTimer::observe`, and the public operation has exactly one datastore span. Focused addressable and parameterized tests moved with the implementation; the ownership guard excludes duplicates from `lib.rs`. Verification: format and diff checks passed; `buzz-db --all-targets` clippy passed with `-D warnings`; DB lib tests passed (111 passed, 200 PostgreSQL tests ignored); ownership (1/1) and observability (1/1) guards passed; all 11 replaceable PostgreSQL tests passed on native PostgreSQL 17 with migrations 1-32 successful; relay lib test target compiled successfully. Final worktree was detached at the exact head and clean. Complete evidence archive SHA-256: `e37f92bbde4e2343196085095b3093b1bc78196be58bc86826325e0e1639d7ea`. ## Comment-addressed restack Review follow-up removed the dedicated `replaceable_store_has_single_ownership` source test as requested. No production code changed. - Exact base: `f24971033178926153b49d320bd876d15d9cb2bf` - Exact head: `da018405cc83605362125c2d5e5a3f91492431ac` - Final cumulative tip: `6fa2f104d42c6ba85bdf62e7ccb74ceaf4a84f67` - Focused PR verification: formatting and diff checks passed; strict `buzz-db`/`buzz-relay` Clippy passed; DB lib 111 passed / 200 ignored; all 11 replaceable PostgreSQL tests passed. - Cumulative Blox gate: formatting and diff checks; strict `buzz-db`/`buzz-relay` Clippy; DB lib 111 passed / 200 ignored; ownership 21/21; observability 1/1; every moved PostgreSQL test; relay lib 910 passed / 49 ignored. - Independent re-review at this exact head: no findings; fresh exact-parent/head Blox review passed fmt/diff, strict `buzz-db` Clippy, DB lib 111 passed / 200 ignored, manual one-owner/one-span replacement checks, observability, 11 replaceable PostgreSQL tests, and relay compilation. --------- Signed-off-by: tornquist <tornquist@squareup.com>
TheSentinel454
added a commit
that referenced
this pull request
Aug 28, 2026
## Summary Finish the remaining database-store extraction tracked by [TheSentinel454#2](TheSentinel454#2) in one reviewable PR. This consolidates the previously stacked domain slices after #6782 merged. It preserves the runtime/store boundary established by #6660, #6668, #6700, and #6782 while separating database runtime infrastructure from domain-owned persistence: - `runtime/` owns pool construction and sizing, writer/reader routing, read sessions and route proofs, transaction infrastructure, observability primitives, replica fencing, health support, migrations, and cross-cutting runtime tests. - `store/` owns domain records, SQL, row parsing, locks and invariants, `Db` domain methods, focused tests, and logical-operation datastore spans. - `lib.rs` remains a 57-line compatibility facade that preserves existing crate-root paths and `Db` method signatures through re-exports. Domain coverage includes API tokens, authentication allowlists, reminders, event queries, threads, reactions, feeds, users and DMs, push, workflows/runs/approvals, relay membership and invites, product feedback, moderation/admin moderation, relay admin actions/operators, git repositories, archived identities, usage, partition maintenance, deletion, channel membership inherited from merged #6782, and the final runtime/store layout. The branch has been rebased onto current `main`. Database changes that landed there were incorporated rather than overwritten: `relay_admin_actions.rs` and `relay_operators.rs` now live under `store/`, their 27 public `Db` wrappers and existing behavior remain intact, and every wrapper has exactly one fixed-name datastore span. Concurrent changes to migration, moderation, admin moderation, and error handling are also retained. ### Exact base and head - Base: `main` at `ed11c8d8bf0a17402be5cf243724f89471530d2f` - Head: `codex/issue-2-store-extraction` at `be24430472d1a87ac5c0d6026c620cd6caea3537` ### Related issue - Structural tracker: [TheSentinel454#2](TheSentinel454#2) - Domain trackers: [#6](TheSentinel454#6), [#7](TheSentinel454#7), [#12](TheSentinel454#12), [#13](TheSentinel454#13) - Acceptance trackers: [#17](TheSentinel454#17), [#19](TheSentinel454#19) This supersedes #6783, #6784, #6787, #6788, #6789, #6792, #6820, #6794, #6796, #6797, #6798, #6799, #6804, #6805, #6806, #6808, #6809, #6811, #6812, #6813, #6814, #6815, and #6890. Their discussions remain available for review history. ### #17 / #19 acceptance - Preserves the metric names, fixed labels, transaction/lock timing boundaries, and privacy/cardinality constraints introduced by #6700. - Keeps exactly one datastore span per public logical operation, including the 27 relay-admin wrappers added on `main`. - Removes `store_ownership.rs`; physical ownership and focused source guards now enforce the boundary directly. - Leaves no `impl Db`, domain SQL, focused domain test group, or datastore span in `lib.rs`. - Preserves existing public paths such as `buzz_db::channel`, `buzz_db::event`, and `buzz_db::workflow` through crate-root re-exports while keeping internal `runtime` and `store` namespaces private. ### Non-goals - No SQL, schema, locking, transaction, retry, timeout, or client-visible behavior changes. - No generic store traits, domain handles, broad `PgExecutor` migration, new store crate, raw pool accessor, or broader directory reorganization. - No tracker issues are closed by this PR. ### Risk The cumulative diff is large but structural. Risk is primarily module-path, ownership, or conflict-resolution drift. It is mitigated by preserving public re-exports, comparing the newly moved `main` implementations to their upstream source, source guards, touched-crate compilation, PostgreSQL-backed test coverage, and an independent exact-head review on a separate clean Blox workstation. ### Testing Author workstation `buzz-tornquist-pr-6987-rebase`, rebased branch ending at exact head `be24430472d1a87ac5c0d6026c620cd6caea3537`: - `cargo fmt --all --check` - `cargo clippy -p buzz-db -p buzz-relay --all-targets -- -D warnings` - `cargo test -p buzz-db --lib` — 113 passed, 240 PostgreSQL tests intentionally ignored - `cargo test -p buzz-db --test observability_source` — 2 passed - PostgreSQL-backed `buzz-db` coverage under native PostgreSQL — 235 passed in the shared serial run; the five shared-state/config-sensitive cases passed as isolated reruns against fresh schemas, including the two owner-limit tests with their fixture's `BUZZ_MAX_COMMUNITIES_PER_OWNER=3` - `cargo test -p buzz-relay --lib -- --test-threads=1` under native PostgreSQL/Redis — 991 passed; the three current-month partition-sensitive identity-archive cases passed after provisioning the August 2026 test partition; 87 infrastructure-marked tests remained ignored - Source/diff guards — relay-admin implementation bodies match current `main`; all 27 public wrapper signatures are retained; exactly one datastore span wraps each wrapper; `lib.rs` has zero `impl Db` blocks and zero datastore spans; no duplicate top-level relay-admin modules or `store_ownership.rs`; `error.rs` matches current `main` Independent clean review workstation `buzz-tornquist-pr-6987-review`, detached at exact head `be24430472d1a87ac5c0d6026c620cd6caea3537`: - `cargo fmt --all --check` - `cargo clippy -p buzz-db -p buzz-relay --all-targets -- -D warnings` - `cargo test -p buzz-db --lib` — 113 passed, 240 ignored - `cargo test -p buzz-db --test observability_source` — 2 passed - Exact-head ownership/re-export/instrumentation audit — no remaining actionable findings --------- Signed-off-by: OpenAI Codex <codex@openai.com> Signed-off-by: tornquist <tornquist@squareup.com> Co-authored-by: OpenAI Codex <codex@openai.com>
wpfleger96
pushed a commit
that referenced
this pull request
Aug 29, 2026
## Summary Finish the remaining database-store extraction tracked by [TheSentinel454#2](TheSentinel454#2) in one reviewable PR. This consolidates the previously stacked domain slices after #6782 merged. It preserves the runtime/store boundary established by #6660, #6668, #6700, and #6782 while separating database runtime infrastructure from domain-owned persistence: - `runtime/` owns pool construction and sizing, writer/reader routing, read sessions and route proofs, transaction infrastructure, observability primitives, replica fencing, health support, migrations, and cross-cutting runtime tests. - `store/` owns domain records, SQL, row parsing, locks and invariants, `Db` domain methods, focused tests, and logical-operation datastore spans. - `lib.rs` remains a 57-line compatibility facade that preserves existing crate-root paths and `Db` method signatures through re-exports. Domain coverage includes API tokens, authentication allowlists, reminders, event queries, threads, reactions, feeds, users and DMs, push, workflows/runs/approvals, relay membership and invites, product feedback, moderation/admin moderation, relay admin actions/operators, git repositories, archived identities, usage, partition maintenance, deletion, channel membership inherited from merged #6782, and the final runtime/store layout. The branch has been rebased onto current `main`. Database changes that landed there were incorporated rather than overwritten: `relay_admin_actions.rs` and `relay_operators.rs` now live under `store/`, their 27 public `Db` wrappers and existing behavior remain intact, and every wrapper has exactly one fixed-name datastore span. Concurrent changes to migration, moderation, admin moderation, and error handling are also retained. ### Exact base and head - Base: `main` at `ed11c8d8bf0a17402be5cf243724f89471530d2f` - Head: `codex/issue-2-store-extraction` at `be24430472d1a87ac5c0d6026c620cd6caea3537` ### Related issue - Structural tracker: [TheSentinel454#2](TheSentinel454#2) - Domain trackers: [#6](TheSentinel454#6), [#7](TheSentinel454#7), [#12](TheSentinel454#12), [#13](TheSentinel454#13) - Acceptance trackers: [#17](TheSentinel454#17), [#19](TheSentinel454#19) This supersedes #6783, #6784, #6787, #6788, #6789, #6792, #6820, #6794, #6796, #6797, #6798, #6799, #6804, #6805, #6806, #6808, #6809, #6811, #6812, #6813, #6814, #6815, and #6890. Their discussions remain available for review history. ### #17 / #19 acceptance - Preserves the metric names, fixed labels, transaction/lock timing boundaries, and privacy/cardinality constraints introduced by #6700. - Keeps exactly one datastore span per public logical operation, including the 27 relay-admin wrappers added on `main`. - Removes `store_ownership.rs`; physical ownership and focused source guards now enforce the boundary directly. - Leaves no `impl Db`, domain SQL, focused domain test group, or datastore span in `lib.rs`. - Preserves existing public paths such as `buzz_db::channel`, `buzz_db::event`, and `buzz_db::workflow` through crate-root re-exports while keeping internal `runtime` and `store` namespaces private. ### Non-goals - No SQL, schema, locking, transaction, retry, timeout, or client-visible behavior changes. - No generic store traits, domain handles, broad `PgExecutor` migration, new store crate, raw pool accessor, or broader directory reorganization. - No tracker issues are closed by this PR. ### Risk The cumulative diff is large but structural. Risk is primarily module-path, ownership, or conflict-resolution drift. It is mitigated by preserving public re-exports, comparing the newly moved `main` implementations to their upstream source, source guards, touched-crate compilation, PostgreSQL-backed test coverage, and an independent exact-head review on a separate clean Blox workstation. ### Testing Author workstation `buzz-tornquist-pr-6987-rebase`, rebased branch ending at exact head `be24430472d1a87ac5c0d6026c620cd6caea3537`: - `cargo fmt --all --check` - `cargo clippy -p buzz-db -p buzz-relay --all-targets -- -D warnings` - `cargo test -p buzz-db --lib` — 113 passed, 240 PostgreSQL tests intentionally ignored - `cargo test -p buzz-db --test observability_source` — 2 passed - PostgreSQL-backed `buzz-db` coverage under native PostgreSQL — 235 passed in the shared serial run; the five shared-state/config-sensitive cases passed as isolated reruns against fresh schemas, including the two owner-limit tests with their fixture's `BUZZ_MAX_COMMUNITIES_PER_OWNER=3` - `cargo test -p buzz-relay --lib -- --test-threads=1` under native PostgreSQL/Redis — 991 passed; the three current-month partition-sensitive identity-archive cases passed after provisioning the August 2026 test partition; 87 infrastructure-marked tests remained ignored - Source/diff guards — relay-admin implementation bodies match current `main`; all 27 public wrapper signatures are retained; exactly one datastore span wraps each wrapper; `lib.rs` has zero `impl Db` blocks and zero datastore spans; no duplicate top-level relay-admin modules or `store_ownership.rs`; `error.rs` matches current `main` Independent clean review workstation `buzz-tornquist-pr-6987-review`, detached at exact head `be24430472d1a87ac5c0d6026c620cd6caea3537`: - `cargo fmt --all --check` - `cargo clippy -p buzz-db -p buzz-relay --all-targets -- -D warnings` - `cargo test -p buzz-db --lib` — 113 passed, 240 ignored - `cargo test -p buzz-db --test observability_source` — 2 passed - Exact-head ownership/re-export/instrumentation audit — no remaining actionable findings --------- Signed-off-by: OpenAI Codex <codex@openai.com> Signed-off-by: tornquist <tornquist@squareup.com> Co-authored-by: OpenAI Codex <codex@openai.com>
matt2e
added a commit
that referenced
this pull request
Sep 2, 2026
…e after send-path side effects Address the two review warnings on the publish-first mention-send stack (f3df4be4f, f0a9915b5), keeping its design intact. 1) Provider replay-floor gap — fixed via the preferred route: thread the floor into the deploy payload. start_managed_agent's provider branch now passes replay_floor_unix through deploy_to_provider, which injects it into the payload REBUILT after the deploy lock as launch.policy_env.BUZZ_ACP_REPLAY_FLOOR — the same env var a local spawn sets — so a cold remote harness's startup watermark replays back past the already-published triggering mention instead of booting blind to it. No synchronous fallback was needed: launch.policy_env already transports per-spawn env (effort level, model, session title) to providers, so the deploy handoff carries the floor cleanly. Any same-named launch.env key is stripped when a caller floor is present (that tier later-wins remotely); the floor is invocation state, never persisted, so redeploys without one never inherit a stale floor. Other deploy callers (create flow, access reconciliation, inbound personas) pass None and are unchanged. 2) Revalidation-at-publish window: the single-pass dedupe now reuses the pre-side-effect authorization pass only when nothing separated it from the publish. completeSend tracks whether an awaited relay round-trip actually ran in between — DM expansion (onPrepareSendChannel), managed-agent access-policy or membership attach writes (reported by ensureManagedAgentMentionsReady; a matching policy that returns the same record does not count), or active-huddle enrollment (sync_agents_to_active_huddle's matched_active_huddle; with no active huddle it returns before touching the relay) — and re-validates at the publish boundary when any did. The common send (member agent, no expansion, no huddle) stays single-pass. 3) startAgentDetached now depends on startAgentMutation.mutateAsync (stable) instead of the whole mutation object (fresh each render, repo gotcha #6), keeping the downstream useCallback chain reference-stable. Tests: five Rust unit tests pin the payload injection (policy_env ride, case-insensitive launch.env shadow strip, None passthrough, tolerance for missing launch/policy_env); the in-channel provider E2E spec now also pins replayFloorUnix on the detached deploy invoke. New E2E specs pin publish-boundary revalidation for the attach path (revocation injected while a delayed membership write holds the publish open strips the p tag; +2 passes with no update_managed_agent) and for a live huddle on the channel (+2 passes, +1 huddle sync); the managed relay-agent DM-expansion spec pins +2; the two fast-path specs keep pinning +1. Verified: cargo test --lib on desktop/src-tauri (3013 passed), clippy -D warnings + fmt, desktop tsc --noEmit, biome, desktop unit tests (5799 passed), full mentions Playwright smoke (81/83 — both failures are pre-existing under-load flakes, green in isolation), a 3x stress rerun of the new specs, and the five DM-expansion channels specs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
matt2e
added a commit
that referenced
this pull request
Sep 3, 2026
…e after send-path side effects Address the two review warnings on the publish-first mention-send stack (f3df4be4f, f0a9915b5), keeping its design intact. 1) Provider replay-floor gap — fixed via the preferred route: thread the floor into the deploy payload. start_managed_agent's provider branch now passes replay_floor_unix through deploy_to_provider, which injects it into the payload REBUILT after the deploy lock as launch.policy_env.BUZZ_ACP_REPLAY_FLOOR — the same env var a local spawn sets — so a cold remote harness's startup watermark replays back past the already-published triggering mention instead of booting blind to it. No synchronous fallback was needed: launch.policy_env already transports per-spawn env (effort level, model, session title) to providers, so the deploy handoff carries the floor cleanly. Any same-named launch.env key is stripped when a caller floor is present (that tier later-wins remotely); the floor is invocation state, never persisted, so redeploys without one never inherit a stale floor. Other deploy callers (create flow, access reconciliation, inbound personas) pass None and are unchanged. 2) Revalidation-at-publish window: the single-pass dedupe now reuses the pre-side-effect authorization pass only when nothing separated it from the publish. completeSend tracks whether an awaited relay round-trip actually ran in between — DM expansion (onPrepareSendChannel), managed-agent access-policy or membership attach writes (reported by ensureManagedAgentMentionsReady; a matching policy that returns the same record does not count), or active-huddle enrollment (sync_agents_to_active_huddle's matched_active_huddle; with no active huddle it returns before touching the relay) — and re-validates at the publish boundary when any did. The common send (member agent, no expansion, no huddle) stays single-pass. 3) startAgentDetached now depends on startAgentMutation.mutateAsync (stable) instead of the whole mutation object (fresh each render, repo gotcha #6), keeping the downstream useCallback chain reference-stable. Tests: five Rust unit tests pin the payload injection (policy_env ride, case-insensitive launch.env shadow strip, None passthrough, tolerance for missing launch/policy_env); the in-channel provider E2E spec now also pins replayFloorUnix on the detached deploy invoke. New E2E specs pin publish-boundary revalidation for the attach path (revocation injected while a delayed membership write holds the publish open strips the p tag; +2 passes with no update_managed_agent) and for a live huddle on the channel (+2 passes, +1 huddle sync); the managed relay-agent DM-expansion spec pins +2; the two fast-path specs keep pinning +1. Verified: cargo test --lib on desktop/src-tauri (3013 passed), clippy -D warnings + fmt, desktop tsc --noEmit, biome, desktop unit tests (5799 passed), full mentions Playwright smoke (81/83 — both failures are pre-existing under-load flakes, green in isolation), a 3x stress rerun of the new specs, and the five DM-expansion channels specs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
birdblues
added a commit
to birdblues/buzz
that referenced
this pull request
Sep 5, 2026
…side Review follow-up (Codex + Grok on e4bf6fa). Both flagged the same blocker: the WebView loaded the app door over plain HTTP and trusted the response CSP, so a LAN MITM (or a header regression) could strip the policy and restore fetch/XHR/WebSocket/subresource exfiltration — the navigation delegate only sees navigations. Grok also warned that iOS 17+ ATS may block raw private-IP HTTP in WKWebView regardless of NSAllowsLocalNetworking. Both go away by never letting the WebView touch the network, the way the desktop proxy works: - shared/relay/app_content.dart: fetchAppDocument — Authorization header, no redirects (a 3xx fails; a custom header must never follow one), text/html only, 8 MiB cap, timeout; stampSandboxCsp — the relay/desktop policy minus `sandbox` (not expressible in <meta>) inserted as the first element after a leading doctype so no script can precede it. - app_webview_page.dart: loadHtmlString with no base URL → about:blank, opaque origin, no storage. Navigation is one-shot: exactly the first main-frame about:blank load; reloads, window.open, same-URL posts and every subframe are prevented (decideAppNavigation, Codex block#2). Retries are generation-fenced so a stale callback cannot overwrite the current load (Codex block#4). Before running, Dart asks the native side whether the WebRTC hook is installed and fails closed otherwise (Codex block#3, Android included). - SandboxWebViewHardening.swift: hook loadHTMLString(_:baseURL:) instead of load(_:) — the page's only entry point now; `installed` is set only after the exchange succeeds; kill list gains WebTransport and webkitGetUserMedia (Grok block#4; desktop script updated for parity). - Info.plist: the ATS exception is removed — nothing in the WebView loads from the network any more. - message_content.dart: an `![…]()` reference to an HTML app never reaches the image decoder; without a door it renders an inert download pill (Codex block#6). New `allowAppCards` flag turned off on the forum post card and compose-note preview, which crop with a box instead of maxLines and could hide a card's safety chrome (Codex block#5). authorLabel wired on forum thread posts/replies and pulse notes (Codex block#7, Grok block#7). - relay_info.dart: a failed NIP-11 lookup while connected retries after 30 s instead of waiting for the next reconnect (Grok block#9). - AppCard: the preview is a plain tap target; Run is the one announced action (Grok block#8). - docs/sandboxed-apps.md: threat-model rows and the mobile table updated; iPad step 3 no longer checks ATS, the probe must see location.href == about:blank. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UAm5qbNPszvcwnMMbfGVGa Signed-off-by: dhseo <birdblues@mac.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Summary
Testing
desktop-check,rust-fmtdesktop-check,desktop-build,desktop-tauri-check,rust-clippy,rust-tests