jouleclaw-compose: attribute a claim to its most authoritative citation #344
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/cited-item-authority"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
compose_verified_answerbuilt each segment'sClaimAttributionfromunder 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::Primaryand 4 isCommunity, 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_timestampandretrieval_axesall come from it. Selection is nowmin_by_keyon the tier, ties broken onitem_idso 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_mapdrops a cited id matching no retrieved item silently. A segment whose citations all dangle came out with the ids still attached and an all-Noneattribution, 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
attribution_takes_the_most_authoritative_cited_item_not_the_first+ 1 FAILEDmax_by_key)attribution_takes_the_most_authoritative_cited_item_not_the_first+ 1 FAILEDan_authority_tie_breaks_reproducibly_not_on_citation_orderFAILEDa_citation_that_resolves_to_nothing_is_reported_not_droppedFAILEDAll 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_idsis read, in three places, and there is nojouleclaw-diagnosecrate — the field lives injouleclaw-composeandjouleclaw-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 0cargo test --workspace --locked— exit 0, 484 test binaries green, no failurescargo clippy -p jouleclaw-compose --all-targets --no-deps --locked -- -D warnings— cleanNo 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-composeis a path dependency only withinjouleclaw-rs, verified rather than assumed.🤖 Generated with Claude Code
`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]>