Skip to content

feat(approvals): credentials are the company lead's job and deduped#45

Merged
antoinekm merged 2 commits into
masterfrom
feat/credentials-lead-owned-and-deduped
Jun 30, 2026
Merged

feat(approvals): credentials are the company lead's job and deduped#45
antoinekm merged 2 commits into
masterfrom
feat/credentials-lead-owned-and-deduped

Conversation

@antoinekm

@antoinekm antoinekm commented Jun 30, 2026

Copy link
Copy Markdown
Owner

What

Operating-model change for credential requests, enforced server-side.

1. Credentials are the company lead's job

A reporting agent (one with a reportsTo manager) can no longer request a credential from the board directly. POST /companies/:id/approvals and POST /approvals/:id/resubmit return 403 for a subordinate, pointing it to its CEO. The lead (no manager) requests and provisions credentials for the company. This stops sub-agents (e.g. a CTO) from going straight to the human board.

2. No duplicate pending requests

A new request_credential whose envKey already has a pending request is rejected with 409 (pointing at the existing one). The lead owns dedup; this is the server safety net so duplicates cannot pile up (we just saw a CTO open two identical GITHUB_TOKEN requests).

3. Role-aware capability guide

renderCapabilityRequestGuide now takes { isLead }. Reporting agents are told to route credential needs through their CEO; the lead is told it owns acquisition and must reuse existing secrets / avoid duplicate requests. Wired from heartbeat via agent.reportsTo == null.

Builds on the prior creation-time schema validation (#44).

Tests

  • reporting agent -> 403 (create); lead well-formed -> 201; duplicate envKey -> 409; resubmit still validates shape;
  • guide: reporting agent gets the route-via-CEO directive, lead gets the ownership + one-pending-per-key directive.

Server suite green (20/20 across the two touched suites), typecheck clean.

Summary by CodeRabbit

  • New Features

    • Credential request guidance now adapts based on whether the agent is a company lead or a reporting agent.
  • Bug Fixes

    • Prevented non-lead agents from submitting credential requests directly; these requests must go through the company lead.
    • Blocked duplicate pending credential requests for the same environment key.
    • Resubmitted credential requests now follow the same access checks and validation rules.

Credential requests to the board are now the company lead's responsibility.
A reporting agent (one with a manager) gets a 403 pointing it to its CEO,
on both create and resubmit, so subordinates route credential needs through
the lead instead of going to the board directly.

A duplicate pending credential request for the same env key is rejected with
409, as a server safety net on top of the lead owning dedup.

The capability-request guide is now role-aware: reporting agents are told to
route credential needs through their CEO, and the lead is told it owns
acquisition and must reuse existing secrets / avoid duplicate requests.
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@antoinekm, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 49 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 77e7db1c-56e7-406b-9a16-1332d7c09307

📥 Commits

Reviewing files that changed from the base of the PR and between d969167 and ec79870.

📒 Files selected for processing (2)
  • server/src/__tests__/approval-routes-idempotency.test.ts
  • server/src/routes/approvals.ts
📝 Walkthrough

Walkthrough

Credential request creation and resubmission routes now enforce that only company leads (agents with reportsTo: null) can submit request_credential approvals (403 otherwise) and reject duplicate pending requests for the same envKey with 409. renderCapabilityRequestGuide gains an isLead option to vary instruction text, and the heartbeat passes the agent's lead status when rendering it.

Lead-aware credential request enforcement

Layer / File(s) Summary
renderCapabilityRequestGuide isLead parameter
server/src/services/capability-requests.ts, server/src/services/heartbeat.ts
Function signature extended with opts: { isLead?: boolean }. Guide text for request_credential is now conditional: non-leads are told to route via the CEO; leads are told to acquire credentials themselves and avoid duplicate pending requests. Heartbeat passes agent.reportsTo == null as isLead.
Authority and duplicate checks in approval routes
server/src/routes/approvals.ts
Creation handler parses the credential payload to extract envKey, returns 403 if the requesting agent has a reportsTo, and queries existing pending approvals to return 409 on a duplicate envKey. Resubmission handler applies the same 403 authority check and re-validates any updated payload via requestCredentialSchema.parse.
Route and service tests
server/src/__tests__/approval-routes-idempotency.test.ts, server/src/__tests__/capability-requests.test.ts
Happy-path test updated to use a lead agent with empty pending list. New tests assert 403 for reporting agents, 409 for duplicate envKey, and that approvalService.create is not called in either blocked case. Capability guide tests verify the conditional text for both isLead: false and isLead: true.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • antoinekm/atelier#19: Modifies the same renderCapabilityRequestGuide function in capability-requests.ts, changing guide wording for autonomous/DIY agent behavior.
  • antoinekm/atelier#43: Also modifies renderCapabilityRequestGuide and its test coverage for different capability-request rule areas.

Poem

🐇 Hop, hop! Only the lead may ask,
For credentials — a company-lead task.
Duplicates? Rejected with 409 flair,
The CEO guides who gets what and where.
One pending request per key, nothing more —
The bunny enforces what the routes have in store! 🗝️

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: company-lead-owned credential requests with duplicate-pending request prevention.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/credentials-lead-owned-and-deduped

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
server/src/routes/approvals.ts (1)

646-660: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Re-check pending envKey conflicts on resubmit.

A revision-requested credential can be resubmitted with an envKey that already has another pending request. This path only validates shape before svc.resubmit, so it can still create duplicate pending credential approvals.

Proposed fix
-      if (req.body.payload) {
-        requestCredentialSchema.parse(req.body.payload);
-      }
+      const credentialPayload = requestCredentialSchema.parse(req.body.payload ?? existing.payload);
+      const pending = await svc.list(existing.companyId, "pending");
+      const duplicate = pending.find(
+        (a) =>
+          a.id !== existing.id &&
+          a.type === "request_credential" &&
+          (a.payload as { envKey?: string } | null)?.envKey === credentialPayload.envKey,
+      );
+      if (duplicate) {
+        res.status(409).json({
+          error: `A pending credential request for ${credentialPayload.envKey} already exists (${duplicate.id}). Resolve it instead of resubmitting a duplicate.`,
+        });
+        return;
+      }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/src/routes/approvals.ts` around lines 646 - 660, The resubmit path in
approvals handling currently only validates the payload shape before calling
svc.resubmit, so it can allow duplicate pending credential approvals for the
same envKey. Add a re-check in the resubmission flow around normalizedPayload
and approval resubmission logic to detect any existing pending request with the
same envKey for the same company/credential and reject it before calling
svc.resubmit. Reuse the same envKey conflict validation used elsewhere in this
route or related approval helpers so the behavior stays consistent for
hire_agent and other credential types.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/src/routes/approvals.ts`:
- Around line 184-195: The duplicate credential check in approvals flow is
currently a non-atomic list-then-create race, so move the uniqueness enforcement
into the service/DB layer used by the approvals route (around svc.list and
svc.create). Add a transaction, row lock, or partial unique constraint keyed by
envKey for pending request_credential approvals, and have the create path
surface conflicts so the route can still return 409 for duplicates instead of
relying on the pre-check. Use the approvals route and the svc methods that
create pending credential requests as the main points to update.
- Around line 168-173: The lead-check in the approvals flow is using
body-controlled `requestedByAgentId`, which allows an agent actor to spoof a
lead and bypass the subordinate restriction. Update the `request_credential`
path in `approvals.ts` to use the authenticated agent from `req.actor.agentId`
for any agent-based lead/subordinate validation, and explicitly reject requests
when `approvalInput.requestedByAgentId` does not match the authenticated agent.
Keep the existing `agentsSvc.getById` lookup, but base the permission decision
on `req.actor` (agent vs board) rather than the request body, and ensure the
company access/actor permission check is enforced before allowing the action.

---

Outside diff comments:
In `@server/src/routes/approvals.ts`:
- Around line 646-660: The resubmit path in approvals handling currently only
validates the payload shape before calling svc.resubmit, so it can allow
duplicate pending credential approvals for the same envKey. Add a re-check in
the resubmission flow around normalizedPayload and approval resubmission logic
to detect any existing pending request with the same envKey for the same
company/credential and reject it before calling svc.resubmit. Reuse the same
envKey conflict validation used elsewhere in this route or related approval
helpers so the behavior stays consistent for hire_agent and other credential
types.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 55cf0c09-ea2a-4f0d-a31e-475ed5ea106d

📥 Commits

Reviewing files that changed from the base of the PR and between 70b92d7 and d969167.

📒 Files selected for processing (5)
  • server/src/__tests__/approval-routes-idempotency.test.ts
  • server/src/__tests__/capability-requests.test.ts
  • server/src/routes/approvals.ts
  • server/src/services/capability-requests.ts
  • server/src/services/heartbeat.ts

Comment thread server/src/routes/approvals.ts Outdated
Comment on lines +184 to +195
const pending = await svc.list(companyId, "pending");
const duplicate = pending.find(
(a) =>
a.type === "request_credential" &&
(a.payload as { envKey?: string } | null)?.envKey === credPayload.envKey,
);
if (duplicate) {
res.status(409).json({
error: `A pending credential request for ${credPayload.envKey} already exists (${duplicate.id}). Resolve or resubmit it instead of creating a duplicate.`,
});
return;
}

@coderabbitai coderabbitai Bot Jun 30, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Make duplicate credential detection atomic.

svc.list(...) followed later by svc.create(...) is a check-then-insert race; two lead requests can both observe no pending request and create duplicates for the same envKey. Move this into the service/DB layer with a transaction, lock, or partial unique constraint, then map conflicts to 409.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/src/routes/approvals.ts` around lines 184 - 195, The duplicate
credential check in approvals flow is currently a non-atomic list-then-create
race, so move the uniqueness enforcement into the service/DB layer used by the
approvals route (around svc.list and svc.create). Add a transaction, row lock,
or partial unique constraint keyed by envKey for pending request_credential
approvals, and have the create path surface conflicts so the route can still
return 409 for duplicates instead of relying on the pre-check. Use the approvals
route and the svc methods that create pending credential requests as the main
points to update.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taking this one with caution and deferring. The duplicate check is an application-level safety net on top of the lead owning dedup; the race needs two near-simultaneous requests from the same lead for the same envKey, and the failure mode is benign (two pending requests, the board rejects one). A fully atomic guard would need a partial unique index on (company_id, payload->>'envKey') where type='request_credential' and status='pending', which we can add if duplicates are ever observed in practice.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@antoinekm, that makes sense — thanks for the context. I agree this is a reasonable defer given the current risk profile and the benign failure mode.

If you want, I can open a small follow-up issue to track the DB-level hardening later, with the partial unique index approach you outlined.


✏️ Learnings added
Learnt from: antoinekm
Repo: antoinekm/atelier PR: 45
File: server/src/routes/approvals.ts:183-194
Timestamp: 2026-06-30T11:23:16.528Z
Learning: In `server/src/routes/approvals.ts`, duplicate detection for pending `request_credential` approvals is intentionally an application-level safety net for now rather than an atomic database constraint. The stronger future hardening, if duplicates are observed in practice, would be a partial unique index on `(company_id, payload->>'envKey')` where `type = 'request_credential'` and `status = 'pending'`.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

requestedByAgentId is body-controlled, so a reporting agent could set it to a
lead agent and bypass the subordinate block. Check req.actor.agentId (the
authenticated agent) instead, and add a regression test for the forged-id case.
@antoinekm antoinekm merged commit 27071ff into master Jun 30, 2026
16 checks passed
@antoinekm antoinekm deleted the feat/credentials-lead-owned-and-deduped branch June 30, 2026 11:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant