mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-10-30 17:31:54 +00:00
Expand doctor operational diagnostics
This commit is contained in:
1 parent
6ac3dec1df
commit
f892ab2402
6 files changed
+105
-7
No files matched your search
+59
-2
@@ -6,12 +6,15 @@ import { projectStatePath } from './paths.mjs';
|
||||
import { inspectManagedWorktree } from './worktree-state.mjs';
|
||||
import { validateProjectConfig } from './config.mjs';
|
||||
import { forgeStatus } from '../forge/service.mjs';
|
||||
import { classifyRecoverability } from '../canonical/local.mjs';
|
||||
import { createClaudeAdapter } from '../adapters/claude.mjs';
|
||||
import { createCodexAdapter } from '../adapters/codex.mjs';
|
||||
|
||||
function parseJsonYaml(path) {
|
||||
return JSON.parse(readFileSync(path, 'utf8'));
|
||||
}
|
||||
|
||||
export function diagnoseProject({ repository, env = process.env }) {
|
||||
export function diagnoseProject({ repository, env = process.env, now = () => new Date() }) {
|
||||
const trackedRoot = join(repository.root, '.torch');
|
||||
const configPath = join(trackedRoot, 'torch.yaml');
|
||||
const manifestPath = join(trackedRoot, 'install-manifest.json');
|
||||
@@ -129,10 +132,28 @@ export function diagnoseProject({ repository, env = process.env }) {
|
||||
const state = inspectManagedWorktree(repository.root, entry, config?.project?.main_branch ?? 'main');
|
||||
for (const problem of state.problems) {
|
||||
findings.push({
|
||||
severity: problem === 'worktree-dirty' || problem.startsWith('unique-commits:') ? 'warning' : 'error',
|
||||
severity: problem === 'worktree-dirty' || problem.startsWith('unique-commits:')
|
||||
|| problem.startsWith('git-operation:') ? 'warning' : 'error',
|
||||
code: 'WORKTREE_PROBLEM', area: entry.area, path: entry.path, problem,
|
||||
});
|
||||
}
|
||||
if (state.ahead || state.behind) {
|
||||
const missingAt = state.oldestMissingCommitAt ? Date.parse(state.oldestMissingCommitAt) : null;
|
||||
findings.push({
|
||||
severity: 'info', code: 'WORKTREE_DRIFT', area: entry.area, path: entry.path,
|
||||
branch: state.branch, commit: state.commit, drift: state.drift,
|
||||
ahead: state.ahead, behind: state.behind,
|
||||
oldestMissingCommitAt: state.oldestMissingCommitAt,
|
||||
driftAgeSeconds: Number.isFinite(missingAt)
|
||||
? Math.max(0, Math.floor((now().getTime() - missingAt) / 1000)) : null,
|
||||
recommendation: 'Ask the owning domain to review an intentional convergence plan at a safe point.',
|
||||
});
|
||||
}
|
||||
if (state.operations.length) findings.push({
|
||||
severity: 'warning', code: 'GIT_OPERATION_ACTIVE', area: entry.area,
|
||||
path: entry.path, operations: state.operations,
|
||||
recommendation: 'The owning domain must complete or deliberately abort the Git operation.',
|
||||
});
|
||||
}
|
||||
const managerWorktree = (manifest.external ?? []).find((entry) =>
|
||||
entry.type === 'worktree' && entry.area === 'session-manager');
|
||||
@@ -169,6 +190,42 @@ export function diagnoseProject({ repository, env = process.env }) {
|
||||
if (repository.dirtyEntries.length) {
|
||||
findings.push({ severity: 'info', code: 'WORKTREE_DIRTY', count: repository.dirtyEntries.length });
|
||||
}
|
||||
try {
|
||||
const recoverability = classifyRecoverability({ repositoryRoot: repository.root, commit: repository.head });
|
||||
findings.push({
|
||||
severity: recoverability.offMachine ? 'info' : 'warning', code: 'RECOVERABILITY',
|
||||
...recoverability,
|
||||
recommendation: recoverability.offMachine
|
||||
? 'No action required for this commit.'
|
||||
: 'Configure and synchronize an off-machine canonical Git boundary before relying on this checkout for recovery.',
|
||||
});
|
||||
} catch (error) {
|
||||
findings.push({ severity: 'error', code: 'RECOVERABILITY_INVALID', message: error.message });
|
||||
}
|
||||
if (config?.runtimes) {
|
||||
const adapters = new Map([
|
||||
['claude', createClaudeAdapter({ env })],
|
||||
['codex', createCodexAdapter({ env })],
|
||||
]);
|
||||
for (const runtime of Object.keys(config.runtimes).filter((name) => name !== 'default')) {
|
||||
const adapter = adapters.get(runtime);
|
||||
if (!adapter) {
|
||||
findings.push({
|
||||
severity: 'warning', code: 'RUNTIME_ADAPTER_UNKNOWN', runtime,
|
||||
recommendation: 'Install or configure a TORCH runtime adapter before starting this identity.',
|
||||
});
|
||||
continue;
|
||||
}
|
||||
const detection = adapter.detect();
|
||||
findings.push({
|
||||
severity: detection.available ? 'info' : 'warning',
|
||||
code: detection.available ? 'RUNTIME_AVAILABLE' : 'RUNTIME_UNAVAILABLE',
|
||||
runtime, executable: detection.executable,
|
||||
recommendation: detection.available ? 'No action required.'
|
||||
: `Install ${runtime} or change the approved runtime assignment before startup.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (config?.forge?.provider && config.forge.provider !== 'none') {
|
||||
try {
|
||||
const forge = forgeStatus({ repositoryRoot: repository.root });
|
||||
|
||||
@@ -14,14 +14,22 @@ export function inspectManagedWorktree(root, entry, mainBranch) {
|
||||
const problems = [];
|
||||
const actualBranch = git(entry.path, ['branch', '--show-current']);
|
||||
if (actualBranch !== entry.branch) problems.push(`branch-mismatch:${actualBranch || 'detached'}`);
|
||||
const operations = ['MERGE_HEAD', 'REBASE_HEAD', 'CHERRY_PICK_HEAD', 'REVERT_HEAD']
|
||||
.filter((name) => existsSync(git(entry.path, ['rev-parse', '--git-path', name])));
|
||||
for (const operation of operations) problems.push(`git-operation:${operation}`);
|
||||
const status = git(entry.path, ['status', '--porcelain=v1', '--untracked-files=all']);
|
||||
if (status) problems.push('worktree-dirty');
|
||||
const commit = git(entry.path, ['rev-parse', 'HEAD']);
|
||||
const ahead = Number(git(root, ['rev-list', '--count', `${mainBranch}..${entry.branch}`]) || 0);
|
||||
const behind = Number(git(root, ['rev-list', '--count', `${entry.branch}..${mainBranch}`]) || 0);
|
||||
const oldestMissingCommitAt = behind > 0
|
||||
? git(root, ['log', '--reverse', '--format=%cI', `${entry.branch}..${mainBranch}`]).split('\n')[0] || null
|
||||
: null;
|
||||
if (ahead > 0) problems.push(`unique-commits:${ahead}`);
|
||||
return {
|
||||
...entry, actualBranch, ahead, behind,
|
||||
drift: ahead === 0 && behind === 0 ? 'current' : 'diverged',
|
||||
...entry, actualBranch, commit, ahead, behind, oldestMissingCommitAt, operations,
|
||||
drift: ahead > 0 && behind > 0 ? 'diverged'
|
||||
: (ahead > 0 ? 'ahead' : (behind > 0 ? 'behind' : 'current')),
|
||||
safeToRemove: problems.length === 0, problems,
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user