Skip to content

Use output validator to retry aggregator on parse failures and GitHub 422s#6

Merged
MicahZoltu merged 3 commits into
mainfrom
micah
Jun 12, 2026
Merged

Use output validator to retry aggregator on parse failures and GitHub 422s#6
MicahZoltu merged 3 commits into
mainfrom
micah

Conversation

@MicahZoltu

Copy link
Copy Markdown
Member

No description provided.

@github-actions github-actions 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.

CI Agent Review

The only finding is an architectural coupling issue: TryResult<T> is exported from configuration.mts solely for use by ai.mts, creating a spurious dependency from the AI analysis layer to the configuration parsing module. This generic utility type belongs in typescript-helpers.mts alongside other shared type helpers like Guard<T>.

Comment thread source/configuration.mts Outdated
export type Configuration = CommentTriggerConfiguration | PullRequestConfiguration | LocalDiffConfiguration

type TryResult<T> = { ok: true; value: T } | { ok: false; reason: string }
export type TryResult<T> = { ok: true; value: T } | { ok: false; reason: string }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Exporting TryResult from configuration.mts creates inappropriate coupling — ai.mts now imports from configuration only to use this generic result wrapper, which has nothing to do with configuration. TryResult<T> is a generic discriminated-union building block that belongs in typescript-helpers.mts alongside other shared utility types like Guard<T>. If configuration.mts is ever refactored or split (e.g., separating GitHub-specific from local-diff configuration), ai.mts breaks despite having no real dependency on any configuration concern. As more modules need TryResult, they'll each accumulate this same spurious dependency on configuration.mts, entrenching the coupling.

Comment thread source/ai.mts
import type { ProviderProfile } from "./provider-profiles.mts"
import type { BaseCommitContext } from "./base-commit.mts"
import type { SpawnGit } from "./diff.mts"
import type { CompletionsMessage } from "./completions.mts"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This import of TryResult from configuration.mts represents a spurious dependency — the AI analysis layer does not depend on any configuration concern. The type should be imported from typescript-helpers.mts instead (see finding on configuration.mts:26).

@MicahZoltu

Copy link
Copy Markdown
Member Author

/review

@github-actions github-actions 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.

CI Agent Review

The changeset introduces a validate-and-retry mechanism for aggregator output with several issues: (1) a security concern where unsanitized GitHub API error response bodies are injected into the AI model's prompt as user messages, creating a prompt injection vector and removing a fail-safe defense against malicious reviews; (2) a bug where emptyTurnCount is not reset when the outputValidator feedback loop continues, breaking the consecutive-empty-turns invariant; (3) an architectural separation-of-concerns issue where the orchestrator layer authors LLM prompt text instead of passing structured data to the AI layer; (4) an inconsistent error-handling contract within github.mts where submitReview returns result objects while sibling functions still throw.

Comment thread source/orchestrator.mts
Comment thread source/ai.mts
Comment thread source/agent-loop.mts
Comment thread source/github.mts
export async function submitReview(dependencies: { githubFetch: GitHubFetch }, configuration: GitHubConfiguration, review: GitHubReviewPayload): Promise<void> {
export type SubmitReviewResult = { ok: true } | { ok: false, status: number, body: string }
export async function submitReview(dependencies: { githubFetch: GitHubFetch }, configuration: GitHubConfiguration, review: GitHubReviewPayload): Promise<SubmitReviewResult> {
const { apiUrl, token, owner, repositoryName, pullRequestNumber } = configuration

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Architectural inconsistency: submitReview now returns a result object (SubmitReviewResult) instead of throwing, while fetchPullRequestDiff and fetchPullRequestBaseCommit in the same module still throw on non-OK HTTP responses. All three functions handle the same class of error (GitHub API returning unexpected status codes), yet they communicate failure through two fundamentally different mechanisms. This splits the module's contract — callers must remember which error-handling strategy each function uses. Consider either having submitReview throw and letting the orchestrator catch and interpret errors (preserving the original contract), or switching all three functions to result objects for consistency.

@MicahZoltu

Copy link
Copy Markdown
Member Author

/review

@github-actions github-actions 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.

CI Agent Review

No issues found across architectural, security, bug, or defense analysis.

@MicahZoltu MicahZoltu merged commit a96e528 into main Jun 12, 2026
1 check passed
@MicahZoltu MicahZoltu deleted the micah branch June 12, 2026 10:55
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