feat(ledger): uses edges — consumption is its own relation, conformance keeps one snippet_id (#2870, milestone 294)
CI & Build / Python lint (push) Successful in 5s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / integration (push) Failing after 29s
CI & Build / TypeScript typecheck (push) Successful in 36s
CI & Build / Python tests (push) Failing after 39s
CI & Build / Build & push image (push) Skipped
CI & Build / Python lint (push) Successful in 5s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / integration (push) Failing after 29s
CI & Build / TypeScript typecheck (push) Successful in 36s
CI & Build / Python tests (push) Failing after 39s
CI & Build / Build & push image (push) Skipped
A shape can follow one convention canon AND call several helper canons; the row's single snippet_id made the 2026-08 audit pick (hash_token won, the service-function convention lost), and hook evidence — pulled a snippet, then wrote code naming it — was stamped as instance when it is a uses fact. - code_shape_uses (migration 0084): shape → snippet, basis, evidence; unique per pair; cascades with both ends. USE_BASES: reference | hook | agent | audit | import. A judgment-grade basis overwrites a mechanical one, never the reverse. - classify_shapes items and classify_shapes_by_rule take uses=[snippet ids] (targets validated like snippet_id; all-or-nothing). - The write-path hook writes a uses edge for every pulled canon the payload names (the instance stamp is unchanged); the proposer writes a uses edge for every canon a body names (reference_canons: kind + language family + stoplist, same rules as the reference basis) — the mechanical form of "auto-confirm own-import references" deferred from #2871. - list_shapes(uses=N) lists the consumers of a canon; get_snippet's consumer map gains `uses` beside instances/variants. Operator decision on #2870 (2026-08-21): keep one snippet_id, add uses edges. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -391,6 +391,35 @@ def test_compact_row_carries_identity_standing_and_the_proposers_word_only():
|
||||
assert noisy not in compact
|
||||
|
||||
|
||||
def test_uses_edges_table_and_validation():
|
||||
"""#2870: consumption is its own relation — a table that cascades with
|
||||
both ends, and `uses` on a classification must be a list of ids."""
|
||||
from scribe.models import Base
|
||||
from scribe.models.code_shape import USE_BASES, CodeShapeUse
|
||||
from scribe.services.shape_ledger import validate_classifications
|
||||
assert "code_shape_uses" in Base.metadata.tables
|
||||
cols = CodeShapeUse.__table__.c
|
||||
assert next(iter(cols.shape_id.foreign_keys)).ondelete == "CASCADE"
|
||||
assert next(iter(cols.snippet_id.foreign_keys)).ondelete == "CASCADE"
|
||||
assert set(USE_BASES) == {"reference", "hook", "agent", "audit", "import"}
|
||||
ok = [{"path": "a.py", "symbol": "f", "status": "instance", "snippet_id": 9, "uses": [3, 4]}]
|
||||
assert validate_classifications(ok) is None
|
||||
bad = [{"path": "a.py", "symbol": "f", "status": "instance", "snippet_id": 9, "uses": "3"}]
|
||||
assert "uses must be a list" in validate_classifications(bad)
|
||||
|
||||
|
||||
def test_reference_canons_names_every_used_canon_not_just_the_best():
|
||||
from scribe.services.shape_ledger import Canon, _norm_text, reference_canons
|
||||
a = Canon(1, "sym", "hash_token", (("src/x.py", "hash_token"),), "def hash_token(raw):", _norm_text("x"), 2, "python")
|
||||
b = Canon(2, "sym", "rules_payload", (("src/y.py", "rules_payload"),), "def rules_payload(r):", _norm_text("y"), 2, "python")
|
||||
ts = Canon(3, "sym", "fmtDate", (("f/d.ts", "fmtDate"),), "export function fmtDate(iso: string): string {", _norm_text("z"), 2, "typescript")
|
||||
body = "def create_invitation(email):\n h = hash_token(raw)\n return rules_payload(h)\n"
|
||||
assert reference_canons("sym", "src/scribe/services/auth.py", "create_invitation", body, [a, b, ts]) == [1, 2]
|
||||
# the shape's own name and the other language family are never "uses"
|
||||
assert reference_canons("sym", "src/x.py", "hash_token", body, [a]) == []
|
||||
assert reference_canons("sym", "f/v.vue", "show", "fmtDate(x); hash_token(y)", [a, ts]) == [3]
|
||||
|
||||
|
||||
def test_reason_codes_are_a_fixed_catalogue_and_validated():
|
||||
"""#2874: an optional index beside the prose reason; unknown codes are a
|
||||
structural error (the batch applies nothing)."""
|
||||
|
||||
Reference in New Issue
Block a user