mirror of
https://github.com/stablyai/orca.git
synced 2026-10-20 08:03:28 +00:00
perf: consume repository snapshots in checks
This commit is contained in:
@@ -107,6 +107,64 @@ describe('GitRepositorySnapshotOwner', () => {
|
||||
expect(second?.freshness.upstream.revision).toBe(2)
|
||||
})
|
||||
|
||||
it('reduces settled polling plus Checks demand from two physical loads to one per projection', async () => {
|
||||
const pushTarget = {
|
||||
remoteName: 'fork',
|
||||
branchName: 'feature',
|
||||
remoteUrl: 'ssh://git.example/repo',
|
||||
remoteCreated: false
|
||||
}
|
||||
const upstreamResult = {
|
||||
hasUpstream: true,
|
||||
upstreamName: 'fork/feature',
|
||||
ahead: 1,
|
||||
behind: 0
|
||||
}
|
||||
const baselineOwner = new GitRepositorySnapshotOwner()
|
||||
const baselineStatusLoads = vi.fn(async () => statusResult)
|
||||
const baselineUpstreamLoads = vi.fn(async () => upstreamResult)
|
||||
await baselineOwner.readStatus(
|
||||
native,
|
||||
'/repo',
|
||||
defaultStatusIdentity,
|
||||
undefined,
|
||||
baselineStatusLoads
|
||||
)
|
||||
await baselineOwner.readUpstream(native, '/repo', pushTarget, baselineUpstreamLoads)
|
||||
await baselineOwner.readStatus(
|
||||
native,
|
||||
'/repo',
|
||||
defaultStatusIdentity,
|
||||
undefined,
|
||||
baselineStatusLoads
|
||||
)
|
||||
await baselineOwner.readUpstream(native, '/repo', pushTarget, baselineUpstreamLoads)
|
||||
|
||||
const migratedOwner = new GitRepositorySnapshotOwner()
|
||||
const migratedStatusLoads = vi.fn(async () => statusResult)
|
||||
const migratedUpstreamLoads = vi.fn(async () => upstreamResult)
|
||||
await migratedOwner.readStatus(
|
||||
native,
|
||||
'/repo',
|
||||
defaultStatusIdentity,
|
||||
undefined,
|
||||
migratedStatusLoads
|
||||
)
|
||||
await migratedOwner.readUpstream(native, '/repo', pushTarget, migratedUpstreamLoads)
|
||||
const snapshot = migratedOwner.getSnapshot(
|
||||
query(native, '/repo', defaultStatusIdentity, pushTarget)
|
||||
)
|
||||
|
||||
expect(baselineStatusLoads).toHaveBeenCalledTimes(2)
|
||||
expect(baselineUpstreamLoads).toHaveBeenCalledTimes(2)
|
||||
expect(migratedStatusLoads).toHaveBeenCalledOnce()
|
||||
expect(migratedUpstreamLoads).toHaveBeenCalledOnce()
|
||||
expect(snapshot).toMatchObject({
|
||||
freshness: { status: { state: 'fresh' }, upstream: { state: 'fresh' } },
|
||||
upstream: { upstreamName: 'fork/feature' }
|
||||
})
|
||||
})
|
||||
|
||||
it('does not publish rejected reads and always retries settled work', async () => {
|
||||
const owner = new GitRepositorySnapshotOwner()
|
||||
const failure = new Error('read failed')
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
import type {
|
||||
GitConflictOperation,
|
||||
GitPushTarget,
|
||||
GitStatusEntry,
|
||||
GitStatusResult,
|
||||
GitUpstreamStatus
|
||||
} from '../../shared/types'
|
||||
import type {
|
||||
GitRepositoryProjectionFreshness,
|
||||
GitRepositorySnapshot
|
||||
} from '../../shared/git-repository-snapshot'
|
||||
import { stableInFlightKey } from '../../shared/in-flight-promise-dedupe'
|
||||
|
||||
export type {
|
||||
GitRepositoryProjectionFreshness,
|
||||
GitRepositorySnapshot
|
||||
} from '../../shared/git-repository-snapshot'
|
||||
|
||||
export const MAX_GIT_REPOSITORY_SNAPSHOT_STATUS_ENTRIES = 1_000
|
||||
export const MAX_GIT_REPOSITORY_SNAPSHOT_IGNORED_PATHS = 1_000
|
||||
export const MAX_GIT_REPOSITORY_SNAPSHOT_TEXT_CODE_UNITS = 64 * 1_024
|
||||
@@ -24,43 +32,11 @@ export type GitRepositoryStatusIdentity = {
|
||||
sharedLinkPaths: readonly string[]
|
||||
}
|
||||
|
||||
export type GitRepositoryProjectionFreshness = Readonly<{
|
||||
state: 'missing' | 'fresh' | 'stale' | 'failed' | 'placeholder'
|
||||
generation: number
|
||||
currentGeneration: number
|
||||
revision: number | null
|
||||
identity: string | null
|
||||
}>
|
||||
|
||||
export type GitRepositorySnapshot = Readonly<{
|
||||
revision: number
|
||||
generatedAt: number
|
||||
repositoryIdentity: Readonly<{ head: string | null; branch: string | null }>
|
||||
status: Readonly<{
|
||||
entries: readonly Readonly<GitStatusEntry>[]
|
||||
didHitLimit: boolean
|
||||
statusLength: number | null
|
||||
ignoredPaths: readonly string[]
|
||||
lineStatsState: 'missing' | 'complete' | 'skipped-at-limit'
|
||||
retentionTruncated: boolean
|
||||
}>
|
||||
upstream: Readonly<GitUpstreamStatus> | null
|
||||
conflicts: GitConflictOperation | null
|
||||
worktreeGraphVersion: number
|
||||
freshness: Readonly<{
|
||||
repositoryIdentity: GitRepositoryProjectionFreshness
|
||||
status: GitRepositoryProjectionFreshness
|
||||
upstream: GitRepositoryProjectionFreshness
|
||||
conflicts: GitRepositoryProjectionFreshness
|
||||
worktreeGraph: GitRepositoryProjectionFreshness
|
||||
}>
|
||||
}>
|
||||
|
||||
export type GitRepositoryStatusProjection = {
|
||||
repositoryIdentity: Readonly<{ head: string | null; branch: string | null }>
|
||||
status: GitRepositorySnapshot['status']
|
||||
upstream: Readonly<GitUpstreamStatus> | null
|
||||
conflicts: GitConflictOperation
|
||||
conflicts: GitStatusResult['conflictOperation']
|
||||
}
|
||||
|
||||
export type GitRepositorySnapshotQuery = {
|
||||
|
||||
@@ -21,6 +21,7 @@ const {
|
||||
lstatMock,
|
||||
commitChangesMock,
|
||||
getStatusMock,
|
||||
getGitRepositorySnapshotMock,
|
||||
abortMergeMock,
|
||||
abortRebaseMock,
|
||||
getDiffMock,
|
||||
@@ -66,6 +67,7 @@ const {
|
||||
lstatMock: vi.fn(),
|
||||
commitChangesMock: vi.fn(),
|
||||
getStatusMock: vi.fn(),
|
||||
getGitRepositorySnapshotMock: vi.fn(),
|
||||
abortMergeMock: vi.fn(),
|
||||
abortRebaseMock: vi.fn(),
|
||||
getDiffMock: vi.fn(),
|
||||
@@ -139,6 +141,7 @@ vi.mock('../local-downloaded-folder-promotion', () => ({
|
||||
vi.mock('../git/status', () => ({
|
||||
commitChanges: commitChangesMock,
|
||||
getStatus: getStatusMock,
|
||||
getGitRepositorySnapshot: getGitRepositorySnapshotMock,
|
||||
abortMerge: abortMergeMock,
|
||||
abortRebase: abortRebaseMock,
|
||||
getDiff: getDiffMock,
|
||||
@@ -287,6 +290,7 @@ describe('registerFilesystemHandlers', () => {
|
||||
recordCrashBreadcrumbMock,
|
||||
commitChangesMock,
|
||||
getStatusMock,
|
||||
getGitRepositorySnapshotMock,
|
||||
abortMergeMock,
|
||||
abortRebaseMock,
|
||||
getDiffMock,
|
||||
@@ -1521,6 +1525,111 @@ describe('registerFilesystemHandlers', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('reads exact local and SSH repository snapshot identities without running Git', async () => {
|
||||
const pushTarget = {
|
||||
remoteName: 'fork',
|
||||
branchName: 'feature/checks',
|
||||
remoteUrl: 'git@github.com:org/repo.git',
|
||||
remoteCreated: false
|
||||
}
|
||||
const snapshot = { revision: 3 }
|
||||
const sharedStore = {
|
||||
...store,
|
||||
getRepos: () => [{ ...store.getRepos()[0], symlinkPaths: ['node_modules'] }],
|
||||
getAllWorktreeMeta: () => ({ [`repo-1::${WORKTREE_FEATURE_PATH}`]: {} })
|
||||
}
|
||||
registerWorktreeRootsForRepo(sharedStore as never, 'repo-1', [REPO_PATH, WORKTREE_FEATURE_PATH])
|
||||
getGitRepositorySnapshotMock.mockReturnValue(snapshot)
|
||||
const sshProvider = { getRepositorySnapshot: vi.fn().mockReturnValue(snapshot) }
|
||||
getSshGitProviderMock.mockReturnValue(sshProvider)
|
||||
registerFilesystemHandlers(sharedStore as never)
|
||||
const args = {
|
||||
includeIgnored: true,
|
||||
bypassEffectiveUpstreamNegativeCache: true,
|
||||
reuseLineStats: true,
|
||||
pushTarget
|
||||
}
|
||||
|
||||
await expect(
|
||||
handlers.get('git:repositorySnapshot')!(null, {
|
||||
worktreePath: WORKTREE_FEATURE_PATH,
|
||||
...args
|
||||
})
|
||||
).resolves.toBe(snapshot)
|
||||
await expect(
|
||||
handlers.get('git:repositorySnapshot')!(null, {
|
||||
worktreePath: '/remote/repo',
|
||||
connectionId: 'ssh-1',
|
||||
...args
|
||||
})
|
||||
).resolves.toBe(snapshot)
|
||||
|
||||
expect(getGitRepositorySnapshotMock).toHaveBeenCalledWith(
|
||||
WORKTREE_FEATURE_PATH,
|
||||
{
|
||||
includeIgnored: true,
|
||||
bypassEffectiveUpstreamNegativeCache: true,
|
||||
reuseLineStats: true,
|
||||
sharedLinkPaths: ['node_modules']
|
||||
},
|
||||
pushTarget
|
||||
)
|
||||
expect(sshProvider.getRepositorySnapshot).toHaveBeenCalledWith(
|
||||
'/remote/repo',
|
||||
{
|
||||
includeIgnored: true,
|
||||
bypassEffectiveUpstreamNegativeCache: true,
|
||||
reuseLineStats: true
|
||||
},
|
||||
pushTarget
|
||||
)
|
||||
expect(getStatusMock).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('reads repository snapshots from the exact configured WSL distro', async () => {
|
||||
await withPlatform('win32', async () => {
|
||||
const wslStore = {
|
||||
...store,
|
||||
getRepos: () => [
|
||||
{
|
||||
id: 'repo-1',
|
||||
path: WORKTREE_FEATURE_PATH,
|
||||
displayName: 'repo',
|
||||
badgeColor: '#000',
|
||||
addedAt: 0
|
||||
}
|
||||
],
|
||||
getProjects: () => [
|
||||
{
|
||||
id: 'project-1',
|
||||
sourceRepoIds: ['repo-1'],
|
||||
localWindowsRuntimePreference: { kind: 'wsl', distro: 'Ubuntu' }
|
||||
}
|
||||
],
|
||||
getSettings: () => ({
|
||||
workspaceDir: WORKSPACE_DIR,
|
||||
localWindowsRuntimeDefault: { kind: 'windows-host' }
|
||||
})
|
||||
}
|
||||
registerWorktreeRootsForRepo(wslStore as never, 'repo-1', [WORKTREE_FEATURE_PATH])
|
||||
getGitRepositorySnapshotMock.mockReturnValue({ revision: 1 })
|
||||
registerFilesystemHandlers(wslStore as never)
|
||||
|
||||
await handlers.get('git:repositorySnapshot')!(null, {
|
||||
worktreePath: WORKTREE_FEATURE_PATH
|
||||
})
|
||||
|
||||
expect(getGitRepositorySnapshotMock).toHaveBeenCalledWith(
|
||||
WORKTREE_FEATURE_PATH,
|
||||
{
|
||||
includeIgnored: false,
|
||||
wslDistro: 'Ubuntu'
|
||||
},
|
||||
undefined
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('aborts tokenized local status without crossing renderer boundaries', async () => {
|
||||
registerWorktreeRootsForRepo(store as never, 'repo-1', [REPO_PATH, WORKTREE_FEATURE_PATH])
|
||||
const statusSignals: AbortSignal[] = []
|
||||
|
||||
@@ -29,6 +29,7 @@ import type {
|
||||
TuiAgent
|
||||
} from '../../shared/types'
|
||||
import type { GitHistoryOptions, GitHistoryResult } from '../../shared/git-history'
|
||||
import type { GitRepositorySnapshot } from '../../shared/git-repository-snapshot'
|
||||
import type { SshMutationExpectation } from '../../shared/ssh-types'
|
||||
import { assertSshMutationExpectation } from '../ssh/ssh-connection-generation'
|
||||
import {
|
||||
@@ -41,6 +42,7 @@ import {
|
||||
} from '../../shared/text-search'
|
||||
import {
|
||||
getStatus,
|
||||
getGitRepositorySnapshot,
|
||||
getSubmoduleStatus,
|
||||
abortMerge,
|
||||
abortRebase,
|
||||
@@ -1141,6 +1143,52 @@ export function registerFilesystemHandlers(
|
||||
gitStatusCancellations.cancel(event, args.requestToken)
|
||||
})
|
||||
|
||||
ipcMain.handle(
|
||||
'git:repositorySnapshot',
|
||||
async (
|
||||
_event,
|
||||
args: {
|
||||
worktreePath: string
|
||||
connectionId?: string
|
||||
includeIgnored?: boolean
|
||||
bypassEffectiveUpstreamNegativeCache?: boolean
|
||||
reuseLineStats?: boolean
|
||||
pushTarget?: GitPushTarget
|
||||
}
|
||||
): Promise<GitRepositorySnapshot | null> => {
|
||||
if (args.pushTarget) {
|
||||
assertGitPushTargetShape(args.pushTarget)
|
||||
}
|
||||
const options = {
|
||||
includeIgnored: args.includeIgnored ?? false,
|
||||
...(args.reuseLineStats === true ? { reuseLineStats: true } : {}),
|
||||
...(args.bypassEffectiveUpstreamNegativeCache === true
|
||||
? { bypassEffectiveUpstreamNegativeCache: true }
|
||||
: {})
|
||||
}
|
||||
if (args.connectionId) {
|
||||
const provider = getSshGitProvider(args.connectionId)
|
||||
if (!provider) {
|
||||
throw new Error(SSH_GIT_PROVIDER_UNAVAILABLE_MESSAGE)
|
||||
}
|
||||
return provider.getRepositorySnapshot(args.worktreePath, options, args.pushTarget)
|
||||
}
|
||||
const worktreePath = await resolveRegisteredWorktreePath(args.worktreePath, store)
|
||||
const repo = getLocalRepoForRegisteredWorktree(store, args.worktreePath, worktreePath)
|
||||
const gitOptions = getLocalGitOptionsForRepo(store, repo)
|
||||
const sharedLinkPaths = repo ? getWorktreeSharedLinkPaths(repo) : []
|
||||
return getGitRepositorySnapshot(
|
||||
worktreePath,
|
||||
{
|
||||
...options,
|
||||
...gitOptions,
|
||||
...(sharedLinkPaths.length > 0 ? { sharedLinkPaths } : {})
|
||||
},
|
||||
args.pushTarget
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
// Why: parent status reports only one gitlink row per submodule; fetch inner per-file changes from the submodule's own worktree.
|
||||
ipcMain.handle(
|
||||
'git:submoduleStatus',
|
||||
|
||||
@@ -1161,6 +1161,70 @@ describe('SshGitProvider', () => {
|
||||
expect(replacement.getRepositorySnapshot('/home/user/repo')).toBeNull()
|
||||
})
|
||||
|
||||
it('reduces settled polling plus Checks demand from two RPCs to one per projection', async () => {
|
||||
const pushTarget = {
|
||||
remoteName: 'fork',
|
||||
branchName: 'feature',
|
||||
remoteUrl: 'ssh://git.example/repo',
|
||||
remoteCreated: false
|
||||
}
|
||||
mux.request.mockImplementation((method) => {
|
||||
if (method === 'git.status') {
|
||||
return Promise.resolve({
|
||||
entries: [],
|
||||
conflictOperation: 'unknown',
|
||||
head: 'abc123',
|
||||
branch: 'feature'
|
||||
})
|
||||
}
|
||||
if (method === 'git.upstreamStatus') {
|
||||
return Promise.resolve({
|
||||
hasUpstream: true,
|
||||
upstreamName: 'fork/feature',
|
||||
ahead: 1,
|
||||
behind: 0
|
||||
})
|
||||
}
|
||||
return Promise.resolve(undefined)
|
||||
})
|
||||
|
||||
await provider.getStatus('/home/user/repo')
|
||||
await provider.getUpstreamStatus('/home/user/repo', pushTarget)
|
||||
await provider.getStatus('/home/user/repo')
|
||||
await provider.getUpstreamStatus('/home/user/repo', pushTarget)
|
||||
const baselineCounts = {
|
||||
status: mux.request.mock.calls.filter(([method]) => method === 'git.status').length,
|
||||
upstream: mux.request.mock.calls.filter(([method]) => method === 'git.upstreamStatus').length
|
||||
}
|
||||
|
||||
mux.request.mockClear()
|
||||
const migratedProvider = new SshGitProvider('conn-1', mux as never)
|
||||
await migratedProvider.getStatus('/home/user/repo')
|
||||
await migratedProvider.getUpstreamStatus('/home/user/repo', pushTarget)
|
||||
const snapshot = migratedProvider.getRepositorySnapshot('/home/user/repo', {}, pushTarget)
|
||||
const migratedCounts = {
|
||||
status: mux.request.mock.calls.filter(([method]) => method === 'git.status').length,
|
||||
upstream: mux.request.mock.calls.filter(([method]) => method === 'git.upstreamStatus').length
|
||||
}
|
||||
|
||||
expect(baselineCounts).toEqual({ status: 2, upstream: 2 })
|
||||
expect(migratedCounts).toEqual({ status: 1, upstream: 1 })
|
||||
expect(snapshot).toMatchObject({
|
||||
freshness: { status: { state: 'fresh' }, upstream: { state: 'fresh' } },
|
||||
upstream: { upstreamName: 'fork/feature' }
|
||||
})
|
||||
expect(mux.request).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
'git.status',
|
||||
{ worktreePath: '/home/user/repo' },
|
||||
{ signal: expect.any(AbortSignal) }
|
||||
)
|
||||
expect(mux.request).toHaveBeenNthCalledWith(2, 'git.upstreamStatus', {
|
||||
worktreePath: '/home/user/repo',
|
||||
pushTarget
|
||||
})
|
||||
})
|
||||
|
||||
it('marks retained snapshots stale across the existing SSH mutation fence', async () => {
|
||||
mux.request.mockImplementation((method) => {
|
||||
if (method === 'git.status') {
|
||||
|
||||
@@ -282,6 +282,7 @@ import type {
|
||||
|
||||
import type { SetupScriptImportCandidate } from '../shared/setup-script-imports'
|
||||
import type { GitHistoryOptions, GitHistoryResult } from '../shared/git-history'
|
||||
import type { GitRepositorySnapshot } from '../shared/git-repository-snapshot'
|
||||
import type { PublicKnownRuntimeEnvironment } from '../shared/runtime-environments'
|
||||
import type { EphemeralVmRecipeDoctorResult } from '../shared/ephemeral-vm-recipes'
|
||||
import type { EphemeralVmRecipeResultWarning } from '../shared/ephemeral-vm-recipe-diagnostics'
|
||||
@@ -2837,6 +2838,14 @@ export type PreloadApi = {
|
||||
requestToken?: string
|
||||
}) => Promise<GitStatusResult>
|
||||
cancelStatus: (args: { requestToken: string }) => Promise<void>
|
||||
repositorySnapshot: (args: {
|
||||
worktreePath: string
|
||||
connectionId?: string
|
||||
includeIgnored?: boolean
|
||||
bypassEffectiveUpstreamNegativeCache?: boolean
|
||||
reuseLineStats?: boolean
|
||||
pushTarget?: GitPushTarget
|
||||
}) => Promise<GitRepositorySnapshot | null>
|
||||
submoduleStatus: (args: {
|
||||
worktreePath: string
|
||||
submodulePath: string
|
||||
|
||||
@@ -14,6 +14,7 @@ import type { AgentHookInstallStatus } from '../shared/agent-hook-types'
|
||||
import type { CodexConfigSyncStatus } from '../shared/codex-config-sync-types'
|
||||
import type { TerminalPaneSplitSource } from '../shared/feature-education-telemetry'
|
||||
import type { ProjectExecutionRuntimeResolution } from '../shared/project-execution-runtime'
|
||||
import type { GitRepositorySnapshot } from '../shared/git-repository-snapshot'
|
||||
import type { StartupCommandDelivery } from '../shared/codex-startup-delivery'
|
||||
import type {
|
||||
AgentProviderSessionMetadata,
|
||||
@@ -3175,6 +3176,14 @@ const api = {
|
||||
}): Promise<unknown> => ipcRenderer.invoke('git:status', args),
|
||||
cancelStatus: (args: { requestToken: string }): Promise<void> =>
|
||||
ipcRenderer.invoke('git:cancelStatus', args),
|
||||
repositorySnapshot: (args: {
|
||||
worktreePath: string
|
||||
connectionId?: string
|
||||
includeIgnored?: boolean
|
||||
bypassEffectiveUpstreamNegativeCache?: boolean
|
||||
reuseLineStats?: boolean
|
||||
pushTarget?: GitPushTarget
|
||||
}): Promise<GitRepositorySnapshot | null> => ipcRenderer.invoke('git:repositorySnapshot', args),
|
||||
submoduleStatus: (args: {
|
||||
worktreePath: string
|
||||
submodulePath: string
|
||||
|
||||
@@ -122,8 +122,10 @@ import {
|
||||
getRuntimeGitUpstreamStatus,
|
||||
type RuntimeGeneratePullRequestFieldsOverrides
|
||||
} from '@/runtime/runtime-git-client'
|
||||
import { getDesktopGitRepositorySnapshot } from '@/runtime/desktop-git-repository-snapshot-client'
|
||||
import {
|
||||
buildChecksPanelGitStatusContextKey,
|
||||
readChecksPanelRepositorySnapshot,
|
||||
readChecksPanelPublishActionGitStatus,
|
||||
readChecksPanelGitStatusSnapshot,
|
||||
readChecksPanelRefreshGitIdentitySnapshot,
|
||||
@@ -1511,6 +1513,33 @@ export default function ChecksPanel(): React.JSX.Element {
|
||||
connectionId
|
||||
}
|
||||
void (async () => {
|
||||
let repositorySnapshot = readChecksPanelRepositorySnapshot(
|
||||
await getDesktopGitRepositorySnapshot(
|
||||
context,
|
||||
activeWorktreePushTarget ? { pushTarget: activeWorktreePushTarget } : undefined
|
||||
),
|
||||
requestContextKey,
|
||||
branch
|
||||
)
|
||||
if (!repositorySnapshot) {
|
||||
repositorySnapshot = readChecksPanelRepositorySnapshot(
|
||||
await getDesktopGitRepositorySnapshot(context, {
|
||||
reuseLineStats: true,
|
||||
...(activeWorktreePushTarget ? { pushTarget: activeWorktreePushTarget } : {})
|
||||
}),
|
||||
requestContextKey,
|
||||
branch
|
||||
)
|
||||
}
|
||||
if (repositorySnapshot) {
|
||||
if (
|
||||
!stale &&
|
||||
shouldCommitChecksPanelGitStatusSnapshot(panelContextKeyRef.current, requestContextKey)
|
||||
) {
|
||||
updateWorktreeGitIdentity(activeWorktreeId, repositorySnapshot.gitIdentity ?? {})
|
||||
}
|
||||
return repositorySnapshot
|
||||
}
|
||||
const status = await getRuntimeGitStatus(context)
|
||||
if (
|
||||
!stale &&
|
||||
@@ -1533,22 +1562,22 @@ export default function ChecksPanel(): React.JSX.Element {
|
||||
) {
|
||||
freshRemoteStatus = await getRuntimeGitUpstreamStatus(context)
|
||||
}
|
||||
return { status, remoteStatus: freshRemoteStatus }
|
||||
return {
|
||||
contextKey: requestContextKey,
|
||||
hasUncommittedChanges: status.entries.length > 0,
|
||||
remoteStatus: freshRemoteStatus,
|
||||
gitIdentity: {
|
||||
head: status.head,
|
||||
branch: status.branch ?? (status.head ? null : undefined)
|
||||
}
|
||||
}
|
||||
})()
|
||||
.then(({ status, remoteStatus }) => {
|
||||
.then((snapshot) => {
|
||||
if (
|
||||
!stale &&
|
||||
shouldCommitChecksPanelGitStatusSnapshot(panelContextKeyRef.current, requestContextKey)
|
||||
) {
|
||||
setGitStatusSnapshot({
|
||||
contextKey: requestContextKey,
|
||||
hasUncommittedChanges: status.entries.length > 0,
|
||||
remoteStatus,
|
||||
gitIdentity: {
|
||||
head: status.head,
|
||||
branch: status.branch ?? (status.head ? null : undefined)
|
||||
}
|
||||
})
|
||||
setGitStatusSnapshot(snapshot)
|
||||
// A fresh probe succeeded, so this context is no longer in the "could not check branch status" state.
|
||||
setGitStatusProbeErrorContextKey((key) => (key === requestContextKey ? null : key))
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { GitRepositorySnapshot } from '../../../../shared/git-repository-snapshot'
|
||||
import {
|
||||
buildChecksPanelGitStatusContextKey,
|
||||
readChecksPanelRepositorySnapshot,
|
||||
readChecksPanelPublishActionGitStatus,
|
||||
readChecksPanelRefreshGitIdentitySnapshot,
|
||||
readChecksPanelGitStatusSnapshot,
|
||||
@@ -26,6 +28,40 @@ const SNAPSHOT: ChecksPanelGitStatusSnapshot = {
|
||||
}
|
||||
}
|
||||
|
||||
function repositorySnapshot(overrides: Partial<GitRepositorySnapshot> = {}): GitRepositorySnapshot {
|
||||
const freshness = {
|
||||
state: 'fresh' as const,
|
||||
generation: 2,
|
||||
currentGeneration: 2,
|
||||
revision: 4,
|
||||
identity: 'identity'
|
||||
}
|
||||
return {
|
||||
revision: 4,
|
||||
generatedAt: 100,
|
||||
repositoryIdentity: { head: 'abc123', branch: 'feature/checks' },
|
||||
status: {
|
||||
entries: [{ path: 'src/app.ts', status: 'modified', area: 'unstaged' }],
|
||||
didHitLimit: false,
|
||||
statusLength: 1,
|
||||
ignoredPaths: [],
|
||||
lineStatsState: 'complete',
|
||||
retentionTruncated: false
|
||||
},
|
||||
upstream: { hasUpstream: true, upstreamName: 'origin/main', ahead: 1, behind: 0 },
|
||||
conflicts: null,
|
||||
worktreeGraphVersion: 0,
|
||||
freshness: {
|
||||
repositoryIdentity: freshness,
|
||||
status: freshness,
|
||||
upstream: freshness,
|
||||
conflicts: freshness,
|
||||
worktreeGraph: { ...freshness, state: 'placeholder' }
|
||||
},
|
||||
...overrides
|
||||
}
|
||||
}
|
||||
|
||||
describe('buildChecksPanelGitStatusContextKey', () => {
|
||||
it('changes when an explicit push target changes', () => {
|
||||
const base = {
|
||||
@@ -50,6 +86,26 @@ describe('buildChecksPanelGitStatusContextKey', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('distinguishes every explicit target field including absent versus false', () => {
|
||||
const base = {
|
||||
repoId: 'repo-1',
|
||||
worktreeId: 'worktree-1',
|
||||
worktreePath: 'repo-worktree',
|
||||
branch: 'feature/checks',
|
||||
runtimeEnvironmentId: null,
|
||||
repoConnectionId: null
|
||||
}
|
||||
const context = (
|
||||
pushTarget: NonNullable<
|
||||
Parameters<typeof buildChecksPanelGitStatusContextKey>[0]['pushTarget']
|
||||
>
|
||||
) => buildChecksPanelGitStatusContextKey({ ...base, pushTarget })
|
||||
const target = { remoteName: 'origin', branchName: 'feature/checks' }
|
||||
|
||||
expect(context(target)).not.toBe(context({ ...target, remoteCreated: false }))
|
||||
expect(context(target)).not.toBe(context({ ...target, remoteUrl: 'ssh://git.example/repo' }))
|
||||
})
|
||||
|
||||
it('changes when linked hosted review metadata changes', () => {
|
||||
const base = {
|
||||
repoId: 'repo-1',
|
||||
@@ -112,6 +168,92 @@ describe('buildChecksPanelGitStatusContextKey', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('readChecksPanelRepositorySnapshot', () => {
|
||||
it('projects a complete current-generation host snapshot without mutating it', () => {
|
||||
const snapshot = repositorySnapshot()
|
||||
|
||||
expect(readChecksPanelRepositorySnapshot(snapshot, 'panel-context', 'feature/checks')).toEqual({
|
||||
contextKey: 'panel-context',
|
||||
hasUncommittedChanges: true,
|
||||
remoteStatus: snapshot.upstream,
|
||||
gitIdentity: { head: 'abc123', branch: 'feature/checks' }
|
||||
})
|
||||
expect(snapshot.status.entries).toHaveLength(1)
|
||||
})
|
||||
|
||||
it.each(['missing', 'stale', 'failed'] as const)('rejects a %s status projection', (state) => {
|
||||
const snapshot = repositorySnapshot({
|
||||
freshness: {
|
||||
...repositorySnapshot().freshness,
|
||||
status: { ...repositorySnapshot().freshness.status, state }
|
||||
}
|
||||
})
|
||||
|
||||
expect(
|
||||
readChecksPanelRepositorySnapshot(snapshot, 'panel-context', 'feature/checks')
|
||||
).toBeNull()
|
||||
})
|
||||
|
||||
it.each(['missing', 'stale', 'failed'] as const)('rejects a %s upstream projection', (state) => {
|
||||
expect(
|
||||
readChecksPanelRepositorySnapshot(
|
||||
repositorySnapshot({
|
||||
freshness: {
|
||||
...repositorySnapshot().freshness,
|
||||
upstream: { ...repositorySnapshot().freshness.upstream, state }
|
||||
}
|
||||
}),
|
||||
'panel-context',
|
||||
'feature/checks'
|
||||
)
|
||||
).toBeNull()
|
||||
})
|
||||
|
||||
it('rejects missing upstream data and retention-truncated status projections', () => {
|
||||
expect(
|
||||
readChecksPanelRepositorySnapshot(
|
||||
repositorySnapshot({
|
||||
upstream: null
|
||||
}),
|
||||
'panel-context',
|
||||
'feature/checks'
|
||||
)
|
||||
).toBeNull()
|
||||
expect(
|
||||
readChecksPanelRepositorySnapshot(
|
||||
repositorySnapshot({
|
||||
status: { ...repositorySnapshot().status, retentionTruncated: true }
|
||||
}),
|
||||
'panel-context',
|
||||
'feature/checks'
|
||||
)
|
||||
).toBeNull()
|
||||
})
|
||||
|
||||
it('rejects an ambiguous embedded upstream projection', () => {
|
||||
expect(
|
||||
readChecksPanelRepositorySnapshot(
|
||||
repositorySnapshot({
|
||||
upstream: {
|
||||
hasUpstream: true,
|
||||
upstreamName: 'origin/main',
|
||||
ahead: 1,
|
||||
behind: 1
|
||||
}
|
||||
}),
|
||||
'panel-context',
|
||||
'feature/checks'
|
||||
)
|
||||
).toBeNull()
|
||||
})
|
||||
|
||||
it('rejects a fresh projection from another branch', () => {
|
||||
expect(
|
||||
readChecksPanelRepositorySnapshot(repositorySnapshot(), 'panel-context', 'feature/other')
|
||||
).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('readChecksPanelGitStatusSnapshot', () => {
|
||||
it('returns status inputs for the matching panel context', () => {
|
||||
expect(readChecksPanelGitStatusSnapshot(SNAPSHOT, SNAPSHOT.contextKey)).toEqual({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { GitPushTarget, GitStatusEntry, GitUpstreamStatus } from '../../../../shared/types'
|
||||
import type { GitRepositorySnapshot } from '../../../../shared/git-repository-snapshot'
|
||||
|
||||
export type ChecksPanelGitStatusContextInput = {
|
||||
repoId: string | null | undefined
|
||||
@@ -70,12 +71,42 @@ export function buildChecksPanelGitStatusContextKey(
|
||||
remoteName: input.pushTarget.remoteName,
|
||||
branchName: input.pushTarget.branchName,
|
||||
remoteUrl: input.pushTarget.remoteUrl ?? null,
|
||||
remoteCreated: input.pushTarget.remoteCreated ?? false
|
||||
remoteCreated: input.pushTarget.remoteCreated ?? null
|
||||
}
|
||||
: null
|
||||
})
|
||||
}
|
||||
|
||||
export function readChecksPanelRepositorySnapshot(
|
||||
snapshot: GitRepositorySnapshot | null,
|
||||
contextKey: string,
|
||||
expectedBranch: string
|
||||
): ChecksPanelGitStatusSnapshot | null {
|
||||
if (
|
||||
!snapshot ||
|
||||
snapshot.freshness.status.state !== 'fresh' ||
|
||||
snapshot.freshness.upstream.state !== 'fresh' ||
|
||||
snapshot.status.retentionTruncated ||
|
||||
!snapshot.upstream ||
|
||||
(snapshot.upstream.ahead > 0 &&
|
||||
snapshot.upstream.behind > 0 &&
|
||||
snapshot.upstream.behindCommitsArePatchEquivalent === undefined) ||
|
||||
canonicalBranchIdentity(snapshot.repositoryIdentity.branch) !==
|
||||
canonicalBranchIdentity(expectedBranch)
|
||||
) {
|
||||
return null
|
||||
}
|
||||
return {
|
||||
contextKey,
|
||||
hasUncommittedChanges: snapshot.status.entries.length > 0,
|
||||
remoteStatus: snapshot.upstream,
|
||||
gitIdentity: {
|
||||
head: snapshot.repositoryIdentity.head ?? undefined,
|
||||
branch: snapshot.repositoryIdentity.branch
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function shouldPollChecksPanelRuntimeSshStatus(input: {
|
||||
isPanelVisible: boolean
|
||||
runtimeEnvironmentId: string | null
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { getDesktopGitRepositorySnapshot } from './desktop-git-repository-snapshot-client'
|
||||
|
||||
const repositorySnapshot = vi.fn()
|
||||
const runtimeEnvironmentCall = vi.fn()
|
||||
|
||||
beforeEach(() => {
|
||||
repositorySnapshot.mockReset()
|
||||
runtimeEnvironmentCall.mockReset()
|
||||
vi.stubGlobal('window', {
|
||||
api: {
|
||||
git: { repositorySnapshot },
|
||||
runtimeEnvironments: { call: runtimeEnvironmentCall }
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('getDesktopGitRepositorySnapshot', () => {
|
||||
it('reads the exact desktop host snapshot identity', async () => {
|
||||
repositorySnapshot.mockResolvedValue(null)
|
||||
const pushTarget = {
|
||||
remoteName: 'fork',
|
||||
branchName: 'feature/checks',
|
||||
remoteUrl: 'ssh://git.example/repo',
|
||||
remoteCreated: false
|
||||
}
|
||||
|
||||
await expect(
|
||||
getDesktopGitRepositorySnapshot(
|
||||
{
|
||||
settings: { activeRuntimeEnvironmentId: null },
|
||||
worktreeId: 'wt-1',
|
||||
worktreePath: '/repo',
|
||||
connectionId: 'ssh-1'
|
||||
},
|
||||
{
|
||||
includeIgnored: true,
|
||||
bypassEffectiveUpstreamNegativeCache: true,
|
||||
reuseLineStats: true,
|
||||
pushTarget
|
||||
}
|
||||
)
|
||||
).resolves.toBeNull()
|
||||
|
||||
expect(repositorySnapshot).toHaveBeenCalledWith({
|
||||
worktreePath: '/repo',
|
||||
connectionId: 'ssh-1',
|
||||
includeIgnored: true,
|
||||
bypassEffectiveUpstreamNegativeCache: true,
|
||||
reuseLineStats: true,
|
||||
pushTarget
|
||||
})
|
||||
expect(runtimeEnvironmentCall).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('uses the backing folder path for a local folder workspace', async () => {
|
||||
repositorySnapshot.mockResolvedValue(null)
|
||||
const workspaceId = '123e4567-e89b-12d3-a456-426614174000'
|
||||
|
||||
await getDesktopGitRepositorySnapshot({
|
||||
settings: { activeRuntimeEnvironmentId: null },
|
||||
worktreeId: `folder-repo::/home/user::workspace:${workspaceId}`,
|
||||
worktreePath: `/home/user::workspace:${workspaceId}`
|
||||
})
|
||||
|
||||
expect(repositorySnapshot).toHaveBeenCalledWith({
|
||||
worktreePath: '/home/user',
|
||||
connectionId: undefined
|
||||
})
|
||||
})
|
||||
|
||||
it('leaves active runtime-environment transport to a later slice', async () => {
|
||||
await expect(
|
||||
getDesktopGitRepositorySnapshot(
|
||||
{
|
||||
settings: { activeRuntimeEnvironmentId: 'env-1' },
|
||||
worktreeId: 'wt-1',
|
||||
worktreePath: '/repo'
|
||||
},
|
||||
{
|
||||
includeIgnored: true,
|
||||
bypassEffectiveUpstreamNegativeCache: true,
|
||||
reuseLineStats: true
|
||||
}
|
||||
)
|
||||
).resolves.toBeNull()
|
||||
|
||||
expect(repositorySnapshot).not.toHaveBeenCalled()
|
||||
expect(runtimeEnvironmentCall).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,36 @@
|
||||
import type { GitPushTarget } from '../../../shared/types'
|
||||
import type { GitRepositorySnapshot } from '../../../shared/git-repository-snapshot'
|
||||
import { splitWorktreeIdForFilesystem } from '../../../shared/worktree-id'
|
||||
import { getActiveRuntimeTarget } from './runtime-rpc-client'
|
||||
import type { RuntimeGitContext } from './runtime-git-client'
|
||||
|
||||
function resolveDesktopSnapshotWorktreePath(context: RuntimeGitContext): string {
|
||||
return context.worktreeId
|
||||
? (splitWorktreeIdForFilesystem(context.worktreeId)?.worktreePath ?? context.worktreePath)
|
||||
: context.worktreePath
|
||||
}
|
||||
|
||||
export async function getDesktopGitRepositorySnapshot(
|
||||
context: RuntimeGitContext,
|
||||
options: {
|
||||
includeIgnored?: boolean
|
||||
bypassEffectiveUpstreamNegativeCache?: boolean
|
||||
reuseLineStats?: boolean
|
||||
pushTarget?: GitPushTarget
|
||||
} = {}
|
||||
): Promise<GitRepositorySnapshot | null> {
|
||||
const target = getActiveRuntimeTarget(context.settings)
|
||||
if (target.kind !== 'local' && context.worktreeId) {
|
||||
return null
|
||||
}
|
||||
return window.api.git.repositorySnapshot({
|
||||
worktreePath: resolveDesktopSnapshotWorktreePath(context),
|
||||
connectionId: context.connectionId,
|
||||
...(options.includeIgnored ? { includeIgnored: true } : {}),
|
||||
...(options.bypassEffectiveUpstreamNegativeCache
|
||||
? { bypassEffectiveUpstreamNegativeCache: true }
|
||||
: {}),
|
||||
...(options.reuseLineStats ? { reuseLineStats: true } : {}),
|
||||
...(options.pushTarget ? { pushTarget: options.pushTarget } : {})
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import type { GitConflictOperation, GitStatusEntry, GitUpstreamStatus } from './types'
|
||||
|
||||
export type GitRepositoryProjectionFreshness = Readonly<{
|
||||
state: 'missing' | 'fresh' | 'stale' | 'failed' | 'placeholder'
|
||||
generation: number
|
||||
currentGeneration: number
|
||||
revision: number | null
|
||||
identity: string | null
|
||||
}>
|
||||
|
||||
export type GitRepositorySnapshot = Readonly<{
|
||||
revision: number
|
||||
generatedAt: number
|
||||
repositoryIdentity: Readonly<{ head: string | null; branch: string | null }>
|
||||
status: Readonly<{
|
||||
entries: readonly Readonly<GitStatusEntry>[]
|
||||
didHitLimit: boolean
|
||||
statusLength: number | null
|
||||
ignoredPaths: readonly string[]
|
||||
lineStatsState: 'missing' | 'complete' | 'skipped-at-limit'
|
||||
retentionTruncated: boolean
|
||||
}>
|
||||
upstream: Readonly<GitUpstreamStatus> | null
|
||||
conflicts: GitConflictOperation | null
|
||||
worktreeGraphVersion: number
|
||||
freshness: Readonly<{
|
||||
repositoryIdentity: GitRepositoryProjectionFreshness
|
||||
status: GitRepositoryProjectionFreshness
|
||||
upstream: GitRepositoryProjectionFreshness
|
||||
conflicts: GitRepositoryProjectionFreshness
|
||||
worktreeGraph: GitRepositoryProjectionFreshness
|
||||
}>
|
||||
}>
|
||||
@@ -0,0 +1,182 @@
|
||||
# Phase 2 Checks repository-snapshot consumer — 2026-07-30
|
||||
|
||||
## Decision
|
||||
|
||||
Retain a read-only desktop projection query and migrate automatic Checks eligibility reads to the
|
||||
host-owned `GitRepositorySnapshot` seam. When active polling has already published a complete
|
||||
current-generation status and upstream projection, Checks now uses that immutable projection
|
||||
instead of repeating status and upstream work. Manual Checks Refresh and every incomplete,
|
||||
stale, failed, truncated, branch-mismatched, or runtime-routed case keep the existing fresh path.
|
||||
|
||||
Checkpoint: `98134f6f7b8921f0964d99395a2fa91b91ea806e`.
|
||||
|
||||
This is intentionally not a TTL cache. Existing status and upstream APIs still run fresh after
|
||||
settlement, and the new IPC query never runs Git or an SSH RPC.
|
||||
|
||||
## Consumer boundary
|
||||
|
||||
The active polling path remains the producer. Its existing native, exact-WSL-distro, and
|
||||
per-`SshGitProvider` owners publish status and upstream projections through
|
||||
`GitRepositorySnapshotOwner`; existing pre/post mutation fences continue to mark those projections
|
||||
stale and suppress late publication into the current generation.
|
||||
|
||||
Automatic Checks now:
|
||||
|
||||
1. queries the exact desktop owner identity;
|
||||
2. admits only `fresh` status and upstream projections;
|
||||
3. rejects retention-truncated status, a missing upstream projection, and a branch mismatch; and
|
||||
4. falls back to the unchanged fresh status/upstream path when admission fails.
|
||||
|
||||
The query checks both existing polling status identities: the ordinary identity and the safety
|
||||
poll identity with `reuseLineStats: true`. These remain separate owner identities; the second
|
||||
lookup is read-only and does not merge them. Identity also retains worktree path, native versus
|
||||
exact WSL distro, shared-link paths, `includeIgnored`, negative-cache bypass, and every explicit
|
||||
push-target field. The Checks context key now preserves absent `remoteCreated` versus explicit
|
||||
`false`, as well as absent versus present `remoteUrl`.
|
||||
|
||||
SSH lookup is routed to the current provider instance for `connectionId`, so replacement and
|
||||
reconnect incarnations cannot share projections. Local folder-workspace IDs are reduced to their
|
||||
registered backing folder path before IPC. Active runtime-environment ownership returns `null`
|
||||
from the desktop client and uses the existing runtime Git path; runtime/mobile snapshot transport
|
||||
is not added in this slice.
|
||||
|
||||
Manual Checks Refresh remains on the existing fresh APIs. No Git command, SSH RPC method or
|
||||
payload, retry timer, visibility interval, slow-task scheduler, signal, mutation wrapper, relay
|
||||
routing, or provider-specific review behavior changed.
|
||||
|
||||
## Deterministic physical-owner measurement
|
||||
|
||||
The focused owner and SSH-provider tests model the same settled overlap: active polling completes
|
||||
status plus upstream publication, then automatic Checks requests the same identity in that refresh
|
||||
horizon.
|
||||
|
||||
| Boundary | A — fresh Checks | B — snapshot query |
|
||||
| ------------------------------------------ | ---------------: | -----------------: |
|
||||
| Local/WSL physical status loads | 2 | 1 |
|
||||
| Local/WSL physical explicit-upstream loads | 2 | 1 |
|
||||
| SSH `git.status` RPCs | 2 | 1 |
|
||||
| SSH `git.upstreamStatus` RPCs | 2 | 1 |
|
||||
|
||||
The B query itself adds two read-only owner lookups in the worst case—ordinary and
|
||||
`reuseLineStats` identities—but zero subprocesses and zero mux requests. If neither snapshot is
|
||||
admissible, the original fresh load count and behavior are preserved.
|
||||
|
||||
The physical status command remains:
|
||||
|
||||
```text
|
||||
git -c core.quotePath=false status --porcelain=v2 --branch --untracked-files=all
|
||||
```
|
||||
|
||||
The configured-upstream chain remains:
|
||||
|
||||
```text
|
||||
git symbolic-ref --quiet --short HEAD
|
||||
git rev-parse --abbrev-ref HEAD@{u}
|
||||
git rev-list --left-right --count HEAD...origin/main
|
||||
git log --oneline --cherry-mark --right-only HEAD...origin/main --
|
||||
```
|
||||
|
||||
The SSH methods and payload omission rules remain:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"method": "git.status",
|
||||
"payload": { "worktreePath": "/home/user/repo" }
|
||||
},
|
||||
{
|
||||
"method": "git.upstreamStatus",
|
||||
"payload": {
|
||||
"worktreePath": "/home/user/repo",
|
||||
"pushTarget": {
|
||||
"remoteName": "fork",
|
||||
"branchName": "feature",
|
||||
"remoteUrl": "ssh://git.example/repo",
|
||||
"remoteCreated": false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Absent options and absent `pushTarget` remain omitted. Existing command and provider suites
|
||||
continue to cover configured upstreams, explicit publish targets, ahead/behind and patch
|
||||
equivalence, no-upstream behavior, Git 2.25 compatibility, WSL path handling, SSH/system-SSH, and
|
||||
relay routing.
|
||||
|
||||
## Production A/B
|
||||
|
||||
Fresh production builds were archived outside the worktree:
|
||||
|
||||
- A: `/tmp/orca-phase2-checks-snapshot-a.hBrWy8/out`
|
||||
- B: `/tmp/orca-phase2-checks-snapshot-b-reviewed.bNuaXj/out`
|
||||
|
||||
| Artifact | A raw / gzip | B raw / gzip | Change | A SHA-256 | B SHA-256 |
|
||||
| ---------------------- | ----------------- | ----------------- | ---------- | ------------------------------------------------------------------ | ------------------------------------------------------------------ |
|
||||
| Electron main entry | 789,517 / 176,812 | 789,869 / 176,858 | +352 / +46 | `6413904f6420882168b8993c1240614bacde1de92dfb26a55c4db67c84007b8e` | `7f4984a54c65cf5b749a87ed7ec7f2074c5b844992215ad25e8c17d179045660` |
|
||||
| Electron preload entry | 130,798 / 20,642 | 130,891 / 20,658 | +93 / +16 | `c388a39cdca9609760e286d95b87ad1e53793720450e507f830ff1f6c5bd259f` | `afa8494e803f0e57cabdb5d2ff636f30fc8021069a7694d57d2ecdcdd6837a8a` |
|
||||
| Renderer manifest | 403,000 / 48,291 | 403,000 / 48,288 | 0 / -3 | `92ad617f8c8aee6d4c4a23622f524266cbe9c93b3fe65544d1acb833dc3f0d67` | `9f57d550a1c9f6c75bf2d3db4f5ff53e53c4c52eceb8b10e9b86d8659a48d7fe` |
|
||||
|
||||
The Electron-main raw budget remains 825,109 bytes; B retains 35,240 bytes of headroom. File
|
||||
counts remain 184 main, one preload, and 787 renderer artifacts.
|
||||
|
||||
| Renderer entry | A raw / gzip | B raw / gzip | Change | A JS/CSS | B JS/CSS |
|
||||
| -------------- | --------------------- | --------------------- | ------ | -------- | -------- |
|
||||
| Index | 8,416,540 / 1,877,823 | 8,416,540 / 1,877,824 | 0 / +1 | 292 / 2 | 292 / 2 |
|
||||
| Popout | 4,507,253 / 984,615 | 4,507,253 / 984,615 | 0 / 0 | 77 / 2 | 77 / 2 |
|
||||
| Web | 4,360,652 / 928,355 | 4,360,652 / 928,352 | 0 / -3 | 33 / 1 | 33 / 1 |
|
||||
|
||||
Deterministic tree hashes use sorted relative paths plus file bytes:
|
||||
|
||||
| Tree | A SHA-256 | B SHA-256 |
|
||||
| -------- | ------------------------------------------------------------------ | ------------------------------------------------------------------ |
|
||||
| Main | `70eee832a22181051dd0a807ac8192f2d2863b0f6706c21199b98245abb11d82` | `8d24293dafe6879300ff618f62582ebc68483a1af85f09c943dcdf63009f2696` |
|
||||
| Preload | `2d85a03ecdb77d9762630a9fe2783b4da2042e09e470a43250b697f24008f209` | `f211a353bdfcb09de909134f1c2d91c920819f20b3f8b26baca5e72ba9f6e05b` |
|
||||
| Renderer | `0b5610733f4e17999a72ea356965276cd728320400af97fdd8c83c42a50a0e71` | `2d792b983f3d45c8d3a2e270599705518f45c2b4a936925256bbea04505b0d52` |
|
||||
|
||||
Complete A and B manifest validation found 778 records, three HTML entries, 6,247 static edges,
|
||||
213 dynamic edges, and 860 emitted references, with zero missing or escaping targets, zero
|
||||
cross-entry imports, and zero cycles.
|
||||
|
||||
An initial rejected renderer arm placed the desktop query in broad `runtime-git-client.ts`. Its
|
||||
archive is `/tmp/orca-phase2-checks-snapshot-b.IaVlMF/out`; it added 643 raw bytes to each of
|
||||
index, popout, and web. Moving the query to the concrete
|
||||
`desktop-git-repository-snapshot-client.ts` boundary eliminates that eager raw-byte tax while
|
||||
keeping the same main/preload cost and call-count win.
|
||||
|
||||
## Validation
|
||||
|
||||
- Focused owner/provider/filesystem/runtime/Checks suite:
|
||||
`pnpm exec vitest run ...` — 276 passed, one skipped after the independent review added an
|
||||
ambiguous embedded-upstream fallback regression.
|
||||
- Broad host owner, status/cache, native/WSL, SSH provider/dispatch, and filesystem suite:
|
||||
`pnpm exec vitest run ...` — 364 passed, one skipped.
|
||||
- Broad renderer runtime, status refresh/polling, push-target cache, Checks, and folder-workspace
|
||||
suite: `pnpm exec vitest run --config config/vitest.config.ts ...` — 128 passed.
|
||||
- Independent combined host/renderer review suite: 481 passed, one skipped.
|
||||
- `pnpm run typecheck:node`: passed.
|
||||
- `pnpm run typecheck:web`: passed.
|
||||
- Targeted `pnpm exec oxlint --deny-warnings`: passed.
|
||||
- Targeted `pnpm exec oxfmt --check`: passed.
|
||||
- `pnpm run check:max-lines-ratchet`: passed with 354 grandfathered suppressions and no new bypass.
|
||||
- Fresh A and B `pnpm run build:electron-vite`: passed.
|
||||
- `pnpm run check:electron-bundle-budgets`: passed against fresh B.
|
||||
- Complete renderer manifest/path and static-cycle validation: passed for A and B.
|
||||
- `git diff --check`: passed.
|
||||
- Added/changed max-lines-disable scan: zero.
|
||||
|
||||
## Limitations and next seam
|
||||
|
||||
- Savings occur when polling has published an admissible projection before automatic Checks
|
||||
demand. A cold Checks open, stale projection, mutation fence, branch transition, truncated
|
||||
status, ambiguous upstream, or failed read deliberately pays the existing fresh work.
|
||||
- The counts are deterministic mocked physical-owner and mux-boundary measurements, not live
|
||||
subprocess latency measurements across every supported host.
|
||||
- The read-only IPC is desktop-only. Active runtime environments intentionally fall back to their
|
||||
existing fresh RPC route; runtime/mobile projection transport remains later work.
|
||||
- This slice does not subscribe Checks to snapshot revisions. The next coherent seam is a bounded
|
||||
host-to-renderer revision subscription that invalidates consumers without adding another polling
|
||||
loop, followed by migration of remaining renderer status consumers.
|
||||
- No packaged smoke was run on Windows, WSL, Linux, or a live SSH host. Existing routing,
|
||||
identity, folder-workspace, provider, mutation-fence, and Git-command suites cover the unchanged
|
||||
boundaries.
|
||||
Reference in New Issue
Block a user