CUST-WP-0061-T06: retire suggestions to read-only legacy
Founder-reviewed decision (WorkOrchestrationArchitectureDraft.md v0.2
section 8 item 6): the fresh intake work-record entity replaces
suggestions, not a rename-bridge. All 5 mutation endpoints (create, vet,
decline, promote, bump-relevance) now 410 with a pointer to POST
/intakes/ and the migration doc; GET/list stay live for the historical
record (10 rows migrated to file-backed intake records in the-custodian,
see that repo's intake-legacy-suggestions-migration.md and CUST-IN-0001
through CUST-IN-0010).
Removed dead code the retirement makes unreachable: Task/TaskPriority/
TaskStatus/normalize_task_status imports (only used by the deleted
promote body), the suggestion_relevance.bump_relevance import, and the
_ALLOWED_*_FROM stage-guard sets + _reject_stage helper (only used by
the deleted vet/decline/promote bodies). WSJF ranking (compute_wsjf,
cost_of_delay, suggestion_sort_key) stays -- still exercised by the
surviving GET /suggestions/?rank=wsjf read path.
MCP tool docstrings (create_suggestion, vet_suggestion,
decline_suggestion, promote_suggestion_to_task,
bump_suggestion_relevance) updated to point at the replacement
(create_intake/route_intake/close_intake) rather than silently 410ing
with no guidance.
tests/test_suggestions.py rewritten: verifies all 5 mutations 410,
GET/list still work for historical rows (seeded directly via the DB
session since creation is retired -- there's no other way to get
historical data into the table anymore), 404 still behaves normally on
unknown ids. Live-verified against the running dev API: POST 410s,
GET with include_terminal=true still returns all 10 migrated-and-declined
historical rows. No regressions: full repo suite green (563 tests).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 01:51:59 +02:00
|
|
|
"""Suggestion (legacy) tests — CUST-WP-0061-T06, 2026-07-21.
|
|
|
|
|
|
|
|
|
|
The suggestions table/router is retired to read-only legacy: a fresh
|
|
|
|
|
`intake` work-record entity replaces it (canon/standards/
|
|
|
|
|
work-record-types_v0.1.md), not a rename. The 10 records still open at
|
|
|
|
|
retirement were migrated to file-backed intake records (see
|
|
|
|
|
the-custodian/intake-legacy-suggestions-migration.md) and declined here.
|
|
|
|
|
|
|
|
|
|
These tests verify the retirement itself: every mutation endpoint 410s;
|
|
|
|
|
GET/list still works for the historical record. Since creation is
|
|
|
|
|
retired, historical rows are seeded directly via the ORM/DB session
|
|
|
|
|
(bypassing the retired POST) rather than through the API.
|
|
|
|
|
"""
|
2026-07-06 10:52:49 +02:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
CUST-WP-0061-T06: retire suggestions to read-only legacy
Founder-reviewed decision (WorkOrchestrationArchitectureDraft.md v0.2
section 8 item 6): the fresh intake work-record entity replaces
suggestions, not a rename-bridge. All 5 mutation endpoints (create, vet,
decline, promote, bump-relevance) now 410 with a pointer to POST
/intakes/ and the migration doc; GET/list stay live for the historical
record (10 rows migrated to file-backed intake records in the-custodian,
see that repo's intake-legacy-suggestions-migration.md and CUST-IN-0001
through CUST-IN-0010).
Removed dead code the retirement makes unreachable: Task/TaskPriority/
TaskStatus/normalize_task_status imports (only used by the deleted
promote body), the suggestion_relevance.bump_relevance import, and the
_ALLOWED_*_FROM stage-guard sets + _reject_stage helper (only used by
the deleted vet/decline/promote bodies). WSJF ranking (compute_wsjf,
cost_of_delay, suggestion_sort_key) stays -- still exercised by the
surviving GET /suggestions/?rank=wsjf read path.
MCP tool docstrings (create_suggestion, vet_suggestion,
decline_suggestion, promote_suggestion_to_task,
bump_suggestion_relevance) updated to point at the replacement
(create_intake/route_intake/close_intake) rather than silently 410ing
with no guidance.
tests/test_suggestions.py rewritten: verifies all 5 mutations 410,
GET/list still work for historical rows (seeded directly via the DB
session since creation is retired -- there's no other way to get
historical data into the table anymore), 404 still behaves normally on
unknown ids. Live-verified against the running dev API: POST 410s,
GET with include_terminal=true still returns all 10 migrated-and-declined
historical rows. No regressions: full repo suite green (563 tests).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 01:51:59 +02:00
|
|
|
import uuid
|
2026-07-06 10:52:49 +02:00
|
|
|
|
CUST-WP-0061-T06: retire suggestions to read-only legacy
Founder-reviewed decision (WorkOrchestrationArchitectureDraft.md v0.2
section 8 item 6): the fresh intake work-record entity replaces
suggestions, not a rename-bridge. All 5 mutation endpoints (create, vet,
decline, promote, bump-relevance) now 410 with a pointer to POST
/intakes/ and the migration doc; GET/list stay live for the historical
record (10 rows migrated to file-backed intake records in the-custodian,
see that repo's intake-legacy-suggestions-migration.md and CUST-IN-0001
through CUST-IN-0010).
Removed dead code the retirement makes unreachable: Task/TaskPriority/
TaskStatus/normalize_task_status imports (only used by the deleted
promote body), the suggestion_relevance.bump_relevance import, and the
_ALLOWED_*_FROM stage-guard sets + _reject_stage helper (only used by
the deleted vet/decline/promote bodies). WSJF ranking (compute_wsjf,
cost_of_delay, suggestion_sort_key) stays -- still exercised by the
surviving GET /suggestions/?rank=wsjf read path.
MCP tool docstrings (create_suggestion, vet_suggestion,
decline_suggestion, promote_suggestion_to_task,
bump_suggestion_relevance) updated to point at the replacement
(create_intake/route_intake/close_intake) rather than silently 410ing
with no guidance.
tests/test_suggestions.py rewritten: verifies all 5 mutations 410,
GET/list still work for historical rows (seeded directly via the DB
session since creation is retired -- there's no other way to get
historical data into the table anymore), 404 still behaves normally on
unknown ids. Live-verified against the running dev API: POST 410s,
GET with include_terminal=true still returns all 10 migrated-and-declined
historical rows. No regressions: full repo suite green (563 tests).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 01:51:59 +02:00
|
|
|
import pytest
|
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
|
|
|
|
|
|
|
|
|
|
from tests.test_capability_requests import _create_domain
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def _seed_suggestion(test_engine, *, domain_slug: str, **overrides) -> str:
|
|
|
|
|
"""Insert a Suggestion row directly, bypassing the retired POST
|
|
|
|
|
endpoint — this is now the only way historical rows get created."""
|
|
|
|
|
from api.models.domain import Domain
|
|
|
|
|
from api.models.suggestion import Suggestion
|
|
|
|
|
from sqlalchemy import select
|
|
|
|
|
|
|
|
|
|
factory = async_sessionmaker(test_engine, class_=AsyncSession, expire_on_commit=False)
|
|
|
|
|
async with factory() as session:
|
|
|
|
|
domain_id = (
|
|
|
|
|
await session.execute(select(Domain.id).where(Domain.slug == domain_slug))
|
|
|
|
|
).scalar_one()
|
|
|
|
|
suggestion = Suggestion(
|
|
|
|
|
domain_id=domain_id,
|
|
|
|
|
title=overrides.get("title", "Legacy suggestion"),
|
|
|
|
|
description=overrides.get("description"),
|
|
|
|
|
origin=overrides.get("origin"),
|
|
|
|
|
origin_ref=overrides.get("origin_ref"),
|
|
|
|
|
base_value=overrides.get("base_value", 3.0),
|
|
|
|
|
job_size=overrides.get("job_size", 3.0),
|
|
|
|
|
)
|
|
|
|
|
session.add(suggestion)
|
|
|
|
|
await session.commit()
|
|
|
|
|
await session.refresh(suggestion)
|
|
|
|
|
return str(suggestion.id)
|
2026-07-06 10:52:49 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
CUST-WP-0061-T06: retire suggestions to read-only legacy
Founder-reviewed decision (WorkOrchestrationArchitectureDraft.md v0.2
section 8 item 6): the fresh intake work-record entity replaces
suggestions, not a rename-bridge. All 5 mutation endpoints (create, vet,
decline, promote, bump-relevance) now 410 with a pointer to POST
/intakes/ and the migration doc; GET/list stay live for the historical
record (10 rows migrated to file-backed intake records in the-custodian,
see that repo's intake-legacy-suggestions-migration.md and CUST-IN-0001
through CUST-IN-0010).
Removed dead code the retirement makes unreachable: Task/TaskPriority/
TaskStatus/normalize_task_status imports (only used by the deleted
promote body), the suggestion_relevance.bump_relevance import, and the
_ALLOWED_*_FROM stage-guard sets + _reject_stage helper (only used by
the deleted vet/decline/promote bodies). WSJF ranking (compute_wsjf,
cost_of_delay, suggestion_sort_key) stays -- still exercised by the
surviving GET /suggestions/?rank=wsjf read path.
MCP tool docstrings (create_suggestion, vet_suggestion,
decline_suggestion, promote_suggestion_to_task,
bump_suggestion_relevance) updated to point at the replacement
(create_intake/route_intake/close_intake) rather than silently 410ing
with no guidance.
tests/test_suggestions.py rewritten: verifies all 5 mutations 410,
GET/list still work for historical rows (seeded directly via the DB
session since creation is retired -- there's no other way to get
historical data into the table anymore), 404 still behaves normally on
unknown ids. Live-verified against the running dev API: POST 410s,
GET with include_terminal=true still returns all 10 migrated-and-declined
historical rows. No regressions: full repo suite green (563 tests).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 01:51:59 +02:00
|
|
|
class TestSuggestionsRetired:
|
|
|
|
|
async def test_create_returns_410(self, client):
|
|
|
|
|
await _create_domain(client, "custodian", "Custodian")
|
|
|
|
|
r = await client.post("/suggestions/", json={
|
|
|
|
|
"domain": "custodian",
|
|
|
|
|
"title": "New suggestion attempt",
|
|
|
|
|
})
|
|
|
|
|
assert r.status_code == 410
|
|
|
|
|
assert "intake" in r.json()["detail"].lower()
|
|
|
|
|
|
|
|
|
|
async def test_vet_returns_410(self, client, test_engine):
|
|
|
|
|
await _create_domain(client, "custodian", "Custodian")
|
|
|
|
|
sid = await _seed_suggestion(test_engine, domain_slug="custodian")
|
|
|
|
|
r = await client.post(f"/suggestions/{sid}/vet", json={"note": "x"})
|
|
|
|
|
assert r.status_code == 410
|
|
|
|
|
|
|
|
|
|
async def test_decline_returns_410(self, client, test_engine):
|
|
|
|
|
await _create_domain(client, "custodian", "Custodian")
|
|
|
|
|
sid = await _seed_suggestion(test_engine, domain_slug="custodian")
|
|
|
|
|
r = await client.post(f"/suggestions/{sid}/decline", json={"note": "x"})
|
|
|
|
|
assert r.status_code == 410
|
|
|
|
|
|
|
|
|
|
async def test_promote_returns_410(self, client, test_engine):
|
|
|
|
|
await _create_domain(client, "custodian", "Custodian")
|
|
|
|
|
sid = await _seed_suggestion(test_engine, domain_slug="custodian")
|
|
|
|
|
r = await client.post(f"/suggestions/{sid}/promote", json={})
|
|
|
|
|
assert r.status_code == 410
|
|
|
|
|
|
|
|
|
|
async def test_bump_relevance_returns_410(self, client, test_engine):
|
|
|
|
|
await _create_domain(client, "custodian", "Custodian")
|
|
|
|
|
sid = await _seed_suggestion(test_engine, domain_slug="custodian")
|
|
|
|
|
r = await client.post(f"/suggestions/{sid}/bump-relevance", json={})
|
|
|
|
|
assert r.status_code == 410
|
|
|
|
|
|
2026-08-20 08:11:14 +02:00
|
|
|
async def test_reads_are_retired_too(self, client, test_engine):
|
|
|
|
|
"""Slice E1 (STATE-WP-0079-T05, 2026-08-20): reads were kept live only
|
|
|
|
|
to keep the historical backlog reachable. That history is archived at
|
|
|
|
|
the-custodian/docs/archived-suggestion-backlog.md, so the read surface
|
|
|
|
|
is retired as well."""
|
CUST-WP-0061-T06: retire suggestions to read-only legacy
Founder-reviewed decision (WorkOrchestrationArchitectureDraft.md v0.2
section 8 item 6): the fresh intake work-record entity replaces
suggestions, not a rename-bridge. All 5 mutation endpoints (create, vet,
decline, promote, bump-relevance) now 410 with a pointer to POST
/intakes/ and the migration doc; GET/list stay live for the historical
record (10 rows migrated to file-backed intake records in the-custodian,
see that repo's intake-legacy-suggestions-migration.md and CUST-IN-0001
through CUST-IN-0010).
Removed dead code the retirement makes unreachable: Task/TaskPriority/
TaskStatus/normalize_task_status imports (only used by the deleted
promote body), the suggestion_relevance.bump_relevance import, and the
_ALLOWED_*_FROM stage-guard sets + _reject_stage helper (only used by
the deleted vet/decline/promote bodies). WSJF ranking (compute_wsjf,
cost_of_delay, suggestion_sort_key) stays -- still exercised by the
surviving GET /suggestions/?rank=wsjf read path.
MCP tool docstrings (create_suggestion, vet_suggestion,
decline_suggestion, promote_suggestion_to_task,
bump_suggestion_relevance) updated to point at the replacement
(create_intake/route_intake/close_intake) rather than silently 410ing
with no guidance.
tests/test_suggestions.py rewritten: verifies all 5 mutations 410,
GET/list still work for historical rows (seeded directly via the DB
session since creation is retired -- there's no other way to get
historical data into the table anymore), 404 still behaves normally on
unknown ids. Live-verified against the running dev API: POST 410s,
GET with include_terminal=true still returns all 10 migrated-and-declined
historical rows. No regressions: full repo suite green (563 tests).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 01:51:59 +02:00
|
|
|
await _create_domain(client, "custodian", "Custodian")
|
|
|
|
|
sid = await _seed_suggestion(
|
|
|
|
|
test_engine, domain_slug="custodian",
|
|
|
|
|
title="Historical gated-need signal",
|
|
|
|
|
origin="WARDEN-WP-0012", origin_ref="example",
|
|
|
|
|
)
|
|
|
|
|
|
2026-08-20 08:11:14 +02:00
|
|
|
for path in (f"/suggestions/{sid}", "/suggestions/", f"/suggestions/{sid}/notes"):
|
|
|
|
|
r = await client.get(path)
|
|
|
|
|
assert r.status_code == 410, path
|
CUST-WP-0061-T06: retire suggestions to read-only legacy
Founder-reviewed decision (WorkOrchestrationArchitectureDraft.md v0.2
section 8 item 6): the fresh intake work-record entity replaces
suggestions, not a rename-bridge. All 5 mutation endpoints (create, vet,
decline, promote, bump-relevance) now 410 with a pointer to POST
/intakes/ and the migration doc; GET/list stay live for the historical
record (10 rows migrated to file-backed intake records in the-custodian,
see that repo's intake-legacy-suggestions-migration.md and CUST-IN-0001
through CUST-IN-0010).
Removed dead code the retirement makes unreachable: Task/TaskPriority/
TaskStatus/normalize_task_status imports (only used by the deleted
promote body), the suggestion_relevance.bump_relevance import, and the
_ALLOWED_*_FROM stage-guard sets + _reject_stage helper (only used by
the deleted vet/decline/promote bodies). WSJF ranking (compute_wsjf,
cost_of_delay, suggestion_sort_key) stays -- still exercised by the
surviving GET /suggestions/?rank=wsjf read path.
MCP tool docstrings (create_suggestion, vet_suggestion,
decline_suggestion, promote_suggestion_to_task,
bump_suggestion_relevance) updated to point at the replacement
(create_intake/route_intake/close_intake) rather than silently 410ing
with no guidance.
tests/test_suggestions.py rewritten: verifies all 5 mutations 410,
GET/list still work for historical rows (seeded directly via the DB
session since creation is retired -- there's no other way to get
historical data into the table anymore), 404 still behaves normally on
unknown ids. Live-verified against the running dev API: POST 410s,
GET with include_terminal=true still returns all 10 migrated-and-declined
historical rows. No regressions: full repo suite green (563 tests).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 01:51:59 +02:00
|
|
|
|
2026-08-20 08:11:14 +02:00
|
|
|
async def test_unknown_id_also_410s(self, client):
|
|
|
|
|
"""The whole prefix is retired, so there is nothing left to 404 on."""
|
CUST-WP-0061-T06: retire suggestions to read-only legacy
Founder-reviewed decision (WorkOrchestrationArchitectureDraft.md v0.2
section 8 item 6): the fresh intake work-record entity replaces
suggestions, not a rename-bridge. All 5 mutation endpoints (create, vet,
decline, promote, bump-relevance) now 410 with a pointer to POST
/intakes/ and the migration doc; GET/list stay live for the historical
record (10 rows migrated to file-backed intake records in the-custodian,
see that repo's intake-legacy-suggestions-migration.md and CUST-IN-0001
through CUST-IN-0010).
Removed dead code the retirement makes unreachable: Task/TaskPriority/
TaskStatus/normalize_task_status imports (only used by the deleted
promote body), the suggestion_relevance.bump_relevance import, and the
_ALLOWED_*_FROM stage-guard sets + _reject_stage helper (only used by
the deleted vet/decline/promote bodies). WSJF ranking (compute_wsjf,
cost_of_delay, suggestion_sort_key) stays -- still exercised by the
surviving GET /suggestions/?rank=wsjf read path.
MCP tool docstrings (create_suggestion, vet_suggestion,
decline_suggestion, promote_suggestion_to_task,
bump_suggestion_relevance) updated to point at the replacement
(create_intake/route_intake/close_intake) rather than silently 410ing
with no guidance.
tests/test_suggestions.py rewritten: verifies all 5 mutations 410,
GET/list still work for historical rows (seeded directly via the DB
session since creation is retired -- there's no other way to get
historical data into the table anymore), 404 still behaves normally on
unknown ids. Live-verified against the running dev API: POST 410s,
GET with include_terminal=true still returns all 10 migrated-and-declined
historical rows. No regressions: full repo suite green (563 tests).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 01:51:59 +02:00
|
|
|
r = await client.get(f"/suggestions/{uuid.uuid4()}")
|
2026-08-20 08:11:14 +02:00
|
|
|
assert r.status_code == 410
|
|
|
|
|
|
|
|
|
|
async def test_retirement_detail_points_at_intake_and_archive(self, client):
|
|
|
|
|
r = await client.get("/suggestions/")
|
|
|
|
|
detail = r.json()["detail"]
|
|
|
|
|
assert "POST /intakes/" in detail
|
|
|
|
|
assert "archived-suggestion-backlog.md" in detail
|