CI & Build / Plugin hooks (push) Successful in 15s
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 1m0s
CI & Build / integration (push) Successful in 1m8s
CI & Build / Python tests (push) Failing after 1m28s
CI & Build / Build & push image (push) Skipped
- services/family_adoption.py: assess one project against one canon idea by the four outcomes in order (exempt, variant, adopted, owed). Every outcome needs a reason and adopted needs evidence. The engine records the precedents itself: this idea's answers elsewhere, and this project's answers to the nearest ideas. The same answer given twice records nothing. - owed files a task in the OWING project, tagged to the System matching the idea's canonical area, naming the gap and the reference for that project's language. The task follows the answer: adopted closes it, exempt or variant cancels it, owed again reopens it. Each move is logged on the task. - the conflict order is enforced: every ground above the deciding one must say why it did not decide. The losing side is folded into the idea's note as a trap, an alternative or a condition branch, the version moves, and both rows are answered against the revision. - recheck is derived (row version != idea version). family.revise moves the version when substance changes. undo covers a project's latest answer too. - set_family_references names the reference implementations. - MCP: get/list/assess adoption, resolve_family_conflict, revise_family_idea, set_family_references. Web: GET /api/family/matrix. - UI: an adoption matrix on /family (platform filter, cell detail with reason, recheck and owed-task link) and the same matrix narrowed to one project on its Family tab. The decision log now reads project-level decisions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
38 lines
1.6 KiB
Python
38 lines
1.6 KiB
Python
"""Structural tests for the family blueprint (milestone 463 steps 3 and 4) — every
|
|
endpoint is routed, and every write hands the service its caller, where the
|
|
note's write gate lives. What the writes do is in
|
|
tests/test_integration_family_promotion.py."""
|
|
import inspect
|
|
|
|
|
|
def test_family_blueprint_routes_every_endpoint():
|
|
from scribe.app import create_app
|
|
app = create_app()
|
|
assert "family" in app.blueprints
|
|
rules = {(r.rule, m) for r in app.url_map.iter_rules() for m in r.methods}
|
|
for rule, method in (
|
|
("/api/family/ideas", "GET"),
|
|
("/api/family/ideas/<int:note_id>", "GET"),
|
|
("/api/family/ideas/<int:note_id>/propose", "POST"),
|
|
("/api/family/ideas/<int:note_id>/promote", "POST"),
|
|
("/api/family/ideas/<int:note_id>/retire", "POST"),
|
|
("/api/family/decisions", "GET"),
|
|
("/api/family/decisions/<int:decision_id>/undo", "POST"),
|
|
("/api/family/matrix", "GET"),
|
|
):
|
|
assert (rule, method) in rules, f"{method} {rule} is not routed"
|
|
|
|
|
|
def test_every_engine_write_takes_the_caller_and_who_decided():
|
|
from scribe.services import family as svc
|
|
for name in ("propose", "promote", "retire", "undo", "revise"):
|
|
params = inspect.signature(getattr(svc, name)).parameters
|
|
assert "user_id" in params and "decided_via" in params, name
|
|
|
|
|
|
def test_every_ledger_write_takes_the_caller_and_who_decided():
|
|
from scribe.services import family_adoption as svc
|
|
for name in ("assess", "resolve_conflict", "undo_assessment"):
|
|
params = inspect.signature(getattr(svc, name)).parameters
|
|
assert "user_id" in params and "decided_via" in params, name
|