Skip to content

feat: implement credential provisioner, queue dispatch, CODEOWNERS stubs#83

Merged
chitcommit merged 2 commits intomainfrom
feat/implement-stubs
Mar 23, 2026
Merged

feat: implement credential provisioner, queue dispatch, CODEOWNERS stubs#83
chitcommit merged 2 commits intomainfrom
feat/implement-stubs

Conversation

@chitcommit
Copy link
Copy Markdown
Contributor

@chitcommit chitcommit commented Mar 23, 2026

Summary

  • Implement createScopedServiceToken — calls ChittyAuth derivative token API
  • Implement createGitHubInstallationToken — uses GitHub App API for scoped installation tokens
  • Add fetchChangedFiles — fetches PR changed files for label and reviewer automation
  • Wire CODEOWNERS parsing into requestReviewers (parsing existed but was disconnected)
  • Fix trust-resolver to extract entity_type from ChittyID segments (DRL reckoning migration)
  • Log normalized MCP events for observability

Resolves all 5 TODOs identified in the brainstorm (Priority 2).

Test plan

  • 255/255 tests passing
  • Trust resolver tests updated for ChittyScore DRL format (ty/vy/ry)
  • Tool dispatcher tests updated for DRL mock responses
  • Integration test: credential provisioner against staging ChittyAuth
  • Integration test: CODEOWNERS resolution on real PR

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced reviewer suggestions now intelligently match CODEOWNERS to changed files.
    • Improved pull request automation with automatic file change detection.
    • Implemented service token generation capabilities.
  • Refactor

    • Updated trust verification system with refined calculation methodology.
    • Streamlined OAuth identifier handling.

Nick Bianchi and others added 2 commits March 23, 2026 07:47
The OAuthProvider encodes authorization codes as userId:grantId:secret
and splits on : expecting exactly 3 parts. Using mcp-client:clientId
created 4 parts and broke token exchange.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…EOWNERS

- Implement createScopedServiceToken via ChittyAuth derivative token API
- Implement createGitHubInstallationToken via GitHub App installation API
- Add fetchChangedFiles for PR automation (labels + reviewer assignment)
- Wire CODEOWNERS parsing into requestReviewers (was already implemented
  but not connected)
- Fix trust-resolver entity_type extraction from ChittyID segments
  (supports DRL reckoning migration from ChittyTrust to ChittyScore)
- Log normalized MCP events for observability (bus dispatch deferred to v3)
- Update tests for ChittyScore DRL response format (ty/vy/ry)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 23, 2026 19:12
@cloudflare-workers-and-pages
Copy link
Copy Markdown
Contributor

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
❌ Deployment failed
View logs
chittyconnect fa7b647 Mar 23 2026, 07:12 PM

@chitcommit chitcommit merged commit 4c19085 into main Mar 23, 2026
14 of 15 checks passed
@chitcommit chitcommit deleted the feat/implement-stubs branch March 23, 2026 19:12
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 23, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 812205db-c60a-4c67-8aed-7c8f349e123f

📥 Commits

Reviewing files that changed from the base of the PR and between 6d87f27 and fa7b647.

📒 Files selected for processing (7)
  • src/github/reviewers.js
  • src/handlers/queue.js
  • src/lib/trust-resolver.js
  • src/middleware/oauth-provider.js
  • src/services/credential-provisioner-enhanced.js
  • tests/lib/trust-resolver.test.js
  • tests/mcp/tool-dispatcher.test.js

📝 Walkthrough

Walkthrough

Extended pull request review automation by integrating CODEOWNERS support and changed files tracking. Refactored trust resolution from ChittyTrust endpoint to ChittyScore DRL API with component-based trust scoring. Implemented GitHub installation token and scoped service token provisioning. Removed OAuth clientId sanitization.

Changes

Cohort / File(s) Summary
PR Review Enhancement
src/github/reviewers.js, src/handlers/queue.js
Added changedFiles option handling to requestReviewers; integrated CODEOWNERS resolution when enabled with de-duplication. Implemented fetchChangedFiles helper in queue handler to retrieve PR modified files list and thread through autoLabelPullRequest and requestReviewers workflows.
Trust Resolution Migration
src/lib/trust-resolver.js, tests/lib/trust-resolver.test.js
Migrated from ChittyTrust endpoint (trust.chitty.cc) to ChittyScore DRL API (score.chitty.cc); replaced single trust_level calculation with component-based derivation from ty, vy, ry parameters. Extracted entity_type from ChittyID segments; removed CHITTY_TRUST_TOKEN dependency.
Token Provisioning
src/services/credential-provisioner-enhanced.js
Implemented createScopedServiceToken for ChittyAuth derivative tokens with configurable TTL, source, and purpose. Implemented createGitHubInstallationToken with App JWT generation, installation discovery, and access token creation; added comprehensive error handling for both flows.
OAuth Provider
src/middleware/oauth-provider.js
Removed clientId sanitization in handleAuthorize; actorId now preserves original clientId without colon-to-hyphen replacement.
Test Updates
tests/mcp/tool-dispatcher.test.js
Updated trust-resolver mock response shape across multiple test cases to reflect new { ty, vy, ry } component structure instead of { trust_level, entity_type }.

Sequence Diagrams

sequenceDiagram
    participant Queue as Queue Handler
    participant GitHub as GitHub API
    participant Reviewers as Reviewers Module
    participant CodeOwners as CODEOWNERS Resolver
    participant Label as Label Automation

    Queue->>GitHub: fetchChangedFiles(prNumber)
    GitHub-->>Queue: [changed filenames]
    
    Queue->>Label: autoLabelPullRequest(..., changedFiles)
    Label-->>Queue: labels applied
    
    Queue->>Reviewers: requestReviewers(..., {changedFiles})
    Reviewers->>CodeOwners: getCodeOwners(changedFiles)
    CodeOwners-->>Reviewers: {users, teams}
    Reviewers->>GitHub: POST /repos/.../pulls/.../requested_reviewers
    GitHub-->>Reviewers: success
    Reviewers-->>Queue: reviewers assigned
Loading
sequenceDiagram
    participant Client as Client
    participant CredProv as Credential Provisioner
    participant ChittyAuth as ChittyAuth API
    participant GitHub as GitHub API

    Client->>CredProv: createScopedServiceToken(parentToken, scopes)
    CredProv->>ChittyAuth: POST /api/v1/tokens/derivative
    ChittyAuth-->>CredProv: {token, expires_at, scopes}
    CredProv-->>Client: {value, expires_at, scopes}

    Client->>CredProv: createGitHubInstallationToken(owner, repo)
    CredProv->>GitHub: POST /app with JWT
    GitHub-->>CredProv: JWT validated
    CredProv->>GitHub: GET /app/installations
    GitHub-->>CredProv: [installations]
    CredProv->>GitHub: POST /app/installations/{id}/access_tokens
    GitHub-->>CredProv: {token, expires_at, permissions, repositories}
    CredProv-->>Client: {value, expires_at, permissions, repositories}
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

security-approved, access-reviewed

Poem

🐰 We've hopped through trust components bright,
CODEOWNERS now guide reviews with might,
GitHub tokens flow, scopes held tight,
Changed files tracked through every flight—
Code connections shine in perfect light! ✨

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/implement-stubs

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 and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fa7b647f09

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// @canon: chittycanon://gov/governance#core-types
const safeClientId = (oauthReqInfo.clientId || "anonymous").replace(/:/g, "-");
const actorId = `mcp-client-${safeClientId}`;
const actorId = `mcp-client-${oauthReqInfo.clientId || "anonymous"}`;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve colon sanitization when deriving userId

This reintroduces : into the userId passed to completeAuthorization. The comment directly above explains that the OAuth provider encodes authorization codes as userId:grantId:secret and later splits on :; dynamically registered OAuth client IDs are commonly URLs like https://…, so /authorize now generates codes that /token cannot parse for those clients.

Useful? React with 👍 / 👎.

Comment on lines +1037 to +1040
return {
value: data.token,
expires_at: data.expires_at,
permissions: data.permissions,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Return the minted GitHub token under token

provisionGitHubToken() in this file still reads installationToken.token for both the returned credential and the gh secret set instructions, but this helper now returns value instead. Even when GitHub successfully issues an installation token, github_deploy_token provisioning will hand back undefined to callers, so the new flow never produces a usable credential.

Useful? React with 👍 / 👎.

Comment on lines +1022 to +1024
body: JSON.stringify({
repositories: [repository.split("/")[1]],
permissions: permissions || { contents: "read", metadata: "read" },
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Serialize GitHub permissions in the shape the API accepts

The default caller passes permissions as ['contents:write', 'actions:write'] (provisionGitHubToken() in the same file), but this request forwards that array unchanged. GitHub's installation-token endpoint expects a permission map such as { contents: 'write', actions: 'write' }, so the default github_deploy_token path will get a 422 instead of an access token.

Useful? React with 👍 / 👎.

Comment thread src/github/reviewers.js
Comment on lines +33 to +35
if (useCODEOWNERS && changedFiles.length > 0) {
try {
const owners = await getCodeOwners(token, owner, repo, changedFiles);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Don't enable CODEOWNERS lookup with the current matcher

This new call path turns getCodeOwners() on for every PR, but the parser below still turns entries like docs/ into ^docs/$ and keeps leading / characters, so common CODEOWNERS rules never match GitHub file paths such as docs/readme.md. In repositories that rely on directory-style or root-anchored patterns, reviewer assignment will silently miss the intended owners and fall back to the default team.

Useful? React with 👍 / 👎.

Comment thread src/handlers/queue.js
Comment on lines +40 to +41
const response = await fetch(
`https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}/files?per_page=100`,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Paginate the pull-request files fetch

This helper only makes a single per_page=100 request to the PR files API and never follows additional pages. On pull requests with more than 100 changed files, the later paths are dropped before autoLabelPullRequest() and requestReviewers() run, which makes label assignment and CODEOWNERS resolution incomplete for large PRs.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Implements several previously-TODO automation and credential features across the GitHub event queue pipeline and credential provisioning, while migrating trust resolution to ChittyScore DRL (TY/VY/RY) and improving observability.

Changes:

  • Implement scoped service-token derivation via ChittyAuth and GitHub App installation token generation.
  • Fetch PR changed files and pass them into labeling + reviewer assignment; wire CODEOWNERS resolution into reviewer requests.
  • Migrate trust resolver/test fixtures to DRL (ty/vy/ry) and log normalized MCP event metadata.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/mcp/tool-dispatcher.test.js Updates trust resolver mocks to DRL response shape (ty/vy/ry).
tests/lib/trust-resolver.test.js Updates trust resolver tests for DRL endpoint + derived trust level behavior.
src/services/credential-provisioner-enhanced.js Implements scoped ChittyAuth derivative tokens and GitHub installation token creation.
src/middleware/oauth-provider.js Adjusts OAuth actorId derivation during authorize flow.
src/lib/trust-resolver.js Switches trust resolution to ChittyScore DRL and derives backward-compatible trust_level.
src/handlers/queue.js Adds PR changed-files fetch; logs normalized MCP events; passes changedFiles into automations.
src/github/reviewers.js Wires CODEOWNERS resolution into reviewer selection using changedFiles.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

// @canon: chittycanon://gov/governance#core-types
const safeClientId = (oauthReqInfo.clientId || "anonymous").replace(/:/g, "-");
const actorId = `mcp-client-${safeClientId}`;
const actorId = `mcp-client-${oauthReqInfo.clientId || "anonymous"}`;
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

actorId is derived directly from oauthReqInfo.clientId, but the comment above notes userId must not contain colons because OAuthProvider encodes/validates auth codes by splitting on :. A dynamically-registered clientId containing : will break the flow (and may enable ambiguous parsing). Reintroduce sanitization (e.g., replace : or otherwise encode clientId) before building actorId.

Suggested change
const actorId = `mcp-client-${oauthReqInfo.clientId || "anonymous"}`;
const rawClientId = oauthReqInfo.clientId || "anonymous";
// Sanitize clientId for use in userId: encode to ensure no ":" characters are present.
const safeClientId = encodeURIComponent(rawClientId);
const actorId = `mcp-client-${safeClientId}`;

Copilot uses AI. Check for mistakes.
Comment on lines +1036 to +1042
const data = await response.json();
return {
value: data.token,
expires_at: data.expires_at,
permissions: data.permissions,
repositories: data.repositories,
};
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

createGitHubInstallationToken() returns { value: data.token, ... }, but provisionGitHubToken() reads installationToken.token when building the credential and usage instructions. This will make the provisioned GitHub token undefined. Align the return shape (e.g., return token instead of value, or update all call sites to use value).

Copilot uses AI. Check for mistakes.
Comment on lines +1022 to +1025
body: JSON.stringify({
repositories: [repository.split("/")[1]],
permissions: permissions || { contents: "read", metadata: "read" },
}),
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

permissions is passed into createGitHubInstallationToken() from provisionGitHubToken() as an array of strings (e.g., ["contents:write", "actions:write"]), but the GitHub API expects an object map (e.g., { contents: "write" }). As written, body.permissions will be an array and the token request will likely be rejected or ignored. Convert the input into the expected object form (or change the higher-level API to accept the object form) before POSTing to /access_tokens.

Copilot uses AI. Check for mistakes.
Comment thread src/handlers/queue.js
Comment on lines +40 to +54
const response = await fetch(
`https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}/files?per_page=100`,
{
headers: {
Authorization: `token ${token}`,
Accept: "application/vnd.github+json",
"User-Agent": "ChittyConnect/1.0",
},
},
);

if (!response.ok) return [];

const files = await response.json();
return files.map((f) => f.filename);
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

fetchChangedFiles() only requests the first page (per_page=100) and silently returns [] on any non-2xx response. For PRs with >100 files (or transient GitHub API issues), this will lead to missing labels/reviewers without any signal. Handle pagination via the Link header (or iterate page=) and log/propagate errors so automations can be retried/observed.

Suggested change
const response = await fetch(
`https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}/files?per_page=100`,
{
headers: {
Authorization: `token ${token}`,
Accept: "application/vnd.github+json",
"User-Agent": "ChittyConnect/1.0",
},
},
);
if (!response.ok) return [];
const files = await response.json();
return files.map((f) => f.filename);
const baseUrl = `https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}/files`;
const perPage = 100;
let page = 1;
const filenames = [];
// Paginate through all changed files to avoid missing files on large PRs
while (true) {
const url = `${baseUrl}?per_page=${perPage}&page=${page}`;
const response = await fetch(url, {
headers: {
Authorization: `token ${token}`,
Accept: "application/vnd.github+json",
"User-Agent": "ChittyConnect/1.0",
},
});
if (!response.ok) {
let bodyText = "";
try {
bodyText = await response.text();
} catch {
// Ignore body read errors; we still log status information.
}
const truncatedBody =
bodyText && bodyText.length > 500
? `${bodyText.slice(0, 500)}...`
: bodyText;
console.error("Failed to fetch changed files from GitHub", {
owner,
repo,
prNumber,
page,
status: response.status,
statusText: response.statusText,
body: truncatedBody,
});
throw new Error(
`GitHub API request for changed files failed with status ${response.status}`,
);
}
const files = await response.json();
if (!Array.isArray(files)) {
console.error(
"Unexpected GitHub API response format when fetching changed files",
{
owner,
repo,
prNumber,
page,
receivedType: typeof files,
},
);
throw new Error("Unexpected GitHub API response format for changed files");
}
filenames.push(
...files
.map((f) => f && f.filename)
.filter((name) => typeof name === "string" && name.length > 0),
);
if (files.length < perPage) {
// Last page reached.
break;
}
page += 1;
}
return filenames;

Copilot uses AI. Check for mistakes.
Comment thread src/github/reviewers.js
Comment on lines +32 to +39
// Resolve CODEOWNERS if we have changed files
if (useCODEOWNERS && changedFiles.length > 0) {
try {
const owners = await getCodeOwners(token, owner, repo, changedFiles);
defaultReviewers = [
...new Set([...defaultReviewers, ...owners.users]),
];
defaultTeams = [...new Set([...defaultTeams, ...owners.teams])];
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

Now that requestReviewers() actively resolves CODEOWNERS, the current matching behavior can request too many reviewers because it accumulates owners from all matching rules. CODEOWNERS semantics are “last matching pattern wins” (with section handling, anchored patterns, etc.). Consider implementing last-match precedence (and more complete pattern rules) or using a proven CODEOWNERS parser to avoid incorrect reviewer assignment.

Copilot uses AI. Check for mistakes.
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.

2 participants