Harness Intelligence Wiki
SpecsCLILefthook Commit Gate for Catalogue Consumers

Implementation Notes: Lefthook Commit Gate Catalogue Consumers

Implementation Notes: Lefthook Commit Gate Catalogue Consumers

IP-426 remains a provider-task implementation on feat/pre-commit-hooks. Tracker completion remains gated on exact-head hosted CI success and fresh Devpunks Linear readback; no Done claim is made before both are observed.

TDD Evidence

TracerREDGREENStatus
Incomplete Quality Command Contractbun run --cwd apps/cli test -- src/features/context-planning/compiler.test.ts -t "rejects an incomplete Commit Gate quality contract" first failed because the compiler returned a normal plan ({version:"1", scopes:[...]}), after the worktree dependency recovery (bun install --frozen-lockfile; initial environment failure was vitest: command not found).Same command passes after the typed IncompleteQualityCommandContract guard and no commit-gate action on failure.Met

Review Repair Epoch 1

  • review_count=1, repair_count=1; scope limited to IP426-CG-001 and IP426-CG-002.
  • IP426-CG-001 RED: bun run --cwd apps/cli test -- src/scaffold/output.test.ts failed because a valid two-space pre-commit.commands fixture placed the new lint entry after the sibling skip key (expected 143 less 80). GREEN: the same test passes after deriving YAML indentation and structural block boundaries; consumer entries remain unchanged and the source file is not written by the merge helper.
  • IP426-CG-002 RED: bun run --cwd apps/cli test -- src/platform/commit-gate-capabilities.test.ts -t "ranged|installed Lefthook" failed for ranged ^2.1.10 and installed 2.1.11 drift. GREEN: the platform observer preserves non-exact declarations, reports installed/lockfile version drift, and rejects lockfile integrity drift; all 7 platform tests pass.

CI Repair and Supplemental Review

  • The affected CI lint/check graph exposed type-aware lint debt in packages transitively affected by packages/scaffold; the gate was preserved and the affected packages were repaired rather than excluded.
  • Exact affected validation passed for 23/23 tasks with bunx turbo run lint check '//#check:repo' --filter="...[<merge-base>...<head>]" --filter='!@punks/wiki' --output-logs=errors-only.
  • Supplemental repair-range review found command-registry type erasure and an over-broad HTTP-adapter suppression. Commits d95cc138, 140c29bc, and 4cd2ff14 restored generic command typing and narrowed adapter boundaries; focused final review was clean.
  • Hosted run 33638523583 then exposed a Node-specific test-helper regression: synchronous child-process failures report numeric status, while the lint repair read only code. Commit b5bcc57d normalizes both shapes; the public runner behavior and exit-1 assertion remain unchanged, and the focused test passes 2/2.
  • Full API tests passed (312 tests) and full backoffice lint, typecheck, and tests passed. Focused CLI release-publication and release-recovery intent tests passed.
  • Full CLI Vitest made normal progress through the subprocess-heavy update and release-recovery matrix but exceeded the local command runner's ten-minute ceiling twice (exit 130); no assertion failure was reported. Hosted exact-head CI remains the authoritative full-suite result.
  • Release classification is none: neither baseline nor npm publication is selected.

Supplied review remediation

The implementation now covers the supplied review findings (excluding .agents/ targets):

  • Scaffold planning serializes root contracts as . and executes each nested contract from its owning workspace with owner-relative staged paths.
  • The packaged runner filters staged deletions and replaces broad . command scopes with the selected staged files. It keeps empty-scope success and reports independent lint and format failures together.
  • Lefthook merging is restricted to pre-commit, recognizes the existing HI-owned entry on reruns, preserves consumer entries, and emits the correct sole-owner uninstall or shared-owner removal handoff when disabled.
  • hi init and hi ensure persist the enabled/disabled policy. The hi commit-gate verify operation performs live dependency, supported lockfile, configuration, and Git-resolved hook checks before writing the lifecycle receipt.
  • Desired-state verification includes the managed configuration, contracts, runner, and contract paths. Hook inspection requires the exact HI pre-commit.commands.lint invocation, and applicable all-null state is unresolved rather than healthy.

Acceptance Coverage

CriterionStatusEvidence
AC-001Met locallyShared QualityCommandContract, compiler completeness guard, pinned lefthook@2.1.10 desired dependency, generated config, and isolated installed consumer proof.
AC-002Met locallyPackaged runner selects staged paths, starts no tools for empty scope, runs lint/format concurrently with Promise.all, and aggregates failures; focused tests pass.
AC-003Met locallyGenerated config owns only pre-commit.commands.lint; isolated hook executed the packaged runner against staged owner scope.
AC-004Met locallycommitGate schema accepts enabled/disabled, absent reads as enabled, and policy change is settings-only.
AC-005Met locallyDisabled policy omits Commit Gate desired dependency/config output.
AC-006Met locallyPublic plan distinguishes sole Lefthook uninstall from shared-manager command preservation.
AC-007Met locallyOther-manager, unsafe/tabbed-config, and lint-collision fixtures defer materialization and return migration/conflict evidence.
AC-008Met locallyStructured merge preserved existing existing and post-merge.keep entries; only HI lint was inserted. Before SHA-256 49e71b723de733b3ba248ab5d9675ab93281e03a07dcb7d37b72cb1de1defeb8; after SHA-256 239a15f9baa52570e19efb5e4d86b6762322dae2a5a900f2e5de54ca4252d85e.
AC-009Met locallyIsolated consumer verified exact dependency, config, and installed hook; lifecycle receipt was written only after the verification tuple matched.
AC-010Met locallyManaged desired-state evidence remains separate from .devpunks/commit-gate-lifecycle-receipt.json; changing observed dependency/config/hook facts invalidated health.
AC-011Met locallyIsolated read-only observations classified all nine required states without repair.
AC-012Met locallyRunner does not enforce bypasses; no historical bypass claim; docs retain CI as merge authority.

Sanity Checks

CommandResult
bun run --cwd packages/scaffold testPassed: 52 tests across 4 files, including 15 context-plan tests.
bun run --cwd packages/scaffold check-typesPassed.
bun run --cwd apps/cli check-typesPassed.
Focused CLI Commit Gate testsPassed: 18 tests across compiler, feature, runner, repository-check, scaffold config merge, and platform observation.
git diff --checkPassed after the final helper/suppression edit.
bun run --cwd apps/cli buildPassed; bundled CLI and baseline artifacts rebuilt.
bun run check:repoPassed.
bun run --cwd apps/cli testReached the local command runner's ten-minute ceiling twice while making normal subprocess progress; exited 130 with no assertion failure, so no full-suite pass is claimed locally.
bun run --cwd apps/cli checkPassed after the affected package lint/type repair.
bun run validate:consumer-repositoriesPassed after preserving relative symlinks with verbatimSymlinks: true in remote-authority, managed-update, and projection fixtures; managed-update and projection scenarios complete successfully.

Runtime Validation Evidence

TaskScenarioRuntime target / provenanceExpected and observed resultCleanupStatus
IP-426Isolated empty and dual-failure runner fixturesTemporary commit-gate-empty-* and commit-gate-failures-* directories, each unique Vitest runPublic packaged runner returned success without starting tools for empty staged scope; dual-failure run returned exit 1 and reported both lint and format failures.Vitest-owned temporary directories; no Harness hooks/global Git config touched.Met for runner unit boundary
IP-426Full project-local Lefthook 2.1.10 install, lifecycle receipt, normal commit, bypass/tmp/hi-commit-gate-runtime.7SzjK6, isolated Git repository/configlefthook@2.1.10 installed; normal commits d2402a8, 9e07b62, 07e34a2 passed; empty scope produced no gate entries; multi-owner log contained exactly format/lint once for packages/b and packages/a; deliberate bypass f73f455 exited 0 and left the log unchanged. Config SHA-256 152cb6fe4b06c242a4cdf0ddffeb369d6ebbd4bbe7b33fa1e1bdca2e6bb7e240; hook SHA-256 e4c7921d22e2dde302657ba3f38842adee75aff67635bfd042e609593a6f79e4; receipt SHA-256 e3ecd949419bfa69f37a8bcc91f785114c76aef6f5ccb3c8e61e50ca58afa2af; final tree 6c77aba4f01ccdf35234ab944366cc0c1d44c5f5.Fixture is disposable and must be removed after evidence capture; no Harness/global Git state touched.Met locally

Architecture Conformance Evidence

CriterionEvidence
CG-ARCH-01packages/scaffold/src/context-plan.ts adds behavior-free commit-gate and QualityCommandContract schemas; package tests and typecheck pass.
CG-ARCH-02context-planning/compiler.ts is the sole applicability/completeness compiler seam; incomplete contract test passes.
CG-ARCH-03features/commit-gate/index.ts owns planning, conflict, handoff, receipt, and health decisions behind one public module.
CG-ARCH-04Scaffold output omits disabled state, avoids overwriting existing lefthook.yml, emits one HI-owned lint config, and recognizes that entry on idempotent reruns.
CG-ARCH-05data/scripts/commit-gate-runner.mjs is the sole packaged runner; focused tests prove empty scope and aggregate failures.
CG-ARCH-06CommitGateHandoff, desired-state receipt, lifecycle receipt, and verifyLifecycleReceipt are separate contracts.
CG-ARCH-07platform/commit-gate-capabilities.ts adapts live dependency/config/hook observation and explicit receipt persistence; domain verification still decides whether a receipt may be written.

Public seam additions are @punks/scaffold/context-plan contracts, features/commit-gate/index.ts, and data/scripts/commit-gate-runner.mjs. No temporary seam or compatibility alias was introduced; migration ledger remains empty.

Health and Coexistence Readback

The isolated scenario root /tmp/hi-commit-gate-scenarios.rpDvwn was read with observeCommitGate and assessCommitGateHealth using the same desired-state hash (runtime-desired). Results were: baseline=healthy, disabled=disabled, unresolved-handoff=unresolved-handoff, dependency-drift=dependency-drift, missing-lint=missing-lint, changed-lint=changed-lint, missing-hook=missing-hook, wrong-hook=wrong-hook, and conflicting-manager=conflicting-manager. The observation pass performed no writes.

The coexistence fixture /tmp/hi-coexistence-merge.S7TC9P/lefthook.yml preserved consumer-owned existing and post-merge.keep entries. Its exact before/after content hashes are recorded under AC-008 above. Collision and unsafe/tabbed configuration fixtures returned deferral evidence without overwriting the source files.

Skill Application Evidence

SkillStatusEvidence
backend-domain-structureappliedCommit Gate policy remains in features/commit-gate; platform observation and runner mechanics remain adapters.
backend-recoverable-actionsappliedPreflight completeness, desired-state planning, external lifecycle handoff, and receipt verification are explicit stages.
codebase-designappliedOne deep features/commit-gate public seam with narrow platform capability observation.
effectappliedExisting Effect Schema/tagged-error conventions used for context compilation failures and contracts.
effect-backend-structureappliedFeature authority and platform adapter direction preserved; feature tests are colocated.
effect-recoverable-actionsappliedRead-only validation and external installation are separated; partial lifecycle state remains unresolved.
effect-service-designnot_applicableNo new long-lived Effect service or Layer was needed; pure feature and synchronous adapter seams were sufficient.
improve-codebase-architectureappliedNew behavior is centralized under the declared Commit Gate authority rather than duplicated in callers.
quality-typesappliedDiscriminated policy/action/health unions and typed contribution contracts avoid flag bags.
tddappliedRequired public compiler RED was captured before production implementation, then focused GREEN was retained.
turborepoappliedPackage-scoped validation commands were used; no task graph or cache configuration changed.
writing-for-agentsappliedOperator docs and structured handoff terminology retain canonical Commit Gate terms.
implement-specappliedPlan-derived T1 artifacts, implementation notes, acceptance table, runtime status, and architecture evidence were maintained.

Manual Review Checklist

AreaCheckHow performExpected result
Consumer runtimeNormal commit gateIn isolated eligible consumer, stage a file and run git commit; inspect runner output and commit result.lint and read-only format-check each run once before commit.
Failure behaviorAggregated failuresMake lint and format-check commands fail independently.Both failures are reported and commit exits non-zero.
CoexistenceExisting manager/configSeed Husky or consumer Lefthook entries, run scaffold planning, compare hashes.No overwrite; migration handoff remains unresolved.
PolicyOpt-out and re-enablePersist commitGate: disabled, run hi ensure/scaffold, then re-enable through settings and rerun scaffold.Policy-only command does not materialize; later enabled scaffold can materialize.
HealthRead-only checkRun hi check --json before and after changing dependency/config/hook facts.Current health changes; files and Git config are unchanged.
TrackerProvider lifecycleRefresh exact Devpunks Linear readback before mutation or closeout.IP-426 Task and IP-425 Story relation are read back exactly.

Blocked / Risky Items

  • Full CLI Vitest remains locally unverified because the command runner enforces a ten-minute execution ceiling; hosted exact-head CI must supply the full-suite result.
  • Consumer validator symlink fixture issue is resolved by preserving relative symlink targets with verbatimSymlinks: true; the validator exits 0 and the affected lint/check graph is green.
  • Devpunks provider completion readback remains pending until hosted exact-head CI is green.
  • The implementation does not install Lefthook in Harness and has not changed Harness global or local hook configuration.

On this page