Skip to content

Witness tool identity in the Bifrost adapters and record Amendment A9 - #87

Merged
DavidBakerEffendi merged 5 commits into
mainfrom
dave/freeze-prep-a9-witnessing
Aug 27, 2026
Merged

Witness tool identity in the Bifrost adapters and record Amendment A9#87
DavidBakerEffendi merged 5 commits into
mainfrom
dave/freeze-prep-a9-witnessing

Conversation

@DavidBakerEffendi

Copy link
Copy Markdown
Collaborator

Pre-freeze corrections ahead of the v0.10.8 re-pin and v0.5.0 freeze. Nothing here binds a Bifrost version: the committed pin stays v0.10.6, no report bytes change (the only additions under reports/ are the 15 new Amendment A9 probe evidence files), and the v0.4.0 freeze manifest verifies green.

1. Witness the tool identity instead of asserting it

Both the Bifrost modeling adapter and the Bifrost tool-native adapter published a hardcoded "Bifrost v0.10.6" identity they never read — the native adapter never invoked the binary at all (its row declines all six templates). Any re-pin would have silently published a false version into every declined cell's retained rationale.

Now both adapters witness identity from the pinned binary once per run, before the population is walked, so a run whose every cell is declined still records a real identity. Retained decision documents rename pinned_tool_identitywitnessed_tool_identity; the native report carries the witnessed build identity; tests assert every retained rationale names a measured identity. Per-cell decline semantics are unchanged (no cell ever reaches the analyzer).

Deliberate reversal of a rule #86 shipped: #86's prose said a 0/6 native row is "never invoked at all — not even for its version banner", and docs/python-native.md verified that by running against a nonexistent binary path. This PR overturns the run-level half of that rule (the cell-level half stands): the run-level identity must be real, so a 0/6 run now fails against a nonexistent binary. docs/native-profile.md gains an outcome-honesty subsection stating this; docs/python-native.md is corrected where it stood.

2. Amendment A9 — Bifrost's modeling category Z promoted to scored

The Bifrost partition marked category Z (sanitizer declarations) unsupported on the adapter README's claim that sanitizer lowering was a future CLI capability. Measured false on Bifrost v0.10.7 (build 44d9a5be): the RQLP analysis grammar accepts a (sanitizer :id … :selector … :input … :output … :removes […]) stanza, and a load-bearing probe on the committed fixtures in all three modeling languages shows

  • the declared sanitizer suppresses the kill-negative (run completes, not vacuous),
  • removing the stanza restores the flow with a full witness,
  • an undeclared sanitizer-shaped call does not suppress (selectivity, both directions).

Evidence retained under reports/raw/amendment-a9-bifrost-sanitizer/ (15 files), reproducible via scripts/probe-bifrost-sanitizer-lowering.sh. Bifrost moves from 2 to 4 scored modeling templates; P/O/E/B stay unsupported — with the record corrected to note :transforms/:external-models are grammar-accepted but their lowering is unshown, and acceptance is not lowering per the preregistration's own rule. MODELING_PARTITION, the count tests, the artifact-declares-only-scored-categories invariant (now requires :sanitizers), and the three model-*.rqlp artifacts updated accordingly. The JS/Java modeling rows' quoted configuration hashes are labelled as describing the pre-A9 run, re-run pending at the v0.10.8 re-pin.

Amendment sequence stays monotonic and cross-document: A1 (challenge tier), A2–A5 + A9 (modeling matrix), A6–A8 (native profile); both amendment preambles now state the interleaving.

Deferred to the v0.10.8 re-pin PR

  • The doc re-pin itself and the full 66-report re-run (the v0.5.0 freeze gates on Bifrost v0.10.8).
  • docs/native-profile.md's category-Z rationale still quotes the README sentence A9 retired; the cell's outcome is unaffected (the CLI ships no endpoint catalog), and correcting the wording is a dated tool-native amendment left for the re-pin PR.

Verification

cargo fmt --check clean · cargo test 162 passed · validate 852 cases · validate-reports 66 reports · generate-results --check current · validate-freeze green (v0.4.0 manifest untouched). The A9 probe re-run post-rebase is byte-identical to the retained evidence.

🤖 Generated with Claude Code

DavidBakerEffendi and others added 5 commits August 27, 2026 18:34
Two adapters were publishing a version they never read. Both are fixed
here, ahead of anything else: a report that asserts its own pin is the
one defect a freeze cannot survive, because the assertion stays green
while the binary underneath it moves.

- **The modeling adapter** built every declined cell's retained
  rationale from `ModelingTool::pinned_identity()` — the constant
  `"Bifrost v0.10.6"`. Twenty of a language's twenty-four modeling
  assertions are such rationales, so twenty retained decisions per run
  named a build by memory rather than by measurement.
- **The tool-native adapter** did the same for its report's
  `tool_version` *and* its `tool_build_identity`
  (`"Bifrost v0.10.6 built-in policy packs"`), which is worse in kind:
  that row declines all six templates, so it never invokes the analyzer
  at all and had no witnessed identity anywhere in the artifact.

`modeling_version_identity` becomes `witness_tool_identity`, is called
once per run **before** the population is walked — including runs whose
every cell is declined — and is threaded into
`modeling_unsupported_reason`, `native_unsupported_reason`,
`native_activation`, and both partitions' retained decision documents
(`pinned_tool_identity` → `witnessed_tool_identity`). The native report
now records the witnessed version and the witnessed build identity
alongside the activation shape.

A declined cell is still decided from the template identity with no
analyzer invocation over the fixture — that contract is untouched. What
changes is that the *run* proves which binary it was pinned to.

Two supporting rules: Bifrost's witnessing no longer degrades to
`"unknown"` on a failed `--version` (a run whose every result is a
capability decision has no other evidence, so an unwitnessed identity
there is a report asserting a pin it never observed), and
`pinned_identity()` is documented as a document reference that may not
reach a report field or a rationale. Tests assert every retained reason
names the identity the caller measured.

No report is re-run here and no pinned-version statement moves: the
evidence re-run lands with the re-pin.

`cargo fmt --check` · `cargo test` (160) · `validate` (840 cases) ·
`validate-reports` (62).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
## Summary

The preregistration declined category Z for Bifrost on a sentence in
this repository's own adapter README — *"Sanitizer lowering is a future
Bifrost CLI capability."* Measured against build
`44d9a5be416432bf8ed414afd3ea0031245ebb57`, the sentence is false.
Category Z moves to scored: Bifrost's set goes from two of the twelve
templates to four, one of six categories to two.

The measurement is four-way rather than two-way, because this is the
direction in which a partition published by the engine's own vendor is
least trustworthy — and it holds in all three wave-M1 languages:

| Direction | Result |
|---|---|
| The grammar accepts `(sanitizer … :input … :output … :removes […])` | empty `diagnostics` |
| The declaration suppresses | template 5's negative, 0 findings, `completion: complete` — not vacuous |
| Removing it restores the flow | 1 finding, `definite` / `complete`, strong source-to-sink anchor |
| Binding is by identity, not name shape | template 6's positive, through the *undeclared* `sanitize`, still reported |

- **The other four cells were re-examined and do not move.** E and B
  have no surface at all — `:entry-points`, `:entry_points`, `:stores`,
  `:persistence` are each rejected as unknown fields. P and O have
  adjacent sections the grammar *does* accept (`:transforms`,
  `:external-models`), but a `transform` entry is a label-rewriting
  declaration rather than argument→return propagation, and acceptance is
  not lowering — the rule this document applied to a v0.9.5 schema
  pointer applies here too. That negative result is recorded in A9
  rather than left implied.
- **The three modeling policies gain the sanitizer stanza**, declaring
  `scrub` and deliberately not its sibling `sanitize`, keeping the
  artifact-declares-exactly-its-scored-categories invariant true in both
  directions: the runner test now *requires* `:sanitizers` and still
  forbids the four declined sections.
- **The README is corrected, not quietly.** The retired sentence is
  quoted by the tool-native profile's own category-Z rationale, whose
  cell turns on the absent shipped endpoint catalog rather than on
  lowering; that document's amendment is not this one's to make, and A9
  says so.

Evidence is retained under
`reports/raw/amendment-a9-bifrost-sanitizer/`, produced by
`scripts/probe-bifrost-sanitizer-lowering.sh` — the same run-it-twice
shape as the per-language load-bearing probes beside it.

No report is re-run and no pinned-version statement moves. The retained
modeling reports predate this amendment and still record category Z as
`unsupported`; the scored evidence lands with the next evidence re-run,
which the README and the two language rows say in place.

## Validation

`cargo fmt --check` · `cargo test` (160) · `validate` (840 cases) ·
`validate-reports` (62).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… count

`docs/adapters.md` and the Semgrep adapter README each restate the
modeling partition's scored counts from outside the preregistration, so
each said "Bifrost 2 of 12" and "Bifrost's one of six" after A9 promoted
category Z. Both now name the amendment rather than the stale number.

No pinned-version statement and no report changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The JavaScript and Java modeling rows quote a configuration hash for a
run made against the pre-A9 artifact. Now that the committed policies
carry the sanitizer declaration, a reader recomputing either hash gets a
different value, so each row says plainly that it describes the retained
pre-A9 run and that the re-run is pending.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Wave N1's rows were written under a rule this branch overturns. #86
stated it in `run_native` and in four documents: a row the partition
scores nothing for is *never invoked at all — not even for its version
banner*, so its report carries `pinned_identity()`, and the Python row
verified this by running against `/nonexistent/joern` and getting
byte-identical evidence.

Half of that rule is right and is preserved exactly: no declined cell is
ever handed to the analyzer, and every outcome is still decided from the
template identity before anything runs. The other half — that the report
should then *assert* a version — is the defect the first commit of this
branch fixes, and a 0 / 6 row is where it bites hardest: its twelve
retained rationales are the whole of its evidence, so a constant would go
on naming the previous pin after the binary underneath it moved.

- `docs/native-profile.md` gains **the run-level identity is witnessed,
  including at 0 / 6** under outcome honesty, which separates the two
  halves: "before the tool is invoked" is a statement about cells;
  `tool_version` and `tool_build_identity` name which binary the run was
  pinned to, and a name is only evidence if something read it. Reading
  `--version` is not analyzing a fixture, so the three-way distinction is
  untouched.
- The consequence is stated rather than hidden: a 0 / 6 run no longer
  completes against a nonexistent binary path. It fails, because a run
  that cannot witness its own pin has nothing truthful to put in those
  two fields. `docs/python-native.md` says so where its original
  verification stood.
- The three adapter READMEs and `docs/java-native.md` say "never asked
  about a fixture" where they said "never invoked", which is what those
  runs actually establish.

Also: both amendment preambles now state the interleaving explicitly —
A8 in the tool-native profile is followed by A9 in the modeling matrix —
since the single sequence now crosses documents in both directions.

Rebased onto #86: main's shapes taken wholesale (the native partition's
language dimension, `native_supported_templates(tool, language)`, the
`{build} — {activation}` build-identity composition, and #86's
all-three-languages population test), with the witnessing semantics
re-applied on top.

## Validation

`cargo fmt --check` · `cargo test` (162) · `validate` (852 cases) ·
`validate-reports` (66).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@DavidBakerEffendi
DavidBakerEffendi merged commit 3b8ccdc into main Aug 27, 2026
2 checks passed
@DavidBakerEffendi
DavidBakerEffendi deleted the dave/freeze-prep-a9-witnessing branch August 27, 2026 16:41
DavidBakerEffendi added a commit that referenced this pull request Aug 28, 2026
The v0.5.0 release: the Bifrost v0.10.7 fix cycle measured, and the
first publication of the modeling (#15) and tool-native (#16) tiers.
Four commits: the 66-report re-run at one fixture revision, the freeze
manifest, the audited results, and the release notes + site snapshot.

## Freeze

852 cases / 66 reports / 4 analyzers — Bifrost v0.10.7 (build
`44d9a5be`, identity witnessed per PR #87), CodeQL 2.26.3, Joern
4.0.610, Semgrep CE 1.174.0 — fixture revision `sha256:9df209ed…`, tag
`v0.5.0` at main tip `7d688c6` (already pushed). `validate-freeze`
green; the v0.4.0 manifest is superseded, never mutated.

## Fix-cycle headlines (derived from the frozen results, diffed against
v0.4.0's)

- Bifrost runner errors **22 → 0**; kernel decisive-correct **222 →
435**; smoke **115 → 117** of 118.
- Ruby kernel decides for the first time: **0/58 → 21/58** (36
inconclusive, 1 FP). Rust `gap_contract` crash class (bifrost-dev#2638):
12 runner errors → 10 correct + 2 inconclusive.
- Honest negatives, published: four **new** v0.10.7 false positives
(bifrost-dev#2731 — C#/Go loop-carried, C#/Ruby infeasible-branch); the
ten `element-object` crash cases resolve to *inconclusive*, not correct;
the Java `direct-propagation` smoke-vs-kernel instability persists
unreconciled on this build; inconclusive concentrations remain (Ruby 36,
Kotlin 30, PHP/C++ 26, C#/TS 24).

## First tier publications

- **Modeling** (benchmark-controlled): Bifrost's Amendment-A9 category-Z
cells decide **12/12 correct** in their first scored run; CodeQL 24/24
×3; Joern 14/16 (A2 partition, 4/6 categories); Semgrep 10/10 over its
5-template A3 partition.
- **Tool-native**: CodeQL Java 11/12, Python 10/12, JavaScript 9/12 —
misses are the preregistered hazards; Semgrep's vendored-snapshot
asymmetry (Python 8/12 with 4 FPs via a shipped audit rule vs JS/Java
0/6); Bifrost and Joern decline-all with witnessed run identity (A10
rationale).
- The landing page gains two separately fenced tier sections — the
core-kernel filter that guarantees no pooling stays; declined tiers
render as "declines the tier", never `0/N`; a Model profile column keeps
benchmark-controlled and tool-native visually distinct; vendor colors
keep the fixed identity map.

## Site

v0.4.0 archived with evidenceRef pinned to `86a29e0`; v0.5.0 snapshot
pages added; `/current` redirects to v0-5-0; `astro check` clean, 31
pages build.

## Verification

`cargo fmt --check` · `cargo test` 162 · `validate` 852 ·
`validate-reports` 66 · `validate-freeze` green · `generate-results
--check` current · `astro check` + build clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant