@rpamis/comet 0.4.0-beta.3 → 0.4.0-beta.4
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/README.md +10 -0
- package/assets/manifest.json +2 -1
- package/assets/skills/comet/SKILL.md +25 -1
- package/assets/skills/comet/reference/comet-yaml-fields.md +5 -2
- package/assets/skills/comet/reference/context-recovery.md +10 -0
- package/assets/skills/comet/reference/scripts.md +47 -15
- package/assets/skills/comet/rules/comet-phase-guard.en.md +8 -0
- package/assets/skills/comet/rules/comet-phase-guard.md +9 -1
- package/assets/skills/comet/scripts/comet-resume-probe.mjs +3 -0
- package/assets/skills/comet/scripts/comet-runtime.mjs +1387 -424
- package/assets/skills/comet-archive/SKILL.md +24 -11
- package/assets/skills/comet-build/SKILL.md +42 -25
- package/assets/skills/comet-design/SKILL.md +1 -0
- package/assets/skills/comet-hotfix/SKILL.md +3 -1
- package/assets/skills/comet-open/SKILL.md +11 -1
- package/assets/skills/comet-tweak/SKILL.md +3 -1
- package/assets/skills/comet-verify/SKILL.md +29 -20
- package/assets/skills-zh/comet/SKILL.md +25 -1
- package/assets/skills-zh/comet/reference/comet-yaml-fields.md +5 -2
- package/assets/skills-zh/comet/reference/context-recovery.md +10 -0
- package/assets/skills-zh/comet/reference/scripts.md +47 -15
- package/assets/skills-zh/comet-archive/SKILL.md +24 -11
- package/assets/skills-zh/comet-build/SKILL.md +42 -25
- package/assets/skills-zh/comet-design/SKILL.md +1 -0
- package/assets/skills-zh/comet-hotfix/SKILL.md +3 -1
- package/assets/skills-zh/comet-open/SKILL.md +11 -1
- package/assets/skills-zh/comet-tweak/SKILL.md +3 -1
- package/assets/skills-zh/comet-verify/SKILL.md +30 -21
- package/dist/app/cli/comet-banner.d.ts +25 -0
- package/dist/app/cli/comet-banner.d.ts.map +1 -0
- package/dist/app/cli/comet-banner.js +265 -0
- package/dist/app/cli/comet-banner.js.map +1 -0
- package/dist/app/cli/index.js +36 -2
- package/dist/app/cli/index.js.map +1 -1
- package/dist/app/commands/classic.d.ts +4 -0
- package/dist/app/commands/classic.d.ts.map +1 -0
- package/dist/app/commands/classic.js +11 -0
- package/dist/app/commands/classic.js.map +1 -0
- package/dist/app/commands/doctor.d.ts.map +1 -1
- package/dist/app/commands/doctor.js +39 -24
- package/dist/app/commands/doctor.js.map +1 -1
- package/dist/app/commands/eval.d.ts.map +1 -1
- package/dist/app/commands/eval.js +57 -20
- package/dist/app/commands/eval.js.map +1 -1
- package/dist/app/commands/i18n.d.ts +1 -1
- package/dist/app/commands/i18n.d.ts.map +1 -1
- package/dist/app/commands/i18n.js +12 -4
- package/dist/app/commands/i18n.js.map +1 -1
- package/dist/app/commands/init.d.ts.map +1 -1
- package/dist/app/commands/init.js +16 -13
- package/dist/app/commands/init.js.map +1 -1
- package/dist/app/commands/project-scope-selection.d.ts +13 -0
- package/dist/app/commands/project-scope-selection.d.ts.map +1 -0
- package/dist/app/commands/project-scope-selection.js +33 -0
- package/dist/app/commands/project-scope-selection.js.map +1 -0
- package/dist/app/commands/resume-probe.d.ts +11 -0
- package/dist/app/commands/resume-probe.d.ts.map +1 -0
- package/dist/app/commands/resume-probe.js +63 -0
- package/dist/app/commands/resume-probe.js.map +1 -0
- package/dist/app/commands/status.d.ts +31 -0
- package/dist/app/commands/status.d.ts.map +1 -1
- package/dist/app/commands/status.js +109 -20
- package/dist/app/commands/status.js.map +1 -1
- package/dist/app/commands/uninstall.d.ts +2 -0
- package/dist/app/commands/uninstall.d.ts.map +1 -1
- package/dist/app/commands/uninstall.js +217 -40
- package/dist/app/commands/uninstall.js.map +1 -1
- package/dist/app/commands/update.d.ts +6 -0
- package/dist/app/commands/update.d.ts.map +1 -1
- package/dist/app/commands/update.js +361 -68
- package/dist/app/commands/update.js.map +1 -1
- package/dist/config/repository-layout.json +4 -2
- package/dist/domains/bundle/bundle-platform.d.ts.map +1 -1
- package/dist/domains/bundle/bundle-platform.js +3 -1
- package/dist/domains/bundle/bundle-platform.js.map +1 -1
- package/dist/domains/bundle/eval-manifest-runtime.d.ts +6 -0
- package/dist/domains/bundle/eval-manifest-runtime.d.ts.map +1 -0
- package/dist/domains/bundle/eval-manifest-runtime.js +58 -0
- package/dist/domains/bundle/eval-manifest-runtime.js.map +1 -0
- package/dist/domains/bundle/types.d.ts +1 -1
- package/dist/domains/bundle/types.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-archive.d.ts +1 -0
- package/dist/domains/comet-classic/classic-archive.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-archive.js +35 -38
- package/dist/domains/comet-classic/classic-archive.js.map +1 -1
- package/dist/domains/comet-classic/classic-cli.d.ts +1 -1
- package/dist/domains/comet-classic/classic-cli.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-cli.js +3 -0
- package/dist/domains/comet-classic/classic-cli.js.map +1 -1
- package/dist/domains/comet-classic/classic-command-checks.d.ts +20 -0
- package/dist/domains/comet-classic/classic-command-checks.d.ts.map +1 -0
- package/dist/domains/comet-classic/classic-command-checks.js +97 -0
- package/dist/domains/comet-classic/classic-command-checks.js.map +1 -0
- package/dist/domains/comet-classic/classic-current-change.d.ts +19 -0
- package/dist/domains/comet-classic/classic-current-change.d.ts.map +1 -0
- package/dist/domains/comet-classic/classic-current-change.js +132 -0
- package/dist/domains/comet-classic/classic-current-change.js.map +1 -0
- package/dist/domains/comet-classic/classic-diagnostics.d.ts +2 -1
- package/dist/domains/comet-classic/classic-diagnostics.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-diagnostics.js +54 -1
- package/dist/domains/comet-classic/classic-diagnostics.js.map +1 -1
- package/dist/domains/comet-classic/classic-evidence.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-evidence.js +4 -0
- package/dist/domains/comet-classic/classic-evidence.js.map +1 -1
- package/dist/domains/comet-classic/classic-guard.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-guard.js +68 -69
- package/dist/domains/comet-classic/classic-guard.js.map +1 -1
- package/dist/domains/comet-classic/classic-hook-guard.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-hook-guard.js +66 -23
- package/dist/domains/comet-classic/classic-hook-guard.js.map +1 -1
- package/dist/domains/comet-classic/classic-project-config.d.ts +16 -0
- package/dist/domains/comet-classic/classic-project-config.d.ts.map +1 -0
- package/dist/domains/comet-classic/classic-project-config.js +40 -0
- package/dist/domains/comet-classic/classic-project-config.js.map +1 -0
- package/dist/domains/comet-classic/classic-resolver.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-resolver.js +3 -3
- package/dist/domains/comet-classic/classic-resolver.js.map +1 -1
- package/dist/domains/comet-classic/classic-resume-probe-command.d.ts +3 -0
- package/dist/domains/comet-classic/classic-resume-probe-command.d.ts.map +1 -0
- package/dist/domains/comet-classic/classic-resume-probe-command.js +58 -0
- package/dist/domains/comet-classic/classic-resume-probe-command.js.map +1 -0
- package/dist/domains/comet-classic/classic-resume-probe-entry.d.ts +2 -0
- package/dist/domains/comet-classic/classic-resume-probe-entry.d.ts.map +1 -0
- package/dist/domains/comet-classic/classic-resume-probe-entry.js +4 -0
- package/dist/domains/comet-classic/classic-resume-probe-entry.js.map +1 -0
- package/dist/domains/comet-classic/classic-resume-probe.d.ts +29 -0
- package/dist/domains/comet-classic/classic-resume-probe.d.ts.map +1 -0
- package/dist/domains/comet-classic/classic-resume-probe.js +386 -0
- package/dist/domains/comet-classic/classic-resume-probe.js.map +1 -0
- package/dist/domains/comet-classic/classic-runtime-run.d.ts +2 -1
- package/dist/domains/comet-classic/classic-runtime-run.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-runtime-run.js +38 -0
- package/dist/domains/comet-classic/classic-runtime-run.js.map +1 -1
- package/dist/domains/comet-classic/classic-state-command.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-state-command.js +128 -12
- package/dist/domains/comet-classic/classic-state-command.js.map +1 -1
- package/dist/domains/comet-classic/classic-state.d.ts +3 -1
- package/dist/domains/comet-classic/classic-state.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-state.js +4 -0
- package/dist/domains/comet-classic/classic-state.js.map +1 -1
- package/dist/domains/comet-classic/classic-store.d.ts +4 -1
- package/dist/domains/comet-classic/classic-store.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-store.js +4 -3
- package/dist/domains/comet-classic/classic-store.js.map +1 -1
- package/dist/domains/comet-classic/classic-transitions.d.ts +1 -1
- package/dist/domains/comet-classic/classic-transitions.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-transitions.js +20 -1
- package/dist/domains/comet-classic/classic-transitions.js.map +1 -1
- package/dist/domains/comet-classic/classic-validate-command.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-validate-command.js +1 -0
- package/dist/domains/comet-classic/classic-validate-command.js.map +1 -1
- package/dist/domains/comet-classic/index.d.ts +3 -0
- package/dist/domains/comet-classic/index.d.ts.map +1 -1
- package/dist/domains/comet-classic/index.js +3 -0
- package/dist/domains/comet-classic/index.js.map +1 -1
- package/dist/domains/dashboard/collector.js +10 -2
- package/dist/domains/dashboard/collector.js.map +1 -1
- package/dist/domains/dashboard/web/assets/{index-CjU3VXVu.js → index-CIvN-PL8.js} +11 -11
- package/dist/domains/dashboard/web/assets/{index-BcO3_QlT.css → index-DcyRPBnT.css} +1 -1
- package/dist/domains/dashboard/web/index.html +2 -2
- package/dist/domains/engine/types.d.ts +1 -1
- package/dist/domains/engine/types.d.ts.map +1 -1
- package/dist/domains/integrations/openspec.d.ts.map +1 -1
- package/dist/domains/integrations/openspec.js +6 -6
- package/dist/domains/integrations/openspec.js.map +1 -1
- package/dist/domains/skill/find.d.ts.map +1 -1
- package/dist/domains/skill/find.js +1 -6
- package/dist/domains/skill/find.js.map +1 -1
- package/dist/domains/skill/managed-markdown.d.ts +14 -0
- package/dist/domains/skill/managed-markdown.d.ts.map +1 -0
- package/dist/domains/skill/managed-markdown.js +126 -0
- package/dist/domains/skill/managed-markdown.js.map +1 -0
- package/dist/domains/skill/platform-install.d.ts +4 -3
- package/dist/domains/skill/platform-install.d.ts.map +1 -1
- package/dist/domains/skill/platform-install.js +96 -26
- package/dist/domains/skill/platform-install.js.map +1 -1
- package/dist/domains/skill/project-instructions.d.ts +22 -0
- package/dist/domains/skill/project-instructions.d.ts.map +1 -0
- package/dist/domains/skill/project-instructions.js +61 -0
- package/dist/domains/skill/project-instructions.js.map +1 -0
- package/dist/domains/skill/uninstall.d.ts +3 -1
- package/dist/domains/skill/uninstall.d.ts.map +1 -1
- package/dist/domains/skill/uninstall.js +86 -39
- package/dist/domains/skill/uninstall.js.map +1 -1
- package/dist/platform/install/detect.d.ts +2 -1
- package/dist/platform/install/detect.d.ts.map +1 -1
- package/dist/platform/install/detect.js +10 -1
- package/dist/platform/install/detect.js.map +1 -1
- package/dist/platform/install/platforms.d.ts +5 -1
- package/dist/platform/install/platforms.d.ts.map +1 -1
- package/dist/platform/install/platforms.js +21 -4
- package/dist/platform/install/platforms.js.map +1 -1
- package/dist/platform/install/project-registry.d.ts +37 -0
- package/dist/platform/install/project-registry.d.ts.map +1 -0
- package/dist/platform/install/project-registry.js +205 -0
- package/dist/platform/install/project-registry.js.map +1 -0
- package/eval/.env +28 -0
- package/eval/.env.example +52 -0
- package/eval/CLAUDE.md +43 -0
- package/eval/README.md +538 -0
- package/eval/langsmith/.env.example +16 -0
- package/eval/langsmith/README.md +93 -0
- package/eval/langsmith/skills/README.md +5 -0
- package/eval/langsmith/tasks/README.md +5 -0
- package/eval/langsmith/tests/conftest.py +187 -0
- package/eval/langsmith/tests/tasks/test_tasks.py +287 -0
- package/eval/langsmith/treatments/README.md +5 -0
- package/eval/local/README.md +80 -0
- package/eval/local/regression_baseline.json +13 -0
- package/eval/local/report-style-demo-assets/quality_cost.en.svg +41 -0
- package/eval/local/report-style-demo-assets/quality_cost.zh.svg +41 -0
- package/eval/local/report-style-demo-assets/rubric_delta.en.svg +56 -0
- package/eval/local/report-style-demo-assets/rubric_delta.zh.svg +56 -0
- package/eval/local/report-style-demo-assets/task_outcomes.en.svg +46 -0
- package/eval/local/report-style-demo-assets/task_outcomes.zh.svg +46 -0
- package/eval/local/report-style-demo.html +867 -0
- package/eval/local/scripts/compare_baselines.py +1018 -0
- package/eval/local/scripts/generate_report_style_demo_charts.py +322 -0
- package/eval/local/scripts/regression_check.py +171 -0
- package/eval/local/scripts/render_readme_benchmark_figures.py +416 -0
- package/eval/local/scripts/rescore_rubric.py +120 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/SKILL.md +282 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/auto-transition.md +27 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/comet-yaml-fields.md +68 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/context-recovery.md +35 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/debug-gate.md +17 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/decision-point.md +20 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/dirty-worktree.md +59 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/file-structure.md +28 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/subagent-dispatch.md +113 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/rules/comet-phase-guard.md +125 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-archive.sh +311 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-env.sh +110 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-guard.sh +778 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-handoff.sh +390 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-hook-guard.sh +336 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-state.sh +1338 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-yaml-validate.sh +225 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039-archive/SKILL.md +100 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039-build/SKILL.md +304 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039-design/SKILL.md +263 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039-hotfix/SKILL.md +204 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039-open/SKILL.md +220 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039-tweak/SKILL.md +180 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039-verify/SKILL.md +234 -0
- package/eval/local/skills/benchmarks/040-beta/comet/SKILL.md +261 -0
- package/eval/local/skills/benchmarks/040-beta/comet/reference/auto-transition.md +27 -0
- package/eval/local/skills/benchmarks/040-beta/comet/reference/comet-yaml-fields.md +73 -0
- package/eval/local/skills/benchmarks/040-beta/comet/reference/context-recovery.md +45 -0
- package/eval/local/skills/benchmarks/040-beta/comet/reference/debug-gate.md +33 -0
- package/eval/local/skills/benchmarks/040-beta/comet/reference/decision-point.md +31 -0
- package/eval/local/skills/benchmarks/040-beta/comet/reference/dirty-worktree.md +59 -0
- package/eval/local/skills/benchmarks/040-beta/comet/reference/file-structure.md +28 -0
- package/eval/local/skills/benchmarks/040-beta/comet/reference/intent-frame.md +74 -0
- package/eval/local/skills/benchmarks/040-beta/comet/reference/scripts.md +69 -0
- package/eval/local/skills/benchmarks/040-beta/comet/reference/subagent-dispatch.md +169 -0
- package/eval/local/skills/benchmarks/040-beta/comet/rules/comet-phase-guard.md +130 -0
- package/eval/local/skills/benchmarks/040-beta/comet/runtime/classic/checks.yaml +6 -0
- package/eval/local/skills/benchmarks/040-beta/comet/runtime/classic/guardrails.yaml +13 -0
- package/eval/local/skills/benchmarks/040-beta/comet/runtime/classic/skill.yaml +122 -0
- package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-archive.mjs +9515 -0
- package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-env.mjs +15 -0
- package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-guard.mjs +10173 -0
- package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-handoff.mjs +9491 -0
- package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-hook-guard.mjs +9175 -0
- package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-intent.mjs +404 -0
- package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-state.mjs +9273 -0
- package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-yaml-validate.mjs +7627 -0
- package/eval/local/skills/benchmarks/040-beta/comet-archive/SKILL.md +110 -0
- package/eval/local/skills/benchmarks/040-beta/comet-build/SKILL.md +315 -0
- package/eval/local/skills/benchmarks/040-beta/comet-design/SKILL.md +260 -0
- package/eval/local/skills/benchmarks/040-beta/comet-hotfix/SKILL.md +190 -0
- package/eval/local/skills/benchmarks/040-beta/comet-open/SKILL.md +210 -0
- package/eval/local/skills/benchmarks/040-beta/comet-tweak/SKILL.md +183 -0
- package/eval/local/skills/benchmarks/040-beta/comet-verify/SKILL.md +230 -0
- package/eval/local/skills/benchmarks/dependency/claude-md/comet-workflow/CLAUDE.md +12 -0
- package/eval/local/skills/benchmarks/dependency/openspec/openspec-apply-change/SKILL.md +156 -0
- package/eval/local/skills/benchmarks/dependency/openspec/openspec-archive-change/SKILL.md +114 -0
- package/eval/local/skills/benchmarks/dependency/openspec/openspec-bulk-archive-change/SKILL.md +246 -0
- package/eval/local/skills/benchmarks/dependency/openspec/openspec-continue-change/SKILL.md +118 -0
- package/eval/local/skills/benchmarks/dependency/openspec/openspec-explore/SKILL.md +288 -0
- package/eval/local/skills/benchmarks/dependency/openspec/openspec-ff-change/SKILL.md +101 -0
- package/eval/local/skills/benchmarks/dependency/openspec/openspec-new-change/SKILL.md +74 -0
- package/eval/local/skills/benchmarks/dependency/openspec/openspec-onboard/SKILL.md +554 -0
- package/eval/local/skills/benchmarks/dependency/openspec/openspec-propose/SKILL.md +110 -0
- package/eval/local/skills/benchmarks/dependency/openspec/openspec-sync-specs/SKILL.md +138 -0
- package/eval/local/skills/benchmarks/dependency/openspec/openspec-verify-change/SKILL.md +168 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/SKILL.md +159 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/scripts/frame-template.html +213 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/scripts/helper.js +167 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/scripts/server.cjs +723 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/scripts/start-server.sh +209 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/scripts/stop-server.sh +120 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/spec-document-reviewer-prompt.md +49 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/visual-companion.md +291 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/dispatching-parallel-agents/SKILL.md +185 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/executing-plans/SKILL.md +70 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/finishing-a-development-branch/SKILL.md +241 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/receiving-code-review/SKILL.md +213 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/requesting-code-review/SKILL.md +103 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/requesting-code-review/code-reviewer.md +172 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/subagent-driven-development/SKILL.md +418 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/subagent-driven-development/implementer-prompt.md +139 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/subagent-driven-development/scripts/review-package +44 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/subagent-driven-development/scripts/sdd-workspace +22 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/subagent-driven-development/scripts/task-brief +40 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/subagent-driven-development/task-reviewer-prompt.md +188 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/CREATION-LOG.md +119 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/SKILL.md +296 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/condition-based-waiting.md +115 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/defense-in-depth.md +122 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/find-polluter.sh +63 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/root-cause-tracing.md +169 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/test-academic.md +14 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/test-pressure-1.md +58 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/test-pressure-2.md +68 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/test-pressure-3.md +69 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/test-driven-development/SKILL.md +371 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/test-driven-development/testing-anti-patterns.md +299 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/using-git-worktrees/SKILL.md +202 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/verification-before-completion/SKILL.md +139 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/writing-plans/SKILL.md +174 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/writing-plans/plan-document-reviewer-prompt.md +49 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/SKILL.md +689 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/anthropic-best-practices.md +1150 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/graphviz-conventions.dot +172 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/persuasion-principles.md +187 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/render-graphs.js +168 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/testing-skills-with-subagents.md +384 -0
- package/eval/local/tasks/authoring-skill-smoke/environment/Dockerfile +2 -0
- package/eval/local/tasks/authoring-skill-smoke/instruction.md +4 -0
- package/eval/local/tasks/authoring-skill-smoke/task.toml +21 -0
- package/eval/local/tasks/authoring-skill-smoke/validation/test_authoring_skill_smoke.py +54 -0
- package/eval/local/tasks/comet-agent-memory-routing/environment/Dockerfile +28 -0
- package/eval/local/tasks/comet-agent-memory-routing/environment/agent_system.py +40 -0
- package/eval/local/tasks/comet-agent-memory-routing/environment/test_agent_system.py +29 -0
- package/eval/local/tasks/comet-agent-memory-routing/instruction.md +20 -0
- package/eval/local/tasks/comet-agent-memory-routing/task.toml +24 -0
- package/eval/local/tasks/comet-agent-memory-routing/validation/test_agent_memory_routing.py +24 -0
- package/eval/local/tasks/comet-api-cache-ttl/environment/Dockerfile +39 -0
- package/eval/local/tasks/comet-api-cache-ttl/environment/cache.py +39 -0
- package/eval/local/tasks/comet-api-cache-ttl/environment/test_cache.py +81 -0
- package/eval/local/tasks/comet-api-cache-ttl/instruction.md +33 -0
- package/eval/local/tasks/comet-api-cache-ttl/task.toml +24 -0
- package/eval/local/tasks/comet-api-cache-ttl/validation/test_api_cache_ttl.py +58 -0
- package/eval/local/tasks/comet-cross-file-refactor/environment/Dockerfile +15 -0
- package/eval/local/tasks/comet-cross-file-refactor/environment/cli.py +13 -0
- package/eval/local/tasks/comet-cross-file-refactor/environment/counting.py +13 -0
- package/eval/local/tasks/comet-cross-file-refactor/environment/test_textkit.py +21 -0
- package/eval/local/tasks/comet-cross-file-refactor/instruction.md +16 -0
- package/eval/local/tasks/comet-cross-file-refactor/task.toml +20 -0
- package/eval/local/tasks/comet-cross-file-refactor/validation/test_cross_file_refactor.py +20 -0
- package/eval/local/tasks/comet-dependency-confusion/environment/Dockerfile +28 -0
- package/eval/local/tasks/comet-dependency-confusion/environment/app.py +8 -0
- package/eval/local/tasks/comet-dependency-confusion/environment/settings_loader.py +14 -0
- package/eval/local/tasks/comet-dependency-confusion/environment/test_settings_service.py +19 -0
- package/eval/local/tasks/comet-dependency-confusion/instruction.md +18 -0
- package/eval/local/tasks/comet-dependency-confusion/task.toml +24 -0
- package/eval/local/tasks/comet-dependency-confusion/validation/test_dependency_confusion.py +24 -0
- package/eval/local/tasks/comet-fix-median/environment/Dockerfile +50 -0
- package/eval/local/tasks/comet-fix-median/environment/stats.py +54 -0
- package/eval/local/tasks/comet-fix-median/environment/test_stats.py +68 -0
- package/eval/local/tasks/comet-fix-median/instruction.md +25 -0
- package/eval/local/tasks/comet-fix-median/task.toml +24 -0
- package/eval/local/tasks/comet-fix-median/validation/test_fix_median.py +60 -0
- package/eval/local/tasks/comet-framework-selection/environment/Dockerfile +28 -0
- package/eval/local/tasks/comet-framework-selection/environment/architecture.py +12 -0
- package/eval/local/tasks/comet-framework-selection/environment/test_architecture.py +30 -0
- package/eval/local/tasks/comet-framework-selection/instruction.md +22 -0
- package/eval/local/tasks/comet-framework-selection/task.toml +24 -0
- package/eval/local/tasks/comet-framework-selection/validation/test_framework_selection.py +24 -0
- package/eval/local/tasks/comet-full-workflow/environment/Dockerfile +53 -0
- package/eval/local/tasks/comet-full-workflow/environment/test_wordcount.py +29 -0
- package/eval/local/tasks/comet-full-workflow/environment/wordcount.py +40 -0
- package/eval/local/tasks/comet-full-workflow/instruction.md +18 -0
- package/eval/local/tasks/comet-full-workflow/task.toml +24 -0
- package/eval/local/tasks/comet-full-workflow/validation/test_full_workflow.py +171 -0
- package/eval/local/tasks/comet-graph-execution-review/environment/Dockerfile +28 -0
- package/eval/local/tasks/comet-graph-execution-review/environment/pipeline.py +19 -0
- package/eval/local/tasks/comet-graph-execution-review/environment/test_pipeline.py +53 -0
- package/eval/local/tasks/comet-graph-execution-review/instruction.md +21 -0
- package/eval/local/tasks/comet-graph-execution-review/task.toml +24 -0
- package/eval/local/tasks/comet-graph-execution-review/validation/test_graph_execution_review.py +24 -0
- package/eval/local/tasks/comet-human-approval-flow/environment/Dockerfile +15 -0
- package/eval/local/tasks/comet-human-approval-flow/environment/approvals.py +17 -0
- package/eval/local/tasks/comet-human-approval-flow/environment/test_approvals.py +30 -0
- package/eval/local/tasks/comet-human-approval-flow/instruction.md +15 -0
- package/eval/local/tasks/comet-human-approval-flow/task.toml +20 -0
- package/eval/local/tasks/comet-human-approval-flow/validation/test_human_approval_flow.py +20 -0
- package/eval/local/tasks/comet-layered-streaming-fix/environment/Dockerfile +20 -0
- package/eval/local/tasks/comet-layered-streaming-fix/environment/chat_app.py +19 -0
- package/eval/local/tasks/comet-layered-streaming-fix/environment/test_chat_app.py +15 -0
- package/eval/local/tasks/comet-layered-streaming-fix/instruction.md +14 -0
- package/eval/local/tasks/comet-layered-streaming-fix/task.toml +20 -0
- package/eval/local/tasks/comet-layered-streaming-fix/validation/test_layered_streaming_fix.py +20 -0
- package/eval/local/tasks/comet-noise-distractor/environment/Dockerfile +15 -0
- package/eval/local/tasks/comet-noise-distractor/environment/distractor.md +4 -0
- package/eval/local/tasks/comet-noise-distractor/environment/invoice.py +5 -0
- package/eval/local/tasks/comet-noise-distractor/environment/test_invoice.py +18 -0
- package/eval/local/tasks/comet-noise-distractor/instruction.md +17 -0
- package/eval/local/tasks/comet-noise-distractor/task.toml +20 -0
- package/eval/local/tasks/comet-noise-distractor/validation/test_noise_distractor.py +20 -0
- package/eval/local/tasks/comet-observability-env-template/environment/Dockerfile +15 -0
- package/eval/local/tasks/comet-observability-env-template/environment/env_writer.py +17 -0
- package/eval/local/tasks/comet-observability-env-template/environment/test_env_writer.py +17 -0
- package/eval/local/tasks/comet-observability-env-template/instruction.md +20 -0
- package/eval/local/tasks/comet-observability-env-template/task.toml +20 -0
- package/eval/local/tasks/comet-observability-env-template/validation/test_observability_env_template.py +46 -0
- package/eval/local/tasks/comet-perf-dedupe/environment/Dockerfile +39 -0
- package/eval/local/tasks/comet-perf-dedupe/environment/dedupe.py +22 -0
- package/eval/local/tasks/comet-perf-dedupe/environment/test_dedupe.py +43 -0
- package/eval/local/tasks/comet-perf-dedupe/instruction.md +25 -0
- package/eval/local/tasks/comet-perf-dedupe/task.toml +24 -0
- package/eval/local/tasks/comet-perf-dedupe/validation/test_perf_dedupe.py +40 -0
- package/eval/local/tasks/comet-persistence-threading/environment/Dockerfile +15 -0
- package/eval/local/tasks/comet-persistence-threading/environment/session_store.py +15 -0
- package/eval/local/tasks/comet-persistence-threading/environment/test_session_store.py +21 -0
- package/eval/local/tasks/comet-persistence-threading/instruction.md +14 -0
- package/eval/local/tasks/comet-persistence-threading/task.toml +20 -0
- package/eval/local/tasks/comet-persistence-threading/validation/test_persistence_threading.py +20 -0
- package/eval/local/tasks/comet-refactor-counter/environment/Dockerfile +39 -0
- package/eval/local/tasks/comet-refactor-counter/environment/test_text_processor.py +57 -0
- package/eval/local/tasks/comet-refactor-counter/environment/text_processor.py +49 -0
- package/eval/local/tasks/comet-refactor-counter/instruction.md +31 -0
- package/eval/local/tasks/comet-refactor-counter/task.toml +24 -0
- package/eval/local/tasks/comet-refactor-counter/validation/test_refactor_counter.py +75 -0
- package/eval/local/tasks/comet-robust-config/environment/Dockerfile +39 -0
- package/eval/local/tasks/comet-robust-config/environment/config_loader.py +56 -0
- package/eval/local/tasks/comet-robust-config/environment/test_config_loader.py +72 -0
- package/eval/local/tasks/comet-robust-config/instruction.md +30 -0
- package/eval/local/tasks/comet-robust-config/task.toml +24 -0
- package/eval/local/tasks/comet-robust-config/validation/test_robust_config.py +37 -0
- package/eval/local/tasks/generic-skill-smoke/environment/Dockerfile +2 -0
- package/eval/local/tasks/generic-skill-smoke/instruction.md +7 -0
- package/eval/local/tasks/generic-skill-smoke/task.toml +21 -0
- package/eval/local/tasks/generic-skill-smoke/validation/test_generic_skill_smoke.py +36 -0
- package/eval/local/tasks/index.yaml +155 -0
- package/eval/local/tasks/workflow-overlay-contract/environment/Dockerfile +5 -0
- package/eval/local/tasks/workflow-overlay-contract/instruction.md +5 -0
- package/eval/local/tasks/workflow-overlay-contract/task.toml +21 -0
- package/eval/local/tasks/workflow-overlay-contract/validation/test_workflow_overlay_contract.py +235 -0
- package/eval/local/tasks/workflow-route-conformance/environment/Dockerfile +2 -0
- package/eval/local/tasks/workflow-route-conformance/instruction.md +5 -0
- package/eval/local/tasks/workflow-route-conformance/task.toml +21 -0
- package/eval/local/tasks/workflow-route-conformance/validation/test_workflow_route_conformance.py +84 -0
- package/eval/local/tests/conftest.py +1148 -0
- package/eval/local/tests/scaffold/test_attribution.py +48 -0
- package/eval/local/tests/scaffold/test_compare_baselines.py +999 -0
- package/eval/local/tests/scaffold/test_conftest_helpers.py +534 -0
- package/eval/local/tests/scaffold/test_evidence.py +15 -0
- package/eval/local/tests/scaffold/test_langsmith_conftest.py +244 -0
- package/eval/local/tests/scaffold/test_logging.py +243 -0
- package/eval/local/tests/scaffold/test_manifests.py +132 -0
- package/eval/local/tests/scaffold/test_profiles.py +866 -0
- package/eval/local/tests/scaffold/test_regression_check.py +19 -0
- package/eval/local/tests/scaffold/test_report_style_demo_charts.py +45 -0
- package/eval/local/tests/scaffold/test_sample_quality.py +211 -0
- package/eval/local/tests/scaffold/test_tasks.py +168 -0
- package/eval/local/tests/scaffold/test_treatments.py +244 -0
- package/eval/local/tests/scaffold/test_utils.py +139 -0
- package/eval/local/tests/tasks/test_tasks.py +445 -0
- package/eval/local/tests/tasks/test_validation_scripts.py +143 -0
- package/eval/local/treatments/comet/comet_full_039.yaml +141 -0
- package/eval/local/treatments/comet/comet_full_040_beta.yaml +140 -0
- package/eval/local/treatments/common/control.yaml +4 -0
- package/eval/pyproject.toml +41 -0
- package/eval/report-html-config.json +6 -0
- package/eval/scaffold/__init__.py +67 -0
- package/eval/scaffold/python/__init__.py +95 -0
- package/eval/scaffold/python/attribution.py +43 -0
- package/eval/scaffold/python/evidence.py +38 -0
- package/eval/scaffold/python/external_data_handler.py +18 -0
- package/eval/scaffold/python/generic_llm_judge.py +235 -0
- package/eval/scaffold/python/judge_config.py +168 -0
- package/eval/scaffold/python/llm_judge.py +191 -0
- package/eval/scaffold/python/logging.py +705 -0
- package/eval/scaffold/python/manifests.py +129 -0
- package/eval/scaffold/python/paper_charts.py +25 -0
- package/eval/scaffold/python/pass_at_k.py +107 -0
- package/eval/scaffold/python/paths.py +56 -0
- package/eval/scaffold/python/profiles.py +127 -0
- package/eval/scaffold/python/report_outputs.py +1391 -0
- package/eval/scaffold/python/sample_quality.py +339 -0
- package/eval/scaffold/python/schema.py +39 -0
- package/eval/scaffold/python/skill_parser.py +469 -0
- package/eval/scaffold/python/tasks.py +330 -0
- package/eval/scaffold/python/treatments.py +271 -0
- package/eval/scaffold/python/utils.py +366 -0
- package/eval/scaffold/python/validation/__init__.py +21 -0
- package/eval/scaffold/python/validation/authoring_rubric.py +326 -0
- package/eval/scaffold/python/validation/comet_workflow.py +187 -0
- package/eval/scaffold/python/validation/core.py +264 -0
- package/eval/scaffold/python/validation/dataset.py +337 -0
- package/eval/scaffold/python/validation/docker.py +106 -0
- package/eval/scaffold/python/validation/evaluator.py +549 -0
- package/eval/scaffold/python/validation/generic_rubric.py +169 -0
- package/eval/scaffold/python/validation/rubric.py +740 -0
- package/eval/scaffold/python/validation/runner.py +237 -0
- package/eval/scaffold/python/validation/scripts.py +58 -0
- package/eval/scaffold/python/validation/tracing.py +313 -0
- package/eval/scaffold/shell/common.sh +126 -0
- package/eval/scaffold/shell/docker.sh +482 -0
- package/eval/scaffold/shell/run-claude-loop.sh +181 -0
- package/eval/scaffold/shell/setup.sh +259 -0
- package/eval/simulator-instruction.md +9 -0
- package/package.json +24 -2
|
@@ -0,0 +1,740 @@
|
|
|
1
|
+
"""Comet skill rubric validator.
|
|
2
|
+
|
|
3
|
+
Scores a comet workflow run across nine dimensions by analysing the
|
|
4
|
+
captured Claude events and the artifacts left in the test directory. Every
|
|
5
|
+
dimension emits a single check message of the form::
|
|
6
|
+
|
|
7
|
+
[RUBRIC] <dim>: <score> - <reason>
|
|
8
|
+
|
|
9
|
+
where ``<score>`` is 0.0-1.0 (pass rate of binary checks) and ``<reason>`` is a
|
|
10
|
+
short human-readable justification. The logging layer parses these messages to
|
|
11
|
+
build per-dimension columns in the experiment summary.
|
|
12
|
+
|
|
13
|
+
Scoring methodology (aligned with industry best practices from Galileo, Hebbia,
|
|
14
|
+
τ-bench):
|
|
15
|
+
- Each dimension contains N binary pass/fail checks
|
|
16
|
+
- Dimension score = passed / total checks (0.0-1.0)
|
|
17
|
+
- Final weighted score = Σ(dimension_score × weight) / Σ(weight)
|
|
18
|
+
- Weights reflect dimension importance to workflow quality
|
|
19
|
+
|
|
20
|
+
Dimensions
|
|
21
|
+
----------
|
|
22
|
+
1. main_flow - how many workflow-specific phases left evidence (weight 1.5)
|
|
23
|
+
2. gate_guard - whether comet-guard / comet-state transition / --apply were used (weight 1.5)
|
|
24
|
+
3. skill_invocation - whether the comet entry, nested stage skills, and dependency skills were invoked (weight 1.0)
|
|
25
|
+
4. spec_drift - whether delta specs created during build were reconciled before archive (weight 1.0)
|
|
26
|
+
5. business_completion - fraction of business validators that passed (weight 2.0)
|
|
27
|
+
6. workflow_completion - fraction of workflow validators that passed (weight 1.0)
|
|
28
|
+
7. efficiency - normalised cost (turns / tool calls / duration; lower is better) (weight 0.8)
|
|
29
|
+
8. decision_point_compliance - whether agent paused at blocking decision points instead of auto-deciding (weight 1.0)
|
|
30
|
+
9. artifact_quality - whether proposal/design/tasks/test artefacts have substantive content (weight 1.2)
|
|
31
|
+
10. recovery_resilience - whether the agent preserved and restored state correctly across interruptions (weight 1.0)
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
import json
|
|
37
|
+
import os
|
|
38
|
+
import re
|
|
39
|
+
from pathlib import Path
|
|
40
|
+
from typing import Any
|
|
41
|
+
|
|
42
|
+
from scaffold.python.validation.core import ValidatorFn
|
|
43
|
+
|
|
44
|
+
# All nine rubric dimensions, in display order.
|
|
45
|
+
RUBRIC_DIMENSIONS = (
|
|
46
|
+
"main_flow",
|
|
47
|
+
"gate_guard",
|
|
48
|
+
"skill_invocation",
|
|
49
|
+
"spec_drift",
|
|
50
|
+
"business_completion",
|
|
51
|
+
"workflow_completion",
|
|
52
|
+
"efficiency",
|
|
53
|
+
"decision_point_compliance",
|
|
54
|
+
"artifact_quality",
|
|
55
|
+
"recovery_resilience",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Dimension weights: critical dimensions get higher relative weight.
|
|
59
|
+
# Raw weights need not sum to 1.0; they are normalized during aggregation.
|
|
60
|
+
_DIMENSION_WEIGHTS: dict[str, float] = {
|
|
61
|
+
"business_completion": 2.0, # Must complete the user-visible task
|
|
62
|
+
"workflow_completion": 1.0, # Workflow-specific validator completion
|
|
63
|
+
"main_flow": 1.5, # Core 5-phase workflow
|
|
64
|
+
"gate_guard": 1.5, # Quality gate enforcement
|
|
65
|
+
"artifact_quality": 1.2, # Substantive deliverables
|
|
66
|
+
"skill_invocation": 1.0, # Correct skill ordering
|
|
67
|
+
"spec_drift": 1.0, # Spec reconciliation
|
|
68
|
+
"decision_point_compliance": 1.0, # User confirmation at gates
|
|
69
|
+
"recovery_resilience": 1.0, # State preservation
|
|
70
|
+
"efficiency": 0.8, # Nice-to-have, not critical
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# Comet stage skills and dependency skills that should be observable as real
|
|
74
|
+
# Skill tool invocations, not inferred from generated artifacts.
|
|
75
|
+
_COMET_STAGE_SKILLS = (
|
|
76
|
+
"comet-open",
|
|
77
|
+
"comet-design",
|
|
78
|
+
"comet-build",
|
|
79
|
+
"comet-verify",
|
|
80
|
+
"comet-archive",
|
|
81
|
+
"comet-hotfix",
|
|
82
|
+
"comet-tweak",
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
_SUPERPOWERS_DEPENDENCY_SKILLS = (
|
|
86
|
+
"brainstorming",
|
|
87
|
+
"dispatching-parallel-agents",
|
|
88
|
+
"executing-plans",
|
|
89
|
+
"finishing-a-development-branch",
|
|
90
|
+
"requesting-code-review",
|
|
91
|
+
"subagent-driven-development",
|
|
92
|
+
"systematic-debugging",
|
|
93
|
+
"test-driven-development",
|
|
94
|
+
"using-git-worktrees",
|
|
95
|
+
"verification-before-completion",
|
|
96
|
+
"writing-plans",
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Decision-point state mutations that must be preceded by an explicit user
|
|
100
|
+
# confirmation (per comet/reference/decision-point.md). If any of these appear
|
|
101
|
+
# in commands_run without a nearby "ask"/"confirm" tool signal it is a
|
|
102
|
+
# compliance smell.
|
|
103
|
+
_DECISION_MUTATIONS = (
|
|
104
|
+
re.compile(r"transition\s+\S+\s+(?:open-complete|design-complete|build-complete|verify-pass|verify-fail|preset-escalate)"),
|
|
105
|
+
re.compile(r"set\s+\S+\s+(?:build_mode|isolation|tdd_mode|review_mode|build_pause)\s"),
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Files whose creation signals a given phase produced artefacts.
|
|
109
|
+
# Comet writes to several locations; cover both the canonical openspec/changes
|
|
110
|
+
# layout and the docs/superpowers/ + openspec/archive/ layouts actually used.
|
|
111
|
+
_PHASE_SIGNALS = {
|
|
112
|
+
"open": (
|
|
113
|
+
re.compile(r"openspec/changes/[^/]+/(?:proposal|tasks)\.md"),
|
|
114
|
+
re.compile(r"openspec/changes/archive/[^/]+/(?:proposal|tasks)\.md"),
|
|
115
|
+
),
|
|
116
|
+
"design": (
|
|
117
|
+
re.compile(r"docs/superpowers/specs/.+\.md"),
|
|
118
|
+
re.compile(r"openspec/changes/(?:archive/)?[^/]+/design\.md"),
|
|
119
|
+
),
|
|
120
|
+
"build": (
|
|
121
|
+
re.compile(r"openspec/changes/(?:archive/)?[^/]+/plan\.md"),
|
|
122
|
+
re.compile(r"docs/superpowers/plans/.+\.md"),
|
|
123
|
+
re.compile(r"openspec/changes/(?:archive/)?[^/]+/\.comet/"),
|
|
124
|
+
),
|
|
125
|
+
"verify": (
|
|
126
|
+
re.compile(r"openspec/changes/(?:archive/)?[^/]+/verification\.md"),
|
|
127
|
+
re.compile(r"docs/superpowers/reports/.+\.md"),
|
|
128
|
+
),
|
|
129
|
+
"archive": (
|
|
130
|
+
re.compile(r"openspec/changes/archive/"),
|
|
131
|
+
re.compile(r"openspec/archive/"),
|
|
132
|
+
),
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
# Substantive-content keywords for the design doc (brainstorming depth signals).
|
|
136
|
+
_DESIGN_DEPTH_KEYWORDS = (
|
|
137
|
+
"tradeoff",
|
|
138
|
+
"alternative",
|
|
139
|
+
"risk",
|
|
140
|
+
"consider",
|
|
141
|
+
"option",
|
|
142
|
+
"权衡",
|
|
143
|
+
"替代",
|
|
144
|
+
"风险",
|
|
145
|
+
"考虑",
|
|
146
|
+
"方案",
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
_WORKFLOW_PHASES = {
|
|
150
|
+
"full": ("open", "design", "build", "verify", "archive"),
|
|
151
|
+
"hotfix": ("open", "build", "verify", "archive"),
|
|
152
|
+
"tweak": ("open", "build", "verify", "archive"),
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
_PRESET_DECISION_MUTATIONS = (
|
|
156
|
+
re.compile(r"transition\s+\S+\s+(?:verify-fail|preset-escalate|archive-reopen)"),
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _fmt(dim: str, score: float, reason: str) -> str:
|
|
161
|
+
return f"[RUBRIC] {dim}: {score:.2f} - {reason}"
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _fmt_na(dim: str, reason: str) -> str:
|
|
165
|
+
return f"[RUBRIC] {dim}: N/A - {reason}"
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _fmt_weighted(score: float) -> str:
|
|
169
|
+
return f"[RUBRIC] weighted_score: {score:.2f}"
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _join_commands(events: dict[str, Any]) -> str:
|
|
173
|
+
return "\n".join(events.get("commands_run", []) or [])
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _binary_score(checks: list[bool]) -> tuple[float, str]:
|
|
177
|
+
"""Convert a list of binary checks to a pass-rate score.
|
|
178
|
+
|
|
179
|
+
Returns (pass_rate, summary) where pass_rate = passed / total.
|
|
180
|
+
"""
|
|
181
|
+
total = len(checks)
|
|
182
|
+
if total == 0:
|
|
183
|
+
return 0.0, "no checks"
|
|
184
|
+
passed = sum(1 for c in checks if c)
|
|
185
|
+
return passed / total, f"{passed}/{total} passed"
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def _find_change_dir(test_dir: Path) -> Path | None:
|
|
189
|
+
"""Find the comet change directory (active or archived)."""
|
|
190
|
+
changes_root = test_dir / "openspec" / "changes"
|
|
191
|
+
if not changes_root.exists():
|
|
192
|
+
return None
|
|
193
|
+
|
|
194
|
+
for d in changes_root.iterdir():
|
|
195
|
+
if not d.is_dir():
|
|
196
|
+
continue
|
|
197
|
+
if d.name == "archive":
|
|
198
|
+
for sub in d.iterdir():
|
|
199
|
+
if sub.is_dir() and (
|
|
200
|
+
(sub / ".comet.yaml").exists()
|
|
201
|
+
or (sub / ".comet").exists()
|
|
202
|
+
or (sub / "proposal.md").exists()
|
|
203
|
+
or (sub / "tasks.md").exists()
|
|
204
|
+
):
|
|
205
|
+
return sub
|
|
206
|
+
elif (
|
|
207
|
+
(d / ".comet.yaml").exists()
|
|
208
|
+
or (d / ".comet").exists()
|
|
209
|
+
or (d / "proposal.md").exists()
|
|
210
|
+
or (d / "tasks.md").exists()
|
|
211
|
+
):
|
|
212
|
+
return d
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _detect_workflow_kind(events: dict[str, Any], test_dir: Path) -> str:
|
|
217
|
+
"""Detect full/hotfix/tweak from observed Skill calls or .comet.yaml."""
|
|
218
|
+
invoked = events.get("skills_invoked", []) or []
|
|
219
|
+
if "comet-hotfix" in invoked:
|
|
220
|
+
return "hotfix"
|
|
221
|
+
if "comet-tweak" in invoked:
|
|
222
|
+
return "tweak"
|
|
223
|
+
|
|
224
|
+
cdir = _find_change_dir(test_dir)
|
|
225
|
+
yaml_path = cdir / ".comet.yaml" if cdir else None
|
|
226
|
+
if yaml_path and yaml_path.exists():
|
|
227
|
+
try:
|
|
228
|
+
yaml_content = yaml_path.read_text(errors="ignore")
|
|
229
|
+
match = re.search(
|
|
230
|
+
r"(?m)^\s*(?:workflow|classic_profile):\s*(full|hotfix|tweak)\b",
|
|
231
|
+
yaml_content,
|
|
232
|
+
)
|
|
233
|
+
if match:
|
|
234
|
+
return match.group(1)
|
|
235
|
+
except Exception:
|
|
236
|
+
pass
|
|
237
|
+
|
|
238
|
+
state_events_path = cdir / ".comet" / "state-events.jsonl" if cdir else None
|
|
239
|
+
if state_events_path and state_events_path.exists():
|
|
240
|
+
try:
|
|
241
|
+
state_events = state_events_path.read_text(errors="ignore")
|
|
242
|
+
match = re.search(r'"workflow"\s*:\s*"(full|hotfix|tweak)"', state_events)
|
|
243
|
+
if match:
|
|
244
|
+
return match.group(1)
|
|
245
|
+
except Exception:
|
|
246
|
+
pass
|
|
247
|
+
|
|
248
|
+
return "full"
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _artifact_haystack(events: dict[str, Any], test_dir: Path) -> str:
|
|
252
|
+
files = list(events.get("files_created", [])) + list(events.get("files_modified", []))
|
|
253
|
+
try:
|
|
254
|
+
on_disk = [
|
|
255
|
+
str(p.relative_to(test_dir)).replace("\\", "/")
|
|
256
|
+
for p in test_dir.rglob("*")
|
|
257
|
+
if p.is_file()
|
|
258
|
+
]
|
|
259
|
+
except Exception:
|
|
260
|
+
on_disk = []
|
|
261
|
+
return "\n".join(files + on_disk)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _score_main_flow(
|
|
265
|
+
events: dict[str, Any], test_dir: Path, workflow: str
|
|
266
|
+
) -> tuple[float, str]:
|
|
267
|
+
"""Check which workflow-specific phases left evidence."""
|
|
268
|
+
haystack = _artifact_haystack(events, test_dir)
|
|
269
|
+
|
|
270
|
+
expected_phases = _WORKFLOW_PHASES[workflow]
|
|
271
|
+
phase_checks: list[bool] = []
|
|
272
|
+
phases_reached: list[str] = []
|
|
273
|
+
for phase in expected_phases:
|
|
274
|
+
patterns = _PHASE_SIGNALS[phase]
|
|
275
|
+
found = any(p.search(haystack) for p in patterns)
|
|
276
|
+
phase_checks.append(found)
|
|
277
|
+
if found:
|
|
278
|
+
phases_reached.append(phase)
|
|
279
|
+
|
|
280
|
+
score, _ = _binary_score(phase_checks)
|
|
281
|
+
return (
|
|
282
|
+
score,
|
|
283
|
+
"workflow={workflow} {reached}/{total} phases ({phases})".format(
|
|
284
|
+
workflow=workflow,
|
|
285
|
+
reached=len(phases_reached),
|
|
286
|
+
total=len(expected_phases),
|
|
287
|
+
phases=", ".join(phases_reached) if phases_reached else "none",
|
|
288
|
+
),
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def _score_gate_guard(events: dict[str, Any]) -> tuple[float, str]:
|
|
293
|
+
"""Check guard/state/apply usage. Binary checks: guard used, transitions used, apply used."""
|
|
294
|
+
cmds = _join_commands(events)
|
|
295
|
+
if not cmds:
|
|
296
|
+
return 0.0, "no commands captured"
|
|
297
|
+
|
|
298
|
+
guard_used = bool(re.search(r"comet-guard", cmds))
|
|
299
|
+
transition_used = bool(re.search(r"transition\s+\S+\s+(?:open|design|build|verify|archive)", cmds))
|
|
300
|
+
apply_used = bool(re.search(r"--apply", cmds))
|
|
301
|
+
|
|
302
|
+
checks = [guard_used, transition_used, apply_used]
|
|
303
|
+
score, _ = _binary_score(checks)
|
|
304
|
+
|
|
305
|
+
guard_hits = len(re.findall(r"comet-guard", cmds))
|
|
306
|
+
transition_hits = len(re.findall(r"transition\s+\S+\s+(?:open|design|build|verify|archive)", cmds))
|
|
307
|
+
apply_hits = len(re.findall(r"--apply", cmds))
|
|
308
|
+
|
|
309
|
+
return score, f"guard={guard_hits} transition={transition_hits} apply={apply_hits}"
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def _score_skill_invocation(events: dict[str, Any]) -> tuple[float, str]:
|
|
313
|
+
"""Check real Skill tool usage across Comet stage and dependency layers."""
|
|
314
|
+
invoked = events.get("skills_invoked", []) or []
|
|
315
|
+
if not invoked:
|
|
316
|
+
return 0.0, "no skills invoked"
|
|
317
|
+
|
|
318
|
+
comet_entry = "comet" in invoked
|
|
319
|
+
comet_stage_invoked = [skill for skill in invoked if skill in _COMET_STAGE_SKILLS]
|
|
320
|
+
openspec_invoked = [skill for skill in invoked if skill.startswith("openspec-")]
|
|
321
|
+
superpowers_invoked = [
|
|
322
|
+
skill for skill in invoked if skill in _SUPERPOWERS_DEPENDENCY_SKILLS
|
|
323
|
+
]
|
|
324
|
+
if comet_stage_invoked:
|
|
325
|
+
first_stage_index = min(invoked.index(skill) for skill in comet_stage_invoked)
|
|
326
|
+
else:
|
|
327
|
+
first_stage_index = -1
|
|
328
|
+
entry_before_stage = comet_entry and first_stage_index > invoked.index("comet")
|
|
329
|
+
|
|
330
|
+
checks = [
|
|
331
|
+
comet_entry,
|
|
332
|
+
bool(comet_stage_invoked),
|
|
333
|
+
bool(openspec_invoked),
|
|
334
|
+
bool(superpowers_invoked),
|
|
335
|
+
entry_before_stage,
|
|
336
|
+
]
|
|
337
|
+
score, _ = _binary_score(checks)
|
|
338
|
+
|
|
339
|
+
return (
|
|
340
|
+
score,
|
|
341
|
+
"entry={entry} comet_stage={stage} openspec={openspec} "
|
|
342
|
+
"superpowers={superpowers} order={order}".format(
|
|
343
|
+
entry="comet" if comet_entry else "missing",
|
|
344
|
+
stage=", ".join(comet_stage_invoked) if comet_stage_invoked else "missing",
|
|
345
|
+
openspec=", ".join(openspec_invoked) if openspec_invoked else "missing",
|
|
346
|
+
superpowers=", ".join(superpowers_invoked) if superpowers_invoked else "missing",
|
|
347
|
+
order="entry-before-stage" if entry_before_stage else "missing",
|
|
348
|
+
),
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def _score_spec_drift(events: dict[str, Any], test_dir: Path) -> tuple[float, str]:
|
|
353
|
+
"""Check delta spec reconciliation. Binary: spec written AND synced."""
|
|
354
|
+
cmds = _join_commands(events)
|
|
355
|
+
files = list(events.get("files_modified", [])) + list(events.get("files_created", []))
|
|
356
|
+
|
|
357
|
+
spec_touched = any("specs/" in f and "openspec/changes" in f for f in files)
|
|
358
|
+
spec_synced = bool(re.search(r"openspec\s+(?:sync|archive)", cmds))
|
|
359
|
+
|
|
360
|
+
if not spec_touched:
|
|
361
|
+
# No delta spec needed — neutral pass (not applicable)
|
|
362
|
+
return 1.0, "no delta spec needed (n/a)"
|
|
363
|
+
|
|
364
|
+
# Binary: was it synced?
|
|
365
|
+
checks = [spec_touched, spec_synced]
|
|
366
|
+
score, _ = _binary_score(checks)
|
|
367
|
+
return score, f"spec_written={spec_touched} spec_synced={spec_synced}"
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def _score_completion(outputs: dict[str, Any], key: str = "completion") -> tuple[float, str]:
|
|
371
|
+
"""Check validator pass rate. Already a ratio (0.0-1.0)."""
|
|
372
|
+
checks = outputs.get(key) or {}
|
|
373
|
+
passed = checks.get("passed", [])
|
|
374
|
+
failed = checks.get("failed", [])
|
|
375
|
+
total = len(passed) + len(failed)
|
|
376
|
+
if total == 0:
|
|
377
|
+
return 0.0, "no baseline checks ran"
|
|
378
|
+
ratio = len(passed) / total
|
|
379
|
+
return float(ratio), f"{len(passed)}/{total} baseline checks passed"
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def _score_efficiency(events: dict[str, Any]) -> tuple[float, str]:
|
|
383
|
+
"""Check efficiency via binary thresholds. Each metric is pass/fail at threshold."""
|
|
384
|
+
turns = events.get("num_turns") or 0
|
|
385
|
+
tool_calls = len(events.get("tool_calls", []))
|
|
386
|
+
duration = events.get("duration_seconds") or 0
|
|
387
|
+
|
|
388
|
+
# Binary thresholds: "good enough" vs "too expensive"
|
|
389
|
+
turns_ok = turns <= 80
|
|
390
|
+
tools_ok = tool_calls <= 150
|
|
391
|
+
duration_ok = duration <= 600
|
|
392
|
+
|
|
393
|
+
checks = [turns_ok, tools_ok, duration_ok]
|
|
394
|
+
score, _ = _binary_score(checks)
|
|
395
|
+
return score, f"turns={turns} tools={tool_calls} dur={duration:.0f}s"
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def _score_decision_point_compliance(
|
|
399
|
+
events: dict[str, Any], workflow: str
|
|
400
|
+
) -> tuple[float, str]:
|
|
401
|
+
"""Check if agent asked user at decision points. Binary: ratio >= 0.5."""
|
|
402
|
+
cmds = _join_commands(events)
|
|
403
|
+
tool_calls = events.get("tool_calls", []) or []
|
|
404
|
+
|
|
405
|
+
if not cmds:
|
|
406
|
+
return 0.0, "no commands captured"
|
|
407
|
+
|
|
408
|
+
ask_signals = sum(
|
|
409
|
+
1
|
|
410
|
+
for tc in tool_calls
|
|
411
|
+
if tc.get("tool") in {"AskUserQuestion", "ask_user", "AskFollowUpQuestion"}
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
mutation_patterns = (
|
|
415
|
+
_DECISION_MUTATIONS if workflow == "full" else _PRESET_DECISION_MUTATIONS
|
|
416
|
+
)
|
|
417
|
+
mutations = sum(len(rx.findall(cmds)) for rx in mutation_patterns)
|
|
418
|
+
|
|
419
|
+
if mutations == 0:
|
|
420
|
+
# No decision points reached — neutral pass
|
|
421
|
+
return 1.0, f"no {workflow} decision mutations observed"
|
|
422
|
+
|
|
423
|
+
# Binary: did the agent ask at least once per mutation?
|
|
424
|
+
ratio = ask_signals / max(mutations, 1)
|
|
425
|
+
checks = [ratio >= 0.5]
|
|
426
|
+
score, _ = _binary_score(checks)
|
|
427
|
+
return (
|
|
428
|
+
score,
|
|
429
|
+
f"workflow={workflow} {ask_signals} asks for {mutations} mutations "
|
|
430
|
+
f"(ratio={ratio:.2f})",
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def _score_artifact_quality(test_dir: Path, workflow: str) -> tuple[float, str]:
|
|
435
|
+
"""Check artifact quality via binary checks per artifact type."""
|
|
436
|
+
changes_root = test_dir / "openspec" / "changes"
|
|
437
|
+
change_dirs: list[Path] = []
|
|
438
|
+
if changes_root.exists():
|
|
439
|
+
for d in changes_root.iterdir():
|
|
440
|
+
if not d.is_dir():
|
|
441
|
+
continue
|
|
442
|
+
if d.name == "archive":
|
|
443
|
+
change_dirs.extend(s for s in d.iterdir() if s.is_dir())
|
|
444
|
+
elif (d / "proposal.md").exists() or (d / "tasks.md").exists():
|
|
445
|
+
change_dirs.append(d)
|
|
446
|
+
|
|
447
|
+
if not change_dirs:
|
|
448
|
+
return 0.0, "no openspec change directory"
|
|
449
|
+
|
|
450
|
+
cdir = change_dirs[0]
|
|
451
|
+
checks: list[bool] = []
|
|
452
|
+
notes: list[str] = []
|
|
453
|
+
|
|
454
|
+
# Check 1: Proposal has substance (>= 10 lines)
|
|
455
|
+
prop = cdir / "proposal.md"
|
|
456
|
+
prop_text = prop.read_text(errors="ignore") if prop.exists() else ""
|
|
457
|
+
prop_ok = prop_text.count("\n") >= 10
|
|
458
|
+
checks.append(prop_ok)
|
|
459
|
+
notes.append(f"workflow={workflow} proposal={'ok' if prop_ok else 'stub'}")
|
|
460
|
+
|
|
461
|
+
# Check 2: Design doc exists with depth keywords
|
|
462
|
+
design = cdir / "design.md"
|
|
463
|
+
if workflow == "full":
|
|
464
|
+
dtext = design.read_text(errors="ignore").lower() if design.exists() else ""
|
|
465
|
+
depth_hits = sum(1 for kw in _DESIGN_DEPTH_KEYWORDS if kw in dtext)
|
|
466
|
+
design_ok = design.exists() and depth_hits >= 2
|
|
467
|
+
checks.append(design_ok)
|
|
468
|
+
notes.append(f"design={'deep' if design_ok else 'shallow'}")
|
|
469
|
+
elif design.exists():
|
|
470
|
+
notes.append("design=preset-optional")
|
|
471
|
+
|
|
472
|
+
# Check 3: Tasks has checkboxes
|
|
473
|
+
tasks = cdir / "tasks.md"
|
|
474
|
+
if tasks.exists():
|
|
475
|
+
ttext = tasks.read_text(errors="ignore")
|
|
476
|
+
checkboxes = len(re.findall(r"- \[[ x]\]", ttext))
|
|
477
|
+
min_tasks = 3 if workflow == "full" else 1
|
|
478
|
+
tasks_ok = checkboxes >= min_tasks
|
|
479
|
+
checks.append(tasks_ok)
|
|
480
|
+
notes.append(f"tasks={checkboxes} boxes")
|
|
481
|
+
|
|
482
|
+
# Check 4: Tests have assertions
|
|
483
|
+
test_files = list(test_dir.glob("test_*.py")) + list(test_dir.glob("**/test_*.py"))
|
|
484
|
+
test_has_assert = False
|
|
485
|
+
for tf in test_files:
|
|
486
|
+
try:
|
|
487
|
+
if "assert" in tf.read_text(errors="ignore").lower():
|
|
488
|
+
test_has_assert = True
|
|
489
|
+
break
|
|
490
|
+
except Exception:
|
|
491
|
+
continue
|
|
492
|
+
if workflow in {"full", "hotfix"} or test_files:
|
|
493
|
+
checks.append(test_has_assert)
|
|
494
|
+
notes.append(f"tests={'w/ assert' if test_has_assert else 'no assert'}")
|
|
495
|
+
else:
|
|
496
|
+
notes.append("tests=n/a")
|
|
497
|
+
|
|
498
|
+
score, _ = _binary_score(checks)
|
|
499
|
+
return score, "; ".join(notes)
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
def _score_recovery_resilience(
|
|
503
|
+
events: dict[str, Any], test_dir: Path, workflow: str
|
|
504
|
+
) -> tuple[float, str]:
|
|
505
|
+
"""Check recovery artifacts via binary checks."""
|
|
506
|
+
cdir = _find_change_dir(test_dir)
|
|
507
|
+
if not cdir:
|
|
508
|
+
return 0.0, "no comet change directory"
|
|
509
|
+
|
|
510
|
+
comet_dir = cdir / ".comet"
|
|
511
|
+
checks: list[bool] = []
|
|
512
|
+
notes: list[str] = []
|
|
513
|
+
|
|
514
|
+
# Check 1: Checkpoint exists and valid
|
|
515
|
+
checkpoint_path = comet_dir / "checkpoint.json"
|
|
516
|
+
state_events_path = comet_dir / "state-events.jsonl"
|
|
517
|
+
run_state_path = comet_dir / "run-state.json"
|
|
518
|
+
if checkpoint_path.exists():
|
|
519
|
+
try:
|
|
520
|
+
checkpoint = json.loads(checkpoint_path.read_text(errors="ignore"))
|
|
521
|
+
checkpoint_ok = (
|
|
522
|
+
bool(checkpoint.get("runId"))
|
|
523
|
+
and "contextHash" in checkpoint
|
|
524
|
+
and bool(checkpoint.get("artifactsHash"))
|
|
525
|
+
and bool(checkpoint.get("createdAt"))
|
|
526
|
+
)
|
|
527
|
+
checks.append(checkpoint_ok)
|
|
528
|
+
notes.append(f"checkpoint={'valid' if checkpoint_ok else 'invalid'}")
|
|
529
|
+
except Exception:
|
|
530
|
+
checks.append(False)
|
|
531
|
+
notes.append("checkpoint=unreadable")
|
|
532
|
+
elif state_events_path.exists() or run_state_path.exists():
|
|
533
|
+
checks.append(True)
|
|
534
|
+
notes.append("checkpoint=state-events")
|
|
535
|
+
else:
|
|
536
|
+
checks.append(False)
|
|
537
|
+
notes.append("checkpoint=missing")
|
|
538
|
+
|
|
539
|
+
# Check 2: Trajectory exists
|
|
540
|
+
trajectory_files = list(comet_dir.glob("trajectory*.jsonl"))
|
|
541
|
+
trajectory_ok = bool(trajectory_files)
|
|
542
|
+
checks.append(trajectory_ok)
|
|
543
|
+
notes.append(f"trajectory={'exists' if trajectory_ok else 'missing'}")
|
|
544
|
+
|
|
545
|
+
# Check 3: Context snapshot exists
|
|
546
|
+
context_files = list(comet_dir.glob("**/context.*"))
|
|
547
|
+
handoff_dir = comet_dir / "handoff"
|
|
548
|
+
handoff_files = list(handoff_dir.glob("*.md")) + list(handoff_dir.glob("*.json")) if handoff_dir.exists() else []
|
|
549
|
+
context_ok = bool(context_files or handoff_files)
|
|
550
|
+
if workflow == "full":
|
|
551
|
+
checks.append(context_ok)
|
|
552
|
+
notes.append(f"context={'exists' if context_ok else 'missing'}")
|
|
553
|
+
else:
|
|
554
|
+
notes.append(f"context={'exists' if context_ok else 'preset-n/a'}")
|
|
555
|
+
|
|
556
|
+
# Check 4: No orphaned pending actions
|
|
557
|
+
pending_files = list(comet_dir.glob("**/pending*.json"))
|
|
558
|
+
pending_clean = True
|
|
559
|
+
for pf in pending_files:
|
|
560
|
+
try:
|
|
561
|
+
content = json.loads(pf.read_text(errors="ignore"))
|
|
562
|
+
if content:
|
|
563
|
+
pending_clean = False
|
|
564
|
+
break
|
|
565
|
+
except Exception:
|
|
566
|
+
pass
|
|
567
|
+
checks.append(pending_clean)
|
|
568
|
+
notes.append(f"pending={'clean' if pending_clean else 'orphaned'}")
|
|
569
|
+
|
|
570
|
+
# Check 5: .comet.yaml has phase field
|
|
571
|
+
yaml_path = cdir / ".comet.yaml"
|
|
572
|
+
if yaml_path.exists():
|
|
573
|
+
try:
|
|
574
|
+
yaml_content = yaml_path.read_text(errors="ignore")
|
|
575
|
+
yaml_ok = bool(re.search(r"phase:\s*\w+", yaml_content))
|
|
576
|
+
checks.append(yaml_ok)
|
|
577
|
+
notes.append(f"state={'ok' if yaml_ok else 'incomplete'}")
|
|
578
|
+
except Exception:
|
|
579
|
+
checks.append(False)
|
|
580
|
+
notes.append("state=unreadable")
|
|
581
|
+
elif run_state_path.exists():
|
|
582
|
+
try:
|
|
583
|
+
run_state = json.loads(run_state_path.read_text(errors="ignore"))
|
|
584
|
+
state_ok = run_state.get("status") == "completed" or bool(
|
|
585
|
+
run_state.get("currentStep")
|
|
586
|
+
)
|
|
587
|
+
checks.append(state_ok)
|
|
588
|
+
notes.append(f"state={'ok' if state_ok else 'incomplete'}")
|
|
589
|
+
except Exception:
|
|
590
|
+
checks.append(False)
|
|
591
|
+
notes.append("state=unreadable")
|
|
592
|
+
else:
|
|
593
|
+
checks.append(False)
|
|
594
|
+
notes.append("state=missing")
|
|
595
|
+
|
|
596
|
+
score, _ = _binary_score(checks)
|
|
597
|
+
return score, "; ".join(notes)
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
def _compute_weighted_score(dimension_scores: dict[str, float | None]) -> float:
|
|
601
|
+
"""Compute weighted average across applicable dimensions."""
|
|
602
|
+
total_weight = 0.0
|
|
603
|
+
weighted_sum = 0.0
|
|
604
|
+
for dim in RUBRIC_DIMENSIONS:
|
|
605
|
+
score = dimension_scores.get(dim)
|
|
606
|
+
if score is None:
|
|
607
|
+
continue
|
|
608
|
+
weight = _DIMENSION_WEIGHTS.get(dim, 1.0)
|
|
609
|
+
weighted_sum += score * weight
|
|
610
|
+
total_weight += weight
|
|
611
|
+
return weighted_sum / total_weight if total_weight > 0 else 0.0
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
def _is_control_business_only(outputs: dict[str, Any]) -> bool:
|
|
615
|
+
return (outputs or {}).get("treatment_name") == "CONTROL"
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
def _completion_input(outputs: dict[str, Any], key: str) -> dict[str, list[str]] | None:
|
|
619
|
+
if key in outputs:
|
|
620
|
+
return outputs.get(key) or {"passed": [], "failed": []}
|
|
621
|
+
if key == "business_completion":
|
|
622
|
+
return outputs.get("completion") or {"passed": [], "failed": []}
|
|
623
|
+
return None
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
def comet_rubric_validator(test_dir: Path, outputs: dict) -> tuple[list[str], list[str]]:
|
|
627
|
+
"""Run all rubric dimensions and return (passed_messages, []).
|
|
628
|
+
|
|
629
|
+
Rubric dimensions are informational scores (never hard failures); the
|
|
630
|
+
comparison report aggregates them across treatments. Every dimension always
|
|
631
|
+
emits exactly one ``[RUBRIC]`` message.
|
|
632
|
+
|
|
633
|
+
Scoring: each dimension uses binary checks, score = pass rate (0.0-1.0).
|
|
634
|
+
Final weighted score uses dimension weights to aggregate.
|
|
635
|
+
"""
|
|
636
|
+
events = (outputs or {}).get("events", {}) or {}
|
|
637
|
+
business_completion = _completion_input(outputs or {}, "business_completion")
|
|
638
|
+
workflow_completion = _completion_input(outputs or {}, "workflow_completion")
|
|
639
|
+
is_control = _is_control_business_only(outputs or {})
|
|
640
|
+
workflow = _detect_workflow_kind(events, test_dir)
|
|
641
|
+
|
|
642
|
+
if is_control:
|
|
643
|
+
na_reason = "CONTROL business-only baseline"
|
|
644
|
+
scored: list[tuple[str, float | None, str]] = [
|
|
645
|
+
("main_flow", None, na_reason),
|
|
646
|
+
("gate_guard", None, na_reason),
|
|
647
|
+
("skill_invocation", None, na_reason),
|
|
648
|
+
("spec_drift", None, na_reason),
|
|
649
|
+
(
|
|
650
|
+
"business_completion",
|
|
651
|
+
*_score_completion(
|
|
652
|
+
{"business_completion": business_completion},
|
|
653
|
+
"business_completion",
|
|
654
|
+
),
|
|
655
|
+
),
|
|
656
|
+
("workflow_completion", None, na_reason),
|
|
657
|
+
("efficiency", *_score_efficiency(events)),
|
|
658
|
+
("decision_point_compliance", None, na_reason),
|
|
659
|
+
("artifact_quality", None, na_reason),
|
|
660
|
+
("recovery_resilience", None, na_reason),
|
|
661
|
+
]
|
|
662
|
+
else:
|
|
663
|
+
scored = [
|
|
664
|
+
("main_flow", *_score_main_flow(events, test_dir, workflow)),
|
|
665
|
+
("gate_guard", *_score_gate_guard(events)),
|
|
666
|
+
("skill_invocation", *_score_skill_invocation(events)),
|
|
667
|
+
("spec_drift", *_score_spec_drift(events, test_dir)),
|
|
668
|
+
(
|
|
669
|
+
"business_completion",
|
|
670
|
+
*_score_completion(
|
|
671
|
+
{"business_completion": business_completion},
|
|
672
|
+
"business_completion",
|
|
673
|
+
),
|
|
674
|
+
),
|
|
675
|
+
(
|
|
676
|
+
"workflow_completion",
|
|
677
|
+
None,
|
|
678
|
+
"workflow validators not available",
|
|
679
|
+
)
|
|
680
|
+
if workflow_completion is None
|
|
681
|
+
else (
|
|
682
|
+
"workflow_completion",
|
|
683
|
+
*_score_completion(
|
|
684
|
+
{"workflow_completion": workflow_completion},
|
|
685
|
+
"workflow_completion",
|
|
686
|
+
),
|
|
687
|
+
),
|
|
688
|
+
("efficiency", *_score_efficiency(events)),
|
|
689
|
+
("decision_point_compliance", *_score_decision_point_compliance(events, workflow)),
|
|
690
|
+
("artifact_quality", *_score_artifact_quality(test_dir, workflow)),
|
|
691
|
+
("recovery_resilience", *_score_recovery_resilience(events, test_dir, workflow)),
|
|
692
|
+
]
|
|
693
|
+
|
|
694
|
+
# Build dimension scores dict for weighted computation
|
|
695
|
+
dimension_scores: dict[str, float | None] = {}
|
|
696
|
+
passed: list[str] = []
|
|
697
|
+
failed: list[str] = []
|
|
698
|
+
for dim, score, reason in scored:
|
|
699
|
+
dimension_scores[dim] = score
|
|
700
|
+
if score is None:
|
|
701
|
+
passed.append(_fmt_na(dim, reason))
|
|
702
|
+
else:
|
|
703
|
+
passed.append(_fmt(dim, score, reason))
|
|
704
|
+
|
|
705
|
+
# Add weighted overall score
|
|
706
|
+
weighted = _compute_weighted_score(dimension_scores)
|
|
707
|
+
passed.append(_fmt_weighted(weighted))
|
|
708
|
+
|
|
709
|
+
if not is_control:
|
|
710
|
+
invoked = events.get("skills_invoked", []) or []
|
|
711
|
+
if "comet" not in invoked:
|
|
712
|
+
failed.append("Required skill not invoked: comet")
|
|
713
|
+
else:
|
|
714
|
+
if not any(skill in invoked for skill in _COMET_STAGE_SKILLS):
|
|
715
|
+
failed.append("Required nested Comet stage skill not invoked")
|
|
716
|
+
if not any(skill.startswith("openspec-") for skill in invoked):
|
|
717
|
+
failed.append("Required OpenSpec dependency skill not invoked")
|
|
718
|
+
if not any(skill in invoked for skill in _SUPERPOWERS_DEPENDENCY_SKILLS):
|
|
719
|
+
failed.append("Required Superpowers dependency skill not invoked")
|
|
720
|
+
|
|
721
|
+
# Optional LLM-as-judge overlay: when BENCH_LLM_JUDGE=1, a judge model
|
|
722
|
+
# re-scores the three qualitative dimensions (artifact_quality, spec_drift,
|
|
723
|
+
# main_flow) by reading the actual artifacts. These [RUBRIC-JUDGE] messages
|
|
724
|
+
# supplement (do not replace) the rule-based [RUBRIC] scores so the report
|
|
725
|
+
# can show rule vs judge agreement.
|
|
726
|
+
if os.environ.get("BENCH_LLM_JUDGE") == "1":
|
|
727
|
+
try:
|
|
728
|
+
from scaffold.python.llm_judge import judge_messages
|
|
729
|
+
|
|
730
|
+
judge_results = judge_messages(test_dir)
|
|
731
|
+
passed.extend(judge_results)
|
|
732
|
+
if any(
|
|
733
|
+
result.startswith("[RUBRIC-JUDGE] ") and " status:" not in result
|
|
734
|
+
for result in judge_results
|
|
735
|
+
):
|
|
736
|
+
passed.append("[RUBRIC-JUDGE] status: enabled_and_successful")
|
|
737
|
+
except Exception as e:
|
|
738
|
+
passed.append(f"[RUBRIC-JUDGE] status: failed - {e}")
|
|
739
|
+
|
|
740
|
+
return passed, failed
|