feat: mount privacy remediation, multi-root dashboard scope, native-integration notifications - #516
Conversation
The dashboard resolves a named multi-root collection (a persisted scope set with a frozen revision and canonical member order) through the daemon application transport: GET /api/multi-root/collection resolves explicit targets and /api/capabilities reports the resolver's typed states instead of a hardcoded unmounted string. Selection precedence lives in the application resolver, where a default collection can never outrank an explicit target. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
The dashboard reads one native-integration transaction status over the catalog-bound dashboard surface (GET /api/native-integration/status), answering the same application result CLI and MCP project; only the read-only status operation gains a dashboard binding, so no gateway can advance a transaction, apply edits, or mutate Git. The daemon handler publishes observed status projections into a per-project fan-out, and ready LSP sessions forward changed projections as read-only tracedecay/nativeIntegrationStatus notifications. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Project-open now spawns a bounded background PrivacyRemediation pass per adopted store: the owner re-runs the current in-process detector over every served project-memory fact, redacts what the detector can make safe, quarantines what it cannot, and settles every mutation through the canonical curation authority so a durable curation receipt records what changed. No scanner binary runs, no network is touched, and no unsanitized payload is persisted back. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
|
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
…plan' into cursor/mount-privacy-multiroot-native-511e # Conflicts: # src/daemon/project_open_owners.rs
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5e513dc2ac
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let context = | ||
| MemoryOperationContext::generated(&self.owner, "privacy_remediation_rescan", None)?; | ||
| let receipt = self | ||
| .apply_project_memory_curation(operations, confidence, context, None, write_control) |
There was a problem hiding this comment.
Chunk remediation mutations within the curation limit
When a project contains at least 257 facts requiring redaction or quarantine, this submits them as one curation batch, but ProjectMemoryFactCurationBatchV1::new rejects more than 256 operations. The rescan consequently commits nothing, leaving every sensitive fact served, and each later project open repeats the same failure; split the work within the canonical batch limit while preserving truthful receipts.
AGENTS.md reference: AGENTS.md:L141-L143
Useful? React with 👍 / 👎.
| fact.tags(), | ||
| fact.entities(), | ||
| fact.metadata(), | ||
| fact.source_label(), |
There was a problem hiding this comment.
Apply canonical source-label sanitization during rescans
For legacy facts whose source_label contains structured sensitive text, this passes the label directly into the generic JSON sanitizer and omits the current ingest path's sanitize_provider_metadata_text preprocessing. A YAML label containing an ordinary-valued vault_passphrase, for example, is only detected through structured parsing, so the rescan classifies it as clean and continues serving a value that current ingest would redact.
AGENTS.md reference: AGENTS.md:L67-L74
Useful? React with 👍 / 👎.
| "/api/multi-root/collection", | ||
| get(multi_root_api::resolve_collection), | ||
| ) |
There was a problem hiding this comment.
Bind selected-project collection reads to that project
When this route is reached as /api/projects/{project_id}/multi-root/collection, the selected state reuses active.application_invocation_executor, whose in-process daemon handshake is bound to the active project's path. The gateway rewrites the request into this shared router, and the daemon reads the scope set using that handshake path, so selecting project B can return project A's collection with the same ID—or incorrectly report B's collection missing. The route needs a selected-project/root-addressed executor or must not be exposed through the project-qualified gateway.
Useful? React with 👍 / 👎.
| let mut recent = statuses.values().cloned().collect::<Vec<_>>(); | ||
| recent.sort_by_key(|status| std::cmp::Reverse(status.updated_at)); | ||
| recent.truncate(maximum); |
There was a problem hiding this comment.
Advance status polling beyond the newest page
After more than 16 transactions are retained between LSP flushes, every poll sorts the same retained map and truncates it to the newest 16. Because the session's dedupe state is not fed back as a cursor or acknowledgement, later flushes keep receiving those already-notified entries and the older transactions are never returned, causing their native-integration notifications to be silently and permanently lost.
Useful? React with 👍 / 👎.
| let result = crate::application_surface::resolve_dashboard_application_surface( | ||
| crate::application_surface::ApplicationSurfaceOperation::NativeIntegrationStatus, | ||
| control.request_id(), |
There was a problem hiding this comment.
Preserve the dashboard request's cancellation controls
When a native-integration status request is disconnected or its admitted dashboard deadline is shorter than the catalog maximum, this helper forwards only the request ID. resolve_dashboard_application_surface creates a fresh cancellation signal and derives a new maximum deadline, so cancelling the live DashboardHttpRequestControlV1 does not cancel the daemon invocation and the read can continue after the HTTP request is gone; dispatch this operation with the supplied deadline and cancellation signal.
Useful? React with 👍 / 👎.
…plan' into cursor/mount-privacy-multiroot-native-511e
…plan' into cursor/mount-privacy-multiroot-native-511e
…-privacy-multiroot-native-511e
…ative-511e' into cursor/mount-privacy-multiroot-native-511e
70182de
into
codex/tracedecay-total-redesign-plan
Mounts the three unmounted product claims (B5/B6/B7 from the V2 implementation audit) as real production journeys. No version bumps.
B5 — At-rest privacy remediation (
feat(privacy))MemoryApplication::privacy_remediation_rescan(crates/tracedecay-usecases/src/memory/privacy_remediation.rs) re-runs the current in-process detector over every currently served project-memory fact, redacts what the detector can make safe, quarantines (removes from serving) what it cannot, and settles all mutations through the canonical curation authority — one atomic batch, one durableProjectMemoryFactCurationReceiptV1, plus a typedProjectMemoryPrivacyRemediationReceiptV1(trigger, detector revision, scanned/clean/redacted/quarantined counts).src/daemon/privacy_remediation.rs) after fail-closed admission; it never blocks admission or retrieval. No scanner binary, no network, no unsanitized payload persisted.B6 — Named multi-root collection resolver for the dashboard (
feat(multi-root))crates/tracedecay-application/src/multi_root/collection.rs): selection precedence (MultiRootCollectionSelectorV1— a default collection can never outrank an explicit target) and typed resolution states (Mountedwith the frozen scope-set revision and canonical member order, or typed unavailable: transport-not-admitted / no-collection-named / not-persisted / authority fault).GET /api/multi-root/collection?collection=<scope-set-id>resolves an explicit named collection through the daemon scope-set read (DashboardApplicationRuntime::read_multi_root_scope_set, implemented overMultiRootScopeSetRead);/api/capabilitiesnow reports the resolver's typed states instead of the hardcoded "no multi-root scope set is mounted" string, andfeatures.multi_rootreflects the mounted resolver.B7 — Native-integration dashboard status + LSP notification (
feat(native-integration))native_integration_statusoperation gains aDashboardbinding surface; every mutating transaction operation stays CLI/MCP-only, so no gateway can advance a transaction, apply edits, or mutate Git.GET /api/native-integration/status?transaction_id=…answers the sameNativeIntegrationSurfaceResultV1CLI and MCP project, resolved through the catalog-bound dashboard surface; standalone dashboards answer the typedauthority_unmountedstate.NativeIntegrationStatusProjectionV1into a per-project fan-out (NativeIntegrationStatusBroadcastV1); ready LSP sessions forward changed projections as read-onlytracedecay/nativeIntegrationStatusserver-to-client notifications with per-session dedupe. No client-callable native-integration LSP method is admitted.Verification
cargo check --all-featuresclean; clippy clean on touched crates.tracedecay-application(390),tracedecay-lsp(181),tracedecay-usecases(780) lib suites green;tests/native_integration_surface_mount(8) green; the new daemon privacy remediation test green.tracedecay-dashboard-apilib: 200 passed, 1 failure (doctor_findings_api::…_preserves_canonical_unknown_entries, 11 vs 10) — verified pre-existing by running the identical test on the base branch in a clean worktree; unrelated to this change.