feat(security-agent): complete security agent and code reviewer loop (w6a) - #5428
feat(security-agent): complete security agent and code reviewer loop (w6a)#5428iscekic wants to merge 47 commits into
Conversation
Export SECURITY_COMMAND_TYPES as the shared tuple and derive SecurityCommandType from it. Add an app-shared invalidation-scope test and an apps/web drift test asserting the db union matches the shared tuple. Type the web create-call literals and the SecurityAgentAdmissionAction redeclaration with the shared type.
Add an additive security_lifecycle variant to pushDataSchema with an eight-value event enum mapped 1:1 to SecurityAuditLogAction. Route the variant to the existing security channel and add lifecycle lock-screen copy in both exhaustive presentation switches.
… review Extend SECURITY_INTENTS to the four command intents with an authority-linked SECURITY_INTENT_FOR_COMMAND_TYPE map. Add the code_review_settled terminal event with a privacy-minimal schema and add the code_review operation ledger domain.
Add getCommandStatuses on the personal and org routers with a min(1).max(100) uuid-array input schema. Batch-fetch owner-scoped commands, omit unknown or foreign ids, and settle terminal commands through the existing ledger settle helper. Keep getCommandStatus for older mobile clients.
Require ORGANIZATION_BILLING_ROLES when an org already has a GitLab integration, on both the OAuth connect and callback paths; first-time connect keeps member access. Add a role matrix over every organization-security-agent procedure and map access denials to permission-specific error codes instead of connection errors.
Extend the notification preferences getter with a capabilities map for the seven category keys, computing availability from organization membership, enabled Security config, and KiloClaw instances.
…ions Narrow the security findings list, code reviews list, and organization list responses so heavy content is fetched only by detail procedures. The findings list nulls raw_data after decoration; the code reviews list drops council_result, manual_config, and previous_summary_body; the organization list selects explicit columns. Each narrowing ships with a response-width test and a detail-preserved test.
Emit analysis_* and remediation_* lifecycle pushes from post-commit, env-holding sites in the security-auto-analysis service. The web internal notifications route accepts a lifecycle body and dispatches push only; the notifications worker gains a security_lifecycle dispatch kind gated on securityFindingsEnabled. Terminal emits fire only when the persist actually made the attempt terminal.
The withMembers procedure stripped member-only fields at runtime but cast the result back to the admin superset type, so TypeScript reported stripe_customer_id as present on member responses. Replace the cast with a typed member variant and wire the member schemas into the response union, then narrow the union in web and mobile consumers.
A Kilo admin whose org role is member receives the stripped member DTO without inviteUrl, so the copy badge rendered but did nothing. Gate the control on inviteUrl presence.
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Executive SummaryIncremental review of 4 mobile files since Files Reviewed (4 files)
Previous Review Summaries (4 snapshots, latest commit 855c445)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit 855c445)Status: No Issues Found | Recommendation: Merge Executive SummaryIncremental review of 5 files since Files Reviewed (5 files)
Previous review (commit 0efadca)Status: No Issues Found | Recommendation: Merge Executive SummaryIncremental review of 4 mobile files since Files Reviewed (4 files)
Previous review (commit afb81fc)Status: No Issues Found | Recommendation: Merge Executive SummaryIncremental review of 18 PR files since Files Reviewed (18 files)
Previous review (commit 180cf27)Status: 3 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
Files Reviewed (139 files)
Reviewed by grok-4.6 · Input: 71.7K · Output: 4.6K · Cached: 257.4K Review guidance: REVIEW.md from base branch |
…viewer-6304 # Conflicts: # apps/mobile/src/components/agents/part-renderer.test.ts
A separately released client can talk to an old backend that omits the new remediationTimeline field, so the non-nullable type is not a runtime guarantee. Treat a missing field as an empty list instead of throwing.
Only a definitively resolved read-only permission shows the member copy. Loading and error states show the Enable button, because the mutation is server-authorized.
Summary
The security-auto-analysis worker now settles an admitted
security-domain operation-ledger row on every terminal remediation transition and dispatches a typedsecurity_lifecyclepush to the finding owner. The settle joins byprovider_ref = attemptIdand is best-effort post-commit, so a settle or push failure never rolls back the terminal persist. The push producer resolves a personal owner to the owner user and an organization owner to the organization owners, then posts the web internal notifications route with a 10-second abort.Files
services/security-auto-analysis/src/remediation.ts— admits and settles the remediation ledger row; adds the lifecycle push producers and wires them into every terminal path.services/security-auto-analysis/src/callbacks.ts— runs auto-remediation as a best-effort post-completion follow-up and dispatchesanalysis_completedandanalysis_failedpushes.services/security-auto-analysis/src/types.ts— adds the approval flag to the worker config schema and default.The
security_lifecyclepush type is added end to end. The push data schema, the internal dispatch request, the Android channel and generic preview copy, the notifications worker dispatch core, and the web internal notifications route all carry the new variant. The route dispatches lifecycle pushes after the response, so push latency never holds the producer's 10-second abort, and mobile taps route lifecycle pushes to the finding detail.Files
packages/notifications/src/push-data.ts— adds thesecurity_lifecyclevariant and event enum.packages/notifications/src/rpc-schemas.ts— adds the lifecycle internal dispatch request schema and event enum.packages/notifications/src/push-presentation.ts— maps lifecycle pushes to the security channel and generic copy.services/notifications/src/lib/internal-dispatch-push.ts— dispatches lifecycle pushes to the recipient list with the preference gate.apps/web/src/lib/notifications-worker-client.ts— adds the lifecycle dispatch client.apps/web/src/app/api/internal/security-agent/notifications/route.ts— accepts the lifecycle body and dispatches push after the response.apps/mobile/src/lib/notification-path.ts— routes lifecycle pushes to the finding detail.The manual sync, dismissal, analysis-start, and apply-auto-remediation procedures now admit
security-domain ledger rows and settle them from terminal command state, emittingsecurity_command_settled. A new boundedgetCommandStatusesbatch procedure reads up to 100 command ids and settles each terminal command; the singlegetCommandStatusstays for older clients. The findings list response now nulls the raw Dependabot alert JSON, and the analysis detail response adds an ordered remediation timeline.Files
apps/web/src/lib/security-agent/router/shared-handlers.ts— adds the ledger admission and settle helpers, the batch status handler, the list DTO, and the remediation timeline query.apps/web/src/lib/security-agent/db/security-commands.ts— adds the batch status reader.packages/db/src/security-agent-command-repository.ts— adds the batch command reader.packages/db/src/index.ts— exports the batch reader.apps/web/src/lib/security-agent/core/schemas.ts— adds the batch input schema and the approval save field.apps/web/src/routers/security-agent-router.ts— registers the batch procedure.apps/web/src/routers/organizations/organization-security-agent-router.ts— registers the organization batch procedure.apps/web/src/components/security-agent/security-agent-command-copy.ts— reuses the shared command-type union.Code review creation now admits a
code_review-domain operation-ledger row, and every terminal transition settles it, emitting onecode_review_settledoutbox event. The settle is a compare-and-set, so callback redelivery and the stale-review reaper are idempotent, and the reaper settles inside its terminalize transaction so a settle failure rolls back the claim. The list row type drops the heavy council result, manual config, and previous summary columns.Files
apps/web/src/lib/code-reviews/code-review-ledger.ts— new; maps terminal status to ledger outcome and settles with the outbox event.apps/web/src/lib/code-reviews/db/code-reviews.ts— admits the ledger row at both creation paths and narrows the list row type.apps/web/src/app/api/internal/code-review-status/[reviewId]/route.ts— settles on completion, terminal short-circuits, and non-analytics terminal updates.apps/web/src/lib/code-reviews/reap-stale-reviews.ts— settles inside the terminalize transaction.apps/web/src/lib/code-reviews/core/schemas.ts— removes the old list response types.packages/db/src/operation-ledger.ts— adds thecode_reviewdomain.apps/web/src/app/api/webhooks/bitbucket/[integrationId]/route.ts— admits and settles ledger rows for webhook-created and superseded reviews.apps/web/src/lib/integrations/platforms/bitbucket/manual-code-review-trigger.ts— admits and settles ledger rows for manually triggered reviews.The shared analytics event map gains the
code_review_settledandsecurity_command_settledterminal events with the DEC-05 base fields, plus thesecurityandcode_reviewintent enums. The durable outbox validates every settle payload against these strict schemas.Files
packages/app-shared/src/analytics/event-map.ts— adds the two events, the intent enums, and the command-type-to-intent map.Auto-remediation now defaults to requiring approval. The policy rejects automatic admission with
approval_requiredwhen the flag is set, and a manual start is the approval path. The include-existing bulk command is skipped while approval is required, because every candidate would be rejected. The web and mobile settings forms expose the toggle, and the config schema, save input, and capability copy all carry the new flag.Files
packages/worker-utils/src/security-remediation-policy.ts— adds the flag to the config type and theapproval_requiredrejection reason.apps/web/src/lib/security-agent/core/types.ts— adds the schema field with a true default.apps/web/src/lib/security-agent/core/constants.ts— adds the default value.apps/web/src/lib/security-agent/db/security-config.ts— skips the include-existing remediation command when approval is required.apps/web/src/lib/security-agent/db/security-remediation.ts— passes the flag into the policy config.apps/web/src/components/security-agent/security-config-types.ts— adds the form state, source, and save payload fields.apps/web/src/components/security-agent/SecurityConfigForm.tsx— renders the approval switch and guards unsaved changes.apps/web/src/components/security-agent/SecurityConfigPage.tsx— hydrates the field from the config.apps/web/src/components/security-agent/SecurityAgentContext.tsx— threads the field through config save.packages/app-shared/src/security-agent/presentation.ts— adds theapproval_requiredunavailable copy.apps/mobile/src/components/security-agent/automation-settings-screen.tsx— renders the approval toggle and the enable confirmation.organizations.withMembersnow returns a role-gated union: member callers get a stripped shape without the Stripe customer id, the invitation secret, or per-member daily usage, while admin-and-above keep the full shape. The code review list and detail similarly null internal ledger and check-run identifiers for non-admin organization callers. Every consumer migrates to the union type and guards the stripped fields.Files
apps/web/src/lib/organizations/organization-types.ts— adds the member and admin response variants and the public member schemas.apps/web/src/lib/organizations/organizations.ts— no behavior change; type-only import updates.apps/web/src/routers/organizations/organization-router.ts— returns the role-gated member shape.apps/web/src/routers/code-reviews/code-reviews-router.ts— nulls internal identifiers for non-admin callers.apps/web/src/app/(app)/cloud/mcp-gateway/McpGatewayDetailContent.tsx— migrates to the union type.apps/web/src/components/organizations/FreeTrialWarningBanner.tsx— migrates to the union type.apps/web/src/components/organizations/FreeTrialWarningDialog.tsx— migrates to the union type.apps/web/src/components/organizations/OrganizationContextWrapper.tsx— migrates to the union member type.apps/web/src/components/organizations/OrganizationInfoCard.tsx— reads the Stripe id only on the admin variant.apps/web/src/components/organizations/OrganizationMembersCard.tsx— guards the stripped invite URL and migrates to the union type.apps/web/src/components/organizations/SSOSignupCard.tsx— migrates to the union type.apps/web/src/components/organizations/groups/drawer/GroupDetailsPanel.tsx— narrows active members through the union.apps/web/src/components/organizations/members/EditDailyUsageLimitUsdDialog.tsx— migrates to the union member type.apps/web/src/components/organizations/members/MemberRoleDropdown.tsx— migrates to the union member type.apps/mobile/src/components/organization/invited-member-row-state.ts— gates the share action on the invite URL and adds resend.apps/mobile/src/components/organization/invited-member-row.tsx— offers share only when the invite URL is present.apps/mobile/src/components/organization/member-limit-sheet.tsx— silences the update toast for inline errors.apps/mobile/src/components/organization/members-screen.tsx— no behavior change; type-only updates.apps/mobile/src/lib/hooks/use-organization-mutations.ts— preserves the union variant in optimistic updates.apps/mobile/src/lib/hooks/use-organization-queries.ts— derives the union member types.apps/mobile/src/components/code-reviewer/review-detail-screen.tsx— reads the redacted-ids flag from the detail response.apps/mobile/src/components/code-reviewer/review-detail-sections.tsx— renders the gate check run as hidden when redacted.The mobile command observer now polls tracked Security Agent commands through one bounded
getCommandStatusesbatch for the first 100 ids, with per-command overflow and a per-command fallback for older servers. Asecurity_lifecyclepush, app foreground, or reconnect invalidates the affected findings and command-status queries for the scope. The shared command helpers define the batch split, the missing-procedure signature, and the per-command failure copy.Files
apps/mobile/src/lib/hooks/use-security-agent-commands.ts— batch polling, overflow fallback, and scope-key invalidation.apps/mobile/src/lib/security-agent.ts— batch limit, split, missing-procedure detection, and reconciliation.apps/mobile/src/lib/hooks/use-security-findings.ts— tracks the command id only when present.apps/mobile/src/lib/hooks/use-security-lifecycle-invalidation.ts— subscribes to push, foreground, and reconnect invalidation.apps/mobile/src/app/(app)/_layout.tsx— mounts the lifecycle invalidation hook.packages/app-shared/src/security-agent/commands.ts— shared command types, invalidation scopes, and failure copy.The mobile app now renders the Security Agent audit report natively, with loading, empty, retryable, and non-retryable states. The organization procedure is billing-gated, so a forbidden viewer sees a non-retryable empty state.
Files
apps/mobile/src/components/security-agent/audit-report-screen.tsx— renders the report summary and per-finding collapsible sections.apps/mobile/src/app/(app)/(tabs)/(3_profile)/security-agent/[scope]/audit-report.tsx— the route.apps/mobile/src/components/security-agent/audit-report-button.tsx— header action that opens the native report.Review memory gains a paginated
listProposalsPageprocedure with keyset cursors, and the mobile app renders a Review memory screen with loading, empty, off, and error states plus an enable action for owners. The array-shapedlistProposalsstays for the web panel and stale clients.Files
apps/web/src/lib/code-reviews/review-memory/db.ts— adds the paginated list with cursor encode and decode.apps/web/src/routers/code-reviews/review-memory-router.ts— adds thelistProposalsPageprocedure.packages/trpc/src/mobile.ts— exposes the review memory router to mobile.apps/mobile/src/components/code-reviewer/review-memory-screen.tsx— renders the paginated proposals and enable action.apps/mobile/src/app/(app)/(tabs)/(3_profile)/code-reviewer/[scope]/review-memory.tsx— the route.apps/mobile/src/components/code-reviewer/platform-overview-rows.ts— adds the review memory overview row.apps/mobile/src/components/code-reviewer/platform-overview-screen.tsx— wires the review memory navigation.Repository selection for Code Reviewer now saves as a debounced delta patch instead of a full-array overwrite, so rapid toggles are not lost and a refetch cannot clobber pending intent. The personal and organization patch procedures accept
selectedRepositoryDeltaand reject sending both the delta and the full array. The mobile overview keeps the shared numeric-id narrowing for the personal path.Files
apps/mobile/src/lib/hooks/use-code-reviewer-repo-selection.ts— debounced delta send with rollback and refetch resync.apps/mobile/src/lib/hooks/use-code-reviewer.ts— numeric-id narrowing, review memory hooks, and the delta save path.apps/mobile/src/app/(app)/(tabs)/(3_profile)/code-reviewer/[scope]/[platform]/(edit)/repos.tsx— uses the debounced toggle.apps/web/src/routers/code-reviews-router.ts— adds the personalselectedRepositoryDeltapatch support.apps/web/src/routers/organizations/organization-code-reviews-router.ts— adds the organizationselectedRepositoryDeltapatch support.The notification preferences response now includes a per-category capability map, and the mobile notifications screen disables categories that do not apply with the server's reason. Balance alerts require an organization, security findings require an enabled Security Agent, and KiloClaw activity requires an instance.
Files
apps/web/src/routers/user-router.ts— computes the capability map and returns it.apps/mobile/src/components/notifications-screen.tsx— renders the per-category disabled state and reason.Session attention acknowledgements are now durable: acks persist to the mobile encrypted KV store under one storage key and survive app restarts, with a 7-day expiry and same-session re-raise handling. The store hydrates lazily so the synchronous API stays free of the native SQLCipher chain.
Files
apps/mobile/src/lib/session-attention.ts— durable ack store with lazy hydration and reconciliation.apps/mobile/src/lib/storage-keys.ts— adds the storage key.Standalone patch parts now render in the mobile transcript as a file-count summary with the file list, instead of rendering nothing. The visibility predicate counts a patch part as content only when it carries files.
Files
apps/mobile/src/components/agents/part-renderer.tsx— renders the patch summary and file list.apps/mobile/src/components/agents/part-types.ts— adds the patch part guard.apps/mobile/src/components/agents/message-visibility.ts— counts patch parts with files as content.Replacing an existing organization GitLab integration now requires a billing role, while a first-time connect keeps member-level access. Self-hosted GitLab OAuth requires custom client credentials, and the connect and callback handlers surface the new denial codes.
Files
apps/web/src/lib/integrations/oauth/common.ts— adds the access-denial code mapping.apps/web/src/lib/integrations/oauth/platforms/gitlab-connect.ts— billing-gates replacement and stores custom credentials.apps/web/src/lib/integrations/oauth/platforms/gitlab-callback.ts— re-checks the billing gate and loads cached credentials.apps/web/src/components/integrations/GitLabIntegrationDetails.tsx— maps the new error codes to copy.The mobile finding detail renders the ordered remediation timeline returned by the analysis procedure, with local labels for each remediation audit action and a fallback to the raw action string.
Files
apps/mobile/src/components/security-agent/finding-remediation-panel.tsx— renders the timeline and attempt history.Tests: 52 test files changed across web, mobile, packages, and services.
Generated: none.
Verification
Two verification rounds ran three cases on iOS.
Defects reproduced on the unfixed build:
Visual Changes
Security Agent audit report screen
The user now sees an Audit report screen with a "No recorded activity" empty state and a date-range reason line. The picture shows the "Audit report" header at the top left and the empty-state text with the date range in the middle of the screen.
Review memory screen
The user now sees a Review memory screen with an off state, a reason line, and an "Enable review memory" button. The picture shows the "Review memory" header under "CODE REVIEWER" at the top left and the off state with the button below it.
Notifications screen
The user now sees disabled notification categories, each with a reason line. The picture shows the dimmed "Balance alerts" and "Security findings" rows in the CATEGORIES list, each with its reason line under the label.
Reviewer Notes
Notes: none.
Human steps: none.