feat(security-agent): complete security agent and code reviewer loop (w6a) - #5428
Merged
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.
Map only an unmatched gitUrl to Connect repository and a matched repo with an unresolved model to Back to sessions. Set persistent retry guidance on repository fetch failure. Open GitHub integration setup instead of the expired repo-picker and clear terminal guidance after the Connect action runs. Cover the Connect action with a unit test.
…tus bar Replace the half-height scrim sheet with an opaque full-window Modal. The surface pads the top safe-area inset, so the content clears the system status bar. iOS keeps the native pageSheet.
The next line already discards the Android detent on iOS, so the guard changed nothing.
The layer covered the session while the transparent Android Modal slid out. The Modal is now opaque and full height, so the cover is dead weight. Three recorded Back dismissals show no white frame without it.
An error occurred while trying to automatically change base from
consolidated-kilo-workflow-2ade
to
main
August 23, 2026 15:29
An error occurred while trying to automatically change base from
consolidated-kilo-workflow-2ade
to
main
August 23, 2026 23:01
An error occurred while trying to automatically change base from
consolidated-kilo-workflow-2ade
to
main
August 23, 2026 23:04
eshurakov
approved these changes
Aug 24, 2026
…viewer-6304 # Conflicts: # services/cloud-agent-next/src/session/session-registration.ts # services/session-ingest/src/dos/SessionIngestDO.test.ts # services/session-ingest/src/dos/SessionIngestDO.ts
pandemicsyn
approved these changes
Aug 24, 2026
pandemicsyn
left a comment
Contributor
There was a problem hiding this comment.
lgtm, just a couple minor ones from the robot. The manual remediation one seems very edge case but might be worth a peek.
Three review findings from PR #5428. - The manual remediation ledger row was admitted in the web handler after the Worker fetch returned. The queue consumer can block or fail a launch in milliseconds, and the terminal settle joins on `provider_ref = attemptId`, so a late admit left the row admitted forever. The Worker now admits the row before the queue hand-off, which also deletes the web-side duplicate. - `patchReviewConfig` never validated selection ids against the platform, so a GitHub or GitLab PATCH could persist string ids the trigger can never match. Both the full array and the delta now run through `requireRepositoryIdsForPlatform`. - The mobile session-attention acks survived sign-out under one shared storage key. Sign-out now clears the store and the encrypted-KV scope.
iscekic
added a commit
that referenced
this pull request
Aug 24, 2026
…elease-ff5f Main carries the merged w6a (#5428), which moved the remediation ledger admit into the Worker before the queue hand-off. Take main for every conflicted security file and drop the stale web-side admit in the retry handler.
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
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.