🎉 init: 小龙的工作空间

This commit is contained in:
大海
2026-06-06 10:40:48 +08:00
commit a188ee1426
3201 changed files with 231817 additions and 0 deletions
+339
View File
@@ -0,0 +1,339 @@
#!/usr/bin/env node
/**
* audit-portfolio.mjs — PORTFOLIO.md Portfolio Management Audit
*
* Checks:
* 1. portfolio.md exists (template or actual)
* 2. Project states are legal (7-state portfolio states)
* 3. Priority / Health / ROI / Agent Allocation fields are complete
* 4. Duplicate work detection mechanism exists
* 5. Portfolio Health Score formula defined
* 6. Kill / Pause / Promotion rules defined
* 7. Executive Dashboard schema defined
*
* Usage:
* node scripts/audit-portfolio.mjs ← standalone
* import { runPortfolioAudit } from './audit-portfolio.mjs' ← via orchestrator
*/
import { fileURLToPath } from 'url';
import path from 'path';
import fs from 'fs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const WORKSPACE = path.resolve(__dirname, '..');
const SELF = process.argv[1].replace(/\\/g, '/');
/**
* @typedef {Object} CheckResult
* @property {string} id
* @property {string} name
* @property {'PASS'|'WARN'|'FAIL'} status
* @property {string} evidence
* @property {string} [suggestion]
*/
/** @returns {CheckResult[]} */
export function runPortfolioAudit(workspaceDir) {
const root = workspaceDir || WORKSPACE;
const checks = [];
// ---- Helpers ----
function readFile(rel) {
const p = path.join(root, rel);
if (!fs.existsSync(p)) return null;
return fs.readFileSync(p, 'utf8');
}
function check(prefix, id, name, ok, passEv, failEv, suggestion) {
checks.push({
id: `${prefix}-${id}`,
name,
status: ok ? 'PASS' : 'FAIL',
evidence: ok ? passEv : failEv,
...(ok ? {} : { suggestion: suggestion || `Implement ${id} in PORTFOLIO.md` }),
});
}
function warn(prefix, id, name, evidence, suggestion) {
checks.push({ id: `${prefix}-${id}`, name, status: 'WARN', evidence, suggestion });
}
// ---- Read protocol file ----
const port = readFile('PORTFOLIO.md');
if (!port) {
checks.push({
id: 'PF-001',
name: 'PORTFOLIO.md exists',
status: 'FAIL',
evidence: 'PORTFOLIO.md not found in workspace root',
suggestion: 'Create PORTFOLIO.md with the Portfolio Management Protocol',
});
return checks;
}
checks.push({
id: 'PF-001',
name: 'PORTFOLIO.md exists',
status: 'PASS',
evidence: `PORTFOLIO.md found (${port.length} bytes)`,
});
// ---- §1 Portfolio Registry ----
const hasReg = /§1 Portfolio Registry/.test(port);
check('PF', '1', '§1 Portfolio Registry defined', hasReg,
'Found "§1 Portfolio Registry" with schema and field definitions',
'Add §1 Portfolio Registry section');
// Registry schema has all required fields
const hasFields = /Project-ID.*Name.*Status.*Priority.*Health.*Agents.*Progress.*Last Updated.*ROI/.test(port);
check('PF', '1a', '§1a Registry schema: all 9 fields (ID/Name/Status/Priority/Health/Agents/Progress/Updated/ROI)',
hasFields,
'Registry schema found with all 9 fields',
'Ensure registry schema includes: Project-ID, Name, Status, Priority, Health, Agents, Progress, Last Updated, ROI');
// portfolio.md template exists
const portTmpl = readFile('scripts/templates/portfolio.md.template');
check('PF', '1b', 'portfolio.md.template exists', portTmpl !== null,
'Found scripts/templates/portfolio.md.template',
'Create scripts/templates/portfolio.md.template');
// Actual portfolio.md in workspace?
const actualPortfolio = readFile('portfolio.md');
warn('PF', '1c', 'Actual portfolio.md in workspace root',
actualPortfolio
? `Found portfolio.md (${actualPortfolio.length} bytes)`
: 'No portfolio.md found — expected when projects are active',
actualPortfolio ? undefined : 'Create portfolio.md when first project becomes active');
// ---- §2 Project States ----
const hasStates = /§2 Project States/.test(port);
check('PF', '2', '§2 Project States defined', hasStates,
'Found "§2 Project States" with 7 portfolio states and transition rules',
'Add §2 Project States with PLANNING/ACTIVE/BLOCKED/RECOVERY/PAUSED/COMPLETED/ARCHIVED');
// 7 states
const states = ['PLANNING', 'ACTIVE', 'BLOCKED', 'RECOVERY', 'PAUSED', 'COMPLETED', 'ARCHIVED'];
const foundStates = states.filter(s => port.includes(s));
check('PF', '2a', `§2a ${states.length} portfolio states defined (found ${foundStates.length})`,
foundStates.length >= states.length,
`All ${states.length} states present: ${foundStates.join(', ')}`,
`Missing: ${states.filter(s => !port.includes(s)).join(', ')}`,
'Add missing portfolio states');
// State diagram
const hasDiagram = /stateDiagram-v2/.test(port);
check('PF', '2b', '§2b Portfolio state transition diagram (mermaid)', hasDiagram,
'Found mermaid stateDiagram-v2 for portfolio states',
'Add mermaid stateDiagram-v2 for portfolio state transitions');
// Prohibited transitions
const hasProhibited = /Prohibited Transitions[\s\S]*❌/.test(port);
check('PF', '2c', '§2c Prohibited portfolio transitions defined', hasProhibited,
'Found Prohibited Transitions with ❌ markers',
'Add Prohibited Transitions section for portfolio states');
// ---- §3 Priority System ----
const hasPriority = /§3 Priority System/.test(port);
check('PF', '3', '§3 Priority System defined (P0/P1/P2/P3)', hasPriority,
'Found "§3 Priority System" with P0(P1(P2(P3 levels)',
'Add §3 Priority System with P0(P1(P2(P3 levels');
const hasP0 = port.includes('P0');
const hasP1 = port.includes('P1');
const hasP2 = port.includes('P2');
const hasP3 = port.includes('P3');
check('PF', '3a', `§3a All priority levels: P0/P1/P2/P3`, hasP0 && hasP1 && hasP2 && hasP3,
`P0=${hasP0}, P1=${hasP1}, P2=${hasP2}, P3=${hasP3}`,
'Define all 4 priority levels: P0(Mission Critical), P1(High Value), P2(Normal), P3(Low Priority)');
// Collision resolution
const hasCollision = /Priority Collision/.test(port);
check('PF', '3b', '§3b Priority collision resolution defined', hasCollision,
'Found Priority Collision Resolution: ROI sort → dependency sort → user decision',
'Add Priority Collision Resolution for multiple P0 conflicts');
// ---- §4 Resource Allocation ----
const hasResources = /§4 Resource Allocation/.test(port);
check('PF', '4', '§4 Resource Allocation defined', hasResources,
'Found "§4 Resource Allocation" with Agent Inventory / Utilization Score / Rebalancing',
'Add §4 Resource Allocation section');
// Agent inventory = 5 metrics
const hasTotal = port.includes('Total Agents');
const hasBusy = port.includes('Busy Agents');
const hasIdle = port.includes('Idle Agents');
const hasBlocked = port.includes('Blocked Agents');
const hasRecovery = port.includes('Recovery Agents');
const all5inv = hasTotal && hasBusy && hasIdle && hasBlocked && hasRecovery;
check('PF', '4a', `§4a Agent inventory: all 5 metrics (Total/Busy/Idle/Blocked/Recovery)`,
all5inv,
`Total=${hasTotal}, Busy=${hasBusy}, Idle=${hasIdle}, Blocked=${hasBlocked}, Recovery=${hasRecovery}`,
'Define Agent Inventory with Total/Busy/Idle/Blocked/Recovery metrics');
// Utilization formula
const hasUtilFormula = /Utilization\s*=\s*Busy_Agents\s*\/\s*Total_Agents/.test(port);
check('PF', '4b', '§4b Agent Utilization formula defined', hasUtilFormula,
'Found Utilization = Busy_Agents / Total_Agents with 4-level rating',
'Define Agent Utilization Score formula and rating');
// ---- §5 Project Health Aggregation ----
const hasAgg = /§5 Project Health Aggregation/.test(port);
check('PF', '5', '§5 Project Health Aggregation defined', hasAgg,
'Found "§5 Project Health Aggregation" with 5 dimensions and weights',
'Add §5 Project Health Aggregation with Delivery/Quality/Learning/Execution/Risk dimensions');
// ---- §6 Portfolio Health Score ----
const hasPortHealth = /§6 Portfolio Health Score/.test(port);
check('PF', '6', '§6 Portfolio Health Score defined', hasPortHealth,
'Found "§6 Portfolio Health Score" with weighted average formula',
'Add §6 Portfolio Health Score with weighted average calculation');
// Weighted formula
const hasWeighted = /加权[\s\S]*平均/.test(port) || /weighted/i.test(port) || /Σ\(Project_Health[\s\S]*Agent_Count\)/.test(port);
check('PF', '6a', '§6a Weighted portfolio formula defined', hasWeighted,
'Found weighted portfolio formula (Project_Health × Agent_Count / Σ Agent_Count)',
'Define Portfolio_Health = Σ(Project_Health × Agent_Count) / Σ(Agent_Count)');
// ---- §7 Learning Reuse ----
const hasReuse = /§7 Learning Reuse/.test(port);
check('PF', '7', '§7 Learning Reuse Detector defined', hasReuse,
'Found "§7 Learning Reuse Detector" with detection criteria and action plan',
'Add §7 Learning Reuse Detector with duplicate work detection');
// Duplicate work criteria
const hasDupCriteria = /Duplicate Work/.test(port);
check('PF', '7a', '§7a Duplicate Work criteria defined', hasDupCriteria,
'Found Duplicate Work criteria (30% overlap, same Pattern Class, same deps, same domain)',
'Add Duplicate Work criteria with actionable thresholds');
// ---- §8 Portfolio Audit ----
const hasAudit = /§8 Portfolio Audit/.test(port);
check('PF', '8', '§8 Portfolio Audit defined', hasAudit,
'Found "§8 Portfolio Audit" with daily generation and audit schema',
'Add §8 Portfolio Audit with daily generation and schema');
// portfolio-audit.md template exists
const auditTmpl = readFile('scripts/templates/portfolio-audit.md.template');
check('PF', '8a', 'portfolio-audit.md.template exists', auditTmpl !== null,
'Found scripts/templates/portfolio-audit.md.template',
'Create scripts/templates/portfolio-audit.md.template');
// ---- §9 Kill Rule ----
const hasKill = /§9 Project Kill/.test(port);
check('PF', '9', '§9 Project Kill Rule defined', hasKill,
'Found "§9 Project Kill Rule" with K1-K4 conditions and graceful shutdown',
'Add §9 Project Kill Rule with K1(Health<50), K2(>3 recovery fails), K3(ROI<threshold), K4(Scope+P3)');
// ---- §10 Pause Rule ----
const hasPause = /§10 Project Pause/.test(port);
check('PF', '10', '§10 Project Pause Rule defined', hasPause,
'Found "§10 Project Pause Rule" with P1-P3 conditions and resume decision',
'Add §10 Project Pause Rule with P1(Priority), P2(Resources), P3(Dependency)');
// ---- §11 Promotion Rule ----
const hasPromotion = /§11 Project Promotion/.test(port);
check('PF', '11', '§11 Project Promotion Rule defined', hasPromotion,
'Found "§11 Project Promotion Rule" with F1(Health>90), F2(Progress>80%), F3(Risk<20)',
'Add §11 Project Promotion Rule with Fast Track criteria');
// ---- §12 Executive Dashboard ----
const hasDash = /§12 Executive Dashboard/.test(port);
check('PF', '12', '§12 Executive Dashboard defined', hasDash,
'Found "§12 Executive Dashboard" with Portfolio Health / Top Projects / Risks / Opportunities / Utilization / Learning Reuse / Quick Actions',
'Add §12 Executive Dashboard schema');
// dashboard.md template exists
const dashTmpl = readFile('scripts/templates/dashboard.md.template');
check('PF', '12a', 'dashboard.md.template exists', dashTmpl !== null,
'Found scripts/templates/dashboard.md.template',
'Create scripts/templates/dashboard.md.template');
// ---- §13 Quality Gate ----
const hasQualityGate = /§13 Portfolio Quality Gate/.test(port);
check('PF', '13', '§13 Portfolio Quality Gate defined', hasQualityGate,
'Found "§13 Portfolio Quality Gate" with 8-item checklist',
'Add §13 Portfolio Quality Gate with 8 pre-output checks');
// ---- §14 Integration ----
const hasIntegration = /§14 Integration Summary/.test(port);
check('PF', '14', '§14 Integration Summary defined', hasIntegration,
'Found "§14 Integration Summary" with data flow and lifecycle',
'Add §14 Integration Summary with data flow and lifecycle diagram');
// ---- Cross-refs ----
const refsGovernance = (port.match(/GOVERNANCE\.md/g) || []).length;
warn('PF', 'XR-gov', `Cross-references to GOVERNANCE.md: ${refsGovernance} (target: ≥5)`,
`Found ${refsGovernance} references to GOVERNANCE.md`,
'PORTFOLIO.md should reference GOVERNANCE.md for project-level health/blockers');
const refsAgents = (port.match(/AGENTS\.md/g) || []).length;
warn('PF', 'XR-ag', `Cross-references to AGENTS.md: ${refsAgents} (target: ≥2)`,
`Found ${refsAgents} references to AGENTS.md`,
'PORTFOLIO.md should reference AGENTS.md for execution protocol');
// ---- Duplicate work scan ----
const projectDirs = findProjectDirs(root);
warn('PF', 'DW-scan', `Potential duplicate work scan: ${projectDirs.length} project directories found`,
projectDirs.length > 0
? `Found ${projectDirs.length} project directories. Manual review recommended for duplicate patterns: ${projectDirs.join(', ')}`
: 'No project directories found for duplicate work analysis',
projectDirs.length >= 2 ? 'Check for duplicate code across project directories' : undefined);
// ---- Portfolio Health Score (from available data) ----
checks.push({
id: 'PF-HS',
name: 'Portfolio Health Score calculated',
status: 'WARN',
evidence: 'No active project data available for portfolio health calculation. Formula is defined in PORTFOLIO.md §6.',
suggestion: 'Portfolio Health will be calculated after at least one project has a Health Score',
});
// ---- Summary ----
const passCount = checks.filter(c => c.status === 'PASS').length;
const warnCount = checks.filter(c => c.status === 'WARN').length;
const failCount = checks.filter(c => c.status === 'FAIL').length;
checks.push({
id: 'PF-SUMMARY',
name: `Summary — ${passCount} PASS / ${warnCount} WARN / ${failCount} FAIL`,
status: failCount > 0 ? 'FAIL' : warnCount > 0 ? 'WARN' : 'PASS',
evidence: `${passCount} passed, ${warnCount} warnings, ${failCount} failures`,
});
return checks;
}
/** Find all directories that look like projects (have progress.log or AGENTS.md ref) */
function findProjectDirs(root) {
const candidates = [];
try {
const entries = fs.readdirSync(root, { withFileTypes: true });
for (const e of entries) {
if (e.isDirectory() && !e.name.startsWith('.') && !['node_modules', 'scripts', 'memory', 'repos'].includes(e.name)) {
// Check if this dir has project-like files
const sub = path.join(root, e.name);
const hasProgress = fs.existsSync(path.join(sub, 'progress.log'));
const hasReadme = fs.existsSync(path.join(sub, 'README.md'));
const hasPkg = fs.existsSync(path.join(sub, 'package.json'));
if (hasProgress || hasReadme || hasPkg) {
candidates.push(e.name);
}
}
}
} catch { /* skip */ }
return candidates;
}
// ---- Standalone runner ----
if (SELF.endsWith('/audit-portfolio.mjs') || SELF.endsWith('\\audit-portfolio.mjs')) {
const results = runPortfolioAudit(WORKSPACE);
console.log(`\n=== PORTFOLIO.md Audit ===\n`);
for (const r of results) {
const icon = r.status === 'PASS' ? '✅' : r.status === 'WARN' ? '⚠️' : '❌';
console.log(` ${icon} [${r.id}] ${r.name}`);
console.log(` ${r.evidence}`);
if (r.suggestion) console.log(`${r.suggestion}`);
console.log();
}
}