Skip to content

fix: implement P0 architecture reliability fixes#31

Merged
digitarald merged 5 commits into
mainfrom
fix/p0-readiness-sdk-architecture
Mar 4, 2026
Merged

fix: implement P0 architecture reliability fixes#31
digitarald merged 5 commits into
mainfrom
fix/p0-readiness-sdk-architecture

Conversation

@digitarald

@digitarald digitarald commented Mar 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • remove analyzer -> git.ts coupling for git-repo detection to avoid pulling simple-git into extension-facing analyzer usage
  • fix stale state closure updates in BatchReadinessTui keyboard handlers
  • fix swallowed auth/login errors from Copilot SDK session event callbacks and ensure sessions are always destroyed
  • propagate real analysis and apps through PolicyContext and compiler readiness bridging
  • extract per-repo readiness processing from BatchReadinessTui into src/services/batch.ts and sanitize token-bearing errors
  • test the real attachDefaultPermissionHandler implementation by exporting and importing it in copilotSdk.test.ts

Validation

  • npm run typecheck — passes
  • npm run lint — passes
  • npm run test — 529/529 tests pass

@digitarald digitarald requested a review from pierceboggan as a code owner March 4, 2026 20:26
Copilot AI review requested due to automatic review settings March 4, 2026 20:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR focuses on improving reliability and architectural boundaries across the readiness/batch workflows and Copilot SDK session handling, reducing extension-facing coupling and tightening error/session lifecycle behavior.

Changes:

  • Extracted per-repo batch readiness processing into src/services/batch.ts and simplified the Batch Readiness TUI to call the service.
  • Propagated repo analysis and apps through the readiness → policy engine bridge to avoid stubbed readiness contexts.
  • Improved Copilot SDK session error handling by capturing auth/login session errors and ensuring sessions are always destroyed; exported the default permission handler and updated tests to use the real implementation.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/ui/BatchReadinessTui.tsx Removes inline clone/readiness logic; delegates per-repo work to a service and fixes stale state updates in input handlers.
src/services/batch.ts Adds processBatchReadinessRepo() + result types and reuses token-bearing error sanitization.
src/services/analyzer.ts Removes dependency on services/git (and transitively simple-git) by inlining git-repo detection.
src/services/readiness.ts Passes analysis and apps into the policy engine context for shadow runs.
src/services/policy/types.ts Extends PolicyContext to optionally include analysis and apps.
src/services/policy/compiler.ts Forwards real analysis/apps into the readiness bridge when available; otherwise uses safe fallbacks.
src/services/instructions.ts Captures auth/login session errors from SDK callbacks; always destroys sessions via finally.
src/services/evalScaffold.ts Same session-error capture pattern; throws captured auth/login errors after session teardown.
src/services/copilotSdk.ts Exports attachDefaultPermissionHandler so it can be tested directly.
src/services/tests/copilotSdk.test.ts Updates tests to import and validate the real permission handler implementation.

Comment thread src/services/batch.ts
Comment thread src/services/batch.ts
Copilot AI review requested due to automatic review settings March 4, 2026 20:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

src/services/instructions.ts:373

  • Same issue as generateCopilotInstructions: sessionError is only thrown after a successful sendAndWait. If sendAndWait throws (e.g., due to an auth/login failure), the code won’t reach the if (sessionError) line, so callers may see an unhelpful/raw error instead of the intended friendly message. Handle this by catching sendAndWait errors and preferring sessionError when present.
    progress(`Analyzing area "${area.name}"...`);
    try {
      await session.sendAndWait({ prompt }, 180000);
    } finally {
      await session.destroy();
    }
    if (sessionError) throw sessionError;

Comment thread src/services/batch.ts
Comment thread src/services/instructions.ts
Comment thread src/services/evalScaffold.ts
@digitarald digitarald requested a review from Copilot March 4, 2026 21:08
@digitarald digitarald merged commit 89a4b3e into main Mar 4, 2026
8 checks passed
@digitarald digitarald deleted the fix/p0-readiness-sdk-architecture branch March 4, 2026 21:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

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

Comment thread src/services/analyzer.ts
Comment on lines 63 to +74
export async function analyzeRepo(repoPath: string): Promise<RepoAnalysis> {
const files = await safeReadDir(repoPath);
let isGit = false;
try {
await fs.access(path.join(repoPath, ".git"));
isGit = true;
} catch {
// not a git repo
}
const analysis: RepoAnalysis = {
path: repoPath,
isGitRepo: await isGitRepo(repoPath),
isGitRepo: isGit,

Copilot AI Mar 4, 2026

Copy link

Choose a reason for hiding this comment

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

This changes isGitRepo semantics from “any path inside a git work tree” to “repoPath has a .git entry”. That will incorrectly report false when analyzing a subdirectory within a repo (common in monorepos / area paths), and can also diverge from previous behavior where git-worktree detection was more robust. Consider implementing a lightweight check that preserves the prior semantics without reintroducing the ./git dependency (e.g., invoking git rev-parse --is-inside-work-tree via child_process, or walking up parent directories looking for .git).

Copilot uses AI. Check for mistakes.
Comment on lines 246 to +256
function policyCtxToReadinessCtx(ctx: PolicyContext): ReadinessContext {
return {
repoPath: ctx.repoPath,
analysis: {
analysis: ctx.analysis ?? {
path: ctx.repoPath,
isGitRepo: true,
languages: [],
frameworks: [],
isMonorepo: false
},
apps: [],
apps: ctx.apps ?? [],

Copilot AI Mar 4, 2026

Copy link

Choose a reason for hiding this comment

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

When ctx.analysis is absent, the stub sets isGitRepo: true, which can produce objectively incorrect downstream behavior (e.g., git-scoped checks passing/triggering when running against a non-git directory). A safer fallback would be false (or a lightweight check), keeping the stub conservative rather than optimistic.

Copilot uses AI. Check for mistakes.
Comment on lines +189 to +197
setSelectedOrgIndices((prev) => {
const next = new Set(prev);
if (next.has(cursorIndex)) {
next.delete(cursorIndex);
} else {
next.add(cursorIndex);
}
return next;
});

Copilot AI Mar 4, 2026

Copy link

Choose a reason for hiding this comment

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

The selection toggle still relies on cursorIndex from the render closure. If the input handler can fire faster than React/Ink commits the cursor update (e.g., rapid key presses), this can toggle the wrong item even though setCursorIndex is now functional. Consider reading the current cursor from a ref that is kept in sync with state (or otherwise ensuring the handler always references the latest cursor index) before mutating the selection set.

Copilot uses AI. Check for mistakes.
Comment on lines +259 to +268
try {
await session.sendAndWait({ prompt }, 180000);
} catch (err) {
sendError = err;
} finally {
await session.destroy();
}
if (sessionError) throw sessionError;
if (sendError !== undefined)
throw sendError instanceof Error ? sendError : new Error(String(sendError));

Copilot AI Mar 4, 2026

Copy link

Choose a reason for hiding this comment

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

If session.destroy() rejects, it will throw from the finally block and can mask the more actionable sessionError/sendError being captured. Consider wrapping session.destroy() in its own try/catch (or capturing destroy errors separately) so the primary failure reason is preserved. The same pattern appears in other session-based helpers (e.g., eval scaffold).

Suggested change
try {
await session.sendAndWait({ prompt }, 180000);
} catch (err) {
sendError = err;
} finally {
await session.destroy();
}
if (sessionError) throw sessionError;
if (sendError !== undefined)
throw sendError instanceof Error ? sendError : new Error(String(sendError));
let destroyError: unknown;
try {
await session.sendAndWait({ prompt }, 180000);
} catch (err) {
sendError = err;
} finally {
try {
await session.destroy();
} catch (err) {
destroyError = err;
}
}
if (sessionError) throw sessionError;
if (sendError !== undefined)
throw sendError instanceof Error ? sendError : new Error(String(sendError));
if (destroyError !== undefined)
throw destroyError instanceof Error ? destroyError : new Error(String(destroyError));

Copilot uses AI. Check for mistakes.
Comment thread .github/workflows/ci.yml
Comment on lines 69 to 71
include:
- node: 22
os: windows-latest

Copilot AI Mar 4, 2026

Copy link

Choose a reason for hiding this comment

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

This removes macOS from the CI matrix, reducing platform coverage. If this is intentional (cost/flake mitigation), consider adding a brief comment in the workflow explaining why macOS was dropped (or limiting macOS to a scheduled workflow) to avoid accidental long-term regressions on that platform.

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