Files
rune/docs/SCAFFOLDING.md
T
Nick 24d14ff8e1 fix(spine): preserve candidate discoveries without inventing module IDs (#2465)
Migrate 22 malformed contract-discovery IDs to construction-surface, retain evidence/edges, keep candidates DECLARED, unify cold/warm language projection, and refresh the pinned aiohttp lock. Red regression tests precede the fix; local full suite 2301 passed, spine canaries 19/19 and 21/21.
2026-10-05 22:14:46 +01:00

14 KiB

Scaffolding V1 — Tranche 1

2026-09-12. Scaffolding is the controlled need-to-capability system: NEED → DISCOVERY → GAP → REUSE/ACQUIRE/BUILD → IMPACT+PLAN → SAFE IMPLEMENTATION → VERIFICATION → PROVISIONAL USE → TRUST/REVISE/RETIRE → REUSABLE CAPABILITY. Tranche 1 is deliberately narrower: make Rune stop rediscovering how Rune is built every time Rune changes.

Tranche 1 contract

scripts/scaffold_projection.py — a PROJECTION over existing authorities, never a new one:

question authority projected
what subsystem owns X? config/umbrellas.json (umbrella manifest + henxel-enforced completeness)
what does X depend on? AST import graph over src/rune/*.py (incl. relative/lazy imports)
who consumes X? reverse import edges
what changes if X changes? mirrors + depends_on umbrellas + consumers
what proof obligations arise? unit-test file, canary registry, umbrella gates, suite/lint/henxels
which are satisfied? filesystem + registry evidence (static); suite/ratchet/henxels are runtime-check commands

Commands: component <module> · impact <module> · obligations <module> · all [outfile]. If the projection disagrees with an authority, fix the authority — never the projection.

Mechanism audit (2026-09-12, deployed 0.66.22)

mechanism verdict basis
Umbrella manifest + impact map + propagation gate KEEP ownership/impact authority; henxel-enforced completeness; consumed by projection
Henxels structural contract KEEP cross-cutting obligations (silent-handler ratchet, registries, coverage debt)
Canary registry (canary_native.MODULES + per-track canaries) KEEP per-subsystem proof obligations; consumed by projection
CI/deploy contract (required checks, TUI startup canary, atomic promote) KEEP deployment proof chain live-proven repeatedly
Version surfaces + auto-version + semver KEEP bump fast lane + push convergence working (0.66.22 earned this session)
Lint/pyright/coverage ratchets KEEP shrinking-baseline gates, all green
Authority map + dead-subnet ledger (D6) EVOLVE static doc today; Scaffolding projections make it derivable; D6 retire-disposition still owed
Issue reconciliation artifact + checker KEEP tracker truth with drift detection
PL1 experience ledger KEEP the trust/revise/retire input for future capability crystallisation
Intent continuity ledger KEEP PART 6 state machine, 7 invariants
MIMO_AUTOMATIC_RESUME / W/R interactive injection KEEP (bounded) headless single-flight proven; interactive auto-resume NOT YET SAFE until a supported busy signal exists
Duplicate authorities risk REPLACE (avoided) Scaffolding v1 adds NO new registry — projections only

Real-change proof (this tranche)

Change: context discovery hit Ollama's /api/show with cloud-pinned model names — 400 invalid model name noise on every cloud task + 8192 misbudget for a 131072-window model (observed live in every PL1 Task A/B run).

  1. BEFORE mutation: scaffold_projection.py impact local_tool_runtime derived — subsystem capability; consumers rune_operator; gates: full suite, lint ratchet, henxels, inference+orchestration canaries; unit-test obligation tests/test_local_tool_runtime.py (exists).
  2. Change made: is_cloud_pinned — THE provider-prefix rule, single authority in openai_compat_transport.py; consumed by context discovery (new) and transport selection (deduped); red-first regression tests.
  3. Obligations tracked: suite 1314 green · lint ratchet PASS · henxels exit 0 · the projection itself caught two gaps mid-flight (relative imports hidden consumers; a broad except biting the lint ratchet) — both fixed at the source the projection pointed to.
  4. Deploy: normal release chain → deployed identity verified.

Maturity (honest)

  • Projection engine: UNIT-PROVEN + proven on one real change.
  • Not yet: CI integration of scaffold_projection.py all (drift detection), capability discovery / gap recognition / reuse-before-build / external acquisition / provisional-trust lifecycle — FOUNDATIONAL ONLY (architecture does not block them; the projection source-list is the extension surface).
  • Deferred with reasons recorded: none blocked by Tranche 1.

Scaffolding V1 — Tranche 2: Construction Substrate (2026-09-13)

Tranche 2 (contract SCAFFOLD-TRANCHE-001) built the construction substrate itself: a shared structural-store primitive, the Project Scaffold layer over it, and durable Construction Contracts — Rune's first build tracked by the system it builds.

Architecture

src/rune/structural_store.py   shared generic primitive — NO Rune constants
        ↓                               ↓
src/rune/spine.py            src/rune/scaffold.py
(Rune Spine — canonical      (Project Scaffold — canonical
 structural self-model)       construction/structural truth
                              about EXTERNAL projects)

Authority map (no duplicates):

fact authority others
Rune's structure spine.py + store/spine/current.json scaffold only references
external project structure scaffold.py store Spine holds bridge facts only
content history / rollback Git both reference commits
coordination (issues/PRs/CI) Gitea scaffold stores observations
why work exists Task/Intent contracts reference task ids

Construction Contract = ONE durable object (contract:<id>, kind construction_contract) with plain phase/status fields governed by a small legal-transition map (draft → approved → building → verifying → migrating → superseding → closed; failed/abandoned terminal-or-retry). No workflow engine. Resumability = store durability, proven across real process boundaries.

Mechanics

  • Repo binding (bind_repo): forge/owner/name/branch/root/placement as scaffold DATA. No secrets — schema rejects secret-named fields AND every save() recursively redacts credential-named keys (defense in depth in the shared core, so Spine inherits it too).
  • Scope check (check_scope): changed files vs approved scope paths → green / red / unknown (an unscoped contract is never silently green). Out-of-scope change = RED until explicitly amended; amendments recorded, original RED preserved in history.
  • Predicted vs observed: frontier snapshot at approval; observed impact captured at closure from INDEPENDENT evidence (git, tests) — never from re-walking the prediction's own graph. Missed entities become CANDIDATE corrections awaiting evidence.
  • Drift: authority-owned facts that fail re-observation go STALE — never silently fresh, never force-reverted. Reality wins.
  • Bridge (spine_bridge_entity): identity + repo surface only. Project internals NEVER leak into Rune Spine (test-enforced). Contract evidence may cite spine:// URIs — citations are inert on the referenced authority.

Real proof (this tranche on itself)

SCAFFOLD-TRANCHE-001 migrated from its pre-scaffold JSON into the new store, advanced through phases with evidence checkpoints, closed with predicted-vs-observed from git/test evidence, scope check caught the store's own runtime artifacts (RED → explicit amendment → green, history kept), and construction provenance attached into Rune Spine without duplicating authority. A fresh interpreter answers what/why/state/ evidence/changed/next from the store alone — no transcripts.

Maturity (honest)

component state
structural_store core EXERCISED (unit-proven + real use)
Rune Spine after refactor LIVE_PROVEN behavior-neutral (68 tests + identical rebuild)
Project Scaffold EXERCISED
Construction Contract EXERCISED (resumability proven cross-process)
repo binding (identity model) UNIT-PROVEN (local git fixture)
external-repo live proof BLOCKED ON USER REPO SELECTION
Scaffolding overall substrate only — capability-fit discovery, gap decisions, toolchain selection, patterns, maintenance TUI all NOT BUILT (data seams exist)

Scaffolding Tranche 4 — Live Need → Decision → Construction Contract (2026-09-13)

The adoption tranche: a real Need now flows automatically through the Tranche 2/3 mechanisms and produces a governed Construction Contract. The model no longer has to remember the procedure — it is durable Scaffolding.

Live flow

NEED (reference to Task/Intent — pointer, never a copy)
  ↓ normalization (gaps recorded, never invented)
  ↓ structural discovery + fit + resolution (Spine + Project Scaffold)
  ↓ toolchain decision (existing project toolchain wins)
  ↓ layout obligations (USER-LOCKED authority inherited)
  ↓ user-approval gate (material changes require approval; approved
    bounded work never re-asks)
  ↓ DecisionTrace (durable, model-independent)
  ↓ generated Construction Contract → existing build/verify pipeline

Authority map (unchanged — the path CONNECTS, never duplicates)

authority owns
Task/Intent why work exists
Rune Spine Rune structural truth
Project Scaffold external-project structural/construction truth
Layout Contract desired interface constraints (USER-LOCKED = user authority)
Construction Contract governed build obligations/history
Git / Gitea history / execution

Boundary honesty

  • ADAPT/research: the live path records a research ROUTE (TARGETED_EVIDENCE_PROBE / FULL_RESEARCH_PIPELINE) when evidence is insufficient — a seam, NOT an executed pipeline call. No fake integration claimed.
  • BUILD_NEW is never a fallback: insufficient evidence blocks it; normalization gaps block it; merge precedence across authorities requires every consulted authority to agree.
  • No second task engine: one decision function + one trace object.

Contract-quality repair (Tranche 3 defects)

Closure now distinguishes:

predicted_existing_affected  planned changes to existing entities
expected_new                 entities the build PLANNED to create
correctly_predicted          predicted AND independently observed
predicted_but_unaffected     predicted, NOT observed
expected_new_observed        planned creation happened
expected_new_missing         planned creation did NOT happen
genuinely_unexpected_new     observed, planned by neither list

Prediction cannot self-certify observation: the observed set comes ONLY from caller evidence (git/tests/build/deploy). Legacy keys (missed/ needless/observed) preserved — old contracts readable without migration.

Layout-obligation inheritance

A change touching a UI component/region/implementation inherits its Layout Contract as a build obligation. USER-LOCKED obligations state that changing placement requires explicit user authorization. Unrelated backend changes inherit nothing. USER-LOCKED drift is never reconciled by editing the contract — code adapts.

Maturity (honest)

  • Need intake: EXERCISED (real live proof)
  • decision orchestration: LIVE-PROVEN for the bounded live flow
  • capability discovery adoption: EXERCISED
  • fit/gap adoption: EXERCISED
  • toolchain adoption: EXERCISED
  • contract generation: LIVE-PROVEN (one real generated contract)
  • contract-quality prediction: EXERCISED (repaired semantics, one live closure)
  • layout-obligation inheritance: UNIT-PROVEN (fixtures) + real TUI binding
  • Scaffolding overall: adoption path live for one bounded flow — NOT general autonomous construction, NOT universal discovery/toolchain intelligence, NOT Scaffolding complete.

Scaffolding Tranche 9 — Release History + Evidence Correction (2026-09-13)

Gitea Releases are now an evidence-derived human-readable history of Rune's evolution. Plus the Tranche 8 builder-evidence correction.

Release semantics (audited)

surface meaning
VERSION semver in pyproject/init (e.g. 0.66.44)
TAG v, created by auto-version at the BUMP commit
release commit the auto-bump SHA — NOT necessarily final state
converged deployed identity last commit deployed under the same VERSION

Same-version convergence commits (closure/accounting/lock fixes landing and deploying after the bump) are normal; the notes record BOTH the tag commit and the final deployed identity, distinction exposed honestly.

Mechanism

release_notes.py: deterministic evidence extraction from governed records (closed contracts, resolved maintenance, contract-quality records) → structured ReleaseNote → Markdown → Gitea API (POST create / PATCH update). Idempotent: publishing twice for a tag UPDATES, never duplicates. Notes are historical documentation — NOT Memory, NOT structural authority.

T8 evidence correction (§1)

The T8 report claimed a close-from-verifying refusal + reactive superseding transition. Durable checkpoints show NO superseding transition and the phase map permits verifying→closed; live output showed first-attempt close success. The claim was RETRACTED with full provenance (original claim, correction, reason, evidence preserved in the contract record) and recorded as process-defect:unsupported-causal-claim. MiMo adoption maturity survives on the SUPPORTED evidence: scope constrained file edits, pattern obligations forced preservation proof, interface-reported observations, fresh resume.

Maturity

Release-note generator: LIVE-PROVEN (real Gitea, 10 releases). Backfill: 0.66.35→0.66.44 carrying historical-backfill provenance. Pre-0.66.35 history: skipped (weak evidence — honest limitation).

Contract discovery → spine (2026-10-05, #2465 CI correction)

The closed construction contract is the authority for discoveries. Spine rebuild reads its observed entities and candidate corrections; it does not claim a correction has been built. Qualified IDs such as rust-crate:cognition_core keep their declared kind. Bare file paths, test suites and other unqualified references become construction-surface:<reference>, never module. An observed surface may be PRESENT; a candidate stays DECLARED until independently observed. Existing bad records are migrated with their evidence and edge references intact. This matters: the first write-back mapped bare paths to modules, made 22 false module records and crashed CI on the next rebuild. The per-contract quick-reference markdown is generated from the scaffold store at close; it is a view, not another authority.