feat: add observability readiness gate

This commit is contained in:
Affaan Mustafa
2026-05-11 18:23:53 -04:00
committed by Affaan Mustafa
parent ab6e998383
commit 8aa8c32d2a
9 changed files with 624 additions and 3 deletions

View File

@@ -109,6 +109,27 @@ if (
passed++;
else failed++;
if (
test('skips Python virtual environments', () => {
const root = makeTempRoot('ecc-unicode-venv-');
fs.mkdirSync(path.join(root, '.venv', 'lib', 'python3.12', 'site-packages'), { recursive: true });
fs.mkdirSync(path.join(root, 'venv', 'lib', 'python3.12', 'site-packages'), { recursive: true });
fs.writeFileSync(
path.join(root, '.venv', 'lib', 'python3.12', 'site-packages', 'package.py'),
`message = "hello ${rocketEmoji}"\n`
);
fs.writeFileSync(
path.join(root, 'venv', 'lib', 'python3.12', 'site-packages', 'package.py'),
`message = "hello ${rocketEmoji}"\n`
);
const result = runCheck(root);
assert.strictEqual(result.status, 0, result.stdout + result.stderr);
})
)
passed++;
else failed++;
console.log(`\nPassed: ${passed}`);
console.log(`Failed: ${failed}`);
process.exit(failed > 0 ? 1 : 0);

View File

@@ -53,6 +53,7 @@ function buildExpectedPublishPaths(repoRoot) {
"scripts/install-plan.js",
"scripts/list-installed.js",
"scripts/loop-status.js",
"scripts/observability-readiness.js",
"scripts/skill-create-output.js",
"scripts/repair.js",
"scripts/harness-audit.js",

View File

@@ -0,0 +1,209 @@
/**
* Tests for scripts/observability-readiness.js
*/
const assert = require('assert');
const fs = require('fs');
const os = require('os');
const path = require('path');
const { execFileSync, spawnSync } = require('child_process');
const SCRIPT = path.join(__dirname, '..', '..', 'scripts', 'observability-readiness.js');
const { buildReport, parseArgs } = require(SCRIPT);
function createTempDir(prefix) {
return fs.mkdtempSync(path.join(os.tmpdir(), prefix));
}
function cleanup(dirPath) {
fs.rmSync(dirPath, { recursive: true, force: true });
}
function writeFile(rootDir, relativePath, content) {
const targetPath = path.join(rootDir, relativePath);
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
fs.writeFileSync(targetPath, content);
}
function run(args = [], options = {}) {
return execFileSync('node', [SCRIPT, ...args], {
cwd: options.cwd || path.join(__dirname, '..', '..'),
encoding: 'utf8',
stdio: ['pipe', 'pipe', 'pipe'],
timeout: 10000
});
}
function runProcess(args = [], options = {}) {
return spawnSync('node', [SCRIPT, ...args], {
cwd: options.cwd || path.join(__dirname, '..', '..'),
encoding: 'utf8',
stdio: ['pipe', 'pipe', 'pipe'],
timeout: 10000
});
}
function seedMinimalRepo(rootDir, overrides = {}) {
const files = {
'package.json': JSON.stringify({
name: 'everything-claude-code',
files: ['scripts/observability-readiness.js'],
scripts: {
'harness:audit': 'node scripts/harness-audit.js',
'observability:ready': 'node scripts/observability-readiness.js'
}
}, null, 2),
'scripts/loop-status.js': '--json --watch --write-dir',
'scripts/session-inspect.js': '--list-adapters --write inspectSessionTarget',
'scripts/lib/session-adapters/registry.js': 'module.exports = {};',
'scripts/harness-audit.js': 'Deterministic harness audit --format overall_score',
'scripts/hooks/session-activity-tracker.js': 'tool-usage.jsonl session_id tool_name',
'ecc2/src/observability/mod.rs': 'ToolCallEvent RiskAssessment ToolLogger',
'ecc2/src/session/store.rs': 'insert_tool_log query_tool_logs',
'ecc2/src/session/manager.rs': 'sync_tool_activity_metrics tool-usage.jsonl',
'docs/architecture/observability-readiness.md': 'node scripts/observability-readiness.js --format json',
'docs/releases/2.0.0-rc.1/quickstart.md': 'observability-readiness.md',
'docs/releases/2.0.0-rc.1/release-notes.md': 'observability-readiness.md'
};
for (const [relativePath, content] of Object.entries({ ...files, ...overrides })) {
if (content === null) {
continue;
}
writeFile(rootDir, relativePath, content);
}
}
function test(name, fn) {
try {
fn();
console.log(`${name}`);
return true;
} catch (error) {
console.log(`${name}`);
console.log(` Error: ${error.message}`);
return false;
}
}
function runTests() {
console.log('\n=== Testing observability-readiness.js ===\n');
let passed = 0;
let failed = 0;
if (test('parseArgs accepts supported forms and rejects invalid input', () => {
const rootDir = createTempDir('observability-readiness-args-');
try {
assert.strictEqual(parseArgs(['node', 'script', '--help']).help, true);
assert.strictEqual(parseArgs(['node', 'script', '-h']).help, true);
const spaced = parseArgs(['node', 'script', '--format', 'json', '--root', rootDir]);
assert.strictEqual(spaced.format, 'json');
assert.strictEqual(spaced.root, path.resolve(rootDir));
const equals = parseArgs(['node', 'script', '--format=json', `--root=${rootDir}`]);
assert.strictEqual(equals.format, 'json');
assert.strictEqual(equals.root, path.resolve(rootDir));
assert.throws(() => parseArgs(['node', 'script', '--format', 'xml']), /Invalid format: xml/);
assert.throws(() => parseArgs(['node', 'script', '--root']), /--root requires a value/);
assert.throws(() => parseArgs(['node', 'script', '--unknown']), /Unknown argument: --unknown/);
} finally {
cleanup(rootDir);
}
})) passed++; else failed++;
if (test('cli help exits cleanly and invalid cli args exit with stderr', () => {
const help = runProcess(['--help']);
assert.strictEqual(help.status, 0);
assert.strictEqual(help.stderr, '');
assert.ok(help.stdout.includes('Usage: node scripts/observability-readiness.js'));
const invalid = runProcess(['--format', 'xml']);
assert.strictEqual(invalid.status, 1);
assert.strictEqual(invalid.stdout, '');
assert.ok(invalid.stderr.includes('Error: Invalid format: xml. Use text or json.'));
})) passed++; else failed++;
if (test('current repo reports a complete readiness score', () => {
const parsed = JSON.parse(run(['--format=json']));
assert.strictEqual(parsed.schema_version, 'ecc.observability-readiness.v1');
assert.strictEqual(parsed.deterministic, true);
assert.strictEqual(parsed.ready, true);
assert.strictEqual(parsed.overall_score, parsed.max_score);
assert.strictEqual(parsed.top_actions.length, 0);
})) passed++; else failed++;
if (test('text output includes summary, categories, and checks', () => {
const output = run();
assert.ok(output.includes('Observability Readiness:'));
assert.ok(output.includes('Categories:'));
assert.ok(output.includes('Checks:'));
assert.ok(output.includes('PASS loop-status-live-signal'));
})) passed++; else failed++;
if (test('minimal seeded repo passes all checks', () => {
const projectRoot = createTempDir('observability-readiness-pass-');
try {
seedMinimalRepo(projectRoot);
const report = buildReport(projectRoot);
assert.strictEqual(report.ready, true);
assert.strictEqual(report.overall_score, report.max_score);
assert.deepStrictEqual(report.top_actions, []);
} finally {
cleanup(projectRoot);
}
})) passed++; else failed++;
if (test('missing tool logger surfaces become prioritized top actions', () => {
const projectRoot = createTempDir('observability-readiness-fail-');
try {
seedMinimalRepo(projectRoot, {
'ecc2/src/observability/mod.rs': 'ToolCallEvent only'
});
const report = buildReport(projectRoot);
assert.strictEqual(report.ready, false);
assert.ok(report.top_actions.some(action => action.id === 'ecc2-tool-risk-ledger'));
assert.ok(report.checks.some(check => check.id === 'ecc2-tool-risk-ledger' && !check.pass));
} finally {
cleanup(projectRoot);
}
})) passed++; else failed++;
if (test('missing release onramp fails without disturbing core tool checks', () => {
const projectRoot = createTempDir('observability-readiness-doc-fail-');
try {
seedMinimalRepo(projectRoot, {
'docs/releases/2.0.0-rc.1/quickstart.md': 'quickstart without link'
});
const report = buildReport(projectRoot);
assert.strictEqual(report.ready, false);
assert.ok(report.checks.some(check => check.id === 'release-observability-onramp' && !check.pass));
assert.ok(report.checks.some(check => check.id === 'loop-status-live-signal' && check.pass));
} finally {
cleanup(projectRoot);
}
})) passed++; else failed++;
console.log('\nResults:');
console.log(` Passed: ${passed}`);
console.log(` Failed: ${failed}`);
if (failed > 0) {
process.exit(1);
}
}
if (require.main === module) {
runTests();
}