jouleclaw-compose: attribute a claim to its most authoritative citation #344

Merged
dcharlot merged 1 commit from fix/cited-item-authority into main 2026-10-19 22:51:54 -04:00
Owner

compose_verified_answer built each segment's ClaimAttribution from

ds.cited_item_ids.iter().filter_map(resolve).next()

under a comment reading "Source URI from the most authoritative cited item".

.next() is the first cited id that happens to resolve, in whatever order the ids were listed. A stated selection criterion the code did not perform.

Why it matters

Authority tier 1 is AuthorityTier::Primary and 4 is Community, so the most authoritative cited item is the minimum tier — which .next() does not find. A segment citing a forum post before a peer-reviewed source was attributed to the forum post.

And the whole attribution rides on that single choice: retrieval_source_uri, retrieval_timestamp and retrieval_axes all come from it. Selection is now min_by_key on the tier, ties broken on item_id so the answer's provenance is reproducible rather than a function of the order citations happen to be listed in — order is not a property of the sources.

A second problem in the same expression

filter_map drops a cited id matching no retrieved item silently. A segment whose citations all dangle came out with the ids still attached and an all-None attribution, with nothing anywhere stating why — a reader sees citations and no source. Dangling citations are now counted and reported as a caveat, through the mechanism this function already uses for dropped segments.

That is the honest shape of what the sweep called "citation laundering": the citations were not read back as provenance, and the gap was invisible in the output.

Falsification

mutation result
C1 the pre-fix behaviour — first resolving citation wins attribution_takes_the_most_authoritative_cited_item_not_the_first + 1 FAILED
C2 treats the least authoritative tier as the most (max_by_key) attribution_takes_the_most_authoritative_cited_item_not_the_first + 1 FAILED
C3 no id tie-break, so an equal-authority tie follows citation order only an_authority_tie_breaks_reproducibly_not_on_citation_order FAILED
C4 a dangling citation is dropped silently again only a_citation_that_resolves_to_nothing_is_reported_not_dropped FAILED

All four killed by their named test, each checked for anchor uniqueness, that the edit landed, that it compiled, and that the named test failed.

C3 is why the third test exists. My first two tests used items of differing tiers, so a missing tie-break changed nothing and the reproducibility claim in my own comment was unpinned. I added the equal-authority test before falsifying rather than after reading a false "SURVIVED" — and C3 confirms it is the only test that catches it.

Every test discriminates on order, citing the same items both ways round, because a single arrangement cannot distinguish "picked by authority" from "picked first".

On the finding itself

The sweep's description was wrong in its specifics: cited_item_ids is read, in three places, and there is no jouleclaw-diagnose crate — the field lives in jouleclaw-compose and jouleclaw-schema. Reading the code to check that is what surfaced the real defect sitting beside it. This is the fourth carried-over finding in a row whose stated details did not survive verification while something real was nearby.

Verification

  • cargo check --workspace --all-targets --locked (jouleclaw-rs, RUSTFLAGS=-C target-feature=+fp16) — exit 0
  • cargo test --workspace --locked — exit 0, 484 test binaries green, no failures
  • cargo clippy -p jouleclaw-compose --all-targets --no-deps --locked -- -D warnings — clean

No signature changed, so the public surface is unchanged. The new caveat only fires where a citation fails to resolve, which does not happen on the normal path — and jouleclaw-compose is a path dependency only within jouleclaw-rs, verified rather than assumed.

🤖 Generated with Claude Code

`compose_verified_answer` built each segment's `ClaimAttribution` from ```rust ds.cited_item_ids.iter().filter_map(resolve).next() ``` under a comment reading **"Source URI from the most authoritative cited item"**. `.next()` is the first cited id that happens to resolve, in whatever order the ids were listed. A stated selection criterion the code did not perform. ## Why it matters Authority tier **1** is `AuthorityTier::Primary` and **4** is `Community`, so the most authoritative cited item is the **minimum** tier — which `.next()` does not find. A segment citing a forum post before a peer-reviewed source was attributed to the forum post. And the whole attribution rides on that single choice: `retrieval_source_uri`, `retrieval_timestamp` and `retrieval_axes` all come from it. Selection is now `min_by_key` on the tier, ties broken on `item_id` so the answer's provenance is reproducible rather than a function of the order citations happen to be listed in — order is not a property of the sources. ## A second problem in the same expression `filter_map` drops a cited id matching no retrieved item **silently**. A segment whose citations all dangle came out with the ids still attached and an all-`None` attribution, with nothing anywhere stating why — a reader sees citations and no source. Dangling citations are now counted and reported as a caveat, through the mechanism this function already uses for dropped segments. That is the honest shape of what the sweep called "citation laundering": the citations were not read back as provenance, and the gap was invisible in the output. ## Falsification | mutation | result | |---|---| | **C1** the pre-fix behaviour — first resolving citation wins | `attribution_takes_the_most_authoritative_cited_item_not_the_first` + 1 FAILED | | **C2** treats the *least* authoritative tier as the most (`max_by_key`) | `attribution_takes_the_most_authoritative_cited_item_not_the_first` + 1 FAILED | | **C3** no id tie-break, so an equal-authority tie follows citation order | **only** `an_authority_tie_breaks_reproducibly_not_on_citation_order` FAILED | | **C4** a dangling citation is dropped silently again | **only** `a_citation_that_resolves_to_nothing_is_reported_not_dropped` FAILED | All four killed by their named test, each checked for anchor uniqueness, that the edit landed, that it **compiled**, and that the named test failed. **C3 is why the third test exists.** My first two tests used items of *differing* tiers, so a missing tie-break changed nothing and the reproducibility claim in my own comment was unpinned. I added the equal-authority test before falsifying rather than after reading a false "SURVIVED" — and C3 confirms it is the only test that catches it. Every test discriminates on **order**, citing the same items both ways round, because a single arrangement cannot distinguish "picked by authority" from "picked first". ## On the finding itself The sweep's description was wrong in its specifics: `cited_item_ids` **is** read, in three places, and there is no `jouleclaw-diagnose` crate — the field lives in `jouleclaw-compose` and `jouleclaw-schema`. Reading the code to check that is what surfaced the real defect sitting beside it. This is the fourth carried-over finding in a row whose stated details did not survive verification while something real was nearby. ## Verification - `cargo check --workspace --all-targets --locked` (`jouleclaw-rs`, `RUSTFLAGS=-C target-feature=+fp16`) — exit **0** - `cargo test --workspace --locked` — exit **0**, **484** test binaries green, no failures - `cargo clippy -p jouleclaw-compose --all-targets --no-deps --locked -- -D warnings` — clean No signature changed, so the public surface is unchanged. The new caveat only fires where a citation fails to resolve, which does not happen on the normal path — and `jouleclaw-compose` is a path dependency only within `jouleclaw-rs`, verified rather than assumed. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
jouleclaw-compose: attribute a claim to its most authoritative citation
All checks were successful
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 7s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 59s
standards workspaces build + test / mesh-rs (mesh/mesh-rs, -p mesh-engine-gguf -p mesh-openai --features mesh-engine-gguf/gguf,mesh-openai/server, mesh-rs, linux-arm64) (pull_request) Successful in 1m31s
standards workspaces build + test / jouleclaw-rs (-nodebug, jouleclaw/jouleclaw-rs, -p jouleclaw-energy --features rapl,nvml, jouleclaw-rs, linux-arm64, -C target-feature=+fp16) (pull_request) Successful in 4m11s
jouleclaw public surface must stay documented / Public-surface audit (jouleclaw-rs) (pull_request) Successful in 1m15s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 13m14s
standards workspaces build + test / jouledesk-rs (jouledesk/jouledesk-rs, jouledesk-rs, macos) (pull_request) Successful in 4m23s
7c44d332e3
`compose_verified_answer` built each segment's `ClaimAttribution` from

    ds.cited_item_ids.iter().filter_map(resolve).next()

under a comment reading "Source URI from the most authoritative cited item".
`.next()` is the first cited id that happens to resolve, in whatever order the
ids were listed — a stated selection criterion the code did not perform.

Authority tier 1 is `AuthorityTier::Primary` and 4 is `Community`, so the most
authoritative cited item is the **minimum** tier. A segment citing a forum post
before a peer-reviewed source was therefore attributed to the forum post, and
the whole attribution rides on that one choice: `retrieval_source_uri`,
`retrieval_timestamp` and `retrieval_axes` all come from it. Selection is now
`min_by_key` on the tier, with ties broken on `item_id` so the result is
reproducible rather than a function of citation order.

A second problem sat in the same expression. `filter_map` drops a cited id that
matches no retrieved item **silently**, so a segment whose citations all dangle
came out with the ids still attached and an all-`None` attribution, with
nothing anywhere stating why. A reader sees citations and no source. Dangling
citations are now counted and reported as a caveat, using the mechanism the
function already uses for dropped segments. This is the honest shape of the
problem the sweep called "citation laundering": the citations are not read back
as provenance, and the gap was invisible.

Three tests, each pinning one property, every one of them discriminating on
*order* rather than on a single arrangement:

- the same two items, tier 1 and tier 4, cited both ways round, must attribute
  to the tier-1 source both times;
- two items of **equal** authority, cited both ways round, must resolve the
  same way — the tie-break is the only thing that makes this true, and it is
  the only test that catches its removal;
- a segment with one real and one dangling citation reports the dangling one
  and still attributes from the real one; a segment where every citation
  dangles has no attribution at all, which is when saying so matters most.

The sweep's own description of this finding was wrong in its specifics —
`cited_item_ids` is read in three places, and there is no `jouleclaw-diagnose`
crate — but reading the code to check surfaced the real defect beside it.

Co-Authored-By: Claude Opus 5 <[email protected]>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Transaction-Science/open-standards!344
No description provided.