Skip to content

feat(people): add provider-neutral sweep profiles - #694

Open
salmonumbrella wants to merge 33 commits into
kenn-io:mainfrom
salmonumbrella:feat/person-provider-registry
Open

feat(people): add provider-neutral sweep profiles#694
salmonumbrella wants to merge 33 commits into
kenn-io:mainfrom
salmonumbrella:feat/person-provider-registry

Conversation

@salmonumbrella

Copy link
Copy Markdown
Contributor

What changed

  • Add named people-sweep profiles for OpenAI Chat, OpenAI Responses, Anthropic Messages, Google Generate Content, and the attested Codex boundary.
  • Add guarded provider add, check, consent, use, status, and remove flows, with credentials kept outside config.toml.
  • Pin checks, primary calls, repairs, budgets, and history to one exact profile identity without automatic provider switching.
  • Use models.dev only for optional interactive setup hints; runtime sweeps never depend on it or send it archive data or credentials.

Why

#685 established the durable people-sweep worker but left provider setup tied to one transport shape. Named profiles make that boundary reusable across protocols while preserving explicit consent, hard budget fences, and fail-closed credential handling.

Usage

msgvault person provider add glm --custom \
  --protocol openai_chat \
  --endpoint https://api.z.ai/api/paas/v4 \
  --model glm-5.3 \
  --auth bearer \
  --credential-env ZAI_API_KEY \
  --retention-posture provider-declared \
  --training-posture provider-declared \
  --source conversation_text \
  --source-since 2026-01-01 \
  --yes
msgvault person provider consent glm --yes
msgvault person provider use glm
msgvault person sweep run --limit 5

Closes #693

@roborev-ci

roborev-ci Bot commented Aug 26, 2026

Copy link
Copy Markdown

roborev: Combined Review (9419cac)

Verdict: Two medium-severity issues remain; no high or critical findings.

Medium

  • cmd/msgvault/cmd/person_provider_setup.go:238-265 — In remote mode, provider add writes local configuration and credentials, then proxies validation to a remote daemon that cannot access those local records. Reject this workflow with an actionable message or implement secure daemon-side provider setup.

  • cmd/msgvault/cmd/person_provider_setup.go:280-311, internal/peoplesweep/capability_check.go:47-53codex_app_server cannot be added through the documented onboarding command because the CLI requires endpoint/auth fields that Codex validation forbids, while capability negotiation excludes Codex. Add a Codex-specific onboarding path or remove it from generic onboarding and document manual configuration.


Reviewers: 2 done | Synthesis: codex, 9s | Total: 27m39s

TestCredentialStoreDeleteConsumesGuardWhenProfileNameIsInvalid deadlocked
CI: it asserted the surviving credential with store.Load while the
PreflightDelete guard still pinned the credential namespace flock. The
production contract is deliberate (openExistingCredentialDelete holds
LOCK_EX until Close so concurrent setup processes serialize), and flock is
bound to the open file description, so the same process blocks on its own
open guard. Every other test already closes guards before ordinary store
operations.

Close the consumed guard first, then load. The load runs bounded in a
goroutine so a future Close that leaks the namespace lock fails this test
in seconds with a pointed message instead of hanging the package until the
go test timeout fires.
With [remote].url configured, person provider add published the provider
profile to the local config file and the credential to the local token
store, then proxied the mandatory check to the remote daemon. That daemon
reads its own host's config and credential store, so the validation could
never see the records the CLI just wrote and the whole setup rolled back
after touching local state.

Reject the operation up front with an actionable message: run setup on the
daemon host, or target a local daemon with --local. No credential
transport to the remote daemon is introduced; people provider secrets
deliberately never cross the daemon request boundary. The new test pins
that remote setup performs no catalog fetch, config read, credential
publication, or proxy call before refusing.
Generic person provider add demands endpoint and auth values, codex
validation forbids exactly those fields, and capability negotiation
deliberately excludes the Codex process transport, so the generic path
could only fail with misleading errors (required-field complaints or
'codex_app_server does not accept HTTP or credential fields').

Reject --protocol codex_app_server up front with the supported manual
path, drop the unreachable codex case from catalog auth resolution, and
document the manual configuration: a [people.sweep.providers.<name>]
table with model and reasoning_effort but no endpoint/auth/credential
keys, followed by person provider login, check, and consent. Note that
the Codex transport stays fail-closed until its isolation gate releases
a verified build.
The new final identity check in editConfigWithMatch rejected every config
creation on Windows with 'committed config identity changed before
return' (CI run 32995343496: the CardDAV account-save test in
internal/api and eight person-provider tests in the cli shard). Root
cause: creation pins the expected mode to 0600, but Go derives Windows
permission bits from the read-only attribute only (0666 writable, 0444
read-only) and can never observe 0600. capturePublishedConfigVersion
then refused to adopt the published identity and the final comparison
failed on both mode and identity, deterministically, for each
missing-file publication.

Introduce sameConfigModePerm: exact permission equality on Unix-family
platforms, where stat observes real bits, and writable/read-only
equivalence on Windows, where that distinction is the only observable
mode information. The owner-only DACL that actually protects Windows
configs is already verified separately by validateOpenedConfigSecurity,
and a read-only flip is still detected.

Windows regression coverage (edit_mode_windows_test.go) runs the real
missing-file creation path and the comparator equivalence classes; it
executes on the Windows CI matrix, which is the only place the Windows
stat behavior exists. A unix guard test keeps equality strict on
platforms that observe real permission bits.
…ential-store test gating

codex_app_server validation on the named-profile path requires auth =
"none" and credential = "none" explicitly (only the legacy table decode
defaults them), so the documented manual example omitted values that
Validate rejects. Update docs/configuration.md to include both keys and
reword the guidance so only endpoint is forbidden while auth/credential
must equal "none"; align the person provider add rejection message and
its focused test expectation with the same guidance. Config defaulting
behavior is unchanged.

Gate the stored people provider credential lifecycle tests on
linux/darwin via requireStoredCredentialStorePlatform: the file-backed
store needs secure no-follow atomic filesystem operations and fails
closed elsewhere (covered by the peoplesweep fail-closed test), so its
happy-path transactions can only be proven where the store is
implemented. The frontend remove proxying test keeps an env credential
and runs on every platform, preserving Windows coverage there; the
stored-credential sweep subtest skips only for the stored case.
@roborev-ci

roborev-ci Bot commented Aug 29, 2026

Copy link
Copy Markdown

roborev: Combined Review (7f31f62)

Verdict: One medium-severity configuration consistency issue identified; no security issues found.

Medium

  • cmd/msgvault/cmd/person_provider.go:359-465, cmd/msgvault/cmd/serve.go:432-434provider use and provider remove modify local configuration without checking for a remote daemon or updating a running local daemon. Commands may report success while the scheduler continues using stale startup configuration, so provider changes have no effect on future sweeps. Reject remote mutations or update and reload the daemon configuration; otherwise clearly require a daemon restart.

Reviewers: 2 done | Synthesis: codex, 6s | Total: 19m28s

Resolve the five content conflicts between the provider-neutral sweep
registry and main's person enrichment (kenn-io#686) plus recent fixes:

- internal/peoplesweep/config.go: drop peoplesweep.PeopleConfig (moved to
  internal/config as the Sweep+Enrichment sibling container on main) while
  keeping the PR's ProviderSelection legacy-table decoding.
- cmd/msgvault/cmd/person_provider_daemon_test.go: keep the PR's registry
  and credential-store daemon harness, reference config.PeopleConfig.
- internal/api/cli_allowlist_person_provider_test.go: keep both imports;
  registry-based provider tests plus main's enrichment allowlist tests.
- internal/api/cli_handlers.go: union of both sides' strict command
  validators and env-forwarding rules. person sweep run still forwards
  only the configured provider key; person provider check keeps rejecting
  request-carried credentials (daemon resolves from its own environment
  and credential store); person enrichment run/suppress forward the
  suppression key and the named enabled provider key per main.
- internal/config/people_sweep_test.go: keep the PR's named-profile,
  save/reload, legacy-migration, and rejection tests plus main's
  enrichment sibling test; adapt main's legacy-compat test to resolve the
  active provider through the registry API.
The default providerStoreOwnedByDaemon probe is compatibility-sensitive,
so a live daemon left running across a CLI upgrade reports no runtime and
provider use/remove omitted the daemon restart guidance even though that
daemon's scheduled sweeps still serve the startup config.

Keep the shared ownership dependency unchanged (check/revoke routing must
never proxy to an incompatible daemon) and add a separate, smallest
liveness signal daemonAliveForRestartNotice built on the established
findAnyDaemonRuntime semantics (new context-aware
findAnyDaemonRuntimeContext). personProviderMutationScope consults it
only when ownership found no compatible daemon, so routing decisions are
unchanged while the restart notice now covers the incompatible-live case.

Regression test fakes a responding incompatible daemon (real ping
endpoint plus runtime record with a mismatched API version, the same
pattern as the restore-into-live-home guard tests) and proves use/remove
keep the mutation local, proxy nothing, and still print the restart
notice.
@roborev-ci

roborev-ci Bot commented Aug 29, 2026

Copy link
Copy Markdown

roborev: Combined Review (44b9ef2)

Verdict: The change has one high-severity credential-exfiltration risk and one medium-severity Codex eligibility bug.

High

  • Catalog-controlled endpoint can receive credentials during onboarding
    Location: cmd/msgvault/cmd/person_provider_setup.go:232, endpoint selection at person_provider_setup.go:372
    When no endpoint is supplied, the command uses one from the fetched models.dev catalog. Because the validator accepts any syntactically valid HTTPS host, a compromised catalog could redirect authenticated capability negotiation—and the user’s credential—to an attacker-controlled server. Require an explicitly supplied or independently trusted endpoint, or enforce signed/allowlisted catalog endpoints.

Medium

  • Codex provider checks always fail driver-version validation
    Location: cmd/msgvault/cmd/person_provider.go:1260
    Codex profiles default to codex-app-server-v2, while the Codex driver returns an attestation-derived identity such as codex-app-server-v2:<hash>. Exact comparison rejects successful checks, preventing profiles from becoming eligible for consent and sweeps. Align the stored identity with the attested version or compare the driver family separately while validating the exact attested identity.

Reviewers: 2 done | Synthesis: codex, 10s | Total: 22m6s

Codex app-server profiles configure the bare driver family
(codex-app-server-v2) while the driver attests its identity as
codex-app-server-v2:<attestation-digest>. The provider check gate compared
the two with plain equality, so every codex_app_server profile failed with
a mismatched driver version before consent or sweeps could ever succeed.

Add peoplesweep.DriverVersionMatches: the configured version is satisfied
by an exact match, or for the codex family only by an attested identity
whose family prefix matches exactly and whose suffix is a canonical
lowercase SHA-256 digest. Digest-suffixed identities stay rejected for
every other driver family. The check command now uses it, keeping unsafe
attested values failing closed with no recorded check.
Person provider add let the models.dev catalog choose the endpoint that
onboarding transmits a credential to: when the operator omitted
--endpoint, the catalog suggestion filled it, the key was then read and
sent there during capability negotiation, and only later policy
validation could fail. A compromised catalog could therefore redirect
API keys to itself.

Onboarding now pairs a credential only with an endpoint the operator
explicitly supplied via --endpoint or with a first-party API host
compiled into the binary (IndependentlyTrustedEndpoint: api.openai.com,
api.anthropic.com, generativelanguage.googleapis.com over HTTPS with the
default port, for their own protocols). The gate runs while the
candidate is resolved, before any credential is read, contacted, or
published, and credentialless profiles are unaffected. Catalog-driven
transport resolution keeps working against first-party defaults, and the
same catalog-listed endpoint stays eligible when passed explicitly.
…tests

The Windows CI run for this branch failed on five exact points; fix each
without skipping Windows coverage:

- peoplesweep runner tests now use a local credential constant: the shared
  credentialCanary lives only in the linux/darwin-tagged credential store
  tests, so the untagged runner_test.go failed to build on Windows.
- retireExactConfigForMissingRestore reopened the rollback target by name
  with openConfigNoFollow and never compared the opened file against the
  pinned published identity, so an operator's byte-identical replacement
  substituted between the initial read and the rollback was quarantined as
  if it were the published config. Retain through
  retainWindowsConfigArtifact(current.Path, current.identity), which both
  pins with attribute-only access and refuses a substituted identity with
  ErrConfigConflict.
- The final-boundary symlink swap surfaces as ErrUnsafeConfigTarget on
  Windows (the reparse point is rejected as a non-regular file before any
  identity comparison) while remaining ErrConfigConflict elsewhere; the
  boundary test now expects the platform-correct sentinel.
- The table-edit mode assertion uses sameConfigModePerm so Unix keeps exact
  0640 equality while Windows asserts the writable/read-only distinction
  its stat reporting can actually observe.
- The final-read replacement race uses a build-tagged swap helper: Windows
  publishes the operator's byte-identical replacement through the
  established ReplaceFileW primitive (os.Rename's MoveFileExW fails with
  Access denied against the retained live target), other platforms keep
  os.Rename.
@roborev-ci

roborev-ci Bot commented Aug 29, 2026

Copy link
Copy Markdown

roborev: Combined Review (1b1ea46)

Verdict: 2 medium-severity issues require attention; no critical or high-severity findings.

Medium

  • internal/peoplesweep/credential_store.go:166-174 — If root.save succeeds but root.pinCleanup fails, SaveNew returns an error while leaving the newly written credential behind, causing retries to report that it already exists. Remove the credential on post-save failure or retain cleanup protection until pinning succeeds.

  • internal/peoplesweep/worker.go:675-692recordCompletedCall appends a completed usage record with an empty request ID before rejecting unsafe metadata. Finalization then fails, leaving the attempt and lease pending until expiry. Validate metadata before appending; on invalid metadata, finalize without a completed record while conservatively accounting for reserved work.


Reviewers: 2 done | Synthesis: codex, 7s | Total: 24m17s

SaveNew published the credential file and only then pinned its cleanup
guard. When pinCleanup failed, the publication was already durable but
no guard existed, so the file stranded on disk with no way to retire it
through the cleanup path, and a retry saw the record as present and
could not create.

Add retirePublished to the pinned credential-store root: while the
namespace lock and directory FD are still held, it opens the published
entry with no-follow semantics, verifies it still carries the exact
identity recorded by save, wipes it to a durable empty record (never
unlinking a pathname), revalidates the identity, and fsyncs the
directory. SaveNew invokes it immediately when pinCleanup fails and
joins any rollback failure into the returned error.

A narrow failedCleanupPin test hook (nil in production) injects the pin
failure for the regression test, which proves SaveNew errors, Load
reports ErrCredentialNotFound, and a retry creates successfully.
recordCompletedCall appended a completed usage record before validating
the provider response. When the response was untrustworthy (unsafe
identity metadata, negative usage, diverged call identities) or
unaccountable (overflow-scale token usage), the appended record was
handed to failure finalization, which then either wrote untrusted
values into durable history as a succeeded provider call or failed
outright on the unaccountable actuals — leaving the attempt running and
the lease held until expiry, with nothing charged.

Validate and account before appending: a rejected response now
contributes no completed record, so FinalizePersonSweepFailure marks
the started batch failed and conservatively charges its reservation,
terminates the attempt, and releases the lease for requeueing.

Regression coverage:
- worker-level table test proving untrusted responses (unsafe request
  ID, negative usage, diverged identities) finalize without a completed
  record while still carrying every reservation, and retain records
  from earlier trustworthy calls;
- end-to-end worker + store + provider test proving an unaccountable
  usage response leaves no pending lease or attempt and charges exactly
  the reserved request, tokens, and cost, leaving the person
  reclaimable.
…ep responses

The missing-response-model and mixed-model-version end-to-end tests still
expected the provider-reported token usage of rejected responses to reach
durable attempt history. Since 74f53cb a rejected response contributes no
completed usage record, and failure finalization conservatively charges the
call's reservation instead.

Derive the expected charge from the fixture: the test provider now captures
the exact wire bytes it served, and the expected reservation is computed
with EstimateWireTokenReservation against the extraction output-token cap
(mirrored as a documented test constant). The mixed-version test adds the
rejected call's reservation to the first, trustworthy call's provider-
reported usage.
@roborev-ci

roborev-ci Bot commented Aug 29, 2026

Copy link
Copy Markdown

roborev: Combined Review (0b1625c)

Verdict: One medium-severity issue found; no critical or high-severity issues.

Medium

  • cmd/msgvault/cmd/person_provider_setup.go:700-702provider add always changes the active provider selector before the new profile has received consent. This can silently switch enabled sweeps to an unconsented profile and cause future runs to fail the consent gate. Leave the existing selector unchanged and let provider use handle selection and enablement, or persist the new profile as disabled with the appropriate restart notice.

Reviewers: 2 done | Synthesis: codex, 5s | Total: 22m48s

Person provider add always rewrote people.sweep.provider to the new
profile before anyone consented to it, so an enabled scheduled sweep
silently switched to the unconsented profile after the next daemon
restart and started failing.

Add now publishes only the named profile (plus any accepted catalog
prices): the operator's active selection and enabled state survive
untouched, and selection and enablement stay with `person provider
use`, which requires its own exact successful check. An unselected
config cannot remain valid once a named profile exists —
people.sweep.provider must name a defined profile even while the sweep
is disabled — so in that case add still publishes the required
selector, which stays inert because the sweep remains disabled; only
`provider use` enables it. The success message now points at
`provider use`, and add keeps printing no daemon restart advice since
a running daemon's scheduled sweeps observe no selection change.

Document the boundary in the people usage guide, including the inert
first-profile selector.
@roborev-ci

roborev-ci Bot commented Aug 29, 2026

Copy link
Copy Markdown

roborev: Combined Review (b25ce45)

Verdict: One medium-severity configuration migration issue identified; no material security vulnerabilities found.

Medium

  • cmd/msgvault/cmd/person_provider_setup.go:724-739 — Adding a named profile does not migrate the supported legacy [people.sweep.provider] table to providers.default. This creates a legacy-plus-named configuration rejected during preflight, causing person provider add to fail for existing legacy installations; use and remove may also fail to mutate them reliably.
    • Suggested fix: Migrate the legacy table to providers.default while preserving selection and enablement, then apply the requested change.

Reviewers: 2 done | Synthesis: codex, 7s | Total: 18m14s

The legacy [people.sweep.provider] migration failed on two valid legacy
layouts. A parentless provider header beside [people.sweep.budgets] left
people.sweep defined only implicitly by sub-table headers, and the
selector edit refused to add the explicit parent header; a root dotted
people.sweep.provider = { ... } assignment defined the table through
dotted keys, which an appended header could never replace.

Extend the targeted table editor with a representation-aware insertion:
exact dotted assignments are replaced in place (preserving operator
comments and position), missing keys join the adjacent dotted family,
and only otherwise is the explicit header appended, which stays valid
because TOML permits defining a super-table after its sub-tables. A
table path assigned as a value keeps refusing edits. ETag/atomic
editing, the named default profile fingerprint, enabled/selection
semantics, and operator content are unchanged, pinned by focused
regressions for add and use across both layouts.
InsertOnly semantics were enforced only for explicit table headers. A
preexisting table encoded as dotted assignments (for example
people.sweep.providers.alpha.model = "...") fell into the insertion
path and was silently overwritten instead of returning
ErrAmbiguousConfigTarget, defeating the concurrent-add guard for named
records. Refuse InsertOnly edits whenever the target table semantically
exists (dotted assignment family or inline-assigned value); non-InsertOnly
dotted-family editing and migration remain unchanged.
@roborev-ci

roborev-ci Bot commented Aug 29, 2026

Copy link
Copy Markdown

roborev: Combined Review (ecf2504)

Verdict: One medium-severity issue found; otherwise, no issues were reported.

Medium

  • Location: internal/peoplesweep/packet.go:506, enforced at internal/peoplesweep/runner.go:804
  • Every packet containing archive seeds or context is marked sensitive, causing the documented default allow_sensitive = false profile to reject ordinary evidence-bearing sweeps before provider I/O.
  • Fix: Adjust the sensitivity classification, or explicitly require and document allow_sensitive = true for normal sweeps, including onboarding guidance.

Reviewers: 2 done | Synthesis: codex, 7s | Total: 1h0m10s

Convert the internal/store test scopes flagged by testifyhelpercheck to
use package-bound helpers (require := require.New(t) / assert :=
assert.New(t)) instead of repeated direct package calls, matching the
style already used across the package. Assertion arguments, messages,
ordering, and subtest structure are unchanged; unflagged scopes and the
existing checks/must/requirements helpers are left as-is.
@roborev-ci

roborev-ci Bot commented Aug 30, 2026

Copy link
Copy Markdown

roborev: Combined Review (02b799b)

Verdict: One medium-severity concurrency issue was identified; no security vulnerabilities were found.

Medium

  • internal/store/person_sweep_budget.go:744-795MarkPersonSweepBudgetStarted validates the reservation and attempt but does not verify that the worker still owns a live lease. A stale worker could mark a reserved call as running and send it after another worker has reclaimed the person, causing duplicate provider calls and usage.
    • Suggested fix: Pass the lease identity into the operation and atomically verify the current owner, fence, and unexpired lease in person_sweep_work before transitioning the reservation.

Reviewers: 2 done | Synthesis: codex, 6s | Total: 25m31s

… lease

MarkPersonSweepBudgetStarted authenticated the reservation and attempt but
never verified the caller still owned a live fenced lease, so a stale worker
could flip a reserved batch to running and send it after another worker had
reclaimed the person.

The store method now receives the caller's peoplesweep.Lease alongside the
reservation (WorkStore interface, worker call site, and fakes updated). In
the same transaction that transitions the batch from reserved to running it
locks the person_sweep_work row (PostgreSQL FOR UPDATE, SQLite writer slot)
and requires matching lease_owner and lease_fence with lease_until strictly
after the database clock, returning peoplesweep.ErrLeaseLost on missing,
mismatched, reclaimed, or expired leases. The lock keeps the established
usage -> batch -> work ordering shared with Apply and Finalize, and the
idempotent running-batch replay path requires the same live lease.

Regression tests prove stale owner, stale fence, expired lease, reclaimed
successor, missing work row, and foreign-person leases cannot start a batch
(batch stays reserved, accounting untouched), and pin the live-lease success
path plus the fenced running replay. The worker fake records the lease it
was handed so a test asserts the pre-IO callback passes the owned lease
identity.
@roborev-ci

roborev-ci Bot commented Aug 30, 2026

Copy link
Copy Markdown

roborev: Combined Review (c7ab437)

Verdict: One Medium-severity migration issue remains; otherwise, no concrete security issues were identified.

Medium

  • Legacy authorization state is not preserved during migration.
    Location: internal/store/migrate_person_inference_provider_v2.go:56-80; internal/peoplesweep/config.go:608-660
    Migrating legacy provider rows adds the new profile columns but preserves the old fingerprint and policy identity. Runtime configuration now computes a provider-neutral fingerprint, so existing successful checks and consents no longer match after upgrade. Enabled sweeps may stop until users manually re-check and re-consent.
    Suggested fix: Migrate checks and consents to the canonical fingerprint, or support an explicit legacy-to-current identity alias in verification and scheduling. Add an upgrade test seeded with a genuinely legacy profile.

Reviewers: 2 done | Synthesis: codex, 7s | Total: 45m42s

@roborev-ci

roborev-ci Bot commented Aug 30, 2026

Copy link
Copy Markdown

roborev: Combined Review (76509ff)

Verdict: One medium-severity lease-ownership bug was identified; no security vulnerabilities were found.

Medium

  • internal/store/person_sweep_budget.go:924-927FinalizePersonSweepFailure discards the lease-ownership result from lockPersonSweepUsageThenWorkTx. If the lease expires before reclamation, a stale worker may mark the running attempt and batches failed and account usage instead of returning peoplesweep.ErrLeaseLost. Capture the result and return ErrLeaseLost when the attempt is not already finalized and the lease is no longer current.

Reviewers: 2 done | Synthesis: codex, 6s | Total: 39m40s

@roborev-ci

roborev-ci Bot commented Aug 30, 2026

Copy link
Copy Markdown

roborev: Combined Review (7db22c4)

Verdict: No Critical, High, or Medium severity findings identified.

Reviewers found no actionable issues at or above Medium severity.


Reviewers: 2 done | Synthesis: codex, 4s | Total: 23m42s

@roborev-ci

roborev-ci Bot commented Aug 30, 2026

Copy link
Copy Markdown

roborev: Combined Review (032499e)

Verdict: One medium-severity documentation issue prevents the documented people-sweep setup from working with typical evidence packets.

Medium

  • docs/usage/people.md:31-45,57-64; docs/configuration.md:208 — The documented profile defaults allow_sensitive to false, but real evidence packets containing seeds or context are marked sensitive, causing person sweep run to fail with “profile does not allow sensitive input.” Add --allow-sensitive to the setup example and update the configuration example, or explain how to provide sanitized non-sensitive evidence.

Reviewers: 2 done | Synthesis: codex, 7s | Total: 31m35s

@roborev-ci

roborev-ci Bot commented Aug 30, 2026

Copy link
Copy Markdown

roborev: Combined Review (1146cb9)

Verdict: One medium-severity issue found; otherwise no security-boundary regressions identified.

Medium

  • internal/peoplesweep/capability_check.go:181-204 — Capability negotiation validates only a small synthetic schema, while real sweeps use a substantially more complex extraction schema. A provider may accept the synthetic request but reject actual sweeps in native mode, causing checks to pass while all sweeps fail.
    • Fix: Negotiate using the real extraction schema, or validate and normalize it against each provider’s supported native-schema subset before selecting native output mode.

Reviewers: 2 done | Synthesis: codex, 6s | Total: 28m36s

@roborev-ci

roborev-ci Bot commented Aug 30, 2026

Copy link
Copy Markdown

roborev: Combined Review (2d0e9f3)

Verdict: One medium-severity configuration validation issue requires attention; no security issues were identified.

Medium

  • internal/peoplesweep/config.go:540-578 — HTTP validation accepts protocol-incompatible options, such as bearer authentication for Anthropic, json_object output for Anthropic/Google, or enabled reasoning for Responses. These configurations pass startup/profile validation but fail at sweep preparation, causing scheduled sweeps to repeatedly fail at runtime. Enforce protocol-specific authentication, output, and reasoning capabilities during configuration/profile validation, while retaining driver-level checks as defense in depth.

Reviewers: 2 done | Synthesis: codex, 6s | Total: 36m21s

@roborev-ci

roborev-ci Bot commented Aug 30, 2026

Copy link
Copy Markdown

roborev: Combined Review (065b1b8)

Verdict: One medium-severity capability negotiation issue requires attention; no material security issues were found.

Medium

  • internal/peoplesweep/capability_check.go:83-87 — If the base capability request succeeds but the reasoning follow-up returns a classified capability miss, negotiation exits instead of trying remaining output modes or token parameters. Continue searching on reasoning-specific capability misses while preserving immediate errors for other failures, and add a fallback test.

Reviewers: 2 done | Synthesis: codex, 6s | Total: 19m44s

@roborev-ci

roborev-ci Bot commented Aug 30, 2026

Copy link
Copy Markdown

roborev: Combined Review (616c255)

Verdict: One medium-severity IMAP synchronization issue remains.

Medium

  • internal/imap/client.go:506-508 — On servers without CONDSTORE, HighestModSeq remains zero, so unchanged UIDNEXT and message count can cause the mailbox to be skipped. Flag changes on existing messages may therefore remain stale. Re-fetch flags for known UIDs or bypass this skip path when no mod-sequence signal is available; add coverage for non-CONDSTORE flag changes.

Reviewers: 2 done | Synthesis: codex, 8s | Total: 49m27s

@roborev-ci

roborev-ci Bot commented Aug 30, 2026

Copy link
Copy Markdown

roborev: Combined Review (52f6822)

Verdict: No Medium, High, or Critical findings; the code appears clean.


Reviewers: 2 done | Synthesis: codex, 7s | Total: 51m59s

@salmonumbrella

Copy link
Copy Markdown
Contributor Author

@wesm ready

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Add provider-neutral profiles for people sweeps

1 participant