all-hands-cli 0.1.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/.allhands/README.md +75 -0
- package/.allhands/agents/compounder.yaml +15 -0
- package/.allhands/agents/coordinator.yaml +17 -0
- package/.allhands/agents/documentor.yaml +15 -0
- package/.allhands/agents/e2e-test-planner.yaml +17 -0
- package/.allhands/agents/emergent.yaml +22 -0
- package/.allhands/agents/executor.yaml +14 -0
- package/.allhands/agents/ideation.yaml +11 -0
- package/.allhands/agents/initiative-steering.yaml +19 -0
- package/.allhands/agents/judge.yaml +13 -0
- package/.allhands/agents/planner.yaml +19 -0
- package/.allhands/agents/pr-reviewer.yaml +15 -0
- package/.allhands/docs.json +5 -0
- package/.allhands/docs.local.json +26 -0
- package/.allhands/flows/COMPOUNDING.md +203 -0
- package/.allhands/flows/COORDINATION.md +89 -0
- package/.allhands/flows/CORE.md +87 -0
- package/.allhands/flows/DOCUMENTATION.md +218 -0
- package/.allhands/flows/E2E_TEST_PLAN_BUILDING.md +140 -0
- package/.allhands/flows/EMERGENT_PLANNING.md +57 -0
- package/.allhands/flows/IDEATION_SCOPING.md +154 -0
- package/.allhands/flows/INITIATIVE_STEERING.md +110 -0
- package/.allhands/flows/JUDGE_REVIEWING.md +79 -0
- package/.allhands/flows/PROMPT_TASK_EXECUTION.md +68 -0
- package/.allhands/flows/PR_REVIEWING.md +43 -0
- package/.allhands/flows/SPEC_PLANNING.md +216 -0
- package/.allhands/flows/harness/WRITING_HARNESS_FLOWS.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_KNOWLEDGE.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_ORCHESTRATION.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_SKILLS.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_TOOLS.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_VALIDATION_TOOLING.md +27 -0
- package/.allhands/flows/shared/CODEBASE_UNDERSTANDING.md +72 -0
- package/.allhands/flows/shared/CREATE_HARNESS_SPEC.md +48 -0
- package/.allhands/flows/shared/CREATE_SPEC.md +41 -0
- package/.allhands/flows/shared/CREATE_VALIDATION_TOOLING_SPEC.md +70 -0
- package/.allhands/flows/shared/DOCUMENTATION_DISCOVERY.md +123 -0
- package/.allhands/flows/shared/DOCUMENTATION_WRITER.md +101 -0
- package/.allhands/flows/shared/EMERGENT_REFINEMENT_ANALYSIS.md +76 -0
- package/.allhands/flows/shared/EXTERNAL_TECH_GUIDANCE.md +97 -0
- package/.allhands/flows/shared/IDEATION_CODEBASE_GROUNDING.md +49 -0
- package/.allhands/flows/shared/PLAN_DEEPENING.md +152 -0
- package/.allhands/flows/shared/PROMPT_TASKS_CURATION.md +113 -0
- package/.allhands/flows/shared/PROMPT_VALIDATION_REVIEW.MD +99 -0
- package/.allhands/flows/shared/QUICK_PREMORTEM.md +70 -0
- package/.allhands/flows/shared/RESEARCH_GUIDANCE.md +38 -0
- package/.allhands/flows/shared/REVIEW_OPTIONS_BREAKDOWN.md +68 -0
- package/.allhands/flows/shared/SKILL_EXTRACTION.md +84 -0
- package/.allhands/flows/shared/SPEC_FLOW_ANALYSIS.md +119 -0
- package/.allhands/flows/shared/TDD_WORKFLOW.md +109 -0
- package/.allhands/flows/shared/UTILIZE_VALIDATION_TOOLING.md +84 -0
- package/.allhands/flows/shared/WRITING_HARNESS_FLOWS.md +11 -0
- package/.allhands/flows/shared/WRITING_HARNESS_MCP_TOOLS.md +84 -0
- package/.allhands/flows/shared/jury/ARCHITECTURE_REVIEW.md +91 -0
- package/.allhands/flows/shared/jury/BEST_PRACTICES_REVIEW.md +80 -0
- package/.allhands/flows/shared/jury/CLAIM_VERIFICATION_REVIEW.md +101 -0
- package/.allhands/flows/shared/jury/EXPECTATIONS_FIT_REVIEW.md +78 -0
- package/.allhands/flows/shared/jury/MAINTAINABILITY_REVIEW.md +110 -0
- package/.allhands/flows/shared/jury/PROMPTS_EXPECTATIONS_FIT.md +74 -0
- package/.allhands/flows/shared/jury/PROMPTS_FLOW_ANALYSIS.md +92 -0
- package/.allhands/flows/shared/jury/PROMPTS_YAGNI.md +78 -0
- package/.allhands/flows/shared/jury/PROMPT_PREMORTEM.md +125 -0
- package/.allhands/flows/shared/jury/SECURITY_REVIEW.md +86 -0
- package/.allhands/flows/shared/jury/YAGNI_REVIEW.md +82 -0
- package/.allhands/flows/wip/DEBUG_INVESTIGATION.md +162 -0
- package/.allhands/flows/wip/MEMORY_RECALL.md +62 -0
- package/.allhands/harness/ah +131 -0
- package/.allhands/harness/package-lock.json +5292 -0
- package/.allhands/harness/package.json +52 -0
- package/.allhands/harness/src/__tests__/e2e/commands.test.ts +307 -0
- package/.allhands/harness/src/__tests__/e2e/event-loop.test.ts +539 -0
- package/.allhands/harness/src/__tests__/e2e/hooks.test.ts +427 -0
- package/.allhands/harness/src/__tests__/e2e/new-initiative-routing.test.ts +137 -0
- package/.allhands/harness/src/__tests__/e2e/run-e2e.ts +109 -0
- package/.allhands/harness/src/__tests__/e2e/specs-type.test.ts +210 -0
- package/.allhands/harness/src/__tests__/e2e/validation-hooks.test.ts +669 -0
- package/.allhands/harness/src/__tests__/e2e/validation-path-consistency.test.ts +354 -0
- package/.allhands/harness/src/__tests__/e2e/validation.test.ts +528 -0
- package/.allhands/harness/src/__tests__/harness/assertions.ts +318 -0
- package/.allhands/harness/src/__tests__/harness/cli-runner.ts +359 -0
- package/.allhands/harness/src/__tests__/harness/fixture.ts +384 -0
- package/.allhands/harness/src/__tests__/harness/hook-runner.ts +411 -0
- package/.allhands/harness/src/__tests__/harness/index.ts +122 -0
- package/.allhands/harness/src/cli.ts +36 -0
- package/.allhands/harness/src/commands/complexity.ts +177 -0
- package/.allhands/harness/src/commands/context7.ts +202 -0
- package/.allhands/harness/src/commands/docs.ts +557 -0
- package/.allhands/harness/src/commands/hooks.ts +24 -0
- package/.allhands/harness/src/commands/index.ts +51 -0
- package/.allhands/harness/src/commands/knowledge.ts +382 -0
- package/.allhands/harness/src/commands/memories.ts +302 -0
- package/.allhands/harness/src/commands/notify.ts +61 -0
- package/.allhands/harness/src/commands/oracle.ts +158 -0
- package/.allhands/harness/src/commands/perplexity.ts +220 -0
- package/.allhands/harness/src/commands/planning.ts +245 -0
- package/.allhands/harness/src/commands/schema.ts +73 -0
- package/.allhands/harness/src/commands/skills.ts +128 -0
- package/.allhands/harness/src/commands/solutions.ts +353 -0
- package/.allhands/harness/src/commands/spawn.ts +158 -0
- package/.allhands/harness/src/commands/specs.ts +532 -0
- package/.allhands/harness/src/commands/tavily.ts +226 -0
- package/.allhands/harness/src/commands/tools.ts +579 -0
- package/.allhands/harness/src/commands/trace.ts +327 -0
- package/.allhands/harness/src/commands/tui.ts +960 -0
- package/.allhands/harness/src/commands/validate.ts +143 -0
- package/.allhands/harness/src/commands/validation-tools.ts +108 -0
- package/.allhands/harness/src/hooks/context.ts +1442 -0
- package/.allhands/harness/src/hooks/enforcement.ts +170 -0
- package/.allhands/harness/src/hooks/index.ts +54 -0
- package/.allhands/harness/src/hooks/lifecycle.ts +229 -0
- package/.allhands/harness/src/hooks/notification.ts +104 -0
- package/.allhands/harness/src/hooks/observability.ts +551 -0
- package/.allhands/harness/src/hooks/session.ts +88 -0
- package/.allhands/harness/src/hooks/shared.ts +815 -0
- package/.allhands/harness/src/hooks/transcript-parser.ts +208 -0
- package/.allhands/harness/src/hooks/validation.ts +617 -0
- package/.allhands/harness/src/lib/__tests__/ctags.test.ts +244 -0
- package/.allhands/harness/src/lib/__tests__/docs-validation.test.ts +344 -0
- package/.allhands/harness/src/lib/__tests__/mcp-runtime.test.ts +190 -0
- package/.allhands/harness/src/lib/__tests__/schema.test.ts +861 -0
- package/.allhands/harness/src/lib/base-command.ts +198 -0
- package/.allhands/harness/src/lib/cli-daemon.ts +343 -0
- package/.allhands/harness/src/lib/compaction.ts +313 -0
- package/.allhands/harness/src/lib/ctags.ts +497 -0
- package/.allhands/harness/src/lib/docs-validation.ts +907 -0
- package/.allhands/harness/src/lib/event-loop.ts +662 -0
- package/.allhands/harness/src/lib/flows.ts +155 -0
- package/.allhands/harness/src/lib/git.ts +276 -0
- package/.allhands/harness/src/lib/knowledge-worker.ts +72 -0
- package/.allhands/harness/src/lib/knowledge.ts +810 -0
- package/.allhands/harness/src/lib/llm.ts +255 -0
- package/.allhands/harness/src/lib/mcp-client.ts +432 -0
- package/.allhands/harness/src/lib/mcp-daemon.ts +486 -0
- package/.allhands/harness/src/lib/mcp-runtime.ts +418 -0
- package/.allhands/harness/src/lib/notification.ts +115 -0
- package/.allhands/harness/src/lib/opencode/index.ts +70 -0
- package/.allhands/harness/src/lib/opencode/profiles.ts +300 -0
- package/.allhands/harness/src/lib/opencode/prompts/codesearch.md +98 -0
- package/.allhands/harness/src/lib/opencode/prompts/knowledge-aggregator.md +67 -0
- package/.allhands/harness/src/lib/opencode/runner.ts +281 -0
- package/.allhands/harness/src/lib/oracle.ts +926 -0
- package/.allhands/harness/src/lib/planning-utils.ts +150 -0
- package/.allhands/harness/src/lib/planning.ts +605 -0
- package/.allhands/harness/src/lib/pr-review.ts +225 -0
- package/.allhands/harness/src/lib/prompts.ts +522 -0
- package/.allhands/harness/src/lib/schema.ts +418 -0
- package/.allhands/harness/src/lib/schemas/agent-profile.ts +141 -0
- package/.allhands/harness/src/lib/schemas/template-vars.ts +138 -0
- package/.allhands/harness/src/lib/session.ts +164 -0
- package/.allhands/harness/src/lib/specs.ts +348 -0
- package/.allhands/harness/src/lib/tldr.ts +829 -0
- package/.allhands/harness/src/lib/tmux.ts +1051 -0
- package/.allhands/harness/src/lib/trace-store.ts +714 -0
- package/.allhands/harness/src/mcp/__tests__/index.test.ts +46 -0
- package/.allhands/harness/src/mcp/_template.ts +47 -0
- package/.allhands/harness/src/mcp/filesystem.ts +33 -0
- package/.allhands/harness/src/mcp/index.ts +69 -0
- package/.allhands/harness/src/mcp/playwright.ts +34 -0
- package/.allhands/harness/src/mcp/xcodebuild.ts +29 -0
- package/.allhands/harness/src/schemas/docs.schema.json +44 -0
- package/.allhands/harness/src/schemas/settings.schema.json +214 -0
- package/.allhands/harness/src/tui/actions.ts +227 -0
- package/.allhands/harness/src/tui/file-viewer-modal.ts +270 -0
- package/.allhands/harness/src/tui/index.ts +1574 -0
- package/.allhands/harness/src/tui/modal.ts +232 -0
- package/.allhands/harness/src/tui/prompts-pane.ts +186 -0
- package/.allhands/harness/src/tui/status-pane.ts +434 -0
- package/.allhands/harness/tsconfig.json +22 -0
- package/.allhands/harness/vitest.config.ts +13 -0
- package/.allhands/pillars.md +33 -0
- package/.allhands/principles.md +88 -0
- package/.allhands/schemas/alignment.yaml +51 -0
- package/.allhands/schemas/documentation.yaml +10 -0
- package/.allhands/schemas/prompt.yaml +92 -0
- package/.allhands/schemas/skill.yaml +34 -0
- package/.allhands/schemas/solution.yaml +131 -0
- package/.allhands/schemas/spec.yaml +67 -0
- package/.allhands/schemas/validation-suite.yaml +49 -0
- package/.allhands/schemas/workflow.yaml +51 -0
- package/.allhands/settings.json +57 -0
- package/.allhands/skills/claude-code-patterns/SKILL.md +60 -0
- package/.allhands/skills/claude-code-patterns/docs/context-hygiene.md +19 -0
- package/.allhands/skills/harness-maintenance/SKILL.md +449 -0
- package/.allhands/skills/harness-maintenance/references/core-architecture.md +187 -0
- package/.allhands/skills/harness-maintenance/references/harness-skills.md +87 -0
- package/.allhands/skills/harness-maintenance/references/knowledge-compounding.md +78 -0
- package/.allhands/skills/harness-maintenance/references/tools-commands-mcp-hooks.md +115 -0
- package/.allhands/skills/harness-maintenance/references/validation-tooling.md +77 -0
- package/.allhands/skills/harness-maintenance/references/writing-flows.md +84 -0
- package/.allhands/validation/browser-automation.md +109 -0
- package/.allhands/validation/xcode-automation.md +195 -0
- package/.allhands/workflows/documentation.md +86 -0
- package/.allhands/workflows/investigation.md +81 -0
- package/.allhands/workflows/milestone.md +91 -0
- package/.allhands/workflows/optimization.md +85 -0
- package/.allhands/workflows/refactor.md +99 -0
- package/.allhands/workflows/triage.md +81 -0
- package/.claude/README.md +1 -0
- package/.claude/agents/explorer.md +10 -0
- package/.claude/agents/researcher.md +11 -0
- package/.claude/agents/task-runner.md +8 -0
- package/.claude/settings.json +231 -0
- package/.env.ai.example +7 -0
- package/.github/workflows/npm-publish.yml +69 -0
- package/.internal.json +45 -0
- package/.tldr/config.json +11 -0
- package/.tldrignore +90 -0
- package/CLAUDE.md +6 -0
- package/README.md +98 -0
- package/bin/sync-cli.js +7552 -0
- package/concerns.md +7 -0
- package/docs/README.md +41 -0
- package/docs/agents/README.md +24 -0
- package/docs/agents/agent-configuration-system.md +86 -0
- package/docs/agents/execution-agents.md +50 -0
- package/docs/agents/knowledge-agents.md +61 -0
- package/docs/agents/orchestration-agent.md +57 -0
- package/docs/agents/planning-agents.md +84 -0
- package/docs/agents/quality-review-agents.md +67 -0
- package/docs/agents/workflow-agent-orchestration.md +69 -0
- package/docs/flows/README.md +44 -0
- package/docs/flows/compounding.md +126 -0
- package/docs/flows/coordination.md +72 -0
- package/docs/flows/core-harness-integration.md +63 -0
- package/docs/flows/documentation-orchestration.md +98 -0
- package/docs/flows/e2e-test-plan-building.md +83 -0
- package/docs/flows/emergent-refinement.md +104 -0
- package/docs/flows/flow-authoring-and-mcp-tools.md +89 -0
- package/docs/flows/judge-reviewing.md +112 -0
- package/docs/flows/plan-deepening-and-research.md +107 -0
- package/docs/flows/plan-review-jury.md +114 -0
- package/docs/flows/pr-reviewing.md +54 -0
- package/docs/flows/prompt-task-execution.md +119 -0
- package/docs/flows/spec-planning.md +162 -0
- package/docs/flows/type-specific-scoping-flows.md +49 -0
- package/docs/flows/validation-and-skills-integration.md +145 -0
- package/docs/flows/wip/wip-flows.md +102 -0
- package/docs/harness/README.md +23 -0
- package/docs/harness/agent-profiles.md +84 -0
- package/docs/harness/cli/README.md +24 -0
- package/docs/harness/cli/cli-entry-and-command-discovery.md +91 -0
- package/docs/harness/cli/docs-command.md +87 -0
- package/docs/harness/cli/knowledge-command.md +91 -0
- package/docs/harness/cli/minor-cli-commands.md +65 -0
- package/docs/harness/cli/oracle-command.md +113 -0
- package/docs/harness/cli/planning-command.md +95 -0
- package/docs/harness/cli/schema-and-validation-commands.md +154 -0
- package/docs/harness/cli/search-commands.md +97 -0
- package/docs/harness/cli/spawn-command.md +136 -0
- package/docs/harness/cli/specs-command.md +102 -0
- package/docs/harness/cli/tools-command.md +122 -0
- package/docs/harness/cli/trace-command.md +122 -0
- package/docs/harness/cli-daemon.md +92 -0
- package/docs/harness/event-loop.md +184 -0
- package/docs/harness/hooks/README.md +15 -0
- package/docs/harness/hooks/context-hooks.md +96 -0
- package/docs/harness/hooks/lifecycle-and-observability-hooks.md +135 -0
- package/docs/harness/hooks/validation-hooks.md +97 -0
- package/docs/harness/test-harness.md +149 -0
- package/docs/harness/tui.md +176 -0
- package/docs/memories.md +20 -0
- package/docs/solutions/agentic-issues/premature-agent-deletion-tui-action-dependency-20260130.md +49 -0
- package/docs/solutions/agentic-issues/ref-anchor-scope-mismatch-skill-references-20260131.md +55 -0
- package/docs/solutions/agentic-issues/tautological-tests-routing-20260131.md +52 -0
- package/docs/solutions/integration_issue/blocktool-output-format-mismatch-hook-runner-20260130.md +52 -0
- package/docs/solutions/integration_issue/dual-validation-path-divergence-schema-20260130.md +66 -0
- package/docs/solutions/security-issues/unsanitized-domain-path-join-20260131.md +52 -0
- package/docs/solutions/test-failures/event-loop-mock-ordering-checkAgentWindows-20260130.md +63 -0
- package/docs/sync-cli/README.md +19 -0
- package/docs/sync-cli/cli-entrypoint-and-commands.md +39 -0
- package/docs/sync-cli/commands/README.md +11 -0
- package/docs/sync-cli/commands/pull-manifest-command.md +36 -0
- package/docs/sync-cli/commands/push-command.md +84 -0
- package/docs/sync-cli/commands/sync-command.md +71 -0
- package/docs/sync-cli/systems/README.md +14 -0
- package/docs/sync-cli/systems/git-and-github-integration.md +49 -0
- package/docs/sync-cli/systems/interactive-ui.md +43 -0
- package/docs/sync-cli/systems/manifest-and-distribution.md +51 -0
- package/docs/sync-cli/systems/path-resolution.md +42 -0
- package/package.json +46 -0
- package/scripts/install-shim.sh +40 -0
- package/scripts/pre-pack.sh +25 -0
- package/specs/harness-maintenance-skill.spec.md +138 -0
- package/specs/roadmap/git-spec-lifecycle-management.spec.md +113 -0
- package/specs/sync-init-flag.spec.md +117 -0
- package/specs/unified-workflow-orchestration.spec.md +250 -0
- package/specs/validation-tooling-practice.spec.md +98 -0
- package/specs/workflow-domain-configuration.spec.md +265 -0
- package/src/commands/pull-manifest.ts +31 -0
- package/src/commands/push.ts +344 -0
- package/src/commands/sync.ts +289 -0
- package/src/lib/constants.ts +10 -0
- package/src/lib/dotfiles.ts +36 -0
- package/src/lib/fs-utils.ts +18 -0
- package/src/lib/gh.ts +40 -0
- package/src/lib/git.ts +63 -0
- package/src/lib/gitignore.ts +167 -0
- package/src/lib/manifest.ts +121 -0
- package/src/lib/marker-sync.ts +39 -0
- package/src/lib/paths.ts +38 -0
- package/src/lib/target-lines.ts +66 -0
- package/src/lib/ui.ts +78 -0
- package/src/sync-cli.ts +120 -0
- package/target-lines.json +23 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation Path Consistency Tests
|
|
3
|
+
*
|
|
4
|
+
* Documents behavioral divergences between the two validation paths:
|
|
5
|
+
* - lib/schema.ts: Core library used by validateFile(), CLI commands
|
|
6
|
+
* - hooks/validation.ts: Hook layer used by schema-pre/schema PostToolUse hooks
|
|
7
|
+
*
|
|
8
|
+
* These paths independently implement: frontmatter parsing, schema loading,
|
|
9
|
+
* and frontmatter validation with subtle differences in regex patterns,
|
|
10
|
+
* type branch coverage, return types, and schema.fields fallback behavior.
|
|
11
|
+
*
|
|
12
|
+
* Purpose: Prevent silent drift — when one path is updated, these tests
|
|
13
|
+
* catch cases where the other path would behave differently.
|
|
14
|
+
*
|
|
15
|
+
* Divergences documented:
|
|
16
|
+
* 1. Frontmatter parsing regex (trailing newline requirement)
|
|
17
|
+
* 2. validateFrontmatter type branch coverage (hooks missing boolean/date/object)
|
|
18
|
+
* 3. Return type shape (ValidationResult vs ValidationError[])
|
|
19
|
+
* 4. schema.fields fallback (lib uses it, hooks ignores it)
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
23
|
+
import {
|
|
24
|
+
extractFrontmatter,
|
|
25
|
+
validateFrontmatter,
|
|
26
|
+
type Schema,
|
|
27
|
+
type ValidationResult,
|
|
28
|
+
} from '../../lib/schema.js';
|
|
29
|
+
import {
|
|
30
|
+
createFixture,
|
|
31
|
+
runHook,
|
|
32
|
+
assertHookAllowed,
|
|
33
|
+
assertHookDenied,
|
|
34
|
+
PROMPT_TEMPLATE,
|
|
35
|
+
type TestFixture,
|
|
36
|
+
} from '../harness/index.js';
|
|
37
|
+
|
|
38
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
39
|
+
// Fixture Setup
|
|
40
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
let fixture: TestFixture;
|
|
43
|
+
|
|
44
|
+
beforeAll(() => {
|
|
45
|
+
fixture = createFixture({
|
|
46
|
+
name: 'validation-path-consistency',
|
|
47
|
+
files: {
|
|
48
|
+
'.planning/test/prompts/01.md': PROMPT_TEMPLATE('pending', 'Seeded', 1),
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
afterAll(() => {
|
|
54
|
+
fixture.cleanup();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
58
|
+
// Divergence 1: Frontmatter Parsing Regex
|
|
59
|
+
//
|
|
60
|
+
// lib extractFrontmatter: /^---\n([\s\S]*?)\n---\n([\s\S]*)$/
|
|
61
|
+
// hooks parseFrontmatter: /^---\n([\s\S]*?)\n---/
|
|
62
|
+
//
|
|
63
|
+
// Key difference: lib requires \n after closing --- and captures body via
|
|
64
|
+
// ([\s\S]*)$ — hooks does NOT require trailing newline after closing ---.
|
|
65
|
+
// This means content ending with "---" (no trailing newline) is parsed by
|
|
66
|
+
// hooks but rejected by lib.
|
|
67
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
describe('Divergence 1: Frontmatter parsing regex', () => {
|
|
70
|
+
it('lib extractFrontmatter returns null for content without trailing newline after closing ---', () => {
|
|
71
|
+
// Content: "---\nkey: val\n---" — no trailing newline or body
|
|
72
|
+
// lib regex /^---\n([\s\S]*?)\n---\n([\s\S]*)$/ requires \n after ---
|
|
73
|
+
// Expected: lib returns null frontmatter
|
|
74
|
+
// DIVERGENCE: hooks parseFrontmatter /^---\n([\s\S]*?)\n---/ WOULD match this
|
|
75
|
+
const content = '---\nkey: val\n---';
|
|
76
|
+
const libResult = extractFrontmatter(content);
|
|
77
|
+
expect(libResult.frontmatter).toBeNull();
|
|
78
|
+
// If hooks were tested directly, parseFrontmatter would return { key: 'val' }
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('lib extractFrontmatter succeeds when trailing newline is present', () => {
|
|
82
|
+
// Both paths agree when trailing newline exists
|
|
83
|
+
const content = '---\nkey: val\n---\n';
|
|
84
|
+
const libResult = extractFrontmatter(content);
|
|
85
|
+
expect(libResult.frontmatter).not.toBeNull();
|
|
86
|
+
expect(libResult.frontmatter!['key']).toBe('val');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('hooks schema-pre denies prompt content without trailing newline after closing ---', async () => {
|
|
90
|
+
// The hooks path uses parseFrontmatter which DOES match without trailing newline,
|
|
91
|
+
// BUT the content below has no required fields, so it gets denied for schema errors
|
|
92
|
+
// (not for missing frontmatter). This confirms hooks successfully parsed the frontmatter.
|
|
93
|
+
const content = '---\nkey: val\n---';
|
|
94
|
+
const result = await runHook(
|
|
95
|
+
'validation',
|
|
96
|
+
'schema-pre',
|
|
97
|
+
{
|
|
98
|
+
tool_name: 'Write',
|
|
99
|
+
tool_input: {
|
|
100
|
+
file_path: `${fixture.root}/.planning/test/prompts/no-trailing-newline.md`,
|
|
101
|
+
content,
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
fixture
|
|
105
|
+
);
|
|
106
|
+
// Hooks should deny (missing required fields), but importantly NOT for "missing frontmatter" —
|
|
107
|
+
// this proves the hooks regex successfully parsed frontmatter where lib would return null
|
|
108
|
+
assertHookDenied(result);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('both paths agree on standard content with trailing newline and body', async () => {
|
|
112
|
+
// Standard format: both paths should parse identically
|
|
113
|
+
const content = PROMPT_TEMPLATE('pending', 'Consistency Check', 99);
|
|
114
|
+
|
|
115
|
+
// lib path
|
|
116
|
+
const libResult = extractFrontmatter(content);
|
|
117
|
+
expect(libResult.frontmatter).not.toBeNull();
|
|
118
|
+
expect(libResult.frontmatter!['title']).toBe('Consistency Check');
|
|
119
|
+
|
|
120
|
+
// hooks path (via E2E runner)
|
|
121
|
+
const hookResult = await runHook(
|
|
122
|
+
'validation',
|
|
123
|
+
'schema-pre',
|
|
124
|
+
{
|
|
125
|
+
tool_name: 'Write',
|
|
126
|
+
tool_input: {
|
|
127
|
+
file_path: `${fixture.root}/.planning/test/prompts/standard-format.md`,
|
|
128
|
+
content,
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
fixture
|
|
132
|
+
);
|
|
133
|
+
assertHookAllowed(hookResult);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
138
|
+
// Divergence 2: validateFrontmatter Type Branch Coverage
|
|
139
|
+
//
|
|
140
|
+
// lib validateField handles: string, integer, boolean, date, enum, array, object
|
|
141
|
+
// hooks validateFrontmatter handles: string, integer, enum, array
|
|
142
|
+
//
|
|
143
|
+
// Missing in hooks: boolean, date, object (with nested property validation)
|
|
144
|
+
// Impact: A schema field typed as boolean/date/object passes hooks validation
|
|
145
|
+
// without type checking, but gets validated by lib. If only hooks enforce
|
|
146
|
+
// (PreToolUse/PostToolUse), incorrect types may slip through.
|
|
147
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
148
|
+
|
|
149
|
+
describe('Divergence 2: Type branch coverage', () => {
|
|
150
|
+
it('lib validates boolean fields — rejects non-boolean value', () => {
|
|
151
|
+
const schema: Schema = {
|
|
152
|
+
frontmatter: {
|
|
153
|
+
flag: { type: 'boolean', required: true },
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
const result = validateFrontmatter({ flag: 'true' }, schema);
|
|
157
|
+
expect(result.valid).toBe(false);
|
|
158
|
+
expect(result.errors[0].field).toBe('flag');
|
|
159
|
+
// DIVERGENCE: hooks has no 'boolean' case in its switch statement,
|
|
160
|
+
// so { flag: 'true' } would pass hooks validation without type error
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('lib validates boolean fields — accepts true', () => {
|
|
164
|
+
const schema: Schema = {
|
|
165
|
+
frontmatter: {
|
|
166
|
+
flag: { type: 'boolean', required: true },
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
const result = validateFrontmatter({ flag: true }, schema);
|
|
170
|
+
expect(result.valid).toBe(true);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('lib validates date fields — rejects invalid date string', () => {
|
|
174
|
+
const schema: Schema = {
|
|
175
|
+
frontmatter: {
|
|
176
|
+
created: { type: 'date', required: true },
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
const result = validateFrontmatter({ created: 'not-a-date' }, schema);
|
|
180
|
+
expect(result.valid).toBe(false);
|
|
181
|
+
expect(result.errors[0].field).toBe('created');
|
|
182
|
+
// DIVERGENCE: hooks has no 'date' case, so 'not-a-date' would pass hooks validation
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('lib validates object fields with nested properties', () => {
|
|
186
|
+
const schema: Schema = {
|
|
187
|
+
frontmatter: {
|
|
188
|
+
config: {
|
|
189
|
+
type: 'object',
|
|
190
|
+
required: true,
|
|
191
|
+
properties: {
|
|
192
|
+
name: { type: 'string', required: true },
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
// Missing required nested property
|
|
198
|
+
const result = validateFrontmatter({ config: {} }, schema);
|
|
199
|
+
expect(result.valid).toBe(false);
|
|
200
|
+
expect(result.errors[0].field).toBe('config.name');
|
|
201
|
+
// DIVERGENCE: hooks has no 'object' case and no nested validation,
|
|
202
|
+
// so { config: {} } would pass hooks validation entirely
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
207
|
+
// Divergence 3: Return Type Shape
|
|
208
|
+
//
|
|
209
|
+
// lib validateFrontmatter returns: ValidationResult { valid: boolean, errors: ValidationError[] }
|
|
210
|
+
// where ValidationError = { field, message, expected?, received? }
|
|
211
|
+
// hooks validateFrontmatter returns: ValidationError[] (array only, no 'valid' wrapper)
|
|
212
|
+
// where ValidationError = { field, message } (no expected/received)
|
|
213
|
+
//
|
|
214
|
+
// Impact: Code consuming hooks errors cannot check .valid — must check .length.
|
|
215
|
+
// Error messages also differ in format (hooks uses "Field 'X' must be..." vs
|
|
216
|
+
// lib uses "Expected string" with expected/received metadata).
|
|
217
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
218
|
+
|
|
219
|
+
describe('Divergence 3: Return type shape', () => {
|
|
220
|
+
it('lib returns ValidationResult with valid flag and error metadata', () => {
|
|
221
|
+
const schema: Schema = {
|
|
222
|
+
frontmatter: {
|
|
223
|
+
name: { type: 'string', required: true },
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
const result: ValidationResult = validateFrontmatter({ name: 42 }, schema);
|
|
227
|
+
// lib returns structured result with valid flag
|
|
228
|
+
expect(typeof result.valid).toBe('boolean');
|
|
229
|
+
expect(result.valid).toBe(false);
|
|
230
|
+
// lib errors include expected/received metadata
|
|
231
|
+
expect(result.errors[0].expected).toBeDefined();
|
|
232
|
+
expect(result.errors[0].received).toBeDefined();
|
|
233
|
+
// DIVERGENCE: hooks returns ValidationError[] directly (no .valid wrapper)
|
|
234
|
+
// and errors only have { field, message } — no expected/received
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('lib error message format uses generic phrasing', () => {
|
|
238
|
+
const schema: Schema = {
|
|
239
|
+
frontmatter: {
|
|
240
|
+
count: { type: 'integer', required: true },
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
const result = validateFrontmatter({ count: 'not-int' }, schema);
|
|
244
|
+
expect(result.errors[0].message).toBe('Expected integer');
|
|
245
|
+
// DIVERGENCE: hooks would produce "Field 'count' must be an integer"
|
|
246
|
+
// (includes field name in message, different phrasing)
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
251
|
+
// Divergence 4: schema.fields Fallback
|
|
252
|
+
//
|
|
253
|
+
// lib validateFrontmatter: iterates schema.frontmatter || schema.fields || {}
|
|
254
|
+
// hooks validateFrontmatter: returns [] immediately if !schema.frontmatter
|
|
255
|
+
//
|
|
256
|
+
// Impact: Schemas that use `fields` key (e.g., status.yaml) are validated by
|
|
257
|
+
// lib but silently pass hooks validation with zero errors.
|
|
258
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
259
|
+
|
|
260
|
+
describe('Divergence 4: schema.fields fallback', () => {
|
|
261
|
+
it('lib validates against schema.fields when schema.frontmatter is absent', () => {
|
|
262
|
+
const schema: Schema = {
|
|
263
|
+
fields: {
|
|
264
|
+
name: { type: 'string', required: true },
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
const result = validateFrontmatter({}, schema);
|
|
268
|
+
expect(result.valid).toBe(false);
|
|
269
|
+
expect(result.errors[0].field).toBe('name');
|
|
270
|
+
// DIVERGENCE: hooks returns [] (no errors) because !schema.frontmatter is true
|
|
271
|
+
// and it returns early without checking schema.fields
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('lib falls through to empty object when neither frontmatter nor fields exist', () => {
|
|
275
|
+
const schema: Schema = {};
|
|
276
|
+
const result = validateFrontmatter({ anything: 'goes' }, schema);
|
|
277
|
+
expect(result.valid).toBe(true);
|
|
278
|
+
expect(result.errors).toHaveLength(0);
|
|
279
|
+
// Both paths agree here: no fields defined means no validation to do
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
284
|
+
// Agreement Tests: Cases Where Both Paths Agree
|
|
285
|
+
//
|
|
286
|
+
// These verify the happy path where both implementations produce consistent
|
|
287
|
+
// results, serving as regression anchors if either path is refactored.
|
|
288
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
289
|
+
|
|
290
|
+
describe('Agreement: both paths produce consistent results', () => {
|
|
291
|
+
it('both paths accept valid string field', () => {
|
|
292
|
+
// lib path
|
|
293
|
+
const schema: Schema = {
|
|
294
|
+
frontmatter: {
|
|
295
|
+
title: { type: 'string', required: true },
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
const result = validateFrontmatter({ title: 'valid' }, schema);
|
|
299
|
+
expect(result.valid).toBe(true);
|
|
300
|
+
// hooks path handles string identically (typeof value !== 'string' check)
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('both paths reject missing required field', () => {
|
|
304
|
+
const schema: Schema = {
|
|
305
|
+
frontmatter: {
|
|
306
|
+
name: { type: 'string', required: true },
|
|
307
|
+
},
|
|
308
|
+
};
|
|
309
|
+
const result = validateFrontmatter({}, schema);
|
|
310
|
+
expect(result.valid).toBe(false);
|
|
311
|
+
// Both paths check: field.required && (value === undefined || value === null)
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('both paths validate array item types consistently', () => {
|
|
315
|
+
const schema: Schema = {
|
|
316
|
+
frontmatter: {
|
|
317
|
+
tools: { type: 'array', required: true, items: 'string' },
|
|
318
|
+
},
|
|
319
|
+
};
|
|
320
|
+
// Valid array
|
|
321
|
+
const valid = validateFrontmatter({ tools: ['a', 'b'] }, schema);
|
|
322
|
+
expect(valid.valid).toBe(true);
|
|
323
|
+
|
|
324
|
+
// Invalid array (number in string array)
|
|
325
|
+
const invalid = validateFrontmatter({ tools: [123, 'b'] }, schema);
|
|
326
|
+
expect(invalid.valid).toBe(false);
|
|
327
|
+
// Both paths use identical item-type checking logic for arrays
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('both paths validate enum fields consistently', () => {
|
|
331
|
+
const schema: Schema = {
|
|
332
|
+
frontmatter: {
|
|
333
|
+
status: { type: 'enum', required: true, values: ['pending', 'done'] },
|
|
334
|
+
},
|
|
335
|
+
};
|
|
336
|
+
const valid = validateFrontmatter({ status: 'pending' }, schema);
|
|
337
|
+
expect(valid.valid).toBe(true);
|
|
338
|
+
|
|
339
|
+
const invalid = validateFrontmatter({ status: 'garbage' }, schema);
|
|
340
|
+
expect(invalid.valid).toBe(false);
|
|
341
|
+
// Both paths check: !field.values?.includes(String(value))
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it('both paths silently pass extra fields not in schema', () => {
|
|
345
|
+
const schema: Schema = {
|
|
346
|
+
frontmatter: {
|
|
347
|
+
name: { type: 'string', required: true },
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
const result = validateFrontmatter({ name: 'valid', extra: 'ignored' }, schema);
|
|
351
|
+
expect(result.valid).toBe(true);
|
|
352
|
+
// Neither path validates unknown fields — both iterate only schema-defined fields
|
|
353
|
+
});
|
|
354
|
+
});
|