feat(scaffolding): affected-surface propagation + requirement completeness gate + impact-aware contract generation (SCAFFOLD-TRANCHE-005)
Origin: maintenance:contract-gen-affected-surface (opened by
DEPLOY-MONOTONIC-001's honest closure — planned work appeared as
genuinely_unexpected because generate_contract dropped the Need's
affected surface).
- construction_decision.py:
* classify_surface/resolve_surface/resolve_affected_surface:
open-world surface classification (structural entity / source path /
workflow config / ui component / layout region / unresolved — never
dropped) with deterministic resolution against authorities via
identity, recorded path attributes, and Layout Contract bindings
* check_completeness: bounded structure-grounded gate BEFORE fit —
affected-surface entities contribute their IMPLEMENTED contracts to
the material scope; unrepresented ones = MATERIAL_GAP with full
provenance (derived_from/rule/evidence) + concrete probe questions.
Verdicts COMPLETE_ENOUGH/MATERIAL_GAP/UNKNOWN; UNKNOWN never
silently COMPLETE. PREMATURE-FIT GUARD: a concrete verdict
(REUSE/SATISFIED/COMPOSE/ADAPT_EXISTING) on a materially incomplete
requirement is downgraded to INSUFFICIENT_EVIDENCE with the
would-have-been verdict retained — the fit engine was right GIVEN
its requirement; the requirement was too narrow
* record_proposed_requirement: model-proposed requirements are
PENDING evidence, never structural authority by themselves
* iterative decision trace: prior_iterations retained with why-it-
changed provenance; final resolution stays singular
* test-surface derivation: tests/test_<name>.py in an affected
surface derives the test:<name> expected-new entity (Rune path
convention; structural consequence, provenance recorded)
* impact-aware generate_contract: distinct affected_paths /
affected_ui_surfaces / affected_regions / workflow_config_surfaces /
unresolved_surfaces / expected_runtime_effects surfaces; file scope
GENERATED from affected paths + selected impl paths + expected-new
paths, caller additions provenance-tracked; file scope != structural
impact != expected new != runtime effect
- scaffold.py: closure classifies expected RUNTIME effects (an observed
entity matching a predicted effect phrase = expected, not unexpected;
prediction still cannot self-certify — independent evidence required);
legacy contracts unaffected (empty lists, no crash)
REAL PROOFS: §23 read-only replay of the deploy incident — ONE pass
identifies MATERIAL_GAP (contract:monotonic_promotion on
module:deploy_stage), blocks the premature REUSE automatically, emits a
concrete probe question; §22 live proof CONTRACT-GEN-REPAIR-001
generated through the improved path with all surfaces propagated,
regeneration proving the planned test surface is now expected-new.
Tests: 28 new (surface/completeness/trace/runtime/compat plans §24-§28);
suite 1525 green; henxels rc=0; pyright clean.
This commit is contained in:
1 parent
6bc31c513e
commit
535c290917
5 files changed
+7212
-149
No files matched your search
@@ -119,6 +119,293 @@ def normalize_need(need: Need) -> tuple[cf.CapabilityRequirement, list[str]]:
|
||||
return req, missing
|
||||
|
||||
|
||||
# ── affected-surface classification + resolution (Tranche 5 §3-5) ──────────
|
||||
|
||||
_SURFACE_UI = "ui:"
|
||||
_SURFACE_REGION = "region:"
|
||||
_SURFACE_CONTRACT = "contract:"
|
||||
_SURFACE_MODULE = "module:"
|
||||
|
||||
|
||||
def classify_surface(surface: str) -> str:
|
||||
"""Classify one affected-surface entry (open-world; Tranche 5 §3).
|
||||
Kinds: structural_entity | source_path | workflow_config | ui_component |
|
||||
layout_region | unresolved. Never drops an entry — unresolved is a
|
||||
first-class kind."""
|
||||
if surface.startswith((_SURFACE_UI,)):
|
||||
return "ui_component"
|
||||
if surface.startswith((_SURFACE_REGION,)):
|
||||
return "layout_region"
|
||||
if surface.startswith(
|
||||
(
|
||||
_SURFACE_CONTRACT,
|
||||
_SURFACE_MODULE,
|
||||
_SURFACE_CAPABILITY,
|
||||
)
|
||||
):
|
||||
return "structural_entity"
|
||||
if surface.endswith(".yml") or surface.endswith(".yaml") or surface.endswith(".json") or surface.endswith(".toml"):
|
||||
return "workflow_config"
|
||||
if (
|
||||
surface.endswith(".py")
|
||||
or "/" in surface
|
||||
or surface.endswith(".md")
|
||||
or surface.endswith(".rs")
|
||||
or surface.endswith(".ts")
|
||||
):
|
||||
return "source_path"
|
||||
return "unresolved"
|
||||
|
||||
|
||||
_SURFACE_CAPABILITY = "capability:"
|
||||
|
||||
|
||||
def resolve_surface(surface: str, kind: str, authorities: dict[str, dict]) -> dict:
|
||||
"""Deterministically resolve one surface against the structural
|
||||
authorities (Tranche 5 §5). No Memory, no LLM archaeology: identity
|
||||
lookups and recorded path->module mappings only.
|
||||
|
||||
Returns {surface, kind, resolved: [...], unresolved_reason: ""}.
|
||||
resolved entries: {entity_id, authority, via} where via records HOW the
|
||||
mapping was established (identity | path_attribute | provides)."""
|
||||
out = {"surface": surface, "kind": kind, "resolved": [], "unresolved_reason": ""}
|
||||
if kind == "structural_entity":
|
||||
for name, doc in authorities.items():
|
||||
e = ss_entity(doc, surface)
|
||||
if e is not None:
|
||||
out["resolved"].append({"entity_id": surface, "authority": name, "via": "identity"})
|
||||
break
|
||||
if not out["resolved"]:
|
||||
out["unresolved_reason"] = "entity id not found in any authority"
|
||||
elif kind == "source_path":
|
||||
# path -> module via recorded identities.path in any authority
|
||||
for name, doc in authorities.items():
|
||||
for e in doc["entities"]:
|
||||
p = e.get("identities", {}).get("path", "")
|
||||
if p and (p == surface or p.endswith("/" + surface) or surface.endswith(p)):
|
||||
out["resolved"].append({"entity_id": e["id"], "authority": name, "via": "path_attribute"})
|
||||
if not out["resolved"]:
|
||||
out["unresolved_reason"] = "no authority records this path as an entity (path remains file-scope only)"
|
||||
elif kind == "ui_component":
|
||||
for name, doc in authorities.items():
|
||||
cid = surface if surface.startswith("ui:") else f"ui:{surface}"
|
||||
e = ss_entity(doc, f"layout:{cid}")
|
||||
if e is not None:
|
||||
out["resolved"].append({"entity_id": f"layout:{cid}", "authority": name, "via": "layout_contract"})
|
||||
if not out["resolved"]:
|
||||
out["unresolved_reason"] = "no Layout Contract bound for component"
|
||||
elif kind == "layout_region":
|
||||
for name, doc in authorities.items():
|
||||
e = ss_entity(doc, surface if surface.startswith("region:") else f"region:{surface}")
|
||||
if e is not None:
|
||||
out["resolved"].append({"entity_id": e["id"], "authority": name, "via": "identity"})
|
||||
if not out["resolved"]:
|
||||
out["unresolved_reason"] = "region not defined in any authority"
|
||||
else:
|
||||
out["unresolved_reason"] = f"kind {kind} has no deterministic resolver"
|
||||
return out
|
||||
|
||||
|
||||
def ss_entity(doc: dict, eid: str):
|
||||
from rune.structural_store import entity as _e
|
||||
|
||||
return _e(doc, eid)
|
||||
|
||||
|
||||
def implemented_contracts(doc: dict, entity_id: str) -> list[str]:
|
||||
"""Contracts an entity implements/exposes/provides (structural fact)."""
|
||||
out = []
|
||||
for r in relationships(doc, from_id=entity_id):
|
||||
if r["type"] in {"implements", "exposes", "provides", "serves"}:
|
||||
out.append(r["to"])
|
||||
return sorted(out)
|
||||
|
||||
|
||||
def resolve_affected_surface(need: Need, authorities: dict[str, dict]) -> dict:
|
||||
"""Classify + resolve the WHOLE affected surface (Tranche 5 §3-5, §13).
|
||||
Produces the propagated representation the generated contract carries:
|
||||
affected_existing_entities structural ids (resolved)
|
||||
affected_paths file-scope entries (mechanical scope)
|
||||
affected_ui_surfaces ui components / regions found
|
||||
unresolved_surfaces retained, never dropped
|
||||
File scope != structural impact: a path stays a path for scope checking
|
||||
even when it also resolves to a module entity (no authority duplication)."""
|
||||
result = {
|
||||
"affected_existing_entities": [],
|
||||
"affected_paths": [],
|
||||
"affected_ui_surfaces": [],
|
||||
"affected_regions": [],
|
||||
"workflow_config_surfaces": [],
|
||||
"unresolved_surfaces": [],
|
||||
"resolutions": [],
|
||||
}
|
||||
for surface in need.affected_surface:
|
||||
kind = classify_surface(surface)
|
||||
res = resolve_surface(surface, kind, authorities)
|
||||
result["resolutions"].append(res)
|
||||
if kind == "source_path":
|
||||
result["affected_paths"].append(surface)
|
||||
elif kind == "workflow_config":
|
||||
result["workflow_config_surfaces"].append(surface)
|
||||
elif kind == "ui_component":
|
||||
result["affected_ui_surfaces"].append(surface)
|
||||
elif kind == "layout_region":
|
||||
result["affected_regions"].append(surface)
|
||||
for r in res["resolved"]:
|
||||
eid = r["entity_id"]
|
||||
if eid not in result["affected_existing_entities"] and not eid.startswith("layout:"):
|
||||
result["affected_existing_entities"].append(eid)
|
||||
if res["unresolved_reason"] and not res["resolved"]:
|
||||
result["unresolved_surfaces"].append({"surface": surface, "kind": kind, "reason": res["unresolved_reason"]})
|
||||
for k in (
|
||||
"affected_existing_entities",
|
||||
"affected_paths",
|
||||
"affected_ui_surfaces",
|
||||
"affected_regions",
|
||||
"workflow_config_surfaces",
|
||||
):
|
||||
result[k] = sorted(set(result[k]))
|
||||
|
||||
# §9/§17 DERIVED EXPECTATION (structural consequence, not invention): a
|
||||
# Rune test path (tests/test_<name>.py) affected by the work implies the
|
||||
# "test:<name>" surface participates in the build. Derived only when not
|
||||
# already an existing entity nor explicitly declared; provenance records
|
||||
# the derivation for closure accounting.
|
||||
derived = []
|
||||
for surface in need.affected_surface:
|
||||
if surface.startswith("tests/test_") and surface.endswith(".py"):
|
||||
stem = surface[len("tests/test_") : -len(".py")]
|
||||
test_entity = f"test:{stem}"
|
||||
exists = any(ss_entity(doc, test_entity) is not None for doc in authorities.values())
|
||||
if test_entity not in need.constraints.get("expected_new_entities", []) and not exists:
|
||||
derived.append(
|
||||
{
|
||||
"entity": test_entity,
|
||||
"derived_from": surface,
|
||||
"rule": "Rune test-path convention: tests/test_<name>.py implies "
|
||||
"the test:<name> surface participates in the build",
|
||||
"evidence": f"affected_surface entry {surface}",
|
||||
}
|
||||
)
|
||||
result["derived_expected_new"] = derived
|
||||
return result
|
||||
|
||||
|
||||
# ── requirement completeness gate (Tranche 5 §6-9) ──────────────────────────
|
||||
|
||||
COMPLETE_ENOUGH = "COMPLETE_ENOUGH"
|
||||
MATERIAL_GAP = "MATERIAL_GAP"
|
||||
COMPLETENESS_UNKNOWN = "UNKNOWN"
|
||||
|
||||
|
||||
def check_completeness(
|
||||
req: cf.CapabilityRequirement, need: Need, authorities: dict[str, dict], surface_result: dict
|
||||
) -> dict:
|
||||
"""Bounded, structure-grounded completeness check run BEFORE fit.
|
||||
|
||||
Rule (deterministic, no model vibes): every affected-surface entry that
|
||||
resolves to a structural entity contributes that entity's IMPLEMENTED
|
||||
contracts to the requirement's material scope. If an affected entity
|
||||
implements contracts NOT represented in the normalized requirement, the
|
||||
requirement is materially incomplete — the need touches a component
|
||||
whose obligations it has not stated. That is exactly the deploy-incident
|
||||
shape: the affected promotion implementation carried an unrepresented
|
||||
ordering contract while the requirement named only qualification.
|
||||
|
||||
Verdicts (Tranche 5 §8):
|
||||
COMPLETE_ENOUGH all affected entities' contracts are represented
|
||||
(or none implement contracts) — fit may proceed
|
||||
MATERIAL_GAP uncovered implemented contracts exist on affected
|
||||
entities — named with provenance, routed to probe
|
||||
UNKNOWN no surface resolved — completeness undeterminable;
|
||||
never silently COMPLETE (§8)
|
||||
|
||||
Derived requirements (§9) are structural consequences, not inventions:
|
||||
each uncovered contract carries derived_from (the entity that
|
||||
implements it) + evidence (the implements relationship) + rule.
|
||||
"""
|
||||
gaps = []
|
||||
resolved_any = False
|
||||
for res in surface_result["resolutions"]:
|
||||
for r in res["resolved"]:
|
||||
resolved_any = True
|
||||
for name, doc in authorities.items():
|
||||
e = ss_entity(doc, r["entity_id"])
|
||||
if e is None:
|
||||
continue
|
||||
for contract in implemented_contracts(doc, r["entity_id"]):
|
||||
if contract not in req.contracts:
|
||||
gaps.append(
|
||||
{
|
||||
"missing_contract": contract,
|
||||
"derived_from": r["entity_id"],
|
||||
"surface": res["surface"],
|
||||
"authority": name,
|
||||
"rule": "affected-surface entity implements a "
|
||||
"contract not represented in the "
|
||||
"normalized requirement",
|
||||
"evidence": f"{r['entity_id']} implements {contract} (authority: {name})",
|
||||
}
|
||||
)
|
||||
break # entity found in one authority is enough
|
||||
if gaps:
|
||||
# dedupe by (missing_contract, derived_from)
|
||||
seen = set()
|
||||
unique = []
|
||||
for g in gaps:
|
||||
key = (g["missing_contract"], g["derived_from"])
|
||||
if key not in seen:
|
||||
seen.add(key)
|
||||
unique.append(g)
|
||||
return {
|
||||
"verdict": MATERIAL_GAP,
|
||||
"gaps": sorted(unique, key=lambda g: (g["missing_contract"], g["derived_from"])),
|
||||
"probe_questions": [
|
||||
f"does the need's stated failure implicate {g['derived_from']}'s {g['missing_contract']} obligation?"
|
||||
for g in unique[:5]
|
||||
],
|
||||
"note": "MATERIAL_GAP: affected surface carries obligations "
|
||||
"the requirement does not represent — refine before "
|
||||
"trusting an apparently-complete fit",
|
||||
}
|
||||
if resolved_any:
|
||||
return {
|
||||
"verdict": COMPLETE_ENOUGH,
|
||||
"gaps": [],
|
||||
"probe_questions": [],
|
||||
"note": "all resolved affected entities' implemented contracts are represented in the requirement",
|
||||
}
|
||||
return {
|
||||
"verdict": COMPLETENESS_UNKNOWN,
|
||||
"gaps": [],
|
||||
"probe_questions": [f"which structural entities does '{s}' map to?" for s in need.affected_surface[:5]],
|
||||
"note": "no affected surface resolved — completeness undeterminable; UNKNOWN never silently becomes COMPLETE",
|
||||
}
|
||||
|
||||
|
||||
def record_proposed_requirement(
|
||||
contract_id: str, *, provider: str, model: str, evidence: list[str], confidence: float
|
||||
) -> dict:
|
||||
"""Record a MODEL-PROPOSED requirement addition (Tranche 5 §12). It is
|
||||
evidence with a verification state — never structural authority by
|
||||
itself. It becomes accepted only via existing authority, research
|
||||
evidence, implementation evidence, or explicit user decision; until
|
||||
then normalize_need excludes it from the requirement's contract set."""
|
||||
if not provider or not model:
|
||||
raise ValueError("model-proposed requirement requires provider+model")
|
||||
if not 0.0 <= confidence <= 1.0:
|
||||
raise ValueError("confidence must be in [0,1]")
|
||||
return {
|
||||
"kind": "model_proposed_requirement",
|
||||
"contract": contract_id,
|
||||
"provider": provider,
|
||||
"model": model,
|
||||
"evidence": sorted(evidence),
|
||||
"confidence": confidence,
|
||||
"verification_state": "PENDING",
|
||||
}
|
||||
|
||||
|
||||
# ── research routing seam (§6 — honest: recorded route, not execution) ─────
|
||||
|
||||
|
||||
@@ -224,11 +511,22 @@ def make_decision(
|
||||
authorities: dict[str, dict],
|
||||
toolchain_constraints: tc.ToolchainConstraints | None = None,
|
||||
project_toolchain: dict | None = None,
|
||||
prior_iterations: list[dict] | None = None,
|
||||
) -> dict:
|
||||
"""Run NEED → decision trace. `authorities` maps authority name → store
|
||||
doc (e.g. {"rune_spine": spine_doc, "project_scaffold": scaffold_doc}).
|
||||
`project_toolchain` seeds the Toolchain Resolver's existing-project
|
||||
constraint (existing toolchain wins — §12)."""
|
||||
constraint (existing toolchain wins — §12).
|
||||
`prior_iterations` (Tranche 5 §11): earlier decision iterations being
|
||||
refined — retained in the trace with why-it-changed provenance; the
|
||||
final resolution stays singular.
|
||||
|
||||
Order of operations (Tranche 5 §6): affected-surface resolution →
|
||||
REQUIREMENT COMPLETENESS GATE → discovery/fit → merge. A MATERIAL_GAP
|
||||
downgrades premature concrete verdicts (REUSE/SATISFIED/COMPOSE/
|
||||
ADAPT_EXISTING) to INSUFFICIENT_EVIDENCE with the gap recorded — the
|
||||
fit engine was right GIVEN its requirement; the requirement was too
|
||||
narrow."""
|
||||
req, missing = normalize_need(need)
|
||||
trace: dict = {
|
||||
"need": need.to_dict(),
|
||||
@@ -243,8 +541,17 @@ def make_decision(
|
||||
"approval": {},
|
||||
"resolution": "",
|
||||
"rationale": "",
|
||||
"iterations": list(prior_iterations or []),
|
||||
}
|
||||
|
||||
# affected-surface classification + resolution (Tranche 5 §3-5)
|
||||
surface_result = resolve_affected_surface(need, authorities)
|
||||
trace["affected_surfaces"] = surface_result
|
||||
|
||||
# REQUIREMENT COMPLETENESS GATE (Tranche 5 §6-8) — before fit
|
||||
completeness = check_completeness(req, need, authorities, surface_result)
|
||||
trace["completeness"] = completeness
|
||||
|
||||
# live discovery + fit + resolution per authority (§7-8)
|
||||
results = []
|
||||
all_fits = []
|
||||
@@ -273,6 +580,30 @@ def make_decision(
|
||||
"resolution": "INSUFFICIENT_EVIDENCE",
|
||||
"rationale": f"requirement normalization incomplete: {missing}; refusing to build on guesses",
|
||||
}
|
||||
if completeness["verdict"] == MATERIAL_GAP and merged["resolution"] in (
|
||||
"SATISFIED",
|
||||
"REUSE",
|
||||
"COMPOSE",
|
||||
"ADAPT_EXISTING",
|
||||
"CONFIGURE",
|
||||
):
|
||||
# PREMATURE-FIT GUARD (Tranche 5 §6-7): the fit engine was correct
|
||||
# GIVEN its requirement — but the requirement is materially
|
||||
# incomplete (affected surface carries unrepresented obligations).
|
||||
# A concrete verdict on an incomplete requirement is not trustworthy.
|
||||
merged = {
|
||||
"resolution": "INSUFFICIENT_EVIDENCE",
|
||||
"rationale": (
|
||||
"premature fit blocked: requirement is materially incomplete "
|
||||
f"({len(completeness['gaps'])} unrepresented obligation(s) on "
|
||||
"affected surfaces); refine the requirement before trusting "
|
||||
"an apparently-complete fit"
|
||||
),
|
||||
"premature_fit_blocked": {
|
||||
"would_have_been": merged["resolution"],
|
||||
"gaps": completeness["gaps"],
|
||||
},
|
||||
}
|
||||
trace["resolution"] = merged["resolution"]
|
||||
trace["rationale"] = merged.get("rationale", "")
|
||||
if missing and trace["resolution"] == "INSUFFICIENT_EVIDENCE":
|
||||
@@ -395,26 +726,68 @@ def obligations_from_trace(trace: dict, req: cf.CapabilityRequirement) -> dict:
|
||||
|
||||
|
||||
def generate_contract(
|
||||
s, doc: dict, trace: dict, *, contract_id: str, scope: list[str], proposer_note: str = ""
|
||||
s, doc: dict, trace: dict, *, contract_id: str, scope: list[str] | None = None, proposer_note: str = ""
|
||||
) -> dict:
|
||||
"""Populate a Construction Contract from the decision trace. The builder
|
||||
no longer hand-reconstructs fields; the trace's structural truth fills
|
||||
them. Human/model additions may still extend afterwards."""
|
||||
"""Populate a Construction Contract from the decision trace (Tranche 5
|
||||
§13-17 IMPACT-AWARE generation). The Need's affected surface is never
|
||||
lost — it is classified and propagated into distinct surfaces:
|
||||
|
||||
affected_entities structural entity ids (existing)
|
||||
expected_new_entities planned new structural entities
|
||||
ext.affected_paths file-scope entries (mechanical scope check)
|
||||
ext.affected_ui_surfaces ui components (layout obligations follow)
|
||||
ext.unresolved_surfaces retained, never dropped
|
||||
ext.expected_runtime_effects operational effects (deployment:current
|
||||
advances etc.) — accounting metadata, NOT a
|
||||
workflow engine; closure requires independent
|
||||
observation before marking them observed.
|
||||
|
||||
File scope: generated from affected paths + selected capability
|
||||
implementation paths + explicit expected-new paths — NOT unrestricted
|
||||
repo scope. `scope` param may still extend it; generated paths are
|
||||
recorded as such (provenance: affected-surface).
|
||||
"""
|
||||
merged = trace["_merged"]
|
||||
req = trace["requirement"]
|
||||
surfaces = trace.get("affected_surfaces", {})
|
||||
selected = merged.get("selected") or []
|
||||
if isinstance(selected, str):
|
||||
selected = [selected]
|
||||
affected = [c["id"] for c in trace["candidates"] if c["id"] in selected] or trace["requirement"].get(
|
||||
"affected_surface", []
|
||||
|
||||
# structural affected: selected candidates + requirement contracts +
|
||||
# resolved surface entities — the union the old generator partially lost
|
||||
affected = {c["id"] for c in trace["candidates"] if c["id"] in selected}
|
||||
affected |= set(trace["requirement"].get("contracts", []))
|
||||
affected |= set(surfaces.get("affected_existing_entities", []))
|
||||
|
||||
# file scope: generated (affected paths + selected impl paths + expected
|
||||
# new paths) UNION caller-supplied additions, provenance-tracked
|
||||
affected_paths = list(surfaces.get("affected_paths", []))
|
||||
impl_paths = []
|
||||
for c in trace["candidates"]:
|
||||
if c["id"] in selected:
|
||||
p = c.get("identities", {}).get("path", "")
|
||||
if p:
|
||||
impl_paths.append(p)
|
||||
expected_new_paths = trace["need"]["constraints"].get("expected_new_source_paths", [])
|
||||
generated_scope = sorted(
|
||||
set(affected_paths)
|
||||
| set(surfaces.get("workflow_config_surfaces", []))
|
||||
| set(impl_paths)
|
||||
| set(expected_new_paths)
|
||||
)
|
||||
caller_scope = sorted(set(scope or []) - set(generated_scope))
|
||||
final_scope = sorted(set(generated_scope) | set(caller_scope))
|
||||
|
||||
expected_runtime = sorted(trace["need"]["constraints"].get("expected_runtime_effects", []))
|
||||
|
||||
obl = trace["obligations"]
|
||||
s.new_contract(
|
||||
doc,
|
||||
contract_id=contract_id,
|
||||
need=trace["need"]["objective"],
|
||||
origin=trace["need"]["origin"],
|
||||
scope=sorted(scope),
|
||||
scope=final_scope,
|
||||
requirement=req.get("objective", ""),
|
||||
gap_evidence=merged.get("rationale", ""),
|
||||
research_refs=(
|
||||
@@ -423,8 +796,10 @@ def generate_contract(
|
||||
),
|
||||
resolution=trace["resolution"],
|
||||
toolchain=trace["toolchain"],
|
||||
affected_entities=sorted(set(affected) | set(trace["requirement"].get("contracts", []))),
|
||||
expected_new_entities=trace["need"]["constraints"].get("expected_new_entities", []),
|
||||
affected_entities=sorted(affected),
|
||||
expected_new_entities=sorted(set(
|
||||
trace["need"]["constraints"].get("expected_new_entities", [])
|
||||
) | {d["entity"] for d in surfaces.get("derived_expected_new", [])}),
|
||||
expected_consumers=[],
|
||||
regression_obligations=obl["regression"],
|
||||
verification_obligations=sorted(set(obl["verification"])),
|
||||
@@ -433,23 +808,39 @@ def generate_contract(
|
||||
non_goals=trace["need"]["constraints"].get("non_goals", []),
|
||||
)
|
||||
e = s.contract(doc, contract_id)
|
||||
e.setdefault("ext", {})["decision_trace"] = {
|
||||
k: trace[k]
|
||||
for k in (
|
||||
"need",
|
||||
"requirement",
|
||||
"candidates",
|
||||
"fits",
|
||||
"resolution",
|
||||
"rationale",
|
||||
"research",
|
||||
"toolchain",
|
||||
"approval",
|
||||
"obligations",
|
||||
"layout_obligations",
|
||||
"normalization_missing",
|
||||
)
|
||||
}
|
||||
e.setdefault("ext", {}).update(
|
||||
{
|
||||
"decision_trace": {
|
||||
k: trace[k]
|
||||
for k in (
|
||||
"need",
|
||||
"requirement",
|
||||
"candidates",
|
||||
"fits",
|
||||
"resolution",
|
||||
"rationale",
|
||||
"research",
|
||||
"toolchain",
|
||||
"approval",
|
||||
"obligations",
|
||||
"layout_obligations",
|
||||
"normalization_missing",
|
||||
"completeness",
|
||||
"affected_surfaces",
|
||||
"iterations",
|
||||
)
|
||||
},
|
||||
# distinct impact surfaces (Tranche 5 §13) — file scope != structural
|
||||
# impact != expected new != runtime effect
|
||||
"affected_paths": affected_paths,
|
||||
"affected_ui_surfaces": surfaces.get("affected_ui_surfaces", []),
|
||||
"affected_regions": surfaces.get("affected_regions", []),
|
||||
"workflow_config_surfaces": surfaces.get("workflow_config_surfaces", []),
|
||||
"unresolved_surfaces": surfaces.get("unresolved_surfaces", []),
|
||||
"expected_runtime_effects": expected_runtime,
|
||||
"scope_provenance": {"generated_from_affected_surface": generated_scope, "caller_added": caller_scope},
|
||||
}
|
||||
)
|
||||
if proposer_note:
|
||||
e["ext"]["decision_trace"]["proposer_note"] = proposer_note
|
||||
return e
|
||||
+17
-1
@@ -465,6 +465,12 @@ class Scaffold:
|
||||
predicted = e.get("ext", {}).get("predicted_frontier")
|
||||
affected = e.get("identities", {}).get("affected_entities", [])
|
||||
expected_new = set(e.get("identities", {}).get("expected_new_entities", []))
|
||||
# Tranche 5 §14: expected RUNTIME effects (deployment:current advances
|
||||
# etc.) are prediction/accounting metadata, not workflow steps. An
|
||||
# observed entity matching an expected runtime effect is EXPECTED,
|
||||
# not genuinely unexpected — but only when independently observed
|
||||
# (§15: prediction never self-certifies; the evidence item must exist).
|
||||
expected_runtime = set(e.get("ext", {}).get("expected_runtime_effects", []))
|
||||
quality: dict = {"contract": contract_id, "captured_at": now()}
|
||||
|
||||
# independent observed impact (§17): evidence items only
|
||||
@@ -484,7 +490,14 @@ class Scaffold:
|
||||
predicted_but_unaffected = sorted(pred_existing - obs_set)
|
||||
expected_new_observed = sorted(expected_new & obs_set)
|
||||
expected_new_missing = sorted(expected_new - obs_set)
|
||||
genuinely_unexpected_new = sorted(obs_set - pred_existing - expected_new)
|
||||
# effect strings are phrases ("deployment:current advances"); an
|
||||
# observed entity matches when an expected effect STARTS with its id
|
||||
matched_runtime = {o for o in obs_set if any(eff.startswith(o) for eff in expected_runtime)}
|
||||
expected_runtime_observed = sorted(
|
||||
eff for eff in expected_runtime if any(eff.startswith(o) for o in matched_runtime)
|
||||
)
|
||||
expected_runtime_missing = sorted(expected_runtime - set(expected_runtime_observed))
|
||||
genuinely_unexpected_new = sorted(obs_set - pred_existing - expected_new - matched_runtime)
|
||||
|
||||
quality.update(
|
||||
{
|
||||
@@ -497,6 +510,9 @@ class Scaffold:
|
||||
"predicted_but_unaffected": predicted_but_unaffected,
|
||||
"expected_new_observed": expected_new_observed,
|
||||
"expected_new_missing": expected_new_missing,
|
||||
"expected_runtime_effects": sorted(expected_runtime),
|
||||
"expected_runtime_observed": expected_runtime_observed,
|
||||
"expected_runtime_missing": sorted(expected_runtime - obs_set),
|
||||
"genuinely_unexpected_new": genuinely_unexpected_new,
|
||||
# legacy aliases (Tranche 2/3 consumers)
|
||||
"missed": genuinely_unexpected_new,
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+137
-121
@@ -2,7 +2,7 @@
|
||||
"authority_generations": {
|
||||
"ast-import-projector": {
|
||||
"generation": 0,
|
||||
"last_success": "2026-09-13T12:08:10Z",
|
||||
"last_success": "2026-09-13T12:30:19Z",
|
||||
"observed": [
|
||||
"consumed_by|module:abstraction|module:memory_consolidation",
|
||||
"consumed_by|module:abstraction|module:memory_selector",
|
||||
@@ -361,7 +361,7 @@
|
||||
},
|
||||
"deploy-identity-projector": {
|
||||
"generation": 1,
|
||||
"last_success": "2026-09-13T12:08:10Z",
|
||||
"last_success": "2026-09-13T12:30:19Z",
|
||||
"observed": [
|
||||
"deployed_as|module:abstraction|deployment:current",
|
||||
"deployed_as|module:adaptive_router|deployment:current",
|
||||
@@ -502,7 +502,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code",
|
||||
"self-model"
|
||||
@@ -522,7 +522,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
@@ -539,7 +539,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
@@ -556,7 +556,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
@@ -573,7 +573,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
@@ -590,7 +590,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
@@ -607,7 +607,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
@@ -616,14 +616,14 @@
|
||||
],
|
||||
"id": "deployment:current",
|
||||
"identities": {
|
||||
"deployed_at": "2026-09-13T12:03:08Z",
|
||||
"deployed_sha": "386137dc14b5",
|
||||
"deployed_at": "2026-09-13T12:09:52Z",
|
||||
"deployed_sha": "6bc31c513e77",
|
||||
"deployed_version": "0.66.38",
|
||||
"observed_from": "/mnt/workdrive/rune-code/current"
|
||||
},
|
||||
"kind": "deployment_surface",
|
||||
"maturity": "LIVE_PROVEN",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
@@ -639,7 +639,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -657,7 +657,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -675,7 +675,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -693,7 +693,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -711,7 +711,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -729,7 +729,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -747,7 +747,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -765,7 +765,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -783,7 +783,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -801,7 +801,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -819,7 +819,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -837,7 +837,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -855,7 +855,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -873,7 +873,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -891,7 +891,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -909,7 +909,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -927,7 +927,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -945,7 +945,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -963,7 +963,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -981,7 +981,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -999,7 +999,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1017,7 +1017,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1035,7 +1035,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1053,7 +1053,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1071,7 +1071,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1089,7 +1089,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1106,7 +1106,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "WIRED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1124,7 +1124,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1142,7 +1142,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1160,7 +1160,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1177,7 +1177,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "WIRED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1195,7 +1195,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1213,7 +1213,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1230,7 +1230,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "WIRED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1247,7 +1247,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "WIRED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1265,7 +1265,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1282,7 +1282,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "WIRED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1300,7 +1300,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1317,7 +1317,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "WIRED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1334,7 +1334,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1350,7 +1350,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "WIRED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1366,7 +1366,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "WIRED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1382,7 +1382,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "WIRED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1400,7 +1400,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1418,7 +1418,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1436,7 +1436,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1454,7 +1454,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1472,7 +1472,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1490,7 +1490,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1508,7 +1508,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1526,7 +1526,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1544,7 +1544,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1562,7 +1562,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1580,7 +1580,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1598,7 +1598,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1615,7 +1615,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "WIRED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1632,7 +1632,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "WIRED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1649,7 +1649,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "WIRED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1667,7 +1667,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1684,7 +1684,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "WIRED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1701,7 +1701,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "WIRED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1719,7 +1719,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1737,7 +1737,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1755,7 +1755,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1773,7 +1773,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1790,7 +1790,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "WIRED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1808,7 +1808,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1825,7 +1825,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "WIRED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1842,7 +1842,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "WIRED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1860,7 +1860,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1878,7 +1878,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1896,7 +1896,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1913,7 +1913,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "WIRED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1931,7 +1931,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1949,7 +1949,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1966,7 +1966,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "WIRED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -1983,7 +1983,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "WIRED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2001,7 +2001,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2019,7 +2019,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2037,7 +2037,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2055,7 +2055,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2073,7 +2073,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2091,7 +2091,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2109,7 +2109,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2127,7 +2127,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2145,7 +2145,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2163,7 +2163,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2181,7 +2181,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2199,7 +2199,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2217,7 +2217,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2235,7 +2235,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2253,7 +2253,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2271,7 +2271,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2289,7 +2289,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2307,7 +2307,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2324,7 +2324,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2340,7 +2340,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "PRESENT",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2357,7 +2357,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2373,7 +2373,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "PRESENT",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2389,7 +2389,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "PRESENT",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2405,7 +2405,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "PRESENT",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2422,7 +2422,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2438,7 +2438,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "PRESENT",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2455,7 +2455,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2471,7 +2471,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "PRESENT",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2487,7 +2487,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2503,7 +2503,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2519,7 +2519,7 @@
|
||||
},
|
||||
"kind": "module",
|
||||
"maturity": "EXERCISED",
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"tags": [
|
||||
"code"
|
||||
]
|
||||
@@ -2788,7 +2788,7 @@
|
||||
"tags": []
|
||||
}
|
||||
],
|
||||
"generated_at": "2026-09-13T12:08:10Z",
|
||||
"generated_at": "2026-09-13T12:30:19Z",
|
||||
"observations": [
|
||||
{
|
||||
"authority": "cli-invocation",
|
||||
@@ -3359,6 +3359,22 @@
|
||||
"observed_at": "2026-09-13T12:08:10Z",
|
||||
"observed_from": "/mnt/workdrive/rune-code/current"
|
||||
},
|
||||
{
|
||||
"deployed_at": "2026-09-13T12:09:52Z",
|
||||
"deployed_sha": "6bc31c513e77",
|
||||
"deployed_version": "0.66.38",
|
||||
"kind": "deployed_identity",
|
||||
"observed_at": "2026-09-13T12:19:03Z",
|
||||
"observed_from": "/mnt/workdrive/rune-code/current"
|
||||
},
|
||||
{
|
||||
"deployed_at": "2026-09-13T12:09:52Z",
|
||||
"deployed_sha": "6bc31c513e77",
|
||||
"deployed_version": "0.66.38",
|
||||
"kind": "deployed_identity",
|
||||
"observed_at": "2026-09-13T12:30:19Z",
|
||||
"observed_from": "/mnt/workdrive/rune-code/current"
|
||||
},
|
||||
{
|
||||
"deployed_sha": "2731db03f4bd",
|
||||
"deployed_version": "0.66.33",
|
||||
|
||||
@@ -0,0 +1,464 @@
|
||||
"""test_surface_completeness.py — SCAFFOLD-TRANCHE-005 test plans
|
||||
§24 (affected surfaces), §25 (requirement completeness), §26 (iterative
|
||||
trace), §27 (expected runtime effects), §28 (backward compat)."""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
|
||||
|
||||
from rune import construction_decision as cd # noqa: E402
|
||||
from rune import layout_contract as lc # noqa: E402
|
||||
from rune import scaffold as sc # noqa: E402
|
||||
from rune import structural_store as ss # noqa: E402
|
||||
|
||||
|
||||
def _authorities():
|
||||
"""Spine-like authority: deploy module with a promotion contract the
|
||||
requirement does NOT represent (the deploy-incident shape), plus a
|
||||
path-mapped module, plus a UI component with a Layout Contract."""
|
||||
spine = ss.new_doc()
|
||||
ss.add_entity(
|
||||
spine,
|
||||
id="module:deploy_stage",
|
||||
kind="module",
|
||||
identities={"path": "scripts/deploy_stage.py"},
|
||||
maturity="WIRED",
|
||||
evidence_refs=["f"],
|
||||
)
|
||||
ss.add_entity(spine, id="contract:monotonic_promotion", kind="contract", identities={}, evidence_refs=["f"])
|
||||
ss.add_relationship(
|
||||
spine,
|
||||
type="implements",
|
||||
from_id="module:deploy_stage",
|
||||
to_id="contract:monotonic_promotion",
|
||||
evidence_refs=["f"],
|
||||
)
|
||||
ss.add_entity(spine, id="contract:fail_closed_deployment", kind="contract", identities={}, evidence_refs=["f"])
|
||||
ss.add_entity(
|
||||
spine,
|
||||
id="module:deploy_qualify",
|
||||
kind="module",
|
||||
identities={"path": "scripts/deploy_qualify.py"},
|
||||
maturity="WIRED",
|
||||
evidence_refs=["f"],
|
||||
)
|
||||
ss.add_relationship(
|
||||
spine,
|
||||
type="implements",
|
||||
from_id="module:deploy_qualify",
|
||||
to_id="contract:fail_closed_deployment",
|
||||
evidence_refs=["f"],
|
||||
)
|
||||
scaffold = ss.new_doc()
|
||||
lc.define_region(scaffold, "footer-bar", provenance="scaffold")
|
||||
lc.bind_component(
|
||||
scaffold, "version_display", "footer-bar", provenance="scaffold", order=0, implementation="src/rune/rune_tui.py"
|
||||
)
|
||||
return {"rune_spine": spine, "project_scaffold": scaffold}
|
||||
|
||||
|
||||
def _need(**kw):
|
||||
d = dict(
|
||||
need_id="n5",
|
||||
objective="prevent deploy regression",
|
||||
origin="user",
|
||||
constraints={"contracts": ["contract:fail_closed_deployment"]},
|
||||
affected_surface=["scripts/deploy_qualify.py"],
|
||||
evidence_refs=["x"],
|
||||
approval_context="covered",
|
||||
)
|
||||
d.update(kw)
|
||||
return cd.Need(**d)
|
||||
|
||||
|
||||
# ── §24 affected surfaces ───────────────────────────────────────────────────
|
||||
|
||||
|
||||
def test_24a_source_path_propagates_to_file_scope(tmp_path):
|
||||
s, doc = _gen(tmp_path)
|
||||
c = s.contract(doc, "G5")
|
||||
assert "scripts/deploy_qualify.py" in c["ext"]["affected_paths"]
|
||||
assert "scripts/deploy_qualify.py" in c["identities"]["scope"]
|
||||
|
||||
|
||||
def test_24b_mapped_path_resolves_to_module():
|
||||
res = cd.resolve_affected_surface(_need(), _authorities())
|
||||
assert "module:deploy_qualify" in res["affected_existing_entities"]
|
||||
r = next(r for r in res["resolutions"] if r["surface"] == "scripts/deploy_qualify.py")
|
||||
assert r["resolved"][0]["via"] == "path_attribute"
|
||||
|
||||
|
||||
def test_24c_unresolved_path_retained_not_dropped():
|
||||
need = _need(affected_surface=["scripts/brand_new_thing.py"])
|
||||
res = cd.resolve_affected_surface(need, _authorities())
|
||||
assert any(u["surface"] == "scripts/brand_new_thing.py" for u in res["unresolved_surfaces"])
|
||||
assert "scripts/brand_new_thing.py" in res["affected_paths"] # still scoped
|
||||
|
||||
|
||||
def test_24d_ui_component_pulls_layout_contract():
|
||||
need = _need(affected_surface=["ui:version_display"])
|
||||
res = cd.resolve_affected_surface(need, _authorities())
|
||||
assert "ui:version_display" in res["affected_ui_surfaces"]
|
||||
r = res["resolutions"][0]
|
||||
assert r["resolved"][0]["via"] == "layout_contract"
|
||||
trace = cd.make_decision(need, authorities=_authorities())
|
||||
assert any(o["component"] == "ui:version_display" for o in trace["layout_obligations"])
|
||||
|
||||
|
||||
def test_24e_region_surface_pulls_layout_obligations():
|
||||
need = _need(affected_surface=["region:footer-bar"])
|
||||
trace = cd.make_decision(need, authorities=_authorities())
|
||||
assert any(o["region"] == "footer-bar" for o in trace["layout_obligations"])
|
||||
|
||||
|
||||
def test_24f_workflow_config_mechanically_scoped(tmp_path):
|
||||
s, doc = _gen(tmp_path, affected_surface=["scripts/deploy_qualify.py", ".gitea/workflows/deploy.yml"])
|
||||
c = s.contract(doc, "G5")
|
||||
assert ".gitea/workflows/deploy.yml" in c["ext"]["workflow_config_surfaces"]
|
||||
assert ".gitea/workflows/deploy.yml" in c["identities"]["scope"]
|
||||
|
||||
|
||||
def test_24g_affected_surface_not_auto_expected_new(tmp_path):
|
||||
s, doc = _gen(tmp_path)
|
||||
c = s.contract(doc, "G5")
|
||||
# affected existing entities never leak into expected-new
|
||||
assert c["identities"]["expected_new_entities"] == []
|
||||
assert c["identities"]["affected_entities"] # but affected is populated
|
||||
|
||||
|
||||
def test_24h_expected_new_path_entity_distinct(tmp_path):
|
||||
s = sc.Scaffold(tmp_path / ".rune" / "scaffold", "p5")
|
||||
doc = s.init(purpose="t")
|
||||
need = _need(
|
||||
constraints={
|
||||
"contracts": ["contract:fail_closed_deployment"],
|
||||
"expected_new_entities": ["module:fresh_thing"],
|
||||
"expected_new_source_paths": ["scripts/fresh_thing.py"],
|
||||
},
|
||||
affected_surface=["scripts/deploy_qualify.py"],
|
||||
evidence_refs=["x"],
|
||||
)
|
||||
trace = cd.make_decision(need, authorities=_authorities())
|
||||
cd.generate_contract(s, doc, trace, contract_id="G5H", scope=None)
|
||||
c = s.contract(doc, "G5H")
|
||||
assert c["identities"]["expected_new_entities"] == ["module:fresh_thing"]
|
||||
assert "scripts/fresh_thing.py" in c["identities"]["scope"] # expected-new path scoped
|
||||
assert "scripts/fresh_thing.py" not in c["ext"]["affected_paths"]
|
||||
|
||||
|
||||
def test_24i_contract_roundtrip_preserves_surfaces(tmp_path):
|
||||
s, doc = _gen(tmp_path, affected_surface=["scripts/deploy_qualify.py", "ui:version_display", "scripts/unmapped.py"])
|
||||
s.save(doc)
|
||||
doc2 = sc.Scaffold(tmp_path / ".rune" / "scaffold", "p5").load()
|
||||
c = doc2 and sc.Scaffold(tmp_path / ".rune" / "scaffold", "p5").contract(doc2, "G5")
|
||||
assert c["ext"]["affected_paths"] == ["scripts/deploy_qualify.py", "scripts/unmapped.py"]
|
||||
assert c["ext"]["affected_ui_surfaces"] == ["ui:version_display"]
|
||||
assert any(u["surface"] == "scripts/unmapped.py" for u in c["ext"]["unresolved_surfaces"])
|
||||
|
||||
|
||||
# ── §25 requirement completeness ────────────────────────────────────────────
|
||||
|
||||
|
||||
def _gen(tmp_path, **need_kw):
|
||||
s = sc.Scaffold(tmp_path / ".rune" / "scaffold", "p5")
|
||||
doc = s.init(purpose="t5")
|
||||
need = _need(**need_kw)
|
||||
trace = cd.make_decision(need, authorities=_authorities(), project_toolchain={"existing_language": "python"})
|
||||
cd.generate_contract(s, doc, trace, contract_id="G5", scope=None)
|
||||
return s, doc
|
||||
|
||||
|
||||
def test_25a_complete_requirement_complete_enough():
|
||||
need = _need(
|
||||
constraints={"contracts": ["contract:fail_closed_deployment"]}, affected_surface=["scripts/deploy_qualify.py"]
|
||||
)
|
||||
trace = cd.make_decision(need, authorities=_authorities())
|
||||
assert trace["completeness"]["verdict"] == cd.COMPLETE_ENOUGH
|
||||
assert trace["resolution"] == "REUSE" # fit proceeds
|
||||
|
||||
|
||||
def test_25b_affected_stage_missing_material_gap():
|
||||
"""THE deploy-incident shape: requirement names qualification only, but
|
||||
the affected surface includes the promotion implementation carrying an
|
||||
unrepresented ordering contract."""
|
||||
need = _need(affected_surface=["scripts/deploy_qualify.py", "scripts/deploy_stage.py"])
|
||||
trace = cd.make_decision(need, authorities=_authorities())
|
||||
assert trace["completeness"]["verdict"] == cd.MATERIAL_GAP
|
||||
gaps = {g["missing_contract"] for g in trace["completeness"]["gaps"]}
|
||||
assert "contract:monotonic_promotion" in gaps
|
||||
# PREMATURE-FIT GUARD: without the guard this resolves REUSE/SATISFIED
|
||||
assert trace["resolution"] == "INSUFFICIENT_EVIDENCE"
|
||||
assert trace["_merged"].get("premature_fit_blocked", {}).get("gaps")
|
||||
# gap provenance: derived_from the entity, with rule + evidence
|
||||
g = next(g for g in trace["completeness"]["gaps"] if g["missing_contract"] == "contract:monotonic_promotion")
|
||||
assert g["derived_from"] == "module:deploy_stage"
|
||||
assert g["rule"] and g["evidence"]
|
||||
|
||||
|
||||
def test_25c_no_resolution_unknown():
|
||||
need = _need(affected_surface=["totally/unknown.xyz"])
|
||||
trace = cd.make_decision(need, authorities=_authorities())
|
||||
assert trace["completeness"]["verdict"] == cd.COMPLETENESS_UNKNOWN
|
||||
|
||||
|
||||
def test_25d_unknown_never_becomes_complete():
|
||||
need = _need(affected_surface=["totally/unknown.xyz"])
|
||||
trace = cd.make_decision(need, authorities=_authorities())
|
||||
assert trace["completeness"]["verdict"] != cd.COMPLETE_ENOUGH
|
||||
assert "undeterminable" in trace["completeness"]["note"]
|
||||
|
||||
|
||||
def test_25e_derived_requirement_provenance():
|
||||
need = _need(affected_surface=["scripts/deploy_qualify.py", "scripts/deploy_stage.py"])
|
||||
trace = cd.make_decision(need, authorities=_authorities())
|
||||
g = trace["completeness"]["gaps"][0]
|
||||
assert g["derived_from"] and g["evidence"] and g["rule"]
|
||||
|
||||
|
||||
def test_25f_model_proposal_not_structural_authority():
|
||||
"""A model-proposed contract is recorded with provider/confidence and
|
||||
verification state PENDING — it does not enter the requirement's
|
||||
contract set as authority."""
|
||||
proposed = cd.record_proposed_requirement(
|
||||
"contract:model_claimed", provider="local-model", model="m", evidence=["hunch"], confidence=0.4
|
||||
)
|
||||
assert proposed["verification_state"] == "PENDING"
|
||||
assert proposed["provider"] == "local-model"
|
||||
# normalization does not accept it as a contract
|
||||
need = _need(constraints={"contracts": ["contract:fail_closed_deployment"], "model_proposed": [proposed]})
|
||||
req, _ = cd.normalize_need(need)
|
||||
assert "contract:model_claimed" not in req.contracts
|
||||
|
||||
|
||||
def test_25g_consumer_contracts_derive_regression():
|
||||
spine = _authorities()["rune_spine"]
|
||||
ss.add_entity(spine, id="module:consumer", kind="module", identities={}, evidence_refs=["f"])
|
||||
ss.add_relationship(
|
||||
spine, type="consumes", from_id="module:consumer", to_id="module:deploy_qualify", evidence_refs=["f"]
|
||||
)
|
||||
need = _need(affected_surface=["scripts/deploy_qualify.py"])
|
||||
trace = cd.make_decision(need, authorities={"rune_spine": spine})
|
||||
assert trace["resolution"] == "REUSE"
|
||||
reg = " ".join(trace["obligations"]["regression"])
|
||||
assert "module:consumer" in reg
|
||||
|
||||
|
||||
def test_25h_adapt_existing_includes_old_plus_new():
|
||||
spine = _authorities()["rune_spine"]
|
||||
ss.add_entity(spine, id="contract:extra_obligation", kind="contract", identities={}, evidence_refs=["f"])
|
||||
ss.add_relationship(
|
||||
spine,
|
||||
type="implements",
|
||||
from_id="module:deploy_qualify",
|
||||
to_id="contract:extra_obligation",
|
||||
evidence_refs=["f"],
|
||||
)
|
||||
need = _need(affected_surface=["scripts/deploy_qualify.py"])
|
||||
need.constraints["contracts"] = ["contract:fail_closed_deployment", "contract:extra_obligation"]
|
||||
trace = cd.make_decision(need, authorities={"rune_spine": spine})
|
||||
assert trace["completeness"]["verdict"] == cd.COMPLETE_ENOUGH
|
||||
assert trace["resolution"] == "REUSE" # exact fit on both contracts
|
||||
assert "new behavior proven by tests" in " ".join(trace["obligations"]["verification"])
|
||||
|
||||
|
||||
def test_25i_compose_derives_boundary_verification():
|
||||
spine = _authorities()["rune_spine"]
|
||||
need = _need(affected_surface=["scripts/deploy_qualify.py", "scripts/deploy_stage.py"])
|
||||
need.constraints["contracts"] = ["contract:fail_closed_deployment", "contract:monotonic_promotion"]
|
||||
trace = cd.make_decision(need, authorities={"rune_spine": spine})
|
||||
assert trace["resolution"] == "COMPOSE"
|
||||
assert "composition boundary behavior verified end-to-end" in " ".join(trace["obligations"]["verification"])
|
||||
comp = trace["obligations"]["composition"][0]
|
||||
assert set(comp["components"]) == {"module:deploy_qualify", "module:deploy_stage"}
|
||||
|
||||
|
||||
def test_25j_layout_lock_derives_preservation():
|
||||
scaffold = _authorities()["project_scaffold"]
|
||||
lc.bind_component(
|
||||
scaffold, "status_bar", "footer-bar", lock=True, provenance="user", order=1, evidence_ref="user-2026"
|
||||
)
|
||||
need = _need(affected_surface=["ui:status_bar"])
|
||||
trace = cd.make_decision(need, authorities=_authorities() | {"project_scaffold": scaffold})
|
||||
lo = [o for o in trace["layout_obligations"] if o["component"] == "ui:status_bar"]
|
||||
assert lo and lo[0]["lock"] is True
|
||||
assert "explicit user authorization" in lo[0]["obligation"]
|
||||
# and the approval gate fires for the locked surface
|
||||
assert any("USER-LOCKED" in r for r in trace["approval"]["reasons"]) or trace["approval"]["covered_by"]
|
||||
|
||||
|
||||
def test_25k_completeness_prevents_premature_reuse():
|
||||
"""§23 core test, fixture form: the deploy-like Need must NOT resolve
|
||||
REUSE while the affected promotion surface carries an unrepresented
|
||||
contract."""
|
||||
need = _need(affected_surface=["scripts/deploy_qualify.py", "scripts/deploy_stage.py"])
|
||||
trace = cd.make_decision(need, authorities=_authorities())
|
||||
assert trace["resolution"] != "REUSE"
|
||||
assert trace["completeness"]["verdict"] == cd.MATERIAL_GAP
|
||||
blocked = trace["_merged"]["premature_fit_blocked"]
|
||||
assert blocked["would_have_been"] in ("REUSE", "SATISFIED")
|
||||
|
||||
|
||||
# ── §26 iterative trace ──────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def test_26_iterative_trace_durable(tmp_path):
|
||||
"""initial normalization → gap → refinement → revised → final decision;
|
||||
all retained; fresh process reconstructs without transcript."""
|
||||
s = sc.Scaffold(tmp_path / ".rune" / "scaffold", "p26")
|
||||
doc = s.init(purpose="t26")
|
||||
# iteration 1: narrow requirement → premature fit blocked
|
||||
need1 = _need(affected_surface=["scripts/deploy_qualify.py", "scripts/deploy_stage.py"])
|
||||
t1 = cd.make_decision(need1, authorities=_authorities())
|
||||
assert t1["resolution"] == "INSUFFICIENT_EVIDENCE"
|
||||
assert t1["_merged"]["premature_fit_blocked"]["would_have_been"] == "REUSE"
|
||||
# evidence/refinement: the promotion contract is added to the requirement
|
||||
need2 = _need(
|
||||
constraints={"contracts": ["contract:fail_closed_deployment", "contract:monotonic_promotion"]},
|
||||
affected_surface=["scripts/deploy_qualify.py", "scripts/deploy_stage.py"],
|
||||
evidence_refs=["x", "refinement:completeness-gap-iter1"],
|
||||
)
|
||||
t2 = cd.make_decision(
|
||||
need2,
|
||||
authorities=_authorities(),
|
||||
prior_iterations=[
|
||||
{
|
||||
"iteration": 1,
|
||||
"normalized_contracts": need1.constraints["contracts"],
|
||||
"completeness": t1["completeness"]["verdict"],
|
||||
"gaps": [g["missing_contract"] for g in t1["completeness"]["gaps"]],
|
||||
"resolution": t1["resolution"],
|
||||
"refined_by": "completeness gap evidence",
|
||||
}
|
||||
],
|
||||
)
|
||||
assert t2["resolution"] == "COMPOSE"
|
||||
assert t2["iterations"][0]["gaps"] == ["contract:monotonic_promotion"]
|
||||
cd.generate_contract(s, doc, t2, contract_id="IT1", scope=None)
|
||||
s.transition_contract(doc, "IT1", "approved")
|
||||
s.save(doc)
|
||||
# fresh process
|
||||
script = (
|
||||
"import sys, json;"
|
||||
"sys.path.insert(0, %r);"
|
||||
"from rune import scaffold;"
|
||||
"s = scaffold.Scaffold(%r, 'p26');"
|
||||
"doc = s.load();"
|
||||
"c = s.contract(doc, 'IT1');"
|
||||
"t = c['ext']['decision_trace'];"
|
||||
"print(json.dumps({'iterations': len(t['iterations']),"
|
||||
"'iter1_gaps': t['iterations'][0]['gaps'],"
|
||||
"'iter1_resolution': t['iterations'][0]['resolution'],"
|
||||
"'refined_by': t['iterations'][0]['refined_by'],"
|
||||
"'final_resolution': t['resolution'],"
|
||||
"'final_contracts': t['requirement']['contracts']}))"
|
||||
) % (os.path.join(os.path.dirname(__file__), "..", "src"), str(tmp_path / ".rune" / "scaffold"))
|
||||
r = subprocess.run([sys.executable, "-c", script], capture_output=True, text=True, timeout=60)
|
||||
assert r.returncode == 0, r.stderr
|
||||
rec = json.loads(r.stdout)
|
||||
assert rec["iterations"] == 1
|
||||
assert rec["iter1_gaps"] == ["contract:monotonic_promotion"]
|
||||
assert rec["iter1_resolution"] == "INSUFFICIENT_EVIDENCE"
|
||||
assert rec["refined_by"] == "completeness gap evidence"
|
||||
assert rec["final_resolution"] == "COMPOSE"
|
||||
assert "contract:monotonic_promotion" in rec["final_contracts"]
|
||||
|
||||
|
||||
# ── §27 expected runtime effects ─────────────────────────────────────────────
|
||||
|
||||
|
||||
def _close_with(tmp_path, expected_runtime, observed):
|
||||
s = sc.Scaffold(tmp_path / ".rune" / "scaffold", "p27")
|
||||
doc = s.init(purpose="t27")
|
||||
s.new_contract(
|
||||
doc, contract_id="R1", need="n", scope=["src/x"], affected_entities=["module:a"], expected_new_entities=[]
|
||||
)
|
||||
c = s.contract(doc, "R1")
|
||||
c.setdefault("ext", {})["expected_runtime_effects"] = expected_runtime
|
||||
s.transition_contract(doc, "R1", "approved")
|
||||
s.transition_contract(doc, "R1", "building")
|
||||
s.transition_contract(doc, "R1", "verifying")
|
||||
s.close_contract(doc, "R1", observed_evidence=observed)
|
||||
return s.contract(doc, "R1")["ext"]["contract_quality"]
|
||||
|
||||
|
||||
def test_27a_expected_runtime_observed_not_unexpected(tmp_path):
|
||||
q = _close_with(
|
||||
tmp_path, ["deployment:current advances"], [{"entity": "deployment:current", "source": "deployed-identity"}]
|
||||
)
|
||||
assert q["expected_runtime_observed"] == ["deployment:current advances"]
|
||||
assert "deployment:current" not in q["genuinely_unexpected_new"]
|
||||
|
||||
|
||||
def test_27b_expected_runtime_missing(tmp_path):
|
||||
q = _close_with(tmp_path, ["cache invalidated"], [])
|
||||
assert q["expected_runtime_missing"] == ["cache invalidated"]
|
||||
assert q["expected_runtime_observed"] == []
|
||||
|
||||
|
||||
def test_27c_unpredicted_runtime_genuinely_unexpected(tmp_path):
|
||||
q = _close_with(tmp_path, [], [{"entity": "deployment:current", "source": "deployed-identity"}])
|
||||
assert "deployment:current" in q["genuinely_unexpected_new"]
|
||||
|
||||
|
||||
def test_27d_no_self_certification(tmp_path):
|
||||
"""Predicting the runtime effect does not observe it: no evidence item →
|
||||
missing, and correctly_predicted unaffected."""
|
||||
q = _close_with(tmp_path, ["deployment:current advances"], [])
|
||||
assert q["expected_runtime_missing"] == ["deployment:current advances"]
|
||||
assert q["expected_runtime_observed"] == []
|
||||
|
||||
|
||||
def test_27e_deployment_current_fixture(tmp_path):
|
||||
"""The DEPLOY-MONOTONIC-001 defect shape, now correct: a release-class
|
||||
contract that predicts the deployment advance closes clean."""
|
||||
q = _close_with(
|
||||
tmp_path,
|
||||
["deployment:current advances"],
|
||||
[{"entity": "module:a", "source": "git-diff"}, {"entity": "deployment:current", "source": "deployed-identity"}],
|
||||
)
|
||||
assert q["correctly_predicted"] == ["module:a"]
|
||||
assert q["expected_runtime_observed"] == ["deployment:current advances"]
|
||||
assert q["genuinely_unexpected_new"] == []
|
||||
|
||||
|
||||
# ── §28 backward compatibility ───────────────────────────────────────────────
|
||||
|
||||
|
||||
def test_28_old_contract_closes_without_new_fields(tmp_path):
|
||||
s = sc.Scaffold(tmp_path / ".rune" / "scaffold", "p28")
|
||||
doc = s.init(purpose="t28")
|
||||
# old-shape: no expected_runtime_effects, no expected_new_entities
|
||||
s.new_contract(doc, contract_id="OLD", need="old", scope=["src/x"], affected_entities=["module:a"])
|
||||
c = s.contract(doc, "OLD")
|
||||
assert not c.get("ext", {}).get("expected_runtime_effects") # empty, not crash
|
||||
s.transition_contract(doc, "OLD", "approved")
|
||||
s.transition_contract(doc, "OLD", "building")
|
||||
s.transition_contract(doc, "OLD", "verifying")
|
||||
s.close_contract(doc, "OLD", observed_evidence=[{"entity": "module:a", "source": "git-diff"}])
|
||||
q = s.contract(doc, "OLD")["ext"]["contract_quality"]
|
||||
assert q["correctly_predicted"] == ["module:a"]
|
||||
assert q["expected_runtime_effects"] == [] # empty, not crash
|
||||
|
||||
|
||||
def test_28_tranche4_contract_data_still_readable(tmp_path):
|
||||
s = sc.Scaffold(tmp_path / ".rune" / "scaffold", "p28b")
|
||||
doc = s.init(purpose="t28b")
|
||||
s.new_contract(
|
||||
doc,
|
||||
contract_id="T4SHAPE",
|
||||
need="n",
|
||||
scope=["src/x"],
|
||||
affected_entities=["module:a"],
|
||||
expected_new_entities=["module:b"],
|
||||
)
|
||||
s.transition_contract(doc, "T4SHAPE", "approved")
|
||||
s.transition_contract(doc, "T4SHAPE", "building")
|
||||
s.transition_contract(doc, "T4SHAPE", "verifying")
|
||||
s.close_contract(doc, "T4SHAPE", observed_evidence=[{"entity": "module:b", "source": "file-added"}])
|
||||
q = s.contract(doc, "T4SHAPE")["ext"]["contract_quality"]
|
||||
assert q["expected_new_observed"] == ["module:b"]
|
||||
Reference in new issue
Block a user