fix(ledger): one-line CSS rules fingerprint their own declarations; the proposer writes uses edges for judged rows too (#2872, #2870)
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / integration (push) Successful in 25s
CI & Build / TypeScript typecheck (push) Successful in 32s
CI & Build / Python tests (push) Successful in 59s
CI & Build / Build & push image (push) Successful in 22s
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / integration (push) Successful in 25s
CI & Build / TypeScript typecheck (push) Successful in 32s
CI & Build / Python tests (push) Successful in 59s
CI & Build / Build & push image (push) Successful in 22s
First deploy of v26.08.21.1 showed two gaps:
- Every one-line CSS rule followed by a blank line hashed to sha1("") — the
declarations live on the selector line, which the #2872 "declarations only"
fingerprint dropped — so 68 unrelated one-liners across 17 files read as
one body-identical copy at the top of the derive readout. The selector
line's tail after "{" is now part of the hash; an all-blank remainder falls
back to the whole block.
- The proposer only examined unjudged rows, so consumers that were already
classified (auth.create_invitation → hash_token) never got a uses edge: 3
edges for hash_token after the first refresh. Judged rows are now scanned
for references (once per body), no proposal is made on them.
- 0084 migration docstring reworded: "function that …" at a line start parsed
as a definition (extractor false positive).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -230,6 +230,19 @@ async def test_uses_edges_are_the_consumer_map(seeded):
|
||||
)
|
||||
assert out["classified"] == 1
|
||||
assert (await list_project_shapes(owner, pid, uses=hid))[1] == 2
|
||||
# The proposer writes uses edges for JUDGED rows too: Config (exempt)
|
||||
# names hash_token in its body → an edge, no proposal.
|
||||
from scribe.services.shape_ledger import propose_for_repo
|
||||
await classify_shapes(owner, pid, [
|
||||
{"path": "src/app.py", "symbol": "Config", "status": "exempt", "reason": "settings"},
|
||||
])
|
||||
defs = _defs(("src/app.py", "sym", "Config", "class Config:", "class Config:\n token = hash_token(raw)\n"))
|
||||
with _quiet_semantic():
|
||||
await propose_for_repo(owner, pid, REPO, defs)
|
||||
rows, total = await list_project_shapes(owner, pid, uses=hid)
|
||||
assert total == 3 and {r.symbol for r in rows} >= {"Config"}
|
||||
cfg = next(r for r in rows if r.symbol == "Config")
|
||||
assert cfg.status == "exempt" and cfg.proposal is None
|
||||
with pytest.raises(ValueError):
|
||||
await classify_shapes(owner, pid, [
|
||||
{"path": "src/app.py", "symbol": "Config", "status": "exempt", "reason": "x", "uses": [999999]},
|
||||
|
||||
@@ -481,6 +481,13 @@ def test_extract_definitions_fingerprints_each_block():
|
||||
css = ".closed-msg {\n text-align: center;\n padding: 0.5rem 0;\n}\n.error-block {\n text-align: center;\n padding: 0.5rem 0;\n}\n.other {\n text-align: left;\n}\n"
|
||||
d = {x.name: x for x in extract_definitions(css)}
|
||||
assert d["closed-msg"].body_sha == d["error-block"].body_sha != d["other"].body_sha
|
||||
# One-line rules hash their own declarations — never the empty string
|
||||
# (first deploy grouped 68 unrelated one-liners as one copy).
|
||||
one = ".a { color: red; }\n\n.b { color: red; }\n\n.c { color: blue; }\n\n.d {\n color: red;\n}\n"
|
||||
e = {x.name: x for x in extract_definitions(one)}
|
||||
import hashlib
|
||||
assert e["a"].body_sha == e["b"].body_sha != e["c"].body_sha
|
||||
assert e["a"].body_sha != hashlib.sha1(b"").hexdigest()[:16]
|
||||
|
||||
|
||||
def test_coverage_line_names_the_proposers_standing():
|
||||
|
||||
Reference in New Issue
Block a user