@event4u/agent-config 9.22.0 → 9.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +84 -238
- package/README.md +2 -2
- package/dist/agent-src/commands/agent-handoff.md +10 -3
- package/dist/agent-src/commands/analyze/inbox.md +190 -0
- package/dist/agent-src/commands/analyze.md +4 -3
- package/dist/agent-src/commands/optimize/deep.md +13 -0
- package/dist/agent-src/commands/pr/create.md +6 -6
- package/dist/agent-src/contexts/execution/cheap-question-mechanics.md +12 -2
- package/dist/agent-src/contexts/execution/mandated-lines.md +180 -0
- package/dist/agent-src/rules/context-hygiene.md +20 -0
- package/dist/agent-src/rules/downstream-changes.md +26 -0
- package/dist/agent-src/rules/think-before-action.md +25 -0
- package/dist/agent-src/rules/token-budget-discipline.md +46 -3
- package/dist/agent-src/rules/token-efficiency.md +13 -0
- package/dist/agent-src/rules/ui-audit-gate.md +33 -2
- package/dist/agent-src/skills/adversarial-review/SKILL.md +18 -4
- package/dist/agent-src/skills/decision-review/SKILL.md +49 -0
- package/dist/agent-src/skills/judge-artifact-completeness/SKILL.md +5 -1
- package/dist/agent-src/skills/judge-bug-hunter/SKILL.md +16 -0
- package/dist/agent-src/skills/judge-synthesis/SKILL.md +14 -1
- package/dist/agent-src/skills/rule-writing/SKILL.md +33 -0
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +31 -0
- package/dist/agent-src/skills/skill-reviewer/SKILL.md +1 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli/registry.js +1 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/chunk-DVRS2KVR.js +4602 -0
- package/dist/cli-delegate/{chunk-ELMFOQPW.js → chunk-SJQMJI7M.js} +8 -4597
- package/dist/cli-delegate/cmd_export.js +2 -1
- package/dist/cli-delegate/cmd_prune.js +2 -1
- package/dist/cli-delegate/cmd_settings_migrate.js +2 -1
- package/dist/cli-delegate/cmd_settings_set.js +3370 -0
- package/dist/cli-delegate/cmd_sync.js +2 -1
- package/dist/cli-delegate/cmd_uninstall.js +2 -1
- package/dist/cli-delegate/cmd_validate.js +2 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +54 -27
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +6 -5
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +5 -4
- package/dist/hooks/dispatch.js +79 -1
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/scripts/condense.js +47 -2
- package/dist/scripts/condense.js.map +1 -1
- package/dist/server/routes/settings.js +82 -0
- package/dist/server/routes/settings.js.map +1 -1
- package/dist/shared/settingsClasses.js +191 -0
- package/dist/shared/settingsClasses.js.map +1 -0
- package/docs/contracts/settings-classes.md +342 -0
- package/docs/guidelines/agent-infra/artifact-drafting-protocol-mechanics.md +23 -2
- package/docs/guidelines/agent-infra/false-green.md +187 -0
- package/docs/guidelines/agent-infra/gate-authoring.md +177 -0
- package/package.json +3 -1
- package/src/config/ci-local-parity.yml +12 -0
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +54 -12
- package/src/config/gate-violation-baselines.json +6 -0
- package/src/config/pack-size-budget.json +4 -3
- package/src/scripts/_cli/cmd_settings_set.ts +509 -0
- package/src/scripts/_dispatch.bash +16 -0
- package/src/scripts/_lib/gate_ledger.ts +295 -0
- package/src/scripts/_lib/gate_result.ts +101 -0
- package/src/scripts/_lib/gate_self_test.ts +129 -0
- package/src/scripts/_lib/measured_render.ts +97 -0
- package/src/scripts/_lib/ratchet_base_ref.ts +373 -0
- package/src/scripts/check_gate_completeness.ts +244 -0
- package/src/scripts/check_gate_coverage.ts +35 -6
- package/src/scripts/check_references.ts +125 -15
- package/src/scripts/check_review_dispositions.ts +14 -1
- package/src/scripts/check_suppression_hygiene.ts +503 -0
- package/src/scripts/condense.ts +47 -2
- package/src/scripts/hook_manifest.yaml +23 -0
- package/src/scripts/hooks/dispatch_hook.ts +65 -1
- package/src/scripts/hooks/host_semantics.ts +140 -0
- package/src/scripts/lint_framework_leakage.ts +147 -15
- package/src/scripts/lint_mandated_lines.ts +339 -0
- package/src/scripts/lint_roadmap_complexity.ts +102 -11
- package/src/scripts/lint_roadmap_family_cap.ts +191 -0
- package/src/scripts/lint_settings_classes.ts +397 -0
- package/src/scripts/lint_token_budget_discipline.ts +109 -1
- package/src/scripts/measure_rule_absoluta.ts +231 -0
- package/src/scripts/report_imperative_density.ts +229 -0
- package/src/scripts/verify_physical_move.ts +10 -1
- package/src/server/io/yamlIO.ts +173 -0
- package/src/shared/settingsClasses.ts +235 -0
|
@@ -52,6 +52,7 @@ import * as yaml from 'js-yaml';
|
|
|
52
52
|
import { checkRatchet } from './_lib/gate_baseline.js';
|
|
53
53
|
import { runCountedProbe } from './_lib/counted_probe.js';
|
|
54
54
|
import { listGateScripts } from './_lib/gate_population.js';
|
|
55
|
+
import { describeOutcome, namesEstateInvalidatingError } from './_lib/gate_result.js';
|
|
55
56
|
import { assertScanned } from './_lib/scan_scope.js';
|
|
56
57
|
|
|
57
58
|
const _HERE = fileURLToPath(import.meta.url);
|
|
@@ -100,7 +101,14 @@ export interface GateSpec {
|
|
|
100
101
|
note?: string;
|
|
101
102
|
}
|
|
102
103
|
|
|
103
|
-
export type Verdict =
|
|
104
|
+
export type Verdict =
|
|
105
|
+
| 'ok'
|
|
106
|
+
| 'below_floor'
|
|
107
|
+
| 'silent'
|
|
108
|
+
| 'crashed'
|
|
109
|
+
| 'estate_invalid'
|
|
110
|
+
| 'pending'
|
|
111
|
+
| 'unavailable';
|
|
104
112
|
|
|
105
113
|
export interface GateResult {
|
|
106
114
|
id: string;
|
|
@@ -186,6 +194,7 @@ export function classify(
|
|
|
186
194
|
scanned: number | null,
|
|
187
195
|
crashed: boolean,
|
|
188
196
|
exit_code?: number | null,
|
|
197
|
+
output = '',
|
|
189
198
|
): GateResult {
|
|
190
199
|
const base = { id: spec.id, status: spec.status, scanned, min_scanned: spec.min_scanned };
|
|
191
200
|
if (spec.status === 'pending') {
|
|
@@ -201,6 +210,18 @@ export function classify(
|
|
|
201
210
|
if (crashed) {
|
|
202
211
|
return { ...base, verdict: 'crashed', message: 'gate could not be executed' };
|
|
203
212
|
}
|
|
213
|
+
// A gate that could not MEASURE is not a gate that found violations, and it
|
|
214
|
+
// is not a gate that passed either. Reporting it as itself is the whole point:
|
|
215
|
+
// three of this repository's recorded traps are estate invalidation
|
|
216
|
+
// misreported as a per-gate red, sending a contributor hunting for a
|
|
217
|
+
// violation that does not exist.
|
|
218
|
+
if (namesEstateInvalidatingError(output)) {
|
|
219
|
+
return {
|
|
220
|
+
...base,
|
|
221
|
+
verdict: 'estate_invalid',
|
|
222
|
+
message: describeOutcome(spec.id, 'estate_invalid'),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
204
225
|
if (
|
|
205
226
|
spec.unavailable_exit !== undefined &&
|
|
206
227
|
exit_code !== undefined &&
|
|
@@ -234,16 +255,23 @@ export function classify(
|
|
|
234
255
|
return { ...base, verdict: 'ok', message: `scanned ${String(scanned)} ≥ ${String(spec.min_scanned)}` };
|
|
235
256
|
}
|
|
236
257
|
|
|
237
|
-
function run_gate(spec: GateSpec): {
|
|
258
|
+
function run_gate(spec: GateSpec): {
|
|
259
|
+
scanned: number | null;
|
|
260
|
+
crashed: boolean;
|
|
261
|
+
exit_code: number | null;
|
|
262
|
+
output: string;
|
|
263
|
+
} {
|
|
238
264
|
const runner = path.join(REPO_ROOT, 'scripts-run');
|
|
239
265
|
// Bounded read: a gate whose output overflowed would lose its `scanned:`
|
|
240
266
|
// line and be classified `silent` — the scan-scope guard reporting a dead
|
|
241
267
|
// scope that is merely truncated. `runCountedProbe` throws instead.
|
|
242
268
|
const r = runCountedProbe(runner, [`src/scripts/${spec.id}`, ...spec.argv], { cwd: REPO_ROOT });
|
|
243
|
-
|
|
269
|
+
const output = `${r.stdout}\n${r.stderr}`;
|
|
270
|
+
if (r.failure !== null && r.status === null)
|
|
271
|
+
return { scanned: null, crashed: true, exit_code: null, output };
|
|
244
272
|
// A gate may legitimately exit non-zero (it found real violations) and still
|
|
245
273
|
// have scanned plenty — coverage and verdict are different questions.
|
|
246
|
-
return { scanned: parse_scanned(
|
|
274
|
+
return { scanned: parse_scanned(output), crashed: false, exit_code: r.status, output };
|
|
247
275
|
}
|
|
248
276
|
|
|
249
277
|
const ICON: Record<Verdict, string> = {
|
|
@@ -251,6 +279,7 @@ const ICON: Record<Verdict, string> = {
|
|
|
251
279
|
below_floor: '❌',
|
|
252
280
|
silent: '❌',
|
|
253
281
|
crashed: '❌',
|
|
282
|
+
estate_invalid: '❌',
|
|
254
283
|
pending: '⚠️',
|
|
255
284
|
unavailable: '⚠️',
|
|
256
285
|
};
|
|
@@ -299,8 +328,8 @@ export function main(argv: readonly string[]): number {
|
|
|
299
328
|
const { scanned, crashed } = probe_pending(s);
|
|
300
329
|
return classify(s, scanned, crashed);
|
|
301
330
|
}
|
|
302
|
-
const { scanned, crashed, exit_code } = run_gate(s);
|
|
303
|
-
return classify(s, scanned, crashed, exit_code);
|
|
331
|
+
const { scanned, crashed, exit_code, output } = run_gate(s);
|
|
332
|
+
return classify(s, scanned, crashed, exit_code, output);
|
|
304
333
|
});
|
|
305
334
|
|
|
306
335
|
if (wantJson) {
|
|
@@ -21,10 +21,18 @@
|
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
23
|
import * as fs from 'node:fs';
|
|
24
|
+
import * as os from 'node:os';
|
|
24
25
|
import * as path from 'node:path';
|
|
25
26
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
26
27
|
import type * as YamlModule from 'yaml';
|
|
27
28
|
|
|
29
|
+
import { GateLedger, type GateSkipReason } from './_lib/gate_ledger.js';
|
|
30
|
+
import { runGateCli, runSelfTest } from './_lib/gate_self_test.js';
|
|
31
|
+
import { assertScanned } from './_lib/scan_scope.js';
|
|
32
|
+
|
|
33
|
+
/** Where this script lives — used only to re-invoke its own CLI in `--self-test`. */
|
|
34
|
+
const REPO_ROOT_FOR_SELF_TEST = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
35
|
+
|
|
28
36
|
type Severity = 'error' | 'warning';
|
|
29
37
|
|
|
30
38
|
interface BrokenRef {
|
|
@@ -480,13 +488,26 @@ function _closest_match(name: string, candidates: Set<string>): string {
|
|
|
480
488
|
return '';
|
|
481
489
|
}
|
|
482
490
|
|
|
483
|
-
function check_file(
|
|
491
|
+
function check_file(
|
|
492
|
+
filepath: string,
|
|
493
|
+
artifacts: Artifacts,
|
|
494
|
+
root: string,
|
|
495
|
+
/**
|
|
496
|
+
* Called instead of checking, when the file is not judged at all. Both
|
|
497
|
+
* early returns below used to return an empty `broken[]`, which is
|
|
498
|
+
* byte-identical to "checked and clean" for every caller — so an
|
|
499
|
+
* opted-out file counted as coverage. The sink lets the ledger tell the
|
|
500
|
+
* two apart without changing this function's return type.
|
|
501
|
+
*/
|
|
502
|
+
onSkip?: (reason: GateSkipReason) => void,
|
|
503
|
+
): BrokenRef[] {
|
|
484
504
|
// Check a single .md file for broken references.
|
|
485
505
|
const broken: BrokenRef[] = [];
|
|
486
506
|
let text: string;
|
|
487
507
|
try {
|
|
488
508
|
text = fs.readFileSync(filepath, 'utf-8');
|
|
489
509
|
} catch {
|
|
510
|
+
onSkip?.('binary_content');
|
|
490
511
|
return broken;
|
|
491
512
|
}
|
|
492
513
|
|
|
@@ -496,6 +517,7 @@ function check_file(filepath: string, artifacts: Artifacts, root: string): Broke
|
|
|
496
517
|
// with the per-line `<!-- ref-ignore -->` below; either suffices.
|
|
497
518
|
const headerLines = text.split('\n').slice(0, 10);
|
|
498
519
|
if (headerLines.some((line) => line.includes(FILE_SKIP_MARKER))) {
|
|
520
|
+
onSkip?.('declared_exemption');
|
|
499
521
|
return broken;
|
|
500
522
|
}
|
|
501
523
|
|
|
@@ -783,37 +805,73 @@ function check_memory_yaml(filepath: string, artifacts: Artifacts, root: string)
|
|
|
783
805
|
function scan_all(root: string): BrokenRef[] {
|
|
784
806
|
const artifacts = collect_artifacts(root);
|
|
785
807
|
const broken: BrokenRef[] = [];
|
|
786
|
-
//
|
|
787
|
-
//
|
|
788
|
-
//
|
|
789
|
-
|
|
808
|
+
// Completeness accounting. The two hidden outcomes this makes visible:
|
|
809
|
+
// a missing scan root (`continue` on a dead directory, previously silent
|
|
810
|
+
// and uncounted — the canonical false green), and a file that opted out of
|
|
811
|
+
// the check but was still counted as coverage.
|
|
812
|
+
const ledger = new GateLedger('check_references');
|
|
790
813
|
for (const scanDir of SCAN_DIRS) {
|
|
791
814
|
const d = path.join(root, scanDir);
|
|
792
815
|
if (!_exists(d)) {
|
|
816
|
+
ledger.plan(scanDir);
|
|
817
|
+
ledger.skip(scanDir, 'dead_scan_root');
|
|
793
818
|
continue;
|
|
794
819
|
}
|
|
795
820
|
const files = _rglob(d, '.md').sort();
|
|
821
|
+
ledger.plan(files.map((f) => _relPosix(root, f)));
|
|
796
822
|
for (const f of files) {
|
|
823
|
+
const rel = _relPosix(root, f);
|
|
797
824
|
// Skip archived directories
|
|
798
825
|
if (SKIP_DIRS.some((skip) => f.startsWith(path.join(root, skip)))) {
|
|
826
|
+
ledger.outOfScope(rel, 'excluded_directory');
|
|
799
827
|
continue;
|
|
800
828
|
}
|
|
801
|
-
|
|
802
|
-
|
|
829
|
+
let skipped = false;
|
|
830
|
+
const found = check_file(f, artifacts, root, (reason) => {
|
|
831
|
+
skipped = true;
|
|
832
|
+
ledger.skip(rel, reason);
|
|
833
|
+
});
|
|
834
|
+
if (!skipped) {
|
|
835
|
+
if (found.length > 0) {
|
|
836
|
+
ledger.fail(rel, `${String(found.length)} broken reference(s)`);
|
|
837
|
+
} else {
|
|
838
|
+
ledger.complete(rel);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
broken.push(...found);
|
|
803
842
|
}
|
|
804
843
|
}
|
|
805
844
|
const memoryDir = path.join(root, MEMORY_YAML_ROOT);
|
|
806
845
|
if (_isDir(memoryDir)) {
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
846
|
+
const yamls = [..._rglob(memoryDir, '.yml').sort(), ..._rglob(memoryDir, '.yaml').sort()];
|
|
847
|
+
ledger.plan(yamls.map((f) => _relPosix(root, f)));
|
|
848
|
+
for (const f of yamls) {
|
|
849
|
+
const rel = _relPosix(root, f);
|
|
850
|
+
const found = check_memory_yaml(f, artifacts, root);
|
|
851
|
+
if (found.length > 0) {
|
|
852
|
+
ledger.fail(rel, `${String(found.length)} broken reference(s)`);
|
|
853
|
+
} else {
|
|
854
|
+
ledger.complete(rel);
|
|
855
|
+
}
|
|
856
|
+
broken.push(...found);
|
|
814
857
|
}
|
|
815
858
|
}
|
|
816
|
-
|
|
859
|
+
// gate-coverage contract (src/config/gate-coverage.yml): a machine-readable
|
|
860
|
+
// count of files actually JUDGED, so `check_gate_coverage` can assert this
|
|
861
|
+
// gate saw a real corpus rather than a moved root. It is the ledger's own
|
|
862
|
+
// completed+failed, so the published number cannot drift from the number
|
|
863
|
+
// the accounting accepted.
|
|
864
|
+
const tally = ledger.report();
|
|
865
|
+
// Fail closed on a fully dead scope. Both scan roots absent used to print
|
|
866
|
+
// `scanned: 0` and then `✅ No broken references found.` — the canonical
|
|
867
|
+
// false green, and the one shape this gate had no guard against.
|
|
868
|
+
assertScanned({
|
|
869
|
+
gate: 'check_references',
|
|
870
|
+
scanned: tally.completed + tally.failed,
|
|
871
|
+
units: 'file(s)',
|
|
872
|
+
roots: SCAN_DIRS,
|
|
873
|
+
});
|
|
874
|
+
process.stderr.write(`scanned: ${String(tally.completed + tally.failed)}\n`);
|
|
817
875
|
return broken;
|
|
818
876
|
}
|
|
819
877
|
|
|
@@ -918,7 +976,59 @@ function _argparse_error(message: string): never {
|
|
|
918
976
|
process.exit(2);
|
|
919
977
|
}
|
|
920
978
|
|
|
979
|
+
/**
|
|
980
|
+
* Prove, against the real CLI, that this gate still rejects what it must.
|
|
981
|
+
*
|
|
982
|
+
* The dead-scope case is the one that matters: until this change, both scan
|
|
983
|
+
* roots absent produced `scanned: 0` followed by "No broken references found"
|
|
984
|
+
* and exit 0.
|
|
985
|
+
*/
|
|
986
|
+
export function selfTest(): number {
|
|
987
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'refs-selftest-'));
|
|
988
|
+
const seed = (root: string, body: string | null): string => {
|
|
989
|
+
const dir = path.join(tmp, root, 'dist', 'agent-src', 'rules');
|
|
990
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
991
|
+
if (body !== null) {
|
|
992
|
+
fs.writeFileSync(path.join(dir, 'demo.md'), body, 'utf-8');
|
|
993
|
+
}
|
|
994
|
+
return path.join(tmp, root);
|
|
995
|
+
};
|
|
996
|
+
const run = (root: string): number =>
|
|
997
|
+
runGateCli(REPO_ROOT_FOR_SELF_TEST, 'src/scripts/check_references.ts', ['--root', root], REPO_ROOT_FOR_SELF_TEST);
|
|
998
|
+
|
|
999
|
+
try {
|
|
1000
|
+
return runSelfTest({
|
|
1001
|
+
gate: 'check_references',
|
|
1002
|
+
minCases: 3,
|
|
1003
|
+
minRejectCases: 2,
|
|
1004
|
+
cases: [
|
|
1005
|
+
{
|
|
1006
|
+
name: 'a broken file reference is rejected',
|
|
1007
|
+
expect: 'reject',
|
|
1008
|
+
run: () =>
|
|
1009
|
+
run(seed('broken', '# Demo\n\nSee `docs/guidelines/definitely-not-here.md` for detail.\n')),
|
|
1010
|
+
},
|
|
1011
|
+
{
|
|
1012
|
+
name: 'a document with no references passes',
|
|
1013
|
+
expect: 'accept',
|
|
1014
|
+
run: () => run(seed('clean', '# Demo\n\nNothing to resolve here.\n')),
|
|
1015
|
+
},
|
|
1016
|
+
{
|
|
1017
|
+
name: 'an EMPTY corpus is rejected — a gate that read nothing has not passed',
|
|
1018
|
+
expect: 'reject',
|
|
1019
|
+
run: () => run(seed('empty', null)),
|
|
1020
|
+
},
|
|
1021
|
+
],
|
|
1022
|
+
});
|
|
1023
|
+
} finally {
|
|
1024
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
|
|
921
1028
|
function main(): number {
|
|
1029
|
+
if (process.argv.slice(2).includes('--self-test')) {
|
|
1030
|
+
return selfTest();
|
|
1031
|
+
}
|
|
922
1032
|
const args = parse_args(process.argv.slice(2));
|
|
923
1033
|
|
|
924
1034
|
let broken: BrokenRef[];
|
|
@@ -45,6 +45,7 @@ import * as fs from 'node:fs';
|
|
|
45
45
|
import * as path from 'node:path';
|
|
46
46
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
47
47
|
|
|
48
|
+
import { GateLedger } from './_lib/gate_ledger.js';
|
|
48
49
|
import { DeadScopeError, assertScanned } from './_lib/scan_scope.js';
|
|
49
50
|
import { parseArtifact } from './check_completion_review.js';
|
|
50
51
|
|
|
@@ -150,12 +151,23 @@ export function main(argv?: readonly string[]): number {
|
|
|
150
151
|
}
|
|
151
152
|
|
|
152
153
|
const violations: Violation[] = [];
|
|
154
|
+
// Per-target completeness accounting: the read below can throw partway and
|
|
155
|
+
// the catch reports `scanned` as if the remaining records had been judged.
|
|
156
|
+
// The ledger names what was actually reached.
|
|
157
|
+
const ledger = new GateLedger('check_review_dispositions');
|
|
158
|
+
ledger.plan(files);
|
|
153
159
|
let scanned = 0;
|
|
154
160
|
try {
|
|
155
161
|
for (const rel of files) {
|
|
156
162
|
const text = fs.readFileSync(path.resolve(repo, rel), 'utf-8');
|
|
157
163
|
scanned += 1;
|
|
158
|
-
|
|
164
|
+
const found = checkRecord(rel, text, { repo });
|
|
165
|
+
if (found.length > 0) {
|
|
166
|
+
ledger.fail(rel, `${String(found.length)} disposition violation(s)`);
|
|
167
|
+
} else {
|
|
168
|
+
ledger.complete(rel);
|
|
169
|
+
}
|
|
170
|
+
violations.push(...found);
|
|
159
171
|
}
|
|
160
172
|
} catch (exc) {
|
|
161
173
|
process.stdout.write(`scanned: ${String(scanned)}\n`);
|
|
@@ -174,6 +186,7 @@ export function main(argv?: readonly string[]): number {
|
|
|
174
186
|
} else if (!quiet) {
|
|
175
187
|
process.stdout.write(`✅ Archived review records all terminal (${String(scanned)} record(s) scanned).\n`);
|
|
176
188
|
}
|
|
189
|
+
ledger.report();
|
|
177
190
|
process.stdout.write(`scanned: ${String(scanned)}\n`);
|
|
178
191
|
return violations.length > 0 ? 1 : 0;
|
|
179
192
|
}
|