@haposoft/cafekit 0.16.0-rc.7 → 0.16.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/README.md +116 -27
- package/bin/__tests__/codex-hooks-ownership.test.js +239 -0
- package/bin/__tests__/codex-hooks.test.js +295 -0
- package/bin/__tests__/codex-native.test.js +2289 -34
- package/bin/__tests__/develop-contract.test.js +400 -23
- package/bin/__tests__/installer-safety.test.js +3 -3
- package/bin/__tests__/optional-skill-inventory.test.js +249 -0
- package/bin/__tests__/output-styles.test.js +214 -0
- package/bin/__tests__/package-inventory.test.js +2131 -16
- package/bin/__tests__/runtime-schema.test.js +111 -0
- package/bin/__tests__/secret-output-guardrail.test.js +122 -0
- package/bin/__tests__/skill-routing-source.test.js +172 -0
- package/bin/__tests__/specs-v2-execution-closeout.test.js +1 -1
- package/bin/install.js +10 -0
- package/bin/lib/codex-install.js +170 -5
- package/bin/lib/context.js +14 -0
- package/bin/lib/i18n.js +12 -6
- package/bin/lib/skill-deps.js +5 -5
- package/bin/phases/claude-runtime.js +23 -0
- package/bin/phases/claude-settings.js +8 -0
- package/bin/phases/codex-hooks.js +156 -0
- package/bin/phases/codex-runtime.js +29 -38
- package/bin/phases/copy-payload.js +8 -2
- package/bin/phases/select-skill-bundles.js +96 -0
- package/bin/phases/skill-inventory.js +71 -0
- package/bin/phases/skills-setup.js +9 -2
- package/bin/phases/write-metadata.js +8 -5
- package/package.json +8 -2
- package/src/claude/CLAUDE.md +3 -3
- package/src/claude/agents/brainstormer.md +52 -69
- package/src/claude/agents/code-auditor.md +32 -14
- package/src/claude/agents/debugger.md +35 -20
- package/src/claude/agents/deployer.md +12 -5
- package/src/claude/agents/docs-keeper.md +11 -1
- package/src/claude/agents/implementer.md +25 -13
- package/src/claude/agents/inspector.md +6 -3
- package/src/claude/agents/project-manager.md +1 -1
- package/src/claude/agents/researcher.md +26 -55
- package/src/claude/agents/spec-maker.md +24 -13
- package/src/claude/agents/strategist.md +52 -0
- package/src/claude/agents/test-runner.md +117 -132
- package/src/claude/agents/ui-ux-designer.md +1 -1
- package/src/claude/hooks/__tests__/fixtures/statusline-default.golden +1 -0
- package/src/claude/hooks/__tests__/precompact.test.js +158 -0
- package/src/claude/hooks/__tests__/spec-gate.test.js +87 -4
- package/src/claude/hooks/__tests__/state.test.js +254 -3
- package/src/claude/hooks/__tests__/statusline.test.js +196 -0
- package/src/claude/hooks/agent.cjs +1 -1
- package/src/claude/hooks/docs-sync.cjs +12 -10
- package/src/claude/hooks/inspect-block.cjs +1 -1
- package/src/claude/hooks/lib/config.cjs +3 -0
- package/src/claude/hooks/lib/hook-state-dir.cjs +39 -0
- package/src/claude/hooks/lib/secret-keywords.cjs +42 -0
- package/src/claude/hooks/precompact.cjs +105 -0
- package/src/claude/hooks/rules.cjs +1 -1
- package/src/claude/hooks/secret-output-guardrail.cjs +43 -0
- package/src/claude/hooks/session.cjs +27 -1
- package/src/claude/hooks/spec-gate.cjs +3 -3
- package/src/claude/hooks/spec-state.cjs +32 -5
- package/src/claude/hooks/state.cjs +1 -1
- package/src/claude/hooks/task-scaffold-guard.cjs +1 -1
- package/src/claude/hooks/usage.cjs +1 -1
- package/src/claude/migration-manifest.json +29 -15
- package/src/claude/output-styles/coding-level-0-eli5.md +103 -0
- package/src/claude/output-styles/coding-level-1-junior.md +124 -0
- package/src/claude/output-styles/coding-level-2-mid.md +146 -0
- package/src/claude/output-styles/coding-level-3-senior.md +148 -0
- package/src/claude/output-styles/coding-level-4-lead.md +159 -0
- package/src/claude/output-styles/coding-level-5-god.md +91 -0
- package/src/claude/references/debugger/core-philosophy.md +5 -3
- package/src/claude/references/debugger/frontend-verification.md +2 -2
- package/src/claude/references/debugger/log-ci-analysis.md +13 -1
- package/src/claude/references/debugger/parallel-agent-hydration.md +19 -34
- package/src/claude/references/debugger/performance-diagnostics.md +2 -2
- package/src/claude/references/debugger/root-cause-tracing.md +37 -6
- package/src/claude/references/debugger/side-effect-gate.md +3 -1
- package/src/claude/references/debugger/verification-protocol.md +13 -12
- package/src/claude/rules/orchestrator.md +7 -1
- package/src/claude/rules/process-management.md +39 -0
- package/src/claude/rules/review-audit-self-decision.md +48 -0
- package/src/claude/rules/skill-domain-routing.md +26 -24
- package/src/claude/rules/skill-workflow-routing.md +22 -14
- package/src/claude/rules/workflow.md +3 -3
- package/src/claude/runtime.json +1 -0
- package/src/claude/runtime.schema.json +205 -0
- package/src/claude/scripts/generate-skill-catalog.cjs +156 -122
- package/src/claude/scripts/provenance.cjs +18 -5
- package/src/claude/scripts/spec-receipt.cjs +76 -21
- package/src/claude/scripts/spec-resolver.cjs +144 -4
- package/src/claude/scripts/workflow-policy.cjs +1 -1
- package/src/claude/settings/settings.json +16 -0
- package/src/claude/skills/agent-browser/SKILL.md +1 -1
- package/src/claude/skills/ai-multimodal/SKILL.md +1 -1
- package/src/claude/skills/brainstorm/SKILL.md +193 -172
- package/src/claude/skills/brainstorm/references/question-framework.md +105 -167
- package/src/claude/skills/chrome-devtools/SKILL.md +1 -1
- package/src/claude/skills/code-review/SKILL.md +43 -25
- package/src/claude/skills/code-review/references/spec-compliance-review.md +3 -4
- package/src/claude/skills/code-review/references/verification-gate.md +1 -1
- package/src/claude/skills/debug/SKILL.md +48 -19
- package/src/claude/skills/delegate/SKILL.md +1 -1
- package/src/claude/skills/develop/SKILL.md +92 -65
- package/src/claude/skills/develop/references/parallel-waves.md +36 -63
- package/src/claude/skills/develop/references/quality-gate.md +31 -4
- package/src/claude/skills/develop/references/subagent-patterns.md +21 -7
- package/src/claude/skills/docs/SKILL.md +42 -23
- package/src/claude/skills/docs/references/init-workflow.md +4 -4
- package/src/claude/skills/docs/references/reconstruct-workflow.md +6 -6
- package/src/claude/skills/docs/references/standard-docs-workflow.md +6 -2
- package/src/claude/skills/docs/references/summarize-workflow.md +2 -2
- package/src/claude/skills/docs/references/update-workflow.md +9 -7
- package/src/claude/skills/docs/templates/requirements-as-is.md +1 -1
- package/src/claude/skills/docx/SKILL.md +1 -1
- package/src/claude/skills/git/SKILL.md +1 -1
- package/src/claude/skills/git/references/commit-protocols.md +1 -1
- package/src/claude/skills/git/references/finish-branch.md +1 -1
- package/src/claude/skills/hotfix/SKILL.md +159 -170
- package/src/claude/skills/hotfix/references/diagnosis-protocol.md +14 -9
- package/src/claude/skills/hotfix/references/parallel-patterns.md +27 -13
- package/src/claude/skills/hotfix/references/prevention-gate.md +5 -0
- package/src/claude/skills/hotfix/references/review-cycle.md +21 -15
- package/src/claude/skills/hotfix/references/workflow-specialized.md +53 -21
- package/src/claude/skills/inspect/SKILL.md +55 -46
- package/src/claude/skills/inspect/references/internal-inspection.md +21 -5
- package/src/claude/skills/loop/SKILL.md +114 -0
- package/src/claude/skills/loop/references/bounded-loop-protocol.md +104 -0
- package/src/claude/skills/loop/references/metric-and-guard-contract.md +76 -0
- package/src/claude/skills/pdf/SKILL.md +1 -1
- package/src/claude/skills/pptx/SKILL.md +1 -1
- package/src/claude/skills/question/SKILL.md +16 -13
- package/src/claude/skills/research/SKILL.md +70 -43
- package/src/claude/skills/route/SKILL.md +94 -0
- package/src/claude/skills/route/references/agent-timing.md +46 -0
- package/src/claude/skills/route/references/chaining-patterns.md +53 -0
- package/src/claude/skills/route/references/task-taxonomy.md +48 -0
- package/src/claude/skills/specs/SKILL.md +48 -57
- package/src/claude/skills/specs/references/review.md +26 -8
- package/src/claude/skills/specs/references/templates.md +76 -31
- package/src/claude/skills/specs/templates/requirements-init.md +1 -1
- package/src/claude/skills/specs/templates/research.md +2 -2
- package/src/claude/skills/sync/SKILL.md +6 -6
- package/src/claude/skills/sync/references/sync-protocols.md +1 -1
- package/src/claude/skills/test/SKILL.md +120 -120
- package/src/claude/skills/test/references/execution-strategy.md +208 -361
- package/src/claude/skills/test/references/failure-triage.md +55 -121
- package/src/claude/skills/test/references/test-memory.md +20 -34
- package/src/claude/skills/ui-ux-pro-max/SKILL.md +1 -1
- package/src/claude/skills/web-testing/SKILL.md +1 -1
- package/src/claude/skills/xlsx/SKILL.md +1 -1
- package/src/claude/status.cjs +135 -33
- package/src/codex/AGENTS.md +14 -4
- package/src/codex/hooks/docs-sync.cjs +8 -3
- package/src/codex/hooks/lib/hook-context.cjs +18 -4
- package/src/codex/hooks/rules.cjs +29 -1
- package/src/codex/hooks/secret-output-guardrail.cjs +30 -0
- package/src/codex/hooks/spec-gate.cjs +3 -8
- package/src/codex/hooks/spec-state.cjs +34 -5
- package/src/codex/hooks.json +5 -0
- package/src/codex/runtime.json +2 -0
- package/src/common/.env.example +1 -1
- package/src/claude/skills/backend-development/SKILL.md +0 -101
- package/src/claude/skills/backend-development/references/backend-api-design.md +0 -495
- package/src/claude/skills/backend-development/references/backend-architecture.md +0 -454
- package/src/claude/skills/backend-development/references/backend-authentication.md +0 -338
- package/src/claude/skills/backend-development/references/backend-code-quality.md +0 -659
- package/src/claude/skills/backend-development/references/backend-debugging.md +0 -904
- package/src/claude/skills/backend-development/references/backend-devops.md +0 -491
- package/src/claude/skills/backend-development/references/backend-mindset.md +0 -387
- package/src/claude/skills/backend-development/references/backend-performance.md +0 -397
- package/src/claude/skills/backend-development/references/backend-security.md +0 -342
- package/src/claude/skills/backend-development/references/backend-technologies.md +0 -256
- package/src/claude/skills/backend-development/references/backend-testing.md +0 -429
- package/src/claude/skills/devops/.env.example +0 -76
- package/src/claude/skills/devops/SKILL.md +0 -102
- package/src/claude/skills/devops/references/browser-rendering.md +0 -305
- package/src/claude/skills/devops/references/cloudflare-d1-kv.md +0 -123
- package/src/claude/skills/devops/references/cloudflare-platform.md +0 -271
- package/src/claude/skills/devops/references/cloudflare-r2-storage.md +0 -280
- package/src/claude/skills/devops/references/cloudflare-workers-advanced.md +0 -312
- package/src/claude/skills/devops/references/cloudflare-workers-apis.md +0 -309
- package/src/claude/skills/devops/references/cloudflare-workers-basics.md +0 -418
- package/src/claude/skills/devops/references/docker-basics.md +0 -297
- package/src/claude/skills/devops/references/docker-compose.md +0 -292
- package/src/claude/skills/devops/references/gcloud-platform.md +0 -297
- package/src/claude/skills/devops/references/gcloud-services.md +0 -304
- package/src/claude/skills/devops/references/kubernetes-basics.md +0 -95
- package/src/claude/skills/devops/references/kubernetes-helm-advanced.md +0 -70
- package/src/claude/skills/devops/references/kubernetes-helm.md +0 -81
- package/src/claude/skills/devops/references/kubernetes-kubectl.md +0 -74
- package/src/claude/skills/devops/references/kubernetes-security-advanced.md +0 -94
- package/src/claude/skills/devops/references/kubernetes-security.md +0 -89
- package/src/claude/skills/devops/references/kubernetes-troubleshooting-advanced.md +0 -74
- package/src/claude/skills/devops/references/kubernetes-troubleshooting.md +0 -49
- package/src/claude/skills/devops/references/kubernetes-workflows-advanced.md +0 -75
- package/src/claude/skills/devops/references/kubernetes-workflows.md +0 -76
- package/src/claude/skills/devops/scripts/cloudflare_deploy.py +0 -269
- package/src/claude/skills/devops/scripts/docker_optimize.py +0 -332
- package/src/claude/skills/devops/scripts/requirements.txt +0 -20
- package/src/claude/skills/devops/scripts/tests/requirements.txt +0 -3
- package/src/claude/skills/devops/scripts/tests/test_cloudflare_deploy.py +0 -285
- package/src/claude/skills/devops/scripts/tests/test_docker_optimize.py +0 -436
- package/src/claude/skills/frontend-design/SKILL.md +0 -135
- package/src/claude/skills/frontend-design/references/ai-multimodal-overview.md +0 -165
- package/src/claude/skills/frontend-design/references/analysis-best-practices.md +0 -80
- package/src/claude/skills/frontend-design/references/analysis-prompts.md +0 -141
- package/src/claude/skills/frontend-design/references/analysis-techniques.md +0 -118
- package/src/claude/skills/frontend-design/references/animejs.md +0 -396
- package/src/claude/skills/frontend-design/references/anti-slop-rules.md +0 -103
- package/src/claude/skills/frontend-design/references/asset-generation.md +0 -337
- package/src/claude/skills/frontend-design/references/bento-motion-engine.md +0 -142
- package/src/claude/skills/frontend-design/references/design-extraction-overview.md +0 -71
- package/src/claude/skills/frontend-design/references/extraction-best-practices.md +0 -141
- package/src/claude/skills/frontend-design/references/extraction-output-templates.md +0 -162
- package/src/claude/skills/frontend-design/references/extraction-prompts.md +0 -127
- package/src/claude/skills/frontend-design/references/magicui-components.md +0 -129
- package/src/claude/skills/frontend-design/references/performance-guardrails.md +0 -169
- package/src/claude/skills/frontend-design/references/premium-design-patterns.md +0 -93
- package/src/claude/skills/frontend-design/references/redesign-audit-checklist.md +0 -114
- package/src/claude/skills/frontend-design/references/technical-accessibility.md +0 -119
- package/src/claude/skills/frontend-design/references/technical-best-practices.md +0 -97
- package/src/claude/skills/frontend-design/references/technical-optimization.md +0 -44
- package/src/claude/skills/frontend-design/references/technical-overview.md +0 -90
- package/src/claude/skills/frontend-design/references/technical-workflows.md +0 -150
- package/src/claude/skills/frontend-design/references/visual-analysis-overview.md +0 -95
- package/src/claude/skills/frontend-design/references/workflow-3d.md +0 -102
- package/src/claude/skills/frontend-design/references/workflow-describe.md +0 -87
- package/src/claude/skills/frontend-design/references/workflow-immersive.md +0 -87
- package/src/claude/skills/frontend-design/references/workflow-quick.md +0 -57
- package/src/claude/skills/frontend-design/references/workflow-screenshot.md +0 -63
- package/src/claude/skills/frontend-design/references/workflow-video.md +0 -74
- package/src/claude/skills/frontend-development/SKILL.md +0 -406
- package/src/claude/skills/frontend-development/resources/common-patterns.md +0 -331
- package/src/claude/skills/frontend-development/resources/complete-examples.md +0 -872
- package/src/claude/skills/frontend-development/resources/component-patterns.md +0 -502
- package/src/claude/skills/frontend-development/resources/data-fetching.md +0 -767
- package/src/claude/skills/frontend-development/resources/file-organization.md +0 -502
- package/src/claude/skills/frontend-development/resources/loading-and-error-states.md +0 -501
- package/src/claude/skills/frontend-development/resources/performance.md +0 -406
- package/src/claude/skills/frontend-development/resources/routing-guide.md +0 -364
- package/src/claude/skills/frontend-development/resources/styling-guide.md +0 -428
- package/src/claude/skills/frontend-development/resources/typescript-standards.md +0 -418
- package/src/claude/skills/mobile-development/SKILL.md +0 -218
- package/src/claude/skills/mobile-development/references/mobile-android.md +0 -604
- package/src/claude/skills/mobile-development/references/mobile-best-practices.md +0 -544
- package/src/claude/skills/mobile-development/references/mobile-debugging.md +0 -1089
- package/src/claude/skills/mobile-development/references/mobile-frameworks.md +0 -465
- package/src/claude/skills/mobile-development/references/mobile-ios.md +0 -496
- package/src/claude/skills/mobile-development/references/mobile-mindset.md +0 -542
- package/src/claude/skills/react-best-practices/AGENTS.md +0 -2249
- package/src/claude/skills/react-best-practices/README.md +0 -123
- package/src/claude/skills/react-best-practices/SKILL.md +0 -128
- package/src/claude/skills/react-best-practices/metadata.json +0 -15
- package/src/claude/skills/react-best-practices/rules/_sections.md +0 -46
- package/src/claude/skills/react-best-practices/rules/_template.md +0 -28
- package/src/claude/skills/react-best-practices/rules/advanced-event-handler-refs.md +0 -55
- package/src/claude/skills/react-best-practices/rules/advanced-use-latest.md +0 -49
- package/src/claude/skills/react-best-practices/rules/async-api-routes.md +0 -38
- package/src/claude/skills/react-best-practices/rules/async-defer-await.md +0 -80
- package/src/claude/skills/react-best-practices/rules/async-dependencies.md +0 -36
- package/src/claude/skills/react-best-practices/rules/async-parallel.md +0 -28
- package/src/claude/skills/react-best-practices/rules/async-suspense-boundaries.md +0 -99
- package/src/claude/skills/react-best-practices/rules/bundle-barrel-imports.md +0 -59
- package/src/claude/skills/react-best-practices/rules/bundle-conditional.md +0 -31
- package/src/claude/skills/react-best-practices/rules/bundle-defer-third-party.md +0 -49
- package/src/claude/skills/react-best-practices/rules/bundle-dynamic-imports.md +0 -35
- package/src/claude/skills/react-best-practices/rules/bundle-preload.md +0 -50
- package/src/claude/skills/react-best-practices/rules/client-event-listeners.md +0 -74
- package/src/claude/skills/react-best-practices/rules/client-swr-dedup.md +0 -56
- package/src/claude/skills/react-best-practices/rules/js-batch-dom-css.md +0 -82
- package/src/claude/skills/react-best-practices/rules/js-cache-function-results.md +0 -80
- package/src/claude/skills/react-best-practices/rules/js-cache-property-access.md +0 -28
- package/src/claude/skills/react-best-practices/rules/js-cache-storage.md +0 -70
- package/src/claude/skills/react-best-practices/rules/js-combine-iterations.md +0 -32
- package/src/claude/skills/react-best-practices/rules/js-early-exit.md +0 -50
- package/src/claude/skills/react-best-practices/rules/js-hoist-regexp.md +0 -45
- package/src/claude/skills/react-best-practices/rules/js-index-maps.md +0 -37
- package/src/claude/skills/react-best-practices/rules/js-length-check-first.md +0 -49
- package/src/claude/skills/react-best-practices/rules/js-min-max-loop.md +0 -82
- package/src/claude/skills/react-best-practices/rules/js-set-map-lookups.md +0 -24
- package/src/claude/skills/react-best-practices/rules/js-tosorted-immutable.md +0 -57
- package/src/claude/skills/react-best-practices/rules/rendering-activity.md +0 -26
- package/src/claude/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +0 -47
- package/src/claude/skills/react-best-practices/rules/rendering-conditional-render.md +0 -40
- package/src/claude/skills/react-best-practices/rules/rendering-content-visibility.md +0 -38
- package/src/claude/skills/react-best-practices/rules/rendering-hoist-jsx.md +0 -46
- package/src/claude/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +0 -82
- package/src/claude/skills/react-best-practices/rules/rendering-svg-precision.md +0 -28
- package/src/claude/skills/react-best-practices/rules/rerender-defer-reads.md +0 -39
- package/src/claude/skills/react-best-practices/rules/rerender-dependencies.md +0 -45
- package/src/claude/skills/react-best-practices/rules/rerender-derived-state.md +0 -29
- package/src/claude/skills/react-best-practices/rules/rerender-functional-setstate.md +0 -74
- package/src/claude/skills/react-best-practices/rules/rerender-lazy-state-init.md +0 -58
- package/src/claude/skills/react-best-practices/rules/rerender-memo.md +0 -44
- package/src/claude/skills/react-best-practices/rules/rerender-transitions.md +0 -40
- package/src/claude/skills/react-best-practices/rules/server-after-nonblocking.md +0 -73
- package/src/claude/skills/react-best-practices/rules/server-cache-lru.md +0 -41
- package/src/claude/skills/react-best-practices/rules/server-cache-react.md +0 -26
- package/src/claude/skills/react-best-practices/rules/server-parallel-fetching.md +0 -79
- package/src/claude/skills/react-best-practices/rules/server-serialization.md +0 -38
|
@@ -12,6 +12,8 @@ const PACKAGE_ROOT = path.join(__dirname, '../..');
|
|
|
12
12
|
const POLICY_PATH = path.join(PACKAGE_ROOT, 'src/claude/scripts/workflow-policy.cjs');
|
|
13
13
|
const POLICY = require(POLICY_PATH);
|
|
14
14
|
const PROVENANCE_HELPER = require(path.join(PACKAGE_ROOT, 'src/claude/scripts/provenance.cjs'));
|
|
15
|
+
const RESOLVER = require(path.join(PACKAGE_ROOT, 'src/claude/scripts/spec-resolver.cjs'));
|
|
16
|
+
const RECEIPTS = require(path.join(PACKAGE_ROOT, 'src/claude/scripts/spec-receipt.cjs'));
|
|
15
17
|
const FINAL_STATE = require(path.join(PACKAGE_ROOT, 'src/claude/scripts/spec-final-state.cjs'));
|
|
16
18
|
const VALIDATOR = require(path.join(PACKAGE_ROOT, 'src/claude/scripts/validate-spec-output.cjs'));
|
|
17
19
|
const GROUNDER = require(path.join(PACKAGE_ROOT, 'src/claude/scripts/spec-ground.cjs'));
|
|
@@ -24,11 +26,24 @@ const SPECS = path.join(PACKAGE_ROOT, 'src/claude/skills/specs/SKILL.md');
|
|
|
24
26
|
const SCAFFOLD = path.join(PACKAGE_ROOT, 'src/claude/scripts/spec-scaffold.cjs');
|
|
25
27
|
const GATE = path.join(PACKAGE_ROOT, 'src/claude/skills/develop/references/quality-gate.md');
|
|
26
28
|
const TEST_SKILL = path.join(PACKAGE_ROOT, 'src/claude/skills/test/SKILL.md');
|
|
29
|
+
const TEST_SOURCE_ROOT = path.dirname(TEST_SKILL);
|
|
30
|
+
const TEST_REFERENCES = ['execution-strategy.md', 'failure-triage.md', 'test-memory.md'];
|
|
31
|
+
const TEST_RUNNER = path.join(PACKAGE_ROOT, 'src/claude/agents/test-runner.md');
|
|
27
32
|
const SYNC_SKILL = path.join(PACKAGE_ROOT, 'src/claude/skills/sync/SKILL.md');
|
|
28
33
|
const CODE_REVIEW_SKILL = path.join(PACKAGE_ROOT, 'src/claude/skills/code-review/SKILL.md');
|
|
29
34
|
const PARALLEL_WAVES = path.join(PACKAGE_ROOT, 'src/claude/skills/develop/references/parallel-waves.md');
|
|
35
|
+
const DEVELOP_REFERENCES = ['quality-gate.md', 'parallel-waves.md', 'subagent-patterns.md'];
|
|
36
|
+
const INSTALLER = path.join(PACKAGE_ROOT, 'bin/install.js');
|
|
30
37
|
const SYNC_PROTOCOLS = path.join(PACKAGE_ROOT, 'src/claude/skills/sync/references/sync-protocols.md');
|
|
38
|
+
const PROCESS_FIRST_AGENTS = [
|
|
39
|
+
'implementer.md',
|
|
40
|
+
'inspector.md',
|
|
41
|
+
'code-auditor.md',
|
|
42
|
+
'docs-keeper.md',
|
|
43
|
+
'deployer.md',
|
|
44
|
+
].map((name) => path.join(PACKAGE_ROOT, 'src/claude/agents', name));
|
|
31
45
|
const PROVENANCE = path.join(PACKAGE_ROOT, '../../docs/provenance.md');
|
|
46
|
+
const SPECS_USAGE_GUIDE = path.join(PACKAGE_ROOT, '../../docs/specs-usage-guide.md');
|
|
32
47
|
const RUNTIME_ROOT = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-policy-runtime-'));
|
|
33
48
|
const RUNTIME_SPEC = path.join(RUNTIME_ROOT, 'specs', 'demo', 'spec.json');
|
|
34
49
|
fs.mkdirSync(path.dirname(RUNTIME_SPEC), { recursive: true });
|
|
@@ -73,9 +88,23 @@ function installClaudeRuntimeClosure(fixtureRoot, entry = 'hooks/spec-gate.cjs')
|
|
|
73
88
|
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
74
89
|
fs.copyFileSync(path.join(hooksRoot, hook), target);
|
|
75
90
|
}
|
|
91
|
+
// Hook helpers the raw-copied hooks require at runtime.
|
|
92
|
+
for (const helper of ['hook-state-dir.cjs']) {
|
|
93
|
+
const target = path.join(destinationRoot, 'hooks', 'lib', helper);
|
|
94
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
95
|
+
fs.copyFileSync(path.join(hooksRoot, 'lib', helper), target);
|
|
96
|
+
}
|
|
76
97
|
return path.join(destinationRoot, entry);
|
|
77
98
|
}
|
|
78
99
|
|
|
100
|
+
function installClaude(root) {
|
|
101
|
+
return spawnSync(process.execPath, [INSTALLER, '--platform', 'claude', '--yes'], {
|
|
102
|
+
cwd: root,
|
|
103
|
+
encoding: 'utf8',
|
|
104
|
+
env: { ...process.env, PATH: '/usr/bin:/bin' },
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
79
108
|
function markdownLegacyRegions(content) {
|
|
80
109
|
const headings = [];
|
|
81
110
|
const primaryLines = [];
|
|
@@ -300,7 +329,7 @@ function canonicalFlashTask(overrides = {}) {
|
|
|
300
329
|
return {
|
|
301
330
|
status: 'in_progress',
|
|
302
331
|
receipt: 'FLASH_UNVERIFIED',
|
|
303
|
-
blocker: 'awaiting /
|
|
332
|
+
blocker: 'awaiting /cf:test <feature>',
|
|
304
333
|
dependencyBlocked: true,
|
|
305
334
|
unblocks: false,
|
|
306
335
|
feature_name: 'demo',
|
|
@@ -348,7 +377,7 @@ test('develop flags retain their process-v3 contracts', () => {
|
|
|
348
377
|
assert.match(develop, /implementation-notes-template\.html/);
|
|
349
378
|
assert.match(develop, /Never create it by default/i);
|
|
350
379
|
assert.match(develop, /--parallel \[N\][\s\S]*parallel-waves\.md[\s\S]*isolated worktrees[\s\S]*one controller writer/i);
|
|
351
|
-
assert.match(develop, /--flash[\s\S]*Status: in_progress[\s\S]*FLASH_UNVERIFIED[\s\S]*awaiting \/
|
|
380
|
+
assert.match(develop, /--flash[\s\S]*Status: in_progress[\s\S]*FLASH_UNVERIFIED[\s\S]*awaiting \/cf:test <feature>/i);
|
|
352
381
|
assert.match(develop, /FLASH_UNVERIFIED[\s\S]*do not unblock dependents[\s\S]*sync-finalize path/i);
|
|
353
382
|
});
|
|
354
383
|
|
|
@@ -771,6 +800,35 @@ test('Specs primary output is a flat process-first packet with isolated legacy c
|
|
|
771
800
|
assertVocabularyIsLegacyOnly(SPECS, regions);
|
|
772
801
|
});
|
|
773
802
|
|
|
803
|
+
test('core execution agents default to process-first state and isolate legacy packets', () => {
|
|
804
|
+
for (const filePath of PROCESS_FIRST_AGENTS) {
|
|
805
|
+
const content = read(filePath);
|
|
806
|
+
const name = path.basename(filePath);
|
|
807
|
+
assert.match(content, /plan\.md/i, `${name} must read the process-first plan`);
|
|
808
|
+
assert.match(content, /task-NN-\*\.md|task-NN-<slug>\.md/i, `${name} must understand flat tasks`);
|
|
809
|
+
assert.match(content, /legacy/i, `${name} must isolate legacy compatibility`);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
const implementer = read(PROCESS_FIRST_AGENTS[0]);
|
|
813
|
+
assert.match(implementer, /Under every dispatch mode[\s\S]{0,160}do NOT edit[\s\S]{0,80}`plan\.md`[\s\S]{0,80}`Status:`[\s\S]{0,80}`## Receipt`/i);
|
|
814
|
+
assert.match(implementer, /controller/i);
|
|
815
|
+
assert.match(implementer, /process-first work[\s\S]{0,100}Ownership[\s\S]{0,100}`Related Files` only for a valid legacy adapter/i);
|
|
816
|
+
|
|
817
|
+
const auditor = read(PROCESS_FIRST_AGENTS[2]);
|
|
818
|
+
assert.match(auditor, /accepted C1\/C2 decisions/i);
|
|
819
|
+
assert.match(auditor, /Only for a valid legacy adapter[\s\S]*`Related Files`[\s\S]*`## Evidence`/i);
|
|
820
|
+
assert.match(auditor, /Never require those legacy artifacts from a[\s\S]*process-first packet/i);
|
|
821
|
+
assert.match(auditor, /attestation belongs only to the valid legacy/i);
|
|
822
|
+
|
|
823
|
+
const docsKeeper = read(PROCESS_FIRST_AGENTS[3]);
|
|
824
|
+
assert.match(docsKeeper, /never[\s\S]{0,80}invent or write Status, Receipt, approval, or execution proof/i);
|
|
825
|
+
|
|
826
|
+
const deployer = read(PROCESS_FIRST_AGENTS[4]);
|
|
827
|
+
assert.match(deployer, /current final inline Receipt/i);
|
|
828
|
+
assert.match(deployer, /C3\/release authorization/i);
|
|
829
|
+
assert.match(deployer, /does not write process-first Status\/Receipt/i);
|
|
830
|
+
});
|
|
831
|
+
|
|
774
832
|
test('R7 Develop and Sync surfaces teach process-v3 and isolate hierarchical Legacy sections', () => {
|
|
775
833
|
const surfaces = [
|
|
776
834
|
['develop', DEVELOP],
|
|
@@ -795,10 +853,10 @@ test('R7 Develop and Sync surfaces teach process-v3 and isolate hierarchical Leg
|
|
|
795
853
|
|
|
796
854
|
const develop = regionsByName.get('develop').primary;
|
|
797
855
|
assert.match(develop, /`specs\/<feature>\/plan\.md` plus flat `task-NN-\*\.md`/i);
|
|
798
|
-
assert.match(develop, /
|
|
856
|
+
assert.match(develop, /one unblocked task at a time/i);
|
|
799
857
|
assert.match(develop, /Outcome and Acceptance/i);
|
|
800
858
|
assert.match(develop, /Verification Plan/);
|
|
801
|
-
assert.match(develop, /final `## Receipt`/i);
|
|
859
|
+
assert.match(develop, /final inline `## Receipt`/i);
|
|
802
860
|
|
|
803
861
|
const sync = regionsByName.get('sync').primary;
|
|
804
862
|
assert.match(sync, /<task-NN-slug\.md>/i);
|
|
@@ -807,10 +865,10 @@ test('R7 Develop and Sync surfaces teach process-v3 and isolate hierarchical Leg
|
|
|
807
865
|
assert.match(sync, /current inline Receipt/i);
|
|
808
866
|
|
|
809
867
|
const waves = regionsByName.get('parallel waves').primary;
|
|
810
|
-
assert.match(waves, /plan
|
|
811
|
-
assert.match(waves, /acceptance,
|
|
868
|
+
assert.match(waves, /dependencies from the plan table and each task/i);
|
|
869
|
+
assert.match(waves, /acceptance, proof command/i);
|
|
812
870
|
assert.match(waves, /one file has one writer per wave/i);
|
|
813
|
-
assert.match(waves, /writes inline Receipts and Status
|
|
871
|
+
assert.match(waves, /writes inline Receipts and Status one task at a time/i);
|
|
814
872
|
|
|
815
873
|
const protocols = regionsByName.get('sync protocols').primary;
|
|
816
874
|
assert.match(protocols, /`plan\.md` and flat `task-\*\.md` files/i);
|
|
@@ -819,6 +877,267 @@ test('R7 Develop and Sync surfaces teach process-v3 and isolate hierarchical Leg
|
|
|
819
877
|
assert.match(legacyCorpus, /sync-finalize/i);
|
|
820
878
|
});
|
|
821
879
|
|
|
880
|
+
test('Develop process-first source contract preserves selection, recovery, final-Head, parallel, and Flash boundaries', () => {
|
|
881
|
+
const develop = markdownLegacyRegions(read(DEVELOP)).primary;
|
|
882
|
+
const quality = read(GATE);
|
|
883
|
+
const waves = markdownLegacyRegions(read(PARALLEL_WAVES)).primary;
|
|
884
|
+
const dispatch = read(path.join(path.dirname(PARALLEL_WAVES), 'subagent-patterns.md'));
|
|
885
|
+
|
|
886
|
+
for (const clause of [
|
|
887
|
+
/first dependency-valid `pending` row in `plan\.md` order/i,
|
|
888
|
+
/Specific-task\s+mode never touches a sibling/i,
|
|
889
|
+
/More than one `in_progress`/i,
|
|
890
|
+
/For an interrupted `in_progress` task/i,
|
|
891
|
+
/only the controller writes Status or Receipt/i,
|
|
892
|
+
/do not research, replan, or add a routine user gate/i,
|
|
893
|
+
]) assert.match(develop, clause);
|
|
894
|
+
assert.match(quality, /consecutive Head captures are identical/i);
|
|
895
|
+
assert.match(quality, /skip, skipped, todo, cancel, canceled, cancelled/i);
|
|
896
|
+
assert.match(quality, /exact command, current Head, required proof level, oracle/i);
|
|
897
|
+
assert.match(waves, /every commit[\s\S]*complete changed tree/i);
|
|
898
|
+
assert.match(waves, /handoff metadata cannot substitute/i);
|
|
899
|
+
assert.match(develop, /later explicit non-Flash invocation may recover/i);
|
|
900
|
+
assert.match(dispatch, /controller alone writes Status and inline Receipt/i);
|
|
901
|
+
|
|
902
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-plan-native-'));
|
|
903
|
+
try {
|
|
904
|
+
const featureDir = path.join(root, 'specs', 'ordered');
|
|
905
|
+
fs.mkdirSync(featureDir, { recursive: true });
|
|
906
|
+
fs.writeFileSync(path.join(featureDir, 'plan.md'), '# Ordered\nSpecs-Contract: process-first-ready-v1\n');
|
|
907
|
+
const task = (status, dependencies, receipt = '<!-- Fill only after execution. -->') =>
|
|
908
|
+
`# Task\n\nStatus: ${status}\n\n## Dependencies\n\n${dependencies}\n\n## Verification Plan\n\n- Command: \`node --test\`\n\n## Receipt\n\n${receipt}\n`;
|
|
909
|
+
fs.writeFileSync(path.join(featureDir, 'task-01-first.md'), task('pending', '- none'));
|
|
910
|
+
fs.writeFileSync(path.join(featureDir, 'task-02-second.md'), task('pending', '- task-01-first.md'));
|
|
911
|
+
initFixtureGit(root);
|
|
912
|
+
|
|
913
|
+
let candidate = RESOLVER.resolveWorkflowCandidate({ projectRoot: root, explicitFeature: 'ordered' });
|
|
914
|
+
assert.equal(candidate.queueReady, true);
|
|
915
|
+
assert.deepEqual(Object.keys(candidate.taskRegistry), ['task-01-first.md', 'task-02-second.md']);
|
|
916
|
+
let runtime = PROVENANCE_HELPER.deriveRuntimeContext({
|
|
917
|
+
projectRoot: root, specsRoot: path.join(root, 'specs'), specFile: candidate.planFile,
|
|
918
|
+
featureName: 'ordered', runtimeSession: 'plan-native-test',
|
|
919
|
+
});
|
|
920
|
+
const preMutationHead = runtime.head;
|
|
921
|
+
fs.mkdirSync(path.join(root, 'src'), { recursive: true });
|
|
922
|
+
fs.writeFileSync(path.join(root, 'src', 'app.js'), 'later worktree mutation\n');
|
|
923
|
+
runtime = PROVENANCE_HELPER.deriveRuntimeContext({
|
|
924
|
+
projectRoot: root, specsRoot: path.join(root, 'specs'), specFile: candidate.planFile,
|
|
925
|
+
featureName: 'ordered', runtimeSession: 'plan-native-test',
|
|
926
|
+
});
|
|
927
|
+
assert.notEqual(runtime.head, preMutationHead, 'a later non-Spec mutation must stale the earlier Head');
|
|
928
|
+
const stableRuntime = PROVENANCE_HELPER.deriveRuntimeContext({
|
|
929
|
+
projectRoot: root, specsRoot: path.join(root, 'specs'), specFile: candidate.planFile,
|
|
930
|
+
featureName: 'ordered', runtimeSession: 'plan-native-test',
|
|
931
|
+
});
|
|
932
|
+
assert.equal(stableRuntime.head, runtime.head, 'unchanged consecutive captures must reach a fixed point');
|
|
933
|
+
let dependencies = RECEIPTS.workflowDependencyProofState(featureDir, candidate.taskRegistry, runtime, POLICY);
|
|
934
|
+
assert.equal(dependencies['task-01-first.md'].eligible, false);
|
|
935
|
+
|
|
936
|
+
const receipt = `Verification: PASS\nCommand: node --test\nExit: 0\nBase: ${runtime.base}\nHead: ${runtime.head}\n\n\`\`\`text\n1 test passed\n\`\`\``;
|
|
937
|
+
fs.writeFileSync(path.join(featureDir, 'task-01-first.md'), task('done', '- none', receipt));
|
|
938
|
+
candidate = RESOLVER.resolveWorkflowCandidate({ projectRoot: root, explicitFeature: 'ordered' });
|
|
939
|
+
runtime = PROVENANCE_HELPER.deriveRuntimeContext({
|
|
940
|
+
projectRoot: root, specsRoot: path.join(root, 'specs'), specFile: candidate.planFile,
|
|
941
|
+
featureName: 'ordered', runtimeSession: 'plan-native-test',
|
|
942
|
+
});
|
|
943
|
+
assert.deepEqual(RECEIPTS.checkWorkflowTaskReceipt(
|
|
944
|
+
featureDir, 'task-01-first.md', runtime, POLICY,
|
|
945
|
+
).failures, []);
|
|
946
|
+
dependencies = RECEIPTS.workflowDependencyProofState(featureDir, candidate.taskRegistry, runtime, POLICY);
|
|
947
|
+
assert.equal(dependencies['task-01-first.md'].eligible, true);
|
|
948
|
+
assert.equal(candidate.taskRegistry['task-02-second.md'].dependencies[0], 'task-01-first.md');
|
|
949
|
+
|
|
950
|
+
fs.writeFileSync(path.join(featureDir, 'task-01-first.md'), task('in_progress', '- none'));
|
|
951
|
+
fs.writeFileSync(path.join(featureDir, 'task-02-second.md'), task('in_progress', '- task-01-first.md'));
|
|
952
|
+
candidate = RESOLVER.resolveWorkflowCandidate({ projectRoot: root, explicitFeature: 'ordered' });
|
|
953
|
+
assert.equal(Object.values(candidate.taskRegistry).filter(({ status }) => status === 'in_progress').length, 2);
|
|
954
|
+
|
|
955
|
+
fs.writeFileSync(path.join(featureDir, 'task-01-first.md'), task('paused', '- none'));
|
|
956
|
+
fs.writeFileSync(path.join(featureDir, 'task-02-second.md'), task('blocked', '- task-01-first.md'));
|
|
957
|
+
candidate = RESOLVER.resolveWorkflowCandidate({ projectRoot: root, explicitFeature: 'ordered' });
|
|
958
|
+
assert.equal(candidate.taskRegistry['task-01-first.md'].status, 'paused');
|
|
959
|
+
assert.equal(candidate.taskRegistry['task-02-second.md'].status, 'blocked');
|
|
960
|
+
} finally {
|
|
961
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
962
|
+
}
|
|
963
|
+
});
|
|
964
|
+
|
|
965
|
+
test('Claude installed Develop preserves plan-native execution and references', () => {
|
|
966
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-claude-develop-'));
|
|
967
|
+
try {
|
|
968
|
+
const result = installClaude(root);
|
|
969
|
+
assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
|
|
970
|
+
const sourceRoot = path.dirname(DEVELOP);
|
|
971
|
+
const installedRoot = path.join(root, '.claude/skills/develop');
|
|
972
|
+
for (const relative of ['SKILL.md', ...DEVELOP_REFERENCES.map((name) => `references/${name}`)]) {
|
|
973
|
+
assert.equal(
|
|
974
|
+
fs.readFileSync(path.join(installedRoot, relative), 'utf8'),
|
|
975
|
+
fs.readFileSync(path.join(sourceRoot, relative), 'utf8'),
|
|
976
|
+
`Claude Develop projection drifted: ${relative}`,
|
|
977
|
+
);
|
|
978
|
+
}
|
|
979
|
+
assert.equal(fs.existsSync(path.join(root, '.agents/skills/develop')), false);
|
|
980
|
+
} finally {
|
|
981
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
982
|
+
}
|
|
983
|
+
});
|
|
984
|
+
|
|
985
|
+
test('Test process-first handoff preserves proof ownership and side-effect boundaries', () => {
|
|
986
|
+
const skill = read(TEST_SKILL);
|
|
987
|
+
const strategy = read(path.join(TEST_SOURCE_ROOT, 'references/execution-strategy.md'));
|
|
988
|
+
const runner = read(TEST_RUNNER);
|
|
989
|
+
const review = read(CODE_REVIEW_SKILL);
|
|
990
|
+
const developConsumers = [read(DEVELOP), read(GATE), read(path.join(
|
|
991
|
+
path.dirname(PARALLEL_WAVES), 'subagent-patterns.md',
|
|
992
|
+
))].join('\n');
|
|
993
|
+
|
|
994
|
+
const exactKeys = [
|
|
995
|
+
'schema_version', 'target', 'verdict', 'command', 'exit', 'counts',
|
|
996
|
+
'provenance', 'proof_level', 'expected', 'observed', 'reachability',
|
|
997
|
+
'artifacts', 'branches', 'raw_output', 'redactions', 'payload_sha256',
|
|
998
|
+
];
|
|
999
|
+
for (const key of exactKeys) assert.match(strategy, new RegExp(`\\b${key}\\b`));
|
|
1000
|
+
const developHandoffFields = new Map([
|
|
1001
|
+
['command', /Verification handoff separately contains the fresh command/i],
|
|
1002
|
+
['exit', /fresh command, exit/i],
|
|
1003
|
+
['branches', /named probes/i],
|
|
1004
|
+
['counts', /counts, output/i],
|
|
1005
|
+
['raw_output', /counts, output/i],
|
|
1006
|
+
['provenance', /observed Head/i],
|
|
1007
|
+
['reachability', /runtime reachability/i],
|
|
1008
|
+
['proof_level', /required proof level/i],
|
|
1009
|
+
]);
|
|
1010
|
+
for (const [field, sourceClause] of developHandoffFields) {
|
|
1011
|
+
assert.match(developConsumers, sourceClause, `Develop must currently consume ${field}`);
|
|
1012
|
+
assert.ok(exactKeys.includes(field), `test-proof-v1 must preserve Develop field ${field}`);
|
|
1013
|
+
}
|
|
1014
|
+
assert.match(runner, /Emit only `schema_version: "test-proof-v1"`/);
|
|
1015
|
+
assert.match(runner, /sole writer of[\s\S]*Status and inline Receipt/i);
|
|
1016
|
+
assert.match(review, /consume only the controller-validated[\s\S]*`test-proof-v1`[\s\S]*handoff/i);
|
|
1017
|
+
assert.match(review, /Never create or search for a separate[\s\S]*process-first receipt/i);
|
|
1018
|
+
assert.doesNotMatch([skill, strategy, runner].join('\n'), /npm\s+install|inject-auth\.js|--cookies\s|Bearer eyJ/);
|
|
1019
|
+
|
|
1020
|
+
const stable = (value) => {
|
|
1021
|
+
if (Array.isArray(value)) return `[${value.map(stable).join(',')}]`;
|
|
1022
|
+
if (value && typeof value === 'object') return `{${Object.keys(value).sort()
|
|
1023
|
+
.map((key) => `${JSON.stringify(key)}:${stable(value[key])}`).join(',')}}`;
|
|
1024
|
+
return JSON.stringify(value);
|
|
1025
|
+
};
|
|
1026
|
+
const proof = {
|
|
1027
|
+
schema_version: 'test-proof-v1',
|
|
1028
|
+
target: { feature: 'fixture', task_path: 'specs/fixture/task-01-proof.md' },
|
|
1029
|
+
verdict: 'PASS', command: 'node --test', exit: 0,
|
|
1030
|
+
counts: { executed: 1, passed: 1, failed: 0, skipped: 0 },
|
|
1031
|
+
provenance: { base: 'a'.repeat(40), head: 'b'.repeat(64) },
|
|
1032
|
+
proof_level: 'source', expected: 'named probe passes', observed: 'named probe passed',
|
|
1033
|
+
reachability: { status: 'PASS', evidence: ['test-runner -> controller'] },
|
|
1034
|
+
artifacts: [],
|
|
1035
|
+
branches: [{
|
|
1036
|
+
id: 'fixture named probe', required: true, verdict: 'PASS', command: 'node --test',
|
|
1037
|
+
exit: 0, counts: { executed: 1, passed: 1, failed: 0, skipped: 0 },
|
|
1038
|
+
proof_level: 'source',
|
|
1039
|
+
}],
|
|
1040
|
+
raw_output: '1 test passed', redactions: [],
|
|
1041
|
+
};
|
|
1042
|
+
proof.payload_sha256 = crypto.createHash('sha256').update(stable(proof)).digest('hex');
|
|
1043
|
+
assert.deepEqual(Object.keys(proof).sort(), exactKeys.slice().sort());
|
|
1044
|
+
assert.match(proof.payload_sha256, /^[a-f0-9]{64}$/);
|
|
1045
|
+
assert.equal(proof.counts.executed, proof.branches.reduce(
|
|
1046
|
+
(sum, branch) => sum + branch.counts.executed, 0,
|
|
1047
|
+
));
|
|
1048
|
+
|
|
1049
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-test-proof-side-effects-'));
|
|
1050
|
+
const absentRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-test-proof-memory-absent-'));
|
|
1051
|
+
const externalTemp = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-test-owned-'));
|
|
1052
|
+
try {
|
|
1053
|
+
fs.mkdirSync(path.join(root, '.hapo'), { recursive: true });
|
|
1054
|
+
const memory = path.join(root, '.hapo/test-memory.json');
|
|
1055
|
+
fs.writeFileSync(memory, '{"known_issues":[]}\n');
|
|
1056
|
+
fs.writeFileSync(path.join(root, '.gitignore'), 'ignored-proof-state/\n');
|
|
1057
|
+
fs.writeFileSync(path.join(root, 'tracked.js'), 'before\n');
|
|
1058
|
+
initFixtureGit(root);
|
|
1059
|
+
const memoryBefore = crypto.createHash('sha256').update(fs.readFileSync(memory)).digest('hex');
|
|
1060
|
+
const command = spawnSync(process.execPath, ['-e', [
|
|
1061
|
+
"const fs=require('node:fs');",
|
|
1062
|
+
"fs.appendFileSync('tracked.js','after\\n');",
|
|
1063
|
+
"fs.writeFileSync('untracked-proof.txt','created by project command\\n');",
|
|
1064
|
+
"fs.mkdirSync('ignored-proof-state',{recursive:true});",
|
|
1065
|
+
"fs.writeFileSync('ignored-proof-state/output.txt','project output\\n');",
|
|
1066
|
+
"console.log('tests 1\\npass 1\\nfail 0\\nskipped 0');",
|
|
1067
|
+
].join('')], { cwd: root, encoding: 'utf8' });
|
|
1068
|
+
assert.equal(command.status, 0, command.stderr);
|
|
1069
|
+
assert.match(command.stdout, /tests 1/);
|
|
1070
|
+
const drift = spawnSync('git', [
|
|
1071
|
+
'-C', root, 'status', '--porcelain=v1', '--ignored', '--untracked-files=all',
|
|
1072
|
+
], { encoding: 'utf8' });
|
|
1073
|
+
assert.equal(drift.status, 0, drift.stderr);
|
|
1074
|
+
assert.match(drift.stdout, / M tracked\.js/);
|
|
1075
|
+
assert.match(drift.stdout, /\?\? untracked-proof\.txt/);
|
|
1076
|
+
assert.match(drift.stdout, /!! ignored-proof-state\//);
|
|
1077
|
+
const memoryAfter = crypto.createHash('sha256').update(fs.readFileSync(memory)).digest('hex');
|
|
1078
|
+
assert.equal(memoryAfter, memoryBefore, 'present Test memory must stay byte-identical');
|
|
1079
|
+
for (const relative of ['test-report.md', 'test-cache', 'test-auth.json']) {
|
|
1080
|
+
assert.equal(fs.existsSync(path.join(root, '.hapo', relative)), false, `${relative} must stay absent`);
|
|
1081
|
+
}
|
|
1082
|
+
assert.equal(fs.existsSync(path.join(root, 'node_modules')), false, 'project-local lazy install must stay absent');
|
|
1083
|
+
|
|
1084
|
+
const absentMemory = path.join(absentRoot, '.hapo/test-memory.json');
|
|
1085
|
+
assert.equal(fs.existsSync(absentMemory), false);
|
|
1086
|
+
const noop = spawnSync(process.execPath, ['-e', "console.log('tests 1; pass 1')"], {
|
|
1087
|
+
cwd: absentRoot, encoding: 'utf8',
|
|
1088
|
+
});
|
|
1089
|
+
assert.equal(noop.status, 0, noop.stderr);
|
|
1090
|
+
assert.equal(fs.existsSync(absentMemory), false, 'absent Test memory must stay absent');
|
|
1091
|
+
|
|
1092
|
+
fs.writeFileSync(path.join(externalTemp, 'ephemeral.txt'), 'proof temp\n');
|
|
1093
|
+
} finally {
|
|
1094
|
+
fs.rmSync(externalTemp, { recursive: true, force: true });
|
|
1095
|
+
fs.rmSync(absentRoot, { recursive: true, force: true });
|
|
1096
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
1097
|
+
}
|
|
1098
|
+
assert.equal(fs.existsSync(externalTemp), false, 'Test-owned external temp must be cleaned');
|
|
1099
|
+
});
|
|
1100
|
+
|
|
1101
|
+
test('Claude installed Test preserves plan-native proof and references', () => {
|
|
1102
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-claude-test-proof-'));
|
|
1103
|
+
try {
|
|
1104
|
+
const result = installClaude(root);
|
|
1105
|
+
assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
|
|
1106
|
+
const installedRoot = path.join(root, '.claude/skills/test');
|
|
1107
|
+
for (const relative of ['SKILL.md', ...TEST_REFERENCES.map((name) => `references/${name}`)]) {
|
|
1108
|
+
assert.equal(
|
|
1109
|
+
fs.readFileSync(path.join(installedRoot, relative), 'utf8'),
|
|
1110
|
+
fs.readFileSync(path.join(TEST_SOURCE_ROOT, relative), 'utf8'),
|
|
1111
|
+
`Claude Test projection drifted: ${relative}`,
|
|
1112
|
+
);
|
|
1113
|
+
}
|
|
1114
|
+
const installedRunner = fs.readFileSync(path.join(root, '.claude/agents/test-runner.md'), 'utf8');
|
|
1115
|
+
assert.equal(installedRunner, fs.readFileSync(TEST_RUNNER, 'utf8'));
|
|
1116
|
+
assert.match(installedRunner, /test-proof-v1/);
|
|
1117
|
+
const installedReview = fs.readFileSync(path.join(root, '.claude/skills/code-review/SKILL.md'), 'utf8');
|
|
1118
|
+
assert.equal(installedReview, fs.readFileSync(CODE_REVIEW_SKILL, 'utf8'));
|
|
1119
|
+
assert.match(installedReview, /controller-validated `test-proof-v1`/);
|
|
1120
|
+
assert.match(installedReview, /Do not load or follow legacy separate-receipt paragraphs/i);
|
|
1121
|
+
assert.equal(fs.existsSync(path.join(root, '.agents/skills/test')), false);
|
|
1122
|
+
} finally {
|
|
1123
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
1124
|
+
}
|
|
1125
|
+
});
|
|
1126
|
+
|
|
1127
|
+
test('specs-usage-guide documents Test proof handoff without timing claims', () => {
|
|
1128
|
+
const guide = read(SPECS_USAGE_GUIDE);
|
|
1129
|
+
const section = guide.split('## Test proof handoff')[1]?.split('### Receipt canonical')[0] || '';
|
|
1130
|
+
assert.match(section, /`test-proof-v1`/);
|
|
1131
|
+
assert.match(section, /Test không ghi `Status:` hay inline[\s\S]*Develop controller là writer duy nhất/i);
|
|
1132
|
+
assert.match(section, /tracked\/untracked\/ignored drift/i);
|
|
1133
|
+
assert.match(section, /HTTPS\/localhost origin[\s\S]*fresh consent/i);
|
|
1134
|
+
assert.match(section, /separate-receipt adapter/i);
|
|
1135
|
+
assert.match(section, /không phải benchmark thời gian/i);
|
|
1136
|
+
assert.match(section, /không chứng minh live adherence/i);
|
|
1137
|
+
assert.doesNotMatch(section, /\b\d+(?:\.\d+)?\s*(?:ms|milliseconds?|seconds?|minutes?)\b/i);
|
|
1138
|
+
assert.doesNotMatch(section, /\[(?:LIVE_)?VERIFIED\]/i);
|
|
1139
|
+
});
|
|
1140
|
+
|
|
822
1141
|
test('canonical verdict adapter handles completion and unfinished decisions', () => {
|
|
823
1142
|
assert.deepEqual(POLICY.REVIEW_VERDICTS, ['PASS', 'PASS_WITH_WARNINGS', 'FAIL', 'BLOCKED']);
|
|
824
1143
|
assert.doesNotThrow(() => POLICY.assertVerdict('PASS_WITH_WARNINGS'));
|
|
@@ -971,7 +1290,7 @@ test('auto authoring reaches readiness only after promotion, validation, groundi
|
|
|
971
1290
|
|
|
972
1291
|
const specs = read(SPECS);
|
|
973
1292
|
const executableBlocks = [...specs.matchAll(/```bash\n([\s\S]*?)```/g)].map((match) => match[1]);
|
|
974
|
-
assert.ok(executableBlocks.every((block) => !/
|
|
1293
|
+
assert.ok(executableBlocks.every((block) => !/cf:?develop/i.test(block)), 'Specs must never execute Develop');
|
|
975
1294
|
assert.match(specs, /An implementation workflow owns execution/i);
|
|
976
1295
|
assert.match(specs, /selects one unblocked task[\s\S]*runs\s+the task's verification[\s\S]*inline `## Receipt`/i);
|
|
977
1296
|
assert.match(specs, /The user decides\s+at C3 whether the feature is done/i);
|
|
@@ -1265,8 +1584,6 @@ test('first-run cache bypass is blocked - canonical receipt required even withou
|
|
|
1265
1584
|
);
|
|
1266
1585
|
// Clear cache to simulate fresh first-run with valid receipt
|
|
1267
1586
|
try { fs.unlinkSync(path.join(claude, 'hooks', '.logs', 'spec-gate-last.json')); } catch {}
|
|
1268
|
-
try { fs.unlinkSync(path.join(__dirname, '..', '.logs', 'spec-gate-last.json')); } catch {}
|
|
1269
|
-
// Need to clear the cache that the hook uses (under claude hooks .logs) - we already cleared above
|
|
1270
1587
|
// Re-run with valid receipt - should not block
|
|
1271
1588
|
const result2 = spawnSync(process.execPath, [specGate], {
|
|
1272
1589
|
cwd: root,
|
|
@@ -1277,7 +1594,6 @@ test('first-run cache bypass is blocked - canonical receipt required even withou
|
|
|
1277
1594
|
assert.equal(result2.stdout, '', 'valid canonical receipt on first run must not block');
|
|
1278
1595
|
} finally {
|
|
1279
1596
|
fs.rmSync(root, { recursive: true, force: true });
|
|
1280
|
-
try { fs.unlinkSync(path.join(PACKAGE_ROOT, 'src/claude/hooks/.logs/spec-gate-last.json')); } catch {}
|
|
1281
1597
|
}
|
|
1282
1598
|
});
|
|
1283
1599
|
|
|
@@ -1415,6 +1731,20 @@ test('runtime provenance derives exact Git evidence, CLI context, and stale/forg
|
|
|
1415
1731
|
'valid-shaped arbitrary SHA values must not bind',
|
|
1416
1732
|
);
|
|
1417
1733
|
|
|
1734
|
+
fs.writeFileSync(specFile, JSON.stringify({
|
|
1735
|
+
status: 'in_progress',
|
|
1736
|
+
feature_name: 'demo',
|
|
1737
|
+
task_registry: {},
|
|
1738
|
+
receipt_refresh: true,
|
|
1739
|
+
}));
|
|
1740
|
+
const specCommit = spawnSync('git', ['-C', root, 'add', 'specs/demo/spec.json'], { encoding: 'utf8' });
|
|
1741
|
+
assert.equal(specCommit.status, 0, specCommit.stderr);
|
|
1742
|
+
const commitReceipt = spawnSync('git', ['-C', root, 'commit', '-qm', 'refresh spec receipt'], { encoding: 'utf8' });
|
|
1743
|
+
assert.equal(commitReceipt.status, 0, commitReceipt.stderr);
|
|
1744
|
+
const afterSpecOnlyCommit = PROVENANCE_HELPER.deriveRuntimeContext(input);
|
|
1745
|
+
assert.equal(afterSpecOnlyCommit.base, initial.base, 'spec-only commits must not stale receipt Base');
|
|
1746
|
+
assert.equal(afterSpecOnlyCommit.head, initial.head, 'spec-only commits must not stale receipt Head');
|
|
1747
|
+
|
|
1418
1748
|
const cli = spawnSync(process.execPath, [
|
|
1419
1749
|
path.join(PACKAGE_ROOT, 'src/claude/scripts/provenance.cjs'), '--json',
|
|
1420
1750
|
'--project-root', root, '--specs-root', specsRoot, '--spec-file', specFile,
|
|
@@ -1522,6 +1852,52 @@ test('runtime provenance derives exact Git evidence, CLI context, and stale/forg
|
|
|
1522
1852
|
}
|
|
1523
1853
|
});
|
|
1524
1854
|
|
|
1855
|
+
test('runtime provenance chooses a deterministic root when all history is Specs-only and multi-root', () => {
|
|
1856
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-provenance-multi-root-'));
|
|
1857
|
+
const specsRoot = path.join(root, 'specs');
|
|
1858
|
+
const featureRoot = path.join(specsRoot, 'demo');
|
|
1859
|
+
const specFile = path.join(featureRoot, 'spec.json');
|
|
1860
|
+
try {
|
|
1861
|
+
fs.mkdirSync(featureRoot, { recursive: true });
|
|
1862
|
+
fs.writeFileSync(specFile, JSON.stringify({ status: 'in_progress', feature_name: 'demo', task_registry: {} }));
|
|
1863
|
+
for (const args of [
|
|
1864
|
+
['init', '-q', '-b', 'first-root'],
|
|
1865
|
+
['config', 'user.email', 'cafekit@example.invalid'],
|
|
1866
|
+
['config', 'user.name', 'CafeKit Test'],
|
|
1867
|
+
['add', 'specs/demo/spec.json'],
|
|
1868
|
+
['commit', '-qm', 'first specs root'],
|
|
1869
|
+
['checkout', '--orphan', 'unrelated-specs'],
|
|
1870
|
+
['rm', '-qrf', '.'],
|
|
1871
|
+
]) {
|
|
1872
|
+
const result = spawnSync('git', ['-C', root, ...args], { encoding: 'utf8' });
|
|
1873
|
+
assert.equal(result.status, 0, result.stderr);
|
|
1874
|
+
}
|
|
1875
|
+
const unrelatedSpec = path.join(specsRoot, 'unrelated', 'spec.json');
|
|
1876
|
+
fs.mkdirSync(path.dirname(unrelatedSpec), { recursive: true });
|
|
1877
|
+
fs.writeFileSync(unrelatedSpec, JSON.stringify({ status: 'in_progress', feature_name: 'unrelated', task_registry: {} }));
|
|
1878
|
+
let result = spawnSync('git', ['-C', root, 'add', 'specs/unrelated/spec.json'], { encoding: 'utf8' });
|
|
1879
|
+
assert.equal(result.status, 0, result.stderr);
|
|
1880
|
+
result = spawnSync('git', ['-C', root, 'commit', '-qm', 'second specs root'], { encoding: 'utf8' });
|
|
1881
|
+
assert.equal(result.status, 0, result.stderr);
|
|
1882
|
+
result = spawnSync('git', ['-C', root, 'merge', '--allow-unrelated-histories', '-qm', 'merge specs roots', 'first-root'], { encoding: 'utf8' });
|
|
1883
|
+
assert.equal(result.status, 0, result.stderr);
|
|
1884
|
+
|
|
1885
|
+
const roots = spawnSync('git', ['-C', root, 'rev-list', '--max-parents=0', '--reverse', 'HEAD'], { encoding: 'utf8' });
|
|
1886
|
+
assert.equal(roots.status, 0, roots.stderr);
|
|
1887
|
+
const expectedBase = roots.stdout.trim().split('\n')[0].toLowerCase();
|
|
1888
|
+
const context = PROVENANCE_HELPER.deriveRuntimeContext({
|
|
1889
|
+
projectRoot: root,
|
|
1890
|
+
specsRoot,
|
|
1891
|
+
specFile,
|
|
1892
|
+
featureName: 'demo',
|
|
1893
|
+
runtimeSession: 'multi-root-session',
|
|
1894
|
+
});
|
|
1895
|
+
assert.equal(context.base, expectedBase);
|
|
1896
|
+
} finally {
|
|
1897
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
1898
|
+
}
|
|
1899
|
+
});
|
|
1900
|
+
|
|
1525
1901
|
test('lane traces - Direct, Standard, explicit Strict classification, override, state mutation and completion', () => {
|
|
1526
1902
|
// Direct: isolated reversible low-risk
|
|
1527
1903
|
const direct = POLICY.classifyLane({ reversible: true, lowRisk: true, isolated: true, taskCount: 1 });
|
|
@@ -1595,26 +1971,27 @@ test('flash selective promotion - only specific task is promoted, not blanket',
|
|
|
1595
1971
|
test('parallel waves require immutable provenance receipts and safe recovery', () => {
|
|
1596
1972
|
const waves = markdownLegacyRegions(read(PARALLEL_WAVES)).primary;
|
|
1597
1973
|
assert.match(waves, /only for explicit parallel execution/i);
|
|
1598
|
-
assert.match(waves, /worktree
|
|
1974
|
+
assert.match(waves, /isolated worktree support[\s\S]*fall back to the sequential loop/i);
|
|
1599
1975
|
assert.match(waves, /Clamp `--parallel N` to 1\.\.5; default to 3/i);
|
|
1600
|
-
assert.match(waves, /clean destination[\s\S]*base_sha[\s\S]*
|
|
1601
|
-
assert.match(waves, /
|
|
1976
|
+
assert.match(waves, /clean destination[\s\S]*base_sha[\s\S]*incompatible bases/i);
|
|
1977
|
+
assert.match(waves, /consent for worker commits and controller cherry-picks/i);
|
|
1602
1978
|
for (const field of ['base_sha', 'head_sha', 'branch', 'worktree_path', 'commit_range']) {
|
|
1603
1979
|
assert.match(waves, new RegExp(`\\b${field}\\b`));
|
|
1604
1980
|
}
|
|
1605
|
-
assert.match(waves, /git
|
|
1606
|
-
assert.match(waves, /
|
|
1607
|
-
assert.match(waves, /
|
|
1981
|
+
assert.match(waves, /git rev-list --reverse <base_sha>\.\.<head_sha>/i);
|
|
1982
|
+
assert.match(waves, /git diff --name-status <base_sha>\.\.<head_sha>/i);
|
|
1983
|
+
assert.match(waves, /code\/spec repair[\s\S]*new commit/i);
|
|
1984
|
+
assert.match(waves, /dependencies from the plan table and each task/i);
|
|
1608
1985
|
for (const sharedSurface of ['registry', 'lockfile', 'manifest', 'generated output', 'migration', 'export barrel']) {
|
|
1609
1986
|
assert.match(waves, new RegExp(sharedSurface, 'i'));
|
|
1610
1987
|
}
|
|
1611
1988
|
assert.match(waves, /One file has one writer per wave/i);
|
|
1612
|
-
assert.match(waves, /
|
|
1613
|
-
assert.match(waves, /
|
|
1989
|
+
assert.match(waves, /only the controller writes state\/proof/i);
|
|
1990
|
+
assert.match(waves, /commit only with user authorization/i);
|
|
1614
1991
|
assert.match(waves, /blocked or failed worker keeps[\s\S]*worktree and branch/i);
|
|
1615
1992
|
assert.match(waves, /Never[\s\S]*force-delete[\s\S]*merge success or explicit discard authorization/i);
|
|
1616
|
-
assert.match(waves, /git cherry-pick <worker-commit>/i);
|
|
1617
|
-
assert.match(waves, /On conflict, abort the
|
|
1993
|
+
assert.match(waves, /git cherry-pick <next-worker-commit>/i);
|
|
1994
|
+
assert.match(waves, /On conflict, abort the pick[\s\S]*retain its branch\/worktree/i);
|
|
1618
1995
|
assert.match(waves, /cleanup_authorization: merged-release/i);
|
|
1619
1996
|
assert.match(waves, /affected integration|final.*integration/i);
|
|
1620
1997
|
for (const category of ['baseline', 'environment', 'spec', 'code']) {
|
|
@@ -2305,7 +2682,7 @@ test('P0 receipt fail-closed: Exit 1/-1/abc/conflict/empty command rejected', ()
|
|
|
2305
2682
|
});
|
|
2306
2683
|
|
|
2307
2684
|
test('P0 flash marker-only must not promote or finalize', () => {
|
|
2308
|
-
const flash = canonicalFlashTask({ blocker: 'awaiting /
|
|
2685
|
+
const flash = canonicalFlashTask({ blocker: 'awaiting /cf:test' });
|
|
2309
2686
|
const markerOnly = 'Verification: PASS';
|
|
2310
2687
|
const promoted = POLICY.promoteFlashTask(flash, 'PASS', markerOnly);
|
|
2311
2688
|
assert.equal(promoted.status, 'in_progress');
|
|
@@ -2388,7 +2765,7 @@ test('P0 sync-finalize derives promotion from current FLASH_UNVERIFIED plus expl
|
|
|
2388
2765
|
assert.equal(forgedOpenResult.status, 'in_progress');
|
|
2389
2766
|
assert.equal(forgedOpenResult.unblocks, false);
|
|
2390
2767
|
|
|
2391
|
-
const current = canonicalFlashTask({ blocker: 'awaiting /
|
|
2768
|
+
const current = canonicalFlashTask({ blocker: 'awaiting /cf:test' });
|
|
2392
2769
|
const supported = spawnSync(process.execPath, [
|
|
2393
2770
|
POLICY_PATH,
|
|
2394
2771
|
'--sync-finalize',
|
|
@@ -228,8 +228,8 @@ test('combined install keeps CORE neutral and records native shared-root trade-o
|
|
|
228
228
|
[agents, CODEX_START]
|
|
229
229
|
]) assert.equal((content.match(new RegExp(marker, 'g')) || []).length, 1);
|
|
230
230
|
|
|
231
|
-
assert.doesNotMatch(core, /Claude|Codex|\.claude|\.codex|\/
|
|
232
|
-
assert.doesNotMatch(claudeBlock, /Codex|\.codex|\$
|
|
231
|
+
assert.doesNotMatch(core, /Claude|Codex|\.claude|\.codex|\/cf:|\$cf-/i);
|
|
232
|
+
assert.doesNotMatch(claudeBlock, /Codex|\.codex|\$cf-/i);
|
|
233
233
|
assert.match(codex, /native project instruction surface is root `AGENTS\.md`/);
|
|
234
234
|
assert.match(agents, /shared-root trade-off is intentional/);
|
|
235
235
|
// H5 remediation: ownership/ignore contract must be explicit — not just marker presence
|
|
@@ -241,7 +241,7 @@ test('combined install keeps CORE neutral and records native shared-root trade-o
|
|
|
241
241
|
assert.doesNotMatch(codex, /If you are Codex CLI or any other runtime, ignore this entire Codex block/i);
|
|
242
242
|
assert.match(codex, /fail-safe/i);
|
|
243
243
|
// CORE must not contain runtime-specific directives that would leak
|
|
244
|
-
assert.doesNotMatch(core, /\$
|
|
244
|
+
assert.doesNotMatch(core, /\$cf-|cf:/i);
|
|
245
245
|
// No cross-runtime directive leakage: Claude block must not contain foreign runtime directives
|
|
246
246
|
assert.doesNotMatch(claudeBlock, /<!-- CAFEKIT CODEX /);
|
|
247
247
|
assert.doesNotMatch(codex, /<!-- CAFEKIT CORE /);
|