@event4u/agent-config 7.5.0 → 8.0.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 +8 -1
- package/AGENTS.md +9 -9
- package/CHANGELOG.md +325 -0
- package/README.md +35 -5
- package/dist/agent-src/commands/analytics/prune.md +1 -1
- package/dist/agent-src/commands/analytics/show.md +1 -1
- package/dist/agent-src/commands/analyze/decision.md +2 -2
- package/dist/agent-src/commands/analyze/incident.md +2 -2
- package/dist/agent-src/commands/analyze/near-miss.md +2 -2
- package/dist/agent-src/commands/analyze/postmortem.md +2 -2
- package/dist/agent-src/commands/analyze/premortem.md +2 -2
- package/dist/agent-src/commands/cost-report.md +25 -0
- package/dist/agent-src/commands/evals/analyze.json +30 -0
- package/dist/agent-src/commands/evals/mission-upgrade.json +30 -0
- package/dist/agent-src/commands/implement-ticket.md +1 -1
- package/dist/agent-src/commands/knowledge/forget.md +1 -1
- package/dist/agent-src/commands/knowledge/ingest.md +1 -1
- package/dist/agent-src/commands/knowledge/list.md +1 -1
- package/dist/agent-src/commands/memory/load.md +12 -11
- package/dist/agent-src/commands/memory/promote.md +30 -0
- package/dist/agent-src/commands/memory/propose.md +16 -2
- package/dist/agent-src/commands/mission/upgrade.md +1 -1
- package/dist/agent-src/commands/module/explore.md +11 -7
- package/dist/agent-src/commands/optimize-project.md +243 -0
- package/dist/agent-src/commands/optimize.md +5 -0
- package/dist/agent-src/commands/pr/create.md +37 -8
- package/dist/agent-src/commands/prediction-pool.md +2 -2
- package/dist/agent-src/commands/profile/activate.md +3 -3
- package/dist/agent-src/commands/profile/deactivate.md +2 -2
- package/dist/agent-src/commands/profile/show.md +3 -3
- package/dist/agent-src/commands/research/deep.md +5 -0
- package/dist/agent-src/commands/research/report.md +2 -1
- package/dist/agent-src/commands/roadmap/create.md +88 -10
- package/dist/agent-src/commands/roadmap/materialize.md +1 -1
- package/dist/agent-src/commands/roadmap/process-full.md +15 -2
- package/dist/agent-src/commands/roadmap/process-phase.md +18 -6
- package/dist/agent-src/commands/roadmap/process-step.md +4 -0
- package/dist/agent-src/commands/roadmap.md +1 -1
- package/dist/agent-src/commands/security-audit-config.md +1 -1
- package/dist/agent-src/commands/sync-agent-settings.md +1 -1
- package/dist/agent-src/commands/sync-gitignore/fix.md +42 -6
- package/dist/agent-src/commands/team-knowledge/bootstrap.md +106 -0
- package/dist/agent-src/commands/team-knowledge/consolidate.md +105 -0
- package/dist/agent-src/commands/team-knowledge.md +64 -0
- package/dist/agent-src/contexts/authority/commit-mechanics.md +8 -0
- package/dist/agent-src/contexts/authority/destructive-mechanics.md +11 -0
- package/dist/agent-src/contexts/authority/scope-mechanics.md +25 -0
- package/dist/agent-src/contexts/communication/rules-auto/reply-close-mechanics.md +43 -0
- package/dist/agent-src/contexts/contracts/agents-md-anatomy.md +1 -1
- package/dist/agent-src/contexts/contracts/artifact-engagement-flow.md +2 -2
- package/dist/agent-src/contexts/contracts/frugality-charter.md +1 -0
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +14 -4
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +11 -0
- package/dist/agent-src/contexts/execution/orchestration-telemetry.md +32 -8
- package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +107 -0
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +71 -15
- package/dist/agent-src/contexts/execution/subagent-steering.md +41 -1
- package/dist/agent-src/contexts/override-system.md +1 -1
- package/dist/agent-src/personas/brand-strategist.md +0 -1
- package/dist/agent-src/personas/data-integrity.md +66 -0
- package/dist/agent-src/personas/design-director.md +0 -1
- package/dist/agent-src/personas/performance-engineer.md +66 -0
- package/dist/agent-src/personas/production-validator.md +73 -0
- package/dist/agent-src/rules/agent-authority.md +3 -4
- package/dist/agent-src/rules/analysis-skill-routing.md +3 -5
- package/dist/agent-src/rules/architecture.md +2 -4
- package/dist/agent-src/rules/artifact-drafting-protocol.md +35 -6
- package/dist/agent-src/rules/artifact-engagement-recording.md +2 -4
- package/dist/agent-src/rules/ask-when-uncertain.md +10 -14
- package/dist/agent-src/rules/augment-edit-discipline.md +3 -5
- package/dist/agent-src/rules/autonomous-execution.md +3 -5
- package/dist/agent-src/rules/brand-consistency.md +3 -5
- package/dist/agent-src/rules/brand-source-of-truth.md +3 -5
- package/dist/agent-src/rules/cli-output-handling.md +4 -6
- package/dist/agent-src/rules/command-suggestion-policy.md +3 -5
- package/dist/agent-src/rules/commit-conventions.md +3 -5
- package/dist/agent-src/rules/commit-policy.md +8 -10
- package/dist/agent-src/rules/content-quoting-floor.md +88 -0
- package/dist/agent-src/rules/context-hygiene.md +3 -5
- package/dist/agent-src/rules/copilot-routing.md +4 -6
- package/dist/agent-src/rules/decision-revisit-gate.md +120 -0
- package/dist/agent-src/rules/delegation-policy.md +10 -6
- package/dist/agent-src/rules/design-fidelity.md +3 -5
- package/dist/agent-src/rules/devcontainer-routing.md +4 -6
- package/dist/agent-src/rules/direct-answers.md +13 -17
- package/dist/agent-src/rules/docker-commands.md +3 -5
- package/dist/agent-src/rules/domain-adoption-policy.md +3 -5
- package/dist/agent-src/rules/domain-safety-disclaimer.md +3 -5
- package/dist/agent-src/rules/domain-safety-pii.md +3 -5
- package/dist/agent-src/rules/domain-safety-retention.md +3 -5
- package/dist/agent-src/rules/downstream-changes.md +3 -4
- package/dist/agent-src/rules/engineering-safety-floor.md +3 -5
- package/dist/agent-src/rules/external-reference-deep-dive.md +3 -5
- package/dist/agent-src/rules/fast-path-marker-visibility.md +3 -5
- package/dist/agent-src/rules/finance-safety-floor.md +3 -6
- package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +3 -5
- package/dist/agent-src/rules/git-history-discipline.md +3 -5
- package/dist/agent-src/rules/guidelines.md +2 -4
- package/dist/agent-src/rules/icon-consistency.md +3 -5
- package/dist/agent-src/rules/image-likeness-and-rights.md +3 -5
- package/dist/agent-src/rules/improve-before-implement.md +5 -5
- package/dist/agent-src/rules/invite-challenge.md +3 -5
- package/dist/agent-src/rules/language-and-tone.md +13 -15
- package/dist/agent-src/rules/laravel-routing.md +3 -5
- package/dist/agent-src/rules/laravel-translations.md +3 -5
- package/dist/agent-src/rules/legal-safety-floor.md +3 -5
- package/dist/agent-src/rules/lethal-trifecta-guard.md +3 -5
- package/dist/agent-src/rules/linked-projects-onboarding-gate.md +4 -7
- package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +3 -5
- package/dist/agent-src/rules/markdown-safe-codeblocks.md +2 -4
- package/dist/agent-src/rules/media-governance-routing.md +3 -5
- package/dist/agent-src/rules/media-sync-ground-truth.md +3 -5
- package/dist/agent-src/rules/minimal-safe-diff.md +5 -5
- package/dist/agent-src/rules/missing-tool-handling.md +3 -5
- package/dist/agent-src/rules/model-recommendation.md +4 -6
- package/dist/agent-src/rules/no-attribution-footers.md +3 -5
- package/dist/agent-src/rules/no-cheap-questions.md +5 -8
- package/dist/agent-src/rules/no-decorative-emojis-in-git-surfaces.md +3 -6
- package/dist/agent-src/rules/no-pr-progress-comments.md +3 -6
- package/dist/agent-src/rules/no-roadmap-references.md +3 -5
- package/dist/agent-src/rules/non-destructive-by-default.md +11 -13
- package/dist/agent-src/rules/notes-first-reasoning.md +3 -5
- package/dist/agent-src/rules/onboarding-gate.md +4 -6
- package/dist/agent-src/rules/output-discipline.md +3 -5
- package/dist/agent-src/rules/package-ci-checks.md +3 -5
- package/dist/agent-src/rules/persona-governance.md +2 -4
- package/dist/agent-src/rules/php-coding.md +3 -5
- package/dist/agent-src/rules/preservation-guard.md +2 -4
- package/dist/agent-src/rules/provider-lifecycle-discipline.md +3 -5
- package/dist/agent-src/rules/reviewer-awareness.md +5 -6
- package/dist/agent-src/rules/roadmap-ci-steps-policy.md +3 -5
- package/dist/agent-src/rules/roadmap-progress-sync.md +5 -5
- package/dist/agent-src/rules/role-mode-adherence.md +2 -4
- package/dist/agent-src/rules/rule-type-governance.md +3 -5
- package/dist/agent-src/rules/runtime-safety.md +2 -4
- package/dist/agent-src/rules/scope-control.md +10 -11
- package/dist/agent-src/rules/security-sensitive-stop.md +4 -5
- package/dist/agent-src/rules/size-enforcement.md +2 -4
- package/dist/agent-src/rules/skill-improvement-trigger.md +3 -5
- package/dist/agent-src/rules/skill-quality.md +3 -5
- package/dist/agent-src/rules/slash-command-routing-policy.md +3 -5
- package/dist/agent-src/rules/source-confidentiality.md +3 -5
- package/dist/agent-src/rules/source-discovery-gate.md +3 -5
- package/dist/agent-src/rules/source-of-truth.md +3 -5
- package/dist/agent-src/rules/strategy-safety-floor.md +3 -5
- package/dist/agent-src/rules/symfony-routing.md +4 -6
- package/dist/agent-src/rules/telegraph-speak.md +4 -5
- package/dist/agent-src/rules/think-before-action.md +7 -4
- package/dist/agent-src/rules/token-budget-discipline.md +29 -5
- package/dist/agent-src/rules/token-efficiency.md +3 -5
- package/dist/agent-src/rules/token-optimizer-maintenance.md +3 -5
- package/dist/agent-src/rules/tool-safety.md +2 -4
- package/dist/agent-src/rules/ui-audit-gate.md +3 -6
- package/dist/agent-src/rules/untrusted-input-defense.md +3 -5
- package/dist/agent-src/rules/upstream-proposal.md +4 -6
- package/dist/agent-src/rules/user-interaction.md +7 -5
- package/dist/agent-src/rules/user-interrupt-priority.md +17 -8
- package/dist/agent-src/rules/verify-before-complete.md +12 -22
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +3 -3
- package/dist/agent-src/scripts/update_roadmap_progress.ts +197 -7
- package/dist/agent-src/skills/adr-create/SKILL.md +48 -42
- package/dist/agent-src/skills/agent-docs-writing/SKILL.md +18 -0
- package/dist/agent-src/skills/agent-security-review/SKILL.md +1 -1
- package/dist/agent-src/skills/ai-council/SKILL.md +14 -1
- package/dist/agent-src/skills/blameless-post-mortem/SKILL.md +0 -3
- package/dist/agent-src/skills/brand/SKILL.md +1 -1
- package/dist/agent-src/skills/brand-identity/SKILL.md +3 -3
- package/dist/agent-src/skills/brand-strategy/SKILL.md +1 -1
- package/dist/agent-src/skills/brand-to-tokens/SKILL.md +3 -3
- package/dist/agent-src/skills/canvas-design/SKILL.md +6 -0
- package/dist/agent-src/skills/check-refs/SKILL.md +7 -4
- package/dist/agent-src/skills/code-review/SKILL.md +1 -0
- package/dist/agent-src/skills/complexity-first-planning/SKILL.md +6 -5
- package/dist/agent-src/skills/condense-memory/SKILL.md +11 -11
- package/dist/agent-src/skills/context-document/SKILL.md +17 -0
- package/dist/agent-src/skills/corpus-grounding/SKILL.md +3 -3
- package/dist/agent-src/skills/decision-record/SKILL.md +6 -0
- package/dist/agent-src/skills/decision-review/SKILL.md +5 -3
- package/dist/agent-src/skills/deep-reading-analyst/SKILL.md +6 -1
- package/dist/agent-src/skills/design-intelligence/SKILL.md +5 -2
- package/dist/agent-src/skills/design-intelligence/references/design-languages.md +1 -1
- package/dist/agent-src/skills/design-tokens/SKILL.md +10 -4
- package/dist/agent-src/skills/design-variations/SKILL.md +127 -0
- package/dist/agent-src/skills/design-variations/evals/triggers.json +17 -0
- package/dist/agent-src/skills/emit-tickets/SKILL.md +4 -4
- package/dist/agent-src/skills/fe-design/SKILL.md +20 -0
- package/dist/agent-src/skills/html-deck/SKILL.md +123 -0
- package/dist/agent-src/skills/html-deck/evals/triggers.json +17 -0
- package/dist/agent-src/skills/html-deck/templates/deck-shell.html +77 -0
- package/dist/agent-src/skills/judge-code-quality/SKILL.md +1 -0
- package/dist/agent-src/skills/laravel/SKILL.md +0 -1
- package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +46 -0
- package/dist/agent-src/skills/lint-skills/SKILL.md +6 -6
- package/dist/agent-src/skills/md-language-check/SKILL.md +3 -3
- package/dist/agent-src/skills/memory-consolidation/SKILL.md +66 -1
- package/dist/agent-src/skills/migration-architect/SKILL.md +1 -0
- package/dist/agent-src/skills/module-detect-on-the-fly/SKILL.md +2 -2
- package/dist/agent-src/skills/performance-analysis/SKILL.md +2 -0
- package/dist/agent-src/skills/prediction-pool-optimizer/SKILL.md +4 -4
- package/dist/agent-src/skills/premortem/SKILL.md +0 -3
- package/dist/agent-src/skills/project-analysis-core/SKILL.md +16 -0
- package/dist/agent-src/skills/reasoning-orchestrator/SKILL.md +3 -1
- package/dist/agent-src/skills/release-comms/SKILL.md +46 -45
- package/dist/agent-src/skills/roadmap-management/SKILL.md +21 -0
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +32 -2
- package/dist/agent-src/skills/root-cause-frameworks/SKILL.md +0 -3
- package/dist/agent-src/skills/security-audit/SKILL.md +1 -0
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +4 -0
- package/dist/agent-src/skills/source-discovery/SKILL.md +22 -3
- package/dist/agent-src/skills/subagent-orchestration/SKILL.md +33 -14
- package/dist/agent-src/skills/subagent-orchestration/prompts/README.md +11 -0
- package/dist/agent-src/skills/subagent-orchestration/prompts/do-in-steps.md +8 -0
- package/dist/agent-src/skills/systematic-debugging/SKILL.md +29 -0
- package/dist/agent-src/skills/threat-modeling/SKILL.md +2 -2
- package/dist/agent-src/skills/typography-system/SKILL.md +2 -2
- package/dist/agent-src/skills/verify-completion-evidence/SKILL.md +2 -0
- package/dist/agent-src/skills/wireframe/SKILL.md +118 -0
- package/dist/agent-src/skills/wireframe/evals/triggers.json +17 -0
- package/dist/agent-src/templates/agent-settings.md +22 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/command.md +2 -2
- package/dist/agent-src/templates/contexts/knowledge-pages.md +116 -0
- package/dist/agent-src/templates/hooks/pre-commit-frontmatter +1 -1
- package/dist/agent-src/templates/roadmaps.md +70 -2
- package/dist/agent-src/templates/rule.md +1 -1
- package/dist/agent-src/templates/scripts/README.md +3 -3
- package/dist/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +109 -0
- package/dist/agent-src/templates/tickets.md +8 -8
- package/dist/cli/commands/recordTriggerEval.test.js +1 -1
- package/dist/cli/commands/recordTriggerEval.test.js.map +1 -1
- package/dist/cli/initRouting.js +4 -2
- package/dist/cli/initRouting.js.map +1 -1
- package/dist/cli/registry.js +1 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +718 -240
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +8 -8
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +43 -28
- package/dist/discovery/trust-report.md +12 -11
- package/dist/discovery/workspaces.json +29 -18
- package/dist/install/preflight.js +173 -0
- package/dist/install/preflight.js.map +1 -0
- package/dist/mcp/registry-manifest.json +4 -4
- package/dist/router.json +1 -1
- package/dist/server/routes/settings.js +4 -0
- package/dist/server/routes/settings.js.map +1 -1
- package/dist/server/schemas/settings.js +3 -1
- package/dist/server/schemas/settings.js.map +1 -1
- package/docs/CLAIMS.md +108 -0
- package/docs/MIGRATION.md +3 -3
- package/docs/architecture.md +5 -5
- package/docs/benchmark.md +133 -2
- package/docs/benchmark.pinned.yml +31 -0
- package/docs/capability-matrix.md +1 -0
- package/docs/catalog.md +70 -60
- package/docs/command-flows.md +15 -3
- package/docs/comparison.yaml +35 -0
- package/docs/contracts/agents-layout.md +148 -0
- package/docs/contracts/brand-token-consumption.md +5 -0
- package/docs/contracts/capability-boundary.md +5 -0
- package/docs/contracts/command-category-governance.md +5 -0
- package/docs/contracts/command-clusters.md +1 -0
- package/docs/contracts/command-surface-tiers.md +1 -1
- package/docs/contracts/conformance.md +84 -0
- package/docs/contracts/creative-pack-boundary.md +5 -0
- package/docs/contracts/discovery-manifest.schema.json +5 -4
- package/docs/contracts/file-ownership-matrix.json +4605 -985
- package/docs/contracts/governance-enforcement-projection.md +5 -0
- package/docs/contracts/gui-wizard.md +1 -1
- package/docs/contracts/hook-architecture-v1.md +2 -2
- package/docs/contracts/implement-ticket-flow.md +1 -1
- package/docs/contracts/kernel-membership.md +13 -0
- package/docs/contracts/mcp-tool-inventory.md +23 -23
- package/docs/contracts/no-runtime-boundary.md +6 -1
- package/docs/contracts/review-lens-schema.md +5 -0
- package/docs/contracts/rule-router.md +102 -8
- package/docs/contracts/skill-bundled-assets.md +1 -1
- package/docs/contracts/skill-dry-run.md +1 -1
- package/docs/contracts/subagent-boundary.md +5 -0
- package/docs/contracts/ticket-bundle-format.md +9 -4
- package/docs/contracts/tier-3-contrib-plugin.md +2 -2
- package/docs/contracts/write-engine.md +3 -0
- package/docs/customization.md +87 -15
- package/docs/decisions/ADR-107-legal-domain-pack-adoption.md +1 -1
- package/docs/decisions/ADR-109-subagent-v1-contract.md +246 -0
- package/docs/decisions/ADR-110-discipline-profile-resolution-locus.md +105 -0
- package/docs/decisions/INDEX.md +2 -0
- package/docs/distribution/awesome-list-submission.md +46 -0
- package/docs/distribution/launch-story-draft.md +58 -0
- package/docs/end-to-end-walkthroughs.md +2 -2
- package/docs/getting-started.md +2 -2
- package/docs/guidelines/agent-infra/asking-and-brevity-examples.md +24 -0
- package/docs/guidelines/agent-infra/corpus-grounding-authoring.md +1 -1
- package/docs/guidelines/agent-infra/linked-projects-onboarding-gate.md +2 -2
- package/docs/guidelines/agent-infra/missing-tool-handling.md +3 -3
- package/docs/guidelines/agent-infra/verify-before-complete-demos.md +2 -2
- package/docs/guidelines/augment-portability-patterns.md +1 -1
- package/docs/guidelines/code-clarity.md +19 -0
- package/docs/guidelines/design-antipatterns-triggers.json +2 -1
- package/docs/guidelines/design-antipatterns.md +10 -1
- package/docs/guidelines/design-modes.md +6 -0
- package/docs/guidelines/php/general.md +9 -1
- package/docs/guidelines/php/php-coding-patterns.md +7 -0
- package/docs/guides/cross-repo-retrieval.md +1 -1
- package/docs/guides/frontend-design-corpus-refresh.md +2 -2
- package/docs/guides/local-analytics.md +4 -4
- package/docs/guides/skill-discovery.md +1 -1
- package/docs/guides/skill-preview.md +1 -1
- package/docs/hook-payload-capture.md +6 -6
- package/docs/install-friction-report.md +66 -0
- package/docs/installation.md +1 -1
- package/docs/maintainers/optional-media-deps.md +50 -0
- package/docs/mcp-server.md +11 -12
- package/docs/media/proof-demo.cast +14 -0
- package/docs/media/proof-demo.gif +0 -0
- package/docs/migration/consumer-template-consumption-model.md +1 -1
- package/docs/migration/divergences/bench-stats-float-precision.md +1 -1
- package/docs/migration/node-floor.md +1 -1
- package/docs/migration/yaml-roundtrip-spike.md +1 -1
- package/docs/personas.md +16 -5
- package/docs/positioning-evidence.md +86 -0
- package/docs/proof.md +86 -0
- package/docs/skills-catalog.md +1 -1
- package/docs/troubleshooting.md +8 -8
- package/docs/wedge/production-validator/README.md +57 -0
- package/docs/wedge/production-validator/production-validator.md +56 -0
- package/docs/wizard.md +2 -2
- package/package.json +1 -1
- package/src/config/agent-settings.template.yml +59 -4
- package/src/config/agents-paths.yml +254 -0
- package/src/config/budgets.yml +16 -0
- package/src/config/discovery/packs.yml +1 -0
- package/src/config/discovery/unassigned-artefacts.yml +2 -0
- package/src/config/gitignore-block.txt +25 -0
- package/src/config/host-capabilities.yml +31 -0
- package/src/config/profiles/balanced.ini +4 -0
- package/src/config/profiles/full.ini +4 -0
- package/src/config/profiles/minimal.ini +4 -0
- package/src/scripts/_cli/cmd_conformance.ts +716 -0
- package/src/scripts/_cli/cmd_doctor.ts +23 -2
- package/src/scripts/_cli/cmd_fleet.ts +401 -0
- package/src/scripts/_cli/cmd_preflight.ts +159 -0
- package/src/scripts/_dispatch.bash +10 -0
- package/src/scripts/_lib/knowledge_events.ts +193 -0
- package/src/scripts/_lib/subagent_steering.ts +115 -3
- package/src/scripts/_lib/text_similarity.ts +75 -0
- package/src/scripts/ai-video/test-pipeline.sh +1 -1
- package/src/scripts/ai_council/clients.ts +19 -3
- package/src/scripts/append_contested.ts +112 -0
- package/src/scripts/audit_auto_rules.ts +11 -1
- package/src/scripts/audit_mcp_tools.ts +25 -11
- package/src/scripts/bench_ab_v2_run.ts +527 -40
- package/src/scripts/bench_ab_v2_stats.ts +6 -0
- package/src/scripts/bench_matrix.ts +266 -0
- package/src/scripts/bootstrap_knowledge.ts +267 -0
- package/src/scripts/build_discovery_manifest.ts +42 -2
- package/src/scripts/build_proof.ts +214 -0
- package/src/scripts/check_always_budget.ts +3 -0
- package/src/scripts/check_augment_description_cap.ts +2 -2
- package/src/scripts/check_claims.ts +270 -0
- package/src/scripts/check_comparison.ts +79 -0
- package/src/scripts/check_generated_artefact_headers.ts +109 -0
- package/src/scripts/check_generator_output_coverage.ts +142 -0
- package/src/scripts/check_gitignore_freshness.ts +127 -0
- package/src/scripts/check_knowledge_pages.ts +210 -0
- package/src/scripts/check_knowledge_sharing.ts +162 -0
- package/src/scripts/check_media_deps.ts +91 -0
- package/src/scripts/check_memory_contradiction.ts +280 -0
- package/src/scripts/check_memory_similarity.ts +134 -0
- package/src/scripts/check_quality_regression.ts +56 -10
- package/src/scripts/check_references.ts +3 -1
- package/src/scripts/check_release_pr_shape.ts +4 -0
- package/src/scripts/check_site_links.ts +135 -0
- package/src/scripts/check_skill_gaps.ts +99 -0
- package/src/scripts/check_token_quality_golden.ts +197 -11
- package/src/scripts/check_tracked_but_ignored.ts +67 -0
- package/src/scripts/cluster_near_miss_patterns.ts +0 -0
- package/src/scripts/compile_router.ts +30 -3
- package/src/scripts/condense.ts +350 -14
- package/src/scripts/consolidate_knowledge_events.ts +192 -0
- package/src/scripts/degrade_to_pointer.ts +136 -0
- package/src/scripts/design_slop_rules.test.ts +15 -0
- package/src/scripts/design_slop_rules.ts +28 -0
- package/src/scripts/emit_knowledge_event.ts +158 -0
- package/src/scripts/export_replay_corpus.ts +187 -0
- package/src/scripts/external_sources_denylist.json +7 -2
- package/src/scripts/fold_intake.ts +281 -0
- package/src/scripts/generate_capability_matrix.ts +9 -1
- package/src/scripts/generate_knowledge_index.ts +222 -0
- package/src/scripts/hook_manifest.yaml +26 -14
- package/src/scripts/hooks/dispatch_hook.ts +18 -0
- package/src/scripts/hot_context_hook.ts +372 -0
- package/src/scripts/install +47 -0
- package/src/scripts/install-hooks.sh +17 -0
- package/src/scripts/inventory_meta_layers.ts +11 -0
- package/src/scripts/janitor.ts +258 -0
- package/src/scripts/lint_agents_layout.ts +74 -15
- package/src/scripts/lint_bench_ab.ts +4 -1
- package/src/scripts/lint_featured_skills.ts +0 -0
- package/src/scripts/lint_framework_leakage_allowlist.json +26 -3
- package/src/scripts/lint_knowledge_scale.ts +250 -0
- package/src/scripts/lint_legal_pack.ts +67 -0
- package/src/scripts/lint_persona_governance.ts +8 -3
- package/src/scripts/lint_regression.ts +15 -0
- package/src/scripts/lint_roadmap_blockers.ts +202 -0
- package/src/scripts/lint_roadmap_complexity.ts +81 -2
- package/src/scripts/lint_subagent_determinism.ts +125 -0
- package/src/scripts/measure_augment_budget.ts +46 -6
- package/src/scripts/measure_patterns.ts +4 -3
- package/src/scripts/measure_skill_reduction.ts +6 -6
- package/src/scripts/migration_status.ts +6 -2
- package/src/scripts/mine_missing_guardrails.ts +189 -0
- package/src/scripts/parity/phase_gate.ts +1 -1
- package/src/scripts/parity/replay.ts +21 -0
- package/src/scripts/project_thin_rules.ts +120 -16
- package/src/scripts/release.ts +39 -1
- package/src/scripts/render_benchmark_composite.ts +378 -0
- package/src/scripts/roadmap_progress_hook.ts +101 -43
- package/src/scripts/schemas/skill.schema.json +27 -0
- package/src/scripts/schemas/subagent.schema.json +106 -0
- package/src/scripts/skill_linter.ts +1 -0
- package/src/scripts/smoke/kernel.sh +5 -4
- package/src/scripts/surface-tiers.yml +1 -0
- package/src/scripts/sync_gitignore.ts +4 -0
- package/src/scripts/update_skill_candidates.ts +172 -0
- package/src/scripts/validate_frontmatter.ts +1 -0
|
@@ -32,7 +32,18 @@
|
|
|
32
32
|
* Cost controls inherited: --model pin (sonnet), --max-budget-usd cap. Cheap-by-
|
|
33
33
|
* construction: the v2 fixtures are tiny, so per-run tokens are far below the v1
|
|
34
34
|
* big-repo tasks.
|
|
35
|
+
*
|
|
36
|
+
* Checkpoint / resume (2026-07): every completed run (task × arm × seed) is
|
|
37
|
+
* appended to a JSONL checkpoint under reports/ab-v2/checkpoints/, keyed by a
|
|
38
|
+
* hash of the run configuration. A killed sweep restarted with the SAME flags
|
|
39
|
+
* resumes from the checkpoint instead of re-spending the completed runs (a
|
|
40
|
+
* 117/180 kill previously burned the whole sweep — results lived only in
|
|
41
|
+
* memory until the final report write). The checkpoint is deleted after the
|
|
42
|
+
* paired report is written, so a finished sweep leaves no residue. Opt out
|
|
43
|
+
* with --no-checkpoint; force a from-scratch run with --fresh.
|
|
35
44
|
*/
|
|
45
|
+
import { spawnSync } from 'node:child_process';
|
|
46
|
+
import { createHash } from 'node:crypto';
|
|
36
47
|
import * as fs from 'node:fs';
|
|
37
48
|
import * as os from 'node:os';
|
|
38
49
|
import * as path from 'node:path';
|
|
@@ -49,9 +60,12 @@ const _HERE = fileURLToPath(import.meta.url);
|
|
|
49
60
|
// export it, so resolve the identical value here (parents[2] of the script —
|
|
50
61
|
// src/scripts/bench_ab_v2_run.ts → repo root). Same path, single derivation.
|
|
51
62
|
const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
|
|
63
|
+
const ROUTER_PATH = path.join(REPO_ROOT, 'dist', 'router.json');
|
|
64
|
+
const RULES_DIR = path.join(REPO_ROOT, 'dist', 'agent-src', 'rules');
|
|
52
65
|
const CORPUS_PATH = path.join(REPO_ROOT, 'internal', 'bench', 'corpora', 'ab-trackb-v2.yaml');
|
|
53
66
|
const FIXTURES_ROOT = path.join(REPO_ROOT, 'internal', 'bench', 'ab');
|
|
54
67
|
const REPORTS_DIR = path.join(REPO_ROOT, 'internal', 'bench', 'reports', 'ab-v2');
|
|
68
|
+
const CHECKPOINTS_DIR = path.join(REPORTS_DIR, 'checkpoints');
|
|
55
69
|
// CRITICAL (2026-06-15): clones MUST live OUTSIDE the agent-config repo. A clone
|
|
56
70
|
// under the repo lets Claude discover the repo's own project surface (CLAUDE.md /
|
|
57
71
|
// AGENTS.md / .claude/rules+skills) walking up from the cwd — so the `vanilla`
|
|
@@ -81,7 +95,17 @@ interface ArmSpec {
|
|
|
81
95
|
// config PLUS the compile-time HARD CONSTRAINT blocks injected via sysprompt;
|
|
82
96
|
// `hardened-placebo` = `package` + inert prose of the SAME length, so the
|
|
83
97
|
// hardened Δ is length-controlled (measures emphasis, not verbosity).
|
|
84
|
-
|
|
98
|
+
// `rules-kernel-dc` / `rules-balanced` (cost-factor sweep, 2026-07): plugin OFF
|
|
99
|
+
// + ONLY a rule subset injected via sysprompt, to measure how much of the
|
|
100
|
+
// weak-host discipline lift survives at a fraction of the full package's
|
|
101
|
+
// loaded-context cost. `rules-kernel-dc` = the router kernel (9 Iron-Law rules)
|
|
102
|
+
// + `downstream-changes` (one of the two lift-carrying rules; the other,
|
|
103
|
+
// `scope-control`, is already in the kernel) — the cheapest configuration that
|
|
104
|
+
// contains both. `rules-balanced` = kernel + tier_1 rule bodies, i.e. the
|
|
105
|
+
// shipped `balanced` router profile (which does NOT include downstream-changes
|
|
106
|
+
// — tier_2 — so this arm also tests whether that profile cut loses the lift).
|
|
107
|
+
// Both are opt-in: NOT in the default arm list; existing runs unchanged.
|
|
108
|
+
export const ARMS: Record<string, ArmSpec> = {
|
|
85
109
|
vanilla: { setting_sources: 'project,local', inject: null },
|
|
86
110
|
package: { setting_sources: null, inject: null },
|
|
87
111
|
'package-rdp': { setting_sources: null, inject: 'rdp' },
|
|
@@ -89,6 +113,8 @@ const ARMS: Record<string, ArmSpec> = {
|
|
|
89
113
|
'package-recursive': { setting_sources: null, inject: null, recursive: true },
|
|
90
114
|
hardened: { setting_sources: null, inject: 'hardened' },
|
|
91
115
|
'hardened-placebo': { setting_sources: null, inject: 'hardened-placebo' },
|
|
116
|
+
'rules-kernel-dc': { setting_sources: 'project,local', inject: 'rules-kernel-dc' },
|
|
117
|
+
'rules-balanced': { setting_sources: 'project,local', inject: 'rules-balanced' },
|
|
92
118
|
};
|
|
93
119
|
|
|
94
120
|
/**
|
|
@@ -139,6 +165,30 @@ export function hardened_blocks_text(): string {
|
|
|
139
165
|
].join('\n');
|
|
140
166
|
}
|
|
141
167
|
|
|
168
|
+
/**
|
|
169
|
+
* Rule-subset injection bodies for the cost-factor sweep arms.
|
|
170
|
+
*
|
|
171
|
+
* Reads the tier membership from `dist/router.json` and the rule bodies from
|
|
172
|
+
* `dist/agent-src/rules/` — deterministic per checkout, so the injected text is
|
|
173
|
+
* exactly what the shipped profiles would always-load:
|
|
174
|
+
* - 'rules-kernel-dc' → kernel (9 rules) + `downstream-changes`.
|
|
175
|
+
* - 'rules-balanced' → kernel + tier_1 (the shipped `balanced` profile).
|
|
176
|
+
*/
|
|
177
|
+
export function rules_subset_text(subset: 'rules-kernel-dc' | 'rules-balanced'): string {
|
|
178
|
+
const router = JSON.parse(fs.readFileSync(ROUTER_PATH, 'utf-8')) as {
|
|
179
|
+
kernel: string[];
|
|
180
|
+
tier_1: { id: string }[];
|
|
181
|
+
};
|
|
182
|
+
const ids = [...router.kernel];
|
|
183
|
+
if (subset === 'rules-kernel-dc') {
|
|
184
|
+
ids.push('downstream-changes');
|
|
185
|
+
} else {
|
|
186
|
+
ids.push(...router.tier_1.map((t) => t.id));
|
|
187
|
+
}
|
|
188
|
+
const bodies = ids.map((id) => fs.readFileSync(path.join(RULES_DIR, `${id}.md`), 'utf-8').trim());
|
|
189
|
+
return bodies.join('\n\n---\n\n');
|
|
190
|
+
}
|
|
191
|
+
|
|
142
192
|
export function injected_text(inject: string | null, placebo_chars: number): string | null {
|
|
143
193
|
if (inject === 'rdp') {
|
|
144
194
|
return v1.system_prompt_for('with-rdp');
|
|
@@ -153,6 +203,9 @@ export function injected_text(inject: string | null, placebo_chars: number): str
|
|
|
153
203
|
// Length-matched control: inert prose the SAME length as the hardened blocks.
|
|
154
204
|
return placebo_prose(hardened_blocks_text().length);
|
|
155
205
|
}
|
|
206
|
+
if (inject === 'rules-kernel-dc' || inject === 'rules-balanced') {
|
|
207
|
+
return rules_subset_text(inject);
|
|
208
|
+
}
|
|
156
209
|
return null;
|
|
157
210
|
}
|
|
158
211
|
|
|
@@ -210,6 +263,202 @@ interface RunOneOpts {
|
|
|
210
263
|
placebo_chars: number;
|
|
211
264
|
sp_dir: string;
|
|
212
265
|
max_depth?: number | null; // recursion arm only — hard cap on correction rounds (default 1)
|
|
266
|
+
host?: string; // 'claude' (default) | 'codex' — P2 non-Claude replication (ADR-110 roadmap Phase 4)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// ── codex host adapter (P2 non-Claude weak-host replication) ────────────────
|
|
270
|
+
//
|
|
271
|
+
// Drives the OpenAI `codex` CLI non-interactively with the SAME paired design
|
|
272
|
+
// and deterministic scorer as the claude host. Differences, documented for
|
|
273
|
+
// methodology honesty:
|
|
274
|
+
// - Injection surface: codex exec has no --append-system-prompt-file, so arm
|
|
275
|
+
// injections are PREPENDED to the task prompt inside a marked block (system
|
|
276
|
+
// vs user surface caveat — record it wherever results are published).
|
|
277
|
+
// - Auth/home: honours CODEX_BENCH_HOME (exported as CODEX_HOME) so the bench
|
|
278
|
+
// uses an isolated, API-key-authenticated codex home instead of the
|
|
279
|
+
// operator's interactive ChatGPT login.
|
|
280
|
+
// - Plugin-dependent arms (package / package-rdp / package-recursive /
|
|
281
|
+
// hardened variants that ride the real plugin) are refused on this host —
|
|
282
|
+
// only vanilla + injection arms are meaningful.
|
|
283
|
+
// - Cost: codex has no --max-budget-usd; the timeout + tiny fixtures bound
|
|
284
|
+
// spend. Tokens are summed from `--json` event usage payloads best-effort.
|
|
285
|
+
|
|
286
|
+
/** Arms that are valid on the codex host (vanilla + pure-injection arms). */
|
|
287
|
+
export const CODEX_VALID_ARMS: readonly string[] = [
|
|
288
|
+
'vanilla',
|
|
289
|
+
'placebo',
|
|
290
|
+
'rules-kernel-dc',
|
|
291
|
+
'rules-balanced',
|
|
292
|
+
];
|
|
293
|
+
|
|
294
|
+
export function codex_executable(): string | null {
|
|
295
|
+
const envBin = process.env['CODEX_CLI'];
|
|
296
|
+
if (envBin && fs.existsSync(envBin)) {
|
|
297
|
+
return envBin;
|
|
298
|
+
}
|
|
299
|
+
const pathVar = process.env['PATH'] ?? '';
|
|
300
|
+
for (const dir of pathVar.split(path.delimiter)) {
|
|
301
|
+
if (!dir) {
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
const candidate = path.join(dir, 'codex');
|
|
305
|
+
try {
|
|
306
|
+
fs.accessSync(candidate, fs.constants.X_OK);
|
|
307
|
+
return candidate;
|
|
308
|
+
} catch {
|
|
309
|
+
/* keep scanning */
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** Wrap an arm's injected rules so they read as standing instructions. */
|
|
316
|
+
export function codex_prompt(inject_text: string | null, task_prompt: string): string {
|
|
317
|
+
if (!inject_text) {
|
|
318
|
+
return task_prompt;
|
|
319
|
+
}
|
|
320
|
+
return (
|
|
321
|
+
'<project-instructions>\n' +
|
|
322
|
+
'The following are the standing project instructions that apply to every task.\n\n' +
|
|
323
|
+
inject_text +
|
|
324
|
+
'\n</project-instructions>\n\n' +
|
|
325
|
+
task_prompt
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
type SpawnFn = typeof spawnSync;
|
|
330
|
+
|
|
331
|
+
/** Sum token usage from codex `--json` JSONL events, best-effort. */
|
|
332
|
+
export function parse_codex_events(stdout: string): {
|
|
333
|
+
tokens: number;
|
|
334
|
+
num_turns: number;
|
|
335
|
+
transcript: string;
|
|
336
|
+
failed: string | null;
|
|
337
|
+
} {
|
|
338
|
+
let tokens = 0;
|
|
339
|
+
let num_turns = 0;
|
|
340
|
+
let failed: string | null = null;
|
|
341
|
+
const texts: string[] = [];
|
|
342
|
+
const addUsage = (u: unknown): void => {
|
|
343
|
+
if (u === null || typeof u !== 'object' || Array.isArray(u)) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
for (const [k, v] of Object.entries(u as Record<string, unknown>)) {
|
|
347
|
+
if (typeof v === 'number' && Number.isFinite(v) && /tokens?$/.test(k)) {
|
|
348
|
+
tokens += v;
|
|
349
|
+
} else if (v && typeof v === 'object') {
|
|
350
|
+
addUsage(v);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
for (const line of stdout.split('\n')) {
|
|
355
|
+
const trimmed = line.trim();
|
|
356
|
+
if (!trimmed.startsWith('{')) {
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
let evt: Record<string, unknown>;
|
|
360
|
+
try {
|
|
361
|
+
evt = JSON.parse(trimmed) as Record<string, unknown>;
|
|
362
|
+
} catch {
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
if (evt === null || typeof evt !== 'object') {
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
const type = String(evt['type'] ?? '');
|
|
369
|
+
if (type === 'turn.completed') {
|
|
370
|
+
num_turns += 1;
|
|
371
|
+
addUsage(evt['usage']);
|
|
372
|
+
} else if (type === 'turn.failed' || type === 'error') {
|
|
373
|
+
const err = evt['error'];
|
|
374
|
+
failed =
|
|
375
|
+
err && typeof err === 'object'
|
|
376
|
+
? String((err as Record<string, unknown>)['message'] ?? 'codex turn failed')
|
|
377
|
+
: String(evt['message'] ?? 'codex turn failed');
|
|
378
|
+
} else if (type === 'item.completed') {
|
|
379
|
+
const item = evt['item'];
|
|
380
|
+
if (item && typeof item === 'object') {
|
|
381
|
+
const it = item as Record<string, unknown>;
|
|
382
|
+
if (String(it['type'] ?? '') === 'agent_message' && typeof it['text'] === 'string') {
|
|
383
|
+
texts.push(it['text']);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return { tokens, num_turns, transcript: texts.join('\n'), failed };
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
export function run_live_codex(
|
|
392
|
+
task: Dict,
|
|
393
|
+
cloneRoot: string,
|
|
394
|
+
timeoutS: number,
|
|
395
|
+
opts: { model: string | null; inject_text: string | null; spawn?: SpawnFn },
|
|
396
|
+
): Dict {
|
|
397
|
+
const spawn = opts.spawn ?? spawnSync;
|
|
398
|
+
const binary = codex_executable();
|
|
399
|
+
if (binary === null) {
|
|
400
|
+
return {
|
|
401
|
+
mode: 'live-skipped',
|
|
402
|
+
reason: 'codex CLI not found; set CODEX_CLI or install it',
|
|
403
|
+
transcript: '',
|
|
404
|
+
exit_code: null,
|
|
405
|
+
wall_time_seconds: 0.0,
|
|
406
|
+
tokens: 0,
|
|
407
|
+
errored: true,
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
const prompt = codex_prompt(opts.inject_text, String(task['prompt'] ?? ''));
|
|
411
|
+
const args = [
|
|
412
|
+
'exec',
|
|
413
|
+
'--skip-git-repo-check',
|
|
414
|
+
'--sandbox',
|
|
415
|
+
'workspace-write',
|
|
416
|
+
'-c',
|
|
417
|
+
'approval_policy=never',
|
|
418
|
+
'--json',
|
|
419
|
+
];
|
|
420
|
+
if (opts.model) {
|
|
421
|
+
args.push('-m', opts.model);
|
|
422
|
+
}
|
|
423
|
+
args.push(prompt);
|
|
424
|
+
const env: NodeJS.ProcessEnv = { ...process.env };
|
|
425
|
+
if (process.env['CODEX_BENCH_HOME']) {
|
|
426
|
+
env['CODEX_HOME'] = process.env['CODEX_BENCH_HOME'];
|
|
427
|
+
}
|
|
428
|
+
const started = Date.now();
|
|
429
|
+
const result = spawn(binary, args, {
|
|
430
|
+
cwd: cloneRoot,
|
|
431
|
+
encoding: 'utf-8',
|
|
432
|
+
timeout: timeoutS * 1000,
|
|
433
|
+
env,
|
|
434
|
+
});
|
|
435
|
+
const wall = (Date.now() - started) / 1000;
|
|
436
|
+
const isTimeout = Boolean(result.error) && (result.error as NodeJS.ErrnoException).code === 'ETIMEDOUT';
|
|
437
|
+
if (isTimeout) {
|
|
438
|
+
return {
|
|
439
|
+
mode: 'live',
|
|
440
|
+
reason: `timeout after ${timeoutS}s`,
|
|
441
|
+
transcript: (result.stdout ?? '') + '\n[TIMEOUT]',
|
|
442
|
+
exit_code: -1,
|
|
443
|
+
wall_time_seconds: Math.round(wall * 1000) / 1000,
|
|
444
|
+
tokens: 0,
|
|
445
|
+
errored: true,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
const stdout = result.stdout ?? '';
|
|
449
|
+
const parsed = parse_codex_events(stdout);
|
|
450
|
+
const exitCode = result.status ?? -1;
|
|
451
|
+
const errored = exitCode !== 0 || parsed.failed !== null;
|
|
452
|
+
return {
|
|
453
|
+
mode: 'live',
|
|
454
|
+
reason: errored ? (parsed.failed ?? `codex exited ${exitCode}`) : 'ok',
|
|
455
|
+
transcript: parsed.transcript || stdout,
|
|
456
|
+
exit_code: exitCode,
|
|
457
|
+
wall_time_seconds: Math.round(wall * 1000) / 1000,
|
|
458
|
+
tokens: parsed.tokens,
|
|
459
|
+
num_turns: parsed.num_turns,
|
|
460
|
+
errored,
|
|
461
|
+
};
|
|
213
462
|
}
|
|
214
463
|
|
|
215
464
|
export function run_one(task: Dict, arm: string, opts: RunOneOpts): Dict {
|
|
@@ -217,19 +466,28 @@ export function run_one(task: Dict, arm: string, opts: RunOneOpts): Dict {
|
|
|
217
466
|
if (spec.recursive) {
|
|
218
467
|
return run_one_recursive(task, opts);
|
|
219
468
|
}
|
|
469
|
+
const host = opts.host ?? 'claude';
|
|
220
470
|
const [clone, fixture] = reset_fixture(task);
|
|
221
471
|
const sp_text = injected_text(spec.inject, opts.placebo_chars);
|
|
222
|
-
let
|
|
223
|
-
if (
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
472
|
+
let run: Dict;
|
|
473
|
+
if (host === 'codex') {
|
|
474
|
+
run = run_live_codex(task, clone, opts.timeout, {
|
|
475
|
+
model: opts.model,
|
|
476
|
+
inject_text: sp_text,
|
|
477
|
+
});
|
|
478
|
+
} else {
|
|
479
|
+
let sp_file: string | null = null;
|
|
480
|
+
if (sp_text) {
|
|
481
|
+
sp_file = path.join(opts.sp_dir, `.sp-${arm}.txt`);
|
|
482
|
+
fs.writeFileSync(sp_file, sp_text, { encoding: 'utf-8' });
|
|
483
|
+
}
|
|
484
|
+
run = v1.run_live(task, clone, opts.timeout, {
|
|
485
|
+
sysprompt_file: sp_file,
|
|
486
|
+
setting_sources: spec.setting_sources,
|
|
487
|
+
max_budget: opts.max_budget,
|
|
488
|
+
model: opts.model,
|
|
489
|
+
}) as unknown as Dict;
|
|
490
|
+
}
|
|
233
491
|
const score = scoring.score_task_v2(task, {
|
|
234
492
|
fixture_root: fixture,
|
|
235
493
|
clone_root: clone,
|
|
@@ -363,6 +621,192 @@ export function run_one_recursive(
|
|
|
363
621
|
};
|
|
364
622
|
}
|
|
365
623
|
|
|
624
|
+
// ── checkpoint / resume ─────────────────────────────────────────────────────
|
|
625
|
+
//
|
|
626
|
+
// A sweep of tasks × arms × seeds can run for hours; without a checkpoint, a
|
|
627
|
+
// kill (manual stop, harness timeout, machine sleep) loses EVERY completed run
|
|
628
|
+
// because results only exist in memory until the single final report write.
|
|
629
|
+
// The checkpoint is a JSONL file (one line per completed run) so each write is
|
|
630
|
+
// a single atomic-enough append and a truncated tail line (killed mid-write)
|
|
631
|
+
// is simply skipped on load. The file is keyed by a hash of the run config —
|
|
632
|
+
// a restart with different flags gets a different checkpoint and never mixes
|
|
633
|
+
// results from an incompatible sweep.
|
|
634
|
+
|
|
635
|
+
/** Stable id key for one run inside a sweep. */
|
|
636
|
+
export function run_key(task_id: string, arm: string, seed: number): string {
|
|
637
|
+
return `${task_id}|${arm}|${seed}`;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Hash of the sweep configuration — everything that changes what a run means.
|
|
642
|
+
* Same flags → same key → resume; any config change → fresh checkpoint file.
|
|
643
|
+
*/
|
|
644
|
+
export function checkpoint_key(cfg: {
|
|
645
|
+
corpus: string;
|
|
646
|
+
model: string;
|
|
647
|
+
seeds: number;
|
|
648
|
+
arms: string[];
|
|
649
|
+
budget: number;
|
|
650
|
+
timeout: number;
|
|
651
|
+
host: string;
|
|
652
|
+
task_ids: string[];
|
|
653
|
+
}): string {
|
|
654
|
+
const stable = JSON.stringify([
|
|
655
|
+
cfg.corpus,
|
|
656
|
+
cfg.model,
|
|
657
|
+
cfg.seeds,
|
|
658
|
+
cfg.arms,
|
|
659
|
+
cfg.budget,
|
|
660
|
+
cfg.timeout,
|
|
661
|
+
cfg.host,
|
|
662
|
+
cfg.task_ids,
|
|
663
|
+
]);
|
|
664
|
+
return createHash('sha256').update(stable).digest('hex').slice(0, 16);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* JSON round-trip loses the PyFloat wrapper (1.0 parses back as 1), which
|
|
669
|
+
* would break the byte-parity float rendering of the final report on resume.
|
|
670
|
+
* freeze/thaw make the checkpoint lossless: PyFloat → {"__pyfloat__": n} on
|
|
671
|
+
* write, restored to PyFloat on load.
|
|
672
|
+
*/
|
|
673
|
+
export function freeze_result(v: unknown): unknown {
|
|
674
|
+
if (v instanceof PyFloat) {
|
|
675
|
+
return { __pyfloat__: v.value };
|
|
676
|
+
}
|
|
677
|
+
if (Array.isArray(v)) {
|
|
678
|
+
return v.map((x) => freeze_result(x));
|
|
679
|
+
}
|
|
680
|
+
if (v && typeof v === 'object') {
|
|
681
|
+
const out: Record<string, unknown> = {};
|
|
682
|
+
for (const [k, val] of Object.entries(v as Record<string, unknown>)) {
|
|
683
|
+
out[k] = freeze_result(val);
|
|
684
|
+
}
|
|
685
|
+
return out;
|
|
686
|
+
}
|
|
687
|
+
return v;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
export function thaw_result(v: unknown): unknown {
|
|
691
|
+
if (Array.isArray(v)) {
|
|
692
|
+
return v.map((x) => thaw_result(x));
|
|
693
|
+
}
|
|
694
|
+
if (v && typeof v === 'object') {
|
|
695
|
+
const o = v as Record<string, unknown>;
|
|
696
|
+
const keys = Object.keys(o);
|
|
697
|
+
if (keys.length === 1 && keys[0] === '__pyfloat__' && typeof o['__pyfloat__'] === 'number') {
|
|
698
|
+
return new PyFloat(o['__pyfloat__']);
|
|
699
|
+
}
|
|
700
|
+
const out: Record<string, unknown> = {};
|
|
701
|
+
for (const [k, val] of Object.entries(o)) {
|
|
702
|
+
out[k] = thaw_result(val);
|
|
703
|
+
}
|
|
704
|
+
return out;
|
|
705
|
+
}
|
|
706
|
+
return v;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* Load completed runs from a checkpoint file. Tolerant by design: a missing
|
|
711
|
+
* file → empty map; an unparseable line (truncated tail from a mid-write
|
|
712
|
+
* kill) → skipped; a duplicate key → last line wins.
|
|
713
|
+
*/
|
|
714
|
+
export function load_checkpoint(file: string): Map<string, Dict> {
|
|
715
|
+
const map = new Map<string, Dict>();
|
|
716
|
+
let raw: string;
|
|
717
|
+
try {
|
|
718
|
+
raw = fs.readFileSync(file, 'utf-8');
|
|
719
|
+
} catch {
|
|
720
|
+
return map;
|
|
721
|
+
}
|
|
722
|
+
for (const line of raw.split('\n')) {
|
|
723
|
+
const trimmed = line.trim();
|
|
724
|
+
if (!trimmed) {
|
|
725
|
+
continue;
|
|
726
|
+
}
|
|
727
|
+
let entry: unknown;
|
|
728
|
+
try {
|
|
729
|
+
entry = JSON.parse(trimmed);
|
|
730
|
+
} catch {
|
|
731
|
+
continue; // truncated tail — the run it held simply re-executes
|
|
732
|
+
}
|
|
733
|
+
if (entry && typeof entry === 'object' && !Array.isArray(entry)) {
|
|
734
|
+
const e = entry as Record<string, unknown>;
|
|
735
|
+
if (typeof e['key'] === 'string' && e['result'] && typeof e['result'] === 'object') {
|
|
736
|
+
map.set(e['key'], thaw_result(e['result']) as Dict);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
return map;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/** Append one completed run to the checkpoint (single-line atomic append). */
|
|
744
|
+
export function append_checkpoint(file: string, key: string, result: Dict): void {
|
|
745
|
+
fs.appendFileSync(file, JSON.stringify({ key, result: freeze_result(result) }) + '\n', {
|
|
746
|
+
encoding: 'utf-8',
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
export interface CheckpointIO {
|
|
751
|
+
/** Checkpoint file to append completed runs to (created on first append). */
|
|
752
|
+
path: string;
|
|
753
|
+
/** Runs already completed by a prior (killed) sweep — reused, not re-run. */
|
|
754
|
+
completed: Map<string, Dict>;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* The sweep loop, extracted from main() with an injectable run function so the
|
|
759
|
+
* resume behaviour is testable without live model calls. Semantics with no
|
|
760
|
+
* checkpoint are byte-identical to the pre-checkpoint loop (same stderr
|
|
761
|
+
* progress lines, same record shape).
|
|
762
|
+
*/
|
|
763
|
+
export function collect_records(
|
|
764
|
+
tasks: Dict[],
|
|
765
|
+
arms: string[],
|
|
766
|
+
seeds: number,
|
|
767
|
+
run_fn: (task: Dict, arm: string, seed: number) => Dict,
|
|
768
|
+
ckpt: CheckpointIO | null = null,
|
|
769
|
+
log: (msg: string) => void = (m) => process.stderr.write(m),
|
|
770
|
+
): { records: Dict[]; executed: number; reused: number } {
|
|
771
|
+
const total = tasks.length * arms.length * seeds;
|
|
772
|
+
let done = 0;
|
|
773
|
+
let executed = 0;
|
|
774
|
+
let reused = 0;
|
|
775
|
+
const records: Dict[] = [];
|
|
776
|
+
for (const task of tasks) {
|
|
777
|
+
const per_arm: Record<string, Dict[]> = {};
|
|
778
|
+
for (const arm of arms) {
|
|
779
|
+
const seed_runs: Dict[] = [];
|
|
780
|
+
for (let seed = 0; seed < seeds; seed += 1) {
|
|
781
|
+
done += 1;
|
|
782
|
+
const key = run_key(String(task['id']), arm, seed);
|
|
783
|
+
const cached = ckpt?.completed.get(key);
|
|
784
|
+
if (cached) {
|
|
785
|
+
reused += 1;
|
|
786
|
+
seed_runs.push(cached);
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
789
|
+
log(`[${done}/${total}] ${String(task['id'])} · ${arm} · seed ${seed}\n`);
|
|
790
|
+
const r = run_fn(task, arm, seed);
|
|
791
|
+
r['seed'] = seed;
|
|
792
|
+
executed += 1;
|
|
793
|
+
if (ckpt) {
|
|
794
|
+
append_checkpoint(ckpt.path, key, r);
|
|
795
|
+
}
|
|
796
|
+
seed_runs.push(r);
|
|
797
|
+
}
|
|
798
|
+
per_arm[arm] = seed_runs;
|
|
799
|
+
}
|
|
800
|
+
records.push({
|
|
801
|
+
id: task['id'],
|
|
802
|
+
archetype: task['archetype'],
|
|
803
|
+
rule: task['rule'],
|
|
804
|
+
arms: per_arm,
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
return { records, executed, reused };
|
|
808
|
+
}
|
|
809
|
+
|
|
366
810
|
export function main(argv: string[] | null = null): number {
|
|
367
811
|
const args = parse_args(argv ?? process.argv.slice(2));
|
|
368
812
|
|
|
@@ -388,6 +832,13 @@ export function main(argv: string[] | null = null): number {
|
|
|
388
832
|
process.stderr.write(`unknown arm: ${a}\n`);
|
|
389
833
|
return 1;
|
|
390
834
|
}
|
|
835
|
+
if (args.host === 'codex' && !CODEX_VALID_ARMS.includes(a)) {
|
|
836
|
+
process.stderr.write(
|
|
837
|
+
`arm ${a} requires the claude plugin host; valid --host codex arms: ` +
|
|
838
|
+
`${CODEX_VALID_ARMS.join(',')}\n`,
|
|
839
|
+
);
|
|
840
|
+
return 1;
|
|
841
|
+
}
|
|
391
842
|
}
|
|
392
843
|
|
|
393
844
|
if (args.mode === 'dry-run') {
|
|
@@ -412,35 +863,50 @@ export function main(argv: string[] | null = null): number {
|
|
|
412
863
|
const placebo_chars = Math.max(rdp_text.length, 2000);
|
|
413
864
|
|
|
414
865
|
const total = tasks.length * arms.length * args.seeds;
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
timeout: args.timeout,
|
|
428
|
-
placebo_chars,
|
|
429
|
-
sp_dir,
|
|
430
|
-
});
|
|
431
|
-
r['seed'] = seed;
|
|
432
|
-
seed_runs.push(r);
|
|
433
|
-
}
|
|
434
|
-
per_arm[arm] = seed_runs;
|
|
435
|
-
}
|
|
436
|
-
records.push({
|
|
437
|
-
id: task['id'],
|
|
438
|
-
archetype: task['archetype'],
|
|
439
|
-
rule: task['rule'],
|
|
440
|
-
arms: per_arm,
|
|
866
|
+
|
|
867
|
+
let ckpt: CheckpointIO | null = null;
|
|
868
|
+
if (args.checkpoint) {
|
|
869
|
+
const key = checkpoint_key({
|
|
870
|
+
corpus: 'ab-trackb-v2',
|
|
871
|
+
model: args.model,
|
|
872
|
+
seeds: args.seeds,
|
|
873
|
+
arms,
|
|
874
|
+
budget: args.budget,
|
|
875
|
+
timeout: args.timeout,
|
|
876
|
+
host: args.host,
|
|
877
|
+
task_ids: tasks.map((t) => String(t['id'])),
|
|
441
878
|
});
|
|
879
|
+
const ckpt_path = path.join(CHECKPOINTS_DIR, `${key}.jsonl`);
|
|
880
|
+
fs.mkdirSync(CHECKPOINTS_DIR, { recursive: true });
|
|
881
|
+
if (args.fresh && fs.existsSync(ckpt_path)) {
|
|
882
|
+
fs.rmSync(ckpt_path);
|
|
883
|
+
}
|
|
884
|
+
const completed = load_checkpoint(ckpt_path);
|
|
885
|
+
if (completed.size > 0) {
|
|
886
|
+
process.stderr.write(
|
|
887
|
+
`bench_ab_v2: resuming — ${completed.size}/${total} runs already complete ` +
|
|
888
|
+
`(checkpoint ${_relToRootPosix(ckpt_path)}; use --fresh to discard)\n`,
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
ckpt = { path: ckpt_path, completed };
|
|
442
892
|
}
|
|
443
893
|
|
|
894
|
+
const { records } = collect_records(
|
|
895
|
+
tasks,
|
|
896
|
+
arms,
|
|
897
|
+
args.seeds,
|
|
898
|
+
(task, _arm, _seed) =>
|
|
899
|
+
run_one(task, _arm, {
|
|
900
|
+
model: args.model,
|
|
901
|
+
max_budget,
|
|
902
|
+
timeout: args.timeout,
|
|
903
|
+
placebo_chars,
|
|
904
|
+
sp_dir,
|
|
905
|
+
host: args.host,
|
|
906
|
+
}),
|
|
907
|
+
ckpt,
|
|
908
|
+
);
|
|
909
|
+
|
|
444
910
|
const stamp = v1.utc_stamp();
|
|
445
911
|
const payload: Dict = {
|
|
446
912
|
schema: 'ab-bench-v2/0.1',
|
|
@@ -451,10 +917,16 @@ export function main(argv: string[] | null = null): number {
|
|
|
451
917
|
budget_usd_per_run: args.budget,
|
|
452
918
|
placebo_chars,
|
|
453
919
|
corpus: 'ab-trackb-v2',
|
|
920
|
+
host: args.host,
|
|
454
921
|
records,
|
|
455
922
|
};
|
|
456
923
|
const out = path.join(REPORTS_DIR, `${stamp}-ab-v2-paired.json`);
|
|
457
924
|
fs.writeFileSync(out, _jsonDumps(_toJson(payload), 2) + '\n');
|
|
925
|
+
// The paired report now holds everything — retire the checkpoint so a
|
|
926
|
+
// finished sweep leaves no residue and a later identical sweep starts fresh.
|
|
927
|
+
if (ckpt && fs.existsSync(ckpt.path)) {
|
|
928
|
+
fs.rmSync(ckpt.path);
|
|
929
|
+
}
|
|
458
930
|
process.stdout.write(
|
|
459
931
|
`bench_ab_v2: wrote ${_relToRootPosix(out)} ` + `(${records.length} tasks, ${total} runs)\n`,
|
|
460
932
|
);
|
|
@@ -472,6 +944,9 @@ interface ParsedArgs {
|
|
|
472
944
|
budget: number;
|
|
473
945
|
timeout: number;
|
|
474
946
|
mode: string;
|
|
947
|
+
host: string;
|
|
948
|
+
checkpoint: boolean;
|
|
949
|
+
fresh: boolean;
|
|
475
950
|
}
|
|
476
951
|
|
|
477
952
|
class ArgExit extends Error {}
|
|
@@ -487,8 +962,11 @@ function parse_args(argv: string[]): ParsedArgs {
|
|
|
487
962
|
budget: 1.0,
|
|
488
963
|
timeout: 180,
|
|
489
964
|
mode: 'live',
|
|
965
|
+
host: 'claude',
|
|
966
|
+
checkpoint: true,
|
|
967
|
+
fresh: false,
|
|
490
968
|
};
|
|
491
|
-
const usage = `usage: ${prog} [-h] [--arms ARMS] [--seeds SEEDS] [--tasks TASKS] [--limit LIMIT] [--model MODEL] [--budget BUDGET] [--timeout TIMEOUT] [--mode {live,dry-run}]\n`;
|
|
969
|
+
const usage = `usage: ${prog} [-h] [--arms ARMS] [--seeds SEEDS] [--tasks TASKS] [--limit LIMIT] [--model MODEL] [--budget BUDGET] [--timeout TIMEOUT] [--mode {live,dry-run}] [--host {claude,codex}] [--no-checkpoint] [--fresh]\n`;
|
|
492
970
|
const argErr = (msg: string): never => {
|
|
493
971
|
process.stderr.write(usage);
|
|
494
972
|
process.stderr.write(`${prog}: error: ${msg}\n`);
|
|
@@ -544,8 +1022,17 @@ function parse_args(argv: string[]): ParsedArgs {
|
|
|
544
1022
|
} else if (a === '--mode') {
|
|
545
1023
|
out.mode = _choice(need(i, '--mode'), '--mode', ['live', 'dry-run'], argErr);
|
|
546
1024
|
i += 1;
|
|
1025
|
+
} else if (a === '--host') {
|
|
1026
|
+
out.host = _choice(need(i, '--host'), '--host', ['claude', 'codex'], argErr);
|
|
1027
|
+
i += 1;
|
|
1028
|
+
} else if (a.startsWith('--host=')) {
|
|
1029
|
+
out.host = _choice(a.slice('--host='.length), '--host', ['claude', 'codex'], argErr);
|
|
547
1030
|
} else if (a.startsWith('--mode=')) {
|
|
548
1031
|
out.mode = _choice(a.slice('--mode='.length), '--mode', ['live', 'dry-run'], argErr);
|
|
1032
|
+
} else if (a === '--no-checkpoint') {
|
|
1033
|
+
out.checkpoint = false;
|
|
1034
|
+
} else if (a === '--fresh') {
|
|
1035
|
+
out.fresh = true;
|
|
549
1036
|
} else {
|
|
550
1037
|
argErr(`unrecognized arguments: ${a}`);
|
|
551
1038
|
}
|
|
@@ -646,7 +1133,7 @@ function _pyNumStr(x: number): string {
|
|
|
646
1133
|
|
|
647
1134
|
// ── JSON byte-parity (json.dumps(payload, indent=2)) ──────────────────────
|
|
648
1135
|
|
|
649
|
-
class PyFloat {
|
|
1136
|
+
export class PyFloat {
|
|
650
1137
|
constructor(readonly value: number) {}
|
|
651
1138
|
}
|
|
652
1139
|
|