mirror of
https://github.com/affaan-m/ECC.git
synced 2026-09-26 19:45:03 +02:00
docs(gateguard): document the graduated gate controls
GateGuard reads five GATEGUARD_* environment variables that were absent from skills/gateguard/SKILL.md, so the only discoverable escape hatch was ECC_GATEGUARD=off - disabling the load-bearing destructive-Bash gate along with the noisy ones (#2573). Documented, with defaults and exact accepted values read from the hook: - GATEGUARD_BASH_ROUTINE_DISABLED (was undocumented everywhere) - GATEGUARD_EXEMPT_GLOBS (previously only in a 2.1.0 release note) - GATEGUARD_BASH_EXTRA_DESTRUCTIVE (was undocumented) - GATEGUARD_DISABLED (was undocumented) - GATEGUARD_STATE_DIR (was undocumented; named in a runtime warning) - GATEGUARD_FACT_FORCE_FULL_DENIALS (already documented; folded into the same table for one lookup point) Adds tests/ci/gateguard-env-documented.test.js, which asserts every GATEGUARD_* variable the hook reads appears in the skill doc, and that the doc names no variable the hook has stopped reading. That surface test is what found the three knobs beyond the two the issue reported. Docs and test only; no hook behaviour changes. Refs #2573
This commit is contained in:
committed by
haelyra
parent
2f8a5a271d
commit
4377ea1753
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Surface test for #2573: every GATEGUARD_* environment variable the hook
|
||||
* reads must be documented in the GateGuard skill doc.
|
||||
*
|
||||
* `GATEGUARD_BASH_ROUTINE_DISABLED` shipped with no documentation at all and
|
||||
* `GATEGUARD_EXEMPT_GLOBS` was mentioned only in a release note, so operators
|
||||
* had no discoverable way to narrow the gate short of disabling it outright.
|
||||
* This pins the surface: adding a knob to the hook without documenting it
|
||||
* fails here.
|
||||
*
|
||||
* Run with: node tests/ci/gateguard-env-documented.test.js
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const repoRoot = path.join(__dirname, '..', '..');
|
||||
const hookPath = path.join(repoRoot, 'scripts', 'hooks', 'gateguard-fact-force.js');
|
||||
const skillPath = path.join(repoRoot, 'skills', 'gateguard', 'SKILL.md');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(` \u2713 ${name}`);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.log(` \u2717 ${name}`);
|
||||
console.log(` Error: ${err.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function readGateguardEnvNames(source) {
|
||||
// process.env.GATEGUARD_X and process.env['GATEGUARD_X']
|
||||
const names = new Set();
|
||||
const dotted = /process\.env\.(GATEGUARD_[A-Z0-9_]+)/g;
|
||||
const bracketed = /process\.env\[\s*['"](GATEGUARD_[A-Z0-9_]+)['"]\s*\]/g;
|
||||
let m;
|
||||
while ((m = dotted.exec(source)) !== null) names.add(m[1]);
|
||||
while ((m = bracketed.exec(source)) !== null) names.add(m[1]);
|
||||
return names;
|
||||
}
|
||||
|
||||
console.log('\nGateGuard env-var documentation surface\n');
|
||||
|
||||
if (test('hook and skill doc both exist', () => {
|
||||
assert.ok(fs.existsSync(hookPath), `missing ${hookPath}`);
|
||||
assert.ok(fs.existsSync(skillPath), `missing ${skillPath}`);
|
||||
})) passed++; else failed++;
|
||||
|
||||
const hookSource = fs.existsSync(hookPath) ? fs.readFileSync(hookPath, 'utf8') : '';
|
||||
const skillDoc = fs.existsSync(skillPath) ? fs.readFileSync(skillPath, 'utf8') : '';
|
||||
const envNames = readGateguardEnvNames(hookSource);
|
||||
|
||||
if (test('hook reads at least one GATEGUARD_* variable', () => {
|
||||
assert.ok(envNames.size > 0, 'no GATEGUARD_* env reads found - has the hook moved?');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('every GATEGUARD_* variable the hook reads is documented', () => {
|
||||
const undocumented = [...envNames].filter(name => !skillDoc.includes(name)).sort();
|
||||
assert.deepStrictEqual(
|
||||
undocumented,
|
||||
[],
|
||||
`undocumented in skills/gateguard/SKILL.md: ${undocumented.join(', ')}`
|
||||
);
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('the documented knobs are the ones the hook actually reads', () => {
|
||||
// Guards the reverse drift: a doc naming a knob the hook no longer reads.
|
||||
const documented = [...new Set(
|
||||
(skillDoc.match(/GATEGUARD_[A-Z0-9_]+/g) || [])
|
||||
)];
|
||||
const stale = documented.filter(name => !hookSource.includes(name)).sort();
|
||||
assert.deepStrictEqual(stale, [], `documented but unread by the hook: ${stale.join(', ')}`);
|
||||
})) passed++; else failed++;
|
||||
|
||||
console.log(`\nPassed: ${passed}`);
|
||||
console.log(`Failed: ${failed}\n`);
|
||||
|
||||
if (failed > 0) {
|
||||
process.exit(1);
|
||||
}
|
||||
Reference in New Issue
Block a user