Manifest-Backed Scaffold Format-Hook Exclusion
Spec: Manifest-Backed Scaffold Format-Hook Exclusion
Context
Harness distributes an automatic format hook that formats eligible files after agent edits. The hook currently excludes only a hard-coded subset of scaffold-owned paths. Other paths recorded in .devpunks/scaffold-manifest.json, including lint selection and workspace Oxlint configuration, can be formatted after Harness writes their authoritative bytes. A later hi check then reports those formatter-only byte changes as managed-file drift.
Repository operators and agents need the automatic hook to preserve every scaffold-managed path without weakening strict scaffold ownership or adding repository formatter execution to CLI commands.
Non-Goals
- Run a repository formatter from
hi check,hi update, scaffold desired-state compilation, or another CLI lifecycle. - Infer ownership from path, extension, generated comments, Git tracking, or formatter behavior.
- Add project-producer declarations, repository mirrors, formatter-specific ignore configuration, or a force-overwrite path.
- Govern manually invoked repository-wide format commands.
- Reconcile or mutate Collective Intelligence, including its existing formatter-changed files or stale
opensrc/guides. - Publish another npm CLI version unless implementation proves that executable CLI code or a schema must change.
- Write or synchronize provider backlog items.
User Stories
US-001: Preserve scaffold-managed output
As a repository operator, I want automatic formatting to skip every path recorded in the scaffold manifest so that Harness-managed bytes remain authoritative.
US-002: Preserve normal project formatting
As a repository contributor, I want eligible unmanaged files to retain the hook's existing format and lint behavior.
US-003: Fail safely without ownership evidence
As a repository operator, I want the hook to make no formatting mutation when it cannot read a valid scaffold manifest so that unavailable ownership evidence cannot create false drift.
US-004: Distribute the corrected hook
As a Harness operator, I want the corrected hook delivered through the stable baseline so compatible CLI consumers receive it without an unnecessary npm release.
Acceptance Criteria
- AC-001: Given a valid
.devpunks/scaffold-manifest.json, an eligible file whose normalized repository-relative path appears inmanagedFilesis not formatted, linted, or otherwise mutated by the automatic format hook.- Covers: US-001
- AC-002: The managed-path rule applies to every manifest entry and does not use a special list for lint, Oxlint, source-guide, skill, hook, or agent paths.
- Covers: US-001
- AC-003: Given a valid manifest, an eligible unmanaged file follows the existing formatter and linter selection, command resolution, result reporting, and failure behavior.
- Covers: US-002
- AC-004: When the scaffold manifest is missing, unreadable, malformed, or has an invalid managed-file collection, the hook invokes no formatter or linter and mutates no candidate file for that invocation.
- Covers: US-003
- AC-005: A missing or invalid manifest does not fall back to the existing partial hard-coded scaffold-owned path rule.
- Covers: US-003
- AC-006: Focused tests prove managed and unmanaged behavior through the hook's supported invocation surfaces and prove that no subprocess mutation occurs on the fail-closed path.
- Covers: US-001, US-002, US-003
- AC-007: The built baseline archive contains the tested corrected hook bytes, uses the existing compatible baseline contract, and has release notes describing the behavior boundary.
- Covers: US-004
- AC-008: The change does not alter scaffold desired bytes, managed-file ownership classification, source-guide replacement policy, or consumer repository state.
- Covers: US-001, US-004
Constraints
- Raw scaffold bytes remain authoritative.
.devpunks/scaffold-manifest.jsonmanagedFilesis the complete managed-path source for the automatic format-hook boundary. The manifest file itself is the sole bootstrap exclusion because it cannot recursively list and hash itself.- Managed-path comparison uses normalized repository-relative paths and cannot escape the repository root.
- The read-only and update CLI lifecycles do not invoke repository format commands.
- Existing strict behavior remains for scaffold-managed
opensrc/guides. Their consumer reconciliation is separate. - A manual repository-wide format command can still create legitimate managed-file drift.
- Baseline publication uses the repository-supported clean-worktree release command and existing compatibility policy.
Dependency Readiness
No Stack Required.
The managed-file receipt and strict ownership behavior are already present on base commit 441d87f5, including the project-generated ownership implementation landed by c89c73cf.
Branch/Base Intent
- Base:
mainat441d87f5(v3.1.4). - Delivery branch:
team/stefan/investigate-generated-file-triggers. - Constraint: keep Collective Intelligence reconciliation and unrelated scaffold divergence out of this branch's implementation scope.
Accepted Technical Decisions
- Extend the distributed automatic format hook's ownership boundary from a partial hard-coded rule to the complete managed-file receipt.
- Treat manifest loading and validation as a fail-closed prerequisite for all formatting and linting in one hook invocation.
- Preserve only
.devpunks/scaffold-manifest.jsonoutside receipt membership as bootstrap evidence; remove the broader hard-coded path fallback. - Preserve the existing unmanaged-file formatter and linter behavior.
- Carry the hook change through the baseline asset surface. Add executable or schema work only if implementation evidence makes it necessary.
Accepted Testing Decisions
- Reproduce the defect with a formattable path present in
managedFilesand prove its bytes and subprocess boundary remain unchanged. - Prove an equivalent unmanaged path still formats and retains existing lint behavior.
- Cover missing, unreadable, malformed, and structurally invalid manifests.
- Exercise the hook's supported invocation modes that produce edited-file candidates.
- Build the baseline and compare the archived hook bytes with the tested source.
- Run focused hook tests before broader CLI checks, documentation checks, and
git diff --check.
Verification Seams
- Public behavior seam: the distributed
format-edited-file.mjshook invoked through its supported agent/file modes. - Ownership seam: normalized candidate path membership in
.devpunks/scaffold-manifest.jsonmanagedFiles. - Mutation seam: candidate file bytes and formatter/linter subprocess observation before and after invocation.
- Distribution seam: hook bytes inside the built scaffold baseline manifest/archive.
Parked Decisions
- Collective Intelligence check and reconciliation. Owner: Stefan. Resume trigger: the new baseline is released and Stefan runs
hi checkin that repository. - Existing formatter-changed managed files and stale
opensrc/guides in consumer repositories. Owner: each repository operator. Resume trigger: explicit consumer reconciliation authorization. - Provider backlog projection. Owner: Stefan. Resume trigger: an explicit request to write or synchronize backlog items.
Decision Log
| Decision | Evidence | Rationale |
|---|---|---|
| Keep all affected files scaffold-managed | Closed grill Q1 and Q10 | No independent repository producer exists; raw scaffold bytes stay authoritative. |
| Skip every manifest-managed path | Closed grill Q10 | The receipt is the existing complete ownership source and avoids path-specific exceptions. |
| Make no mutation without a valid manifest | Closed grill Q11 | A missed format is reversible; formatting an unknown managed file can recreate drift. |
| Leave manual format commands outside Harness | Closed grill Q13 | Formatter-specific repository control would expand scope and complexity. |
| Release through a stable baseline | Closed grill Q14 and Q16 | The hook is a baseline asset and CLI 3.1.4 already supports the compatible distribution contract. |
| Park consumer reconciliation | Closed grill Q15 and Q16 | Consumer mutation is separate from the preventive Harness hook fix. |