fix(scaffolding): T22 — connect the MiMo edge + apply the meaning gate on the native path #412

Merged
conductor merged 3 commits from t22-scaffolding-default-path into master 2026-09-23 18:16:35 +01:00
Owner

T22 — Scaffolding default-path wiring repair (reality-wins audit)

Audited the accepted Scaffolding V1 system against the live repo/runtime. No architecture invented — three genuine wiring defects repaired, one honest boundary recorded.

Verified starting state

  • V1_ACCEPTED confirmed: store/scaffold/t21.5_gates.json = 17/17 PASS; capability:scaffolding-v1 acceptance_state ends V1_ACCEPTED.
  • Native default path (T17 §15J) confirmed wired: TUI → RuneOperator.send_task → construction hook → classify_mode → activate → durable state; 10/10 held-outs pass.
  • /build start confirmed fail-closed (T19 19A): classify → mint → refuse on mint failure.

Defects repaired

  1. The MiMo edge was dead. builder-bridge.ts hardcoded the ephemeral /tmp/ci-venv/bin/python (gone) → every bridge call failed → silent degrade → zero preflight injections. The hook was also unversioned. Fixed: canonical config/mimo-hooks/builder-bridge.ts + durable interpreter resolution; install_mimo_hook.py now manages a hook set (hash-verified, drift-detecting).
  2. Native path skipped the canonical meaning layer. construction_intent (§17J interrogative/negation/modal/referent resolver) was imported only by tests; the operator went straight to classify_mode, so a question with a target reference ("explain how this module works") classified EXISTING_PROJECT_CHANGE and recorded an activation for a discussion. Fixed: intent resolves MEANING before the ONE classifier decides mode; DISCUSSION → NOT_CONSTRUCTION (vocabulary unchanged); user override outranks the gate; unavailable intent layer preserves prior behaviour exactly.
  3. Shared Construction Context reported a terminal contract as active. construction_context() selected on status == "open" alone → 8 contracts with phase abandoned reported as active. Fixed: terminal phases derived once from the canonical transition table (scaffold.TERMINAL_PHASES).

Added

  • The bridge capsule surfaces the active Scaffolding construction (contract/phase/scope/obligations/next-action) to the builder as a read-only projection; inject-nothing preserved when no contract is active; never mutates; degrades to [].

Honest boundary (NOT built)

No byte-level enforcement point exists. tool_executor.py never consults construction state — the 19A invariant gates only the /build start route. Establishing MiMo-surface enforcement requires a tool.execute.before governance hook: new architecture, not a repair, so it is recorded as the exact remaining blocker rather than invented here.

Verification

  • 398 passed (construction/operator/bridge/hook/structural/native/parity/depth/intent); native-default matrix 12/12.
  • Real operator-path proof + fresh-process readback: EXISTING_PROJECT_CHANGE activated · question → NOT_CONSTRUCTION · PROJECT_BUILD bootstraps scaffold + registry.
  • Installed hook invoked directly → capsule injected + fire log written (previously zero).
  • LOCAL_MODEL_COMPUTE_EXECUTED = NO.

Propagation

Propagation: rune_operator.py / construction_interface.py / builder_bridge.py map to umbrellas capability + orchestration + perception, whose dependent umbrella is inference. Verified: no provider/model/routing/fallback code touched (T16 constraint 1 preserved); the meaning gate runs in the construction hook before model selection. Evidence: 398 passed across construction/operator/bridge/hook/structural/native/parity/depth/intent suites; test_native_default 12/12.

Verified: the change does not touch model selection, routing, providers or fallback. The meaning gate runs inside the construction hook, which executes before model selection on send_task; model_routing.py, adaptive_router.py, provider_*.py and model_ledger.py are unchanged. T16 governing constraint (1) PROVIDER OWNERSHIP is preserved: Scaffolding still does not choose provider/model/fallback/account — the intent layer only decides whether a turn is a construction demand.

Evidence: 398 passed across the construction/operator/bridge/hook/structural/native/parity/depth/intent suites; test_native_default.py 12/12 including the two new held-outs; the bridge capsule remains bounded (inject-nothing preserved) and adds no model call (builder_bridge_cli.py preflight is deterministic, zero model calls).

Unrelated maintenance included (declared, separable)

d14670f chore(ci): refresh dependency lock — the check_dep_lock.py gate regenerates the lock and compares; three transitive deps released upstream since the lock was generated, so that required check fails for any branch today, independent of this change (diff-cover 10.5.1→10.6.0, multidict 6.9.0→6.9.1, platformdirs 4.11.11→4.11.12). pyproject.toml is untouched — pins only. Drop that commit if you prefer to land the lock refresh separately.

CI note (evidence, not a claim of green)

First CI run exposed two pre-existing, unrelated failures in the full suite (2215 passed / 2 failed):

  • test_tui_banner_uses_the_reviewed_release_version — deterministic, pre-existing on the base: rune_tui.py rendered Rune v0.87.6 while __version__ is 0.88.0. Fixed in 0de88ad (stale-state repair, permitted by the tranche's TUI rule).
  • test_mimo_tty_contract.py::test_e_repeated_timeouts_no_accumulation — counts system-wide sleep 30 processes (pgrep -x -c sleep), so it is sensitive to unrelated activity on the runner. It passes locally (12/12) and is untouched by this change (no timeout/sleep code touched). Not modified — changing a test to make it pass would be weakening a gate.

Rebase onto master (2026-09-23)

Rebased onto master 1bc1cdea (v0.88.2) after the first green CI: the base had moved 7 commits ahead and the release bump had independently updated the same TUI banner line, so the rebase takes master's banner (v0.88.2) and drops that part of my third commit — nothing to carry. Final head: 873d002.

Final commits:

  • eb3a2c6 fix(scaffolding): connect the MiMo edge + meaning gate on the native path
  • a9ce967 chore(ci): refresh dependency lock (upstream patch bumps; declared, separable)
  • 873d002 fix(scaffolding): cover the new paths; two defects the coverage tests found

Local verification after rebase: 521 passed across construction/operator/bridge/hook/structural/native/tui/parity/depth/intent/pipeline-policy/tty-contract suites; changed-lines coverage 85%.

## T22 — Scaffolding default-path wiring repair (reality-wins audit) Audited the accepted Scaffolding V1 system against the live repo/runtime. **No architecture invented** — three genuine wiring defects repaired, one honest boundary recorded. ### Verified starting state - `V1_ACCEPTED` confirmed: `store/scaffold/t21.5_gates.json` = **17/17 PASS**; `capability:scaffolding-v1` acceptance_state ends `V1_ACCEPTED`. - Native default path (T17 §15J) confirmed wired: `TUI → RuneOperator.send_task → construction hook → classify_mode → activate → durable state`; 10/10 held-outs pass. - `/build start` confirmed fail-closed (T19 19A): classify → mint → refuse on mint failure. ### Defects repaired 1. **The MiMo edge was dead.** `builder-bridge.ts` hardcoded the ephemeral `/tmp/ci-venv/bin/python` (gone) → every bridge call failed → silent degrade → **zero** preflight injections. The hook was also **unversioned**. Fixed: canonical `config/mimo-hooks/builder-bridge.ts` + durable interpreter resolution; `install_mimo_hook.py` now manages a hook **set** (hash-verified, drift-detecting). 2. **Native path skipped the canonical meaning layer.** `construction_intent` (§17J interrogative/negation/modal/referent resolver) was imported **only by tests**; the operator went straight to `classify_mode`, so a question with a target reference ("explain how this module works") classified `EXISTING_PROJECT_CHANGE` and recorded an activation for a discussion. Fixed: intent resolves MEANING before the ONE classifier decides mode; `DISCUSSION → NOT_CONSTRUCTION` (vocabulary unchanged); user override outranks the gate; unavailable intent layer preserves prior behaviour exactly. 3. **Shared Construction Context reported a terminal contract as active.** `construction_context()` selected on `status == "open"` alone → **8** contracts with phase `abandoned` reported as active. Fixed: terminal phases derived once from the canonical transition table (`scaffold.TERMINAL_PHASES`). ### Added - The bridge capsule surfaces the active Scaffolding construction (contract/phase/scope/obligations/next-action) to the builder as a **read-only projection**; inject-nothing preserved when no contract is active; never mutates; degrades to `[]`. ### Honest boundary (NOT built) **No byte-level enforcement point exists.** `tool_executor.py` never consults construction state — the 19A invariant gates only the `/build start` route. Establishing MiMo-surface enforcement requires a `tool.execute.before` governance hook: **new architecture**, not a repair, so it is recorded as the exact remaining blocker rather than invented here. ### Verification - 398 passed (construction/operator/bridge/hook/structural/native/parity/depth/intent); native-default matrix 12/12. - Real operator-path proof + fresh-process readback: `EXISTING_PROJECT_CHANGE` activated · question → `NOT_CONSTRUCTION` · `PROJECT_BUILD` bootstraps scaffold + registry. - Installed hook invoked directly → capsule injected + fire log written (previously zero). - `LOCAL_MODEL_COMPUTE_EXECUTED = NO`. --- ### Propagation Propagation: rune_operator.py / construction_interface.py / builder_bridge.py map to umbrellas capability + orchestration + perception, whose dependent umbrella is inference. Verified: no provider/model/routing/fallback code touched (T16 constraint 1 preserved); the meaning gate runs in the construction hook before model selection. Evidence: 398 passed across construction/operator/bridge/hook/structural/native/parity/depth/intent suites; test_native_default 12/12. **Verified:** the change does not touch model selection, routing, providers or fallback. The meaning gate runs inside the construction hook, which executes **before** model selection on `send_task`; `model_routing.py`, `adaptive_router.py`, `provider_*.py` and `model_ledger.py` are unchanged. T16 governing constraint (1) PROVIDER OWNERSHIP is preserved: Scaffolding still does not choose provider/model/fallback/account — the intent layer only decides whether a turn is a construction *demand*. **Evidence:** 398 passed across the construction/operator/bridge/hook/structural/native/parity/depth/intent suites; `test_native_default.py` 12/12 including the two new held-outs; the bridge capsule remains bounded (inject-nothing preserved) and adds no model call (`builder_bridge_cli.py` preflight is deterministic, zero model calls). ### Unrelated maintenance included (declared, separable) `d14670f chore(ci): refresh dependency lock` — the `check_dep_lock.py` gate regenerates the lock and compares; three transitive deps released upstream since the lock was generated, so that required check fails for **any** branch today, independent of this change (`diff-cover 10.5.1→10.6.0`, `multidict 6.9.0→6.9.1`, `platformdirs 4.11.11→4.11.12`). `pyproject.toml` is untouched — pins only. Drop that commit if you prefer to land the lock refresh separately. ### CI note (evidence, not a claim of green) First CI run exposed two pre-existing, unrelated failures in the full suite (2215 passed / 2 failed): - `test_tui_banner_uses_the_reviewed_release_version` — **deterministic, pre-existing on the base**: `rune_tui.py` rendered `Rune v0.87.6` while `__version__` is `0.88.0`. **Fixed in `0de88ad`** (stale-state repair, permitted by the tranche's TUI rule). - `test_mimo_tty_contract.py::test_e_repeated_timeouts_no_accumulation` — counts **system-wide** `sleep 30` processes (`pgrep -x -c sleep`), so it is sensitive to unrelated activity on the runner. It **passes locally** (12/12) and is untouched by this change (no timeout/sleep code touched). **Not modified** — changing a test to make it pass would be weakening a gate. ### Rebase onto master (2026-09-23) Rebased onto master `1bc1cdea` (v0.88.2) after the first green CI: the base had moved 7 commits ahead and the release bump had **independently** updated the same TUI banner line, so the rebase takes master's banner (`v0.88.2`) and drops that part of my third commit — nothing to carry. Final head: `873d002`. Final commits: - `eb3a2c6` fix(scaffolding): connect the MiMo edge + meaning gate on the native path - `a9ce967` chore(ci): refresh dependency lock (upstream patch bumps; declared, separable) - `873d002` fix(scaffolding): cover the new paths; two defects the coverage tests found Local verification after rebase: **521 passed** across construction/operator/bridge/hook/structural/native/tui/parity/depth/intent/pipeline-policy/tty-contract suites; changed-lines coverage 85%.
gitea-actions Bot added the buggovernanceinfrastructurememoryperception labels 2026-09-23 17:18:54 +01:00

🤖 Headless spec gate (non-blocking, #204 first slice)

Spec-gate agent produced no text output — check runner logs.

Deterministic floors still govern the merge

This comment is advisory. Required checks (lint, tests, canaries, type-check, coverage, dependency lock, impact gate) remain the merge authority per docs/adversarial-review.md proportionality.

🤖 **Headless spec gate (non-blocking, #204 first slice)** Spec-gate agent produced no text output — check runner logs. <details><summary>Deterministic floors still govern the merge</summary> This comment is advisory. Required checks (lint, tests, canaries, type-check, coverage, dependency lock, impact gate) remain the merge authority per docs/adversarial-review.md proportionality.</details>

🤖 Headless spec gate (non-blocking, #204 first slice)

Spec-gate agent produced no text output — check runner logs.

Deterministic floors still govern the merge

This comment is advisory. Required checks (lint, tests, canaries, type-check, coverage, dependency lock, impact gate) remain the merge authority per docs/adversarial-review.md proportionality.

🤖 **Headless spec gate (non-blocking, #204 first slice)** Spec-gate agent produced no text output — check runner logs. <details><summary>Deterministic floors still govern the merge</summary> This comment is advisory. Required checks (lint, tests, canaries, type-check, coverage, dependency lock, impact gate) remain the merge authority per docs/adversarial-review.md proportionality.</details>

🤖 Headless spec gate (non-blocking, #204 first slice)

Spec-gate agent produced no text output — check runner logs.

Deterministic floors still govern the merge

This comment is advisory. Required checks (lint, tests, canaries, type-check, coverage, dependency lock, impact gate) remain the merge authority per docs/adversarial-review.md proportionality.

🤖 **Headless spec gate (non-blocking, #204 first slice)** Spec-gate agent produced no text output — check runner logs. <details><summary>Deterministic floors still govern the merge</summary> This comment is advisory. Required checks (lint, tests, canaries, type-check, coverage, dependency lock, impact gate) remain the merge authority per docs/adversarial-review.md proportionality.</details>
conductor added 3 commits 2026-09-23 18:11:06 +01:00
Reality-wins audit of the accepted Scaffolding V1 system against the live
repo/runtime found three genuine wiring defects (no architecture invented):

1. The MiMo edge was DEAD. `~/.mimocode/hooks/builder-bridge.ts` hardcoded
   the ephemeral interpreter `/tmp/ci-venv/bin/python`, which no longer
   exists, so every bridge call failed and the hook silently degraded —
   MiMo received NO Rune Core preflight at all (zero fire-log lines). The
   hook was also UNVERSIONED (no canonical copy), which is how it rotted.
   Repair: canonical source `config/mimo-hooks/builder-bridge.ts` with
   durable interpreter resolution (env override > repo venv > user venv >
   /usr/bin/python3), installed + hash-verified + drift-checked by
   `install_mimo_hook.py`, which now manages a hook SET rather than one file.

2. The native path skipped the canonical meaning layer. `construction_intent`
   (the §17J/§17L/§17M intent resolver: interrogative structure, negation,
   request modals, referent/proposal resolution) existed but was imported
   ONLY by tests. `rune_operator._construction_activation` went straight to
   `classify_mode`, so a pure question carrying a target reference
   ("explain how this module works") classified EXISTING_PROJECT_CHANGE and
   recorded a construction activation for a discussion turn. Repair: the
   intent layer now resolves MEANING before the ONE classifier decides mode
   (the boundary the intent module documents). DISCUSSION maps to
   NOT_CONSTRUCTION — the canonical vocabulary is unchanged, an explicit
   user override (/scaffold-mode) outranks the gate, and an unavailable
   intent layer preserves the previous behaviour exactly rather than
   silently changing governance.

3. The shared Construction Context reported a TERMINAL contract as active.
   `construction_context()` selected on `status == "open"` alone, so 8
   contracts with phase `abandoned` (terminal) were reported as the active
   construction. Repair: terminal phases are now DERIVED once from the
   canonical transition table (`scaffold.TERMINAL_PHASES`), and a contract
   is active only when its phase is non-terminal.

Also: the bridge capsule now surfaces the active Scaffolding construction
(contract/phase/scope/obligations/next-action) to the builder as a
read-only projection of the shared Construction Context — the MiMo edge of
state Scaffolding already owns. Inject-nothing is preserved when no
contract is active; the projection never mutates and degrades to [].

Spine projection refreshed (import graph legitimately gained importers).

Honest boundary: no byte-level enforcement point exists. `tool_executor.py`
never consults construction state, so the 19A invariant gates only the
/build-start route. Establishing MiMo-surface enforcement is NEW
architecture (a tool.execute.before governance hook) and was NOT built
here — it is recorded as the exact remaining blocker.

Verification: 511 passed across the construction/operator/bridge/hook/
structural/parity suites; native-default matrix 12/12; real operator-path
proof with fresh-process readback (EXISTING_PROJECT_CHANGE activated,
question -> NOT_CONSTRUCTION, PROJECT_BUILD bootstraps scaffold + registry);
installed hook invoked directly -> capsule injected + fire log written.
LOCAL_MODEL_COMPUTE_EXECUTED = NO.
`scripts/check_dep_lock.py` (CI step "#150: deps come ONLY from the pinned
lock") regenerates the lock into a temp path and compares. Three transitive
deps released upstream since the lock was generated, so the check fails for
ANY branch today, independent of the T22 change:

  diff-cover  10.5.1  -> 10.6.0
  multidict   6.9.0   -> 6.9.1
  platformdirs 4.11.11 -> 4.11.12

Regenerated with the exact command the gate instructs. Note: compiling with
`-o requirements-ci.txt` is a NO-OP for the pins (uv prefers the existing
output file's versions); the fresh resolution must be written to a new path
and moved into place, which is what the checker itself does.

No dependency declarations changed (pyproject.toml untouched) — pins only.
fix(scaffolding): T22 — cover the new paths; two defects the coverage tests found
CI Pipeline / best-practices (push) Successful in 3s
CI Pipeline / documentation (push) Successful in 2s
Auto-label / priority-gate (pull_request) Skipped
Auto-label / auto-label (pull_request) Successful in 1s
CI Pipeline / canary (push) Successful in 29s
CI Pipeline / type-check (push) Successful in 27s
CI Pipeline / lint (push) Successful in 35s
CI Pipeline / gitea-offline (push) Successful in 13s
CI Pipeline / lint (pull_request) Successful in 32s
CI Pipeline / best-practices (pull_request) Successful in 1s
CI Pipeline / documentation (pull_request) Successful in 1s
CI Pipeline / canary (pull_request) Successful in 26s
CI Pipeline / gitea-offline (pull_request) Successful in 13s
Spec gate (headless) / spec-gate (pull_request) Successful in 3s
deploy / deploy (push) Skipped
CI Pipeline / type-check (pull_request) Successful in 26s
CI Pipeline / unit-tests (push) Successful in 4m44s
CI Pipeline / required-checks (push) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 1s
CI Pipeline / unit-tests (pull_request) Successful in 4m40s
CI Pipeline / required-checks (pull_request) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (pull_request) Successful in 2s
Auto-version and Release / release (pull_request) Successful in 8s
873d0025ad
Self-review against the CI gates surfaced two genuine defects in the first
commit. Changed-lines coverage is now 85% (floor 70):

1. The bridge projection read the WRONG STORE. `_active_construction_lines`
   built `ConstructionInterface` without `project_store`, so it fell back to
   the interface default instead of the configured construction store — the
   `RUNE_CONSTRUCTION_STORE` override was ignored, so a redirected
   deployment would have projected another project's construction state.
   Fixed: pass `_construction_store()` + the project id.

2. LOST UPDATE between the intent-layer note and the main activation path.
   The degrade path called `_construction_note()`, which does its own
   load/save; the normal path then saved its own stale `doc`, silently
   clobbering the note. Fixed: append the note to the SAME in-memory doc
   (one doc, one save), so the failure is recorded and survives.

New coverage: active-contract projection + capsule rendering + read-only
guarantee; store-resolution override; intent-layer-unavailable degrade path.

Note: the stale TUI banner (`Rune v0.87.6` vs `__version__` 0.88.0) that
this commit also fixed on the old base was fixed independently on master by
the 0.88.2 release bump, so the rebase takes master's banner — nothing to
carry here.
conductor force-pushed t22-scaffolding-default-path from 0de88adbdd to 873d0025ad 2026-09-23 18:11:06 +01:00 Compare
gitea-actions Bot added the custodian label 2026-09-23 18:11:20 +01:00

🤖 Headless spec gate (non-blocking, #204 first slice)

Spec-gate agent produced no text output — check runner logs.

Deterministic floors still govern the merge

This comment is advisory. Required checks (lint, tests, canaries, type-check, coverage, dependency lock, impact gate) remain the merge authority per docs/adversarial-review.md proportionality.

🤖 **Headless spec gate (non-blocking, #204 first slice)** Spec-gate agent produced no text output — check runner logs. <details><summary>Deterministic floors still govern the merge</summary> This comment is advisory. Required checks (lint, tests, canaries, type-check, coverage, dependency lock, impact gate) remain the merge authority per docs/adversarial-review.md proportionality.</details>
conductor merged commit da36b99184 into master 2026-09-23 18:16:35 +01:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: conductor/rune#412