Skip to content

Add the verbatim engine - content-hash fixed units against canonical (#374, PR-C) - #377

Merged
ptr727 merged 8 commits into
developfrom
verbatim-engine-374
Jul 21, 2026
Merged

Add the verbatim engine - content-hash fixed units against canonical (#374, PR-C)#377
ptr727 merged 8 commits into
developfrom
verbatim-engine-374

Conversation

@ptr727

@ptr727 ptr727 commented Jul 21, 2026

Copy link
Copy Markdown
Owner

PR-C of the fidelity series (#374). Adds the second engine: verbatim content-hashing of fixed units against the hub's canonical.

What it does

  • normalize (EOL-only) → content_hash (sha256) → classify_verbatim: None (matches current canonical), stale (matches a past hub revision → re-vendor), or modified (matches no revision → the repo changed fixed content). The discriminator is the content hash walked over the canonical's git history, never a version stamp (spoofable).
  • Runs at DRIFT — a byte diff is a hint to review, never proof of breakage; exit code unchanged.
  • Enabled on two real units, exercising both granularities:
    • Whole file.markdownlint-cli2.jsonc.
    • Job region — the canonical github-release job of build-release-task.yml, extracted by job key from both sides (reference → catalog snippet, contract.verbatimJobs).

Normalization is EOL-only (design note)

A verbatim unit carries no per-repo placeholder. The files that declare placeholders are fidelity intent (judged by hand, never hashed). Masking could not serve a hash anyway: a downstream copy holds the substituted value (ptr727), not the token (<owner>), so masking the token in the canonical alone would guarantee a mismatch. A unit that ever needed per-repo substitution would require template-matching, not this hash — none does today.

Verification

  • python3 spec/audit.py --selftest — interface (14) + verbatim: identical / CRLF / bare-CR match, body edit → violated, past-revision → stale, stale-modulo-EOL, no-revision → violated, forked region hashes differently.
  • python3 spec/validate.py green (21 cataloged).
  • Positive controls: .markdownlint-cli2.jsonc and the snippet github-release region self-hash clean; both deliberate forks → DRIFT.
  • Live audit.py PlexCleaner exit 0 — surfaced a real advisory finding (PlexCleaner forked the canonical github-release orchestration).

Held on develop; no develop → main promotion.

🤖 Generated with Claude Code

…374, PR-C)

Content-hash a carried unit against the hub's canonical to detect a change to
content meant to stay fixed, and classify a mismatch as stale (matches a past
hub revision, re-vendor) or modified (matches none, the repo changed fixed
content) via the canonical's git history. Integrity is by hash, never a version
stamp. Runs at DRIFT: a byte diff is a hint to review, not proof of breakage.

Normalization is line-endings only. A verbatim unit carries no per-repo
placeholder: the files that declare `placeholders` are fidelity intent, judged
by hand and never hashed, and masking could not serve a hash anyway - a copy
holds the substituted value, not the token, so masking the token in the
canonical alone would guarantee a mismatch. A unit that ever needed a per-repo
substitution would require template-matching, not this hash; none does today.

Enable two real units to exercise both granularities: the whole
.markdownlint-cli2.jsonc file, and the canonical github-release job region of
build-release-task.yml (extracted by job key from both sides). Self-tests cover
identical/EOL-only match, body edit -> violated, past-revision -> stale, and a
forked region hashing differently.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 21, 2026 13:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds the second fidelity engine to spec/audit.py: a verbatim content-hash check (EOL-normalized) that can classify mismatches as stale (matches a past canonical revision) vs violated (matches no canonical revision), and wires it into the baseline via spec/files.json.

Changes:

  • Extend spec/audit.py with EOL-only normalization + sha256 hashing and a verbatim checker that consults the hub file’s git history.
  • Mark the github-release job region in build-release-task.yml as verbatim-pinned via the interface contract (verbatimJobs) and document the model update.
  • Update audit procedure docs to include verbatim checks in the mechanized subset.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
spec/files.json Adds verbatimJobs for the build-release interface contract and points canonical comparison at the catalog snippet via reference.
spec/fidelity-model.md Updates the verbatim definition/normalization rules to EOL-only and documents stale-vs-violated semantics.
spec/audit.py Implements verbatim hashing/classification and integrates interface+verbatim checks into the audit loop and selftests.
AUDIT.md Documents verbatim checks as part of the mechanized audit subset (needs terminology alignment per review comment).

Comment thread AUDIT.md Outdated
Comment thread spec/audit.py
…#377)

The verbatim verdict for the non-stale case was the enum 'violated' in code
while every doc and DRIFT message said 'modified' - two labels for one outcome.
Rename the enum to 'modified' so the code matches the guidance.

git_file_history() shelled out per audited repo; one canonical is compared
against every repo, so it recomputed the same history O(repos * commits) times.
Cache it per rel_path for the run.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 21, 2026 13:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

Comment thread spec/fidelity-model.md Outdated
Comment thread spec/fidelity-model.md Outdated
Comment thread spec/audit.py
- Narrow the verbatim-region claim to what the engine implements: a whole file
  or a workflow job region. Markdown-section-by-heading extraction is not wired,
  so the doc no longer promises it.
- Rename the "Stale Versus Violated" heading to "Stale Versus Modified" - the
  last place still using the old verdict word.
- Trim the normalize() docstring to terse form, pointing at the fidelity-model
  "Normalization" section instead of duplicating the design narrative.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 21, 2026 13:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

spec/audit.py:303

  • check_verbatim() always walks the canonical's git history (git_file_history) even when the downstream content matches the current canonical. That forces at least one full history scan per canonical per run, even for clean repos, and also does unnecessary work before discovering an extract region is missing. Compute the current canonical match first (after any extract) and only call git_file_history() when there's an actual mismatch to classify as stale vs modified.
    try:
        canon_text = (ROOT / canonical_rel).read_text(encoding="utf-8")
    except OSError:
        return [("DRIFT", f"verbatim: {label} canonical {canonical_rel} is unreadable from the hub (spec error?)")]
    history = git_file_history(canonical_rel)
    if extract is not None:

Comment thread spec/audit.py
…shing (#377)

classify_verbatim already hashed the canonical once and each past revision once
per call with an early exit, but across the ~21 per-repo audits the same
canonical and history texts were re-hashed every time. Memoize content_hash
(bounded input domain) so those recurring hashes are computed once - the hashing
analogue of the git_file_history cache - while keeping the early-exit loop
rather than a full set scan.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 21, 2026 13:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

Comment thread spec/audit.py Outdated
Comment thread spec/audit.py Outdated
#377)

- Memoize content_hash on the normalized form, not the raw text, so EOL-only
  variants (CRLF vs LF) share one cache entry as they normalize equal.
- Decode git history (git log/show) as UTF-8 with replacement, and read the
  canonical the same way, matching the downstream base64 -> decode("utf-8",
  "replace") path. A locale-default or strict decode on one source could hash
  otherwise-equal content differently and fabricate a mismatch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 21, 2026 14:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

spec/audit.py:250

  • The normalize() docstring is longer than it needs to be. AGENTS.md comment guidance says one line is the default; this can be expressed tersely without wrapping while still pointing to the spec doc for rationale.
    """Reduce a carried unit to its comparable form: neutralize line endings, since EOL variance is governed
    separately, not a fidelity deviation. No placeholder masking - see spec/fidelity-model.md "Normalization".
    """

Comment thread spec/audit.py Outdated
Comment thread spec/audit.py Outdated
Collapse the content_hash cache-key comment and the git_file_history docstring
to one line each, and tighten the history-decode comment, per the AGENTS.md
one-line-default comment guidance. No behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 21, 2026 14:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment thread spec/audit.py Outdated
The contents-decode guard used truthiness (`if content.get("content")`), so a
legitimately empty file (encoding "base64", content "") was misread as no inline
content and flagged for hand-verification. Guard on `encoding == "base64"`
instead: an empty file decodes to "", while a genuinely non-inline payload
(too large, encoding "none") still yields None and is flagged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 21, 2026 14:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

spec/audit.py:250

  • normalize()'s docstring is still a multi-line design narrative. Per AGENTS.md comment guidance (one line is the default), this can be collapsed to a single-line docstring that states the non-obvious behavior (EOL normalization) and points to spec/fidelity-model.md for rationale.
def normalize(text):
    """Reduce a carried unit to its comparable form: neutralize line endings, since EOL variance is governed
    separately, not a fidelity deviation. No placeholder masking - see spec/fidelity-model.md "Normalization".
    """

Comment thread spec/audit.py Outdated
Comment thread spec/audit.py Outdated
Comment thread AUDIT.md Outdated
- Cap _hash_normalized's lru_cache at 1024 entries instead of unbounded. The
  keys that recur across repos are the canonical and its history (few); unique
  downstream copies only cycle through, so a bound cannot cost a real hit while
  it removes the unbounded-growth risk on a large fleet run.
- Recast three semicolons that joined independent clauses (AUDIT.md verbatim
  sentence, the history-decode comment, the fidelity-model template-matching
  note) into separate sentences, per the prose style guidance.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 21, 2026 14:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

spec/audit.py:251

  • normalize()'s docstring is wrapped over multiple lines with design rationale. Per AGENTS.md comment guidance (one line default), collapse this to a single-line docstring that just states the non-obvious behavior and points to the model doc for rationale (AGENTS.md:146-148).
def normalize(text):
    """Reduce a carried unit to its comparable form: neutralize line endings, since EOL variance is governed
    separately, not a fidelity deviation. No placeholder masking - see spec/fidelity-model.md "Normalization".
    """
    return text.replace("\r\n", "\n").replace("\r", "\n")

spec/audit.py:254

  • This inline comment uses a semicolon to join two independent clauses, which the repo's prose rules forbid for agent-authored text (AGENTS.md:158). Recast without a semicolon.
@functools.lru_cache(maxsize=1024)  # bounded; the keys that recur across repos are the canonical and its history

@ptr727
ptr727 merged commit 722ceb1 into develop Jul 21, 2026
7 checks passed
@ptr727
ptr727 deleted the verbatim-engine-374 branch July 21, 2026 14:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants