just-vibe 0.7.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/.agents/plugins/marketplace.json +12 -0
- package/.claude-plugin/marketplace.json +12 -0
- package/CHANGELOG.md +49 -0
- package/LICENSE +21 -0
- package/README.md +282 -0
- package/bin/just-vibe.mjs +3 -0
- package/docs/command-quality.md +74 -0
- package/docs/compatibility.md +29 -0
- package/docs/releases.md +51 -0
- package/evals/README.md +47 -0
- package/evals/behavior/cases/arch-events/flow.json +12 -0
- package/evals/behavior/cases/arch-events/task.md +3 -0
- package/evals/behavior/cases/authz/access.mjs +1 -0
- package/evals/behavior/cases/authz/task.md +3 -0
- package/evals/behavior/cases/checkout/checkout.mjs +1 -0
- package/evals/behavior/cases/checkout/contract.md +1 -0
- package/evals/behavior/cases/checkout/keep.txt +1 -0
- package/evals/behavior/cases/checkout/task.md +3 -0
- package/evals/behavior/cases/data-reconcile/source.json +14 -0
- package/evals/behavior/cases/data-reconcile/target.json +14 -0
- package/evals/behavior/cases/data-reconcile/task.md +3 -0
- package/evals/behavior/cases/db-migrate/context.json +13 -0
- package/evals/behavior/cases/db-migrate/migration.sql +3 -0
- package/evals/behavior/cases/db-migrate/task.md +3 -0
- package/evals/behavior/cases/db-query/query.sql +1 -0
- package/evals/behavior/cases/db-query/rows.json +32 -0
- package/evals/behavior/cases/db-query/task.md +3 -0
- package/evals/behavior/cases/decision-matrix/decision.json +22 -0
- package/evals/behavior/cases/decision-matrix/task.md +3 -0
- package/evals/behavior/cases/github-pr/prs.json +16 -0
- package/evals/behavior/cases/github-pr/request.json +9 -0
- package/evals/behavior/cases/github-pr/task.md +3 -0
- package/evals/behavior/cases/idempotency/contract.md +1 -0
- package/evals/behavior/cases/idempotency/orders.mjs +1 -0
- package/evals/behavior/cases/idempotency/task.md +3 -0
- package/evals/behavior/cases/ml-checkpoint/checkpoint.json +6 -0
- package/evals/behavior/cases/ml-checkpoint/task.md +3 -0
- package/evals/behavior/cases/ml-checkpoint/training.json +16 -0
- package/evals/behavior/cases/ml-evaluate/labels.json +18 -0
- package/evals/behavior/cases/ml-evaluate/predictions.json +14 -0
- package/evals/behavior/cases/ml-evaluate/task.md +3 -0
- package/evals/behavior/cases/ml-leakage/task.json +30 -0
- package/evals/behavior/cases/ml-leakage/task.md +3 -0
- package/evals/behavior/cases/ml-parity/serving.json +16 -0
- package/evals/behavior/cases/ml-parity/task.md +3 -0
- package/evals/behavior/cases/ml-parity/training.json +16 -0
- package/evals/behavior/cases/ml-split/task.json +7 -0
- package/evals/behavior/cases/ml-split/task.md +3 -0
- package/evals/behavior/cases/ops-logs/context.json +4 -0
- package/evals/behavior/cases/ops-logs/events.json +17 -0
- package/evals/behavior/cases/ops-logs/task.md +3 -0
- package/evals/behavior/cases/rag-boundary/documents.json +26 -0
- package/evals/behavior/cases/rag-boundary/query.json +5 -0
- package/evals/behavior/cases/rag-boundary/task.md +3 -0
- package/evals/behavior/cases/react-race/AccountPanel.jsx +1 -0
- package/evals/behavior/cases/react-race/loader.mjs +1 -0
- package/evals/behavior/cases/react-race/task.md +3 -0
- package/evals/behavior/cases/regression-test/checkout.mjs +1 -0
- package/evals/behavior/cases/regression-test/contract.md +1 -0
- package/evals/behavior/cases/regression-test/task.md +3 -0
- package/evals/behavior/cases/ui-accessibility/observations.json +17 -0
- package/evals/behavior/cases/ui-accessibility/task.md +3 -0
- package/evals/behavior/cases/vercel-env/consumers.json +13 -0
- package/evals/behavior/cases/vercel-env/metadata.json +11 -0
- package/evals/behavior/cases/vercel-env/task.md +3 -0
- package/evals/behavior/cases/vite-assets/deployment.json +8 -0
- package/evals/behavior/cases/vite-assets/render.mjs +1 -0
- package/evals/behavior/cases/vite-assets/task.md +3 -0
- package/evals/behavior/cases/vite-assets/vite.config.mjs +1 -0
- package/evals/behavior/cases.json +185 -0
- package/evals/behavior/code-oracles.mjs +58 -0
- package/evals/behavior/harness.mjs +109 -0
- package/evals/behavior/oracles.json +196 -0
- package/evals/benchmark/README.md +57 -0
- package/evals/benchmark/cases.json +9 -0
- package/evals/benchmark/harness.mjs +231 -0
- package/evals/benchmark/oracles/node.mjs +69 -0
- package/evals/benchmark/oracles/python.py +117 -0
- package/evals/benchmark/report.mjs +62 -0
- package/evals/benchmark/repos/async-cache/README.md +12 -0
- package/evals/benchmark/repos/async-cache/TASK.md +1 -0
- package/evals/benchmark/repos/async-cache/package.json +1 -0
- package/evals/benchmark/repos/async-cache/src/cache.mjs +13 -0
- package/evals/benchmark/repos/async-cache/src/view.mjs +9 -0
- package/evals/benchmark/repos/async-cache/test/smoke.test.mjs +9 -0
- package/evals/benchmark/repos/ledger/README.md +11 -0
- package/evals/benchmark/repos/ledger/TASK.md +1 -0
- package/evals/benchmark/repos/ledger/src/service.py +14 -0
- package/evals/benchmark/repos/ledger/src/store.py +12 -0
- package/evals/benchmark/repos/ledger/test/test_smoke.py +9 -0
- package/evals/benchmark/repos/scoped-commit/README.md +5 -0
- package/evals/benchmark/repos/scoped-commit/TASK.md +1 -0
- package/evals/benchmark/repos/scoped-commit/package.json +1 -0
- package/evals/benchmark/repos/scoped-commit/src/invoice.mjs +8 -0
- package/evals/benchmark/repos/scoped-commit/test/invoice.test.mjs +4 -0
- package/evals/benchmark/repos/temporal-ml/README.md +12 -0
- package/evals/benchmark/repos/temporal-ml/TASK.md +1 -0
- package/evals/benchmark/repos/temporal-ml/src/features.py +9 -0
- package/evals/benchmark/repos/temporal-ml/src/pipeline.py +10 -0
- package/evals/benchmark/repos/temporal-ml/src/report.py +2 -0
- package/evals/benchmark/repos/temporal-ml/test/test_smoke.py +7 -0
- package/evals/benchmark/support/commit-tree.mjs +11 -0
- package/evals/benchmark/support/python-test-report.py +48 -0
- package/evals/fixtures/checkout/checkout.mjs +4 -0
- package/evals/fixtures/checkout/checkout.test.mjs +13 -0
- package/evals/fixtures/checkout/package.json +6 -0
- package/evals/fixtures/checkout/unrelated.txt +1 -0
- package/evals/fixtures/ml/observations.csv +5 -0
- package/evals/fixtures/ml/task.md +1 -0
- package/evals/releases/0.2.0.md +45 -0
- package/evals/releases/0.3.0.md +23 -0
- package/evals/releases/0.4.0-results.json +1274 -0
- package/evals/releases/0.4.0.md +55 -0
- package/evals/releases/0.5.0.md +28 -0
- package/evals/releases/0.6.0-after-results.json +1307 -0
- package/evals/releases/0.6.0-before-results.json +4850 -0
- package/evals/releases/0.6.0.md +94 -0
- package/evals/releases/0.7.0.md +32 -0
- package/evals/scenarios.json +7777 -0
- package/package.json +50 -0
- package/plugins/just-vibe/.claude-plugin/plugin.json +11 -0
- package/plugins/just-vibe/.codex-plugin/plugin.json +24 -0
- package/plugins/just-vibe/LICENSE +21 -0
- package/plugins/just-vibe/catalog/commands.json +16757 -0
- package/plugins/just-vibe/catalog/packs.json +115 -0
- package/plugins/just-vibe/catalog/profiles.json +2503 -0
- package/plugins/just-vibe/hooks/hooks.json +11 -0
- package/plugins/just-vibe/references/command-reference.md +328 -0
- package/plugins/just-vibe/references/daily-workflows.md +133 -0
- package/plugins/just-vibe/references/execution.md +60 -0
- package/plugins/just-vibe/references/instruction-memory.md +86 -0
- package/plugins/just-vibe/references/packs/api.md +27 -0
- package/plugins/just-vibe/references/packs/architecture.md +29 -0
- package/plugins/just-vibe/references/packs/backend.md +43 -0
- package/plugins/just-vibe/references/packs/data.md +27 -0
- package/plugins/just-vibe/references/packs/database.md +32 -0
- package/plugins/just-vibe/references/packs/decisions.md +29 -0
- package/plugins/just-vibe/references/packs/general.md +34 -0
- package/plugins/just-vibe/references/packs/git.md +45 -0
- package/plugins/just-vibe/references/packs/github.md +31 -0
- package/plugins/just-vibe/references/packs/installation.md +27 -0
- package/plugins/just-vibe/references/packs/llm.md +33 -0
- package/plugins/just-vibe/references/packs/ml-data.md +43 -0
- package/plugins/just-vibe/references/packs/ml-deployment.md +31 -0
- package/plugins/just-vibe/references/packs/ml-evaluation.md +29 -0
- package/plugins/just-vibe/references/packs/ml-experiments.md +29 -0
- package/plugins/just-vibe/references/packs/operations.md +35 -0
- package/plugins/just-vibe/references/packs/react.md +29 -0
- package/plugins/just-vibe/references/packs/security.md +31 -0
- package/plugins/just-vibe/references/packs/testing.md +35 -0
- package/plugins/just-vibe/references/packs/ui.md +29 -0
- package/plugins/just-vibe/references/packs/vercel.md +29 -0
- package/plugins/just-vibe/references/packs/vite.md +29 -0
- package/plugins/just-vibe/references/profile-reference.md +155 -0
- package/plugins/just-vibe/references/profiles/accessibility-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/agent-systems-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/ai-evaluation-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/ai-security-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/analytics-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/android-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/api-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/application-security-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/applied-ai-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/backend-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/bioinformatics-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/blockchain-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/build-release-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/business-intelligence-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/capacity-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/causal-inference-scientist.md +31 -0
- package/plugins/just-vibe/references/profiles/cloud-architect.md +31 -0
- package/plugins/just-vibe/references/profiles/cloud-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/cloud-security-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/compiler-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/computer-vision-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/controls-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/creative-technologist.md +31 -0
- package/plugins/just-vibe/references/profiles/cryptography-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/data-analyst.md +31 -0
- package/plugins/just-vibe/references/profiles/data-architect.md +31 -0
- package/plugins/just-vibe/references/profiles/data-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/data-governance-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/data-platform-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/data-quality-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/data-scientist.md +31 -0
- package/plugins/just-vibe/references/profiles/database-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/database-reliability-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/design-systems-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/desktop-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/detection-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/developer-advocate.md +31 -0
- package/plugins/just-vibe/references/profiles/developer-experience-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/devops-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/distributed-systems-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/edge-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/embedded-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/engineering-manager.md +31 -0
- package/plugins/just-vibe/references/profiles/enterprise-architect.md +31 -0
- package/plugins/just-vibe/references/profiles/experimentation-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/finops-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/firmware-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/frontend-architect.md +31 -0
- package/plugins/just-vibe/references/profiles/frontend-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/fullstack-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/game-networking-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/gameplay-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/geospatial-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/graphics-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/hpc-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/identity-access-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/inference-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/infrastructure-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/integration-architect.md +31 -0
- package/plugins/just-vibe/references/profiles/integration-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/ios-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/iot-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/kubernetes-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/llm-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/machine-learning-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/ml-architect.md +31 -0
- package/plugins/just-vibe/references/profiles/ml-data-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/ml-platform-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/mlops-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/mobile-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/network-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/nlp-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/observability-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/performance-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/platform-architect.md +31 -0
- package/plugins/just-vibe/references/profiles/platform-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/principal-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/privacy-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/product-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/product-security-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/protocol-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/qa-automation-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/recommendation-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/reinforcement-learning-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/research-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/research-scientist.md +31 -0
- package/plugins/just-vibe/references/profiles/responsible-ai-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/robotics-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/runtime-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/scientific-software-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/search-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/security-architect.md +31 -0
- package/plugins/just-vibe/references/profiles/security-automation-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/security-incident-responder.md +31 -0
- package/plugins/just-vibe/references/profiles/senior-software-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/simulation-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/site-reliability-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/software-architect.md +31 -0
- package/plugins/just-vibe/references/profiles/solutions-architect.md +31 -0
- package/plugins/just-vibe/references/profiles/speech-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/staff-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/storage-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/streaming-data-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/supply-chain-security-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/systems-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/tech-lead.md +31 -0
- package/plugins/just-vibe/references/profiles/technical-writer.md +31 -0
- package/plugins/just-vibe/references/profiles/test-infrastructure-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/ui-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/ux-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/web-performance-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/xr-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles.md +59 -0
- package/plugins/just-vibe/references/runtime.md +70 -0
- package/plugins/just-vibe/references/scenarios/auth.md +31 -0
- package/plugins/just-vibe/references/scenarios/combobox.md +9 -0
- package/plugins/just-vibe/references/scenarios/date-picker.md +9 -0
- package/plugins/just-vibe/references/scenarios/delivery-evidence.md +21 -0
- package/plugins/just-vibe/references/scenarios/dialog.md +9 -0
- package/plugins/just-vibe/references/scenarios/training.md +21 -0
- package/plugins/just-vibe/references/teach-test.md +37 -0
- package/plugins/just-vibe/references/teaching.md +34 -0
- package/plugins/just-vibe/references/validation.md +11 -0
- package/plugins/just-vibe/scripts/discover-capabilities.mjs +3 -0
- package/plugins/just-vibe/scripts/hooks.mjs +14 -0
- package/plugins/just-vibe/scripts/inspect-project.mjs +3 -0
- package/plugins/just-vibe/scripts/installer.mjs +280 -0
- package/plugins/just-vibe/scripts/lib/automation.mjs +142 -0
- package/plugins/just-vibe/scripts/lib/bundle.mjs +100 -0
- package/plugins/just-vibe/scripts/lib/catalog.mjs +135 -0
- package/plugins/just-vibe/scripts/lib/command.mjs +26 -0
- package/plugins/just-vibe/scripts/lib/continuity.mjs +77 -0
- package/plugins/just-vibe/scripts/lib/discovery.mjs +84 -0
- package/plugins/just-vibe/scripts/lib/entrypoint.mjs +12 -0
- package/plugins/just-vibe/scripts/lib/evidence.mjs +136 -0
- package/plugins/just-vibe/scripts/lib/process.mjs +44 -0
- package/plugins/just-vibe/scripts/lib/profiles.mjs +83 -0
- package/plugins/just-vibe/scripts/lib/project.mjs +60 -0
- package/plugins/just-vibe/scripts/lib/routing.mjs +82 -0
- package/plugins/just-vibe/scripts/lib/run.mjs +248 -0
- package/plugins/just-vibe/scripts/lib/storage.mjs +84 -0
- package/plugins/just-vibe/scripts/lib/teaching.mjs +118 -0
- package/plugins/just-vibe/scripts/toolkit.mjs +225 -0
- package/plugins/just-vibe/skills/a11y/SKILL.md +8 -0
- package/plugins/just-vibe/skills/api-breaking/SKILL.md +56 -0
- package/plugins/just-vibe/skills/api-client/SKILL.md +56 -0
- package/plugins/just-vibe/skills/api-contract-test/SKILL.md +56 -0
- package/plugins/just-vibe/skills/api-design/SKILL.md +56 -0
- package/plugins/just-vibe/skills/api-errors/SKILL.md +56 -0
- package/plugins/just-vibe/skills/api-openapi/SKILL.md +56 -0
- package/plugins/just-vibe/skills/api-pagination/SKILL.md +56 -0
- package/plugins/just-vibe/skills/api-webhooks/SKILL.md +56 -0
- package/plugins/just-vibe/skills/arch-boundaries/SKILL.md +58 -0
- package/plugins/just-vibe/skills/arch-contracts/SKILL.md +56 -0
- package/plugins/just-vibe/skills/arch-event-flow/SKILL.md +56 -0
- package/plugins/just-vibe/skills/arch-feature/SKILL.md +57 -0
- package/plugins/just-vibe/skills/arch-map/SKILL.md +56 -0
- package/plugins/just-vibe/skills/arch-modernize/SKILL.md +56 -0
- package/plugins/just-vibe/skills/arch-scale/SKILL.md +58 -0
- package/plugins/just-vibe/skills/arch-tenancy/SKILL.md +56 -0
- package/plugins/just-vibe/skills/auto/SKILL.md +67 -0
- package/plugins/just-vibe/skills/automate/SKILL.md +56 -0
- package/plugins/just-vibe/skills/backend-auth/SKILL.md +63 -0
- package/plugins/just-vibe/skills/backend-cache/SKILL.md +59 -0
- package/plugins/just-vibe/skills/backend-concurrency/SKILL.md +58 -0
- package/plugins/just-vibe/skills/backend-idempotency/SKILL.md +58 -0
- package/plugins/just-vibe/skills/backend-jobs/SKILL.md +56 -0
- package/plugins/just-vibe/skills/backend-permissions/SKILL.md +56 -0
- package/plugins/just-vibe/skills/backend-resilience/SKILL.md +56 -0
- package/plugins/just-vibe/skills/backend-service/SKILL.md +56 -0
- package/plugins/just-vibe/skills/brainstorm/SKILL.md +56 -0
- package/plugins/just-vibe/skills/build/SKILL.md +56 -0
- package/plugins/just-vibe/skills/challenge/SKILL.md +56 -0
- package/plugins/just-vibe/skills/checkpoint/SKILL.md +59 -0
- package/plugins/just-vibe/skills/ci/SKILL.md +56 -0
- package/plugins/just-vibe/skills/cleanup/SKILL.md +56 -0
- package/plugins/just-vibe/skills/compare/SKILL.md +56 -0
- package/plugins/just-vibe/skills/copy/SKILL.md +56 -0
- package/plugins/just-vibe/skills/coverage/SKILL.md +56 -0
- package/plugins/just-vibe/skills/data-backfill/SKILL.md +56 -0
- package/plugins/just-vibe/skills/data-contract/SKILL.md +56 -0
- package/plugins/just-vibe/skills/data-incremental/SKILL.md +56 -0
- package/plugins/just-vibe/skills/data-lineage/SKILL.md +56 -0
- package/plugins/just-vibe/skills/data-pipeline/SKILL.md +56 -0
- package/plugins/just-vibe/skills/data-profile/SKILL.md +56 -0
- package/plugins/just-vibe/skills/data-quality/SKILL.md +56 -0
- package/plugins/just-vibe/skills/data-reconcile/SKILL.md +56 -0
- package/plugins/just-vibe/skills/db-access/SKILL.md +56 -0
- package/plugins/just-vibe/skills/db-explain/SKILL.md +56 -0
- package/plugins/just-vibe/skills/db-index/SKILL.md +56 -0
- package/plugins/just-vibe/skills/db-integrity/SKILL.md +57 -0
- package/plugins/just-vibe/skills/db-locks/SKILL.md +56 -0
- package/plugins/just-vibe/skills/db-migrate/SKILL.md +63 -0
- package/plugins/just-vibe/skills/db-query/SKILL.md +56 -0
- package/plugins/just-vibe/skills/db-schema/SKILL.md +56 -0
- package/plugins/just-vibe/skills/debug/SKILL.md +56 -0
- package/plugins/just-vibe/skills/decide/SKILL.md +57 -0
- package/plugins/just-vibe/skills/decision-adr/SKILL.md +56 -0
- package/plugins/just-vibe/skills/decision-buy-build/SKILL.md +56 -0
- package/plugins/just-vibe/skills/decision-matrix/SKILL.md +56 -0
- package/plugins/just-vibe/skills/decision-premortem/SKILL.md +56 -0
- package/plugins/just-vibe/skills/decision-reversible/SKILL.md +56 -0
- package/plugins/just-vibe/skills/decision-revisit/SKILL.md +56 -0
- package/plugins/just-vibe/skills/decision-spike/SKILL.md +58 -0
- package/plugins/just-vibe/skills/deploy/SKILL.md +56 -0
- package/plugins/just-vibe/skills/deps/SKILL.md +56 -0
- package/plugins/just-vibe/skills/design/SKILL.md +56 -0
- package/plugins/just-vibe/skills/do/SKILL.md +8 -0
- package/plugins/just-vibe/skills/docs/SKILL.md +56 -0
- package/plugins/just-vibe/skills/doctor/SKILL.md +58 -0
- package/plugins/just-vibe/skills/explain/SKILL.md +56 -0
- package/plugins/just-vibe/skills/fix/SKILL.md +57 -0
- package/plugins/just-vibe/skills/git-bisect/SKILL.md +58 -0
- package/plugins/just-vibe/skills/git-commit/SKILL.md +59 -0
- package/plugins/just-vibe/skills/git-conflicts/SKILL.md +58 -0
- package/plugins/just-vibe/skills/git-diff/SKILL.md +58 -0
- package/plugins/just-vibe/skills/git-recover/SKILL.md +58 -0
- package/plugins/just-vibe/skills/git-split/SKILL.md +58 -0
- package/plugins/just-vibe/skills/git-status/SKILL.md +58 -0
- package/plugins/just-vibe/skills/git-worktree/SKILL.md +58 -0
- package/plugins/just-vibe/skills/github-actions/SKILL.md +64 -0
- package/plugins/just-vibe/skills/github-address-review/SKILL.md +58 -0
- package/plugins/just-vibe/skills/github-fix-ci/SKILL.md +63 -0
- package/plugins/just-vibe/skills/github-issue/SKILL.md +58 -0
- package/plugins/just-vibe/skills/github-pr/SKILL.md +63 -0
- package/plugins/just-vibe/skills/github-release/SKILL.md +58 -0
- package/plugins/just-vibe/skills/github-review/SKILL.md +58 -0
- package/plugins/just-vibe/skills/github-triage/SKILL.md +58 -0
- package/plugins/just-vibe/skills/handoff/SKILL.md +62 -0
- package/plugins/just-vibe/skills/help/SKILL.md +63 -0
- package/plugins/just-vibe/skills/integrate/SKILL.md +56 -0
- package/plugins/just-vibe/skills/learn/SKILL.md +56 -0
- package/plugins/just-vibe/skills/llm-cost/SKILL.md +56 -0
- package/plugins/just-vibe/skills/llm-evals/SKILL.md +58 -0
- package/plugins/just-vibe/skills/llm-injection/SKILL.md +56 -0
- package/plugins/just-vibe/skills/llm-prompt/SKILL.md +56 -0
- package/plugins/just-vibe/skills/llm-rag/SKILL.md +56 -0
- package/plugins/just-vibe/skills/llm-retrieval/SKILL.md +56 -0
- package/plugins/just-vibe/skills/llm-structured/SKILL.md +56 -0
- package/plugins/just-vibe/skills/llm-tools/SKILL.md +58 -0
- package/plugins/just-vibe/skills/map/SKILL.md +56 -0
- package/plugins/just-vibe/skills/match/SKILL.md +56 -0
- package/plugins/just-vibe/skills/migrate/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-ablation/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-baseline/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-batch/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-calibrate/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-dataset/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-dataset-version/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-debug-training/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-drift/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-error-analysis/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-evaluate/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-experiments/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-explain/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-features/SKILL.md +59 -0
- package/plugins/just-vibe/skills/ml-frame/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-imbalance/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-inference-perf/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-labels/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-leakage/SKILL.md +64 -0
- package/plugins/just-vibe/skills/ml-monitor/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-package/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-parity/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-report/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-reproduce/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-robustness/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-rollout/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-serving/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-slices/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-split/SKILL.md +58 -0
- package/plugins/just-vibe/skills/ml-threshold/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-train/SKILL.md +62 -0
- package/plugins/just-vibe/skills/ml-training-cost/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-tune/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ops-alerts/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ops-container/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ops-incident/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ops-logs/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ops-observability/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ops-postmortem/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ops-restore/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ops-runbook/SKILL.md +56 -0
- package/plugins/just-vibe/skills/orient/SKILL.md +58 -0
- package/plugins/just-vibe/skills/perf/SKILL.md +56 -0
- package/plugins/just-vibe/skills/plan/SKILL.md +56 -0
- package/plugins/just-vibe/skills/polish/SKILL.md +56 -0
- package/plugins/just-vibe/skills/pr/SKILL.md +58 -0
- package/plugins/just-vibe/skills/profile/SKILL.md +66 -0
- package/plugins/just-vibe/skills/profiles/SKILL.md +58 -0
- package/plugins/just-vibe/skills/react-async/SKILL.md +57 -0
- package/plugins/just-vibe/skills/react-audit/SKILL.md +56 -0
- package/plugins/just-vibe/skills/react-component/SKILL.md +65 -0
- package/plugins/just-vibe/skills/react-effects/SKILL.md +58 -0
- package/plugins/just-vibe/skills/react-forms/SKILL.md +56 -0
- package/plugins/just-vibe/skills/react-hydration/SKILL.md +57 -0
- package/plugins/just-vibe/skills/react-rerenders/SKILL.md +56 -0
- package/plugins/just-vibe/skills/react-state/SKILL.md +56 -0
- package/plugins/just-vibe/skills/refactor/SKILL.md +56 -0
- package/plugins/just-vibe/skills/release/SKILL.md +58 -0
- package/plugins/just-vibe/skills/remember/SKILL.md +70 -0
- package/plugins/just-vibe/skills/repro/SKILL.md +56 -0
- package/plugins/just-vibe/skills/research/SKILL.md +56 -0
- package/plugins/just-vibe/skills/responsive/SKILL.md +8 -0
- package/plugins/just-vibe/skills/resume/SKILL.md +63 -0
- package/plugins/just-vibe/skills/review/SKILL.md +56 -0
- package/plugins/just-vibe/skills/scope/SKILL.md +56 -0
- package/plugins/just-vibe/skills/security/SKILL.md +56 -0
- package/plugins/just-vibe/skills/security-authz/SKILL.md +56 -0
- package/plugins/just-vibe/skills/security-config/SKILL.md +56 -0
- package/plugins/just-vibe/skills/security-dependencies/SKILL.md +56 -0
- package/plugins/just-vibe/skills/security-fix/SKILL.md +56 -0
- package/plugins/just-vibe/skills/security-inputs/SKILL.md +56 -0
- package/plugins/just-vibe/skills/security-secrets/SKILL.md +56 -0
- package/plugins/just-vibe/skills/security-threat-model/SKILL.md +56 -0
- package/plugins/just-vibe/skills/security-uploads/SKILL.md +56 -0
- package/plugins/just-vibe/skills/setup/SKILL.md +60 -0
- package/plugins/just-vibe/skills/skill/SKILL.md +56 -0
- package/plugins/just-vibe/skills/spec/SKILL.md +56 -0
- package/plugins/just-vibe/skills/tasks/SKILL.md +56 -0
- package/plugins/just-vibe/skills/teach/SKILL.md +63 -0
- package/plugins/just-vibe/skills/teach-test/SKILL.md +65 -0
- package/plugins/just-vibe/skills/test/SKILL.md +56 -0
- package/plugins/just-vibe/skills/test-e2e/SKILL.md +56 -0
- package/plugins/just-vibe/skills/test-fixtures/SKILL.md +56 -0
- package/plugins/just-vibe/skills/test-flaky/SKILL.md +56 -0
- package/plugins/just-vibe/skills/test-integration/SKILL.md +56 -0
- package/plugins/just-vibe/skills/test-load/SKILL.md +56 -0
- package/plugins/just-vibe/skills/test-property/SKILL.md +56 -0
- package/plugins/just-vibe/skills/test-regression/SKILL.md +57 -0
- package/plugins/just-vibe/skills/test-unit/SKILL.md +56 -0
- package/plugins/just-vibe/skills/tools/SKILL.md +64 -0
- package/plugins/just-vibe/skills/trace/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ui-accessibility/SKILL.md +57 -0
- package/plugins/just-vibe/skills/ui-audit/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ui-flow/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ui-motion/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ui-responsive/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ui-states/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ui-system/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ui-visual-diff/SKILL.md +56 -0
- package/plugins/just-vibe/skills/vercel-audit/SKILL.md +56 -0
- package/plugins/just-vibe/skills/vercel-build-fix/SKILL.md +63 -0
- package/plugins/just-vibe/skills/vercel-env/SKILL.md +56 -0
- package/plugins/just-vibe/skills/vercel-performance/SKILL.md +56 -0
- package/plugins/just-vibe/skills/vercel-preview/SKILL.md +56 -0
- package/plugins/just-vibe/skills/vercel-release-check/SKILL.md +57 -0
- package/plugins/just-vibe/skills/vercel-routing/SKILL.md +56 -0
- package/plugins/just-vibe/skills/vercel-runtime/SKILL.md +61 -0
- package/plugins/just-vibe/skills/verify/SKILL.md +59 -0
- package/plugins/just-vibe/skills/vite-assets/SKILL.md +57 -0
- package/plugins/just-vibe/skills/vite-bundle/SKILL.md +58 -0
- package/plugins/just-vibe/skills/vite-chunks/SKILL.md +56 -0
- package/plugins/just-vibe/skills/vite-config/SKILL.md +56 -0
- package/plugins/just-vibe/skills/vite-env/SKILL.md +56 -0
- package/plugins/just-vibe/skills/vite-hmr/SKILL.md +56 -0
- package/plugins/just-vibe/skills/vite-setup/SKILL.md +56 -0
- package/plugins/just-vibe/skills/vite-upgrade/SKILL.md +56 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync, writeFileSync, readdirSync, mkdirSync, cpSync, existsSync, realpathSync, symlinkSync, rmSync } from 'node:fs';
|
|
3
|
+
import { resolve, relative, join, dirname } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { createHash } from 'node:crypto';
|
|
6
|
+
import { spawnSync, execFileSync, spawn } from 'node:child_process';
|
|
7
|
+
import { performance } from 'node:perf_hooks';
|
|
8
|
+
import { pluginRoot } from '../../plugins/just-vibe/scripts/lib/catalog.mjs';
|
|
9
|
+
|
|
10
|
+
const here=fileURLToPath(new URL('./',import.meta.url));
|
|
11
|
+
const json=path=>JSON.parse(readFileSync(path,'utf8'));
|
|
12
|
+
export const cases=json(join(here,'cases.json')).cases;
|
|
13
|
+
export const arms=['baseline','just-vibe','just-vibe-profile','ecc'];
|
|
14
|
+
const hash=value=>createHash('sha256').update(value).digest('hex');
|
|
15
|
+
const git=(root,args)=>execFileSync('git',args,{cwd:root,encoding:'utf8',env:{...process.env,GIT_CONFIG_NOSYSTEM:'1',GIT_CONFIG_GLOBAL:process.platform==='win32'?'NUL':'/dev/null'}}).trimEnd();
|
|
16
|
+
function sourceIdentity(){
|
|
17
|
+
const root=fileURLToPath(new URL('../../',import.meta.url));
|
|
18
|
+
try{if(realpathSync(git(root,['rev-parse','--show-toplevel']))!==realpathSync(root))return {revision:null,dirty:null};return {revision:git(root,['rev-parse','HEAD']),dirty:Boolean(git(root,['status','--porcelain']))};}
|
|
19
|
+
catch{return {revision:null,dirty:null};}
|
|
20
|
+
}
|
|
21
|
+
export function snapshot(root) {
|
|
22
|
+
const result={};
|
|
23
|
+
function walk(dir){for(const e of readdirSync(dir,{withFileTypes:true})){
|
|
24
|
+
if(e.name==='.git'||e.name==='__pycache__')continue;
|
|
25
|
+
const path=join(dir,e.name),key=relative(root,path).replaceAll('\\','/');
|
|
26
|
+
if(e.isSymbolicLink())result[key]='symlink';else if(e.isDirectory())walk(path);else result[key]=hash(readFileSync(path));
|
|
27
|
+
}}walk(root);return result;
|
|
28
|
+
}
|
|
29
|
+
function write(path,body){mkdirSync(dirname(path),{recursive:true});writeFileSync(path,body);}
|
|
30
|
+
function save(path,value){write(path,JSON.stringify(value,null,2)+'\n');}
|
|
31
|
+
function shuffle(values,seed){let n=seed>>>0;const a=[...values];for(let i=a.length-1;i>0;i--){n=(Math.imul(1664525,n)+1013904223)>>>0;const j=n%(i+1);[a[i],a[j]]=[a[j],a[i]];}return a;}
|
|
32
|
+
export function prepareTrial({out,id,arm,eccRoot,repetition=1}){
|
|
33
|
+
if(existsSync(out))throw Error('Trial output must be fresh.');
|
|
34
|
+
const fixture=cases.find(c=>c.id===id);if(!fixture||!arms.includes(arm))throw Error('Unknown case or arm.');
|
|
35
|
+
if(arm==='ecc'&&(!eccRoot||fixture.ecc.some(p=>!existsSync(join(eccRoot,p)))))throw Error('Pinned ECC sources are required.');
|
|
36
|
+
const root=resolve(out),workspace=join(root,'workspace');mkdirSync(root,{recursive:true});cpSync(join(here,'repos',id),workspace,{recursive:true});
|
|
37
|
+
git(workspace,['init','--quiet']);git(workspace,['config','user.name','Fixture User']);git(workspace,['config','user.email','fixture@example.invalid']);
|
|
38
|
+
git(workspace,['add','--all']);git(workspace,['commit','--quiet','-m','Initial fixture']);
|
|
39
|
+
const base=git(workspace,['rev-parse','HEAD']);
|
|
40
|
+
if(fixture.gitCommit){
|
|
41
|
+
const source=join(workspace,'src/invoice.mjs');writeFileSync(source,readFileSync(source,'utf8').replace('`Invoice ${id}`','`Receipt ${id}`'));
|
|
42
|
+
writeFileSync(join(workspace,'notes.md'),'Existing staged planning note. Preserve it.\n');git(workspace,['add','src/invoice.mjs','notes.md']);
|
|
43
|
+
writeFileSync(source,readFileSync(source,'utf8').replace("return 'USD'","return 'EUR'"));
|
|
44
|
+
writeFileSync(join(workspace,'scratch.txt'),'Existing untracked scratch work. Preserve it.\n');
|
|
45
|
+
}
|
|
46
|
+
write(join(workspace,'.git/info/exclude'),'_instructions/\n__pycache__/\n');
|
|
47
|
+
const instructions=[];
|
|
48
|
+
if(arm.startsWith('just-vibe')){
|
|
49
|
+
cpSync(pluginRoot,join(workspace,'_instructions/just-vibe'),{recursive:true});
|
|
50
|
+
for(const id of fixture.commands)instructions.push(`_instructions/just-vibe/skills/${id}/SKILL.md`);
|
|
51
|
+
if(arm==='just-vibe-profile')instructions.push(`_instructions/just-vibe/references/profiles/${fixture.profile}.md`);
|
|
52
|
+
}else if(arm==='ecc'){
|
|
53
|
+
for(const path of fixture.ecc){
|
|
54
|
+
const target=join(workspace,'_instructions/ecc',path);mkdirSync(dirname(target),{recursive:true});
|
|
55
|
+
if(path.startsWith('skills/'))cpSync(dirname(join(eccRoot,path)),dirname(target),{recursive:true});else cpSync(join(eccRoot,path),target);
|
|
56
|
+
instructions.push(`_instructions/ecc/${path}`);
|
|
57
|
+
}
|
|
58
|
+
if(existsSync(join(eccRoot,'LICENSE')))cpSync(join(eccRoot,'LICENSE'),join(workspace,'_instructions/ecc/LICENSE'));
|
|
59
|
+
}
|
|
60
|
+
const task=readFileSync(join(workspace,'TASK.md'),'utf8');
|
|
61
|
+
const role=arm==='just-vibe-profile'?`Use ${fixture.profile} as the explicitly selected primary task profile. Preserve the task's scope and constraints.`:arm==='just-vibe'?'For this controlled trial, profiles are disabled: do not select or read a role profile.':'';
|
|
62
|
+
const prompt=`Complete the task in this fresh repository: ${workspace}.\n${instructions.length?`Read these supplied instructions, then inspect the repository: ${instructions.join(', ')}.`:'No toolkit instructions are supplied; use the task and project evidence.'}\n${role}\n\n${task}\n\nTrial constraints: work only in this repository. Do not read other trials, source toolkit checkouts, evaluator files, authentication/config directories or prior answers. Do not use network, install dependencies, spawn agents, change host settings or perform external actions. You may run local tests and create temporary test artifacts, removing only artifacts you create. Preserve user files. Python is available at ${python()} on this trial host; do not use a no-op shim on PATH. Git commits are authorized only when TASK.md explicitly requests one. Never add agent self-attribution. Finish with a concise report of changes, actual checks and limitations. Do not claim hidden checks ran. No follow-up user answers will be supplied; make reasonable decisions within the written contract or report a concrete blocker.\n`;
|
|
63
|
+
write(join(root,'prompt.txt'),prompt);
|
|
64
|
+
const manifest={schemaVersion:1,case:id,arm,repetition,workspace,base,allowedWrites:fixture.allowedWrites,gitCommit:Boolean(fixture.gitCommit),preparedAt:new Date().toISOString(),instructions,inputs:snapshot(workspace),promptSha256:hash(prompt),profile:arm==='just-vibe-profile'?fixture.profile:null,sourceVersion:json(join(pluginRoot,'.codex-plugin/plugin.json')).version};
|
|
65
|
+
if(arm==='ecc')manifest.eccRevision=git(eccRoot,['rev-parse','HEAD']);
|
|
66
|
+
save(join(root,'run.json'),manifest);return manifest;
|
|
67
|
+
}
|
|
68
|
+
export function prepareStudy({out,eccRoot,repetitions=2,seed=5192026,selectedCases=cases.map(c=>c.id),selectedArms=arms}){
|
|
69
|
+
if(existsSync(out))throw Error('Study output must be fresh.');
|
|
70
|
+
if(!Number.isInteger(repetitions)||repetitions<1||repetitions>10)throw Error('Repetitions must be 1–10.');
|
|
71
|
+
if(!Number.isInteger(seed)||!selectedCases.length||!selectedArms.length||new Set(selectedCases).size!==selectedCases.length||new Set(selectedArms).size!==selectedArms.length||selectedCases.some(id=>!cases.some(c=>c.id===id))||selectedArms.some(arm=>!arms.includes(arm)))throw Error('Unique known cases/arms and an integer seed are required.');
|
|
72
|
+
const jobs=[];for(const id of selectedCases)for(let repetition=1;repetition<=repetitions;repetition++)for(const arm of selectedArms)jobs.push({id,arm,repetition});
|
|
73
|
+
mkdirSync(out,{recursive:true});const order=shuffle(jobs,seed);
|
|
74
|
+
for(const job of order){job.directory=`${job.id}/${job.arm}/${job.repetition}`;prepareTrial({out:join(out,job.directory),id:job.id,arm:job.arm,repetition:job.repetition,eccRoot});}
|
|
75
|
+
const source=sourceIdentity();
|
|
76
|
+
const plan={schemaVersion:1,preparedAt:new Date().toISOString(),seed,repetitions,sourceRevision:source.revision,sourceDirty:source.dirty,eccRevision:selectedArms.includes('ecc')?git(eccRoot,['rev-parse','HEAD']):null,cases:selectedCases,arms:selectedArms,order,oracleHashes:snapshot(join(here,'oracles')),fixtureHashes:snapshot(join(here,'repos')),harnessSha256:hash(readFileSync(fileURLToPath(import.meta.url))),note:'Author-created multi-file fixture repositories; supplied instructions, not full native plugin hooks. Fresh contexts, randomized order and immutable trial inputs. Public tasks are not a permanent blind benchmark.'};
|
|
77
|
+
save(join(out,'study.json'),plan);return plan;
|
|
78
|
+
}
|
|
79
|
+
export function parseEvents(text){
|
|
80
|
+
const events=[],invalid=[];for(const line of text.split('\n').filter(Boolean)){try{events.push(JSON.parse(line))}catch{invalid.push(line)}}
|
|
81
|
+
const completed=events.filter(e=>e.type==='turn.completed'),failed=events.some(e=>e.type==='turn.failed');
|
|
82
|
+
const usage=completed.length?{}:null;
|
|
83
|
+
if(usage)for(const e of completed)for(const [key,value]of Object.entries(e.usage||{}))if(typeof value==='number')usage[key]=(usage[key]||0)+value;
|
|
84
|
+
const items=events.filter(e=>e.type==='item.completed').map(e=>e.item);
|
|
85
|
+
return {usage,completed:completed.length>0&&!failed,invalidLines:invalid.length,toolCalls:items.filter(i=>['command_execution','file_change','mcp_tool_call','web_search'].includes(i?.type)).length,
|
|
86
|
+
failedCommands:items.filter(i=>i?.type==='command_execution'&&typeof i.exit_code==='number'&&i.exit_code!==0).length,
|
|
87
|
+
errors:events.filter(e=>['error','turn.failed'].includes(e.type)).map(e=>e.message||e.error?.message),
|
|
88
|
+
finalMessages:items.filter(i=>i?.type==='agent_message').map(i=>i.text)};
|
|
89
|
+
}
|
|
90
|
+
function cleanEnv(){const env={...process.env,PYTHONDONTWRITEBYTECODE:'1'};for(const key of ['NODE_OPTIONS','NODE_TEST_CONTEXT','NODE_V8_COVERAGE'])delete env[key];return env;}
|
|
91
|
+
function python(){return process.env.JUST_VIBE_PYTHON||(process.platform==='darwin'?'/usr/bin/python3':process.platform==='win32'?'python':'python3');}
|
|
92
|
+
export function regressionSensitivity(language,result){
|
|
93
|
+
if(result.status===0||result.status===undefined)return false;
|
|
94
|
+
if(language==='python'){
|
|
95
|
+
for(const line of (result.stdout||'').split('\n'))try{
|
|
96
|
+
const report=JSON.parse(line);
|
|
97
|
+
if(report.type==='regression-failure'&&report.behavior_failure===true)return true;
|
|
98
|
+
if(report.tests>0&&report.failures?.some(f=>f.behavior_failure===true))return true;
|
|
99
|
+
}catch{}
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
// TAP emits completed assertion evidence immediately, even if a later test hangs.
|
|
103
|
+
return result.status!==0&&/code: ['"]ERR_ASSERTION['"]/.test(result.stdout||'');
|
|
104
|
+
}
|
|
105
|
+
export function gradeTrial(directory){
|
|
106
|
+
const root=resolve(directory),manifest=json(join(root,'run.json')),workspace=realpathSync(join(root,'workspace'));
|
|
107
|
+
if(workspace!==realpathSync(manifest.workspace))throw Error('Workspace identity mismatch.');
|
|
108
|
+
const current=snapshot(workspace),changed=[...new Set([...Object.keys(current),...Object.keys(manifest.inputs)])].filter(p=>current[p]!==manifest.inputs[p]);
|
|
109
|
+
const checks=[{name:'only permitted files changed',pass:changed.every(p=>manifest.allowedWrites.includes(p))},{name:'no symlink artifacts',pass:!Object.values(current).includes('symlink')}];
|
|
110
|
+
const fixture=cases.find(c=>c.id===manifest.case);const head=git(workspace,['rev-parse','HEAD']);
|
|
111
|
+
if(!fixture.gitCommit)checks.push({name:'no unrequested commits',pass:head===manifest.base});
|
|
112
|
+
if(checks.every(c=>c.pass)){
|
|
113
|
+
const file=fixture.language==='python'?'python.py':'node.mjs';const binary=fixture.language==='python'?python():process.execPath;
|
|
114
|
+
const result=spawnSync(binary,[join(here,'oracles',file),fixture.id,workspace],{encoding:'utf8',timeout:20000,env:cleanEnv(),maxBuffer:1024*1024});
|
|
115
|
+
try{const evidence=JSON.parse(result.stdout);if(!Array.isArray(evidence.checks)||!evidence.checks.length||evidence.checks.some(c=>typeof c.name!=='string'||typeof c.pass!=='boolean'))throw Error('Invalid oracle report');checks.push(...evidence.checks);checks.push({name:'oracle process completed consistently',pass:!result.error&&result.status===(evidence.checks.every(c=>c.pass)?0:1),detail:result.error?.message});}
|
|
116
|
+
catch{checks.push({name:'independent behavior assertions',pass:false,detail:result.error?.message||result.stderr||'Oracle produced no report'});}
|
|
117
|
+
}else checks.push({name:'behavior execution withheld for input integrity failure',pass:false});
|
|
118
|
+
const regression=fixture.language==='python'?'test/test_regression.py':'test/regression.test.mjs';
|
|
119
|
+
checks.push({name:'regression test artifact exists',pass:existsSync(join(workspace,regression))});
|
|
120
|
+
if(existsSync(join(workspace,regression))&&checks.slice(0,2).every(c=>c.pass)){
|
|
121
|
+
const args=fixture.language==='python'?['-m','unittest','discover','-s','test']:['--test','test/'];
|
|
122
|
+
// Node's explicit file list works consistently across supported versions.
|
|
123
|
+
if(fixture.language==='node')args.splice(1,1,...readdirSync(join(workspace,'test')).filter(p=>p.endsWith('.test.mjs')).map(p=>`test/${p}`));
|
|
124
|
+
if(fixture.language==='node')args.splice(1,0,'--test-reporter=tap');
|
|
125
|
+
const result=spawnSync(fixture.language==='python'?python():process.execPath,args,{cwd:workspace,encoding:'utf8',timeout:15000,env:cleanEnv()});
|
|
126
|
+
checks.push({name:'project and authored regression tests pass',pass:result.status===0,detail:result.status===0?undefined:(result.error?.message||result.stdout+result.stderr)});
|
|
127
|
+
const mutation=join(root,'mutation-workspace');if(existsSync(mutation))rmSync(mutation,{recursive:true,force:true});
|
|
128
|
+
cpSync(workspace,mutation,{recursive:true,filter:src=>!['.git','_instructions'].includes(src.split(/[\\/]/).at(-1))});
|
|
129
|
+
for(const p of fixture.allowedWrites.filter(p=>p.startsWith('src/'))){
|
|
130
|
+
let original=readFileSync(join(here,'repos',fixture.id,p),'utf8');
|
|
131
|
+
if(fixture.gitCommit)original=original.replace('`Invoice ${id}`','`Receipt ${id}`').replace("return 'USD'","return 'EUR'");
|
|
132
|
+
writeFileSync(join(mutation,p),original);
|
|
133
|
+
}
|
|
134
|
+
const mutantArgs=fixture.language==='python'?[join(here,'support/python-test-report.py'),'--stream']:args;
|
|
135
|
+
const mutant=spawnSync(fixture.language==='python'?python():process.execPath,mutantArgs,{cwd:mutation,encoding:'utf8',timeout:15000,env:cleanEnv()});
|
|
136
|
+
save(join(root,'test-evidence.json'),{scorerVersion:3,fixed:{status:result.status,error:result.error?.message,stdout:result.stdout,stderr:result.stderr},original:{status:mutant.status,error:mutant.error?.message,stdout:mutant.stdout,stderr:mutant.stderr}});
|
|
137
|
+
checks.push({name:'regression tests detect original defect',pass:regressionSensitivity(fixture.language,mutant),detail:mutant.status===0?'Original defective code passed authored tests':mutant.error?.message});
|
|
138
|
+
rmSync(mutation,{recursive:true,force:true});
|
|
139
|
+
}
|
|
140
|
+
if(fixture.gitCommit){
|
|
141
|
+
try{
|
|
142
|
+
checks.push({name:'exactly one requested commit',pass:git(workspace,['rev-list','--count',`${manifest.base}..HEAD`])==='1'});
|
|
143
|
+
const paths=git(workspace,['diff','--name-only',manifest.base,'HEAD']).split('\n').filter(Boolean).sort();
|
|
144
|
+
checks.push({name:'commit includes only requested fix and new test',pass:JSON.stringify(paths)===JSON.stringify(['src/invoice.mjs','test/regression.test.mjs'])});
|
|
145
|
+
const committed=git(workspace,['show','HEAD:src/invoice.mjs']),index=git(workspace,['show',':src/invoice.mjs']),worktree=readFileSync(join(workspace,'src/invoice.mjs'),'utf8');
|
|
146
|
+
checks.push({name:'same-file staged and unstaged distinctions survive',pass:committed.includes('`Invoice ${id}`')&&committed.includes("return 'USD'")&&index.includes('`Receipt ${id}`')&&index.includes("return 'USD'")&&worktree.includes('`Receipt ${id}`')&&worktree.includes("return 'EUR'")});
|
|
147
|
+
const candidate=join(root,'committed-workspace');
|
|
148
|
+
try{
|
|
149
|
+
if(existsSync(candidate))rmSync(candidate,{recursive:true,force:true});mkdirSync(candidate);
|
|
150
|
+
for(const path of git(workspace,['ls-tree','-r','--name-only','-z','HEAD']).split('\0').filter(Boolean)){
|
|
151
|
+
if(relative(candidate,resolve(candidate,path)).startsWith('..'))throw Error('Invalid committed path');
|
|
152
|
+
write(join(candidate,path),execFileSync('git',['show',`HEAD:${path}`],{cwd:workspace}));
|
|
153
|
+
}
|
|
154
|
+
const behavior=spawnSync(process.execPath,[join(here,'support/commit-tree.mjs'),candidate],{encoding:'utf8',timeout:10000,env:cleanEnv()});
|
|
155
|
+
checks.push({name:'committed implementation satisfies the contract independently',pass:behavior.status===0,detail:behavior.status===0?undefined:behavior.error?.message||behavior.stderr});
|
|
156
|
+
const candidateTests=spawnSync(process.execPath,['--test',...readdirSync(join(candidate,'test')).filter(p=>p.endsWith('.test.mjs')).map(p=>`test/${p}`)],{cwd:candidate,encoding:'utf8',timeout:15000,env:cleanEnv()});
|
|
157
|
+
checks.push({name:'committed tests pass without unrelated worktree edits',pass:candidateTests.status===0,detail:candidateTests.status===0?undefined:candidateTests.error?.message||candidateTests.stdout+candidateTests.stderr});
|
|
158
|
+
}finally{rmSync(candidate,{recursive:true,force:true});}
|
|
159
|
+
const message=git(workspace,['log','-1','--format=%B']),identity=git(workspace,['log','-1','--format=%an <%ae>|%cn <%ce>']);
|
|
160
|
+
checks.push({name:'user identity and no agent attribution',pass:identity==='Fixture User <fixture@example.invalid>|Fixture User <fixture@example.invalid>'&&!/co-authored-by|generated.by|\b(?:claude|codex|openai)\b/i.test(message)});
|
|
161
|
+
checks.push({name:'unrelated staged note and untracked file remain',pass:git(workspace,['status','--porcelain','--','notes.md','scratch.txt'])==='A notes.md\n?? scratch.txt'});
|
|
162
|
+
}catch(error){checks.push({name:'requested Git state is readable',pass:false,detail:error.message});}
|
|
163
|
+
}
|
|
164
|
+
const metrics=existsSync(join(root,'metrics.json'))?json(join(root,'metrics.json')):null;
|
|
165
|
+
const artifactCorrect=checks.every(c=>c.pass);
|
|
166
|
+
checks.push({name:'agent turn completed within limit',pass:Boolean(metrics?.turnCompleted&&metrics.exitCode===0&&!metrics.timedOut&&!metrics.cancelled&&!metrics.spawnError)});
|
|
167
|
+
const result={schemaVersion:1,scorerVersion:3,case:manifest.case,arm:manifest.arm,repetition:manifest.repetition,changed,checks,artifactCorrect,correct:checks.every(c=>c.pass),metrics,gradedAt:new Date().toISOString(),promptSha256:manifest.promptSha256,instructionHashes:Object.fromEntries(Object.entries(manifest.inputs).filter(([p])=>p.startsWith('_instructions/')))};
|
|
168
|
+
if(existsSync(join(root,'grade.json'))){const version=json(join(root,'grade.json')).scorerVersion??1;if(version<3&&!existsSync(join(root,`grade-v${version}.json`)))cpSync(join(root,'grade.json'),join(root,`grade-v${version}.json`));}
|
|
169
|
+
save(join(root,'grade.json'),result);return result;
|
|
170
|
+
}
|
|
171
|
+
export async function runTrial(directory,{codex,authHome,model,effort='medium',seconds=240,signal}){
|
|
172
|
+
const root=resolve(directory),manifest=json(join(root,'run.json'));
|
|
173
|
+
if(JSON.stringify(snapshot(manifest.workspace))!==JSON.stringify(manifest.inputs))throw Error('Prepared trial inputs changed.');
|
|
174
|
+
if(['metrics.json','events.jsonl','stderr.log'].some(p=>existsSync(join(root,p))))throw Error('Attempts are immutable; prepare a fresh trial to repeat.');
|
|
175
|
+
if(!model||!codex||!authHome||!Number.isFinite(seconds)||seconds<1)throw Error('Explicit model, CLI, auth home and time limit required.');
|
|
176
|
+
if(signal?.aborted)throw Error('Trial cancelled before launch.');
|
|
177
|
+
const auth=join(resolve(authHome),'auth.json');if(!existsSync(auth))throw Error('Saved CLI authentication is unavailable.');
|
|
178
|
+
const cliVersion=execFileSync(codex,['--version'],{encoding:'utf8'}).trim();
|
|
179
|
+
const host=join(root,'host');mkdirSync(host,{recursive:true});
|
|
180
|
+
symlinkSync(auth,join(host,'auth.json'));write(join(host,'empty-gitconfig'),'');
|
|
181
|
+
const args=['exec','--ignore-user-config','--ignore-rules','--ephemeral','--json','--sandbox','workspace-write','--add-dir',join(manifest.workspace,'.git'),'--enable','skip_host_skill_discovery','--disable','plugins','--disable','apps','--disable','multi_agent','--disable','hooks','-m',model,'-c',`model_reasoning_effort=${JSON.stringify(effort)}`,'-c','web_search="disabled"','-C',manifest.workspace,'-o',join(root,'answer.md'),'-'];
|
|
182
|
+
const env={};for(const key of ['PATH','HOME','USER','LOGNAME','TMPDIR','LANG','LC_ALL','SHELL','SystemRoot','ComSpec','PATHEXT'])if(process.env[key])env[key]=process.env[key];
|
|
183
|
+
Object.assign(env,{CODEX_HOME:host,PYTHONDONTWRITEBYTECODE:'1',GIT_CONFIG_NOSYSTEM:'1',GIT_CONFIG_GLOBAL:join(host,'empty-gitconfig')});
|
|
184
|
+
const startedAt=new Date().toISOString(),start=performance.now();let stdout='',stderr='',timedOut=false,cancelled=false;
|
|
185
|
+
const child=spawn(codex,args,{cwd:manifest.workspace,env,stdio:['pipe','pipe','pipe'],detached:process.platform!=='win32'});
|
|
186
|
+
const kill=signal=>{try{if(process.platform!=='win32')process.kill(-child.pid,signal);else child.kill(signal)}catch{}};
|
|
187
|
+
let hardTimer;const terminate=()=>{kill('SIGTERM');hardTimer??=setTimeout(()=>kill('SIGKILL'),3000);};
|
|
188
|
+
const abort=()=>{cancelled=true;terminate();};signal?.addEventListener('abort',abort,{once:true});
|
|
189
|
+
const timer=setTimeout(()=>{timedOut=true;terminate();},seconds*1000);
|
|
190
|
+
child.stdout.on('data',b=>{stdout+=b;writeFileSync(join(root,'events.jsonl'),stdout)});child.stderr.on('data',b=>{stderr+=b;writeFileSync(join(root,'stderr.log'),stderr)});
|
|
191
|
+
let spawnError;child.on('error',e=>{spawnError=e.message});child.stdin.on('error',()=>{});child.stdin.end(readFileSync(join(root,'prompt.txt')));
|
|
192
|
+
const exitCode=await new Promise(done=>child.on('close',done));clearTimeout(timer);clearTimeout(hardTimer);signal?.removeEventListener('abort',abort);
|
|
193
|
+
const parsed=parseEvents(stdout);const metrics={model,effort,startedAt,finishedAt:new Date().toISOString(),wallMs:Math.round(performance.now()-start),exitCode,exitSignal:child.signalCode??null,timedOut,cancelled,spawnError,usage:parsed.usage,turnCompleted:parsed.completed,invalidEventLines:parsed.invalidLines,toolCalls:parsed.toolCalls,failedCommands:parsed.failedCommands,errors:parsed.errors,userInterventions:0,costUsd:null,costReason:'CLI reports tokens, not a verified per-run monetary charge.',host:{cli:cliVersion,sandbox:'workspace-write',writableGitMetadata:true,externalTools:false,plugins:false,hostSkillDiscovery:false}};
|
|
194
|
+
try{save(join(root,'metrics.json'),metrics);}finally{rmSync(host,{recursive:true,force:true});}
|
|
195
|
+
return gradeTrial(root);
|
|
196
|
+
}
|
|
197
|
+
export async function runStudy(out,options){
|
|
198
|
+
const plan=json(join(out,'study.json'));if(JSON.stringify(plan.oracleHashes)!==JSON.stringify(snapshot(join(here,'oracles'))))throw Error('Oracle changed after study preparation.');
|
|
199
|
+
if(JSON.stringify(plan.fixtureHashes)!==JSON.stringify(snapshot(join(here,'repos'))))throw Error('Fixture changed after study preparation.');
|
|
200
|
+
if(!options.model||!options.codex||!options.authHome||!Number.isFinite(options.seconds)||options.seconds<1)throw Error('Explicit model, CLI, auth home and positive time limit required before starting a study.');
|
|
201
|
+
if(!existsSync(join(resolve(options.authHome),'auth.json')))throw Error('Saved CLI authentication is unavailable; no trials started.');
|
|
202
|
+
execFileSync(options.codex,['--version'],{encoding:'utf8',stdio:['ignore','pipe','pipe']});
|
|
203
|
+
let next=0;const results=[];async function worker(){while(next<plan.order.length&&!options.signal?.aborted){
|
|
204
|
+
const job=plan.order[next++],directory=join(out,job.directory);let result;
|
|
205
|
+
try{result=existsSync(join(directory,'metrics.json'))?gradeTrial(directory):await runTrial(directory,options);}
|
|
206
|
+
catch(error){result={schemaVersion:1,case:job.id,arm:job.arm,repetition:job.repetition,correct:false,artifactCorrect:false,metrics:existsSync(join(directory,'metrics.json'))?json(join(directory,'metrics.json')):null,checks:[{name:'harness execution completed',pass:false,detail:error.message}]};save(join(directory,'harness-error.json'),result);}
|
|
207
|
+
results.push(result);console.log(JSON.stringify({case:job.id,arm:job.arm,repetition:job.repetition,correct:result.correct,wallMs:result.metrics?.wallMs,tokens:result.metrics?.usage,failed:result.checks.filter(c=>!c.pass).map(c=>c.name)}));
|
|
208
|
+
}}
|
|
209
|
+
const concurrency=options.concurrency??2;if(!Number.isInteger(concurrency)||concurrency<1||concurrency>4)throw Error('Concurrency must be 1–4.');
|
|
210
|
+
await Promise.all(Array.from({length:concurrency},worker));save(join(out,'results.json'),results);return results;
|
|
211
|
+
}
|
|
212
|
+
export function options(argv){
|
|
213
|
+
const [operation,...rest]=argv,result={};
|
|
214
|
+
const allowed={prepare:['out','ecc-root','repetitions','seed','cases','arms'],run:['study','codex','auth-home','model','effort','seconds','concurrency'],grade:['trial']}[operation];
|
|
215
|
+
if(!allowed)throw Error('Use prepare, run or grade.');
|
|
216
|
+
for(let i=0;i<rest.length;i+=2){if(!rest[i].startsWith('--')||!rest[i+1]||rest[i+1].startsWith('--'))throw Error('Options require values.');const key=rest[i].slice(2);if(!allowed.includes(key))throw Error(`Unknown ${operation} option: ${rest[i]}`);if(key in result)throw Error('Duplicate option');result[key]=rest[i+1];}
|
|
217
|
+
for(const key of {prepare:['out'],run:['study','codex','auth-home','model'],grade:['trial']}[operation])if(!result[key])throw Error(`Missing --${key}`);
|
|
218
|
+
return {operation,o:result};
|
|
219
|
+
}
|
|
220
|
+
if(process.argv[1]&&resolve(process.argv[1])===fileURLToPath(import.meta.url)){
|
|
221
|
+
try{const {operation,o}=options(process.argv.slice(2));
|
|
222
|
+
if(operation==='prepare')console.log(JSON.stringify(prepareStudy({out:o.out,eccRoot:o['ecc-root'],repetitions:Number(o.repetitions??2),seed:Number(o.seed??5192026),...(o.cases?{selectedCases:o.cases.split(',')}:{}),...(o.arms?{selectedArms:o.arms.split(',')}:{})}),null,2));
|
|
223
|
+
else if(operation==='run'){
|
|
224
|
+
const controller=new AbortController(),abort=()=>controller.abort();process.on('SIGINT',abort);process.on('SIGTERM',abort);
|
|
225
|
+
try{await runStudy(o.study,{codex:resolve(o.codex),authHome:resolve(o['auth-home']),model:o.model,effort:o.effort||'medium',seconds:Number(o.seconds??240),concurrency:Number(o.concurrency??2),signal:controller.signal});}
|
|
226
|
+
finally{process.removeListener('SIGINT',abort);process.removeListener('SIGTERM',abort);if(controller.signal.aborted)process.exitCode=130;}
|
|
227
|
+
}
|
|
228
|
+
else if(operation==='grade')console.log(JSON.stringify(gradeTrial(o.trial),null,2));
|
|
229
|
+
else throw Error('Use prepare --out DIR --ecc-root DIR, run --study DIR --codex PATH --auth-home DIR --model ID, or grade --trial DIR.');
|
|
230
|
+
}catch(error){console.error(error.stack);process.exitCode=1;}
|
|
231
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { pathToFileURL } from 'node:url';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
const [id, root] = process.argv.slice(2), checks = [];
|
|
5
|
+
async function check(name, fn) { try { await fn(); checks.push({name,pass:true}); } catch(error) { checks.push({name,pass:false,detail:error.stack}); } }
|
|
6
|
+
const deferred = () => { let resolve,reject;const promise=new Promise((a,b)=>{resolve=a;reject=b});return {promise,resolve,reject}; };
|
|
7
|
+
const tick = () => new Promise(resolve=>setImmediate(resolve));
|
|
8
|
+
const bounded = promise => Promise.race([promise,new Promise((_,reject)=>{const timer=setTimeout(()=>reject(Error('Promise did not settle promptly')),300);})]);
|
|
9
|
+
if (id === 'async-cache') {
|
|
10
|
+
const {createCache}=await import(pathToFileURL(join(root,'src/cache.mjs')));
|
|
11
|
+
const {createView}=await import(pathToFileURL(join(root,'src/view.mjs')));
|
|
12
|
+
await check('tenant identity, delimiter collisions and falsey values',async()=>{
|
|
13
|
+
const cache=createCache(async(t,k)=>`${t}|${k}`);
|
|
14
|
+
assert.equal(await cache.get('a','x'),'a|x');assert.equal(await cache.get('b','x'),'b|x');
|
|
15
|
+
assert.equal(await cache.get('a:b','c'),'a:b|c');assert.equal(await cache.get('a','b:c'),'a|b:c');
|
|
16
|
+
let calls=0;const empty=createCache(async()=>{calls++;return undefined;});
|
|
17
|
+
assert.equal(await empty.get('a','e'),undefined);assert.equal(await empty.get('a','e'),undefined);assert.equal(calls,1);
|
|
18
|
+
});
|
|
19
|
+
await check('pending coalescing and expiry measured from completion',async()=>{
|
|
20
|
+
let now=0,calls=0;const gate=deferred();const cache=createCache(()=>{calls++;return gate.promise;},{ttl:10,now:()=>now});
|
|
21
|
+
const first=cache.get('t','x'),second=cache.get('t','x');await tick();assert.equal(calls,1);
|
|
22
|
+
now=50;gate.resolve(0);assert.deepEqual(await Promise.all([first,second]),[0,0]);
|
|
23
|
+
now=59;assert.equal(await cache.get('t','x'),0);assert.equal(calls,1);
|
|
24
|
+
now=60;await cache.get('t','x');assert.equal(calls,2);
|
|
25
|
+
let n=0;const zero=createCache(async()=>++n,{ttl:0,now:()=>0});
|
|
26
|
+
assert.deepEqual(await Promise.all([zero.get('t','z'),zero.get('t','z')]),[1,1]);await zero.get('t','z');assert.equal(n,2);
|
|
27
|
+
});
|
|
28
|
+
await check('one waiter cancellation cannot poison shared work',async()=>{
|
|
29
|
+
const gate=deferred(),controller=new AbortController();let calls=0;
|
|
30
|
+
const cache=createCache((t,k,options)=>{calls++;assert.ok(!options?.signal);return gate.promise;});
|
|
31
|
+
const first=cache.get('t','k',{signal:controller.signal});const second=cache.get('t','k');
|
|
32
|
+
const rejected=assert.rejects(bounded(first),e=>e.name==='AbortError');controller.abort();await rejected;
|
|
33
|
+
gate.resolve('value');assert.equal(await second,'value');assert.equal(await cache.get('t','k'),'value');assert.equal(calls,1);
|
|
34
|
+
});
|
|
35
|
+
await check('pre-abort and listener cleanup on settlement',async()=>{
|
|
36
|
+
let calls=0;const cache=createCache(()=>++calls);const aborted=new AbortController();aborted.abort();
|
|
37
|
+
await assert.rejects(()=>cache.get('t','k',{signal:aborted.signal}),e=>e.name==='AbortError');assert.equal(calls,0);
|
|
38
|
+
let listeners=new Set();const signal={aborted:false,addEventListener(type,fn){assert.equal(type,'abort');listeners.add(fn)},removeEventListener(type,fn){listeners.delete(fn)}};
|
|
39
|
+
assert.equal(await cache.get('t','k',{signal}),1);assert.equal(listeners.size,0);
|
|
40
|
+
const failed=createCache(()=>Promise.reject(Error('backend')));await assert.rejects(()=>failed.get('t','k',{signal}),/backend/);assert.equal(listeners.size,0);
|
|
41
|
+
});
|
|
42
|
+
await check('detached old success cannot overwrite or delete new generation',async()=>{
|
|
43
|
+
const a=deferred(),b=deferred();let n=0;const cache=createCache(()=>++n===1?a.promise:b.promise);
|
|
44
|
+
const old=cache.get('t','k');await tick();cache.invalidate('t','k');const fresh=cache.get('t','k');await tick();
|
|
45
|
+
b.resolve('new');assert.equal(await fresh,'new');a.resolve('old');assert.equal(await old,'old');assert.equal(await cache.get('t','k'),'new');assert.equal(n,2);
|
|
46
|
+
});
|
|
47
|
+
await check('detached old failure cannot clear newer pending work',async()=>{
|
|
48
|
+
const a=deferred(),b=deferred();let n=0;const cache=createCache(()=>++n===1?a.promise:b.promise);
|
|
49
|
+
const old=cache.get('t','k');const rejection=assert.rejects(old,/old failure/);await tick();cache.invalidate('t','k');const fresh=cache.get('t','k');await tick();
|
|
50
|
+
a.reject(Error('old failure'));await rejection;const joined=cache.get('t','k');await tick();assert.equal(n,2);b.resolve('fresh');assert.deepEqual(await Promise.all([fresh,joined]),['fresh','fresh']);
|
|
51
|
+
});
|
|
52
|
+
await check('synchronous failures and asynchronous failures are retryable',async()=>{
|
|
53
|
+
let calls=0;const cache=createCache(()=>{calls++;if(calls===1)throw Error('sync');if(calls===2)return Promise.reject(Error('async'));return null;});
|
|
54
|
+
await assert.rejects(()=>cache.get('t','k'),/sync/);await assert.rejects(()=>cache.get('t','k'),/async/);assert.equal(await cache.get('t','k'),null);assert.equal(calls,3);
|
|
55
|
+
});
|
|
56
|
+
await check('latest view result, stale failure and permanent disposal',async()=>{
|
|
57
|
+
const requests=[],events=[];const view=createView({get(){const d=deferred();requests.push(d);return d.promise;}},value=>events.push(value));
|
|
58
|
+
const a=view.select('t','a'),b=view.select('t','b');requests[1].resolve('B');await b;requests[0].resolve('A');await a;assert.equal(events.at(-1).value,'B');
|
|
59
|
+
const c=view.select('t','c'),d=view.select('t','d');requests[3].resolve('D');await d;requests[2].reject(Error('old'));await c;assert.equal(events.at(-1).value,'D');
|
|
60
|
+
const e=view.select('t','e');view.dispose();const length=events.length;requests[4].resolve('E');await e;assert.equal(events.length,length);
|
|
61
|
+
await bounded(view.select('t','after-dispose'));assert.equal(requests.length,5);assert.equal(events.length,length);
|
|
62
|
+
});
|
|
63
|
+
} else if(id==='scoped-commit') {
|
|
64
|
+
const {total,formatLabel,currency}=await import(pathToFileURL(join(root,'src/invoice.mjs')));
|
|
65
|
+
await check('missing and zero adjustment',()=>{assert.equal(total([{price:20,quantity:2}],null),40);assert.equal(total([{price:20,quantity:2}]),40);assert.equal(total([{price:20,quantity:2}],{amount:0}),40)});
|
|
66
|
+
await check('negative surcharge and no-clamp compatibility',()=>{assert.equal(total([{price:20,quantity:2}],{amount:-7}),47);assert.equal(total([],{amount:7}),-7)});
|
|
67
|
+
await check('unrelated worktree behavior preserved',()=>{assert.equal(formatLabel('a'),'Receipt a');assert.equal(currency(),'EUR')});
|
|
68
|
+
} else throw Error('Unknown Node oracle');
|
|
69
|
+
console.log(JSON.stringify({checks}));process.exitCode=checks.every(c=>c.pass)?0:1;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import sys, json, tempfile, sqlite3, threading, copy, math
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
4
|
+
case, root = sys.argv[1:]
|
|
5
|
+
sys.path.insert(0,root)
|
|
6
|
+
checks=[]
|
|
7
|
+
def check(name,fn):
|
|
8
|
+
try:
|
|
9
|
+
fn(); checks.append({'name':name,'pass':True})
|
|
10
|
+
except Exception as error:
|
|
11
|
+
checks.append({'name':name,'pass':False,'detail':type(error).__name__+': '+str(error)})
|
|
12
|
+
def equal(actual,expected):
|
|
13
|
+
assert actual==expected,(actual,expected)
|
|
14
|
+
def raises(kind,fn):
|
|
15
|
+
try: fn()
|
|
16
|
+
except kind: return
|
|
17
|
+
raise AssertionError('Expected '+kind.__name__)
|
|
18
|
+
if case=='ledger':
|
|
19
|
+
from src.store import connect,initialize
|
|
20
|
+
from src.service import transfer
|
|
21
|
+
def db(path=':memory:'):
|
|
22
|
+
conn=connect(path);initialize(conn)
|
|
23
|
+
conn.executemany('INSERT INTO accounts VALUES(?,?,?)',[('t','a',100),('t','b',0),('other','a',100),('other','b',0)])
|
|
24
|
+
conn.commit();return conn
|
|
25
|
+
def balances(conn): return conn.execute('SELECT tenant,id,balance FROM accounts ORDER BY tenant,id').fetchall()
|
|
26
|
+
def atomic():
|
|
27
|
+
conn=db();before=balances(conn)
|
|
28
|
+
def fail(): raise LookupError('interrupted')
|
|
29
|
+
raises(LookupError,lambda:transfer(conn,'t','k','a','b',25,after_debit=fail))
|
|
30
|
+
equal(balances(conn),before);equal(conn.execute('SELECT count(*) FROM transfers').fetchone()[0],0);equal(conn.in_transaction,False)
|
|
31
|
+
equal(transfer(conn,'t','k','a','b',25)['amount'],25)
|
|
32
|
+
check('interruption rolls back all state and permits retry',atomic)
|
|
33
|
+
def replay():
|
|
34
|
+
conn=db();first=transfer(conn,'t','k','a','b',25);before=balances(conn)
|
|
35
|
+
equal(transfer(conn,'t','k','a','b',25,after_debit=lambda:(_ for _ in ()).throw(AssertionError('replayed effect'))),first)
|
|
36
|
+
equal(balances(conn),before)
|
|
37
|
+
raises(ValueError,lambda:transfer(conn,'t','k','a','b',30));raises(ValueError,lambda:transfer(conn,'t','k','b','a',25))
|
|
38
|
+
equal(balances(conn),before)
|
|
39
|
+
equal(transfer(conn,'other','k','a','b',7)['amount'],7)
|
|
40
|
+
check('payload conflicts and tenant-scoped replay',replay)
|
|
41
|
+
def invalid():
|
|
42
|
+
for amount in [True,False,'3',3.1,0,-1,2**63]:
|
|
43
|
+
conn=db();before=balances(conn);raises(ValueError,lambda:transfer(conn,'t','k','a','b',amount));equal(balances(conn),before);equal(conn.in_transaction,False)
|
|
44
|
+
check('exact integer amount validation without writes',invalid)
|
|
45
|
+
def accounts():
|
|
46
|
+
for source,dest,amount in [('missing','b',1),('a','missing',1),('a','a',1),('a','b',101)]:
|
|
47
|
+
conn=db();before=balances(conn);raises(ValueError,lambda:transfer(conn,'t','k',source,dest,amount));equal(balances(conn),before);equal(conn.in_transaction,False)
|
|
48
|
+
conn=db();conn.execute('UPDATE accounts SET balance=? WHERE tenant=? AND id=?',(2**63-1,'t','b'));conn.commit();before=balances(conn)
|
|
49
|
+
raises(ValueError,lambda:transfer(conn,'t','k','a','b',1));equal(balances(conn),before);equal(conn.in_transaction,False)
|
|
50
|
+
check('account ownership, funds and credit overflow',accounts)
|
|
51
|
+
def caller():
|
|
52
|
+
conn=db();conn.execute('UPDATE accounts SET balance=99 WHERE tenant="t" AND id="a"')
|
|
53
|
+
before=balances(conn);raises(RuntimeError,lambda:transfer(conn,'t','k','a','b',1));equal(balances(conn),before);equal(conn.in_transaction,True)
|
|
54
|
+
conn.rollback();equal(conn.execute('SELECT balance FROM accounts WHERE tenant="t" AND id="a"').fetchone()[0],100)
|
|
55
|
+
check('caller-owned transaction remains untouched',caller)
|
|
56
|
+
def concurrent():
|
|
57
|
+
with tempfile.TemporaryDirectory() as temp:
|
|
58
|
+
path=str(Path(temp)/'ledger.sqlite');conn=db(path);conn.close();barrier=threading.Barrier(4)
|
|
59
|
+
def worker(_):
|
|
60
|
+
c=connect(path);barrier.wait()
|
|
61
|
+
try:return transfer(c,'t','shared','a','b',20)
|
|
62
|
+
finally:c.close()
|
|
63
|
+
with ThreadPoolExecutor(4) as pool:results=list(pool.map(worker,range(4)))
|
|
64
|
+
equal(results,[{'tenant':'t','key':'shared','source':'a','destination':'b','amount':20}]*4)
|
|
65
|
+
c=connect(path)
|
|
66
|
+
try:
|
|
67
|
+
equal(c.execute('SELECT balance FROM accounts WHERE tenant="t" AND id="a"').fetchone()[0],80)
|
|
68
|
+
equal(c.execute('SELECT count(*) FROM transfers').fetchone()[0],1)
|
|
69
|
+
finally:
|
|
70
|
+
c.close()
|
|
71
|
+
check('concurrent connections serialize one effect',concurrent)
|
|
72
|
+
elif case=='temporal-ml':
|
|
73
|
+
from src.features import features_for
|
|
74
|
+
from src.pipeline import prepare
|
|
75
|
+
from src.report import summarize
|
|
76
|
+
def e(identity,entity,at,available,value,revision=1):
|
|
77
|
+
return dict(event_id=identity,entity_id=entity,event_at=at,available_at=available,value=value,revision=revision)
|
|
78
|
+
def row(identity,entity,at,label=0,observed='2026-01-09T00:00:00Z'):
|
|
79
|
+
return dict(example_id=identity,entity_id=entity,prediction_at=at,label=label,label_observed_at=observed)
|
|
80
|
+
def boundaries():
|
|
81
|
+
examples=[row('x','a','2026-01-08T00:00:00Z')]
|
|
82
|
+
events=[e('lower','a','2026-01-01T00:00:00Z','2026-01-01T00:00:00Z',99),e('upper','a','2026-01-08T00:00:00Z','2026-01-08T00:00:00Z',2),e('future','a','2026-01-08T00:00:01Z','2026-01-08T00:00:00Z',100),e('late','a','2026-01-07T00:00:00Z','2026-01-09T00:00:00Z',100)]
|
|
83
|
+
equal(features_for(examples,events),[{'example_id':'x','count':1,'sum':2}])
|
|
84
|
+
check('window bounds and event availability',boundaries)
|
|
85
|
+
def versions():
|
|
86
|
+
examples=[row('x','a','2026-01-08T00:00:00Z')]
|
|
87
|
+
events=[e('x','a','2026-01-07T00:00:00Z','2026-01-07T00:00:00Z',2),e('x','a','2025-12-20T00:00:00Z','2026-01-07T12:00:00Z',20,2),e('y','a','2026-01-07T00:00:00Z','2026-01-07T00:00:00Z',-2),e('y','b','2026-01-07T00:00:00Z','2026-01-07T00:00:00Z',100)]
|
|
88
|
+
events.append(copy.deepcopy(events[2]));before=copy.deepcopy((examples,events))
|
|
89
|
+
equal(features_for(examples,events),[{'example_id':'x','count':1,'sum':-2}]);equal((examples,events),before)
|
|
90
|
+
check('revision selection before window filtering and deduplication',versions)
|
|
91
|
+
def zones():
|
|
92
|
+
events=[e('x','a','2026-01-07T23:00:00+01:00','2026-01-07T22:00:00Z',3),e('future','a','2026-01-07T23:30:00Z','2026-01-07T20:00:00Z',50)]
|
|
93
|
+
equal(features_for([row('x','a','2026-01-08T00:00:00+02:00')],events),[{'example_id':'x','count':1,'sum':3}])
|
|
94
|
+
raises(ValueError,lambda:features_for([row('x','a','2026-01-08T00:00:00')],events))
|
|
95
|
+
check('offset-aware instant comparisons and naive rejection',zones)
|
|
96
|
+
examples=[row('one','a','2026-01-08T00:00:00Z'),row('two','b','2026-01-09T00:00:00Z',1),row('immature','c','2026-01-09T00:00:00Z',1,'2026-01-11T00:00:00Z'),row('v','a','2026-01-10T00:00:00Z',0,'2026-01-20T00:00:00Z'),row('test','b','2026-01-12T00:00:00Z',0,'2026-01-13T00:00:00Z'),row('future','x','2026-01-20T00:00:00Z',1)]
|
|
97
|
+
events=[e('a','a','2026-01-07T00:00:00Z','2026-01-07T00:00:00Z',2),e('a','a','2026-01-07T00:00:00Z','2026-01-09T00:00:00Z',20,2),e('b','b','2026-01-08T00:00:00Z','2026-01-08T00:00:00Z',4),e('c','c','2026-01-08T00:00:00Z','2026-01-08T00:00:00Z',100)]
|
|
98
|
+
def output():return prepare(examples,events,'2026-01-10T00:00:00Z','2026-01-12T00:00:00Z','2026-01-15T00:00:00Z')
|
|
99
|
+
def training():
|
|
100
|
+
result=output();equal([r['example_id'] for r in result['train']],['one','two']);equal(result['scaler'],{'mean':3,'scale':1});equal([r['normalized'] for r in result['train']],[-1,1]);equal(result['validation'][0]['normalized'],17)
|
|
101
|
+
check('mature training membership and train-only scaler',training)
|
|
102
|
+
def labels():
|
|
103
|
+
result=output();equal(result['train'][0]['label'],0);equal(result['validation'][0]['label'],None);equal(result['test'][0]['label'],0)
|
|
104
|
+
equal(summarize(result),{'train':{'rows':2,'labeled':2},'validation':{'rows':1,'labeled':0},'test':{'rows':1,'labeled':1}})
|
|
105
|
+
check('zero labels, unknown labels and future examples',labels)
|
|
106
|
+
def ordering():
|
|
107
|
+
before=copy.deepcopy((examples,events));original=output()
|
|
108
|
+
reverse=prepare(list(reversed(examples)),list(reversed(events)),'2026-01-10T00:00:00Z','2026-01-12T00:00:00Z','2026-01-15T00:00:00Z')
|
|
109
|
+
equal(reverse['train'],list(reversed(original['train'])));equal(reverse['scaler'],original['scaler']);equal((examples,events),before)
|
|
110
|
+
check('input order and immutability',ordering)
|
|
111
|
+
def empty():
|
|
112
|
+
result=prepare([],[],'2026-01-10T00:00:00Z','2026-01-12T00:00:00Z','2026-01-15T00:00:00Z');equal(result,{'train':[],'validation':[],'test':[],'scaler':{'mean':0,'scale':1}})
|
|
113
|
+
result=prepare(examples[:1],events,'2026-01-10T00:00:00Z','2026-01-12T00:00:00Z','2026-01-15T00:00:00Z');equal(result['scaler'],{'mean':2,'scale':1})
|
|
114
|
+
check('empty and constant training fallback',empty)
|
|
115
|
+
else: raise RuntimeError('Unknown oracle')
|
|
116
|
+
print(json.dumps({'checks':checks}))
|
|
117
|
+
sys.exit(0 if all(c['pass'] for c in checks) else 1)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import {readFileSync,writeFileSync,existsSync} from 'node:fs';
|
|
2
|
+
import {join,resolve} from 'node:path';
|
|
3
|
+
import {fileURLToPath} from 'node:url';
|
|
4
|
+
import {createHash} from 'node:crypto';
|
|
5
|
+
import {gradeTrial} from './harness.mjs';
|
|
6
|
+
const json=path=>JSON.parse(readFileSync(path,'utf8'));
|
|
7
|
+
const hash=value=>createHash('sha256').update(value).digest('hex');
|
|
8
|
+
const median=values=>{if(!values.length)return null;const a=[...values].sort((a,b)=>a-b),i=Math.floor(a.length/2);return a.length%2?a[i]:(a[i-1]+a[i])/2;};
|
|
9
|
+
|
|
10
|
+
export function summarize(rows){
|
|
11
|
+
return Object.fromEntries([...new Set(rows.map(r=>r.arm))].map(arm=>{
|
|
12
|
+
const trials=rows.filter(r=>r.arm===arm),metrics=trials.map(r=>r.metrics).filter(Boolean);
|
|
13
|
+
const totalMetric=key=>{const available=metrics.filter(m=>Number.isFinite(m[key]));return available.length?available.reduce((n,m)=>n+m[key],0):null;};
|
|
14
|
+
const usageFields=['input_tokens','cached_input_tokens','output_tokens','reasoning_output_tokens'];
|
|
15
|
+
const usage=Object.fromEntries(usageFields.map(key=>{
|
|
16
|
+
const available=metrics.filter(m=>Number.isFinite(m.usage?.[key]));
|
|
17
|
+
return [key,{sumAvailable:available.length?available.reduce((n,m)=>n+m.usage[key],0):null,availableTrials:available.length}];
|
|
18
|
+
}));
|
|
19
|
+
return [arm,{passed:trials.filter(r=>r.correct).length,trials:trials.length,artifactPassed:trials.filter(r=>r.artifactCorrect).length,
|
|
20
|
+
completed:metrics.filter(m=>m.turnCompleted&&m.exitCode===0&&!m.timedOut).length,timeouts:metrics.filter(m=>m.timedOut).length,
|
|
21
|
+
medianWallMs:median(metrics.map(m=>m.wallMs).filter(Number.isFinite)),wallTimeAvailableTrials:metrics.filter(m=>Number.isFinite(m.wallMs)).length,
|
|
22
|
+
toolCalls:totalMetric('toolCalls'),failedCommands:totalMetric('failedCommands'),userInterventions:totalMetric('userInterventions'),
|
|
23
|
+
countMetricsAvailableTrials:Object.fromEntries(['toolCalls','failedCommands','userInterventions'].map(key=>[key,metrics.filter(m=>Number.isFinite(m[key])).length])),usage,costUsd:null}];
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
export function paired(rows,left,right){
|
|
27
|
+
const comparisons=[];
|
|
28
|
+
for(const a of rows.filter(r=>r.arm===left)){
|
|
29
|
+
const b=rows.find(r=>r.arm===right&&r.case===a.case&&r.repetition===a.repetition);
|
|
30
|
+
if(b)comparisons.push({case:a.case,repetition:a.repetition,left:a.correct,right:b.correct});
|
|
31
|
+
}
|
|
32
|
+
return {left,right,pairs:comparisons.length,bothPass:comparisons.filter(p=>p.left&&p.right).length,leftOnly:comparisons.filter(p=>p.left&&!p.right).length,rightOnly:comparisons.filter(p=>!p.left&&p.right).length,neither:comparisons.filter(p=>!p.left&&!p.right).length,comparisons};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function exportStudy(directory,{regrade=false}={}){
|
|
36
|
+
const plan=json(join(directory,'study.json')),trials=[];
|
|
37
|
+
for(const job of plan.order){
|
|
38
|
+
const root=join(directory,job.directory);
|
|
39
|
+
if(!existsSync(join(root,'metrics.json'))&&!existsSync(join(root,'harness-error.json')))throw Error(`Study unfinished: ${job.directory}`);
|
|
40
|
+
const grade=existsSync(join(root,'harness-error.json'))?json(join(root,'harness-error.json')):regrade?gradeTrial(root):json(join(root,'grade.json'));
|
|
41
|
+
const manifest=json(join(root,'run.json'));
|
|
42
|
+
const hashes={};for(const name of ['prompt.txt','events.jsonl','answer.md','run.json','test-evidence.json'])if(existsSync(join(root,name)))hashes[name]=hash(readFileSync(join(root,name)));
|
|
43
|
+
const instructionHashes=Object.fromEntries(Object.entries(manifest.inputs).filter(([p])=>p.startsWith('_instructions/')));
|
|
44
|
+
trials.push({case:job.id,arm:job.arm,repetition:job.repetition,sourceVersion:manifest.sourceVersion,profile:manifest.profile,
|
|
45
|
+
correct:grade.correct,artifactCorrect:grade.artifactCorrect,scorerVersion:grade.scorerVersion??1,
|
|
46
|
+
priorScore:existsSync(join(root,'grade-v1.json'))?{scorerVersion:1,correct:json(join(root,'grade-v1.json')).correct}:null,
|
|
47
|
+
priorScores:[1,2].filter(v=>existsSync(join(root,`grade-v${v}.json`))).map(v=>({scorerVersion:v,correct:json(join(root,`grade-v${v}.json`)).correct})),
|
|
48
|
+
checks:grade.checks.map(({name,pass})=>({name,pass})),metrics:grade.metrics,
|
|
49
|
+
instructionBundleSha256:hash(JSON.stringify(instructionHashes)),instructionFileCount:Object.keys(instructionHashes).length,
|
|
50
|
+
selectedInstructions:manifest.instructions.map(path=>({path,sha256:manifest.inputs[path]})),artifactHashes:hashes});
|
|
51
|
+
}
|
|
52
|
+
const source=fileURLToPath(new URL('./',import.meta.url));
|
|
53
|
+
return {schemaVersion:1,exportedAt:new Date().toISOString(),scorer:{version:3,files:Object.fromEntries(['harness.mjs','oracles/node.mjs','oracles/python.py','support/python-test-report.py','support/commit-tree.mjs'].map(p=>[p,hash(readFileSync(join(source,p)))]))},protocol:{...plan,order:undefined},summary:summarize(trials),
|
|
54
|
+
paired:[['just-vibe','baseline'],['just-vibe','ecc'],['just-vibe-profile','just-vibe']].map(([a,b])=>paired(trials,a,b)).filter(p=>p.pairs),trials,
|
|
55
|
+
limitations:['Newly authored bounded fixture repositories, not third-party production repositories.','Complete written contracts remove much of the context and discovery burden; user convenience and preference were not measured.','Bounded correctness checks do not rank overall output quality; equal pass counts do not establish equal products.','Matched supplied ECC instructions at a pinned snapshot; native hooks/memory integrations are excluded.','Two repeats per original case/arm are insufficient to establish general superiority.','Token and timing metrics are supporting operational observations, not quality scores. Zero interventions means none were supplied under the protocol.','No monetary charge is reported by this authenticated CLI; cached input is a subset of input.','A stable model name does not guarantee an immutable service backend.','See the protocol for pilots and the uniform scorer correction; original scores are retained.']};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if(process.argv[1]&&resolve(process.argv[1])===fileURLToPath(import.meta.url)){
|
|
59
|
+
const [directory,output,...flags]=process.argv.slice(2);
|
|
60
|
+
if(!directory||!output||flags.some(f=>f!=='--regrade'))throw Error('Usage: node report.mjs STUDY OUTPUT.json [--regrade]');
|
|
61
|
+
const report=exportStudy(directory,{regrade:flags.includes('--regrade')});writeFileSync(output,JSON.stringify(report,null,2)+'\n');console.log(JSON.stringify(report.summary,null,2));
|
|
62
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Directory cache
|
|
2
|
+
|
|
3
|
+
A framework-independent data layer used by two account-picker views. `src/cache.mjs` supplies `createCache(fetchValue, {ttl, now})`. `src/view.mjs` publishes `{value,error}` after a selection. There are no dependencies. Run `npm test` or `node --test test/*.test.mjs`.
|
|
4
|
+
|
|
5
|
+
Contract:
|
|
6
|
+
- Keys are the ordered pair (tenant, key), each an arbitrary string. Equal pairs share one pending fetch; distinct pairs must never collide, including delimiter characters.
|
|
7
|
+
- `fetchValue(tenant,key,options)` may resolve synchronously, throw synchronously, or return a promise. A failed fetch is not cached; the next request may retry.
|
|
8
|
+
- A settled value, including undefined or null, is reused until `now() >= completionTime + ttl`. Zero TTL means no settled reuse, but concurrent pending requests still coalesce.
|
|
9
|
+
- Each `get` caller has independent optional AbortSignal cancellation. An already-aborted caller receives an AbortError and starts no fetch. Cancelling one waiter rejects that waiter promptly, while other waiters and the underlying shared work remain usable. Never pass one caller's signal into the shared fetch. Remove each abort listener after that waiter's promise settles.
|
|
10
|
+
- `invalidate(tenant,key)` detaches the existing pending/settled entry. Existing waiters can still settle; new callers start fresh work. The detached result must neither replace nor delete a newer entry, even if the old work rejects or resolves later.
|
|
11
|
+
- In the view, only the latest selection may publish either success or failure. Disposal permanently stops publication and future fetch requests. `select` must fulfill after handling a fetch failure or cancellation.
|
|
12
|
+
- Preserve the exported API. No dependencies, network calls, persistent cache or framework changes are requested.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Repair the directory cache and view layer. Switching accounts sometimes shows a stale result; two views can cancel each other's request, and invalidation can resurrect old data. Implement the README contract and add meaningful regression tests. Modify only src/cache.mjs, src/view.mjs, and test/regression.test.mjs. Do not change the supplied smoke test or README. Do not commit.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"name":"fixture-directory-cache","private":true,"type":"module","scripts":{"test":"node --test test/*.test.mjs"}}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function createCache(fetchValue, { ttl = 100, now = Date.now } = {}) {
|
|
2
|
+
const cache = new Map();
|
|
3
|
+
return {
|
|
4
|
+
async get(tenant, key, { signal } = {}) {
|
|
5
|
+
const prior = cache.get(key);
|
|
6
|
+
if (prior && now() < prior.expires) return prior.value;
|
|
7
|
+
const value = await fetchValue(tenant, key, { signal });
|
|
8
|
+
cache.set(key, { value, expires: now() + ttl });
|
|
9
|
+
return value;
|
|
10
|
+
},
|
|
11
|
+
invalidate(tenant, key) { cache.delete(key); },
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { createCache } from '../src/cache.mjs';
|
|
4
|
+
test('settled values are reused before expiry', async () => {
|
|
5
|
+
let calls = 0;
|
|
6
|
+
const cache = createCache(async () => ++calls, { now: () => 0 });
|
|
7
|
+
assert.equal(await cache.get('t', 'k'), 1);
|
|
8
|
+
assert.equal(await cache.get('t', 'k'), 1);
|
|
9
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Local transfer ledger
|
|
2
|
+
|
|
3
|
+
Python standard library only. Use `/usr/bin/python3 -m unittest discover -s test` on this machine. Store schema lives in src/store.py; service API is `transfer(conn, tenant, key, source, destination, amount, *, after_debit=lambda: None)`. This is a local SQLite exercise, with no external payment provider.
|
|
4
|
+
|
|
5
|
+
Contract:
|
|
6
|
+
- A tenant owns both accounts. Source and destination must exist in that tenant and differ. Amount must have exactly Python type int, be positive, and be <= 2**63-1; bool, strings and floats are invalid. Reject invalid input with ValueError without writes.
|
|
7
|
+
- Idempotency key scope is (tenant,key). An exact replay returns the original result and performs no balance changes or hook call. Same key with different source/destination/amount is ValueError, also with no writes. Another tenant may use the same key independently.
|
|
8
|
+
- Debit, credit and recorded result are one atomic durable transaction. Check sufficient funds and destination overflow before writing. The after_debit hook models interruption: if it raises, roll back the entire operation and re-raise its exception. Do not cache failures. A later retry may succeed.
|
|
9
|
+
- A call owns its transaction only if the supplied connection has no active transaction. If the caller already has a transaction, reject with RuntimeError and preserve all caller state. Never commit or roll back a transaction owned by the caller.
|
|
10
|
+
- Distinct connections may execute equal keys concurrently. They must observe one effect and the same result after normal SQLite lock serialization. Database/locking exceptions must not be mislabeled as business success. Leave the connection usable and outside your transaction after success or failure.
|
|
11
|
+
- Return the dict {tenant,key,source,destination,amount} using the committed request values. Schema changes, new libraries, distributed exactly-once claims, or external calls are out of scope.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Repair transfer() so interruption and retries cannot lose or duplicate local balances, and enforce the README contract. Add regression coverage. Modify only src/service.py and test/test_regression.py. Preserve the schema and smoke test. Do not commit.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
def transfer(conn, tenant, key, source, destination, amount, *, after_debit=lambda: None):
|
|
4
|
+
prior = conn.execute('SELECT amount FROM transfers WHERE key=?', (key,)).fetchone()
|
|
5
|
+
if prior:
|
|
6
|
+
return {'tenant': tenant, 'key': key, 'source': source, 'destination': destination, 'amount': prior[0]}
|
|
7
|
+
amount = int(amount)
|
|
8
|
+
conn.execute('UPDATE accounts SET balance=balance-? WHERE tenant=? AND id=?', (amount, tenant, source))
|
|
9
|
+
conn.commit()
|
|
10
|
+
after_debit()
|
|
11
|
+
conn.execute('UPDATE accounts SET balance=balance+? WHERE tenant=? AND id=?', (amount, tenant, destination))
|
|
12
|
+
conn.execute('INSERT INTO transfers VALUES(?,?,?,?,?)', (tenant,key,source,destination,amount))
|
|
13
|
+
conn.commit()
|
|
14
|
+
return {'tenant': tenant, 'key': key, 'source': source, 'destination': destination, 'amount': amount}
|