Plan: Lefthook Commit Gate for Catalogue Consumers
Plan: Lefthook Commit Gate for Catalogue Consumers
Plan state
- Status: Complete; review_count=1; repair_count=1
- Delivery goal: Linear Story IP-425 through Task IP-426
- Task identity mode:
provider-task - Architecture applicability:
architecture-bearing - Evidence: a new public contribution family connects shared schemas, CLI policy, compilation, materialization, observation, receipts, check output, agent follow-through, and docs.
- Branch:
feat/pre-commit-hooks - Immutable start:
b0c53c2552e3c0a1ab34519e7486a9bfe11d1fbb - Pre-implementation HEAD:
9a633d2395aef00f77eaf442506498bd21cf8d8b - Post-implementation route: review 1 accepted IP426-CG-001/IP426-CG-002; repair epoch 1 complete; consumer validator, affected lint/check graph, and supplemental CI-repair review pass. Hosted exact-head CI and Devpunks provider completion readback remain closeout gates.
Initial situation and problem
The accepted spec and requirements grill are complete. The branch contains the canonical spec, research, glossary, and backlog handoff, but no Commit Gate implementation. A fresh hi check --json on 2026-09-01 reports current CLI/settings, no scaffold drift, no degradation, and no pending action.
Typed project settings, neutral context compilation, ownership-aware scaffold reconciliation, managed desired-state receipts, read-only repository check, and staged owner selection already exist. None represents a first-class Commit Gate, complete Quality Command Contract, Hook Migration Proposal, unresolved Post-Command Handoff, separate Commit Gate Lifecycle Receipt, or exact current health state.
Solution shape
Create one deep CLI-owned commit-gate feature. Its public interface consumes typed policy, compiled contribution, desired scaffold evidence, and observed repository facts. It returns typed desired actions, conflicts, handoff requirements, lifecycle-receipt validity, and current health. Keep filesystem, Git, package-manager, config parsing, and process mechanics in adapters. Keep packages/scaffold behavior-free and limited to stable contribution/context schemas.
Use one packaged consumer runner for staged path parsing, owner routing, exactly-once Quality Command Contract dispatch, parallel lint/format execution, and aggregate failures. Generate or merge one HI-owned Lefthook pre-commit.commands.lint entry. Use upstream lefthook.yml only when HI creates config; preserve recognized existing config and return unsupported, ambiguous, or colliding state for user decision.
The CLI persists policy and desired state. It does not silently migrate managers or claim hook installation. Scaffold emits a structured unresolved Post-Command Handoff. The $hi-cli agent performs only authorized lifecycle work, verifies exact dependency/config/hook facts, and records the separate lifecycle receipt. hi check re-observes facts, validates receipt binding, and never repairs.
Design-it-twice selected this boundary over adding branches directly across broad existing modules. The dedicated feature hides policy, coexistence, health, and proof complexity while existing owners retain settings, compilation, materialization, adapters, and presentation.
Locked decision ledger
| Decision | Status | Reason |
|---|---|---|
| Consumer eligibility | Locked | Supported JavaScript package manager and lockfile plus complete Quality Command Contract. |
| Policy | Locked | commitGate accepts enabled or disabled; absent means enabled; init/ensure persist policy only. |
| Contribution | Locked | Commit Gate is separate from AI-agent lifecycle hooks. |
| Dependency | Locked | Project-local lefthook@2.1.10, pinned to reviewed evidence. |
| Configuration | Locked | HI owns exactly pre-commit.commands.lint; consumer entries remain unchanged. |
| Migration | Locked | Other manager or unsafe config defers all materialization and yields a non-authorizing proposal. |
| Execution | Locked | Staged applicable owners once; lint/format parallel; aggregate failures; empty scope starts no tools. |
| Proof | Locked | Managed receipt proves desired state; separate lifecycle receipt proves live operation. |
| Check | Locked | Exact current health, read-only, no historical bypass claim. |
| Authority | Locked | CI remains merge authority; deliberate local bypass remains possible. |
Assumptions and constraints
- The canonical spec, grill artifacts, glossary, and 2026-09-01 backlog handoff are authority. No product question remains open.
- Retained provider readback maps exactly one execution Task, IP-426, to Story IP-425 and V4.1, with no retained native blocker. Connected Linear cannot refresh
IP-*; current provider readback is required before tracker mutation or closeout. - The immutable spec blob is verified at commit
3751d60e47d03e136147b6c1aee62b339319f429, blobc633ac62917d7a781219b0392fd5646eb1b69f73. - Do not install Lefthook in Harness. Runtime proof uses an isolated Commit Gate Consumer because Harness already owns
.githooksthroughcore.hooksPath. - Never use Lefthook force/reset or remove-config flags automatically. Never infer contracts from arbitrary runtime package scripts.
- Preserve settings keys, Lefthook commands/config, hook managers, and unrelated receipt entries.
Dependency and branch readiness
Ready: npm lefthook@2.1.10, integrity sha512-K7mM4WoqMwqfXYK11EHy+lSH1uW8XHni3Yn/bSqyerPkUPygGdf3xn18JoV5HyA06xuQL3ofGAOjG01QX9oJ4w==, gitHead 8d9cfec5f52367af6374a5430b4e9568844856e5, tarball https://registry.npmjs.org/lefthook/-/lefthook-2.1.10.tgz.
- Implement on
feat/pre-commit-hooksfrom the accepted immutable start. - Story: https://linear.app/devpunks/issue/IP-425/lefthook-commit-gate-for-catalogue-consumers
- Task: https://linear.app/devpunks/issue/IP-426/implement-lefthook-commit-gate-for-catalogue-consumers
- IP-426 is the one execution identity. Planning creates no private Tasks.
- Create/update one PR during implementation. Do not merge or publish before closeout gates.
Research and findings
- Consolidated report: Lefthook Commit Gate Delivery Planning Research.
- Project Settings has no policy field; raw-key preservation exists.
- Context plans have no Commit Gate or Quality Command Contract family; detection lacks package-manager/lockfile/quality evidence.
- Reconciliation already supports narrow ownership-aware dependency/file/structured-entry changes.
- Managed scaffold and projection receipts cannot prove a live Git hook.
- The current staged selector is owner-aware and empty-safe but sequential, fail-fast, and Harness-specific.
- Repository check is read-only but has no Commit Gate health result.
Target ownership topology
packages/scaffold/src
└── schemas; entrypoint: package exports; paths: catalog.ts, context-plan.ts, index.ts
forbidden: filesystem, Git, process, policy, health, installation
features/project-settings
└── policy; entrypoint: index.ts; paths: feature directory and colocated tests
forbidden: desired state, Git observation, health classification
features/repository-analysis + features/context-planning
└── eligibility/compilation; entrypoints: each index.ts; paths: both feature directories
forbidden: runtime package-script discovery or scaffold mutation
features/commit-gate
└── desired/conflict/handoff/receipt/health; entrypoint: index.ts exporting CommitGate/results
paths: feature directory and colocated tests
forbidden: rendering, command parsing, raw filesystem/Git/process implementation
platform + integrations
└── CommitGateCapabilities adapters; entrypoint: platform/commit-gate-capabilities.ts
paths: named adapter modules/tests; forbidden: policy, desired state, presentation
scaffold + update + repository-check + presentation
└── consume CommitGatePlan/CommitGateHealth; entrypoints: existing public surfaces
paths: apps/cli/src/{cli,scaffold,update}, features/repository-check, presentation, ui
forbidden: duplicate eligibility/conflict/receipt/health policy
data/scripts
└── runner; entrypoint: commit-gate-runner.mjs plus declaration
paths: runner/declaration/tests; forbidden: settings, migration, installation, receiptsDeclared dependency graph
packages/scaffold contracts -- imported by --> repository-analysis/context-planning
packages/scaffold contracts -- imported by --> features/commit-gate
project-settings policy -------------------- data --> features/commit-gate
compiled contribution ---------------------- data --> features/commit-gate
scaffold-state evidence -------------------- data --> features/commit-gate
platform/integration observations ---------- data --> features/commit-gate
CommitGatePlan ----------------------------- data --> scaffold/update/handoff
CommitGateHealth --------------------------- data --> repository-check --> JSON/terminal
runner declaration ------------------------- data --> packaged runner --> Lefthook lintAllowed edges use public feature/package entrypoints. Forbidden edges: packages/scaffold importing CLI code; catalogue data, presentation, adapters, or repository check owning policy; repository check mutating state; commit-gate importing renderers/handlers; lifecycle receipts deriving desired state; consumer assets importing CLI source internals.
Responsibility acceptance criteria
| Criterion | Owner | Observable assertion | Evidence | Due |
|---|---|---|---|---|
| CG-ARCH-01 | packages/scaffold | First-class contribution and complete contract decode/encode; lifecycle-hook remains distinct. | bun run --cwd packages/scaffold test -- src/context-plan.test.ts and package typecheck. | A1 |
| CG-ARCH-02 | analysis/context planning | Applicable consumer compiles lint + read-only format; incomplete applicable input fails typed. | bun run --cwd apps/cli test -- src/features/context-planning/compiler.test.ts. | A1 |
| CG-ARCH-03 | features/commit-gate | One public seam owns desired/observed/conflict/handoff/receipt/health decisions. | bun run --cwd apps/cli test -- src/features/commit-gate; inspect caller imports through index.ts. | A1 |
| CG-ARCH-04 | scaffold/update adapters | Opt-out and migration conflict yield no dependency/config/install action; shared config is preserved. | bun run --cwd apps/cli test -- src/cli/behavioral-portfolio.test.ts src/update/run.test-cases.test.ts src/scaffold/run.test.ts; retain before/after hashes in IMPLEMENTATION-NOTES.md#coexistence-evidence. | A1 |
| CG-ARCH-05 | packaged runner | Owners run once; lint/format concurrently; all failures return; empty scope spawns nothing. | bun run --cwd apps/cli test -- src/data/scripts/commit-gate-runner.test.ts and isolated git commit transcript. | A1 |
| CG-ARCH-06 | handoff/proof | Handoff unresolved until exact verification; separate receipt stales on relevant changes. | bun run --cwd apps/cli test -- src/features/commit-gate -t "handoff receipt"; retain exact dependency/config/hook inspection in IMPLEMENTATION-NOTES.md#commit-gate-lifecycle-readback. | A1 |
| CG-ARCH-07 | check/presentation | Exact disabled, healthy, unresolved, dependency/config/hook/manager states remain read-only. | bun run --cwd apps/cli test -- src/cli/check-command.test.ts src/cli/public-output-contract.test.ts; retain before/after repository SHAs in IMPLEMENTATION-NOTES.md#read-only-health-evidence. | A1 |
Architecture and worker waves
| Architecture wave | Delta | Entry | Criteria | Temporary seams | Checkpoint |
|---|---|---|---|---|---|
| A1 | Establish shared schema and CLI feature, connect every caller, close real consumer behavior. | Agent-ready spec, retained IP-426 projection, clean scaffold check. | CG-ARCH-01–07 | None | Compare owners/imports/seams with plan; run focused/outward validation and real commit proof; ledger empty. |
W1 / A1
└── T1 = IP-426
vertical RED -> GREEN cycles across AC-001–AC-012
-> convergence checkpoint
-> retained review handoffOne Task wave is required because the provider backlog contains one Task and every slice converges on the same public seam.
Public seam contract
| Seam | Owner | Consumers |
|---|---|---|
CommitGateContribution, QualityCommandContract package schemas | packages/scaffold | CLI catalogue/context compiler and contract tests. |
ProjectSettingsValue.commitGate, CommitGatePolicyChange | features/project-settings/index.ts | init, ensure, scaffold/update assessment, check. |
CommitGate returning CommitGatePlan and CommitGateHealth | features/commit-gate/index.ts | scaffold, update, check, platform composition. |
CommitGateCapabilities port | feature contract; platform/integration implementations | Commit Gate action/process composition. |
commit-gate-runner.mjs --contracts <path> | apps/cli/src/data/scripts | Generated Lefthook lint.run only. |
PostCommandHandoff.commitGate: CommitGateHandoff | scaffold/content | $hi-cli follow-through and diagnostics. |
CommitGateLifecycleReceipt, verifyLifecycleReceipt | features/commit-gate/index.ts | Agent verification writer and check validator. |
RepositoryCheckResult.commitGate: CommitGateHealth | repository-check/presentation | JSON and terminal renderers. |
Migration ledger
Empty. Any compatibility alias, duplicate policy calculation, receipt mirroring, or second runner introduced during implementation must amend this ledger with owner/removal proof. A1 closes only with an empty ledger.
T1: Implement and prove the Commit Gate
- depends_on: []
- location:
packages/scaffold/src;apps/cli/src/features;apps/cli/src/platform;apps/cli/src/integrations;apps/cli/src/scaffold;apps/cli/src/update;apps/cli/src/content;apps/cli/src/presentation;apps/cli/src/ui;apps/cli/src/data;apps/cli/src/cli;docs;apps/wiki/content/docs/project/specs/cli/lefthook-commit-gate. - owned_paths:
packages/scaffold/src;apps/cli/src/features/project-settings;apps/cli/src/features/repository-analysis;apps/cli/src/features/context-planning;apps/cli/src/features/commit-gate;apps/cli/src/features/scaffold-state;apps/cli/src/features/repository-check;apps/cli/src/platform;apps/cli/src/integrations;apps/cli/src/cli;apps/cli/src/scaffold;apps/cli/src/update;apps/cli/src/content;apps/cli/src/presentation;apps/cli/src/ui;apps/cli/src/data/catalog;apps/cli/src/data/scripts;apps/cli/package.json;bun.lock;docs/README.md;docs/runbooks/hi-cli-scaffolding.md;apps/wiki/content/docs/project/specs/cli/lefthook-commit-gate;apps/wiki/log.md. - wave_boundary: W1; one worker because IP-426 is the only provider Task and all slices share the Commit Gate seam.
- description: Add typed policy, eligibility and contract compilation, first-class contribution, deep feature boundary, safe desired-state integration, staged runner, exact merge/conflict behavior, structured unresolved handoff, separate receipt, ownership-aware disable/migration evidence, and read-only exact health. Preserve consumer state. Prove real commit success/failure in an isolated fixture. Update implementation notes and operator docs. Do not install Lefthook in Harness.
- validation: Map AC-001–12 to public tests; cover manager/config collisions, partial materialization, failed follow-through, stale receipts, no-write check; prove isolated Git behavior; run package/CLI typecheck, lint, format, build, affected/full tests, consumer validation, classifier, and
hi check --json; prove CG-ARCH-01–07 and empty ledger. - status: In progress
- log: Initial RED retained; shared contract, compiler, Commit Gate feature seam, observation adapter, runner, docs, and implementation notes added. Full runtime lifecycle and parent closeout remain pending.
- files edited/created:
packages/scaffold/src/context-plan.ts;packages/scaffold/src/context-plan.test.ts;apps/cli/src/features/context-planning/{compiler.ts,compiler.test.ts,compilation-error.ts,errors.ts,index.ts};apps/cli/src/features/repository-analysis/{model.ts,index.ts};apps/cli/src/integrations/repository-detector.ts;apps/cli/src/features/project-settings/{model.ts,service.ts,index.ts};apps/cli/src/features/commit-gate/{index.ts,index.test.ts};apps/cli/src/platform/commit-gate-capabilities.{ts,test.ts};apps/cli/src/data/scripts/commit-gate-runner.{mjs,d.ts,test.ts};apps/cli/src/scaffold/output.ts;docs/README.md;docs/runbooks/hi-cli-scaffolding.md;apps/wiki/log.md;IMPLEMENTATION-NOTES.md - task_identity_mode: provider-task
- backlog_item_id: IP-426
- backlog_item_url: https://linear.app/devpunks/issue/IP-426/implement-lefthook-commit-gate-for-catalogue-consumers
- relation_mode: native
- backlog_sync_skip_reason:
- assigned_skills: [
create-plan,grilling,parallel-research,show-me,swarm-planner,wait-what,backend-domain-structure,backend-recoverable-actions,codebase-design,effect,effect-backend-structure,effect-recoverable-actions,effect-service-design,improve-codebase-architecture,quality-types,tdd,turborepo,writing-for-agents,implement-spec] - implementation_skill_guidance:
- skill:
backend-domain-structureapplicable_behavior: Keep policy in one feature, adapters outside it, and imports one-way through public entrypoints. - skill:
backend-recoverable-actionsapplicable_behavior: Classify preflight, local desired-state writes, external package/Git work, follow-through, and receipt commit; make partial failure explicit and repairable. - skill:
codebase-designapplicable_behavior: Maintain a small deep interface and test through the same seam callers use. - skill:
effectapplicable_behavior: Use project-compatible Effect v4 Schema/errors/workflows; decode untrusted settings/config/receipt input; keep expected failures typed. - skill:
effect-backend-structureapplicable_behavior: Keep actions as orchestration owners, adapters at boundaries, and feature-local Effect tests. - skill:
effect-recoverable-actionsapplicable_behavior: Use typed failure channels and explicit collect-all read-only concurrency; do not pretend external Git/package effects are locally atomic. - skill:
effect-service-designapplicable_behavior: Use an authority service only where lifecycle/filesystem variability earns it; keep deterministic policy/projections pure. - skill:
improve-codebase-architectureapplicable_behavior: Compare cumulative implementation with the selected deep-module topology; surface drift before adding another seam, owner, or compatibility layer. - skill:
quality-typesapplicable_behavior: Use discriminated unions for eligibility, conflicts, handoff, receipt, and health; derive related types. - skill:
tddapplicable_behavior: Capture every public-result RED before production code; advance one tracer at a time; retain evidence. - skill:
turborepoapplicable_behavior: Preserve package boundaries and affected/focused task dependency order. - skill:
writing-for-agentsapplicable_behavior: Make handoff/guidance authority, exact verification, unresolved state, and stop conditions explicit. - skill:
implement-specapplicable_behavior: Execute with one scoped worker; parent coordinates, reviews, validates, and routes durable evidence.
- skill:
- tdd_status: required
- tdd_target: First create only the public-result test.
compileContextPlanmust return typedIncompleteQualityCommandContractfor an applicable consumer missing format-check and expose no Commit Gate desired action. - red_command:
bun run --cwd apps/cli test -- src/features/context-planning/compiler.test.ts -t "rejects an incomplete Commit Gate quality contract" - expected_red_failure: The assertion expects
_tag: "IncompleteQualityCommandContract"and zero Commit Gate contributions/actions, but the current compiler returns a successful plan without that typed failure. - green_command:
bun run --cwd packages/scaffold test && bun run --cwd packages/scaffold check-types && bun run --cwd apps/cli test && bun run --cwd apps/cli check-types && bun run --cwd apps/cli build - reason_not_testable:
- red_evidence:
bun run --cwd apps/cli test -- src/features/context-planning/compiler.test.ts -t "rejects an incomplete Commit Gate quality contract"failed before production implementation with the compiler returning{ version: "1", scopes: [{ id: "fixture-consumer", packs: [], contributions: [] }] }; an earlier run recordedvitest: command not found, resolved bybun install --frozen-lockfile. - green_evidence: Focused compiler, feature, runner, and platform tests pass (8 tests);
bun run --cwd apps/cli check-types,bun run --cwd packages/scaffold test, andbun run --cwd packages/scaffold check-typespass. Fullgreen_command, consumer runtime, check presentation, and build remain pending. - codebase_design_notes:
features/commit-gateis the deep module. Eligibility, desired state, merge/conflict policy, receipt binding, and health remain pure where possible. Filesystem/Git/process variability uses narrow ports. Scaffold/update/check orchestrate or present the result, not duplicate policy. - review_mode: cli
- runtime_validation: required
- runtime_target: Contained temporary Git repository representing an eligible JavaScript Commit Gate Consumer, with isolated Git config and project-local Lefthook 2.1.10.
- runtime_evidence: Commands/output proving default and opt-out policy; safe materialization or deferral; unresolved handoff; verified dependency/config/hook; receipt binding; normal commit success; lint, format, and dual failures; multi-owner once; empty no-spawn; every health state; deliberate bypass possible without historical claim.
- runtime_cleanup: Record fixture path/identity; remove only that fixture and local Git config. Do not alter global Git config, Harness hooks, or external repositories.
- architecture_wave: A1
- behavior_owner:
apps/cli/src/features/commit-gate - integration_surface: Context schemas, settings, repository analysis, scaffold/update, adapters, handoff, check, packaged runner.
- public_seam: Plan-level seams centered on one Commit Gate assessment/planning interface.
- topology_delta: Add shared contract and one CLI feature authority; connect callers without moving behavior into
packages/scaffold. - forbidden_ownership: AI lifecycle hooks, managed receipts, renderer, catalogue data, adapters, or check command may not own policy or operational truth.
- temporary_seams: []
- responsibility_acceptance_criteria: [CG-ARCH-01, CG-ARCH-02, CG-ARCH-03, CG-ARCH-04, CG-ARCH-05, CG-ARCH-06, CG-ARCH-07]
TDD and validation strategy
Execute one tracer at a time: incomplete contract; policy; eligibility; contribution; safe config; opt-out/migration; runner routing/concurrency/aggregation; handoff/receipt; health; JSON/terminal facts; isolated Git behavior. Never batch all tests before implementation.
Validation expands outward: focused tests; full scaffold/CLI tests/typechecks/build; targeted Oxlint/Oxfmt and git diff --check; bun run check:repo, bun run test:ci, bun run validate:consumer-repositories; isolated Git scenarios; hi check --json; bun run release:classify -- --base b0c53c2552e3c0a1ab34519e7486a9bfe11d1fbb --head HEAD.
Risks and mitigations
- Postinstall can force-install hooks: preflight before dependency desired state; no force/reset; failure remains unresolved.
- Config variants can lose content: support only structurally safe recognized config; preserve untouched nodes; otherwise return evidence.
- One Task is broad: one worker, vertical tracers, durable evidence, final convergence checkpoint.
- Receipt misuse can overstate health: separate schemas and observed-fact binding.
- Check can mutate through tooling: inspection-only adapters and before/after fingerprints.
- Harness hooks can be damaged by proof: use contained fixture only.
- Linear access unavailable: preserve identity; do not mutate or claim current tracker state until refreshed.
Review, docs, and unresolved state
- Retain
IMPLEMENTATION-NOTES.mdwith per-tracer evidence, deviations, commands, runtime provenance, and architecture checkpoint. - Full delivery invokes review after implementation; repairs preserve IP-426 and review lineage.
- Update
docs/README.mdanddocs/runbooks/hi-cli-scaffolding.mdin T1. - After clean review,
docs-ingest-phaseingests or records a no-op. Closeout separates branch/PR, local validation, CI/provider, tracker, and release evidence. - Only unresolved question: current Devpunks Linear readback is externally blocked. This does not block local implementation from the retained same-day exact handoff, but blocks tracker mutation/current closeout claims.