fan-out: ForkScore CLI · authorship warrant ladder · 5F Phase 1c

Three streams in one commit since they're independent and each is
small.

#000012 Phase 1b — ForkScore CLI surface
========================================

`arborist v8 score` already existed; this adds `--out` for JSON-
artifact emission so CI / downstream graders / mesh peers can
ingest without parsing stdout. New Makefile targets:

- `make bench-fork-baseline` — pins current bench-suite output as
  the ForkScore parent (one-shot per iteration).
- `make bench-fork-score` — runs bench-suite again, scores child
  vs pinned parent, writes bench/results/fork_score_report.json.
  Exit 1 on REJECT so CI can gate.

`FORK_PARENT` / `FORK_CHILD` / `FORK_REPORT` env-vars override
default paths. New regression test pins the --out contract:
stdout and file are byte-identical artifacts; --out auto-creates
parent directories.

#000026 Phase 3 — authorship warrant ladder
============================================

Sidecar classifier in `arborist/qa/warrant_authorship.py`. Six
tiers strongest-to-weakest: AUTHOR_PACKAGE_METADATA →
AUTHOR_REPOSITORY_OWNER → AUTHOR_PAGE_BYLINE →
AUTHOR_PRIMARY_PAGE_TITLE → AUTHOR_COPYRIGHT_FOOTER →
AUTHOR_SECONDARY_SOURCE. Plus NO_AUTHORSHIP_SIGNAL when the
question doesn't smell like authorship (sidecar stays quiet).

Detector regexes for each tier:
- Tier 1: `author = "X"` simple form + TOML inline-table
  `authors = [{ name = "X" }]` form (PEP 621).
- Tier 2: github.com / gitlab.com / codeberg.org / bitbucket.org
  URL pattern.
- Tier 3: "By NAME" / "Author: NAME" prose + <meta name="author">.
  Inline-flag regex keeps the prefix case-insensitive while the
  capitalized-name capture stays case-sensitive.
- Tier 4: cited evidence is the entity's own primary page (host
  tokens overlap title + answer; third-party indexers like
  wikipedia.org explicitly excluded).
- Tier 5: `© NAME` / `Copyright YYYY NAME` (the current
  `virt-back` warrant).
- Tier 6: fall-through when authorship-shaped question hits cited
  evidence with no direct markers.

Sidecar discipline: never enters proof path; never raises;
returns dict with `tier`, `tier_rank` (1=strongest, 99=quiet),
`signals`, `candidate_names`, `note`. 20 tests cover each tier
+ noise filtering + sidecar contract + tier-ordering (strongest
wins when multiple fire).

Wiring into `arborist inspect` sidecar output + audit-line
render-tail is queued as a follow-up — sidecar itself ready.

#000025 5F Phase 1c — fixture catalog expansion
================================================

Synthetic side of all five 5F sub-batteries expanded 10 → 30:

  function       — varied claim_count, pointer_set, threshold cases
  falsification  — 13 violation tags (WARRANT_MISSING, TITLE_MISMATCH,
                   FORMAT_COLLAPSED, NO_EVIDENCE_POINTER, BARE_NAME_CLAIM,
                   LAZY_ANCHOR_DEMOTED, etc.) + 7 fail cases
  feedback-loop  — 10 chain templates × 2 cycles
  finetuning     — 20 capability transitions across all 5S/5T/5F/5R
                   sub-batteries + canonical math/logic
  formulate      — 12 lattice shapes × 2 (with deliberate fail cases)

150/150 fixtures pass through `bench-5f-*` runners.
test_session_integration.py total updated 462 → 562. Pinned
test_5f_*_runs counts updated 10 → 30 across all assertions.

Tests
=====

Full suite: 1388 passed, 36 skipped (was 1367; +21 — 20 warrant
tests + 1 ForkScore --out test).
This commit is contained in:
russell@unturf.com 2026-05-09 12:14:38 -04:00
parent 62d9c7a440
commit 60b5748ff9
No known key found for this signature in database
14 changed files with 981 additions and 24 deletions

View file

@ -227,31 +227,32 @@ def test_5f_function_runs():
res = b_5f.run_function(F5F / "function-v1.jsonl")
assert res.battery == "5f"
assert res.sub_battery == "function"
assert res.pass_count == 10
# Phase 1c (2026-05-09) — fixture catalog expanded 10 → 30.
assert res.pass_count == 30
assert res.metrics["function_pass_rate"] == 1.0
def test_5f_finetuning_runs():
res = b_5f.run_finetuning(F5F / "finetuning-v1.jsonl")
assert res.pass_count == 10
assert res.pass_count == 30
assert res.metrics["adaptation_improvement_rate"] == 1.0
def test_5f_falsification_runs():
res = b_5f.run_falsification(F5F / "falsification-v1.jsonl")
assert res.pass_count == 10
assert res.pass_count == 30
assert res.metrics["error_detection_rate"] == 1.0
def test_5f_formulate_runs():
res = b_5f.run_formulate(F5F / "formulate-v1.jsonl")
assert res.pass_count == 10
assert res.pass_count == 30
assert res.metrics["structural_match_rate"] == 1.0
def test_5f_feedback_loop_runs():
res = b_5f.run_feedback_loop(F5F / "feedback-loop-v1.jsonl")
assert res.pass_count == 10
assert res.pass_count == 30
assert res.metrics["integration_coverage_rate"] == 1.0
@ -430,9 +431,10 @@ def test_5f_formulate_live_path_routes_through_parse_claims():
def test_5f_formulate_embedded_path_still_works():
"""Phase 1a fixtures (embedded produced_lattice) keep working
after the Phase 1b.2 wire-up. Backward compat invariant."""
after the Phase 1b.2 wire-up + Phase 1c expansion. Backward
compat invariant."""
res = b_5f.run_formulate(F5F / "formulate-v1.jsonl")
assert res.pass_count == 10
assert res.pass_count == 30 # Phase 1c — expanded 10 → 30
for t in res.per_task:
assert t.detail["source"] == "embedded"
@ -485,7 +487,7 @@ def test_5f_feedback_loop_live_path_writes_real_audit_events():
def test_5f_feedback_loop_embedded_path_still_works():
res = b_5f.run_feedback_loop(F5F / "feedback-loop-v1.jsonl")
assert res.pass_count == 10
assert res.pass_count == 30 # Phase 1c
for t in res.per_task:
assert t.detail["source"] == "embedded"
@ -558,7 +560,7 @@ def test_5f_function_live_path_routes_through_parse_claims():
def test_5f_function_embedded_path_still_works():
res = b_5f.run_function(F5F / "function-v1.jsonl")
assert res.pass_count == 10
assert res.pass_count == 30 # Phase 1c
for t in res.per_task:
assert t.detail["source"] == "embedded"
@ -582,7 +584,7 @@ def test_5f_finetuning_live_path_round_trips_selfmodel():
def test_5f_finetuning_embedded_path_still_works():
res = b_5f.run_finetuning(F5F / "finetuning-v1.jsonl")
assert res.pass_count == 10
assert res.pass_count == 30 # Phase 1c
for t in res.per_task:
assert t.detail["source"] == "embedded"
@ -616,7 +618,7 @@ def test_5f_falsification_live_path_routes_through_verify_quotes():
def test_5f_falsification_embedded_path_still_works():
res = b_5f.run_falsification(F5F / "falsification-v1.jsonl")
assert res.pass_count == 10
assert res.pass_count == 30 # Phase 1c
for t in res.per_task:
assert t.detail["source"] == "embedded"

View file

@ -239,15 +239,21 @@ def test_full_dav1dprometheus_suite_runs_end_to_end(tmp_path, capsys):
def test_full_suite_total_fixture_count():
"""Sanity check: the complete Dav1DPrometheus suite executes 462
deterministic tasks across 21 sub-batteries (5S+5T+5F+5R)."""
"""Sanity check: the complete Dav1DPrometheus suite executes 562
deterministic tasks across 21 sub-batteries (5S+5T+5F+5R).
History:
- Phase 1a baseline: 462 tasks.
- Phase 1c (#000025, 2026-05-09): 5F synthetic side expanded
10 30 across all 5 sub-batteries; +100 562.
"""
from bench.batteries.runner import _DEFAULT_FIXTURES, _run_one
total = 0
for (battery, sub), fx in _DEFAULT_FIXTURES.items():
result = _run_one(battery, sub, Path(fx))
total += result.pass_count + result.fail_count
assert total == 462
assert total == 562
def test_5s_phase1a_digests_unchanged_after_phase1b():

View file

@ -449,6 +449,39 @@ def test_cli_v8_score_rejects_returns_nonzero(tmp_path, capsys):
assert payload["verdict"] == "REJECT"
def test_cli_v8_score_out_writes_json_artifact(tmp_path, capsys):
"""#000012 Phase 1b — `--out` mirrors stdout to a file so CI /
mesh peers / downstream graders can ingest the artifact without
parsing pipe output."""
from arborist.cli import build_parser
parent_path = tmp_path / "parent.json"
child_path = tmp_path / "child.json"
out_path = tmp_path / "results" / "fork_score.json" # parent dir missing on purpose
_write_bench_result(parent_path, {
"5s": {"syntax": {"parse_pass_rate": 0.80}},
"5t": {}, "5f": {},
})
_write_bench_result(child_path, {
"5s": {"syntax": {"parse_pass_rate": 0.85}},
"5t": {}, "5f": {},
})
parser = build_parser()
args = parser.parse_args([
"v8", "score",
"--parent", str(parent_path),
"--child", str(child_path),
"--out", str(out_path),
])
args.func(args)
stdout_payload = json.loads(capsys.readouterr().out)
assert out_path.is_file(), "--out should create parent directories"
file_payload = json.loads(out_path.read_text(encoding="utf-8"))
# Stdout and file are byte-identical artifacts.
assert stdout_payload == file_payload
assert "verdict" in file_payload
# ---------------------------------------------------------------------
# Determinism
# ---------------------------------------------------------------------

View file

@ -0,0 +1,273 @@
"""Tests for the authorship warrant ladder (#000026 Phase 3)."""
from __future__ import annotations
from arborist.qa.warrant_authorship import (
NO_AUTHORSHIP_SIGNAL,
TIER_RANK,
diagnose_authorship_warrant,
)
# ---------- Question gating ------------------------------------------------
def test_non_authorship_question_returns_no_signal():
"""Sidecar should stay quiet on irrelevant questions."""
out = diagnose_authorship_warrant(
question_text="what is the capital of france?",
answer_text="Paris.",
cited_evidence_spans=["© 2024 Wikipedia Foundation"],
)
assert out["tier"] == NO_AUTHORSHIP_SIGNAL
assert out["tier_rank"] == 99
def test_no_evidence_no_question_returns_no_signal():
out = diagnose_authorship_warrant()
assert out["tier"] == NO_AUTHORSHIP_SIGNAL
def test_no_evidence_with_authorship_question_returns_no_signal():
"""Authorship question but zero cited evidence — sidecar stays
quiet rather than reporting tier-6 secondary on no evidence."""
out = diagnose_authorship_warrant(
question_text="who wrote virt-back?",
answer_text="Russell Ballestrini.",
)
assert out["tier"] == NO_AUTHORSHIP_SIGNAL
# ---------- Tier 1 — package metadata --------------------------------------
def test_tier1_python_pyproject_author():
out = diagnose_authorship_warrant(
question_text="who wrote arborist?",
answer_text="Russell Ballestrini wrote arborist.",
cited_evidence_spans=[
'authors = [\n { name = "Russell Ballestrini" },\n]'
],
)
assert out["tier"] == "AUTHOR_PACKAGE_METADATA"
assert out["tier_rank"] == 1
assert "Russell Ballestrini" in out["candidate_names"]
def test_tier1_python_setup_py_author():
out = diagnose_authorship_warrant(
question_text="who is the author of virt-back?",
answer_text="Russell Ballestrini.",
cited_evidence_spans=['author="Russell Ballestrini"'],
)
assert out["tier"] == "AUTHOR_PACKAGE_METADATA"
def test_tier1_package_json_author():
out = diagnose_authorship_warrant(
question_text="who created this library?",
answer_text="Foo Bar.",
cited_evidence_spans=['{"name":"my-pkg","author":"Foo Bar"}'],
)
assert out["tier"] == "AUTHOR_PACKAGE_METADATA"
# ---------- Tier 2 — repository owner --------------------------------------
def test_tier2_github_uri_owner():
out = diagnose_authorship_warrant(
question_text="who maintains this?",
answer_text="The repository is owned by russellballestrini.",
cited_source_uris=["https://github.com/russellballestrini/virt-back"],
cited_evidence_spans=["A README about the project."],
)
assert out["tier"] == "AUTHOR_REPOSITORY_OWNER"
assert "russellballestrini" in out["candidate_names"]
def test_tier2_gitlab_uri_owner():
out = diagnose_authorship_warrant(
question_text="who owns this project?",
answer_text="The team-arborist group.",
cited_source_uris=[
"https://gitlab.com/team-arborist/some-project",
],
cited_evidence_spans=["x"],
)
assert out["tier"] == "AUTHOR_REPOSITORY_OWNER"
# ---------- Tier 3 — page byline -------------------------------------------
def test_tier3_byline_prose_by_pattern():
out = diagnose_authorship_warrant(
question_text="who wrote this article?",
answer_text="Russell Ballestrini.",
cited_evidence_spans=["By Russell Ballestrini, posted 2024-01-01."],
)
assert out["tier"] == "AUTHOR_PAGE_BYLINE"
def test_tier3_byline_meta_html():
out = diagnose_authorship_warrant(
question_text="who is the author of this page?",
answer_text="Russell Ballestrini.",
cited_evidence_spans=[
'<meta name="author" content="Russell Ballestrini">'
],
)
assert out["tier"] == "AUTHOR_PAGE_BYLINE"
# ---------- Tier 4 — primary page title ------------------------------------
def test_tier4_primary_page_title_match():
"""Site host token appears in the cited title AND the answer
references the same entity. The cited evidence is the entity's
own primary page."""
out = diagnose_authorship_warrant(
question_text="who wrote virt-back?",
answer_text="russellballestrini wrote virt-back.",
cited_source_uris=[
"https://russellballestrini.net/virt-back-restoring-from-backups/",
],
cited_source_titles=["virt-back: restoring from backups Russell Ballestrini"],
cited_evidence_spans=["A blog post about virt-back."],
)
# Should fire at tier 4 absent stronger signals (no package
# metadata, no repo URL, no byline, no copyright).
assert out["tier"] == "AUTHOR_PRIMARY_PAGE_TITLE"
def test_tier4_third_party_host_does_not_fire():
"""Wikipedia is a third-party indexer; primary-page heuristic
must not fire even if tokens overlap."""
out = diagnose_authorship_warrant(
question_text="who wrote virt-back?",
answer_text="russell wrote virt-back.",
cited_source_uris=[
"https://en.wikipedia.org/wiki/virt-back-russell"
],
cited_source_titles=["virt-back russell"],
cited_evidence_spans=["x"],
)
# With NO other signals firing, falls through to secondary.
assert out["tier"] == "AUTHOR_SECONDARY_SOURCE"
# ---------- Tier 5 — copyright footer --------------------------------------
def test_tier5_copyright_footer_with_year():
out = diagnose_authorship_warrant(
question_text="who wrote virt-back?",
answer_text="Russell Ballestrini wrote virt-back.",
cited_evidence_spans=["© 2024 Russell Ballestrini"],
)
assert out["tier"] == "AUTHOR_COPYRIGHT_FOOTER"
def test_tier5_copyright_footer_no_year():
out = diagnose_authorship_warrant(
question_text="who wrote virt-back?",
answer_text="Russell Ballestrini.",
cited_evidence_spans=["© Russell Ballestrini"],
)
assert out["tier"] == "AUTHOR_COPYRIGHT_FOOTER"
def test_tier5_copyright_word_form():
out = diagnose_authorship_warrant(
question_text="who created this site?",
answer_text="Russell Ballestrini.",
cited_evidence_spans=["Copyright 2024 Russell Ballestrini. All rights reserved."],
)
assert out["tier"] == "AUTHOR_COPYRIGHT_FOOTER"
# ---------- Tier 6 — secondary source --------------------------------------
def test_tier6_third_party_evidence_no_authorship_markers():
"""Question is authorship; cited evidence has none of the direct
markers falls through to secondary."""
out = diagnose_authorship_warrant(
question_text="who wrote virt-back?",
answer_text="Russell Ballestrini, according to the documentation.",
cited_evidence_spans=[
"virt-back is a tool for backing up libvirt VMs."
],
)
assert out["tier"] == "AUTHOR_SECONDARY_SOURCE"
assert out["tier_rank"] == TIER_RANK["AUTHOR_SECONDARY_SOURCE"]
# ---------- Tier ordering: strongest wins ----------------------------------
def test_strongest_tier_wins_when_multiple_fire():
"""Both copyright AND package-metadata fire — the package-metadata
signal is strictly stronger (tier 1 < tier 5)."""
out = diagnose_authorship_warrant(
question_text="who wrote virt-back?",
answer_text="Russell Ballestrini.",
cited_evidence_spans=[
"© Russell Ballestrini",
'author = "Russell Ballestrini"',
],
)
assert out["tier"] == "AUTHOR_PACKAGE_METADATA"
# But signals list captures BOTH for the operator.
tiers_seen = {s["tier"] for s in out["signals"]}
assert "AUTHOR_PACKAGE_METADATA" in tiers_seen
assert "AUTHOR_COPYRIGHT_FOOTER" in tiers_seen
# ---------- Sidecar contract -----------------------------------------------
def test_returns_dict_always():
"""Sidecar contract: never raises, always returns a dict."""
out = diagnose_authorship_warrant(
question_text=None,
answer_text="",
cited_evidence_spans=None,
cited_source_uris=None,
cited_source_titles=None,
)
assert isinstance(out, dict)
assert "tier" in out
assert "tier_rank" in out
def test_does_not_raise_on_garbage_input():
out = diagnose_authorship_warrant(
question_text="who wrote x?",
answer_text="\x00\x01" * 100,
cited_evidence_spans=["", None, "<<<>>>"], # type: ignore
)
assert out["tier"] in (
"NO_AUTHORSHIP_SIGNAL", "AUTHOR_SECONDARY_SOURCE",
"AUTHOR_PACKAGE_METADATA", "AUTHOR_REPOSITORY_OWNER",
"AUTHOR_PAGE_BYLINE", "AUTHOR_PRIMARY_PAGE_TITLE",
"AUTHOR_COPYRIGHT_FOOTER",
)
def test_noise_capitalized_words_not_classified_as_names():
"""`Author` / `Copyright` / etc. shouldn't slip through as names."""
out = diagnose_authorship_warrant(
question_text="who wrote x?",
answer_text="x",
cited_evidence_spans=[
"Copyright Reserved", # should NOT classify "Reserved" as name
"Author All Rights", # garbage shape
],
)
# Even if regex matches, _looks_like_noise filters; tier may fall
# to secondary or no-signal.
assert "Reserved" not in out.get("candidate_names", [])
assert "All" not in out.get("candidate_names", [])