@event4u/agent-config 8.9.0 → 8.11.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 +189 -0
- package/README.md +5 -5
- package/dist/agent-src/commands/challenge-me/vision.md +121 -32
- package/dist/agent-src/commands/challenge-me/with-docs.md +21 -8
- package/dist/agent-src/commands/commit/in-chunks.md +1 -1
- package/dist/agent-src/commands/commit.md +2 -1
- package/dist/agent-src/commands/cost/report.md +14 -3
- package/dist/agent-src/commands/feature/dev.md +4 -4
- package/dist/agent-src/commands/humanize.md +101 -0
- package/dist/agent-src/commands/jira-ticket.md +2 -2
- package/dist/agent-src/commands/memory/load.md +15 -0
- package/dist/agent-src/commands/optimize/project.md +12 -0
- package/dist/agent-src/commands/roadmap/create.md +12 -11
- package/dist/agent-src/commands/tests/create.md +29 -7
- package/dist/agent-src/commands/work.md +33 -0
- package/dist/agent-src/commands/worktree/cleanup.md +83 -0
- package/dist/agent-src/commands/worktree/create.md +64 -0
- package/dist/agent-src/commands/worktree/status.md +75 -0
- package/dist/agent-src/commands/worktree/verify.md +70 -0
- package/dist/agent-src/commands/worktree.md +85 -0
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +66 -0
- package/dist/agent-src/contexts/execution/orchestration-benchmark-gate.md +1 -0
- package/dist/agent-src/contexts/execution/orchestration-telemetry.md +91 -34
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +14 -0
- package/dist/agent-src/contexts/execution/roadmap-writing-source-derived.md +65 -0
- package/dist/agent-src/contexts/execution/subagent-modes-detail.md +84 -0
- package/dist/agent-src/rules/autonomous-execution.md +7 -37
- package/dist/agent-src/rules/broken-access-control.md +7 -45
- package/dist/agent-src/rules/code-comment-discipline.md +148 -0
- package/dist/agent-src/rules/context-hygiene.md +14 -0
- package/dist/agent-src/rules/delegation-policy.md +10 -8
- package/dist/agent-src/rules/design-fidelity.md +44 -0
- package/dist/agent-src/rules/git-history-discipline.md +4 -79
- package/dist/agent-src/rules/legal-safety-floor.md +11 -55
- package/dist/agent-src/rules/roadmap-progress-sync.md +5 -56
- package/dist/agent-src/rules/spreadsheet-source-quality.md +61 -0
- package/dist/agent-src/rules/think-before-action.md +10 -0
- package/dist/agent-src/rules/verify-before-complete.md +12 -0
- package/dist/agent-src/skills/accessibility-auditor/evals/evals.json +15 -0
- package/dist/agent-src/skills/agent-docs-writing/SKILL.md +13 -12
- package/dist/agent-src/skills/ai-council/SKILL.md +19 -9
- package/dist/agent-src/skills/analysis-skill-router/evals/evals.json +13 -0
- package/dist/agent-src/skills/api-design/SKILL.md +7 -5
- package/dist/agent-src/skills/api-testing/SKILL.md +16 -2
- package/dist/agent-src/skills/authz-review/SKILL.md +59 -2
- package/dist/agent-src/skills/blast-radius-analyzer/SKILL.md +1 -1
- package/dist/agent-src/skills/bug-analyzer/SKILL.md +19 -16
- package/dist/agent-src/skills/character-consistency/evals/evals.json +16 -0
- package/dist/agent-src/skills/code-refactoring/SKILL.md +9 -9
- package/dist/agent-src/skills/command-routing/evals/evals.json +14 -0
- package/dist/agent-src/skills/content-funnel-design/SKILL.md +2 -0
- package/dist/agent-src/skills/dashboard-design/evals/evals.json +16 -0
- package/dist/agent-src/skills/data-flow-mapper/SKILL.md +1 -1
- package/dist/agent-src/skills/dcf-modeling/evals/evals.json +20 -0
- package/dist/agent-src/skills/decision-record/evals/evals.json +20 -0
- package/dist/agent-src/skills/defense-in-depth/SKILL.md +2 -2
- package/dist/agent-src/skills/dependency-upgrade/SKILL.md +7 -7
- package/dist/agent-src/skills/design-intelligence/SKILL.md +8 -8
- package/dist/agent-src/skills/design-intelligence/evals/evals.json +14 -0
- package/dist/agent-src/skills/design-review/SKILL.md +29 -2
- package/dist/agent-src/skills/design-system-capture/SKILL.md +21 -0
- package/dist/agent-src/skills/design-system-capture/evals/evals.json +15 -0
- package/dist/agent-src/skills/developer-like-execution/evals/evals.json +16 -0
- package/dist/agent-src/skills/doc-coauthoring/SKILL.md +29 -0
- package/dist/agent-src/skills/doc-coauthoring/evals/evals.json +16 -0
- package/dist/agent-src/skills/editorial-calendar/SKILL.md +2 -0
- package/dist/agent-src/skills/editorial-calendar/evals/domain-truth.json +16 -0
- package/dist/agent-src/skills/editorial-calendar/evals/evals.json +16 -0
- package/dist/agent-src/skills/estimate-ticket/evals/evals.json +16 -0
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +30 -0
- package/dist/agent-src/skills/existing-ui-audit/evals/triggers.json +47 -0
- package/dist/agent-src/skills/fe-design/SKILL.md +43 -14
- package/dist/agent-src/skills/fe-design/evals/triggers.json +47 -0
- package/dist/agent-src/skills/forecasting/evals/evals.json +20 -0
- package/dist/agent-src/skills/fundraising-narrative/evals/domain-truth.json +16 -0
- package/dist/agent-src/skills/fundraising-narrative/evals/evals.json +16 -0
- package/dist/agent-src/skills/git-workflow/SKILL.md +86 -0
- package/dist/agent-src/skills/html-deck/SKILL.md +27 -0
- package/dist/agent-src/skills/humanizer/SKILL.md +147 -0
- package/dist/agent-src/skills/humanizer/data/patterns.md +190 -0
- package/dist/agent-src/skills/humanizer/evals/triggers.json +17 -0
- package/dist/agent-src/skills/iconography/SKILL.md +24 -0
- package/dist/agent-src/skills/incident-commander/evals/domain-truth.json +16 -0
- package/dist/agent-src/skills/incident-commander/evals/evals.json +20 -0
- package/dist/agent-src/skills/laravel/SKILL.md +14 -1
- package/dist/agent-src/skills/launch-readiness/SKILL.md +20 -19
- package/dist/agent-src/skills/launch-readiness/evals/evals.json +21 -0
- package/dist/agent-src/skills/legal-practice-profile/SKILL.md +126 -0
- package/dist/agent-src/skills/logging-monitoring/SKILL.md +1 -1
- package/dist/agent-src/skills/logging-monitoring/evals/evals.json +16 -0
- package/dist/agent-src/skills/mcp/SKILL.md +1 -1
- package/dist/agent-src/skills/memory-consolidation/SKILL.md +14 -1
- package/dist/agent-src/skills/messaging-architecture/evals/domain-truth.json +16 -0
- package/dist/agent-src/skills/messaging-architecture/evals/evals.json +16 -0
- package/dist/agent-src/skills/migration-architect/SKILL.md +2 -1
- package/dist/agent-src/skills/perf-feedback-craft/evals/evals.json +16 -0
- package/dist/agent-src/skills/performance/SKILL.md +1 -1
- package/dist/agent-src/skills/pest-testing/SKILL.md +1 -1
- package/dist/agent-src/skills/php-debugging/SKILL.md +17 -0
- package/dist/agent-src/skills/playwright-testing/SKILL.md +16 -0
- package/dist/agent-src/skills/prompt-engineering-patterns/evals/evals.json +16 -0
- package/dist/agent-src/skills/readme-writing/SKILL.md +2 -0
- package/dist/agent-src/skills/readme-writing-package/SKILL.md +2 -0
- package/dist/agent-src/skills/refine-prompt/evals/evals.json +16 -0
- package/dist/agent-src/skills/refine-ticket/evals/evals.json +16 -0
- package/dist/agent-src/skills/release-comms/SKILL.md +2 -0
- package/dist/agent-src/skills/release-comms/evals/evals.json +16 -0
- package/dist/agent-src/skills/rice-prioritization/evals/evals.json +20 -0
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +22 -51
- package/dist/agent-src/skills/runway-cognition/evals/evals.json +20 -0
- package/dist/agent-src/skills/scenario-modeling/evals/evals.json +20 -0
- package/dist/agent-src/skills/security-audit/SKILL.md +6 -9
- package/dist/agent-src/skills/sentry-integration/SKILL.md +2 -2
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +8 -0
- package/dist/agent-src/skills/source-discovery/SKILL.md +26 -24
- package/dist/agent-src/skills/spreadsheet-authoring/SKILL.md +98 -0
- package/dist/agent-src/skills/spreadsheet-authoring/evals/triggers.json +47 -0
- package/dist/agent-src/skills/subagent-orchestration/SKILL.md +55 -46
- package/dist/agent-src/skills/subagent-orchestration/evals/evals.json +42 -0
- package/dist/agent-src/skills/systematic-debugging/SKILL.md +10 -0
- package/dist/agent-src/skills/systematic-debugging/evals/evals.json +20 -0
- package/dist/agent-src/skills/test-case-discovery/SKILL.md +118 -0
- package/dist/agent-src/skills/test-case-discovery/evals/triggers.json +52 -0
- package/dist/agent-src/skills/test-driven-development/SKILL.md +32 -6
- package/dist/agent-src/skills/test-driven-development/evals/evals.json +16 -0
- package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +7 -6
- package/dist/agent-src/skills/threat-modeling/evals/evals.json +21 -0
- package/dist/agent-src/skills/typography-system/evals/evals.json +14 -0
- package/dist/agent-src/skills/unit-economics-modeling/evals/evals.json +24 -0
- package/dist/agent-src/skills/using-git-worktrees/SKILL.md +10 -0
- package/dist/agent-src/skills/verify-completion-evidence/evals/evals.json +16 -0
- package/dist/agent-src/skills/vision-articulation/evals/evals.json +16 -0
- package/dist/agent-src/skills/voice-and-tone-design/evals/evals.json +16 -0
- package/dist/agent-src/skills/worktree-lifecycle/SKILL.md +187 -0
- package/dist/agent-src/skills/worktree-lifecycle/evals/evals.json +38 -0
- package/dist/agent-src/skills/worktree-lifecycle/evals/triggers.json +52 -0
- package/dist/agent-src/templates/agent-settings.md +3 -2
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +15 -1
- package/dist/agent-src/templates/roadmaps.md +14 -0
- package/dist/cli/commands/recordTriggerEval.js +4 -14
- package/dist/cli/commands/recordTriggerEval.js.map +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +374 -88
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +9 -9
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +30 -18
- package/dist/discovery/trust-report.md +5 -5
- package/dist/discovery/workspaces.json +31 -15
- package/dist/install/install.mjs +12 -7
- package/dist/mcp/registry-manifest.json +4 -4
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/claude_settings_hooks.js +8 -0
- package/dist/scripts/_lib/claude_settings_hooks.js.map +1 -1
- package/dist/scripts/_lib/trigger_eval_floors.js +24 -0
- package/dist/scripts/_lib/trigger_eval_floors.js.map +1 -0
- package/dist/scripts/condense.js +12 -2
- package/dist/scripts/condense.js.map +1 -1
- package/dist/server/schemas/settings.js +5 -2
- package/dist/server/schemas/settings.js.map +1 -1
- package/docs/CLAIMS.md +24 -3
- package/docs/architecture.md +3 -3
- package/docs/benchmark.md +51 -0
- package/docs/catalog.md +23 -9
- package/docs/command-flows.md +7 -1
- package/docs/contracts/CHANGELOG-conventions.md +6 -0
- package/docs/contracts/ai-council-config.md +92 -0
- package/docs/contracts/command-clusters.md +1 -0
- package/docs/contracts/design-artifact-lifecycle.md +93 -0
- package/docs/contracts/design-artifact-verification.md +30 -1
- package/docs/contracts/evidence-based-pruning.md +1 -0
- package/docs/contracts/hook-architecture-v1.md +25 -0
- package/docs/contracts/kernel-membership.md +35 -0
- package/docs/contracts/measurement-baseline.md +1 -0
- package/docs/contracts/release-pr-gating.md +26 -0
- package/docs/contracts/release-sizing.md +107 -0
- package/docs/contracts/surface-agent-contracts.md +153 -0
- package/docs/contracts/write-engine.md +38 -1
- package/docs/customization.md +12 -0
- package/docs/decisions/ADR-002-kernel-bucket-overrides.md +11 -0
- package/docs/decisions/ADR-003-flat-cluster-subs-and-colon-syntax.md +11 -0
- package/docs/decisions/ADR-006-skill-tools-python-pilot.md +1 -1
- package/docs/decisions/ADR-028-root-layout.md +1 -1
- package/docs/decisions/ADR-038-canonical-settings-path.md +10 -0
- package/docs/decisions/ADR-092-defer-command-tier-alias-removal.md +29 -5
- package/docs/decisions/ADR-093-ai-council-config-user-global.md +1 -1
- package/docs/decisions/ADR-100-global-knowledge-card-sharing.md +7 -0
- package/docs/decisions/ADR-103-global-knowledge-default-off-until-measured.md +2 -2
- package/docs/decisions/ADR-105-automatic-subagent-orchestration.md +1 -1
- package/docs/decisions/ADR-114-commit-policy-iron-law-override.md +11 -0
- package/docs/decisions/ADR-115-command-cluster-consolidation-phase-4.md +1 -1
- package/docs/decisions/ADR-116-memory-tripwire-activation-path.md +11 -1
- package/docs/decisions/ADR-117-subagents-auto-default-on.md +2 -1
- package/docs/decisions/ADR-118-loop-engineering-boundaries.md +162 -0
- package/docs/decisions/ADR-119-global-knowledge-default-on.md +104 -0
- package/docs/decisions/ADR-200-python-to-typescript-migration.md +11 -1
- package/docs/decisions/ADR-rule-kernel-and-router.md +11 -0
- package/docs/decisions/INDEX.md +11 -9
- package/docs/design/cross-model-parity-eval.md +82 -0
- package/docs/design/length-neutral-judge-rerun.md +92 -0
- package/docs/distribution/consumer-matrix.md +96 -0
- package/docs/featured-skills.md +2 -2
- package/docs/getting-started-by-role.md +1 -1
- package/docs/getting-started.md +2 -2
- package/docs/governance-advantage.md +1 -1
- package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +80 -5
- package/docs/guidelines/code-clarity.md +106 -1
- package/docs/guidelines/design-antipatterns.md +20 -0
- package/docs/guidelines/php/php-coding-patterns.md +12 -2
- package/docs/proof.md +21 -15
- package/docs/release-runbook.md +3 -0
- package/docs/self-review-gate.md +51 -0
- package/docs/wedge/production-validator/production-validator.md +27 -0
- package/package.json +1 -1
- package/src/config/agent-settings.template.yml +29 -9
- package/src/scripts/_cli/cmd_doctor.ts +34 -1
- package/src/scripts/_lib/auto_dispatch.corpus.test.ts +87 -0
- package/src/scripts/_lib/claude_settings_hooks.ts +8 -0
- package/src/scripts/_lib/knowledge_global.ts +6 -2
- package/src/scripts/_lib/knowledge_global_redaction.ts +42 -4
- package/src/scripts/_lib/lexical_index.ts +15 -2
- package/src/scripts/_lib/orchestration_record.ts +27 -0
- package/src/scripts/_lib/orchestration_savings.ts +52 -0
- package/src/scripts/_lib/trigger_eval_floors.ts +31 -0
- package/src/scripts/ai_council/chairman.ts +90 -0
- package/src/scripts/ai_council/clients.ts +18 -3
- package/src/scripts/ai_council/config.ts +157 -0
- package/src/scripts/ai_council/debate_gates.ts +89 -0
- package/src/scripts/ai_council/orchestrator.ts +65 -2
- package/src/scripts/ai_council/prompts.ts +121 -11
- package/src/scripts/ai_council/replay.ts +17 -0
- package/src/scripts/ai_council/stance_tally.ts +184 -0
- package/src/scripts/ai_tells_rules.ts +433 -0
- package/src/scripts/bench_humanizer_eval.ts +240 -0
- package/src/scripts/build_discovery_manifest.ts +6 -0
- package/src/scripts/build_proof.ts +21 -9
- package/src/scripts/check_claims.ts +40 -0
- package/src/scripts/check_release_adjacent_health.ts +121 -0
- package/src/scripts/check_rule_invariants.ts +282 -0
- package/src/scripts/condense.ts +16 -2
- package/src/scripts/consumer_matrix.ts +392 -0
- package/src/scripts/council_cli.ts +147 -0
- package/src/scripts/detect_ai_tells.ts +312 -0
- package/src/scripts/generate_index.ts +3 -2
- package/src/scripts/install.ts +12 -0
- package/src/scripts/knowledge_global_cli.ts +1 -1
- package/src/scripts/lint_changelog_rollback.ts +293 -0
- package/src/scripts/lint_framework_leakage.ts +88 -7
- package/src/scripts/lint_framework_leakage_allowlist.json +59 -572
- package/src/scripts/lint_knowledge_scale.ts +30 -0
- package/src/scripts/lint_originality_shingles.ts +210 -0
- package/src/scripts/orchestration_record.ts +17 -0
- package/src/scripts/orchestration_savings_report.ts +36 -3
- package/src/scripts/schemas/agent-settings.schema.json +11 -0
- package/src/scripts/second_brain_retrieval.ts +44 -2
- package/src/scripts/self_review_gate.test.ts +65 -0
- package/src/scripts/self_review_gate.ts +298 -0
- package/src/scripts/skill_eval_coverage.ts +71 -3
- package/src/scripts/skill_linter.ts +1 -0
- package/src/scripts/trigger_eval_grandfather.json +223 -224
- package/src/scripts/trigger_eval_rotation.ts +344 -0
- package/src/scripts/update_counts.ts +5 -17
- package/src/scripts/worktree_cleanup_check.ts +263 -0
- package/src/server/schemas/settings.ts +8 -3
- package/src/scripts/analysis_freshness.ts +0 -362
- package/src/scripts/export_replay_corpus.ts +0 -208
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-review gate — the package reviewing its own PRs with the exact machinery
|
|
3
|
+
* it sells (`adversarial-review` + `agent-security-review`).
|
|
4
|
+
*
|
|
5
|
+
* road-to-maintainer-bus-factor Phase 1. Ships ADVISORY + INERT-WITHOUT-SECRET:
|
|
6
|
+
* - `--dry-run` (the PR gate job): no API, no spend, no secret. Assembles and
|
|
7
|
+
* prints the review plan (which skills, which files, budget estimate) and
|
|
8
|
+
* validates the harness. Always exit 0.
|
|
9
|
+
* - live `--advisory` (the secret-gated job): loads the two review skill bodies
|
|
10
|
+
* as the system prompt, sends the diff to the model, collects structured
|
|
11
|
+
* findings, classifies them, records a verdict, and posts a PR review. In
|
|
12
|
+
* advisory mode it NEVER blocks the merge (exit 0) — it records what WOULD
|
|
13
|
+
* block. If no key is available it is a logged no-op (exit 0), never a failure.
|
|
14
|
+
*
|
|
15
|
+
* Turning the gate REQUIRED (block-on-verdict) and wiring the API secret + the
|
|
16
|
+
* per-PR budget are the maintainer's acts (blocker `self-review-gate-cost`); this
|
|
17
|
+
* script exposes the `--enforce` path so that flip is a one-flag change, but the
|
|
18
|
+
* shipped workflow runs advisory only.
|
|
19
|
+
*
|
|
20
|
+
* `classifyBlocking` + `gateVerdict` are pure and unit-tested (no API) — mirrors
|
|
21
|
+
* the `check_quality_regression.gateVerdict` pattern.
|
|
22
|
+
*/
|
|
23
|
+
import { spawnSync } from 'node:child_process';
|
|
24
|
+
import { appendFileSync, existsSync, readFileSync } from 'node:fs';
|
|
25
|
+
import path from 'node:path';
|
|
26
|
+
import { fileURLToPath } from 'node:url';
|
|
27
|
+
|
|
28
|
+
import { AnthropicClient, load_anthropic_key } from './ai_council/clients.js';
|
|
29
|
+
|
|
30
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
31
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
32
|
+
|
|
33
|
+
// ── Finding model ─────────────────────────────────────────────────────
|
|
34
|
+
export type Severity = 'critical' | 'high' | 'medium' | 'low';
|
|
35
|
+
export type FindingKind = 'security' | 'claim' | 'correctness' | 'style';
|
|
36
|
+
|
|
37
|
+
export interface Finding {
|
|
38
|
+
severity: Severity;
|
|
39
|
+
kind: FindingKind;
|
|
40
|
+
title: string;
|
|
41
|
+
detail: string;
|
|
42
|
+
file?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* A finding is merge-blocking iff it is security- or claim-affecting AND at
|
|
47
|
+
* least `high` severity. Style and correctness findings advise; low/medium
|
|
48
|
+
* security findings advise. Council 2026-07-08 (claude-sonnet-4-5 + gpt-4o):
|
|
49
|
+
* a 100 %-blocking gate at solo-maintainer token cost gets ignored or gamed —
|
|
50
|
+
* block ONLY on the narrow security/claim × {critical,high} intersection.
|
|
51
|
+
*/
|
|
52
|
+
export function classifyBlocking(f: Finding): boolean {
|
|
53
|
+
return (f.kind === 'security' || f.kind === 'claim') && (f.severity === 'critical' || f.severity === 'high');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Deterministic gate verdict. Mirrors `check_quality_regression.gateVerdict`:
|
|
58
|
+
* exit 2 blocks the merge, exit 0 passes.
|
|
59
|
+
* - advisory (default shipped mode): always 0; the caller reports the
|
|
60
|
+
* would-block set without failing the check.
|
|
61
|
+
* - enforce (maintainer flip): 2 iff any finding is merge-blocking.
|
|
62
|
+
*/
|
|
63
|
+
export function gateVerdict(findings: readonly Finding[], opts: { enforce: boolean }): 0 | 2 {
|
|
64
|
+
if (!opts.enforce) return 0;
|
|
65
|
+
return findings.some(classifyBlocking) ? 2 : 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ── Diff collection ───────────────────────────────────────────────────
|
|
69
|
+
/** Generated projections + lockfiles a self-review should not re-read. */
|
|
70
|
+
const SKIP_PREFIXES = ['dist/agent-src/', '.augment/', '.claude/', '.cursor/', '.clinerules/'];
|
|
71
|
+
const SKIP_EXACT = new Set(['.windsurfrules', 'GEMINI.md', 'package-lock.json']);
|
|
72
|
+
|
|
73
|
+
export function isReviewablePath(p: string): boolean {
|
|
74
|
+
if (SKIP_EXACT.has(p)) return false;
|
|
75
|
+
return !SKIP_PREFIXES.some((pre) => p.startsWith(pre));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function changedFiles(baseRef: string): string[] {
|
|
79
|
+
const r = spawnSync('git', ['diff', '--name-only', `${baseRef}...HEAD`], {
|
|
80
|
+
cwd: REPO_ROOT,
|
|
81
|
+
encoding: 'utf8',
|
|
82
|
+
});
|
|
83
|
+
if (r.status !== 0) {
|
|
84
|
+
throw new Error(`git diff failed: ${(r.stderr ?? '').toString().slice(0, 300)}`);
|
|
85
|
+
}
|
|
86
|
+
return (r.stdout ?? '')
|
|
87
|
+
.split('\n')
|
|
88
|
+
.map((s) => s.trim())
|
|
89
|
+
.filter(Boolean)
|
|
90
|
+
.filter(isReviewablePath);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function diffText(baseRef: string, files: string[]): string {
|
|
94
|
+
if (files.length === 0) return '';
|
|
95
|
+
const r = spawnSync('git', ['diff', `${baseRef}...HEAD`, '--', ...files], {
|
|
96
|
+
cwd: REPO_ROOT,
|
|
97
|
+
encoding: 'utf8',
|
|
98
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
99
|
+
});
|
|
100
|
+
return (r.stdout ?? '').toString();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ── Review skill bodies (the system prompt) ───────────────────────────
|
|
104
|
+
const REVIEW_SKILLS = ['adversarial-review', 'agent-security-review'] as const;
|
|
105
|
+
|
|
106
|
+
function loadSkillBody(name: string): string {
|
|
107
|
+
const p = path.join(REPO_ROOT, 'src', 'skills', name, 'SKILL.md');
|
|
108
|
+
return readFileSync(p, 'utf8');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function buildSystemPrompt(): string {
|
|
112
|
+
const bodies = REVIEW_SKILLS.map((s) => `# Skill: ${s}\n\n${loadSkillBody(s)}`).join('\n\n---\n\n');
|
|
113
|
+
return [
|
|
114
|
+
'You are the self-review gate for an AI-agent governance package. Apply the',
|
|
115
|
+
'two skills below to the supplied PR diff. Return ONLY a JSON object of the',
|
|
116
|
+
'shape {"findings":[{"severity":"critical|high|medium|low",',
|
|
117
|
+
'"kind":"security|claim|correctness|style","title":"...","detail":"...",',
|
|
118
|
+
'"file":"optional/path"}]}. Use kind="claim" for an unbacked headline number',
|
|
119
|
+
'or a proof/CLAIMS-affecting statement, kind="security" for an auth/tenant/',
|
|
120
|
+
'secret/egress/injection gap. No prose outside the JSON.',
|
|
121
|
+
'',
|
|
122
|
+
bodies,
|
|
123
|
+
].join('\n');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ── Plan (dry-run) ────────────────────────────────────────────────────
|
|
127
|
+
export interface ReviewPlan {
|
|
128
|
+
skills: string[];
|
|
129
|
+
files: string[];
|
|
130
|
+
promptChars: number;
|
|
131
|
+
note: string;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function buildPlan(baseRef: string): ReviewPlan {
|
|
135
|
+
const files = changedFiles(baseRef);
|
|
136
|
+
const diff = diffText(baseRef, files);
|
|
137
|
+
return {
|
|
138
|
+
skills: [...REVIEW_SKILLS],
|
|
139
|
+
files,
|
|
140
|
+
promptChars: buildSystemPrompt().length + diff.length,
|
|
141
|
+
note:
|
|
142
|
+
files.length === 0
|
|
143
|
+
? 'No reviewable (non-generated) files changed — the live review would no-op.'
|
|
144
|
+
: `${files.length} reviewable file(s); live review would send ~${Math.ceil(
|
|
145
|
+
(buildSystemPrompt().length + diff.length) / 4,
|
|
146
|
+
)} input tokens.`,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ── Live review ───────────────────────────────────────────────────────
|
|
151
|
+
function parseFindings(text: string): Finding[] {
|
|
152
|
+
const start = text.indexOf('{');
|
|
153
|
+
const end = text.lastIndexOf('}');
|
|
154
|
+
if (start < 0 || end <= start) return [];
|
|
155
|
+
let parsed: unknown;
|
|
156
|
+
try {
|
|
157
|
+
parsed = JSON.parse(text.slice(start, end + 1));
|
|
158
|
+
} catch {
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
const arr = (parsed as { findings?: unknown })?.findings;
|
|
162
|
+
if (!Array.isArray(arr)) return [];
|
|
163
|
+
const sev = new Set<Severity>(['critical', 'high', 'medium', 'low']);
|
|
164
|
+
const kind = new Set<FindingKind>(['security', 'claim', 'correctness', 'style']);
|
|
165
|
+
return arr
|
|
166
|
+
.filter((f): f is Record<string, unknown> => typeof f === 'object' && f !== null)
|
|
167
|
+
.filter((f) => sev.has(f.severity as Severity) && kind.has(f.kind as FindingKind))
|
|
168
|
+
.map((f) => ({
|
|
169
|
+
severity: f.severity as Severity,
|
|
170
|
+
kind: f.kind as FindingKind,
|
|
171
|
+
title: String(f.title ?? '').slice(0, 300),
|
|
172
|
+
detail: String(f.detail ?? '').slice(0, 2000),
|
|
173
|
+
...(typeof f.file === 'string' ? { file: f.file } : {}),
|
|
174
|
+
}));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function renderReview(findings: Finding[], enforce: boolean): string {
|
|
178
|
+
const banner =
|
|
179
|
+
'> HUMAN REVIEW REQUIRED — dogfooded AI adversarial-review + security gate. ' +
|
|
180
|
+
'Findings are decision support, not a guarantee; detection is probabilistic. ' +
|
|
181
|
+
'This is a floor, **not** independent human review.';
|
|
182
|
+
if (findings.length === 0) {
|
|
183
|
+
return `${banner}\n\n✅ Self-review gate: no findings.`;
|
|
184
|
+
}
|
|
185
|
+
const blocking = findings.filter(classifyBlocking);
|
|
186
|
+
const rows = findings
|
|
187
|
+
.map((f) => `| ${f.severity} | ${f.kind} | ${f.file ?? '—'} | ${f.title} |`)
|
|
188
|
+
.join('\n');
|
|
189
|
+
const verdictLine = blocking.length
|
|
190
|
+
? enforce
|
|
191
|
+
? `❌ ${blocking.length} merge-blocking finding(s) (security/claim × high+).`
|
|
192
|
+
: `⚠️ ${blocking.length} finding(s) WOULD block merge under an enforced gate (advisory now).`
|
|
193
|
+
: '✅ No merge-blocking findings (style/correctness advise only).';
|
|
194
|
+
return `${banner}\n\n${verdictLine}\n\n| severity | kind | file | finding |\n|---|---|---|---|\n${rows}`;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function postReview(body: string): void {
|
|
198
|
+
const pr = process.env.PR_NUMBER;
|
|
199
|
+
if (!pr) {
|
|
200
|
+
process.stdout.write('::notice::self-review-gate — no PR_NUMBER; printing review instead of posting.\n');
|
|
201
|
+
process.stdout.write(body + '\n');
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const r = spawnSync('gh', ['pr', 'comment', pr, '--body', body], { cwd: REPO_ROOT, encoding: 'utf8' });
|
|
205
|
+
if (r.status !== 0) {
|
|
206
|
+
process.stdout.write(`::warning::self-review-gate — gh pr comment failed: ${(r.stderr ?? '').toString().slice(0, 300)}\n`);
|
|
207
|
+
process.stdout.write(body + '\n');
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function resolveKey(): string | null {
|
|
212
|
+
const env = process.env.ANTHROPIC_API_KEY;
|
|
213
|
+
if (env && env.trim()) return env.trim();
|
|
214
|
+
try {
|
|
215
|
+
return load_anthropic_key();
|
|
216
|
+
} catch {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ── CLI ───────────────────────────────────────────────────────────────
|
|
222
|
+
export function main(argv: string[]): 0 | 2 {
|
|
223
|
+
const dryRun = argv.includes('--dry-run');
|
|
224
|
+
const enforce = argv.includes('--enforce');
|
|
225
|
+
const baseIdx = argv.indexOf('--base');
|
|
226
|
+
const baseRef = (baseIdx >= 0 ? argv[baseIdx + 1] : undefined) ?? 'origin/main';
|
|
227
|
+
|
|
228
|
+
const plan = buildPlan(baseRef);
|
|
229
|
+
|
|
230
|
+
if (dryRun) {
|
|
231
|
+
process.stdout.write(
|
|
232
|
+
`self-review-gate (dry-run — no spend, advisory):\n` +
|
|
233
|
+
` skills: ${plan.skills.join(', ')}\n` +
|
|
234
|
+
` files: ${plan.files.length}\n` +
|
|
235
|
+
` ${plan.note}\n`,
|
|
236
|
+
);
|
|
237
|
+
return 0;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (plan.files.length === 0) {
|
|
241
|
+
process.stdout.write('::notice::self-review-gate — no reviewable files changed; skipping.\n');
|
|
242
|
+
return 0;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const key = resolveKey();
|
|
246
|
+
if (!key) {
|
|
247
|
+
// Explicit NEUTRAL state — never a bare green that reads as "reviewed".
|
|
248
|
+
process.stdout.write(
|
|
249
|
+
'::warning::self-review-gate NEUTRAL — no ANTHROPIC_API_KEY configured, NOTHING was reviewed. ' +
|
|
250
|
+
'Set the repo secret to enable the live dogfooded review.\n',
|
|
251
|
+
);
|
|
252
|
+
const summary = process.env.GITHUB_STEP_SUMMARY;
|
|
253
|
+
if (summary) {
|
|
254
|
+
appendFileSync(
|
|
255
|
+
summary,
|
|
256
|
+
'### Self-review gate: NEUTRAL\n\nNo `ANTHROPIC_API_KEY` secret — nothing was reviewed. This is not a pass.\n',
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
return 0;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Belt-and-suspenders: the WHOLE live path is wrapped so that ANYTHING going
|
|
263
|
+
// wrong — no API credit / balance (HTTP 402), rate-limit (429), network error,
|
|
264
|
+
// a client-ctor throw, a parse failure — resolves to a NEUTRAL exit 0, never a
|
|
265
|
+
// red CI. A missing key or no-credit must never become a merge blocker; the
|
|
266
|
+
// gate is advisory by design. (Only an --enforce run with real blocking
|
|
267
|
+
// findings returns 2 — an error is not a finding.)
|
|
268
|
+
try {
|
|
269
|
+
const client = new AnthropicClient({ api_key: key });
|
|
270
|
+
const resp = client.ask(buildSystemPrompt(), diffText(baseRef, plan.files), 4096);
|
|
271
|
+
if (resp.error) {
|
|
272
|
+
process.stdout.write(
|
|
273
|
+
`::warning::self-review-gate NEUTRAL — model call did not complete (${resp.error}); ` +
|
|
274
|
+
'nothing was reviewed, not a blocker.\n',
|
|
275
|
+
);
|
|
276
|
+
return 0; // no credit / transport / API error → never blocks the merge
|
|
277
|
+
}
|
|
278
|
+
const findings = parseFindings(resp.text);
|
|
279
|
+
const body = renderReview(findings, enforce);
|
|
280
|
+
postReview(body);
|
|
281
|
+
|
|
282
|
+
const code = gateVerdict(findings, { enforce });
|
|
283
|
+
if (code === 2) {
|
|
284
|
+
process.stdout.write('::error::self-review-gate — merge-blocking findings under enforce mode.\n');
|
|
285
|
+
}
|
|
286
|
+
return code;
|
|
287
|
+
} catch (e) {
|
|
288
|
+
process.stdout.write(
|
|
289
|
+
`::warning::self-review-gate NEUTRAL — unexpected error (${e instanceof Error ? e.message : String(e)}); ` +
|
|
290
|
+
'nothing was reviewed, not a blocker.\n',
|
|
291
|
+
);
|
|
292
|
+
return 0;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (existsSync(process.argv[1] ?? '') && import.meta.url === `file://${process.argv[1]}`) {
|
|
297
|
+
process.exit(main(process.argv.slice(2)));
|
|
298
|
+
}
|
|
@@ -26,6 +26,12 @@
|
|
|
26
26
|
* drops below the pinned floor (internal/evals/coverage-floor.json).
|
|
27
27
|
* A missing floor file is treated as an all-zero floor
|
|
28
28
|
* (bootstrap-inert: never fails before the first --write-floor).
|
|
29
|
+
* ALSO enforces the tier floor (road-to-fable-feedback-5
|
|
30
|
+
* Phase 4a, council 2026-07-10): every `priority`-tier skill
|
|
31
|
+
* (rich + default-surface + router) MUST carry a behavioural
|
|
32
|
+
* evals.json, unless exempted WITH a reason in
|
|
33
|
+
* internal/evals/tier-floor-exemptions.json. No silent
|
|
34
|
+
* exemptions — the bright line replaces weighted scoring.
|
|
29
35
|
* --write-floor pin the current coverage as the new floor. Maintainer action
|
|
30
36
|
* after authoring evals — the ratchet only ever rises.
|
|
31
37
|
*
|
|
@@ -47,6 +53,7 @@ export interface CoverageOptions {
|
|
|
47
53
|
skillsDir?: string;
|
|
48
54
|
profilesDir?: string;
|
|
49
55
|
floorPath?: string;
|
|
56
|
+
exemptionsPath?: string;
|
|
50
57
|
}
|
|
51
58
|
|
|
52
59
|
export interface TierCoverage {
|
|
@@ -222,6 +229,52 @@ export function writeFloor(opts: CoverageOptions = {}): CoverageReport {
|
|
|
222
229
|
return report;
|
|
223
230
|
}
|
|
224
231
|
|
|
232
|
+
export function _exemptionsPath(opts: CoverageOptions = {}): string {
|
|
233
|
+
return (
|
|
234
|
+
opts.exemptionsPath ??
|
|
235
|
+
path.join(REPO_ROOT, 'internal', 'evals', 'tier-floor-exemptions.json')
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function _readExemptions(p: string): Record<string, string> {
|
|
240
|
+
try {
|
|
241
|
+
const raw = JSON.parse(fs.readFileSync(p, 'utf-8')) as {
|
|
242
|
+
exemptions?: Record<string, unknown>;
|
|
243
|
+
};
|
|
244
|
+
const out: Record<string, string> = {};
|
|
245
|
+
for (const [k, v] of Object.entries(raw.exemptions ?? {})) {
|
|
246
|
+
if (typeof v === 'string' && v.trim().length > 0) out[k] = v;
|
|
247
|
+
}
|
|
248
|
+
return out;
|
|
249
|
+
} catch {
|
|
250
|
+
return {};
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Tier floor: every priority-tier skill (rich + default-surface + router)
|
|
256
|
+
* must carry evals.json, unless exempted with a reason. Stale exemptions
|
|
257
|
+
* (exempted but now covered, or no longer priority) are reported so the
|
|
258
|
+
* list cannot silently accumulate.
|
|
259
|
+
*/
|
|
260
|
+
export function checkTierFloor(opts: CoverageOptions = {}): {
|
|
261
|
+
ok: boolean;
|
|
262
|
+
violations: string[];
|
|
263
|
+
stale: string[];
|
|
264
|
+
} {
|
|
265
|
+
const report = computeCoverage(opts);
|
|
266
|
+
const exemptions = _readExemptions(_exemptionsPath(opts));
|
|
267
|
+
const uncovered = report.tiers.priority.uncovered;
|
|
268
|
+
const violations = uncovered
|
|
269
|
+
.filter((n) => !(n in exemptions))
|
|
270
|
+
.map((n) => `priority-tier skill '${n}' has no evals/evals.json and no exemption`);
|
|
271
|
+
const uncoveredSet = new Set(uncovered);
|
|
272
|
+
const stale = Object.keys(exemptions)
|
|
273
|
+
.filter((n) => !uncoveredSet.has(n))
|
|
274
|
+
.map((n) => `exemption for '${n}' is stale (covered or no longer priority-tier)`);
|
|
275
|
+
return { ok: violations.length === 0, violations, stale };
|
|
276
|
+
}
|
|
277
|
+
|
|
225
278
|
/** Ratchet: current coverage must be >= floor, overall and per tier. */
|
|
226
279
|
export function checkRatchet(opts: CoverageOptions = {}): { ok: boolean; regressions: string[] } {
|
|
227
280
|
const report = computeCoverage(opts);
|
|
@@ -281,12 +334,27 @@ export function main(argv: readonly string[] = process.argv.slice(2)): number {
|
|
|
281
334
|
|
|
282
335
|
if (argv.includes('--check')) {
|
|
283
336
|
const { ok, regressions } = checkRatchet();
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
337
|
+
const floor = checkTierFloor();
|
|
338
|
+
if (!ok || !floor.ok) {
|
|
339
|
+
if (!ok) {
|
|
340
|
+
process.stdout.write('❌ eval-coverage ratchet regressed:\n');
|
|
341
|
+
for (const r of regressions) process.stdout.write(` - ${r}\n`);
|
|
342
|
+
}
|
|
343
|
+
if (!floor.ok) {
|
|
344
|
+
process.stdout.write('❌ priority-tier eval floor violated:\n');
|
|
345
|
+
for (const v of floor.violations) process.stdout.write(` - ${v}\n`);
|
|
346
|
+
process.stdout.write(
|
|
347
|
+
' Author a behavioural evals.json, or add an exemption WITH a reason\n' +
|
|
348
|
+
' to internal/evals/tier-floor-exemptions.json.\n',
|
|
349
|
+
);
|
|
350
|
+
}
|
|
287
351
|
return 1;
|
|
288
352
|
}
|
|
353
|
+
for (const s of floor.stale) {
|
|
354
|
+
process.stdout.write(`⚠️ ${s}\n`);
|
|
355
|
+
}
|
|
289
356
|
process.stdout.write('✅ eval-coverage ratchet: no regression.\n');
|
|
357
|
+
process.stdout.write('✅ priority-tier eval floor: every rich/default-surface/router skill covered or exempted-with-reason.\n');
|
|
290
358
|
return 0;
|
|
291
359
|
}
|
|
292
360
|
|