feat: FC links a conclusive pair itself instead of asking (4392)
CI and images / lint (push) Failing after 2s
CI and images / extension-version (push) Successful in 2s
CI and images / frontend-build (push) Successful in 20s
CI and images / backend-lint-and-test (push) Successful in 31s
CI and images / integration (push) Failing after 2m15s
CI and images / sign-extension (push) Skipped
CI and images / build-web (push) Skipped
CI and images / smoke-web (push) Skipped
CI and images / promote (push) Skipped
CI and images / build-agent (push) Skipped

Operator, 2026-09-24: *"I don't want this to be manual that defeats the
convenience that I'm going for."*

Confirm-only was right while every signal was circumstantial. Time proximity
and a body that mentions Discord can never be more than suggestive, so asking
was the honest response to what FC actually knew. A shared working name is
different in kind: when the creator's own name for a piece appears in exactly
these two posts and nowhere else in their library, there is nothing left for
the operator to adjudicate, and asking is a chore FC invented for them.

AUTO_LINK_FLOOR is 1.0 and sits deliberately above IDENTITY_FLOOR's 0.75. The
gap between them IS the review queue — real evidence, not certain enough for
FC to act on alone. Measured on artist 8, of 15 name-sharing pairs: 11 are
conclusive, 2 more propose, 2 fall short of both.

Three refusals, because an auto-link is FC asserting something the operator
never saw:

  * Exactly one candidate may be conclusive. Two is not a tie to be broken by
    score — one post can tease two pieces dropped separately, and then each
    drop carries a different name from the same teaser, each individually
    conclusive. Two conclusive answers to "which drop is this" means the
    question was wrong, so both queue and FC says nothing.
  * Neither end may already be claimed by an accepted link. That link is the
    operator's decision and reassigning its other end would overrule them
    silently.
  * The whole thing is one setting, defaulting on, reversible in the UI —
    an accepted link is a row they can dismiss.

`match_post` now returns (proposed, linked) so a sweep can report what it did
on its own rather than only what it queued.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVjrnpQjRgHdvq95rASoiR
This commit is contained in:
2026-09-24 08:14:04 -04:00
co-authored by Claude Opus 5
parent 81f23991e9
commit 31de54e326
7 changed files with 369 additions and 28 deletions
+32
View File
@@ -103,3 +103,35 @@ async def test_a_negative_window_is_refused(client):
"/api/settings/import", json={"download_revisit_days": -1}
)
assert resp.status_code == 400
# --- the announcement matcher's own switch ---------------------------------
@pytest.mark.asyncio
async def test_auto_linking_is_on_by_default(client):
"""Operator, 2026-09-24: "I don't want this to be manual that defeats the
convenience that I'm going for." Defaulting ON is the change of posture
that request asks for, so the default is what this pins."""
resp = await client.get("/api/settings/import")
assert resp.status_code == 200
assert (await resp.get_json())["discord_link_auto"] is True
@pytest.mark.asyncio
async def test_auto_linking_can_be_turned_off(client):
"""Confirm-only has to stay reachable — linking without asking is a change
of posture, and not everyone wants it."""
resp = await client.patch(
"/api/settings/import", json={"discord_link_auto": False}
)
assert resp.status_code == 200
assert (await resp.get_json())["discord_link_auto"] is False
@pytest.mark.asyncio
async def test_auto_linking_refuses_a_non_boolean(client):
resp = await client.patch(
"/api/settings/import", json={"discord_link_auto": "yes"}
)
assert resp.status_code == 400