BOHICA-LABS / BOHICA-LABS/vsdd-factory

input-hash includes YAML frontmatter → populating an artifact's own hash spuriously drifts all its downstream consumers

Open
#314 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
2
Forks
1
Avg merge
6h 43m
Merged PRs (30d)
29

Description

## Summary

`compute-input-hash` hashes the **entire input file including its YAML frontmatter**, not just the body. Because the pipeline populates `input-hash:` fields progressively, the act of populating/correcting an upstream artifact's *own* frontmatter (its `input-hash` or `inputs:` path) changes that file's bytes — which spuriously invalidates the stored `input-hash` of **every downstream artifact that lists it as an input**, even though the upstream's actual content never changed. Drift detection eats its own tail.

## Reproduction (observed in a real greenfield run)

1. `product-brief.md` is the input to 11 `domain-spec/*.md` files (capabilities, invariants, entities, events, edge-cases, risks, failure-modes, assumptions, event-flow, differentiators, …). Each stores `input-hash: "2a5676f"`.
2. At the Phase-1 spec-gate approval commit (`4235b76`, message: *"Phase 1 spec gate APPROVED — drift clean, L1 brief path fixed"*), the ONLY change to `product-brief.md` was **4 frontmatter lines**:
```diff
- - PRODUCT_BRIEF.md
-input-hash: ""
+ - ../../PRODUCT_BRIEF.md
+input-hash: "1b28b81"
```
The brief's **body was byte-identical.** (The change was fixing the brief's own `inputs:` path and populating the brief's own `input-hash`.)
3. Result: all 11 domain-spec files now report DRIFT — stored `2a5676f` ≠ computed `059ddf1` — purely because `product-brief.md`'s frontmatter bytes changed. No body content changed anywhere.
4. The same commit that introduced this drift was labeled **"drift clean"** — the gate did not catch its own downstream drift.

## Impact

- **Phantom drift indistinguishable from real drift.** A later drift scan shows N stale files mixing genuine content changes with frontmatter-feedback phantoms. The operator must do manual `git show` archaeology on each to tell which are real — defeating the purpose of automated drift detection. (In our run: a scan for an unrelated spec edit returned 13 stale files; only 6 were real content edits, 7 were this phantom drift.)
- **Self-inflicted by normal pipeline operation.** Progressive hash population means every upstream whose hash is filled in *after* its consumers were derived will spuriously drift those consumers. This is not an edge case; it's the default flow.
- **Gates can pass while mislabeling drift state.** A gate marked "drift clean" introduced 11 drifted artifacts in the same commit.

## Suspected root cause

The hash function reads the whole file rather than stripping the YAML frontmatter block (`--- ... ---`) before hashing. Downstream artifacts only semantically depend on the upstream's *content/body*, not on the upstream's bookkeeping frontmatter (`input-hash`, `version`, `timestamp`, `inputs`, `traces_to`).

## Suggested fix

1. **Strip frontmatter before hashing inputs.** When computing an artifact's `input-hash` from its inputs, hash only each input's body (content after the closing `---`), not its frontmatter. Bookkeeping-frontmatter changes on an upstream should not drift downstream consumers.
- Alternatively, hash a normalized subset of frontmatter that excludes self-referential bookkeeping fields (`input-hash`, `timestamp`, `version`) — but body-only is simpler and matches intent.
2. **Make gate "drift clean" assertions run the scan they claim.** The commit asserting "drift clean" should fail if any artifact (including downstream of files edited in that commit) is stale.
3. If frontmatter-in-hash is intentional for some reason, document it and provide a `--body-only` mode for input hashing so the progressive-population feedback loop can be avoided.

## Environment

- vsdd-factory plugin 1.0.0-rc.21 (claude-mp marketplace), greenfield mode
- Helper: `bin/compute-input-hash` (`--scan`, `--check`, `--update`)
- Phase: Phase-1 complete, pre-Phase-2

## Note

Discovered while triaging drift for a legitimate spec revision (a separate K5 hardware-spike change touched 6 files). The 6 real edits were obscured among 7 phantom-drift domain-spec files, which is what surfaced the bug. Related but distinct from #313 (CI artifacts never committed).

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.