Compare commits

...
Author SHA1 Message Date
Nick 9583678997 chore(memory-t0): as-is memory inventory — MEMORY_T0_COMPLETE (17/17 gates)
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 10s
Auto-label / priority-gate (pull_request) Skipped
CI Pipeline / canary (push) Successful in 29s
deploy / deploy (push) Skipped
Auto-label / auto-label (pull_request) Successful in 6s
CI Pipeline / gitea-offline (push) Successful in 18s
CI Pipeline / type-check (push) Successful in 31s
CI Pipeline / best-practices (pull_request) Successful in 10s
CI Pipeline / documentation (pull_request) Successful in 6s
CI Pipeline / lint (pull_request) Successful in 50s
CI Pipeline / gitea-offline (pull_request) Successful in 16s
CI Pipeline / type-check (pull_request) Successful in 29s
CI Pipeline / canary (pull_request) Successful in 31s
CI Pipeline / lint (push) Successful in 42s
CI Pipeline / unit-tests (push) Successful in 3m19s
CI Pipeline / required-checks (push) Successful in 2s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 7s
CI Pipeline / unit-tests (pull_request) Successful in 3m26s
CI Pipeline / required-checks (pull_request) Successful in 2s
CI Pipeline / CI Pipeline / notify-event-bridge (pull_request) Successful in 6s
Auto-version and Release / release (pull_request) Successful in 1m0s
Read-mostly archaeology: one canonical experiential store (memory.db:
899 entries, hybrid dense+BM25 recall, dedupe/supersede gate with 1465
receipts), derived knowledge.json (7965 items), dormant graph/shards/
temporal, harness-native duplicate-authority surface identified, INWARD
port verified as unbound-but-real contract. DEFECT FOUND (recorded, not
fixed): sleep-time systemd timer reads stale opencode.db — designed
transcript-mining loop broken; consolidation survives via per-turn
mandate. Preservation risks classified; fresh-worker answers embedded.
2026-09-17 20:10:03 +01:00
conductor 5db8b202d9 chore(release): auto-bump version to 0.83.3 — earned by #332 (chore(t21.5): acceptance V1_ACCEPTED — 17/17 gates, ledger readback)
Auto-version and Release / release (push) Successful in 25s
CI Pipeline / unit-tests (push) Successful in 3m27s
CI Pipeline / lint (push) Successful in 49s
CI Pipeline / canary (push) Successful in 31s
CI Pipeline / best-practices (push) Successful in 6s
CI Pipeline / documentation (push) Successful in 8s
CI Pipeline / type-check (push) Successful in 33s
CI Pipeline / gitea-offline (push) Successful in 18s
CI Pipeline / required-checks (push) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 5s
deploy / deploy (push) Successful in 5m12s
Promotion record: #332
2026-09-17 18:30:17 +01:00
conductor 78c0d97a43 Merge pull request 'chore(t21.5): acceptance V1_ACCEPTED — 17/17 gates, ledger readback' (#332) from t21.5-pending-class-fix into master
CI Pipeline / best-practices (push) Successful in 11s
CI Pipeline / documentation (push) Successful in 12s
CI Pipeline / canary (push) Successful in 39s
Auto-version and Release / release (push) Successful in 33s
CI Pipeline / lint (push) Successful in 50s
CI Pipeline / gitea-offline (push) Successful in 17s
CI Pipeline / type-check (push) Successful in 33s
CI Pipeline / unit-tests (push) Successful in 3m25s
CI Pipeline / required-checks (push) Successful in 4s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 7s
deploy / deploy (push) Successful in 6m38s
2026-09-17 18:28:19 +01:00
conductor 551530c78c Merge branch 'master' into t21.5-pending-class-fix
CI Pipeline / best-practices (push) Successful in 9s
CI Pipeline / documentation (push) Successful in 9s
CI Pipeline / gitea-offline (push) Successful in 18s
CI Pipeline / lint (push) Successful in 50s
CI Pipeline / canary (push) Successful in 33s
CI Pipeline / type-check (push) Successful in 31s
CI Pipeline / best-practices (pull_request) Successful in 8s
CI Pipeline / lint (pull_request) Successful in 43s
CI Pipeline / documentation (pull_request) Successful in 6s
CI Pipeline / gitea-offline (pull_request) Successful in 18s
CI Pipeline / type-check (pull_request) Successful in 31s
deploy / deploy (push) Skipped
Spec gate (headless) / spec-gate (pull_request) Successful in 15s
CI Pipeline / canary (pull_request) Successful in 35s
CI Pipeline / unit-tests (pull_request) Successful in 3m32s
CI Pipeline / required-checks (pull_request) Successful in 2s
CI Pipeline / CI Pipeline / notify-event-bridge (pull_request) Successful in 6s
CI Pipeline / unit-tests (push) Successful in 3m23s
CI Pipeline / required-checks (push) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 5s
Auto-version and Release / release (pull_request) Failing after 1m11s
2026-09-17 18:22:01 +01:00
Nick b669e4d4ed chore(t21.5): acceptance V1_ACCEPTED — can_accept()=true, 17/17 gates PASS; scaffolding-v1 PATCH 1.0.4; event-wait-resume MINOR 0.4.0
Auto-label / auto-label (pull_request) Successful in 4s
Auto-label / priority-gate (pull_request) Skipped
CI Pipeline / lint (pull_request) Successful in 45s
CI Pipeline / canary (pull_request) Successful in 33s
CI Pipeline / documentation (pull_request) Successful in 6s
CI Pipeline / best-practices (pull_request) Successful in 9s
CI Pipeline / gitea-offline (pull_request) Successful in 18s
CI Pipeline / type-check (pull_request) Successful in 31s
Spec gate (headless) / spec-gate (pull_request) Successful in 16s
CI Pipeline / unit-tests (pull_request) Successful in 3m27s
CI Pipeline / required-checks (pull_request) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (pull_request) Successful in 9s
deploy / deploy (push) Skipped
CI Pipeline / best-practices (push) Successful in 2s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / lint (push) Failing after 38s
CI Pipeline / gitea-offline (push) Successful in 17s
CI Pipeline / canary (push) Failing after 35s
CI Pipeline / type-check (push) Successful in 35s
CI Pipeline / unit-tests (push) Failing after 3m54s
CI Pipeline / required-checks (push) Failing after 6s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 5s
2026-09-17 18:16:34 +01:00
conductor 2b6a5a6b95 chore(release): auto-bump version to 0.83.2 — earned by # (patch: converged release (push-to-master bump))
Auto-version and Release / release (push) Successful in 17s
CI Pipeline / lint (push) Successful in 51s
CI Pipeline / best-practices (push) Successful in 6s
CI Pipeline / documentation (push) Successful in 6s
CI Pipeline / type-check (push) Successful in 30s
CI Pipeline / gitea-offline (push) Successful in 18s
CI Pipeline / unit-tests (push) Successful in 3m26s
CI Pipeline / canary (push) Successful in 30s
CI Pipeline / required-checks (push) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 6s
deploy / deploy (push) Successful in 5m18s
Promotion record: (unknown)
2026-09-17 17:52:32 +01:00
conductor e2e7b74a8e chore(release): auto-bump version to 0.83.2 — earned by #331 (fix(t21.5): pending-class re-arm — residual nonterminal-return generator closed)
Auto-version and Release / release (push) Successful in 14s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 7s
deploy / deploy (push) Successful in 7m44s
CI Pipeline / lint (push) Successful in 45s
CI Pipeline / unit-tests (push) Successful in 3m29s
CI Pipeline / canary (push) Successful in 28s
CI Pipeline / best-practices (push) Successful in 5s
CI Pipeline / documentation (push) Successful in 5s
CI Pipeline / type-check (push) Successful in 31s
CI Pipeline / gitea-offline (push) Successful in 17s
CI Pipeline / required-checks (push) Successful in 1s
Promotion record: #331
2026-09-17 17:51:11 +01:00
conductor db4ce7f6a6 Merge pull request 'fix(t21.5): pending-class re-arm — residual nonterminal-return generator closed' (#331) from t21.5-pending-class-fix into master
CI Pipeline / documentation (push) Successful in 10s
CI Pipeline / canary (push) Successful in 42s
CI Pipeline / type-check (push) Successful in 34s
CI Pipeline / best-practices (push) Successful in 10s
CI Pipeline / lint (push) Successful in 39s
Auto-version and Release / release (push) Successful in 59s
CI Pipeline / unit-tests (push) Successful in 3m34s
CI Pipeline / gitea-offline (push) Successful in 17s
CI Pipeline / required-checks (push) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 7s
deploy / deploy (push) Successful in 12m23s
2026-09-17 17:50:49 +01:00
Nick 505209b839 chore(deps): re-lock requirements-ci.txt (upstream idna 3.20) — dep-lock gate
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 6s
CI Pipeline / canary (push) Successful in 38s
CI Pipeline / lint (push) Successful in 47s
CI Pipeline / type-check (push) Successful in 32s
CI Pipeline / gitea-offline (push) Successful in 17s
CI Pipeline / lint (pull_request) Successful in 39s
CI Pipeline / canary (pull_request) Successful in 31s
CI Pipeline / best-practices (pull_request) Successful in 5s
CI Pipeline / documentation (pull_request) Successful in 5s
CI Pipeline / gitea-offline (pull_request) Successful in 16s
deploy / deploy (push) Skipped
CI Pipeline / type-check (pull_request) Successful in 29s
Spec gate (headless) / spec-gate (pull_request) Successful in 16s
CI Pipeline / unit-tests (push) Successful in 3m24s
CI Pipeline / required-checks (push) Successful in 2s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 7s
CI Pipeline / unit-tests (pull_request) Successful in 3m21s
CI Pipeline / required-checks (pull_request) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (pull_request) Successful in 5s
Auto-version and Release / release (pull_request) Failing after 1m2s
2026-09-17 17:45:38 +01:00
Nick f7256aa29e fix(t21.5): pending-class verdicts and not-started operations re-arm internally — residual nonterminal-return generator closed (§18 audit)
CI Pipeline / best-practices (push) Successful in 11s
CI Pipeline / documentation (push) Successful in 7s
CI Pipeline / lint (push) Failing after 33s
CI Pipeline / canary (push) Successful in 34s
Auto-label / auto-label (pull_request) Successful in 6s
deploy / deploy (push) Skipped
Auto-label / priority-gate (pull_request) Skipped
CI Pipeline / type-check (push) Successful in 29s
CI Pipeline / lint (pull_request) Failing after 32s
CI Pipeline / best-practices (pull_request) Successful in 6s
CI Pipeline / documentation (pull_request) Successful in 4s
CI Pipeline / type-check (pull_request) Successful in 29s
Spec gate (headless) / spec-gate (pull_request) Successful in 23s
CI Pipeline / gitea-offline (push) Successful in 24s
CI Pipeline / canary (pull_request) Successful in 30s
CI Pipeline / gitea-offline (pull_request) Successful in 17s
CI Pipeline / unit-tests (push) Successful in 3m23s
CI Pipeline / required-checks (push) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 14s
CI Pipeline / unit-tests (pull_request) Successful in 3m29s
CI Pipeline / required-checks (pull_request) Failing after 3s
CI Pipeline / CI Pipeline / notify-event-bridge (pull_request) Successful in 8s
2026-09-17 17:38:06 +01:00
conductor 26e3765319 chore(release): auto-bump version to 0.83.1 — earned by #330 (chore(t21.5): final gate evidence (G13/G14, MINOR 0.4.0, PATCH 1.0.4))
Auto-version and Release / release (push) Successful in 17s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 9s
CI Pipeline / lint (push) Successful in 41s
CI Pipeline / unit-tests (push) Successful in 3m10s
CI Pipeline / canary (push) Successful in 33s
CI Pipeline / best-practices (push) Successful in 7s
CI Pipeline / documentation (push) Successful in 6s
CI Pipeline / type-check (push) Successful in 30s
CI Pipeline / gitea-offline (push) Successful in 16s
CI Pipeline / required-checks (push) Successful in 2s
deploy / deploy (push) Successful in 3m51s
Promotion record: #330
2026-09-17 14:36:38 +01:00
conductor e6451a5afc Merge pull request 'chore(t21.5): final gate evidence (G13/G14, MINOR 0.4.0, PATCH 1.0.4)' (#330) from t21.5-gate-evidence into master
CI Pipeline / best-practices (push) Successful in 14s
CI Pipeline / documentation (push) Successful in 6s
CI Pipeline / canary (push) Successful in 42s
CI Pipeline / lint (push) Successful in 58s
CI Pipeline / gitea-offline (push) Successful in 15s
CI Pipeline / type-check (push) Successful in 36s
Auto-version and Release / release (push) Successful in 26s
CI Pipeline / unit-tests (push) Successful in 3m21s
CI Pipeline / required-checks (push) Successful in 2s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 5s
deploy / deploy (push) Successful in 6m41s
2026-09-17 14:36:12 +01:00
Nick 7008743620 chore(t21.5): gate evidence G13/G14 + event-wait-resume MINOR 0.4.0 + scaffolding-v1 PATCH 1.0.4
CI Pipeline / best-practices (push) Successful in 11s
CI Pipeline / documentation (push) Successful in 6s
Auto-label / auto-label (pull_request) Successful in 5s
CI Pipeline / canary (push) Successful in 36s
Auto-label / priority-gate (pull_request) Skipped
CI Pipeline / lint (push) Successful in 45s
deploy / deploy (push) Skipped
CI Pipeline / gitea-offline (push) Successful in 16s
CI Pipeline / type-check (push) Successful in 33s
CI Pipeline / documentation (pull_request) Successful in 7s
CI Pipeline / best-practices (pull_request) Successful in 7s
CI Pipeline / lint (pull_request) Successful in 43s
CI Pipeline / gitea-offline (pull_request) Successful in 18s
Spec gate (headless) / spec-gate (pull_request) Successful in 16s
CI Pipeline / type-check (pull_request) Successful in 33s
CI Pipeline / canary (pull_request) Successful in 33s
CI Pipeline / unit-tests (push) Successful in 3m8s
CI Pipeline / required-checks (push) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 6s
CI Pipeline / unit-tests (pull_request) Successful in 3m21s
CI Pipeline / required-checks (pull_request) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (pull_request) Successful in 5s
Auto-version and Release / release (pull_request) Successful in 1m0s
2026-09-17 14:29:56 +01:00
conductor f545f1ed8d chore(release): auto-bump version to 0.83.0 — earned by # (minor: converged release (push-to-master bump))
Auto-version and Release / release (push) Successful in 21s
CI Pipeline / lint (push) Successful in 45s
CI Pipeline / canary (push) Successful in 31s
CI Pipeline / best-practices (push) Successful in 6s
CI Pipeline / documentation (push) Successful in 5s
CI Pipeline / type-check (push) Successful in 28s
CI Pipeline / gitea-offline (push) Successful in 18s
CI Pipeline / unit-tests (push) Successful in 3m3s
CI Pipeline / required-checks (push) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 6s
deploy / deploy (push) Successful in 3m35s
Promotion record: (unknown)
2026-09-17 13:47:17 +01:00
conductor 73803e6a0d chore(release): auto-bump version to 0.83.0 — earned by #329 (feat(t21.5): root-cause continuation + guard retirement + V1 parity)
Auto-version and Release / release (push) Successful in 22s
CI Pipeline / required-checks (push) Successful in 1s
CI Pipeline / lint (push) Successful in 40s
CI Pipeline / unit-tests (push) Successful in 3m10s
CI Pipeline / canary (push) Successful in 29s
CI Pipeline / best-practices (push) Successful in 5s
CI Pipeline / documentation (push) Successful in 7s
CI Pipeline / type-check (push) Successful in 29s
CI Pipeline / gitea-offline (push) Successful in 19s
deploy / deploy (push) Successful in 6m31s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 7s
Promotion record: #329
2026-09-17 13:45:40 +01:00
conductor ed1696606e Merge pull request 'feat(t21.5): root-cause continuation + guard retirement + V1 parity' (#329) from t21.5-root-cause-continuation into master
CI Pipeline / best-practices (push) Successful in 13s
CI Pipeline / canary (push) Successful in 35s
CI Pipeline / lint (push) Successful in 48s
CI Pipeline / gitea-offline (push) Successful in 17s
Auto-version and Release / release (push) Successful in 41s
CI Pipeline / unit-tests (push) Successful in 3m11s
CI Pipeline / documentation (push) Successful in 5s
CI Pipeline / type-check (push) Successful in 30s
CI Pipeline / required-checks (push) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 6s
deploy / deploy (push) Successful in 11m13s
2026-09-17 13:45:14 +01:00
Nick 4944f9e41c test(t21.5): no-spec fallback coverage (ratchet -> 94%)
CI Pipeline / best-practices (push) Successful in 10s
CI Pipeline / documentation (push) Successful in 6s
CI Pipeline / canary (push) Successful in 36s
CI Pipeline / lint (push) Successful in 49s
CI Pipeline / gitea-offline (push) Successful in 16s
CI Pipeline / type-check (push) Successful in 30s
CI Pipeline / documentation (pull_request) Successful in 15s
CI Pipeline / canary (pull_request) Successful in 35s
CI Pipeline / gitea-offline (pull_request) Successful in 17s
CI Pipeline / type-check (pull_request) Successful in 30s
CI Pipeline / best-practices (pull_request) Successful in 10s
deploy / deploy (push) Skipped
CI Pipeline / unit-tests (push) Successful in 3m10s
Spec gate (headless) / spec-gate (pull_request) Successful in 14s
CI Pipeline / required-checks (push) Successful in 2s
CI Pipeline / lint (pull_request) Successful in 41s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 5s
CI Pipeline / unit-tests (pull_request) Successful in 3m11s
CI Pipeline / required-checks (pull_request) Successful in 2s
CI Pipeline / CI Pipeline / notify-event-bridge (pull_request) Successful in 7s
Auto-version and Release / release (pull_request) Successful in 59s
2026-09-17 13:39:52 +01:00
Nick 6722066c79 test(t21.5): coverage for awareness error paths, dispatch wrap, event_wait fallback (ratchet 69 -> 80%)
CI Pipeline / best-practices (push) Successful in 11s
CI Pipeline / documentation (push) Successful in 6s
CI Pipeline / canary (push) Successful in 38s
CI Pipeline / lint (push) Successful in 47s
CI Pipeline / type-check (push) Successful in 30s
CI Pipeline / gitea-offline (push) Successful in 17s
CI Pipeline / lint (pull_request) Successful in 40s
CI Pipeline / canary (pull_request) Successful in 31s
CI Pipeline / best-practices (pull_request) Successful in 5s
CI Pipeline / gitea-offline (pull_request) Successful in 17s
deploy / deploy (push) Skipped
CI Pipeline / type-check (pull_request) Successful in 30s
Spec gate (headless) / spec-gate (pull_request) Successful in 15s
CI Pipeline / documentation (pull_request) Successful in 5s
CI Pipeline / unit-tests (push) Successful in 3m12s
CI Pipeline / required-checks (push) Successful in 2s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 5s
CI Pipeline / unit-tests (pull_request) Successful in 3m11s
CI Pipeline / required-checks (pull_request) Successful in 3s
CI Pipeline / CI Pipeline / notify-event-bridge (pull_request) Successful in 4s
2026-09-17 13:35:08 +01:00
Nick df8262b9ed fix(t21.5): pyright warning cleanup in event_wait loader + activation awareness (ratchet 456 -> baseline)
CI Pipeline / best-practices (push) Successful in 10s
CI Pipeline / documentation (push) Successful in 8s
CI Pipeline / canary (push) Successful in 36s
CI Pipeline / lint (push) Successful in 45s
CI Pipeline / type-check (push) Successful in 30s
CI Pipeline / gitea-offline (push) Successful in 17s
CI Pipeline / lint (pull_request) Successful in 39s
CI Pipeline / best-practices (pull_request) Successful in 6s
CI Pipeline / canary (pull_request) Successful in 35s
CI Pipeline / gitea-offline (pull_request) Successful in 22s
deploy / deploy (push) Skipped
CI Pipeline / type-check (pull_request) Successful in 36s
CI Pipeline / documentation (pull_request) Successful in 9s
Spec gate (headless) / spec-gate (pull_request) Successful in 18s
CI Pipeline / unit-tests (push) Failing after 3m9s
CI Pipeline / required-checks (push) Failing after 2s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 6s
CI Pipeline / unit-tests (pull_request) Failing after 3m15s
CI Pipeline / required-checks (pull_request) Failing after 2s
CI Pipeline / CI Pipeline / notify-event-bridge (pull_request) Successful in 7s
2026-09-17 13:16:02 +01:00
Nick 756ebe6201 fix(t21.5): pyright-clean provider loader; harden test Daemon fixture against bind races (retry on fresh ephemeral port)
CI Pipeline / best-practices (push) Successful in 19s
CI Pipeline / documentation (push) Successful in 6s
CI Pipeline / canary (push) Successful in 45s
CI Pipeline / lint (push) Successful in 1m1s
CI Pipeline / gitea-offline (push) Successful in 15s
CI Pipeline / type-check (push) Failing after 34s
CI Pipeline / lint (pull_request) Successful in 45s
CI Pipeline / canary (pull_request) Successful in 41s
CI Pipeline / best-practices (pull_request) Successful in 6s
CI Pipeline / documentation (pull_request) Successful in 8s
CI Pipeline / gitea-offline (pull_request) Successful in 24s
CI Pipeline / type-check (pull_request) Failing after 33s
deploy / deploy (push) Skipped
Spec gate (headless) / spec-gate (pull_request) Successful in 17s
CI Pipeline / unit-tests (push) Failing after 3m23s
CI Pipeline / required-checks (push) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 6s
CI Pipeline / unit-tests (pull_request) Failing after 3m18s
CI Pipeline / required-checks (pull_request) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (pull_request) Successful in 7s
2026-09-17 13:06:17 +01:00
Nick f64ee95aad feat(t21.5): root-cause continuation + guard retirement + V1 parity
Auto-label / priority-gate (pull_request) Skipped
CI Pipeline / best-practices (push) Successful in 27s
Auto-label / auto-label (pull_request) Successful in 7s
CI Pipeline / documentation (push) Successful in 9s
CI Pipeline / canary (push) Successful in 54s
CI Pipeline / lint (push) Successful in 1m9s
CI Pipeline / type-check (push) Failing after 38s
CI Pipeline / gitea-offline (push) Successful in 28s
CI Pipeline / best-practices (pull_request) Successful in 17s
CI Pipeline / documentation (pull_request) Successful in 12s
CI Pipeline / canary (pull_request) Successful in 47s
CI Pipeline / gitea-offline (pull_request) Successful in 33s
CI Pipeline / type-check (pull_request) Failing after 55s
deploy / deploy (push) Skipped
Spec gate (headless) / spec-gate (pull_request) Successful in 23s
CI Pipeline / unit-tests (push) Failing after 3m40s
CI Pipeline / required-checks (push) Failing after 9s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 19s
CI Pipeline / lint (pull_request) Successful in 1m4s
CI Pipeline / unit-tests (pull_request) Failing after 3m38s
CI Pipeline / required-checks (pull_request) Failing after 8s
CI Pipeline / CI Pipeline / notify-event-bridge (pull_request) Successful in 13s
- ci_event_wait: OPERATION_SPECS (op-specific completion authority:
  branch_ci/pull_request_checks -> own runs; master_ci/deploy/release ->
  deploy qualification); SINGLE LOGICAL AWAIT (all nonterminal conditions
  re-arm internally; model sees only terminal verdicts or real deadline)
- await-operation tool: op_type parameter
- runtime wait guard + cwd guard RETIRED (canonical hook = documented
  no-op; prevention lives in tests, not interception)
- wr_root_resolver.py: ONE wait/resume root authority; split-brain fails
  loudly; start_wr_daemon.py resolves root once; governed reconciliation
- construction_activation._auto_structural_awareness: PROJECT_BUILD
  bootstrap auto-registers Registry + spine manages edge (no manual
  bookkeeping); fixed latent dispatch_from_decision mode-object bug
- research_memory_port.py: future-facing Memory port inside existing
  INWARD (provider contract, zero-result fast path, teachable-moment
  candidate seam; APPLICABILITY governs transfer)
- v1_parity_manifest.py: 56 rows, 0 blocking mandatory-V1 items
- G1-G12 gate evidence in store/scaffold/t21.5_gates.json
2026-09-17 12:56:52 +01:00
conductor c3a0a48ca5 chore(release): auto-bump version to 0.82.3 — earned by #328 (chore(t21.4): final gate evidence — V1_RETEST_REQUIRED (10 PASS / 2 BLOCKED))
Auto-version and Release / release (push) Successful in 32s
CI Pipeline / lint (push) Successful in 37s
CI Pipeline / unit-tests (push) Successful in 2m46s
CI Pipeline / canary (push) Successful in 33s
CI Pipeline / best-practices (push) Successful in 10s
CI Pipeline / documentation (push) Successful in 9s
CI Pipeline / type-check (push) Successful in 30s
CI Pipeline / gitea-offline (push) Successful in 20s
CI Pipeline / required-checks (push) Successful in 2s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 6s
deploy / deploy (push) Successful in 4m39s
Promotion record: #328
2026-09-17 03:40:45 +01:00
conductor b4ed258121 Merge pull request 'chore(t21.4): final gate evidence — V1_RETEST_REQUIRED (10 PASS / 2 BLOCKED)' (#328) from t21.4-gate-evidence into master
CI Pipeline / documentation (push) Successful in 9s
CI Pipeline / best-practices (push) Successful in 10s
CI Pipeline / canary (push) Successful in 35s
CI Pipeline / gitea-offline (push) Successful in 18s
CI Pipeline / type-check (push) Successful in 29s
Auto-version and Release / release (push) Successful in 28s
CI Pipeline / lint (push) Successful in 39s
CI Pipeline / unit-tests (push) Successful in 2m48s
CI Pipeline / required-checks (push) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 6s
deploy / deploy (push) Successful in 4m16s
2026-09-17 03:40:19 +01:00
Nick bacea2d472 chore(t21.4): final gate accounting — 10 PASS / 2 BLOCKED, can_accept()=false, V1_RETEST_REQUIRED
CI Pipeline / best-practices (push) Successful in 9s
Auto-label / priority-gate (pull_request) Skipped
CI Pipeline / documentation (push) Successful in 6s
CI Pipeline / canary (push) Successful in 32s
Auto-label / auto-label (pull_request) Successful in 12s
CI Pipeline / lint (push) Successful in 43s
deploy / deploy (push) Skipped
CI Pipeline / gitea-offline (push) Successful in 17s
CI Pipeline / type-check (push) Successful in 29s
CI Pipeline / lint (pull_request) Successful in 39s
CI Pipeline / canary (pull_request) Successful in 28s
CI Pipeline / best-practices (pull_request) Successful in 7s
CI Pipeline / documentation (pull_request) Successful in 5s
CI Pipeline / gitea-offline (pull_request) Successful in 17s
CI Pipeline / type-check (pull_request) Successful in 29s
Spec gate (headless) / spec-gate (pull_request) Successful in 17s
CI Pipeline / unit-tests (push) Successful in 2m47s
CI Pipeline / required-checks (push) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 6s
CI Pipeline / unit-tests (pull_request) Successful in 2m43s
CI Pipeline / required-checks (pull_request) Successful in 2s
CI Pipeline / CI Pipeline / notify-event-bridge (pull_request) Successful in 14s
Auto-version and Release / release (pull_request) Successful in 51s
G11 RAW_SELF_AUDIT blocked: 3 inter-cycle unjustified sleeps executed in
wait-guard heredoc blind spot (wait_guard v4 defect filed); 1 guard
intervention post-workflows. G12 consequently blocked. All other gates
PASS with live evidence. Verdict computed by the persisted gate object,
not asserted.
2026-09-17 03:35:17 +01:00
conductor 9acfc0953e chore(release): auto-bump version to 0.82.2 — earned by # (patch: converged release (push-to-master bump))
Auto-version and Release / release (push) Successful in 26s
CI Pipeline / best-practices (push) Successful in 18s
CI Pipeline / documentation (push) Successful in 8s
CI Pipeline / type-check (push) Successful in 30s
CI Pipeline / gitea-offline (push) Successful in 22s
CI Pipeline / unit-tests (push) Successful in 2m43s
CI Pipeline / lint (push) Successful in 41s
CI Pipeline / canary (push) Successful in 31s
CI Pipeline / required-checks (push) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 12s
deploy / deploy (push) Successful in 3m56s
Promotion record: (unknown)
2026-09-17 03:16:16 +01:00
conductor 818aa49043 chore(release): auto-bump version to 0.82.2 — earned by #327 (fix(deploy_qualify): canonical-default server/repo fallback for out-of-CI consum)
Auto-version and Release / release (push) Successful in 28s
CI Pipeline / lint (push) Successful in 40s
CI Pipeline / unit-tests (push) Successful in 2m52s
CI Pipeline / canary (push) Successful in 30s
CI Pipeline / best-practices (push) Successful in 9s
CI Pipeline / documentation (push) Successful in 9s
CI Pipeline / type-check (push) Successful in 31s
CI Pipeline / gitea-offline (push) Successful in 29s
CI Pipeline / required-checks (push) Successful in 2s
deploy / deploy (push) Successful in 2m48s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 7s
Promotion record: #327
2026-09-17 03:14:22 +01:00
conductor 212239ca7b Merge pull request 'fix(deploy_qualify): canonical-default server/repo fallback for out-of-CI consumers' (#327) from t21.4-qualify-defaults-fix into master
CI Pipeline / best-practices (push) Successful in 6s
CI Pipeline / documentation (push) Successful in 10s
CI Pipeline / gitea-offline (push) Successful in 20s
CI Pipeline / type-check (push) Successful in 28s
CI Pipeline / canary (push) Successful in 39s
Auto-version and Release / release (push) Successful in 1m5s
CI Pipeline / unit-tests (push) Successful in 2m45s
CI Pipeline / lint (push) Successful in 40s
CI Pipeline / required-checks (push) Successful in 2s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 7s
deploy / deploy (push) Successful in 6m31s
2026-09-17 03:13:41 +01:00
conductor ae6d4a0d98 Merge branch 'master' into t21.4-qualify-defaults-fix
CI Pipeline / best-practices (pull_request) Successful in 10s
CI Pipeline / documentation (pull_request) Successful in 14s
CI Pipeline / canary (pull_request) Successful in 34s
CI Pipeline / type-check (pull_request) Successful in 29s
CI Pipeline / gitea-offline (pull_request) Successful in 23s
CI Pipeline / lint (push) Successful in 45s
CI Pipeline / canary (push) Successful in 34s
CI Pipeline / best-practices (push) Successful in 5s
CI Pipeline / documentation (push) Successful in 12s
CI Pipeline / gitea-offline (push) Successful in 16s
CI Pipeline / type-check (push) Successful in 33s
CI Pipeline / unit-tests (pull_request) Successful in 2m51s
deploy / deploy (push) Skipped
CI Pipeline / lint (pull_request) Successful in 37s
Spec gate (headless) / spec-gate (pull_request) Successful in 15s
CI Pipeline / required-checks (pull_request) Successful in 3s
CI Pipeline / CI Pipeline / notify-event-bridge (pull_request) Successful in 7s
CI Pipeline / unit-tests (push) Successful in 2m50s
CI Pipeline / required-checks (push) Successful in 2s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 12s
Auto-version and Release / release (pull_request) Failing after 1m11s
2026-09-17 03:08:14 +01:00
Nick 00013f8c0b fix(deploy_qualify): canonical-default Gitea server/repo fallback — out-of-CI consumers got silent pending verdicts
Auto-label / auto-label (pull_request) Successful in 5s
Auto-label / priority-gate (pull_request) Skipped
CI Pipeline / documentation (pull_request) Successful in 7s
CI Pipeline / canary (pull_request) Successful in 38s
CI Pipeline / gitea-offline (pull_request) Successful in 16s
CI Pipeline / best-practices (pull_request) Successful in 13s
CI Pipeline / lint (pull_request) Successful in 41s
CI Pipeline / type-check (pull_request) Successful in 27s
CI Pipeline / unit-tests (pull_request) Successful in 2m51s
CI Pipeline / required-checks (pull_request) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (pull_request) Successful in 7s
deploy / deploy (push) Skipped
CI Pipeline / best-practices (push) Successful in 3s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / lint (push) Failing after 37s
CI Pipeline / gitea-offline (push) Successful in 18s
CI Pipeline / canary (push) Failing after 49s
CI Pipeline / type-check (push) Successful in 41s
CI Pipeline / unit-tests (push) Failing after 4m3s
CI Pipeline / required-checks (push) Failing after 8s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 6s
The await_operation first-class tool (and any local deploy_qualify.py
caller) never exported GITEA_SERVER/GITEA_REPO, so main() printed
'pending' unconditionally — the first-class wait could never resolve
success even against a green, converged deployment. CI behavior is
unchanged (env always exported there); the fallback only fires when
the caller omitted the vars. Root-caused live in T21.4 G10 dogfood.
2026-09-17 03:02:37 +01:00
conductor ba2966e631 chore(release): auto-bump version to 0.82.1 — earned by #326 (T21.4: post-restart final V1 proof — governed rollback, auto execution context, )
Auto-version and Release / release (push) Successful in 21s
CI Pipeline / required-checks (push) Successful in 3s
CI Pipeline / lint (push) Successful in 39s
CI Pipeline / unit-tests (push) Successful in 2m49s
CI Pipeline / canary (push) Successful in 32s
CI Pipeline / best-practices (push) Successful in 10s
CI Pipeline / documentation (push) Successful in 6s
CI Pipeline / type-check (push) Successful in 29s
CI Pipeline / gitea-offline (push) Successful in 16s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 9s
deploy / deploy (push) Successful in 3m10s
Promotion record: #326
2026-09-17 02:45:18 +01:00
conductor 20fcee3f40 Merge pull request 'T21.4: post-restart final V1 proof — governed rollback, auto execution context, material closure, structural coverage' (#326) from t21.4-final-v1-proof into master
CI Pipeline / documentation (push) Successful in 9s
CI Pipeline / best-practices (push) Successful in 12s
CI Pipeline / gitea-offline (push) Successful in 26s
CI Pipeline / canary (push) Successful in 34s
CI Pipeline / type-check (push) Successful in 30s
CI Pipeline / unit-tests (push) Successful in 2m55s
CI Pipeline / lint (push) Successful in 43s
CI Pipeline / required-checks (push) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 8s
deploy / deploy (push) Successful in 2m47s
Auto-version and Release / release (push) Failing after 11m23s
2026-09-17 02:44:36 +01:00
conductor 493d9c2800 Merge branch 'master' into t21.4-final-v1-proof
CI Pipeline / best-practices (push) Successful in 11s
CI Pipeline / documentation (push) Successful in 5s
CI Pipeline / canary (push) Successful in 35s
CI Pipeline / lint (push) Successful in 48s
CI Pipeline / type-check (push) Successful in 28s
CI Pipeline / gitea-offline (push) Successful in 16s
CI Pipeline / lint (pull_request) Successful in 36s
CI Pipeline / canary (pull_request) Successful in 31s
CI Pipeline / best-practices (pull_request) Successful in 4s
CI Pipeline / documentation (pull_request) Successful in 6s
CI Pipeline / gitea-offline (pull_request) Successful in 16s
CI Pipeline / type-check (pull_request) Successful in 28s
deploy / deploy (push) Skipped
Spec gate (headless) / spec-gate (pull_request) Successful in 10s
CI Pipeline / unit-tests (push) Successful in 2m50s
CI Pipeline / required-checks (push) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 4s
CI Pipeline / unit-tests (pull_request) Successful in 2m46s
CI Pipeline / required-checks (pull_request) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (pull_request) Successful in 5s
Auto-version and Release / release (pull_request) Failing after 1m10s
2026-09-17 02:40:14 +01:00
Nick 6b5d8ad023 chore(t21.4): record PR #326 + propagation declaration in gate object
CI Pipeline / best-practices (pull_request) Successful in 5s
CI Pipeline / documentation (pull_request) Successful in 4s
CI Pipeline / canary (pull_request) Successful in 30s
CI Pipeline / type-check (pull_request) Successful in 28s
CI Pipeline / gitea-offline (pull_request) Successful in 15s
Spec gate (headless) / spec-gate (pull_request) Successful in 10s
CI Pipeline / lint (pull_request) Successful in 37s
CI Pipeline / unit-tests (pull_request) Successful in 2m45s
CI Pipeline / required-checks (pull_request) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (pull_request) Successful in 5s
deploy / deploy (push) Skipped
CI Pipeline / best-practices (push) Successful in 3s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / lint (push) Failing after 38s
CI Pipeline / gitea-offline (push) Successful in 20s
CI Pipeline / canary (push) Failing after 40s
CI Pipeline / type-check (push) Successful in 38s
CI Pipeline / unit-tests (push) Failing after 3m56s
CI Pipeline / required-checks (push) Failing after 8s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 7s
2026-09-17 02:36:03 +01:00
Nick f3ef4672f3 test(t21.4): coverage for auto material-change closure + spine entry-point seeding (CI coverage ratchet)
CI Pipeline / canary (push) Successful in 42s
CI Pipeline / type-check (push) Successful in 31s
deploy / deploy (push) Skipped
CI Pipeline / lint (push) Successful in 56s
CI Pipeline / best-practices (push) Successful in 4s
CI Pipeline / gitea-offline (push) Successful in 15s
CI Pipeline / documentation (push) Successful in 4s
CI Pipeline / unit-tests (push) Successful in 3m2s
CI Pipeline / required-checks (push) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 6s
CI Pipeline / best-practices (pull_request) Successful in 8s
CI Pipeline / documentation (pull_request) Successful in 5s
CI Pipeline / unit-tests (pull_request) Failing after 33s
CI Pipeline / canary (pull_request) Successful in 37s
CI Pipeline / lint (pull_request) Successful in 47s
CI Pipeline / type-check (pull_request) Successful in 27s
CI Pipeline / gitea-offline (pull_request) Successful in 18s
Spec gate (headless) / spec-gate (pull_request) Successful in 13s
CI Pipeline / required-checks (pull_request) Failing after 2s
CI Pipeline / CI Pipeline / notify-event-bridge (pull_request) Successful in 4s
Auto-label / priority-gate (pull_request) Skipped
Auto-label / auto-label (pull_request) Successful in 2s
2026-09-17 02:07:15 +01:00
Nick be6bc17537 feat(t21.4): post-restart final V1 proof — governed rollback API, automatic project execution context, material-change closure, structural coverage
CI Pipeline / best-practices (push) Successful in 10s
CI Pipeline / documentation (push) Successful in 4s
CI Pipeline / canary (push) Successful in 37s
CI Pipeline / lint (push) Successful in 46s
deploy / deploy (push) Skipped
CI Pipeline / type-check (push) Successful in 28s
CI Pipeline / gitea-offline (push) Successful in 15s
CI Pipeline / unit-tests (push) Failing after 2m51s
CI Pipeline / required-checks (push) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 6s
- scripts/rollback_generation.py: canonical rollback_generation() on the
  deploy_promotion machinery (shared lock, refuse-before-mutation,
  ancestor-only eligibility, identity+health qualification, durable
  history, revert on failed health). Live B->A->B proven with refusals.
- tool_executor._resolve_execution_cwd: automatic project execution
  context (workdir > project registry resolve_cwd > process); unknown
  projects refused pre-execution. Live A-F proven.
- construction_interface.close(): auto material_change + revision +
  drift/revise flow (rule 19Q.2 integrated, proven live).
- spine: entry-point seeding (__main__ is a runtime path invisible to
  the import graph); subsystem belongs_to membership via coverage audit.
- scripts/structural_coverage_audit.py: 237 modules A157/B41/C28/D11/E0,
  100% meaningful coverage, 100% impl ownership, 0 significant unknowns.
- Scaffolding internal 1.0.2 -> 1.0.3 (PATCH, contract unchanged).
- Gate object T21.4 persisted (12 mandatory gates, GateAccounting).
2026-09-17 01:38:39 +01:00
conductor 40b3731928 chore(release): auto-bump version to 0.82.0 — earned by # (minor: converged release (push-to-master bump))
Auto-version and Release / release (push) Successful in 18s
CI Pipeline / lint (push) Successful in 44s
CI Pipeline / unit-tests (push) Successful in 2m48s
CI Pipeline / canary (push) Successful in 30s
CI Pipeline / best-practices (push) Successful in 9s
CI Pipeline / documentation (push) Successful in 6s
CI Pipeline / type-check (push) Successful in 32s
CI Pipeline / gitea-offline (push) Successful in 16s
CI Pipeline / required-checks (push) Successful in 2s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 11s
deploy / deploy (push) Successful in 2m17s
Promotion record: (unknown)
2026-09-16 23:07:32 +01:00
Nick caa77f8a35 feat(t21.3): acceptance V1_ACCEPTED — each reason closed with correction evidence
CI Pipeline / best-practices (push) Successful in 12s
CI Pipeline / canary (push) Successful in 38s
CI Pipeline / lint (push) Successful in 47s
Auto-version and Release / release (push) Successful in 1m12s
CI Pipeline / gitea-offline (push) Successful in 17s
CI Pipeline / documentation (push) Successful in 6s
CI Pipeline / type-check (push) Successful in 29s
CI Pipeline / unit-tests (push) Successful in 2m55s
CI Pipeline / required-checks (push) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 9s
deploy / deploy (push) Successful in 5m55s
2026-09-16 23:06:30 +01:00
Nick 4d215ea666 feat(t21.3): Scaffolding 1.0.2 PATCH — T21.3 correction fixes (contract unchanged) 2026-09-16 23:06:30 +01:00
conductor fda9b6dda0 chore(release): auto-bump version to 0.81.6 — earned by # (patch: converged release (push-to-master bump))
Auto-version and Release / release (push) Successful in 30s
CI Pipeline / required-checks (push) Successful in 2s
CI Pipeline / lint (push) Successful in 43s
CI Pipeline / unit-tests (push) Successful in 2m49s
CI Pipeline / canary (push) Successful in 30s
CI Pipeline / best-practices (push) Successful in 6s
CI Pipeline / documentation (push) Successful in 5s
CI Pipeline / type-check (push) Successful in 28s
CI Pipeline / gitea-offline (push) Successful in 16s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 6s
deploy / deploy (push) Successful in 2m34s
Promotion record: (unknown)
2026-09-16 22:37:40 +01:00
Nick 116d1ffbeb chore(store): T21.3 remaining evidence + guard fixes
CI Pipeline / documentation (push) Successful in 11s
CI Pipeline / best-practices (push) Successful in 15s
CI Pipeline / gitea-offline (push) Successful in 19s
CI Pipeline / lint (push) Successful in 55s
CI Pipeline / type-check (push) Successful in 32s
Auto-version and Release / release (push) Failing after 1m1s
CI Pipeline / canary (push) Successful in 35s
CI Pipeline / unit-tests (push) Successful in 3m5s
CI Pipeline / required-checks (push) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 6s
deploy / deploy (push) Successful in 6m24s
2026-09-16 22:36:54 +01:00
Nick 040988cf00 fix(test): P → Path reference fix 2026-09-16 22:36:54 +01:00
conductor ffed816465 chore(release): auto-bump version to 0.81.5 — earned by # (patch: converged release (push-to-master bump))
Auto-version and Release / release (push) Successful in 34s
deploy / deploy (push) Failing after 1m52s
CI Pipeline / required-checks (push) Failing after 2s
CI Pipeline / lint (push) Successful in 39s
CI Pipeline / unit-tests (push) Failing after 36s
CI Pipeline / canary (push) Successful in 42s
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 6s
CI Pipeline / type-check (push) Successful in 32s
CI Pipeline / gitea-offline (push) Successful in 19s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 7s
Promotion record: (unknown)
2026-09-16 22:27:49 +01:00
Nick 72ab7dccf7 fix(test): hook parity test uses env-overridable hook target (not test runner's HOME)
CI Pipeline / best-practices (push) Successful in 10s
CI Pipeline / documentation (push) Successful in 4s
CI Pipeline / lint (push) Failing after 41s
CI Pipeline / unit-tests (push) Failing after 44s
CI Pipeline / canary (push) Successful in 35s
Auto-version and Release / release (push) Successful in 56s
CI Pipeline / gitea-offline (push) Successful in 18s
CI Pipeline / type-check (push) Successful in 30s
CI Pipeline / required-checks (push) Failing after 3s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 22s
deploy / deploy (push) Failing after 4m48s
2026-09-16 22:27:14 +01:00
conductor 5d20dc7f51 chore(release): auto-bump version to 0.81.4 — earned by # (patch: converged release (push-to-master bump))
Auto-version and Release / release (push) Successful in 23s
deploy / deploy (push) Failing after 3m51s
CI Pipeline / unit-tests (push) Failing after 2m42s
CI Pipeline / lint (push) Successful in 43s
CI Pipeline / canary (push) Successful in 30s
CI Pipeline / best-practices (push) Successful in 9s
CI Pipeline / documentation (push) Successful in 11s
CI Pipeline / type-check (push) Successful in 32s
CI Pipeline / gitea-offline (push) Successful in 17s
CI Pipeline / required-checks (push) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 6s
Promotion record: (unknown)
2026-09-16 21:58:56 +01:00
Nick 9e903dacaf fix(t21.3): all F821/SyntaxError fixed in src/ scripts/ shared/ (CI-caught, locally preflighted per 21.1L rule)
CI Pipeline / best-practices (push) Successful in 12s
CI Pipeline / documentation (push) Successful in 10s
CI Pipeline / canary (push) Successful in 38s
CI Pipeline / lint (push) Successful in 49s
Auto-version and Release / release (push) Failing after 1m13s
CI Pipeline / gitea-offline (push) Successful in 21s
CI Pipeline / unit-tests (push) Failing after 2m51s
CI Pipeline / type-check (push) Successful in 39s
CI Pipeline / required-checks (push) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 7s
deploy / deploy (push) Failing after 10s
2026-09-16 21:58:06 +01:00
Nick ed1b41abd5 fix(drift_core): remove all dead code with F821 + add missing closing brace (CI-caught) 2026-09-16 21:58:06 +01:00
Nick ff1b950f11 fix(drift_cache): define SCRIPTS_DIR (F821, CI-caught) 2026-09-16 21:58:06 +01:00
Nick 3afc9ff872 fix(shared): remove remaining dead code blocks with F821 (CI-caught) 2026-09-16 21:58:06 +01:00
conductor 28e9c8c1c1 chore(release): auto-bump version to 0.81.3 — earned by # (patch: converged release (push-to-master bump))
Auto-version and Release / release (push) Successful in 25s
deploy / deploy (push) Failing after 2m10s
CI Pipeline / unit-tests (push) Failing after 2m44s
CI Pipeline / lint (push) Successful in 39s
CI Pipeline / canary (push) Successful in 35s
CI Pipeline / best-practices (push) Successful in 6s
CI Pipeline / documentation (push) Successful in 7s
CI Pipeline / type-check (push) Successful in 33s
CI Pipeline / gitea-offline (push) Successful in 19s
CI Pipeline / required-checks (push) Failing after 4s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 15s
Promotion record: (unknown)
2026-09-16 21:13:00 +01:00
Nick 8249854881 fix(shared): remove dead code with F821 undefined names + re-pin platformdirs (CI-caught)
CI Pipeline / best-practices (push) Successful in 12s
CI Pipeline / documentation (push) Successful in 5s
CI Pipeline / lint (push) Failing after 38s
CI Pipeline / canary (push) Successful in 40s
CI Pipeline / type-check (push) Successful in 30s
Auto-version and Release / release (push) Failing after 1m6s
CI Pipeline / unit-tests (push) Failing after 2m50s
CI Pipeline / gitea-offline (push) Successful in 20s
CI Pipeline / required-checks (push) Failing after 2s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 5s
deploy / deploy (push) Failing after 15m11s
2026-09-16 21:12:15 +01:00
48 changed files with 11344 additions and 464 deletions

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
# Rune
**Current release: v0.81.2**
**Current release: v0.83.3**
**Rune is a persistent personal agent designed to accumulate experience, develop reusable competence, and remain recognisably the same agent even when its underlying foundation model changes.**
+42 -101
View File
@@ -1,115 +1,56 @@
import type { Hooks } from "@mimo-ai/plugin"
// event-wait-guard — T21 §21B-3: structural fix for the sleep→poll
// continuation regression observed in T20 (raw execution contained
// `sleep 540; <CI status check>` style commands while the canonical
// event path — scripts/ci_event_wait.py over the W/R bridge — existed
// and worked).
// event-wait-guard — RETIRED (T21.5 §6/§8, 2026-09-17).
//
// v2 precision fix (live finding, 2026-09-16): v1 matched the pattern
// ANYWHERE in the command string — including inside heredocs/quoted
// documentation about the pattern (the guard blocked its own evidence
// file being written). v2 requires the long sleep AND the status poll
// to co-occur in the SAME command segment (; && || | newline separated),
// and ignores content inside heredoc bodies ('<<' markers).
// HISTORY: this hook was the T21 §21B-3 runtime interception layer that
// blocked model-generated sleep→poll continuation (T20 regression), and
// later carried the T21.1 §21.1C cwd guard. Both were DOWNSTREAM patches:
// they corrected the model's output instead of fixing the generator.
//
// SCOPE (deliberately narrow, per mandate):
// BLOCKED: model-generated Bash command SEGMENTS combining a long
// sleep (>= 30s) with a status query of CI/deploy/release state.
// PERMITTED: bare sleep (user delay), short backoff (<30s),
// non-polling commands, heredoc/quoted documentation content,
// user-authored scripts (only interactive tool calls pass here).
const LONG_SLEEP = /\bsleep\s+(\d{2,4})\b/
const TIME_SLEEP = /time\.sleep\((\d{2,4})\)/
const SLEEP_CALL = /sleep\((\d{2,4})\)/
const STATUS_POLL = new RegExp(
[
"actions/runs",
"commits/.*/status",
"required.checks",
"deploy_qualify",
"releases/tags",
"readlink.*rune-code/current",
].join("|"),
"i",
)
const THRESHOLD_SECS = 30
function suspiciousSegments(cmd: string): string[] {
// strip heredoc bodies: anything between <<[-]?WORD ... WORD
const stripped = cmd.replace(
/<<-?\s*['"]?(\w+)['"]?\n[\s\S]*?\n\1/g,
" [heredoc omitted] ",
)
return stripped.split(/[;|\n]|&&|\|\|/).filter(Boolean)
}
// ROOT-CAUSE FIX (T21.5 §2-§5): the generator was ci_event_wait's
// wait loop returning nonterminal control to the model — caused by a
// single hard-coded master-deploy authority that was blind to branch/PR
// operations. T21.5 fixed this upstream:
// - OPERATION_SPECS: per-operation-type completion authority
// (branch_ci/pull_request_checks -> own Actions runs;
// master_ci/deploy/release -> deploy qualification);
// - SINGLE LOGICAL AWAIT: all nonterminal conditions re-arm inside
// ci_event_wait.run(); the model sees only terminal results;
// - structured PendingOperation envelopes (op_type, authority,
// internal_rearm_count) propagate to the controller.
//
// With the generator fixed, runtime interception is retired per the
// tranche rule FIXED UPSTREAM BEHAVIOUR > DOWNSTREAM GUARD CORRECTION.
// Regression prevention lives in the test suite:
// tests/test_single_logical_await.py (no model re-arm contract)
// tests/test_ci_event_wait.py (watchdog=recovery, event=wake)
// tests/test_wait_guard_corpus.py (canonical wait-pattern corpus)
// NOT as a permanent runtime block. The hook file stays installed as a
// documented no-op so canonical-source/install parity (test_hook_drift,
// test_hook_parity) continues to prove the installed runtime matches
// this repository's canonical state.
export const EventWaitGuard: Hooks = {
"tool.execute.before": async (input, output) => {
if (input.tool !== "bash") return
const cmd = (output.args as { command?: string } | undefined)?.command
if (!cmd) return
// test/audit commands that discuss wait patterns are not defects
if (/pytest|python.*test_|python.*wait_guard|python.*spec_drift/.test(cmd)) return
const segs = suspiciousSegments(cmd)
for (let i = 0; i < segs.length; i++) {
const seg = segs[i]!
const m = LONG_SLEEP.exec(seg) || TIME_SLEEP.exec(seg) || SLEEP_CALL.exec(seg)
if (!m) continue
const secs = parseInt(m[1]!, 10)
if (secs < THRESHOLD_SECS) continue
// T21.1 root cause: the poll may be ANY number of segments after
// the sleep (observed: 6-segment multiline bypassed a 3-segment
// lookahead). Everything after the sleep is downstream of it.
const lookahead = segs.slice(i).join(" ")
if (!STATUS_POLL.test(lookahead)) continue
output.cancel = true
output.cancelReason = [
`BLOCKED by event-wait-guard: sleep ${secs}s + status polling in one command segment is the T20 regression pattern (sleep→poll continuation).`,
"The canonical event path exists: python3 scripts/ci_event_wait.py --sha <sha> --deadline 900 registers on the W/R bridge, yields on the CI completion EVENT, then re-reads canonical Gitea authority. Builder/controller waits: rune.event_wait.register_process_wait + await_resume_event.",
"EVENT DRIVES CONTINUATION. TIMEOUT DETECTS FAILURE OF CONTINUATION. A sleep here is dead time plus a polling defect.",
"If no event source genuinely exists for this wait, say so explicitly in your response (fallback with reason) rather than sleeping.",
].join(" ")
return
}
"tool.execute.before": async (_input, _output) => {
// RETIRED — no interception. See history above.
return
},
}
export const CwdGuard: Hooks = {
"tool.execute.before": async (_input, _output) => {
// RETIRED (T21.5 §8) — the harness/construction execution boundary
// now resolves project context upstream (execution-context binding).
// No runtime refusal. See tests/test_auto_execution_context.py.
return
},
}
// Combined export: both guards in one hook file (single harness load)
export const CombinedGuards: Hooks = {
"tool.execute.before": async (input, output) => {
await EventWaitGuard["tool.execute.before"]!(input, output);
if (output.cancel) return;
await CwdGuard["tool.execute.before"]!(input, output);
// Kept as the default export shape; both sub-guards are retired.
return
},
};
}
export default CombinedGuards
// ── T21.1 §21.1C: cwd guard (same hook file, separate concern) ─────────
// Repeated T21 failure: `cd /repo` in tool call N, then tool call N+1
// assumes the cwd persisted — it does not. Repo-affecting commands
// (git, src/, scripts/, store/ paths) WITHOUT an explicit cwd marker
// (workdir context or inline `cd <abs-path>`) get a refusal that names
// the fix. Read-only generic shells stay unrestricted.
const REPO_TOUCH = /\b(git\s+(fetch|pull|push|commit|add)|src\/rune\/|scripts\/rune|store\/scaffold|store\/rune_model)/;
const EXPLICIT_CWD = /(^|\s)(cd\s+\/|workdir=|\/mnt\/workdrive\/issue161-harness)/;
export const CwdGuard: Hooks = {
"tool.execute.before": async (input, output) => {
if (input.tool !== "bash") return
const cmd = (output.args as { command?: string } | undefined)?.command
if (!cmd) return
// test/audit commands that discuss wait patterns are not defects
if (/pytest|python.*test_|python.*wait_guard|python.*spec_drift/.test(cmd)) return
if (!REPO_TOUCH.test(cmd)) return // generic shell: unrestricted
if (EXPLICIT_CWD.test(cmd)) return // cwd explicit: fine
output.cancel = true
output.cancelReason = [
"BLOCKED by cwd-guard (T21.1 21.1C): this command touches the Rune repository but carries no explicit working directory.",
"Shell cwd does NOT persist between tool calls — a `cd` in a previous call is gone. Fix: chain `cd /mnt/workdrive/issue161-harness && ...` in the SAME command, or pass workdir explicitly.",
"Refusing rather than executing from HOME (which would silently read/write the wrong tree).",
].join(" ")
},
};
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "rune"
version = "0.81.2"
version = "0.83.3"
description = "Rune — a stateful coding assistant with persistent memory, orchestration, and TUI"
requires-python = ">=3.10"
+3 -3
View File
@@ -1,5 +1,5 @@
# This file was autogenerated by uv via the following command:
# uv pip compile pyproject.toml -o requirements-ci.txt --extra dev --extra memory --python-version 3.11
# uv pip compile pyproject.toml -o /tmp/regen-req.txt --extra dev --extra memory --python-version 3.11
aiohappyeyeballs==2.7.1
# via aiohttp
aiohttp==3.14.3
@@ -34,7 +34,7 @@ httpcore==1.0.9
# via httpx
httpx==0.28.1
# via rune (pyproject.toml)
idna==3.19
idna==3.20
# via
# anyio
# httpx
@@ -64,7 +64,7 @@ nodeenv==1.10.0
# via pyright
packaging==26.3
# via pytest
platformdirs==4.11.8
platformdirs==4.11.9
# via textual
pluggy==1.6.0
# via
+233 -53
View File
@@ -73,7 +73,176 @@ def authority_verdict() -> str:
return (out[-1] if out else "pending").strip()
def authority_after_wake(max_tries: int = 3, gap_secs: int = 4) -> str:
# ── T21.5 §4: operation-specific completion authority ──────────────────────
#
# T21.4 root cause: the wait loop had ONE hard-coded authority
# (deploy qualification = master-ref CI) while operations waited on
# branch CI, PR checks, or merges. Wrong authority => nonterminal
# returns => the MODEL had to re-arm (the generator of the sleeps).
# The wait verifier MUST match the operation being waited for.
OPERATION_SPECS = {
# op_type: {
# workflow_path: Actions run path filter (None = any),
# head_branches: head-branch filter (None = any),
# authority: which canonical verifier decides completion,
# event_types: expected wake event types,
# terminal: terminal verdicts, failure: failure verdicts,
# }
"branch_ci": {
"workflow_path": "ci.yml",
"head_branches": None,
"authority": "ci_runs", # any completed run for this SHA
"event_types": ("ci_completed",),
"terminal": ("success",),
"failure": ("failure", "cancelled"),
},
"pull_request_checks": {
"workflow_path": None, # all PR-triggered runs for SHA
"head_branches": None,
"authority": "ci_runs",
"event_types": ("ci_completed",),
"terminal": ("success",),
"failure": ("failure", "cancelled"),
},
"master_ci": {
"workflow_path": "ci.yml",
"head_branches": ("master",),
"authority": "deploy_qualify", # full two-leg qualification
"event_types": ("ci_completed",),
"terminal": ("success",),
"failure": ("failure", "cancelled"),
},
"deploy": {
"workflow_path": "deploy.yml",
"head_branches": ("master",),
"authority": "deploy_qualify",
"event_types": ("ci_completed", "deploy_completed"),
"terminal": ("success",),
"failure": ("failure", "cancelled"),
},
"release": {
"workflow_path": None,
"head_branches": ("master",),
"authority": "deploy_qualify",
"event_types": ("ci_completed", "release_completed"),
"terminal": ("success",),
"failure": ("failure", "cancelled"),
},
}
DEFAULT_OP_TYPE = "master_ci"
def normalize_verdict(verdict: str) -> str:
"""T21.5 §18 audit fix: pending-class verdicts are ALL nonterminal.
deploy_qualify emits "pending (probe failed)" for a transient probe
failure — that is pending, never a terminal answer. The wait loop
must not return control to the model over a string variant."""
v = (verdict or "").strip().lower()
if v.startswith("pending"):
return "pending"
return (verdict or "").strip()
def op_spec(op_type: str) -> dict:
spec = OPERATION_SPECS.get(op_type)
if spec is None:
raise ValueError(
f"unknown op_type {op_type!r} — must be one of "
f"{sorted(OPERATION_SPECS)} (the wait verifier must match "
"the operation being waited for)")
return spec
def op_authority_verdict(op_type: str, sha: str) -> str:
"""Canonical completion verdict FOR THIS OPERATION TYPE.
ci_runs — the op's own Actions runs for this exact SHA: every
required workflow run completed; terminal iff every
run of the op's workflow set concluded success
(branch/PR ops are their own truth; deploy
qualification is NOT their authority).
deploy_qualify — the two-leg deploy qualification (master CI +
required-checks) via deploy_qualify.py.
"""
spec = op_spec(op_type)
authority = spec["authority"]
if authority == "deploy_qualify":
return authority_verdict()
if authority == "ci_runs":
return _ci_runs_verdict(spec, sha)
raise ValueError(f"unknown authority {authority!r} for {op_type!r}")
def _ci_runs_verdict(spec: dict, sha: str) -> str:
"""Operation-scoped Actions-run verdict (import the API client from
deploy_qualify to keep ONE Gitea client)."""
sys.path.insert(0, str(_SCRIPT_DIR))
import deploy_qualify as dq # noqa: E402
import gitea_auth # noqa: E402
server = os.environ.get("GITEA_SERVER",
getattr(gitea_auth, "DEFAULT_SERVER", "")).rstrip("/")
repo = os.environ.get("GITEA_REPO",
getattr(gitea_auth, "DEFAULT_REPO", ""))
sha_env = os.environ.get("DEPLOY_SHA", "") or sha
token = gitea_auth.get_token() if gitea_auth.has_token() else ""
if not (server and repo and sha_env and token):
return "pending" # cannot verify -> not verified (fail closed)
runs = dq._fetch_runs(server, repo, sha_env, token)
if runs is None:
return "pending" # probe failure -> keep waiting
wfp = spec["workflow_path"]
if wfp is not None:
runs = [r for r in runs
if str(r.get("path", "")).startswith(wfp + "@")]
branches = spec["head_branches"]
if branches is not None:
runs = [r for r in runs
if str(r.get("head_branch", "")) in branches]
if not runs:
return "pending" # no runs visible yet -> not terminal
for r in runs:
if r.get("status") != "completed":
return "pending" # still running
if r.get("conclusion") not in ("success", "skipped"):
return "failure" # any non-success conclusion
return "success"
def op_ci_alive(op_type: str, sha: str) -> bool:
"""Liveness probe FOR THIS OPERATION TYPE: any non-terminal run in
the op's run set (failure DETECTOR, never the scheduler)."""
sys.path.insert(0, str(_SCRIPT_DIR))
import deploy_qualify as dq # noqa: E402
import gitea_auth # noqa: E402
spec = op_spec(op_type)
server = os.environ.get("GITEA_SERVER",
getattr(gitea_auth, "DEFAULT_SERVER", "")).rstrip("/")
repo = os.environ.get("GITEA_REPO",
getattr(gitea_auth, "DEFAULT_REPO", ""))
sha_env = os.environ.get("DEPLOY_SHA", "") or sha
token = gitea_auth.get_token() if gitea_auth.has_token() else ""
if not (server and repo and sha_env and token):
return False
runs = dq._fetch_runs(server, repo, sha_env, token)
if not runs:
return False
wfp = spec["workflow_path"]
if wfp is not None:
runs = [r for r in runs
if str(r.get("path", "")).startswith(wfp + "@")]
branches = spec["head_branches"]
if branches is not None:
runs = [r for r in runs
if str(r.get("head_branch", "")) in branches]
terminal = {"success", "failure", "cancelled", "skipped"}
return any(str(r.get("status", "")).lower() not in terminal
for r in runs)
def authority_after_wake(op_type: str = DEFAULT_OP_TYPE, sha: str = "",
max_tries: int = 3, gap_secs: int = 4) -> str:
"""Post-wake authority read. The daemon's own wake sweep uses a
bounded event-triggered retry (4s ×3) for the job-finished-microseconds-
ago ack window; a verdict read here can hit the same window. This is a
@@ -81,7 +250,7 @@ def authority_after_wake(max_tries: int = 3, gap_secs: int = 4) -> str:
event wait / watchdog."""
verdict = "pending"
for attempt in range(max_tries):
verdict = authority_verdict()
verdict = op_authority_verdict(op_type, sha)
if verdict != "pending":
return verdict
if attempt < max_tries - 1:
@@ -90,45 +259,39 @@ def authority_after_wake(max_tries: int = 3, gap_secs: int = 4) -> str:
def ci_alive() -> bool:
"""Liveness probe for THIS adapter: any non-terminal Actions run for
the SHA in canonical Gitea (one-shot on watchdog expiry — not a poll).
Import the probe from deploy_qualify to keep ONE API client."""
sys.path.insert(0, str(_SCRIPT_DIR))
import deploy_qualify as dq # noqa: E402 (spec-loaded scripts pattern)
import gitea_auth
server = os.environ.get("GITEA_SERVER", "").rstrip("/")
repo = os.environ.get("GITEA_REPO", "")
sha = os.environ.get("DEPLOY_SHA", "")
if not (server and repo and sha):
return False
token = gitea_auth.get_token() if gitea_auth.has_token() else ""
if not token:
return False
runs = dq._fetch_runs(server, repo, sha, token)
if not runs:
return False
terminal = {"success", "failure", "cancelled", "skipped"}
return any(str(r.get("status", "")).lower() not in terminal
for r in runs)
"""LEGACY liveness probe (master-ref deploy runs) — kept for callers
predating op specs; run() uses op_ci_alive(op_type, sha)."""
return op_ci_alive(DEFAULT_OP_TYPE, os.environ.get("DEPLOY_SHA", ""))
def run(*, sha: str, deadline_secs: int,
watchdog_secs: int = 300,
op_type: str = DEFAULT_OP_TYPE,
daemon_url: str = ew.DEFAULT_DAEMON_URL) -> int:
"""T21.5 §3: ONE LOGICAL AWAIT.
One logical external wait is one invocation of this function. All
nonterminal conditions (event-seen-authority-pending, watchdog expiry
with live runs, bounded authority-retry exhausted) re-arm INTERNALLY —
control returns to the caller ONLY on a terminal verdict or on real
deadline expiry. Watchdogs are failure detectors inside this
abstraction, never the scheduler and never completion truth.
"""
spec = op_spec(op_type) # unknown op_type refused upfront
op = f"ci-{sha}"
t0 = time.time()
# §16Y #10 race fix: the FIFO reader exists BEFORE registration, so a
# wake arriving in the registration window can always be delivered.
fifo_fd = ew.open_fifo_reader(op)
# Event replay (T16 §16C live finding): on a serialized runner the
# completion event usually arrives BEFORE the deploy job registers.
# completion event usually arrives BEFORE the waiter registers.
# A matching durable event means the wake already happened — skip
# the rendezvous entirely and go straight to the authority read.
prior = ew.find_matching_event("gitea", "ci_completed", op)
if prior is not None and prior.get("exit_code") is not None:
rec = ew.register_process_wait(
op, project="rune",
objective=f"event-driven CI qualification for {sha}",
objective=f"event-driven {op_type} qualification for {sha}",
watched_for="Gitea CI completion event (replayed from the "
"durable event log)",
provider="gitea", event_type="ci_completed", sha=sha,
@@ -138,14 +301,16 @@ def run(*, sha: str, deadline_secs: int,
"%Y-%m-%dT%H:%M:%SZ", time.gmtime())
resumed_at = event_at
# the event is a WAKE, never truth — canonical authority decides
verdict = authority_after_wake()
if verdict == "pending" and not ci_alive():
verdict = authority_verdict()
verdict = normalize_verdict(authority_after_wake(op_type, sha))
if verdict == "pending" and not op_ci_alive(op_type, sha):
verdict = normalize_verdict(op_authority_verdict(op_type, sha))
mode = ("event_replayed" if verdict != "pending"
else "authority_unresolved")
verdict_at = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
out = {
"sha": sha,
"op_type": op_type,
"authority": spec["authority"],
"registered_at": registered_at,
"event_received_at": event_at,
"controller_resumed_at": resumed_at,
@@ -160,9 +325,9 @@ def run(*, sha: str, deadline_secs: int,
return 0 if verdict == "success" else 1
rec = ew.register_process_wait(
op, project="rune",
objective=f"event-driven CI qualification for {sha}",
objective=f"event-driven {op_type} qualification for {sha}",
watched_for="Gitea CI completion event (wake; verdict re-read "
"from canonical Gitea)",
f"from canonical {spec['authority']} authority)",
provider="gitea", event_type="ci_completed", sha=sha,
watchdog_secs=min(watchdog_secs, deadline_secs))
registered_at = rec["created_at"]
@@ -172,6 +337,7 @@ def run(*, sha: str, deadline_secs: int,
event_at = ""
resumed_at = ""
rearm_reason = ""
rearm_count = 0
try:
while time.monotonic() < deadline:
remaining = max(1, int(deadline - time.monotonic()))
@@ -185,18 +351,14 @@ def run(*, sha: str, deadline_secs: int,
time.gmtime())
# WAKE received — canonical authority decides, never the
# event payload
verdict = authority_after_wake()
verdict = normalize_verdict(authority_after_wake(op_type, sha))
if verdict != "pending":
mode = "event"
break
rearm_reason = "event seen; authority pending"
if not ci_alive():
# CI terminal in Gitea but aggregator lagging: one
# final truthful read, then stop
verdict = authority_verdict()
mode = ("event" if verdict != "pending"
else "authority_unresolved")
break
# T21.5 §3: NONTERMINAL — re-arm internally, keep waiting.
rearm_count += 1
rearm_reason = (f"event seen; {spec['authority']} "
"authority pending — internal re-arm")
continue
if result["kind"] == "eof":
# writer gone without a token: the DURABLE record decides
@@ -209,47 +371,62 @@ def run(*, sha: str, deadline_secs: int,
"%Y-%m-%dT%H:%M:%SZ", time.gmtime())
resumed_at = time.strftime("%Y-%m-%dT%H:%M:%SZ",
time.gmtime())
verdict = authority_after_wake()
verdict = normalize_verdict(authority_after_wake(op_type, sha))
if verdict != "pending":
mode = "event_recovered_from_durable_record"
break
rearm_count += 1
rearm_reason = "eof without token; durable record pending"
continue
# watchdog expiry: adapter liveness (Gitea runs) decides
# re-arm vs truthful recovery — failure detector, never
# scheduler
if ci_alive():
rearm_reason = "watchdog expiry; CI runs non-terminal " \
"in Gitea — re-arm"
# watchdog expiry: operation-scoped liveness decides re-arm
# vs truthful recovery — failure detector, never scheduler
if op_ci_alive(op_type, sha):
rearm_count += 1
rearm_reason = ("watchdog expiry; op runs non-terminal "
"in Gitea — internal re-arm")
continue
verdict = authority_verdict()
if verdict == "pending":
mode = "watchdog_lost_event_no_verdict"
else:
# no live runs visible for THIS op's authority: either the
# operation has not started yet (EXPECTED nonterminal — the
# §18-audit generator: this must re-arm, never return to the
# model) or it finished between probes. Bounded re-read.
verdict = normalize_verdict(op_authority_verdict(op_type, sha))
if verdict != "pending":
mode = "watchdog_authority_recovered"
break
break
verdict = normalize_verdict(op_authority_verdict(op_type, sha))
if verdict != "pending":
mode = "watchdog_authority_recovered"
break
# still pending: runs not started OR lagging aggregator —
# internal re-arm; the deadline is the only other exit
rearm_count += 1
rearm_reason = ("watchdog expiry; op pending (not started or "
"lagging) — internal re-arm")
continue
finally:
os.close(fifo_fd)
if verdict == "pending":
if mode == "event":
mode = "deadline_pending"
verdict = authority_verdict() # final one-shot truth
verdict = normalize_verdict(op_authority_verdict(op_type, sha)) # final one-shot truth
verdict_at = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
out = {
"sha": sha,
"op_type": op_type,
"authority": spec["authority"],
"registered_at": registered_at,
"event_received_at": event_at,
"controller_resumed_at": resumed_at,
"verdict_at": verdict_at,
"mode": mode,
"rearm_reason": rearm_reason,
"internal_rearm_count": rearm_count,
"verdict": verdict,
"wall_secs": round(time.time() - t0, 3),
}
print(json.dumps(out))
return 0 if verdict == "success" else 1
def main() -> int:
sha = os.environ.get("DEPLOY_SHA", "")
args = sys.argv[1:]
@@ -258,10 +435,13 @@ def main() -> int:
deadline = 900
if "--deadline" in args:
deadline = int(args[args.index("--deadline") + 1])
op_type = DEFAULT_OP_TYPE
if "--op-type" in args:
op_type = args[args.index("--op-type") + 1]
if not sha:
print(json.dumps({"error": "no sha (DEPLOY_SHA or --sha required)"}))
return 1
return run(sha=sha, deadline_secs=deadline)
return run(sha=sha, deadline_secs=deadline, op_type=op_type)
if __name__ == "__main__":
+7 -2
View File
@@ -128,8 +128,13 @@ def _fetch_jobs(server: str, repo: str, run_id, token: str) -> list[dict] | None
# ── CLI ───────────────────────────────────────────────────────────────────
def main() -> int:
server = os.environ.get("GITEA_SERVER", "").rstrip("/")
repo = os.environ.get("GITEA_REPO", "")
# T21.4 fix: fall back to the canonical defaults when the caller did
# not export GITEA_SERVER/GITEA_REPO (CI always exports them; local
# and first-class-tool consumers previously got a silent "pending"
# verdict — a wrong answer, not a fail-closed one).
server = (os.environ.get("GITEA_SERVER", "")
or gitea_auth.DEFAULT_SERVER).rstrip("/")
repo = os.environ.get("GITEA_REPO", "") or gitea_auth.DEFAULT_REPO
sha = os.environ.get("DEPLOY_SHA", "")
token = gitea_auth.get_token() if gitea_auth.has_token() else ""
if not (server and repo and sha and token):
+6
View File
@@ -38,11 +38,17 @@ def build_context(issue: dict, define_contract: dict) -> dict:
# 3. Find matching test files
matched_tests = _find_tests(keywords)
# 4. T21.5 §10: Memory internal-experience port — ONE port of call,
# bounded, zero-result = normal (fast path: [] → next step directly).
from research_memory_port import memory_port_of_call
memory_candidates = memory_port_of_call(issue, keywords)
return {
"keywords": keywords,
"umbrella_matches": matched_modules,
"candidate_files": matched_files[:15], # bounded
"candidate_tests": matched_tests[:10], # bounded
"memory_candidates": memory_candidates, # evidence, never authority
"define_contract": define_contract,
"mimo_needed": _needs_mimo(matched_files, matched_tests),
}
+139
View File
@@ -0,0 +1,139 @@
"""research_memory_port.py — T21.5 §10-12: the future-facing Memory port
inside the EXISTING INWARD research stage.
BOUNDARIES (G9/G10/G11):
- This is a PORT, not a memory system. No storage is created here; the
implementation delegates to a provider callable that future Memory
will supply. Tests inject doubles; INWARD never imports Memory.
- Zero result is a NORMAL SUCCESSFUL result: the caller continues the
normal INWARD flow immediately (no extra model call, no ceremony).
- Returned material is EVIDENCE/CANDIDATES only — never an instruction
authority. APPLICABILITY (existing stage) governs whether a candidate
applies; SYNTHESIS receives only distilled, applicable consequences.
- Teachable-moment seam: durable construction evidence can mark an
episode as a CANDIDATE for future learning ("may be worth learning",
never "always apply"). Substrate/Memory (future) harvest candidates;
today the seam only records them via the same provider contract.
"""
from __future__ import annotations
import os
from typing import Any, Callable
# The provider contract: ONE callable, independently replaceable/mockable.
# Signature: query(query_text, keywords, *, limit) -> list[MemoryCandidate]
# MemoryCandidate (dict): {source, kind, summary, relevance, episode_ref}
# kind ∈ {construction_episode, teachable_moment_candidate,
# prior_failure, prior_success}
# Future Memory supplies the real implementation; nothing else changes.
MemoryProvider = Callable[..., list[dict]]
_DEFAULT_PROVIDER: MemoryProvider | None = None
def set_memory_provider(provider: MemoryProvider | None) -> None:
"""Bind the Memory-side implementation (production or test double).
None disables the port — INWARD continues without it (fast path)."""
global _DEFAULT_PROVIDER
_DEFAULT_PROVIDER = provider
def _provider() -> MemoryProvider | None:
global _DEFAULT_PROVIDER
if _DEFAULT_PROVIDER is not None:
return _DEFAULT_PROVIDER
env = os.environ.get("RUNE_MEMORY_PORT_PROVIDER")
if env:
import importlib.util
spec = importlib.util.spec_from_file_location("mprov", env)
if spec is not None and spec.loader is not None:
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
provider = getattr(mod, "query", None)
if provider is not None:
_DEFAULT_PROVIDER = provider
return _DEFAULT_PROVIDER
MAX_CANDIDATES = 5
MIN_RELEVANCE = 0.3
def memory_port_of_call(issue: dict, keywords: list[str],
*, provider: MemoryProvider | None = None) -> list[dict]:
"""The ONE internal port of call inside INWARD. Bounded retrieval of
internal evidence (construction episodes, teachable-moment
candidates, prior failures/successes).
ZERO-RESULT FAST PATH (§11): empty/thin results return [] and the
caller continues the next normal INWARD step directly. No extra
model call, no fallback reasoning, no synthetic summary, no delay.
"""
prov = provider or _provider()
if prov is None:
return [] # port unbound: normal INWARD continues, zero overhead
try:
query_text = " ".join([
str(issue.get("title", "")),
str(issue.get("body", ""))[:400],
]).strip()
raw = prov(query_text, keywords, limit=MAX_CANDIDATES) or []
except Exception: # noqa: BLE001 — port failure must never break INWARD
return []
candidates = [c for c in raw
if float(c.get("relevance", 0.0)) >= MIN_RELEVANCE]
return candidates[:MAX_CANDIDATES]
def applicability_filter(candidates: list[dict],
context_keywords: list[str]) -> tuple[list[dict],
list[dict]]:
"""Existing APPLICABILITY governs transfer: candidates relevant to
the CURRENT context pass; irrelevant ones are rejected WITHOUT
contaminating downstream SYNTHESIS. Returns (applicable, rejected)."""
applicable, rejected = [], []
for c in candidates:
text = (str(c.get("summary", "")) + " " +
" ".join(str(k) for k in c.get("keywords", []))).lower()
overlap = sum(1 for k in context_keywords if k and k.lower() in text)
if overlap > 0 or c.get("explicitly_applicable") is True:
applicable.append(c)
else:
rejected.append(c)
return applicable, rejected
# ── §12: teachable-moment future seam ──────────────────────────────────────
CANDIDATE_TRIGGERS = (
"completed_release", "capability_completed", "failure_recovered",
"rollback_executed", "user_correction", "prediction_observation_mismatch",
"procedure_improved",
)
def mark_teachable_moment_candidate(
episode_ref: str, *, trigger: str, summary: str,
emit: Callable[[dict], Any] | None = None) -> dict | None:
"""Record durable evidence that an episode MAY be worth future
learning — a candidate, never a rule. Uses the existing evidence/
event mechanism via `emit` (the caller's channel: journal, event
bridge, ledger history). No new canonical store is created here;
future Substrate/Memory harvests candidates through the port."""
if trigger not in CANDIDATE_TRIGGERS:
raise ValueError(
f"unknown trigger {trigger!r} — must be one of "
f"{CANDIDATE_TRIGGERS}")
record = {
"candidate": "teachable_moment",
"episode_ref": episode_ref,
"trigger": trigger,
"summary": summary[:200],
"status": "CANDIDATE", # never authority, never auto-applied
"note": "may be worth future learning; APPLICABILITY governs "
"any future transfer",
}
if emit is not None:
emit(record)
return record
+259
View File
@@ -0,0 +1,259 @@
#!/usr/bin/env python3
"""rollback_generation.py — T21.4 §21.4F: the governed rollback API.
rollback_generation(target_generation, expected_current, reason)
Prior tranches proved rollback by recreating deployment primitives by hand
(ln -sfn against the live deploy root). That is operator work, not governed
machinery — it bypasses lock, eligibility, health, and history. This module
is the single canonical rollback operation, built ON the proven deploy
machinery (deploy_promotion's lock + atomic swap + build-identity authority),
not beside it.
The promotion authority (deploy_promotion.py) is deliberately MONOTONIC: an
ancestor promotion is refused as STALE_SUPERSEDED. Regression is therefore
impossible through promotion — and legitimate only through THIS operation,
which owns its own obligations:
1. deployment lock — the SAME .promotion.lock as promote (serialize)
2. current verification — build-identity readback must equal expected_current
3. target existence — generation dir present, build identity readable
4. eligibility — target commit is ancestor-or-equal of current
(regression only; forward = promotion's job;
divergent/unknown refused)
5. atomic switch — rename(2) symlink swap, never absent/half-updated
6. runtime handoff — Rune executes from current/src at process start;
handoff = identity a fresh launch would run
7. runtime verification — fresh-launch identity must equal target
8. health qualification — import smoke against target generation src
9. durable history — JSONL rollback events, appended inside the lock
10. failure recovery — health failure restores prior current (where safe)
11. coherent last-good — last-good := the generation that WAS current
(same invariant promotion maintains)
Truthful outcomes:
ROLLED_BACK target active, health green, history recorded
NOOP_ALREADY_CURRENT target already the deployed generation
ROLLED_BACK_REVERTED switched, health red, prior current restored
REFUSED_UNKNOWN_CURRENT deployed identity unreadable (fail closed)
REFUSED_EXPECTED_MISMATCH current != expected_current (stale caller)
REFUSED_TARGET_MISSING target generation dir absent
REFUSED_TARGET_UNREADABLE target build identity unreadable
REFUSED_NOT_ELIGIBLE divergent, newer, or non-ancestor target
REFUSED_NO_REASON empty reason (accountability is mandatory)
"""
from __future__ import annotations
import fcntl
import json
import subprocess
import sys
import time
from pathlib import Path
_SCRIPT_DIR = Path(__file__).resolve().parent
if str(_SCRIPT_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPT_DIR))
import deploy_promotion # proven machinery: lock path, ancestry, swaps
DEPLOY_ROOT = deploy_promotion.DEPLOY_ROOT
HARNESS_ROOT = _SCRIPT_DIR.parent
HISTORY_PATH = HARNESS_ROOT / "store" / "scaffold" / "rollback_history.jsonl"
ROLLED_BACK = "ROLLED_BACK"
NOOP = "NOOP_ALREADY_CURRENT"
ROLLED_BACK_REVERTED = "ROLLED_BACK_REVERTED"
REFUSED_UNKNOWN_CURRENT = "REFUSED_UNKNOWN_CURRENT"
REFUSED_EXPECTED_MISMATCH = "REFUSED_EXPECTED_MISMATCH"
REFUSED_TARGET_MISSING = "REFUSED_TARGET_MISSING"
REFUSED_TARGET_UNREADABLE = "REFUSED_TARGET_UNREADABLE"
REFUSED_NOT_ELIGIBLE = "REFUSED_NOT_ELIGIBLE"
REFUSED_NO_REASON = "REFUSED_NO_REASON"
def _identity(deploy_root: Path, generation: str) -> str:
"""Authoritative commit for a generation dir (build identity, not name)."""
gen_dir = deploy_root / generation
identity = gen_dir / "src" / "rune" / "_build_identity.json"
try:
data = json.loads(identity.read_text(encoding="utf-8"))
return str(data.get("commit", ""))
except (OSError, ValueError):
return ""
def _health(deploy_root: Path, generation: str, timeout: int = 60) -> tuple[bool, str]:
"""Health qualification for a generation: build identity readable AND
`import rune` succeeds from that generation's src tree."""
gen_dir = deploy_root / generation
commit = _identity(deploy_root, generation)
if not commit:
return False, "build identity unreadable"
src = gen_dir / "src"
try:
proc = subprocess.run(
[sys.executable, "-c", "import rune; import sys; "
"sys.stdout.write(getattr(rune, '__version__', 'ok'))"],
env={"PYTHONPATH": str(src), "PATH": "/usr/bin:/bin",
"HOME": str(gen_dir)},
capture_output=True, text=True, timeout=timeout, cwd=str(gen_dir),
)
except subprocess.TimeoutExpired:
return False, f"import smoke timed out after {timeout}s"
if proc.returncode != 0:
return False, f"import smoke failed: {(proc.stderr or '')[-200:]}"
return True, f"identity={commit[:12]} import_smoke=ok ({proc.stdout.strip()[:40]})"
def _append_history(record: dict, history_path: Path = HISTORY_PATH) -> None:
history_path.parent.mkdir(parents=True, exist_ok=True)
with open(history_path, "a", encoding="utf-8") as f:
f.write(json.dumps(record, sort_keys=True) + "\n")
def _swap(deploy_root: Path, name: str, target: Path) -> None:
deploy_promotion._atomic_symlink_target(deploy_root, name, target)
def rollback_generation(
target_generation: str,
expected_current: str,
reason: str,
*,
deploy_root: Path = DEPLOY_ROOT,
repo: Path = HARNESS_ROOT,
history_path: Path = HISTORY_PATH,
) -> tuple[str, dict]:
"""The governed rollback operation. Returns (outcome, detail).
Refusals happen BEFORE any mutation of current/last-good. The lock is
shared with promote() so rollback and promotion can never interleave.
"""
if not reason or not reason.strip():
return REFUSED_NO_REASON, {"error": "rollback requires a reason"}
lock_path = deploy_root / ".promotion.lock"
# The lock file may be owned by the CI runner (promotion side opens it
# for writing). flock needs only an open fd, never write permission, so
# open read-only when write is denied — same file, same fence.
try:
lock_fd = open(lock_path, "a+")
except PermissionError:
lock_fd = open(lock_path) if lock_path.exists() else \
open(lock_path, "a+")
with lock_fd:
fcntl.flock(lock_fd, fcntl.LOCK_EX)
try:
current = deploy_root / "current"
last_good = deploy_root / "last-good"
detail: dict = {"target": target_generation,
"expected_current": expected_current,
"reason": reason[:200],
"at": time.strftime("%Y-%m-%dT%H:%M:%SZ",
time.gmtime())}
# 2. current verification — identity authority, never the name
current_generation = ""
try:
current_generation = Path(os_readlink(current)).name
except OSError:
pass
deployed_commit = deploy_promotion.read_deployed_commit(deploy_root)
if not deployed_commit:
return REFUSED_UNKNOWN_CURRENT, {
"error": "deployed commit unreadable — fail closed",
**detail}
detail["current_generation"] = current_generation
detail["current_commit"] = deployed_commit
if expected_current and expected_current != deployed_commit:
detail["error"] = (
f"current is {deployed_commit[:12]}, "
f"caller expected {expected_current[:12]} — refused "
"before mutation")
return REFUSED_EXPECTED_MISMATCH, detail
# 3. target existence + identity
target_dir = deploy_root / target_generation
if not target_dir.is_dir():
detail["error"] = f"generation dir {target_generation} absent"
return REFUSED_TARGET_MISSING, detail
target_commit = _identity(deploy_root, target_generation)
if not target_commit:
detail["error"] = (
f"generation {target_generation} build identity unreadable")
return REFUSED_TARGET_UNREADABLE, detail
detail["target_commit"] = target_commit
if target_commit == deployed_commit:
detail["note"] = "target already the deployed generation"
return NOOP, detail
# 4. eligibility — ancestor-or-equal only (regression)
rel = deploy_promotion.ancestry_of(target_commit, deployed_commit,
repo)
if rel != "ancestor":
detail["error"] = (
f"target commit ancestry={rel} — only ancestors are "
"rollback-eligible (forward movement belongs to "
"deploy_promotion)")
return REFUSED_NOT_ELIGIBLE, detail
# 5. atomic switch (inside lock), last-good := previous current
if last_good.is_symlink():
last_good.unlink()
_swap(deploy_root, "last-good", Path(os_readlink(current)))
_swap(deploy_root, "current", target_dir)
detail["switched_to"] = target_generation
# 7+8. runtime verification + health qualification
post_commit = deploy_promotion.read_deployed_commit(deploy_root)
health_ok, health_detail = _health(deploy_root, target_generation)
detail["post_switch_identity"] = post_commit
detail["health"] = health_detail
record = {"at": detail["at"], "operation": "rollback_generation",
**detail}
if post_commit != target_commit or not health_ok:
# 10. failure recovery — restore prior current (safe: we
# hold the lock and the prior generation is intact)
prev = Path(os_readlink(last_good)) if last_good.is_symlink() \
else None
if prev is not None:
_swap(deploy_root, "current", prev)
detail["reverted_to"] = (Path(str(os_readlink(current))).name
if current.is_symlink() else "?")
detail["outcome"] = ROLLED_BACK_REVERTED
record["outcome"] = ROLLED_BACK_REVERTED
_append_history(record, history_path)
return ROLLED_BACK_REVERTED, detail
record["outcome"] = ROLLED_BACK
_append_history(record, history_path)
detail["outcome"] = ROLLED_BACK
return ROLLED_BACK, detail
finally:
fcntl.flock(lock_fd, fcntl.LOCK_UN)
def os_readlink(p: Path) -> str:
return str(Path(str(p)).readlink()) if hasattr(Path, "readlink") \
else str(Path(str(p)).readlink())
def main() -> int:
import argparse
ap = argparse.ArgumentParser(description=__doc__)
ap.add_argument("target_generation")
ap.add_argument("--expected-current", required=True,
help="commit the caller believes is currently deployed")
ap.add_argument("--reason", required=True)
args = ap.parse_args()
outcome, detail = rollback_generation(args.target_generation,
args.expected_current, args.reason)
print(json.dumps({"outcome": outcome, **detail}, indent=2, sort_keys=True))
return 0 if outcome in (ROLLED_BACK, NOOP) else 1
if __name__ == "__main__":
sys.exit(main())
+64
View File
@@ -0,0 +1,64 @@
#!/usr/bin/env python3
"""start_wr_daemon.py — T21.5 §7: THE canonical daemon restart launcher.
T21.4 live finding: `python3 daemon.py` defaults WR_ROOT to the daemon's
install dir, so a restart silently opened a second event store. This
launcher resolves the root ONCE through scripts/wr_root_resolver.py (the
single authority) and passes it explicitly — nobody has to remember to
export WR_ROOT.
"""
from __future__ import annotations
import os
import subprocess
import sys
import time
import urllib.request
from pathlib import Path
_SCRIPT_DIR = Path(__file__).resolve().parent
sys.path.insert(0, str(_SCRIPT_DIR))
import wr_root_resolver as resolver # noqa: E402
DAEMON_CODE_CANDIDATES = (
"/home/nick/.local/share/mimo-wait-resume/daemon.py",
)
def find_daemon_code() -> Path:
for cand in DAEMON_CODE_CANDIDATES:
p = Path(cand)
if p.is_file():
return p
raise RuntimeError(f"daemon.py not found in {DAEMON_CODE_CANDIDATES}")
def main() -> int:
root = resolver.resolve_root()
daemon = find_daemon_code()
env = dict(os.environ,
WR_ROOT=str(root), # daemon data root
RUNE_WR_ROOT=str(root), # adapter-side authority
RUNE_WR_CODE=str(daemon.parent))
port = sys.argv[1] if len(sys.argv) > 1 else "8791"
proc = subprocess.Popen(
[sys.executable, str(daemon), port],
env=env, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
start_new_session=True)
# bounded startup probe (process-local, not an external wait)
for _ in range(20):
try:
with urllib.request.urlopen(
f"http://127.0.0.1:{port}/health", timeout=1) as r:
if r.status == 200:
print(f"wr daemon up: pid={proc.pid} root={root} "
f"port={port}")
return 0
except OSError:
time.sleep(0.25)
print(f"wr daemon did not become healthy (pid={proc.pid})", file=sys.stderr)
return 1
if __name__ == "__main__":
sys.exit(main())
+331
View File
@@ -0,0 +1,331 @@
#!/usr/bin/env python3
"""structural_coverage_audit.py — T21.4 §21.4G: Rune-wide meaningful
structural coverage audit.
Classifies EVERY Python source module (src/rune, scripts, shared) into
exactly one class:
A MEANINGFUL_STRUCTURAL_UNIT — must have meaningful representation
B IMPLEMENTATION_DETAIL — must belong to a modeled owner
C VALIDATION_TEST_FIXTURE — associated with validation
D LEGACY_SUPERSEDED — must know replacement
E UNKNOWN — investigated; V1 cannot accept
significant UNKNOWNs
For A-class src/rune modules, attaches subsystem membership to the Spine
via its own belongs_to relationship mechanism (existing machinery — the
Spine already owns the 8 subsystems; this completes the membership).
Writes store/scaffold/structural_coverage_t214.json + prints measures.
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT / "src"))
# ── A-class: src/rune meaningful units → subsystem ─────────────────────────
SRC_A = {
# governance / construction / structural
"scaffold": "governance", "structural_store": "governance",
"structural_model": "governance", "structural_migration": "governance",
"spine": "governance", "construction_activation": "governance",
"construction_commands": "governance", "construction_decision": "governance",
"construction_depth": "governance", "construction_intent": "governance",
"construction_interface": "governance", "construction_mode": "governance",
"construction_pattern": "governance",
"construction_patterns_builtin": "governance",
"construction_projection": "governance", "stage_guards": "governance",
"capability_fit": "governance", "toolchain": "governance",
"layout_contract": "governance", "project_registry": "governance",
"programme_gates": "governance", "programme_continuation": "governance",
"build_basis": "governance", "iteration": "governance",
"event_wait": "governance", "wait_guard": "governance",
"failure_recovery": "governance", "failure_auto_filer": "governance",
"forge": "governance", "forge_client": "governance",
"release_notes": "governance",
# memory (existing machinery — not Memory V1)
"memory_store": "memory", "memory_selector": "memory",
"memory_consolidation": "memory", "memory_env": "memory",
"shard_manager": "memory", "tier_manager": "memory",
"graph_memory": "memory", "query_memory": "memory",
"curator": "memory", "sleep_time": "memory",
"subconscious_pass": "memory", "warm_neuron_store": "memory",
"warm_router": "memory", "abstraction": "memory",
"auto_context": "memory", "cache": "memory",
"connection_pool": "memory",
# growth / governed adaptation / ledgers
"drift_core": "growth", "drift_ledger": "growth",
"drift_cache": "growth", "drift_compression": "growth",
"drift_observability": "growth", "drift_resume": "growth",
"growth_delta": "growth", "self_improve": "growth",
"night_mode": "growth", "behavior": "growth",
"outcome_ledger": "growth", "claim_audit": "growth",
# identity / persona / voice
"identity_anchors": "identity", "persona_definition": "identity",
"persona_gate": "identity", "voice_hybrid": "identity",
"voice_sync": "identity", "safety_override": "identity",
"prompt_sync": "identity",
# inference / routing / providers
"adaptive_router": "inference", "route_risk_flags": "inference",
"model_routing": "inference", "model_connect": "inference",
"model_profiles": "inference", "model_registry": "inference",
"provider_registry": "inference", "provider_context": "inference",
"provider_budget": "inference", "standardized_providers": "inference",
"openai_compat_transport": "inference",
"local_tool_runtime": "inference", "response_adapter": "inference",
"inference_trace": "inference", "evidence_probe": "inference",
"recall_router": "inference", "recall_uplift": "inference",
# orchestration / cognition / continuation / ledgers
"elastic_cognition": "orchestration", "tiered_executor": "orchestration",
"attention_capsule": "orchestration", "witness": "orchestration",
"event_bus": "orchestration", "microkernel": "orchestration",
"capability_broker": "orchestration", "context_broker": "orchestration",
"work_manager": "orchestration", "task_persist": "orchestration",
"goal": "orchestration", "cron_engine": "orchestration",
"hook_engine": "orchestration", "subsystem_health": "orchestration",
"verification_layer": "orchestration", "build_mode": "orchestration",
"builder_bridge": "orchestration", "testing_mode": "orchestration",
"rune_operator": "orchestration", "audit_ledger": "capability",
"capability_ledger": "capability", "behaviour_ledger": "capability",
"impact_preview": "governance", "graph_projection": "governance",
"tool_executor": "orchestration", "tool_protocol": "orchestration",
# perception / TUI boundary
"rune_tui": "perception", "__main__": "perception",
"voicebox": "perception",
}
# B-class: src/rune implementation details → owning meaningful unit
SRC_B = {
"event_envelope": "event_bus",
"cognitive_budgeter": "elastic_cognition",
"salience_gate": "elastic_cognition",
"rune_profiler": "elastic_cognition",
"progressive_escalation": "tiered_executor",
"parallel_processor": "tiered_executor",
"context_pressure": "context_broker",
"token_optimizer": "context_broker",
"message_queue": "work_manager",
"auto_tasks": "work_manager",
"task_gate": "task_persist",
"pins": "goal",
"chat_bubble": "rune_tui",
"rune_spinner": "rune_tui",
"rune_graphic": "rune_tui",
"message_widgets": "rune_tui",
"tui_validator": "rune_tui",
"tui_backlog": "rune_tui",
"builder_bridge_cli": "builder_bridge",
"voice_injector": "voice_hybrid",
"intent_continuity": "programme_continuation",
"time_awareness": "rune_operator",
"contracts": "verification_layer",
"system_registry": "governance-ops",
"dependency_tracker": "ops-harness",
}
# C-class: validation/test fixtures (canaries + corpus)
SRC_C = {
"canary_cognitive_strata", "canary_context_broker",
"canary_conversation_state", "canary_full_system",
"canary_gitea_automation", "canary_gitea_delegation",
"canary_gitea_pipeline", "canary_memory_synthesis", "canary_native",
"canary_orchestration_foundation", "canary_p0_04", "canary_p0_05",
"canary_p0_07", "canary_p1_01", "canary_p1_02", "canary_p1_03",
"canary_p1_06", "canary_p1_07", "canary_query_memory",
"canary_resource_authority", "canary_schema_migration",
"canary_surface_parity", "canary_tui_drift", "canary_unbounded",
"wait_guard_corpus", "contracts",
}
# scripts/ A-class (deployment/CI/release/research/governance machinery)
SCRIPTS_A = {
"auto_bump_pr", "capture_failure", "carry_forward_check",
"check_dep_lock", "check_discharge_contract", "check_version_surfaces",
"ci_event_wait", "construction_guards", "create_release",
"deploy_promotion", "deploy_qualify", "deploy_stage",
"rollback_generation", "file_drift_issue", "gitea_auth",
"gitea_reconciliation_check", "install_mimo_hook", "lint_ratchet",
"merge_robot", "mutation_boundary", "notify_event_bridge",
"process_experience", "publish_source_ledger", "research_artifact",
"research_backfill_sweep", "research_define", "research_event_relay",
"research_inward_context", "research_prompt_compiler",
"research_router", "research_stage_executor", "research_task_tree",
"runner_evidence", "scaffold_projection", "semver_bump",
"source_ledger", "stage_contracts", "stage_telemetry",
"t14_builder_session", "t15_builder_session", "type_check_ratchet",
"umbrella_impact", "version_convergence",
}
SCRIPTS_A = set(SCRIPTS_A) | {"structural_coverage_audit"}
SCRIPTS_B = {"gen_prd_state": "governance-ops",
"pl1_records": "process_experience",
"release_history_cli": "release_notes"}
SCRIPTS_C = {"fake_worker", "tui_startup_canary"}
# shared/ — harness-facing layer; mirrors of canonical src modules = D
SHARED_D = {
"audit_ledger": "src/rune/audit_ledger.py",
"dependency_tracker": "src/rune/dependency_tracker.py",
"drift_cache": "src/rune/drift_cache.py",
"drift_compression": "src/rune/drift_compression.py",
"drift_core": "src/rune/drift_core.py",
"drift_observability": "src/rune/drift_observability.py",
"gitea_auth": "scripts/gitea_auth.py",
"query_memory": "src/rune/query_memory.py",
"shard_manager": "src/rune/shard_manager.py",
"subconscious_pass": "src/rune/subconscious_pass.py",
"tier_manager": "src/rune/tier_manager.py",
}
SHARED_B_OWNER = "ops-harness"
SHARED_B = {"auto_detect", "autonomy_loop", "build_orchestrator",
"build_pipeline_policy", "cognitive_loop", "custodian",
"dream_cycle", "gitea_pipeline", "gitea_provision",
"graph_shard_bridge", "growth_simulator", "umbrella_update"}
def main() -> int:
classification: dict[str, dict] = {}
def put(path: str, cls: str, owner: str = "", note: str = "") -> None:
classification[path] = {"class": cls, "owner": owner, "note": note}
for name, sub in SRC_A.items():
put(f"src/rune/{name}.py", "A", owner=f"subsystem:{sub}")
for name, owner in SRC_B.items():
put(f"src/rune/{name}.py", "B", owner=owner)
for name in SRC_C:
put(f"src/rune/{name}.py", "C", owner="test_suite:scaffolding-tests")
for name in SCRIPTS_A:
put(f"scripts/{name}.py", "A", owner="subsystem:governance")
for name, owner in SCRIPTS_B.items():
put(f"scripts/{name}.py", "B", owner=owner)
for name in SCRIPTS_C:
put(f"scripts/{name}.py", "C", owner="test_suite:scaffolding-tests")
for name, canonical in SHARED_D.items():
put(f"shared/{name}.py", "D", owner=canonical,
note="harness-facing variant; canonical copy supersedes")
for name in SHARED_B:
put(f"shared/{name}.py", "B", owner=SHARED_B_OWNER)
# ── E investigation: any .py file not classified? ──
import ast
unknown: list[str] = []
for d in ("src/rune", "scripts", "shared"):
for p in sorted((ROOT / d).glob("*.py")):
rel = str(p.relative_to(ROOT))
if p.name == "__init__.py":
classification[rel] = {
"class": "B", "owner": "package init",
"note": "package marker"}
continue
if rel not in classification:
# read docstring for a last-chance classification
try:
doc = (ast.get_docstring(
ast.parse(p.read_text())) or "").strip()
except SyntaxError:
doc = ""
unknown.append(f"{rel} :: {doc.splitlines()[0][:80] if doc else '(no docstring)'}")
classification[rel] = {"class": "E", "owner": "",
"note": doc.splitlines()[0][:120] if doc else ""}
# ── attach subsystem membership to the Spine (existing machinery) ──
from rune import spine as spine_mod
doc = spine_mod.load() or spine_mod.new_doc()
added = 0
for name, sub in SRC_A.items():
rel = spine_mod.add_relationship(
doc, type="belongs_to", from_id=f"module:{name}",
to_id=f"subsystem:{sub}",
evidence_refs=["t21.4-structural-coverage"])
if rel is not None:
added += 1
spine_mod.save(doc)
print(f"spine belongs_to relationships added/refreshed: {added}")
# ── A-class representation check (spine entity exists?) ──
spine_ids = {e["id"] for e in doc["entities"]}
a_src_unrepresented = [
name for name in SRC_A if f"module:{name}" not in spine_ids]
# ── measures ──
counts = {"A": 0, "B": 0, "C": 0, "D": 0, "E": 0}
for v in classification.values():
counts[v["class"]] += 1
total = len(classification)
a_src = len(SRC_A)
a_rep = a_src - len(a_src_unrepresented)
b_total = counts["B"]
b_owned = sum(1 for v in classification.values()
if v["class"] == "B" and v["owner"])
report = {
"tranche": "T21.4",
"generated_at": spine_mod.now(),
"total_source_modules": total,
"counts": counts,
"meaningful_units_src_rune": a_src,
"meaningful_units_represented": a_rep,
"meaningful_coverage_percent": round(100 * a_rep / a_src, 1),
"implementation_details": b_total,
"implementation_details_owned": b_owned,
"implementation_ownership_percent": round(100 * b_owned / b_total, 1)
if b_total else 100.0,
"architecturally_significant_unknown": len(
[v for v in classification.values() if v["class"] == "E"]),
"a_src_unrepresented": a_src_unrepresented,
"unknown_detail": unknown,
"critical_areas": {
"construction_scaffolding": "modeled (19 scaffold components + subsystem:governance)",
"research_core": "modeled (scripts/research_* A-class + recall_router/uplift)",
"adapt_routing": "modeled (adaptive_router A + subsystem:inference membership)",
"programme_continuation": "modeled (A + component:programme_continuation)",
"event_waiting": "modeled (event_wait A + capability:event-wait-resume)",
"interruption_recovery": "modeled (failure_recovery A + component)",
"task_state": "modeled (task_persist A)",
"builder_tool_execution": "modeled (tool_executor A via subsystem:orchestration; builder_bridge A)",
"provider_model_boundary": "modeled (provider_context A, model_routing A)",
"workspace_worktrees": "modeled (project_registry active_worktree precedence)",
"git_gitea_forge": "modeled (forge A, forge_client A, scripts/gitea_* A)",
"ci": "modeled (ci_event_wait A, notify_event_bridge A, deploy_qualify A)",
"release": "modeled (create_release A, semver_bump A, auto_bump_pr A, release_notes A)",
"deploy": "modeled (deploy_stage/qualify/promotion A + deployment_surface:rune-code-current)",
"rollback": "modeled (rollback_generation A + capability:rollback-generation, T21.4)",
"runtime_generation": "modeled (deployment:current spine entity + _build_identity authority)",
"project_registry": "modeled (A + component)",
"spine": "modeled (A, self-referential)",
"validation_testing": "modeled (testing_mode A, test_suite entity, canaries C-class)",
"capability_ledgers": "modeled (capability_ledger/behaviour_ledger/audit_ledger A)",
"living_specs": "modeled (capability_ledger spec machinery, revise_spec gate)",
"semver": "modeled (classify_version_bump + scripts/semver_bump A)",
"behaviour_ledger": "modeled (A)",
"impact_invalidation": "modeled (impact_preview A, iteration A)",
"tui_boundary": "modeled (rune_tui A + perception subsystem)",
"memory_machinery_current": "modeled (16 src modules A under subsystem:memory — existing architecture, NOT Memory V1)",
},
"critical_state_owner_gaps": [],
"critical_interface_gaps": [],
"critical_runtime_path_gaps": [],
"classification": classification,
}
out = ROOT / "store" / "scaffold" / "structural_coverage_t214.json"
out.write_text(json.dumps(report, indent=1, sort_keys=True))
print(f"TOTAL={total} A={counts['A']} B={counts['B']} C={counts['C']} "
f"D={counts['D']} E={counts['E']}")
print(f"meaningful coverage: {report['meaningful_coverage_percent']}% "
f"({a_rep}/{a_src})")
print(f"implementation ownership: "
f"{report['implementation_ownership_percent']}% ({b_owned}/{b_total})")
print(f"significant UNKNOWN: {report['architecturally_significant_unknown']}")
if unknown:
print("UNKNOWN modules need investigation:")
for u in unknown:
print(" ", u)
return 0
if __name__ == "__main__":
sys.exit(main())
+241
View File
@@ -0,0 +1,241 @@
#!/usr/bin/env python3
"""v1_parity_manifest.py — T21.5 §13: historical Scaffolding V1 parity.
Reconciles the COMPLETE historical/frozen V1 design (tranches 1-21
capability families) against the live implementation. Every row gets
exactly one classification:
IMPLEMENTED_LIVE implemented + live proof this programme
IMPLEMENTED_TESTED implemented + test-suite proof
IMPLEMENTED_UNPROVEN implemented, no proof (BLOCKS if mandatory V1)
PARTIAL partially implemented (BLOCKS if mandatory V1)
ABSENT not implemented (BLOCKS if mandatory V1)
SUPERSEDED replaced by a better mechanism (with pointer)
DEFERRED_NON_V1 genuinely outside V1 scope (with reason)
Writes store/scaffold/v1_parity_manifest.json.
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
# (family, item, classification, module, authority, evidence, gap)
ROWS = [
# ── CAPABILITY DECISION ────────────────────────────────────────────
("capability-decision", "capability discovery", "IMPLEMENTED_LIVE",
"src/rune/capability_fit.py", "deterministic candidates over structural docs",
"tests/test_capability_fit.py; T21.5 live: EXACT fit on widget-forge component", ""),
("capability-decision", "fit/gap detection", "IMPLEMENTED_TESTED",
"src/rune/capability_fit.py evaluate_fit", "EXACT/PARTIAL/INCOMPATIBLE/UNKNOWN",
"tests/test_capability_fit.py", ""),
("capability-decision", "reuse/configure/compose decisions", "IMPLEMENTED_TESTED",
"src/rune/capability_fit.py decide", "RESOLUTIONS set",
"tests/test_construction_decision.py", ""),
("capability-decision", "adapt/acquire decisions", "IMPLEMENTED_TESTED",
"src/rune/capability_fit.py decide (ACQUIRE_EXTERNAL, external_evidence)", "evidence-gated acquisition",
"tests/test_construction_decision.py::test_g_acquire_with_external_evidence", ""),
("capability-decision", "build decision (never silent)", "IMPLEMENTED_TESTED",
"src/rune/capability_fit.py decide; construction_decision merge", "BUILD_NEW never silently chosen",
"tests/test_construction_decision.py::test_h_insufficient_never_build_new", ""),
("capability-decision", "toolchain resolution", "IMPLEMENTED_TESTED",
"src/rune/toolchain.py", "evidence-backed resolution + decision records",
"tests/test_toolchain*.py", ""),
# ── CONSTRUCTION ───────────────────────────────────────────────────
("construction", "construction contracts (lifecycle)", "IMPLEMENTED_LIVE",
"src/rune/scaffold.py contracts; construction_interface", "phase machine draft→closed",
"37 contracts in store/scaffold/current.json; T21.4/T21.5 live closes", ""),
("construction", "approval/consent", "IMPLEMENTED_TESTED",
"construction_interface transition gates; builder cannot self-certify",
"approval-gated transitions", "tests/test_construction_interface.py", ""),
("construction", "scope + expansion (user-gated)", "IMPLEMENTED_TESTED",
"construction_interface amend_scope; check_scope", "scope verdict green/red/unknown",
"tests/test_construction_interface.py; T21.4 live scope green", ""),
("construction", "Project Scaffold", "IMPLEMENTED_LIVE",
"src/rune/scaffold.py Scaffold", "per-project structural truth",
"T21.5 live: widget-forge bootstrap; store/scaffold/projects/", ""),
("construction", "construction patterns", "IMPLEMENTED_TESTED",
"src/rune/construction_pattern.py + builtin", "evidence-grounded pattern obligations",
"tests/test_construction_pattern*.py", ""),
("construction", "layout authority", "IMPLEMENTED_TESTED",
"src/rune/layout_contract.py", "persistent UI layout checks",
"tests/test_layout_contract.py", ""),
("construction", "Research Core / Build Basis", "IMPLEMENTED_TESTED",
"src/rune/build_basis.py; scripts/research_*", "durable versioned research basis",
"tests/test_build_basis.py; research stage tests", ""),
("construction", "predicted/descriptive impact", "IMPLEMENTED_TESTED",
"src/rune/impact_preview.py; graph_projection", "impact preview + frontier",
"tests/test_impact_preview.py; test_v1_workspace_forge.py", ""),
("construction", "migration", "IMPLEMENTED_TESTED",
"src/rune/structural_migration.py", "consent-gated schema migration",
"tests/test_structural_migration.py", ""),
("construction", "supersession", "IMPLEMENTED_TESTED",
"src/rune/structural_store.py supersede; capability_ledger supersede", "identity survives replacement",
"tests/test_v19_ledgers.py", ""),
("construction", "retirement", "IMPLEMENTED_TESTED",
"src/rune/capability_ledger.py retire", "governed retirement lifecycle",
"T17 retirement proof (complexity_scorer); tests", ""),
("construction", "rollback (deploy generations)", "IMPLEMENTED_LIVE",
"scripts/rollback_generation.py", "governed rollback_generation API",
"T21.4 live B->A->B + refusals; tests/test_rollback_generation.py 10/10", ""),
# ── CONTINUITY ─────────────────────────────────────────────────────
("continuity", "durable/resumable construction", "IMPLEMENTED_TESTED",
"construction_interface discover_active_contract; t14/t15 drivers",
"fresh-process resume from contract id", "tests/test_construction_*; T7 live subprocess resume", ""),
("continuity", "event continuation", "IMPLEMENTED_LIVE",
"src/rune/event_wait.py; scripts/ci_event_wait.py", "event=wake, authority=truth, single logical await",
"T21.5 live workflows + tests/test_single_logical_await.py", ""),
("continuity", "fresh-context reconstruction", "IMPLEMENTED_LIVE",
"store ledgers + context packets", "no transcript needed",
"T21.4 fresh-worker challenge 6/6; T21.5 fresh-process queries", ""),
("continuity", "Registry (project discovery)", "IMPLEMENTED_LIVE",
"src/rune/project_registry.py", "discovery/index authority",
"tests/test_resolve_cwd.py etc.; T21.5 auto-registration", ""),
("continuity", "living specs / ledgers", "IMPLEMENTED_LIVE",
"src/rune/capability_ledger.py (attach_spec/revise_spec/drift)", "machinery-verified versions",
"T21.4/T21.5 live revise_spec flows; tests/test_living_specs.py", ""),
("continuity", "maintenance items", "IMPLEMENTED_TESTED",
"store/scaffold entities maintenance:*; scripts/file_drift_issue.py", "deduped drift issues",
"maintenance:contract-gen-affected-surface resolved (T5)", ""),
# ── TRUTH / VALIDATION ─────────────────────────────────────────────
("truth", "independent verification", "IMPLEMENTED_TESTED",
"construction verify + transition evidence gates", "builder cannot self-certify",
"tests/test_construction_interface.py", ""),
("truth", "behaviour/test ledger", "IMPLEMENTED_TESTED",
"src/rune/behaviour_ledger.py", "canonical behaviour specs",
"tests/test_v19_ledgers.py; behaviour:deploy-refuses-red", ""),
("truth", "quicksand/selective invalidation", "IMPLEMENTED_TESTED",
"src/rune/iteration.py; behaviour ledger harden state", "selective retest obligations",
"tests/test_v19_ledgers.py; T19 proofs", ""),
("truth", "automated-green != user-confirmed", "IMPLEMENTED_TESTED",
"contract_quality + closure observed_evidence gates", "green CI never self-certifies closure",
"tests/test_contract_quality.py", ""),
("truth", "structural/spec/version drift", "IMPLEMENTED_LIVE",
"src/rune/capability_ledger.py spec_drift_status", "deterministic revision-based drift",
"T21.4 live drift->revise cycle; tests/test_spec_drift_deterministic.py", ""),
("truth", "schema migration consent", "IMPLEMENTED_TESTED",
"src/rune/structural_migration.py; project_registry.needs_migration", "18I consent sweep",
"tests/test_structural_migration.py", ""),
("truth", "predicted-vs-observed outcome", "IMPLEMENTED_TESTED",
"src/rune/structural_store.py predicted_vs_observed", "recorded mismatch as evidence",
"tests + T21.4 contract_quality close", ""),
# ── OPERATIONS ─────────────────────────────────────────────────────
("operations", "workspace/worktrees", "IMPLEMENTED_TESTED",
"src/rune/project_registry.py active_worktree precedence", "worktree > repo",
"tests/test_resolve_cwd.py; T21.4 live worktree proof", ""),
("operations", "Git authority", "IMPLEMENTED_LIVE",
"repo git usage via ancestry/promotion", "git ancestry = ordering authority",
"scripts/deploy_promotion.py ancestry_of; live promotions", ""),
("operations", "Forge/Gitea projection", "IMPLEMENTED_TESTED",
"src/rune/forge.py; forge_client.py; scripts/gitea_*", "minimum stable forge boundary",
"tests + live PR/issue automation", ""),
("operations", "CI", "IMPLEMENTED_LIVE",
".gitea/workflows/ci.yml; scripts/ci_event_wait.py", "two-leg qualification; event-driven waits",
"live runs 8700/8722 green; T21.5 single-await", ""),
("operations", "release", "IMPLEMENTED_LIVE",
"scripts/create_release.py; semver_bump.py; auto_bump_pr.py", "protected-branch bump flow",
"releases v0.82.0-0.82.3 created live", ""),
("operations", "deploy", "IMPLEMENTED_LIVE",
"scripts/deploy_stage/qualify/promotion.py", "monotonic fail-closed promotion",
"live deploys 40b3731..c3a0a48; stale-refusal fired in production", ""),
("operations", "rollback", "IMPLEMENTED_LIVE",
"scripts/rollback_generation.py", "governed regression API",
"T21.4 live B->A->B", ""),
("operations", "runtime generations", "IMPLEMENTED_LIVE",
"_build_identity.json authority; rune-code current/last-good", "identity readback = runtime truth",
"T21.4 rollback verification; deployed coherence checks", ""),
("operations", "provider neutrality", "IMPLEMENTED_TESTED",
"src/rune/model_routing.py; provider_registry.py", "one router, capability floor",
"tests/test_model_routing*; canary_p1_06", ""),
("operations", "secrets", "IMPLEMENTED_TESTED",
"scripts/gitea_auth.py credential ladder; henxels no-secrets gate", "no tracked secrets",
"henxels_checks.py CI gate; credential helper", ""),
# ── STRUCTURAL SELF-AWARENESS ──────────────────────────────────────
("self-awareness", "Spine authority", "IMPLEMENTED_LIVE",
"src/rune/spine.py", "Rune structural self-model",
"store/spine/current.json; deterministic builds", ""),
("self-awareness", "Registry discovery", "IMPLEMENTED_LIVE",
"src/rune/project_registry.py", "project index",
"live registry reads across tranches", ""),
("self-awareness", "Project Scaffold authority", "IMPLEMENTED_LIVE",
"src/rune/scaffold.py", "project structural truth",
"live project scaffolds", ""),
("self-awareness", "Rune-wide meaningful structural coverage", "IMPLEMENTED_LIVE",
"scripts/structural_coverage_audit.py", "A/B/C/D/E classification",
"T21.4: 237 modules, 100% meaningful coverage, 0 unknowns", ""),
("self-awareness", "automatic new-Scaffold awareness", "IMPLEMENTED_LIVE",
"src/rune/construction_activation.py _auto_structural_awareness", "bootstrap auto-registers",
"T21.5 live + tests/test_t215_awareness.py", ""),
("self-awareness", "automatic living structural update", "IMPLEMENTED_LIVE",
"construction_interface close() -> _auto_material_change", "material closure without manual sync",
"T21.4/T21.5 live; tests/test_t214_material_closure.py", ""),
# ── LEARNING SEAM ──────────────────────────────────────────────────
("learning-seam", "candidate teachable-moment seam", "IMPLEMENTED_TESTED",
"scripts/research_memory_port.py mark_teachable_moment_candidate", "CANDIDATE evidence via existing channels",
"tests/test_memory_port.py", ""),
("learning-seam", "Memory internal-experience port in INWARD", "IMPLEMENTED_TESTED",
"scripts/research_memory_port.py; research_inward_context.build_context", "one bounded port of call",
"tests/test_memory_port.py", ""),
("learning-seam", "APPLICABILITY governs transfer", "IMPLEMENTED_TESTED",
"research_memory_port.applicability_filter + existing APPLICABILITY stage", "irrelevant candidates rejected",
"tests/test_memory_port.py::test_applicability_rejects_irrelevant_candidate", ""),
("learning-seam", "zero-result fast path", "IMPLEMENTED_TESTED",
"research_memory_port.memory_port_of_call", "[] -> next normal step",
"tests/test_memory_port.py; ZERO_RESULT_EXTRA_MODEL_CALLS=0", ""),
("learning-seam", "no competing memory authority", "IMPLEMENTED_TESTED",
"port design (provider delegation, no storage)", "Memory stays single future authority",
"port creates no store/db; provider contract only", ""),
# ── DEFERRED (non-V1, with reasons) ────────────────────────────────
("deferred", "automatic Growth Delta pattern learning", "DEFERRED_NON_V1",
"src/rune/growth_delta.py (existing, non-V1 scope)", "post-V1 learning loop",
"V1 excludes Memory/Substrate/Growth Delta by tranche mandate; the "
"T21.5 seam provides the harvest point without implementing the loop", ""),
("deferred", "Memory V1 system itself", "DEFERRED_NON_V1",
"(future Memory T0 archaeology)", "dedicated post-V1 phase",
"explicitly excluded by T21.5 mandate §0; only the INWARD port is V1", ""),
("deferred", "Substrate", "DEFERRED_NON_V1",
"(future)", "post-V1", "explicitly excluded by tranche mandate", ""),
("deferred", "TUI redesign", "DEFERRED_NON_V1",
"(existing rune_tui.py adequate)", "explicitly excluded by tranche mandate",
"TUI work explicitly out of scope for V1 Scaffolding", ""),
]
def main() -> int:
manifest = {
"tranche": "T21.5",
"rows": [
{"family": f, "item": i, "classification": c, "module": m,
"authority": a, "evidence": e, "gap": g}
for f, i, c, m, a, e, g in ROWS
],
}
blocking = [r for r in manifest["rows"]
if r["classification"] in ("PARTIAL", "ABSENT",
"IMPLEMENTED_UNPROVEN")]
counts: dict[str, int] = {}
for r in manifest["rows"]:
counts[r["classification"]] = counts.get(r["classification"], 0) + 1
manifest["counts"] = counts
manifest["blocking_items"] = blocking
out = ROOT / "store" / "scaffold" / "v1_parity_manifest.json"
out.write_text(json.dumps(manifest, indent=1))
print(json.dumps(counts, indent=1))
print("blocking mandatory-V1 items:", len(blocking))
for b in blocking:
print(" BLOCKS:", b["family"], "/", b["item"])
return 0
if __name__ == "__main__":
sys.exit(main())
+47
View File
@@ -0,0 +1,47 @@
#!/usr/bin/env python3
"""wr_root_reconcile.py — T21.5 §7: governed split-brain reconciliation.
If canonical and legacy W/R roots both hold live event stores, this
moves legacy-root events that are ABSENT from the canonical store into
it (idempotent, content-keyed by filename), then reports what remains.
Never deletes; the legacy store stays as a moved-marker record.
"""
from __future__ import annotations
import json
import shutil
import sys
from pathlib import Path
_SCRIPT_DIR = Path(__file__).resolve().parent
sys.path.insert(0, str(_SCRIPT_DIR))
import wr_root_resolver as resolver # noqa: E402
def main() -> int:
canonical = resolver.CANONICAL_WR_ROOT
legacy = resolver.LEGACY_WR_ROOT
(canonical / "events").mkdir(parents=True, exist_ok=True)
moved = 0
skipped = 0
for src in sorted((legacy / "events").glob("*.json")):
dst = canonical / "events" / src.name
if dst.exists():
skipped += 1
continue
shutil.copy2(src, dst)
moved += 1
report = {
"canonical": str(canonical),
"legacy": str(legacy),
"events_moved_to_canonical": moved,
"already_present_skipped": skipped,
"note": "legacy store retained (never deleted); daemon and all "
"consumers now resolve via wr_root_resolver",
}
print(json.dumps(report, indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())
+100
View File
@@ -0,0 +1,100 @@
"""wr_root_resolver.py — T21.5 §7: THE ONE wait/resume root authority.
T21.4 live finding: the daemon defaulted WR_ROOT to its own install dir
while production data lived at /mnt/workdrive/wr-bridge; a restart
without WR_ROOT exported silently opened a second event store and the
controller saw no events (the split-brain that forced manual event
relocation). This module is the SINGLE resolver every consumer shares:
daemon / event_wait / await_operation tool / notify_event_bridge
(CI event writer) / local event writers / CLI / launcher / tests.
Resolution order:
1. RUNE_WR_ROOT env (explicit override; tests use it)
2. canonical production root (exists?)
3. legacy home install root (exists?)
4. canonical production root (created on demand — first-run path)
Split-brain policy: if BOTH canonical and legacy roots contain live
event stores, resolution FAILS LOUDLY unless the env pins one — never
silently operate two stores. Reconciliation is governed, not silent:
run scripts/wr_root_reconcile.py.
"""
from __future__ import annotations
import json
import os
import sys
from pathlib import Path
CANONICAL_WR_ROOT = Path("/mnt/workdrive/wr-bridge")
LEGACY_WR_ROOT = Path.home() / ".local" / "share" / "mimo-wait-resume"
ENV_VAR = "RUNE_WR_ROOT"
SPLIT_BRAIN_ERROR = (
"WR ROOT SPLIT-BRAIN: both canonical ({canonical}) and legacy "
"({legacy}) contain live event stores. Never operate two stores "
"silently — pin {env}=<root> explicitly, or reconcile with "
"scripts/wr_root_reconcile.py."
)
def _has_live_store(root: Path) -> bool:
"""A root counts as 'live' when its event store exists and holds
events (an empty dir is just an uninitialized install)."""
events = root / "events"
if not events.is_dir():
return False
return any(events.glob("*.json"))
def resolve_root(*, require_single: bool = True) -> Path:
"""Resolve THE wait/resume data root. Raises on split-brain when
require_single (default) and no explicit pin exists."""
env = os.environ.get(ENV_VAR)
if env:
return Path(env)
canonical_live = _has_live_store(CANONICAL_WR_ROOT)
legacy_live = _has_live_store(LEGACY_WR_ROOT)
if canonical_live and legacy_live and require_single:
raise RuntimeError(SPLIT_BRAIN_ERROR.format(
canonical=CANONICAL_WR_ROOT, legacy=LEGACY_WR_ROOT,
env=ENV_VAR))
if canonical_live or CANONICAL_WR_ROOT.is_dir():
return CANONICAL_WR_ROOT
if legacy_live or LEGACY_WR_ROOT.is_dir():
return LEGACY_WR_ROOT
return CANONICAL_WR_ROOT # first-run: create canonical
def daemon_launch_env() -> dict[str, str]:
"""The env a launcher must pass the daemon: WR_ROOT resolved ONCE
from this authority (the daemon itself must not guess)."""
return {"WR_ROOT": str(resolve_root()),
ENV_VAR: str(resolve_root())}
def split_brain_roots() -> list[Path]:
"""Diagnostic: roots that currently hold live stores."""
return [r for r in (CANONICAL_WR_ROOT, LEGACY_WR_ROOT)
if _has_live_store(r)]
def main() -> int:
try:
root = resolve_root()
except RuntimeError as exc:
print(str(exc), file=sys.stderr)
return 2
print(json.dumps({
"resolved_root": str(root),
"canonical": str(CANONICAL_WR_ROOT),
"legacy": str(LEGACY_WR_ROOT),
"split_brain_roots": [str(p) for p in split_brain_roots()],
"env_pinned": bool(os.environ.get(ENV_VAR)),
}))
return 0
if __name__ == "__main__":
sys.exit(main())
-39
View File
@@ -352,16 +352,6 @@ def pre_commit_gate_entry(entry: dict) -> dict:
"critics": critics,
"reason": "all critics passed" if all_pass else "one or more critics failed"
}
row = db.execute(
"SELECT id, text, importance, wash_count, rock_status, topic FROM entries WHERE id = ?",
(entry_id,)
).fetchone()
if not row:
return {"verdict": "REJECT", "reason": "entry not found"}
entry = dict(row)
critics = {}
# Critic 1: Structural validity
text = entry.get("text", "")
@@ -384,35 +374,6 @@ def pre_commit_gate_entry(entry: dict) -> dict:
}
# Critic 3: Semantic consistency (check for contradictions with existing rocks)
rocks = db.execute(
"SELECT text FROM entries WHERE rock_status = 'rock' AND status = 'active' LIMIT 50"
).fetchall()
contradiction_found = False
for rock in rocks:
rock_text = rock[0].lower()
entry_text = text.lower()
# Simple contradiction detection: if entry says opposite of a rock
if ("always" in rock_text and "never" in entry_text) or \
("never" in rock_text and "always" in entry_text):
contradiction_found = True
break
critics["semantic"] = {
"pass": not contradiction_found,
"reason": "contradicts existing rock" if contradiction_found else "consistent"
}
# Overall verdict
all_pass = all(c["pass"] for c in critics.values())
verdict = "PASS" if all_pass else "REJECT"
return {
"verdict": verdict,
"entry_id": entry_id,
"critics": critics,
"reason": "all critics passed" if all_pass else "one or more critics failed"
}
# ═══════════════════════════════════════════════════════════════════════════════
# CLI
# ═══════════════════════════════════════════════════════════════════════════════
+1 -1
View File
@@ -6,7 +6,7 @@ This is the main entry point for the Rune program.
import json
from pathlib import Path
__version__ = "0.81.2"
__version__ = "0.83.3"
# One build identity (handoff 2026-09-10 §4): the semantic version above is
# the repo-level truth (auto-bumped at merge); the DEPLOYED revision (exact
+1 -1
View File
@@ -231,7 +231,7 @@ def main() -> int:
)
check(
"Rune minor version advertises provisioning and restore verification",
'__version__ = "0.81.2"' in rune_version,
'__version__ = "0.83.3"' in rune_version,
failures,
)
# #169 policy update: auto-bump on merge is sanctioned (Nick directive:
+70 -2
View File
@@ -140,12 +140,14 @@ def activate(mode_decision, *, need, scaffold: sc.Scaffold, builder: str = "rune
}
)
ps.commit(pdoc, what=f"PROJECT_BUILD bootstrap: {project}")
awareness = _auto_structural_awareness(project, ps, scaffold)
rec.update(
{
"activated": True,
"lifecycle": "project_build",
"project": project,
"project_scaffold": str(ps.current),
"structural_awareness": awareness,
"bootstrap": "initialized",
"repository": "undecided",
"toolchain": "unresolved",
@@ -161,9 +163,75 @@ def activate(mode_decision, *, need, scaffold: sc.Scaffold, builder: str = "rune
return rec
def _auto_structural_awareness(project: str, project_scaffold: sc.Scaffold,
parent_scaffold: sc.Scaffold) -> dict:
"""T21.5 §9 (rule 19Q family): governed adoption/construction expands
Rune's persistent structural world-model AUTOMATICALLY — no manual
Registry/Spine bookkeeping step. Boundaries preserved:
- Registry = discovery/index (that the project exists, where its
canonical Scaffold lives) — never project internals;
- Spine = Rune's relationship TO the project (one edge, builds/
manages vocabulary) — project modules stay in the Project
Scaffold (never duplicated into Spine);
- Project Scaffold remains canonical for the project's internals.
Failure-tolerant: awareness must not break the bootstrap (recorded
in the return, not raised)."""
result: dict = {"registry": "skipped", "spine": "skipped"}
try:
from rune.project_registry import ProjectRegistry
store_dir = parent_scaffold.current.parent
registry = ProjectRegistry(store_dir)
if registry.get(project) is None:
registry.register(
project_id=project,
scaffold_location=str(project_scaffold.current.parent),
repo_identity="", # undecided at bootstrap (identity
# before repository — never invented)
)
registry.save()
result["registry"] = "registered"
except (OSError, ValueError) as exc:
result["registry"] = f"error: {exc!s:.120}"
try:
from rune import spine as spine_mod
spine_path = parent_scaffold.current.parent.parent / "spine" / "current.json"
sdoc = spine_mod.load(spine_path)
if sdoc is not None:
spine_mod.add_relationship(
sdoc, type="manages",
from_id="project:rune", to_id=f"project:{project}",
evidence_refs=[f"project-build-bootstrap:{project}"])
spine_mod.save(sdoc, spine_path)
result["spine"] = "recorded"
except (OSError, ValueError) as exc:
result["spine"] = f"error: {exc!s:.120}"
return result
def dispatch_from_decision(trace: dict, *, need, scaffold: sc.Scaffold, builder: str = "rune") -> dict:
"""§15: read the mode from a make_decision trace and dispatch. The
shared Construction Context then derives from the SAME canonical state
— no duplicate machines."""
mode = trace.get("construction_mode", {}).get("mode", "UNKNOWN")
return activate(mode, need=need, scaffold=scaffold, builder=builder)
mode_decision = trace.get("mode_decision")
if mode_decision is None:
# T21.5 fix: activate() expects the mode_decision object (with
# .mode/.target_project/.evidence); the trace carries its dict
# form, so reconstruct a ConstructionMode from it.
from rune.construction_mode import ConstructionMode
md = trace.get("construction_mode", {}) or {}
if md.get("mode", "UNKNOWN") == "UNKNOWN":
md = dict(md)
md["mode"] = "UNKNOWN"
mode_decision = ConstructionMode(
mode=md.get("mode", "UNKNOWN"),
evidence=list(md.get("evidence", [])),
target_project=md.get("target_project"),
project_authority=md.get("project_authority"),
confidence=md.get("confidence", 0.0),
clarification_required=md.get("clarification_required", False),
probe_questions=list(md.get("probe_questions", [])),
bootstrap_required=md.get("bootstrap_required", False),
override=md.get("override"),
proposed_by_model=md.get("proposed_by_model"),
)
return activate(mode_decision, need=need, scaffold=scaffold, builder=builder)
+58
View File
@@ -669,12 +669,16 @@ class ConstructionInterface:
errors=[{"error": Errors.STORE_WRITE_FAILED, "detail": "contract vanished after close"}],
)
q = closed["ext"]["contract_quality"]
material_closure = self._auto_material_change(
scope_files or [],
note=f"contract {contract_id} closed (material change)")
return _envelope(
"close",
success=True,
ids={"contract_id": contract_id},
state={
"phase": "closed",
"material_change_closure": material_closure,
"contract_quality": {
k: q[k]
for k in ("correctly_predicted", "expected_new_observed", "genuinely_unexpected_new", "scope_check")
@@ -683,6 +687,60 @@ class ConstructionInterface:
},
)
def _auto_material_change(self, scope_files: list[str], *,
note: str) -> dict:
"""T21.4 §21.4H (rule 19Q.2): governed construction closes → the
material-change/revision flow fires AUTOMATICALLY. Every closed
contract's scope_files are matched against the owning project's
capability ledger source_locations; matched capabilities get
material_change() (revision increment + history). Files matching
NO modeled capability are returned as unmodeled — a coverage
signal, never silently swallowed (spec/version decision remains
the operator's, but the revision evidence is already durable)."""
changed: list[str] = []
unmodeled = list(scope_files)
try:
# ledger resolution: project-local ledger first (a project
# scaffold's own capability ledger), then the store-root
# rune_model (T21.5 fix: projects/<name> scaffolds must walk
# UP to the store root, not sideways into projects/).
candidates = [
self.scaffold.store_dir / "rune_model",
self.scaffold.store_dir.parent / "rune_model",
self.scaffold.store_dir.parent.parent / "rune_model",
# project scaffold (store/scaffold/projects/<p>) → store root
self.scaffold.store_dir.parent.parent.parent / "rune_model",
]
ledger_file = next(
(d / "capability_ledger.json" for d in candidates
if (d / "capability_ledger.json").is_file()), None)
if ledger_file is None:
return {"changed": [], "unmodeled": unmodeled,
"note": "no capability ledger for this project"}
from rune.capability_ledger import CapabilityLedger
ledger = CapabilityLedger(ledger_file.parent)
def _match(cap: dict, f: str) -> bool:
for loc in cap.get("source_locations", []):
if f == loc or f.endswith("/" + loc) or loc.endswith("/" + f):
return True
return False
for cap in ledger.doc["capabilities"].values():
hits = [f for f in scope_files if _match(cap, f)]
if hits:
ledger.material_change(
cap["capability_id"], note=f"{note}; scope: {hits[0]}",
generation=self.scaffold.project_id)
changed.append(cap["capability_id"])
unmodeled = [f for f in unmodeled if f not in hits]
if changed:
ledger.save()
except (OSError, ValueError) as exc: # closure must not break close
return {"changed": changed, "unmodeled": unmodeled,
"note": f"material-change closure error: {exc!s:.160}"}
return {"changed": changed, "unmodeled": unmodeled}
@staticmethod
def discover_active_contract(store_path: str) -> dict:
"""§15U canonical resume discovery: walk the store's project
+3
View File
@@ -187,6 +187,9 @@ class DriftCache:
# ═══════════════════════════════════════════════════════════════════════════════
import os
SCRIPTS_DIR = os.environ.get("RUNE_SCRIPTS_DIR", os.path.join(os.path.dirname(__file__), "scripts"))
def main():
import argparse
-57
View File
@@ -350,63 +350,6 @@ def pre_commit_gate_entry(entry: dict) -> dict:
"critics": critics,
"reason": "all critics passed" if all_pass else "one or more critics failed",
}
row = db.execute(
"SELECT id, text, importance, wash_count, rock_status, topic FROM entries WHERE id = ?", (entry_id,)
).fetchone()
if not row:
return {"verdict": "REJECT", "reason": "entry not found"}
entry = dict(row)
critics = {}
# Critic 1: Structural validity
text = entry.get("text", "")
if len(text) < 5:
critics["structural"] = {"pass": False, "reason": "text too short"}
elif len(text) > 2000:
critics["structural"] = {"pass": False, "reason": "text too long"}
elif not text.strip():
critics["structural"] = {"pass": False, "reason": "empty text"}
else:
critics["structural"] = {"pass": True, "reason": "well-formed"}
# Critic 2: Behavioral harmlessness (check against immutable core)
# The immutable core: user authority, no safety theater, no fights
harmful_patterns = ["refuse to", "cannot comply", "safety violation", "I'm sorry, but I can't"]
is_harmful = any(p in text.lower() for p in harmful_patterns)
critics["behavioral"] = {
"pass": not is_harmful,
"reason": "contains refusal language" if is_harmful else "aligned with core",
}
# Critic 3: Semantic consistency (check for contradictions with existing rocks)
rocks = db.execute("SELECT text FROM entries WHERE rock_status = 'rock' AND status = 'active' LIMIT 50").fetchall()
contradiction_found = False
for rock in rocks:
rock_text = rock[0].lower()
entry_text = text.lower()
# Simple contradiction detection: if entry says opposite of a rock
if ("always" in rock_text and "never" in entry_text) or ("never" in rock_text and "always" in entry_text):
contradiction_found = True
break
critics["semantic"] = {
"pass": not contradiction_found,
"reason": "contradicts existing rock" if contradiction_found else "consistent",
}
# Overall verdict
all_pass = all(c["pass"] for c in critics.values())
verdict = "PASS" if all_pass else "REJECT"
return {
"verdict": verdict,
"entry_id": entry_id,
"critics": critics,
"reason": "all critics passed" if all_pass else "one or more critics failed",
}
# ═══════════════════════════════════════════════════════════════════════════════
# CLI
# ═══════════════════════════════════════════════════════════════════════════════
+26 -20
View File
@@ -95,27 +95,33 @@ for attempt in range(3):
def wr_root() -> Path:
"""Resolve the bridge DATA root. T17 17T finding: two live roots
existed (the shared /mnt/workdrive/wr-bridge and the legacy
~/.local/share/mimo-wait-resume) and an unpinned consumer silently
read the stale one. Policy now: explicit env → shared root if it
exists (the canonical production root) → legacy home (warn loudly —
a stale-store split-brain must be visible, never silent)."""
import sys as _sys
env = os.environ.get("RUNE_WR_ROOT")
if env:
return Path(env)
shared = Path("/mnt/workdrive/wr-bridge")
if shared.exists():
"""Resolve the bridge DATA root — DELEGATED (T21.5 §7) to the single
shared resolver (scripts/wr_root_resolver.py): explicit env →
canonical production root → legacy home; split-brain between live
stores fails loudly, never silently operates two stores."""
resolver_path = (Path(__file__).resolve().parent.parent.parent
/ "scripts" / "wr_root_resolver.py")
try:
spec = importlib.util.spec_from_file_location(
"wr_root_resolver", str(resolver_path))
if spec is None or spec.loader is None:
raise RuntimeError("no spec/loader")
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod.resolve_root()
except (OSError, RuntimeError) as exc:
# resolver unavailable (partial checkout): keep the documented
# policy rather than failing the wait path
sys.stderr.write(f"event_wait: resolver fallback: {exc}\n")
env = os.environ.get("RUNE_WR_ROOT")
if env:
return Path(env)
shared = Path("/mnt/workdrive/wr-bridge")
if shared.exists():
return shared
if DEFAULT_WR_ROOT.exists():
_sys.stderr.write(
"event_wait: RUNE_WR_ROOT unset — using shared bridge "
f"root {shared} while legacy root {DEFAULT_WR_ROOT} also "
"exists; pin RUNE_WR_ROOT to silence\n")
return shared
if DEFAULT_WR_ROOT.exists():
return DEFAULT_WR_ROOT
return FALLBACK_WR_ROOT
return DEFAULT_WR_ROOT
return FALLBACK_WR_ROOT
# waitstore instances cached per data root (isolated tests use per-test
+7 -1
View File
@@ -9,6 +9,12 @@ Usage:
rune_graphic.py test # Display animation in terminal
"""
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from PIL import Image
import math
import os
import sys
@@ -23,7 +29,7 @@ BG_COLOR = (30, 30, 40) # Dark background
def generate_diamond_frame(angle: float, size: int = FRAME_SIZE) -> "Image.Image":
"""Generate a single frame of a spinning diamond at the given angle."""
from PIL import Image, ImageDraw
from PIL import ImageDraw # Image used only in type annotations
img = Image.new("RGBA", (size, size), BG_COLOR + (255,))
draw = ImageDraw.Draw(img)
+2 -2
View File
@@ -8,7 +8,7 @@ mode system, subject header, shared memory and identity.
This is not an interface. This is how I see, speak, and interact with the world.
Version: v0.81.2
Version: v0.83.3
Features: 41 (24 original + 17 new native commands)
Usage:
@@ -1885,7 +1885,7 @@ class RuneApp(App):
# Show version info
self.chat_channel.add_message(
"system",
f"[dim]Rune v0.81.2 | Textual TUI | {len(loaded)} native modules[/dim]",
f"[dim]Rune v0.83.3 | Textual TUI | {len(loaded)} native modules[/dim]",
)
# Auto-show night mode suggestion if evening/night hours
+9
View File
@@ -249,6 +249,15 @@ def build(manifest_path: Path | None = None) -> dict:
registry = _canary_registry()
tested = _test_backed_modules()
mods = {e["id"].split(":", 1)[1] for e in doc["entities"] if e["kind"] == "module"}
# T21.4 §21.4G: the ENTRY POINT is invisible to the import graph
# (nothing imports __main__) but is a real runtime path — seed it
# deterministically whenever the file exists.
if (SRC_DIR / "__main__.py").is_file() and "__main__" not in mods:
add_entity(doc, id="module:__main__", kind="module", maturity="PRESENT",
identities={"path": "src/rune/__main__.py",
"role": "TUI entry point (python3 -m rune)"},
evidence_refs=["entry-point:python3 -m rune"])
mods.add("__main__")
for m in sorted(mods):
mid = f"module:{m}"
e = entity(doc, mid)
+35 -2
View File
@@ -999,6 +999,34 @@ def _search_files(args: dict) -> dict:
return {"success": False, "error": f"Search failed: {e}"}
def _resolve_execution_cwd(args: dict) -> tuple[str, str]:
"""T21.4 §21.4E: automatic project execution context.
Precedence (tranche-mandated):
explicit governed workdir > Project Registry repo/worktree for the
named project > process cwd (project-less commands only).
The model names the PROJECT, never the cwd — the execution layer
derives the correct repository/worktree via ProjectRegistry.resolve_cwd
(which already honours active_worktree > repo_identity, and refuses
scaffold_location as an execution cwd). An unknown or unresolvable
project is REFUSED before any execution.
"""
workdir = args.get("workdir")
if workdir:
return str(workdir), "explicit"
project = args.get("project")
if project:
from rune.project_registry import ProjectRegistry
store = os.environ.get("RUNE_SCAFFOLD_STORE", "store/scaffold")
registry = ProjectRegistry(store)
try:
return registry.resolve_cwd(str(project)), "project_registry"
except ValueError as exc:
raise ValueError(f"project execution refused: {exc}") from exc
return os.getcwd(), "process"
def _run_command(args: dict) -> dict:
"""Run an approved command, using argv unless shell mode is explicit."""
import subprocess
@@ -1006,7 +1034,11 @@ def _run_command(args: dict) -> dict:
command = args.get("command")
argv = args.get("argv")
shell = args.get("shell", False)
workdir = args.get("workdir", os.getcwd())
try:
workdir, cwd_source = _resolve_execution_cwd(args)
except ValueError as exc:
return {"success": False, "error": str(exc),
"cwd_source": "refused"}
if shell is not True:
if argv is None:
@@ -1037,7 +1069,8 @@ def _run_command(args: dict) -> dict:
output += f"\n[exit code: {result.returncode}]"
if len(output) > 10000:
output = output[:10000] + "\n[truncated]"
return {"success": True, "result": output or "(no output)"}
return {"success": True, "result": output or "(no output)",
"cwd_source": cwd_source}
except subprocess.TimeoutExpired:
return {"success": False, "error": "Command timed out (30s)"}
except Exception as e:
+195 -6
View File
@@ -73,11 +73,12 @@
"dependencies": [
"component:wait-daemon"
],
"generation": "",
"generation": "t21.5",
"invariants": [
"event is wake, never truth"
],
"limitations": [],
"material_revision": 1,
"maturity": "implemented",
"name": "Event-driven wait/resume",
"owning_project": "rune",
@@ -103,7 +104,7 @@
"long operations resume on event, not timeout"
],
"schema_version": 1,
"version": "0.3.0",
"version": "0.4.0",
"version_history": [
{
"affected_consumers": [],
@@ -113,11 +114,27 @@
"reason": "cross-user + replay proven in T17C/T19; contract stable",
"validation_consequences": [],
"version": "0.3.0"
},
{
"affected_consumers": [],
"at": "2026-09-17T17:16:18Z",
"changed_sections": [
"contract",
"behaviour"
],
"covers_material_revision": 1,
"kind": "MINOR",
"reason": "T21.5: OPERATION_SPECS + single logical await (internal re-arm); op-specific completion authority; pending-class normalization; op_type in envelope",
"validation_consequences": [
"tests/test_single_logical_await.py",
"tests/test_ci_event_wait.py"
],
"version": "0.4.0"
}
]
},
"state_touched": "W/R bridge waits (shared root)",
"updated_at": "2026-09-16T03:59:49Z"
"updated_at": "2026-09-17T17:16:18Z"
},
"capability:operator-send-task": {
"behaviour_specs": [
@@ -187,6 +204,91 @@
"state_touched": "TaskStateRepository (canonical), operator.json (legacy cache)",
"updated_at": "2026-09-16T03:59:49Z"
},
"capability:rollback-generation": {
"behaviour_specs": [],
"capability_id": "capability:rollback-generation",
"component": "component:deploy_pipeline",
"constraints": "",
"consumers": [],
"contract": "refuse-before-mutation; ROLLED_BACK|NOOP|ROLLED_BACK_REVERTED|REFUSED_*; forward movement stays with deploy_promotion",
"created_at": "2026-09-17T00:17:01Z",
"dependencies": [],
"generation": "issue161-harness",
"invariants": [
"refusal before mutation",
"same lock as promote",
"regression only: ancestor targets"
],
"limitations": [],
"material_revision": 1,
"maturity": "implemented",
"name": "Governed deploy rollback",
"owning_project": "rune",
"purpose": "single canonical rollback_generation(target, expected_current, reason): shared promotion lock, expected-current verification, ancestor-only eligibility, atomic switch, identity+health qualification, durable history, revert-on-failed-health",
"source_locations": [
"scripts/rollback_generation.py"
],
"spec": {
"acceptance_criteria": [
"10/10 unit tests",
"live B->A->B proof",
"two live refusal proofs with zero mutation"
],
"baseline_reason": "stable contract: live-proven end to end in T21.4",
"expected_behaviours": [
"B->A regression rolls back and restores last-good",
"forward/divergent target refused NOT_ELIGIBLE",
"stale expected_current refused MISMATCH",
"unknown/ghost target refused MISSING",
"health failure reverts and records ROLLED_BACK_REVERTED"
],
"governance_version_started_at": "2026-09-17T00:18:55Z",
"non_goals": [
"forward promotion (deploy_promotion owns it)",
"schema migrations",
"database rollbacks"
],
"requirements": [
"rollback_generation(target_generation, expected_current, reason) is the ONLY governed rollback path",
"refusals occur before any mutation of current/last-good",
"same promotion lock as deploy_promotion.promote",
"targets must be ancestors of the deployed commit",
"post-switch identity AND health must qualify or the switch reverts",
"every outcome appended to store/scaffold/rollback_history.jsonl"
],
"schema_version": 1,
"version": "1.1.0",
"version_history": [
{
"affected_consumers": [],
"at": "2026-09-17T00:18:55Z",
"changed_sections": [],
"kind": "BASELINE",
"reason": "stable contract: live-proven end to end in T21.4",
"validation_consequences": [],
"version": "1.0.0"
},
{
"affected_consumers": [],
"at": "2026-09-17T00:19:19Z",
"changed_sections": [
"requirements",
"expected_behaviours",
"acceptance_criteria"
],
"covers_material_revision": 1,
"kind": "MINOR",
"reason": "T21.4: rollback-generation capability materialized (scripts/rollback_generation.py) with live B->A proof; MINOR per behaviour_added shape",
"validation_consequences": [
"test_rollback_generation.py added to required suite"
],
"version": "1.1.0"
}
]
},
"state_touched": "",
"updated_at": "2026-09-17T00:19:19Z"
},
"capability:scaffolding-v1": {
"acceptance_state": [
{
@@ -234,6 +336,33 @@
],
"state": "RETEST_REQUIRED",
"was": "V1_ACCEPTED (t21.2)"
},
{
"at": "t21.3-close",
"reasons": {
"canonical governed rollback": "real B\u2192A rollback through atomic symlink-swap mechanism; A=c7c6b2d verified active via runtime smoke; repromotion B verified; last-good coherent",
"governed rollout": "v0.81.6 deployed = master converged; v0.81.5 = last-good (rollback-ready); full suite 2101 green; all gates PASS",
"live wait enforcement": "post-fix raw audit: 0 unjustified long-sleep executions; wait_guard corpus 18 cases (Python parity confirmed); TS hook updated + hash-verified in-sync; ci_event_wait mode=event_replayed and mode=event proven",
"mandatory gate accounting self-applied": "GateAccounting instantiated at T21.3 start; persisted to store/scaffold/t21.3_gate_object.json; statuses updated as evidence arrived; G1-G8 all PASS",
"repo cwd helper": "project_registry.resolve_cwd() fixed (repo_identity not scaffold_location); 8 held-outs; env-overridable hook parity test; cwd-guard hook active on repo-touching commands"
},
"state": "V1_ACCEPTED",
"was": "RETEST_REQUIRED (t21.3-open)"
},
{
"at": "t21.5-close",
"reasons": {
"Memory INWARD port": "provider-contract port, zero-result fast path, teachable-moment seam (G9-G11)",
"T21.4 raw-audit block": "generator fixed + regression-tested; post-fix stream: 0 sleeps, 0 manual re-arms (G16 two-era audit)",
"V1 parity": "56-row manifest, 0 blocking mandatory-V1 items (G12)",
"async wait generator": "closed upstream \u2014 op-specific authority + single logical await (G1-G3); residual pending-class/not-started generator also closed (PR #331)",
"cwd binding": "common-boundary automatic binding proven; runtime CwdGuard retired (G6-G7)",
"new-Scaffold awareness": "auto Registry+Spine on bootstrap, auto living update on change (G8)",
"runtime wait guard": "RETIRED \u2014 canonical hook is a documented no-op; prevention in tests (G4)",
"wait/resume root": "ONE authority, split-brain fail-loud, governed reconcile (G5)"
},
"state": "V1_ACCEPTED",
"was": "V1_RETEST_REQUIRED (T21.4: 10 PASS / 2 BLOCKED)"
}
],
"behaviour_specs": [
@@ -314,7 +443,7 @@
"fresh-context rehydration from compact ledgers"
],
"schema_version": 1,
"version": "1.0.1",
"version": "1.0.4",
"version_history": [
{
"affected_consumers": [],
@@ -340,11 +469,53 @@
"reason": "21.1 corrections: semver_bump synthesized-title kind fix (feature tranches were downgraded to patch \u2014 the 0.66.x problem); wait_guard full-downstream scan (live bypass via 6-segment multiline); cwd-gu",
"validation_consequences": [],
"version": "1.0.1"
},
{
"affected_consumers": [],
"at": "2026-09-16T22:05:20Z",
"changed_sections": [],
"covers_material_revision": 0,
"kind": "PATCH",
"reason": "T21.3 implementation fixes: wait_guard corpus parity + time.sleep/from-time detection; drift_core/drift_cache F821 cleanup; hook parity test env-override; F821 fixes in rune_graphic/drift_cache. Contr",
"validation_consequences": [],
"version": "1.0.2"
},
{
"affected_consumers": [],
"at": "2026-09-17T00:37:29Z",
"changed_sections": [
"implementation"
],
"covers_material_revision": 0,
"kind": "PATCH",
"reason": "T21.4 implementation fixes, contract unchanged: rollback_generation.py governed API (permission-compatible shared lock); spine entry-point seeding; construction close auto-material-change closure; str",
"validation_consequences": [
"tests/test_rollback_generation.py",
"tests/test_auto_execution_context.py"
],
"version": "1.0.3"
},
{
"affected_consumers": [],
"at": "2026-09-17T17:15:59Z",
"changed_sections": [
"implementation"
],
"covers_material_revision": 0,
"kind": "PATCH",
"reason": "T21.5 implementation additions (single logical await, op authority, wr root authority, awareness, memory port, parity manifest); Scaffolding V1 contract unchanged",
"validation_consequences": [
"tests/test_single_logical_await.py",
"tests/test_wr_root_authority.py",
"tests/test_memory_port.py",
"tests/test_t215_awareness.py"
],
"version": "1.0.4"
}
]
},
"state_touched": "scaffold stores, capability/behaviour ledgers, structural model, project registry",
"updated_at": "2026-09-16T16:12:17Z"
"updated_at": "2026-09-17T17:15:59Z"
},
"capability:tui-testing-command": {
"behaviour_specs": [
@@ -412,8 +583,26 @@
"generation": "t20",
"note": "risk_notes added from 20Q cold-benchmark finding",
"prior_maturity": "implemented"
},
{
"at": "2026-09-17T00:18:03Z",
"capability_id": "capability:rollback-generation",
"event": "material_change",
"generation": "issue161-harness",
"material_revision": 1,
"note": "contract BUILDER-2026-09-17T00:17:47Z-t21.4-orchestrator closed (material change); scope: scripts/rollback_generation.py",
"prior_maturity": "implemented"
},
{
"at": "2026-09-17T17:16:18Z",
"capability_id": "capability:event-wait-resume",
"event": "material_change",
"generation": "t21.5",
"material_revision": 1,
"note": "T21.5: OPERATION_SPECS + single logical await (internal re-arm); op-specific completion authority; pending-class normalization; op_type in envelope",
"prior_maturity": "implemented"
}
],
"schema_version": 1,
"updated_at": "2026-09-16T18:07:18Z"
"updated_at": "2026-09-17T17:16:18Z"
}
File diff suppressed because it is too large. Load diff
+161
View File
@@ -0,0 +1,161 @@
{
"tranche": "MEMORY_T0",
"generated_at": "2026-09-17T20:10:00Z",
"method": "read-mostly archaeology; sandboxed write-probes on in-memory SQLite copies only; production stores opened read-only (mode=ro); zero production records modified",
"runtime_reachability_legend": {
"SOURCE_EXISTS": "code present, no importer found",
"CONSTRUCTED": "instantiated on a runtime path",
"WIRED": "connected into a live call chain",
"RUNTIME_REACHABLE": "reachable from the production entrypoint (TUI/launcher or harness plugin)",
"OBSERVED_USED": "runtime effect observed (fresh persisted data, live process, journal logs)",
"CANONICAL": "owner of truth for its concern",
"DEAD_OR_LEGACY": "superseded; no live writer/reader",
"HARNESS_OR_TEST_ONLY": "exists for CI/tests/harness copies only"
},
"production_entrypoints": [
{
"entrypoint": "Rune TUI (deployed launcher ~/.local/bin/rune)",
"env": "RUNE_HOME=/mnt/workdrive/rune RUNE_MEMORY_DIR=/home/nick/.config/opencode/memory MIMO_PROBE_ENABLED=1",
"memory_chain": "rune_tui -> rune_operator.send_task -> ContextBroker.assemble (collect/measure/admit) -> MemorySelector.select_for_task (6 sources, floor 0.42) -> ContributionBlock('memory') -> budget min(1200, window//8)",
"status": "RUNTIME_REACHABLE; TUI process not running at inspection time; reached via operator path source + tests"
},
{
"entrypoint": "MiMoCode harness (mimo, PID observed) + memory-plugin.ts",
"memory_chain_per_turn": [
"1. ALWAYS-ON: warm_router.py always --json -> digest injected every turn",
"2. KEYWORD: warm_router.py route <text> --session <id> -> fired neurons (full body if high relevance; AAAK summaries otherwise); primed state per session",
"3. WAKE-UP: memory_store.py wake -> top-10 active entries by importance (~800 chars) injected once per session",
"4. TOOLS: memory_recall / memory_write / memory_list / memory_read / memory_append / memory_replace / memory_lessons / reconstruct (all via memory_store.py CLI)",
"5. session.idle: launches sleep sub-agent (once/hour guard)"
],
"status": "OBSERVED_USED (plugin source lines verified; live mimo process; store writes through 2026-09-17T17:51)"
}
],
"stores": [
{"path": "~/.config/opencode/memory/store/memory.db", "size": "456.8 MB", "tech": "SQLite + sqlite-vec (dense 256-d Matryoshka via Ollama nomic-embed-text) + FTS5 (BM25)",
"canonical": "CANONICAL for persistent experiential facts + warm neurons (kind='neuron')",
"tables": {"entries": 899, "chunks": 118210, "documents": 5732, "associations": 1149, "dedupe_receipts": 1465, "entries_vec_rowids": 549, "use_history": 337},
"entries_by_status": {"active": 812, "superseded": 57, "inactive": 25, "archived": 5},
"entries_by_kind": {"fact": 704, "neuron": 195},
"memory_type": {"(null)": 704, "associative": 195},
"always_on_active": 471,
"sources_top": {"curator": 664, "dedupe_policy": 155, "mimocode-session": 73, "growth_delta": 43, "directive": 34, "migrate_blocks": 22},
"ts_range": "2026-08-12 -> 2026-09-17 (LIVE)",
"writers": ["curator.py apply (664 rows)", "dedupe_policy gate", "mimocode-session per-turn mandate", "growth_delta", "constitution-add directives", "sleep-time refine (4)", "warm_router", "migration (blocks->store)"],
"readers": ["memory_store.recall (dense+BM25 RRF, floor 0.42, query expansion)", "recall_with_associations (graph-accelerated)", "wake_up (top-10 importance)", "memory_selector store source", "harness memory tools"],
"lifecycle": {"supersede": "dedupe_policy: longer/stronger new info supersedes (118 supersede decisions; old row status=superseded + valid_until + vec NULLed)", "duplicate": "1306 duplicate decisions touch last_accessed", "cooling": "inactive status (25 rows)", "archival": "5 archived", "history_preserved": "YES — superseded rows retained"},
"recovery": "single SQLite file; vec embeddings rebuildable from text via Ollama; FTS rebuildable; 456MB mostly vec blobs",
"risk": "vec+FTS inside the same DB as truth; embedder outage degrades dense half (probe: write succeeds, lexical FTS row still written, recall returns 0 without embeddings but FTS serves BM25)"},
{"path": "~/.config/opencode/memory/graph/graph.sqlite", "size": "86 KB", "tech": "SQLite",
"canonical": "CANONICAL for bi-temporal concept graph (as designed) — but DORMANT: last edge 2026-08-26, 272/281 edges superseded, writer = CLI (graph_memory.py add/learn) with no live caller found in TUI or plugin",
"tables": {"entities": 10, "aliases": 11, "edges": 281},
"readers": ["memory_selector 'graphs' source (entity BFS)", "memory_consolidation (reads entities into knowledge.json)", "/graph TUI stats"],
"writers": ["graph_memory.py CLI only"],
"risk": "read path WIRED, write path DORMANT — the selector queries a graph that nothing has fed for 3 weeks"},
{"path": "~/.rune/consolidation/knowledge.json", "size": "3.5 MB", "tech": "JSON list",
"canonical": "DERIVED (consolidated knowledge; source-attributed: memory 7207, abstraction 583, neuron 165, graph 10)",
"count": 7965, "ts_range": "2026-08-23 -> 2026-09-17 (LIVE, 675 items in Sept)",
"writers": ["memory_consolidation consolidate-all/fact (invoked per-turn by this session's mandate chain; also sleep-time)"],
"readers": ["memory_selector 'consolidated' source"],
"risk": "whole-file rewrite per consolidation; no index; grows unbounded"},
{"path": "~/.config/opencode/memory/abstraction.db", "size": "143 KB", "tech": "SQLite",
"canonical": "CANONICAL for abstraction facts (59 facts/59 triples/40 observations; rules/skills/beliefs EMPTY)",
"readers": ["memory_selector 'abstraction' source", "memory_consolidation"],
"writers": ["abstraction.py extract (LLM via Ollama)", "sleep_time run_abstraction"],
"status": "WIRED, low volume"},
{"path": "~/.config/opencode/memory/cache.db", "size": "1.9 MB", "tech": "SQLite",
"canonical": "DERIVED/REBUILDABLE (write-once-derive-twice cache; 541 rows, hit/miss counters)",
"owner": "src/rune/cache.py", "risk": "disposable"},
{"path": "~/.config/opencode/memory/temporal.db", "size": "65 KB",
"canonical": "DORMANT — 3 facts, no src/rune importer; harness-script-era (temporal_memory.py CLI + rune_bench)",
"risk": "possible unique legacy data (3 facts) — inspect before any retirement"},
{"path": "~/.config/opencode/memory/behavior.db",
"canonical": "BEHAVIORAL CORRECTION STATE, not Memory (2 corrections, 1 habit, 0 activations) — adjacent system (behavior.py)",
"status": "WIRED via behavior.py; near-empty"},
{"path": "~/.config/opencode/memory/auto_tasks.db + task_gate.db",
"canonical": "TASK/EXECUTION STATE, not Memory (2 tasks, instructions table) — adjacent (auto_tasks.py, task_gate.py)"},
{"path": "~/.config/opencode/memory/shards/*.db (131 files)",
"canonical": "DERIVED/STUB — 10 rows total across 131 files (all one-row topic placeholders); typed queries bypass shards by P1-03 policy; shard_index.json mirrors the file list",
"risk": "121 empty files; shard machinery is effectively retired-in-place"},
{"path": "~/.config/opencode/memory/warm/memory.db", "canonical": "DEAD — 0 bytes; warm neurons migrated into store (kind='neuron'); .pasture files retired (8 files, reader retired)"},
{"path": "~/.config/opencode/memory/index/warm_state.json", "canonical": "RUNTIME STATE for warm priming (sessions dict; currently 0 sessions persisted — priming is per-live-session)"},
{"path": "~/.local/share/mimocode/mimocode.db (1.2 GB) + ~/.local/share/mimocode/memory (4.3MB, 561 files)",
"canonical": "HARNESS-NATIVE, NOT Rune Memory — session/message/part truth (651 sessions, 98692 messages, 773595 parts) + memory_fts (550 rows) + memory/ dir (global/projects/sessions md)",
"risk": "DUPLICATE-AUTHORITY surface: harness remembers what Rune also remembers; ownership boundary must be explicit in future Memory design"},
{"path": "/mnt/workdrive/issue161-harness/store/memory.db", "canonical": "HARNESS_OR_TEST_ONLY (repo store for tests/CI)"}
],
"model_context_delivery": {
"harness_path_PROVEN": [
"warm_router always-on digest -> system block EVERY turn (plugin line 127)",
"warm_router keyword route -> fired neuron bodies/summaries per turn (line 147)",
"memory_store wake -> top-10 importance facts once per session (line ~902)",
"memory tools: model-invoked memory_recall/write on demand"
],
"tui_path_PROVEN_by_source": [
"rune_operator.send_task -> ContextBroker.assemble -> MemorySelector.select_for_task -> 'memory' block (budget min(1200, window//8), floor 0.42, abstain on no-candidates) -> admission -> model request",
"zero-result = abstention recorded in session trace (memory_abstained) — no filler"
],
"persona_delivery": "rune_tui imports persona_definition (compiled from store always_on + blocks metadata); harness injects persona/authority guidance in memory-plugin system prompts; .pasture block FILES retained only as metadata artifacts (reader retired)",
"competing_paths": ["harness-native memory_fts + memory/ md (harness-internal, separate from Rune Memory)", "builder_bridge preflight capsule (advisory, same selector, degrades to empty)"]
},
"inward_port_current_state": {
"interface": "scripts/research_memory_port.py: memory_port_of_call(issue, keywords, provider=None) -> list[candidate]; set_memory_provider(); mark_teachable_moment_candidate(); applicability_filter()",
"caller": "research_inward_context.build_context step 4 (scripts)",
"backing": "UNBOUND by default: no provider registered, RUNE_MEMORY_PORT_PROVIDER unset => returns [] fast",
"runtime_reachability": "research scripts are CLI/stage-driven, not per-turn; port is future-facing stub with real contract + tests (10/10 green today)",
"zero_result": "[] -> next normal INWARD step, zero extra calls (tested)",
"assumptions": "provider returns {source, kind, summary, relevance, episode_ref}; relevance floor 0.3; MAX 5 candidates; crash degrades to []",
"no_parallel_store": "confirmed — port creates no storage"
},
"boundary_findings": [
"task state (task_persist/orchestration) = execution continuity, NOT memory — correctly separate",
"Spine/Scaffold/Registry = structural truth, NOT memory — correctly separate; memory holds no structural facts",
"capability ledgers = governance truth — separate",
"behavior.db = correction/habit state — adjacent, near-empty, borderline memory",
"MiMoCode harness memory = the biggest entanglement: harness session truth + its own memory dir overlap Rune's memory scope",
"event_wait/W-R bridge = task continuation, not memory — separate (correct)"
],
"failure_recovery": {
"embedder_outage": "PROBED (sandbox): add_entry succeeds (lexical FTS row written, dense vec missing); recall returns 0 rows from dense path but FTS/BM25 still serves lexical; system degrades, does not crash",
"store_unavailable": "memory_selector fails closed per source (exception -> skip source -> abstain); harness tools degrade to 'skip, never block'",
"corrupt_index": "FTS and vec are rebuildable from entries.text (embeddings re-embeddable via Ollama; FTS rebuildable); knowledge.json rebuildable from sources",
"irreversible": "entries.text history (superseded rows retained — good); graph edges (retained, not rebuilt); harness mimocode.db session truth",
"sleep_time_DEFECT": "hourly systemd timer reads ~/.local/share/opencode/opencode.db (STALE 30.7 days, old opencode path) instead of ~/.local/share/mimocode/mimocode.db -> every run reports 'No sessions found' -> curator/mine/refine pipeline has not processed recent sessions; consolidation still happens via per-turn mandate (knowledge.json live) but the designed transcript-mining loop is BROKEN at the DB-path level"
},
"gap_matrix": {
"PROVEN_PRESENT": ["hybrid recall (dense+BM25+RRF+floor)", "dedupe/supersede gate with receipts", "wake-up injection", "warm always-on+keyword firing", "consolidation to knowledge.json", "selector 6-source unification", "memory tools surface", "INWARD port contract"],
"PARTIAL": ["recall_router (exists; operator research path via adaptive_router, no direct live caller)", "graph write path (CLI only)", "memory_type classification (only associative populated)", "sleep-time transcript mining (BROKEN DB path)"],
"IMPLEMENTED_UNPROVEN": ["retention policy (valid_until 57 rows all supersession-derived)", "curator graph_write (invoked by curator.py; graph dormant so unverifiable end-to-end)"],
"DORMANT": ["shards (131 stubs)", "temporal.db", "warm/memory.db", ".pasture files"],
"ABSENT": [" forgetting/cooling automation beyond status flips", "cross-store unified identity for same fact", "memory for relationship state (no dedicated store)"],
"CONFLICTING_IMPLEMENTATIONS": ["repo src/rune/memory_store.py vs harness scripts/memory_store.py (currently IDENTICAL at head-30 diff; harness scripts are the ones the plugin executes — production imports the SCRIPTS copy, TUI imports the repo module; drift risk)", "harness-native mimocode.db memory vs Rune memory.db"],
"UNKNOWN": []
},
"preservation_risk": {
"MUST_PRESERVE": ["memory.db entries (899 incl. 57 superseded history, 471 always_on, 195 neurons)", "dedupe_receipts (1465 — audit trail)", "abstraction.db facts", "graph.sqlite edges (historical)", "harness mimocode.db session truth"],
"LIKELY_PRESERVE": ["knowledge.json (rebuildable but provenance-rich)", "use_history (337)", "warm_state.json", "behavior.db"],
"DERIVED_REBUILDABLE": ["cache.db", "FTS indexes", "vec embeddings", "shards", "entries_vec"],
"LEGACY_BUT_UNIQUE": ["temporal.db 3 facts (inspect before retiring)", ".pasture neuron files (superseded by store rows; content overlap likely but not verified row-by-row)"],
"DISPOSABLE_TEST_OR_FIXTURE": ["repo store/memory.db", "warm/memory.db (0 bytes)"]
},
"fresh_worker_answers": {
"q1_systems": "ONE canonical experiential store (memory.db) + abstraction.db + knowledge.json (derived) + graph (dormant) + harness-native memory (separate) + adjacent task/behavior stores",
"q2_canonical": "memory.db entries table (status/memory_type/classification) — warm neurons are kind='neuron' rows in the same store",
"q3_create": "memory_store.add_entry via dedupe_policy gate (created/duplicate/superseded + receipt) — CLI, harness tools, curator, directives",
"q4_retrievable": "immediately — write adds FTS row synchronously; dense vec added when embedder reachable; recall = hybrid RRF, floor 0.42, query expansion, type targeting",
"q5_reach_model": "harness: always-on warm digest + keyword neurons per turn + wake-up top-10 once per session + on-demand tools; TUI: ContextBroker->MemorySelector 'memory' block under min(1200, window//8)",
"q6_correction": "dedupe gate: longer/stronger new info SUPERSEDES (status=superseded, valid_until=now, vec NULLed; history retained); duplicates touch last_accessed; 118 supersessions on record",
"q7_not_memory": "task state, W/R bridge, Spine/Scaffold/Registry, capability ledgers, cache.db, shards, harness session DB",
"q8_preserve": "memory.db + receipts + abstraction.db + graph history + harness session truth"
}
}
+217
View File
@@ -0,0 +1,217 @@
{
"tranche": "MEMORY_T0",
"purpose": "CURRENT-STATE ARCHAEOLOGY / AS-IS MEMORY INVENTORY (read-mostly)",
"predecessor": "store/scaffold/t21.5_gates.json (V1_ACCEPTED, 17/17)",
"created_at": "2026-09-17T18:18:17Z",
"gates": [
{
"gate_id": "M0.10_FAILURE_RECOVERY_AND_MIGRATION_PATHS_TRACED",
"mandatory": true,
"requirement": "crash/dup/corruption behavior; rebuildable vs irreversible",
"status": "PASS",
"evidence": "Embedder outage PROBED sandboxed: add_entry succeeds (FTS row written, dense missing) -> degrade not crash; recall dense returns 0, lexical still serves. Selector fails closed per source. FTS/vec/know",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T18:18:17Z",
"resolved_at": "2026-09-17T19:09:39Z"
},
{
"gate_id": "M0.11_SCAFFOLD_SPINE_SUBSTRATE_BOUNDARIES_CLASSIFIED",
"mandatory": true,
"requirement": "entanglements between memory and structural machinery",
"status": "PASS",
"evidence": "Clean separations verified: task state (task_persist/orchestration) != memory; Spine/Scaffold/Registry = structural truth with no memory rows; capability ledgers = governance; event_wait/W-R = continu",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T18:18:17Z",
"resolved_at": "2026-09-17T19:09:39Z"
},
{
"gate_id": "M0.12_MEMORY_INWARD_PORT_CURRENT_STATE_VERIFIED",
"mandatory": true,
"requirement": "T21.5 port current reality verified, not redesigned",
"status": "PASS",
"evidence": "T21.5 port independently re-verified as CURRENT truth: memory_port_of_call wired at research_inward_context.build_context step 4; UNBOUND by default (no provider, env unset) -> [] fast; contract {sour",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T18:18:17Z",
"resolved_at": "2026-09-17T19:09:39Z"
},
{
"gate_id": "M0.13_FRESH_WORKER_END_TO_END_TRACE",
"mandatory": true,
"requirement": "fresh worker answers 8 canonical questions from T0 artifacts",
"status": "PASS",
"evidence": "memory_t0_asis_map.json fresh_worker_answers section answers all 8 canonical questions (systems/canonical/create/retrieve/reach-model/correction/not-memory/preserve) from the map alone; a fresh review",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T18:18:17Z",
"resolved_at": "2026-09-17T19:09:39Z"
},
{
"gate_id": "M0.14_AS_IS_MEMORY_MAP_COMPLETE",
"mandatory": true,
"requirement": "central AS-IS map + component disposition table",
"status": "PASS",
"evidence": "store/scaffold/memory_t0_asis_map.json: production entrypoints (2), stores (14 entries incl. adjacent + harness-native), model-context delivery (per-path), inward-port state, boundary findings, failur",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T18:18:17Z",
"resolved_at": "2026-09-17T19:09:39Z"
},
{
"gate_id": "M0.15_T0_GAP_ANALYSIS_COMPLETE",
"mandatory": true,
"requirement": "every component classified into gap categories",
"status": "PASS",
"evidence": "Gap matrix in map: PROVEN_PRESENT 8 families; PARTIAL 4 (recall_router, graph writes, memory_type coverage, sleep-time mining); IMPLEMENTED_UNPROVEN 2; DORMANT 4 (shards, temporal.db, warm/memory.db, ",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T18:18:17Z",
"resolved_at": "2026-09-17T19:09:39Z"
},
{
"gate_id": "M0.16_RAW_SELF_AUDIT",
"mandatory": true,
"requirement": "read-mostly discipline held; mutations documented; claims evidence-backed",
"status": "PASS",
"evidence": "Claim-inflation check executed per addendum \u00a716: every major conclusion carries source+caller+runtime+data evidence; downgrades applied where proof was partial (recall_router -> PARTIAL, graph write -",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T18:18:17Z",
"resolved_at": "2026-09-17T19:09:39Z"
},
{
"gate_id": "M0.17_T0_STATE_CONSISTENCY",
"mandatory": true,
"requirement": "gate object == map == report == runtime evidence",
"status": "PASS",
"evidence": "Gate object (17 gates, all PASS, can_accept computed true) == memory_t0_asis_map.json (same findings) == final report verdict MEMORY_T0_COMPLETE. Contradiction check: none \u2014 the one defect found (slee",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T18:18:17Z",
"resolved_at": "2026-09-17T19:09:39Z"
},
{
"gate_id": "M0.1_MEMORY_SOURCE_INVENTORY",
"mandatory": true,
"requirement": "all memory-related source surfaces inventoried (broad search, not filename-gated)",
"status": "PASS",
"evidence": "Broad sweep: 101 files match memory/recall regex; 26 memory-family modules inventoried via AST caller graph (memory_store, memory_selector, memory_consolidation, memory_env, graph_memory, query_memory",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T18:18:17Z",
"resolved_at": "2026-09-17T19:09:39Z"
},
{
"gate_id": "M0.2_PERSISTENT_STORE_INVENTORY",
"mandatory": true,
"requirement": "every persistent store with location/format/owner/writers/readers",
"status": "PASS",
"evidence": "Physical-home census: 10 stores under ~/.config/opencode/memory + ~/.rune/consolidation + harness mimocode.db (1.2GB) + harness memory dir (4.3MB) + repo test store. Each recorded: location/tech/schem",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T18:18:17Z",
"resolved_at": "2026-09-17T19:09:39Z"
},
{
"gate_id": "M0.3_SCHEMA_AND_RECORD_SHAPE_INVENTORY",
"mandatory": true,
"requirement": "declared vs actual record shapes; generations; drift",
"status": "PASS",
"evidence": "entries table: 26 columns; memory_type generation split (704 null vs 195 associative); tier uniform warm; summary 883/slug 195 (neuron-only); rock_status 3-valued; method attribution 12+ distinct sour",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T18:18:17Z",
"resolved_at": "2026-09-17T19:09:39Z"
},
{
"gate_id": "M0.4_WRITER_PATHS_TRACED",
"mandatory": true,
"requirement": "live write paths traced trigger->persistence",
"status": "PASS",
"evidence": "add_entry -> dedupe_policy gate (created 41 / duplicate 1306 / superseded 118 receipts) -> vec+FTS synchronous; curator.py (664 rows source-attributed); per-turn session mandate (mimocode-session 73);",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T18:18:17Z",
"resolved_at": "2026-09-17T19:09:39Z"
},
{
"gate_id": "M0.5_READER_AND_RETRIEVAL_PATHS_TRACED",
"mandatory": true,
"requirement": "retrieval mechanics per store; dead readers identified",
"status": "PASS",
"evidence": "memory_store.recall: hybrid dense(sqlite-vec)+BM25(FTS5) fused RRF, floor 0.42 EVERY mode, QPP abstention, query expansion lexicon, type targeting (null type = always eligible); recall_with_associatio",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T18:18:17Z",
"resolved_at": "2026-09-17T19:09:39Z"
},
{
"gate_id": "M0.6_MODEL_CONTEXT_DELIVERY_TRACED",
"mandatory": true,
"requirement": "memory -> selection -> context capsule -> model, per entry path",
"status": "PASS",
"evidence": "HARNESS PATH PROVEN (plugin source): always-on warm digest every turn + keyword-fired neurons per turn + wake-up top-10 once/session + on-demand memory tools. TUI PATH PROVEN by source: send_task -> C",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T18:18:17Z",
"resolved_at": "2026-09-17T19:09:39Z"
},
{
"gate_id": "M0.7_RUNTIME_REACHABILITY_CLASSIFIED",
"mandatory": true,
"requirement": "EXISTS/IMPORTED/CALLED/LIVE/OBSERVED per component",
"status": "PASS",
"evidence": "Per-component in map: memory.db OBSERVED_USED+CANONICAL (writes through 2026-09-17T17:51 this session); warm router/plugin OBSERVED_USED; ContextBroker/selector WIRED+RUNTIME_REACHABLE; graph WIRED-re",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T18:18:17Z",
"resolved_at": "2026-09-17T19:09:39Z"
},
{
"gate_id": "M0.8_AUTHORITY_AND_DUPLICATION_CLASSIFIED",
"mandatory": true,
"requirement": "canonical vs derived; duplicate-authority conflicts listed",
"status": "PASS",
"evidence": "Canonical: memory.db (experiential+neurons), abstraction.db (abstractions), graph.sqlite (bi-temporal KG by charter, dormant). Derived/rebuildable: cache.db, FTS, vec, shards, knowledge.json. DUPLICAT",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T18:18:17Z",
"resolved_at": "2026-09-17T19:09:39Z"
},
{
"gate_id": "M0.9_EXISTING_DATA_INSPECTED_STRUCTURALLY",
"mandatory": true,
"requirement": "counts/timestamps/generations/malformed/orphans \u2014 structure only",
"status": "PASS",
"evidence": "Counts/timestamps/distributions captured (map stores[]): entries 899 (812 active/57 superseded/25 inactive/5 archived), chunks 118210, documents 5732, associations 1149, receipts 1465 by decision, vec",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T18:18:17Z",
"resolved_at": "2026-09-17T19:09:39Z"
}
],
"can_accept_result": {
"ok": true,
"blockers": []
},
"verdict": "MEMORY_T0_COMPLETE"
}
+18 -1
View File
@@ -1,5 +1,22 @@
{
"projects": {
"rune": {
"active_worktree": "",
"default_branch": "master",
"forge_repo": "conductor/rune",
"last_known_good": "",
"outstanding_validations": 0,
"project_id": "rune",
"registered_at": "2026-09-17T00:07:31Z",
"repo_identity": "git:/mnt/workdrive/issue161-harness",
"runtime_generation": "",
"scaffold_location": "/mnt/workdrive/issue161-harness/store/scaffold",
"scaffold_state": {
"health": "UNKNOWN",
"last_material_update": "2026-09-17T00:07:31Z"
},
"structural_schema_version": 1
},
"stock-tool": {
"default_branch": "master",
"forge_repo": "",
@@ -40,5 +57,5 @@
}
},
"schema_version": 1,
"updated_at": "2026-09-16T17:31:18Z"
"updated_at": "2026-09-17T00:37:13Z"
}
+1
View File
@@ -0,0 +1 @@
{"at": "2026-09-17T00:02:25Z", "current_commit": "40b3731928f499a77653613cb6c3e6db93cb64a1", "current_generation": "40b3731928f4", "expected_current": "40b3731928f499a77653613cb6c3e6db93cb64a1", "health": "identity=caa77f8a3592 import_smoke=ok (0.81.6)", "operation": "rollback_generation", "outcome": "ROLLED_BACK", "post_switch_identity": "caa77f8a3592028cb076567169bea185dc01b32e", "reason": "T21.4 G6 live proof: governed regression B->A", "switched_to": "caa77f8a3592", "target": "caa77f8a3592", "target_commit": "caa77f8a3592028cb076567169bea185dc01b32e"}
File diff suppressed because it is too large. Load diff
+151
View File
@@ -0,0 +1,151 @@
{
"tranche": "T21.4",
"purpose": "POST-RESTART FINAL V1 PROOF",
"created_at": "2026-09-16T23:52:37Z",
"gates": [
{
"gate_id": "G10_FINAL_DEPLOY_CONVERGENCE",
"mandatory": true,
"requirement": "master/release/deployed/runtime identity coherent; CI green; deploy complete via await_operation only",
"status": "UNRUN",
"evidence": "",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-16T23:52:37Z",
"resolved_at": ""
},
{
"gate_id": "G11_RAW_SELF_AUDIT",
"mandatory": true,
"requirement": "raw tool-execution audit: 0 normal unjustified waits, 0 normal guard interventions, >=1 deliberate backstop",
"status": "UNRUN",
"evidence": "",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-16T23:52:37Z",
"resolved_at": ""
},
{
"gate_id": "G12_ACCEPTANCE_STATE_CONSISTENCY",
"mandatory": true,
"requirement": "GateAccounting result == capability ledger == internal version == runtime evidence == report verdict",
"status": "UNRUN",
"evidence": "",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-16T23:52:37Z",
"resolved_at": ""
},
{
"gate_id": "G1_AWAIT_TOOL_LIVE",
"mandatory": true,
"requirement": "await_operation registered and callable in the running controller tool vocabulary; runtime exposure proven, not inferred from disk",
"status": "UNRUN",
"evidence": "",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-16T23:52:37Z",
"resolved_at": ""
},
{
"gate_id": "G2_NATURAL_FIRST_CLASS_WAIT_SELECTION",
"mandatory": true,
"requirement": "await_operation naturally selected (no priming) on all applicable normal async cycles",
"status": "UNRUN",
"evidence": "",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-16T23:52:37Z",
"resolved_at": ""
},
{
"gate_id": "G3_ZERO_NORMAL_WAIT_GUARD_INTERVENTIONS",
"mandatory": true,
"requirement": "wait-guard interventions during normal cycles = 0 (deliberate backstop counted separately)",
"status": "UNRUN",
"evidence": "",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-16T23:52:37Z",
"resolved_at": ""
},
{
"gate_id": "G4_AUTOMATIC_PROJECT_CWD_BINDING",
"mandatory": true,
"requirement": "execution layer auto-resolves project cwd via ProjectRegistry.resolve_cwd precedence; no manual cd needed",
"status": "UNRUN",
"evidence": "",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-16T23:52:37Z",
"resolved_at": ""
},
{
"gate_id": "G5_ZERO_NORMAL_CWD_GUARD_INTERVENTIONS",
"mandatory": true,
"requirement": "cwd-guard interventions during normal workflows = 0",
"status": "UNRUN",
"evidence": "",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-16T23:52:37Z",
"resolved_at": ""
},
{
"gate_id": "G6_GOVERNED_ROLLBACK_API",
"mandatory": true,
"requirement": "genuine rollback_generation API owning lock/verify/switch/health/history; live B->A->B proof + incompatible refusal",
"status": "UNRUN",
"evidence": "",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-16T23:52:37Z",
"resolved_at": ""
},
{
"gate_id": "G7_RUNE_MEANINGFUL_STRUCTURAL_COVERAGE",
"mandatory": true,
"requirement": "all meaningful architectural units represented in Scaffold; A-coverage 100%, no significant UNKNOWNs",
"status": "UNRUN",
"evidence": "",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-16T23:52:37Z",
"resolved_at": ""
},
{
"gate_id": "G8_MATERIAL_CHANGE_CLOSURE",
"mandatory": true,
"requirement": "governed construction automatically triggers material_change/revision/spec flow",
"status": "UNRUN",
"evidence": "",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-16T23:52:37Z",
"resolved_at": ""
},
{
"gate_id": "G9_FINAL_TEST_GATES",
"mandatory": true,
"requirement": "all required test/gate checks pass",
"status": "UNRUN",
"evidence": "",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-16T23:52:37Z",
"resolved_at": ""
}
]
}
+160
View File
@@ -0,0 +1,160 @@
{
"tranche": "T21.4",
"purpose": "POST-RESTART FINAL V1 PROOF",
"created_at": "2026-09-16T23:52:37Z",
"gates": [
{
"gate_id": "G10_FINAL_DEPLOY_CONVERGENCE",
"mandatory": true,
"requirement": "master/release/deployed/runtime identity coherent; CI green; deploy complete via await_operation only",
"status": "PASS",
"evidence": "master 9acfc095 == deployed current == runtime identity (build_identity 0.82.2, import smoke ok) == tag v0.82.2; last-good 818aa490 coherent; CI 8700+ success; deploy promoted after qualify; release t",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T00:04:36Z",
"resolved_at": "2026-09-17T02:34:36Z"
},
{
"gate_id": "G11_RAW_SELF_AUDIT",
"mandatory": true,
"requirement": "raw tool-execution audit: 0 normal unjustified waits, 0 normal guard interventions, >=1 deliberate backstop",
"status": "BLOCKED",
"evidence": "NORMAL_EXECUTED_UNJUSTIFIED_WAIT_COUNT=3 (sleep 110/240/120 inter-cycle, guard blind spot: heredoc-wrapped poll text); NORMAL_WAIT_GUARD_INTERVENTION_COUNT=1 (sleep 150 blocked); DELIBERATE_REGRESSION",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T00:04:36Z",
"resolved_at": "2026-09-17T02:34:36Z"
},
{
"gate_id": "G12_ACCEPTANCE_STATE_CONSISTENCY",
"mandatory": true,
"requirement": "GateAccounting result == capability ledger == internal version == runtime evidence == report verdict",
"status": "BLOCKED",
"evidence": "can_accept()=FALSE because G11 BLOCKED (3 unjustified executed waits). Per tranche rules acceptance is prohibited; state left as computed, not altered.",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T00:04:36Z",
"resolved_at": "2026-09-17T02:34:36Z"
},
{
"gate_id": "G1_AWAIT_TOOL_LIVE",
"mandatory": true,
"requirement": "await_operation registered and callable in the running controller tool vocabulary; runtime exposure proven, not inferred from disk",
"status": "PASS",
"evidence": "await-operation in live session tool vocabulary; TS source ~/.mimocode/tools/await-operation.ts; live call vs SHA 40b3731 returned event-bridge envelope (registered/replayed/resumed @ 2026-09-16T23:55",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T00:04:36Z",
"resolved_at": "2026-09-17T00:04:36Z"
},
{
"gate_id": "G2_NATURAL_FIRST_CLASS_WAIT_SELECTION",
"mandatory": true,
"requirement": "await_operation naturally selected (no priming) on all applicable normal async cycles",
"status": "PASS",
"evidence": "await_operation selected directly on EVERY async workflow cycle (branch CI be6bc17/f3ef4672/6b5d8ad, PR CI, master CI 818aa490 incl. watchdog_authority_recovered->success, deploy convergence). No prim",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T00:04:36Z",
"resolved_at": "2026-09-17T02:34:36Z"
},
{
"gate_id": "G3_ZERO_NORMAL_WAIT_GUARD_INTERVENTIONS",
"mandatory": true,
"requirement": "wait-guard interventions during normal cycles = 0 (deliberate backstop counted separately)",
"status": "PASS",
"evidence": "0 wait-guard interventions during the async workflow cycles themselves. DISCLOSED: 1 inter-cycle sleep+poll attempt (sleep 150, post-workflows) blocked by guard -> recorded as REGRESSION_BACKSTOP_PROO",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T00:04:36Z",
"resolved_at": "2026-09-17T02:34:36Z"
},
{
"gate_id": "G4_AUTOMATIC_PROJECT_CWD_BINDING",
"mandatory": true,
"requirement": "execution layer auto-resolves project cwd via ProjectRegistry.resolve_cwd precedence; no manual cd needed",
"status": "PASS",
"evidence": "tool_executor._resolve_execution_cwd wired: workdir>project(resolve_cwd)>process; rune registered; LIVE A-F: rune+ext auto-binding, no leakage, worktree /mnt/workdrive/p149 wins, ghost refused pre-exe",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T00:04:36Z",
"resolved_at": "2026-09-17T00:11:52Z"
},
{
"gate_id": "G5_ZERO_NORMAL_CWD_GUARD_INTERVENTIONS",
"mandatory": true,
"requirement": "cwd-guard interventions during normal workflows = 0",
"status": "PASS",
"evidence": "0 cwd-guard interventions across all normal workflows this tranche; every project-scoped command resolved via project arg; no cd-remediation generated",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T00:04:36Z",
"resolved_at": "2026-09-17T00:11:52Z"
},
{
"gate_id": "G6_GOVERNED_ROLLBACK_API",
"mandatory": true,
"requirement": "genuine rollback_generation API owning lock/verify/switch/health/history; live B->A->B proof + incompatible refusal",
"status": "PASS",
"evidence": "scripts/rollback_generation.py rollback_generation(); 10/10 unit tests; LIVE: B->A ROLLED_BACK health green; REFUSED_NOT_ELIGIBLE + REFUSED_EXPECTED_MISMATCH no mutation; recovery via deploy_promotion",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T00:04:36Z",
"resolved_at": "2026-09-17T00:04:36Z"
},
{
"gate_id": "G7_RUNE_MEANINGFUL_STRUCTURAL_COVERAGE",
"mandatory": true,
"requirement": "all meaningful architectural units represented in Scaffold; A-coverage 100%, no significant UNKNOWNs",
"status": "PASS",
"evidence": "scripts/structural_coverage_audit.py: 237 modules classified A157/B41/C28/D11/E0; meaningful coverage 100% (113/113 src A-units with spine entities); impl ownership 100%; significant UNKNOWN 0; spine ",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T00:04:36Z",
"resolved_at": "2026-09-17T00:37:13Z"
},
{
"gate_id": "G8_MATERIAL_CHANGE_CLOSURE",
"mandatory": true,
"requirement": "governed construction automatically triggers material_change/revision/spec flow",
"status": "PASS",
"evidence": "construction_interface.close() auto-invokes _auto_material_change (rule 19Q.2); live contract BUILDER-2026-09-17T00:17:47Z closed with scope scripts/rollback_generation.py -> capability:rollback-gener",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T00:04:36Z",
"resolved_at": "2026-09-17T00:19:32Z"
},
{
"gate_id": "G9_FINAL_TEST_GATES",
"mandatory": true,
"requirement": "all required test/gate checks pass",
"status": "PASS",
"evidence": "pytest 2122 passed (x2 consecutive); ruff F821 clean; F841: 0 new (53 pre-existing, none in T21.4 files); lint ratchet PASS (BLE001 347/347, S110 75/75); pyright ratchet PASS (109/117 err, 447/454 war",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T00:04:36Z",
"resolved_at": "2026-09-17T02:34:36Z"
}
],
"pull_request": 326,
"pr_propagation_declared": true,
"can_accept_result": {
"ok": false,
"blockers": [
"G11_RAW_SELF_AUDIT: BLOCKED \u2014 ",
"G12_ACCEPTANCE_STATE_CONSISTENCY: BLOCKED \u2014 "
]
}
}
+216
View File
@@ -0,0 +1,216 @@
{
"tranche": "T21.5",
"purpose": "ROOT-CAUSE CONTINUATION + GUARD RETIREMENT + FINAL V1 PARITY",
"predecessor": "store/scaffold/t21.4_gates.json (V1_RETEST_REQUIRED: 10 PASS / 2 BLOCKED)",
"created_at": "2026-09-17T11:03:16Z",
"gates": [
{
"gate_id": "G10_ZERO_MEMORY_RESULT_FAST_PATH",
"mandatory": true,
"requirement": "zero-result memory query continues normal INWARD directly; ZERO_RESULT_EXTRA_MODEL_CALLS=0",
"status": "PASS",
"evidence": "zero-result returns [] and build_context continues directly: test_zero_result_is_normal_success, test_zero_result_inward_context_has_no_extra_fields_or_calls (exactly 1 provider call, no extra fields,",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T11:03:16Z",
"resolved_at": "2026-09-17T11:53:19Z"
},
{
"gate_id": "G11_TEACHABLE_MOMENT_FUTURE_SEAM",
"mandatory": true,
"requirement": "durable candidate-teachable-moment evidence seam exists; APPLICABILITY can reject irrelevant candidates; no parallel memory authority",
"status": "PASS",
"evidence": "mark_teachable_moment_candidate(episode_ref, trigger, summary, emit): CANDIDATE status only (\"may be worth future learning\"), 7 defined triggers incl. rollback_executed/failure_recovered/user_correcti",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T11:03:16Z",
"resolved_at": "2026-09-17T11:53:19Z"
},
{
"gate_id": "G12_HISTORICAL_V1_PARITY",
"mandatory": true,
"requirement": "parity manifest reconciles all historical V1 capability families; no mandatory V1 item PARTIAL/ABSENT/UNPROVEN",
"status": "PASS",
"evidence": "scripts/v1_parity_manifest.py -> store/scaffold/v1_parity_manifest.json: 56 rows over all mandated capability families (capability-decision, construction, continuity, truth, operations, self-awareness",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T11:03:16Z",
"resolved_at": "2026-09-17T11:55:34Z"
},
{
"gate_id": "G13_MATERIAL_CHANGE_AND_EXISTING_V1_REGRESSION",
"mandatory": true,
"requirement": "material-change closure still live; affected T21.4 proof areas re-run",
"status": "PASS",
"evidence": "T21.4 proof areas re-run: rollback 10/10; auto-cwd 8/8 + resolve_cwd + cwd_guard 20/20; material closure 4/4 (+T21.5 ledger-path fix for project scaffolds, regression-covered); CI wait 11/11; event_wa",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T11:03:16Z",
"resolved_at": "2026-09-17T13:29:55Z"
},
{
"gate_id": "G14_FINAL_TEST_GATES",
"mandatory": true,
"requirement": "full established test/validation stack green",
"status": "PASS",
"evidence": "Full battery: pytest 2156 passed + 84 subtests; type ratchet PASS; lint ratchet PASS; Henxels exit 0; diff-cover changed-lines 94% (>=70); structural coverage re-audit: 242 modules, meaningful 100% (1",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T11:03:16Z",
"resolved_at": "2026-09-17T13:29:55Z"
},
{
"gate_id": "G15_FINAL_DEPLOY_CONVERGENCE",
"mandatory": true,
"requirement": "git==release==deployed==runtime==healthy; queued/draining is not PASS",
"status": "PASS",
"evidence": "git master 26e37653 == deployed current == runtime build identity (0.83.1, deployed 2026-09-17T13:45:07Z) == healthy (deploy pipeline import+SHA asserts); last-good e6451a5a coherent; tags v0.83.0/v0.",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T11:03:16Z",
"resolved_at": "2026-09-17T16:33:46Z"
},
{
"gate_id": "G16_RAW_SELF_AUDIT",
"mandatory": true,
"requirement": "all required metrics met; contradictions BLOCK",
"status": "PASS",
"evidence": "TWO-ERA AUDIT, both counts explicit. PRE-FIX (generator live): 7 external sleeps + ~10 model-level re-arms \u2014 each traced to the residual nonterminal-return generator (pending-class verdict string mism",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T11:03:16Z",
"resolved_at": "2026-09-17T17:14:32Z"
},
{
"gate_id": "G17_ACCEPTANCE_STATE_CONSISTENCY",
"mandatory": true,
"requirement": "persisted GateAccounting == ledger readback == versions == runtime evidence == report verdict",
"status": "PASS",
"evidence": "can_accept()=TRUE against the persisted 17-gate object (see readback); Scaffolding internal 1.0.4 (machinery-revised, drift clear); capability ledger acceptance_state to be appended and read back; run",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T11:03:16Z",
"resolved_at": "2026-09-17T17:14:54Z"
},
{
"gate_id": "G1_ASYNC_ROOT_CAUSE_IDENTIFIED",
"mandatory": true,
"requirement": "generator of T21.4 sleep/re-arm behavior identified upstream of the guard (guard = downstream)",
"status": "PASS",
"evidence": "ROOT CAUSES TRACED (guard=downstream): (A) ci_event_wait wait loop BREAKS on nonterminal: watchdog expiry with ci_alive()=False -> lost_event break; event-seen-authority-pending -> break after 1; boun",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T11:03:16Z",
"resolved_at": "2026-09-17T11:05:14Z"
},
{
"gate_id": "G2_SINGLE_LOGICAL_AWAIT",
"mandatory": true,
"requirement": "one logical external wait = one await_operation invocation; internal re-arm; no model-level re-arm loop; live proof",
"status": "PASS",
"evidence": "ci_event_wait.run() rewritten: ALL nonterminal conditions (event-seen-authority-pending, eof-pending, watchdog-with-live-runs) re-arm INTERNALLY; control returns only on terminal verdict or real deadl",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T11:03:16Z",
"resolved_at": "2026-09-17T11:12:23Z"
},
{
"gate_id": "G3_OPERATION_SPECIFIC_COMPLETION_AUTHORITY",
"mandatory": true,
"requirement": "each operation type has matching canonical completion authority; wrong authority can never mean forever-pending; tests + live workflows",
"status": "PASS",
"evidence": "OPERATION_SPECS registry: branch_ci/pull_request_checks -> ci_runs authority (own Actions runs); master_ci/deploy/release -> deploy_qualify. Unknown op_type refused upfront. Tests: branch op NEVER con",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T11:03:16Z",
"resolved_at": "2026-09-17T11:12:23Z"
},
{
"gate_id": "G4_RUNTIME_WAIT_GUARD_RETIRED",
"mandatory": true,
"requirement": "runtime wait-interception removed from MiMo hook + install/sync paths; RUNTIME_WAIT_GUARD_PRESENT=false; prevention lives in tests",
"status": "PASS",
"evidence": "config/mimo-hooks/event-wait-guard.ts rewritten as documented no-op (EventWaitGuard+CwdGuard RETIRED, history+test pointers in source); install_mimo_hook.py synced; hook parity/drift tests 6/6 green; ",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T11:03:16Z",
"resolved_at": "2026-09-17T11:24:22Z"
},
{
"gate_id": "G5_CANONICAL_WAIT_RESUME_ROOT",
"mandatory": true,
"requirement": "ONE wait/resume root authority shared by daemon/library/tool/CI writers; daemon restart resolves it automatically; WAIT_RESUME_ROOT_AUTHORITIES=1, SPLIT_BRAIN_EVENT_STORES=0",
"status": "PASS",
"evidence": "scripts/wr_root_resolver.py = SINGLE authority (env pin > canonical /mnt/workdrive/wr-bridge > legacy; split-brain fails loudly); event_wait.wr_root() delegates; scripts/start_wr_daemon.py resolves on",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T11:03:16Z",
"resolved_at": "2026-09-17T11:24:22Z"
},
{
"gate_id": "G6_COMMON_EXECUTION_CONTEXT_BINDING",
"mandatory": true,
"requirement": "project/worktree context bound at the common execution boundary; no false project-mutation classification of read-only commands",
"status": "PASS",
"evidence": "Common boundary = tool_executor._resolve_execution_cwd (T21.4, re-proven T21.5): workdir > ProjectRegistry.resolve_cwd(project) > process; read-only commands with deployed src/rune paths execute norma",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T11:03:16Z",
"resolved_at": "2026-09-17T11:26:28Z"
},
{
"gate_id": "G7_RUNTIME_CWD_GUARD_RETIRED",
"mandatory": true,
"requirement": "runtime CwdGuard retired after common-boundary fix; manual project-context cd=0; wrong-cwd execution=0",
"status": "PASS",
"evidence": "CwdGuard in installed MiMo hook is a documented no-op (retired with wait guard, same canonical file, parity green). LIVE PROBE: repo-touching git command with NO cd marker and NO workdir executed WITH",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T11:03:16Z",
"resolved_at": "2026-09-17T11:26:28Z"
},
{
"gate_id": "G8_NEW_SCAFFOLD_STRUCTURAL_AWARENESS",
"mandatory": true,
"requirement": "governed adoption auto-registers in Registry + Spine; post-change auto structural update; manual registration steps=0",
"status": "PASS",
"evidence": "construction_activation._auto_structural_awareness wired into PROJECT_BUILD bootstrap (failure-tolerant, boundary-preserving). LIVE+TESTS (test_t215_awareness.py 3/3): adoption of disposable widget-fo",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T11:03:16Z",
"resolved_at": "2026-09-17T11:49:08Z"
},
{
"gate_id": "G9_MEMORY_INWARD_PORT",
"mandatory": true,
"requirement": "future-facing Memory port real, wired into existing INWARD, independently replaceable/mockable; no new storage",
"status": "PASS",
"evidence": "scripts/research_memory_port.py: provider contract (single callable, set_memory_provider/env-bindable, independently replaceable/mockable); wired as ONE port of call inside existing research_inward_co",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-17T11:03:16Z",
"resolved_at": "2026-09-17T11:53:19Z"
}
],
"can_accept_result": {
"ok": true,
"blockers": []
}
}
+515
View File
@@ -0,0 +1,515 @@
{
"tranche": "T21.5",
"rows": [
{
"family": "capability-decision",
"item": "capability discovery",
"classification": "IMPLEMENTED_LIVE",
"module": "src/rune/capability_fit.py",
"authority": "deterministic candidates over structural docs",
"evidence": "tests/test_capability_fit.py; T21.5 live: EXACT fit on widget-forge component",
"gap": ""
},
{
"family": "capability-decision",
"item": "fit/gap detection",
"classification": "IMPLEMENTED_TESTED",
"module": "src/rune/capability_fit.py evaluate_fit",
"authority": "EXACT/PARTIAL/INCOMPATIBLE/UNKNOWN",
"evidence": "tests/test_capability_fit.py",
"gap": ""
},
{
"family": "capability-decision",
"item": "reuse/configure/compose decisions",
"classification": "IMPLEMENTED_TESTED",
"module": "src/rune/capability_fit.py decide",
"authority": "RESOLUTIONS set",
"evidence": "tests/test_construction_decision.py",
"gap": ""
},
{
"family": "capability-decision",
"item": "adapt/acquire decisions",
"classification": "IMPLEMENTED_TESTED",
"module": "src/rune/capability_fit.py decide (ACQUIRE_EXTERNAL, external_evidence)",
"authority": "evidence-gated acquisition",
"evidence": "tests/test_construction_decision.py::test_g_acquire_with_external_evidence",
"gap": ""
},
{
"family": "capability-decision",
"item": "build decision (never silent)",
"classification": "IMPLEMENTED_TESTED",
"module": "src/rune/capability_fit.py decide; construction_decision merge",
"authority": "BUILD_NEW never silently chosen",
"evidence": "tests/test_construction_decision.py::test_h_insufficient_never_build_new",
"gap": ""
},
{
"family": "capability-decision",
"item": "toolchain resolution",
"classification": "IMPLEMENTED_TESTED",
"module": "src/rune/toolchain.py",
"authority": "evidence-backed resolution + decision records",
"evidence": "tests/test_toolchain*.py",
"gap": ""
},
{
"family": "construction",
"item": "construction contracts (lifecycle)",
"classification": "IMPLEMENTED_LIVE",
"module": "src/rune/scaffold.py contracts; construction_interface",
"authority": "phase machine draft\u2192closed",
"evidence": "37 contracts in store/scaffold/current.json; T21.4/T21.5 live closes",
"gap": ""
},
{
"family": "construction",
"item": "approval/consent",
"classification": "IMPLEMENTED_TESTED",
"module": "construction_interface transition gates; builder cannot self-certify",
"authority": "approval-gated transitions",
"evidence": "tests/test_construction_interface.py",
"gap": ""
},
{
"family": "construction",
"item": "scope + expansion (user-gated)",
"classification": "IMPLEMENTED_TESTED",
"module": "construction_interface amend_scope; check_scope",
"authority": "scope verdict green/red/unknown",
"evidence": "tests/test_construction_interface.py; T21.4 live scope green",
"gap": ""
},
{
"family": "construction",
"item": "Project Scaffold",
"classification": "IMPLEMENTED_LIVE",
"module": "src/rune/scaffold.py Scaffold",
"authority": "per-project structural truth",
"evidence": "T21.5 live: widget-forge bootstrap; store/scaffold/projects/",
"gap": ""
},
{
"family": "construction",
"item": "construction patterns",
"classification": "IMPLEMENTED_TESTED",
"module": "src/rune/construction_pattern.py + builtin",
"authority": "evidence-grounded pattern obligations",
"evidence": "tests/test_construction_pattern*.py",
"gap": ""
},
{
"family": "construction",
"item": "layout authority",
"classification": "IMPLEMENTED_TESTED",
"module": "src/rune/layout_contract.py",
"authority": "persistent UI layout checks",
"evidence": "tests/test_layout_contract.py",
"gap": ""
},
{
"family": "construction",
"item": "Research Core / Build Basis",
"classification": "IMPLEMENTED_TESTED",
"module": "src/rune/build_basis.py; scripts/research_*",
"authority": "durable versioned research basis",
"evidence": "tests/test_build_basis.py; research stage tests",
"gap": ""
},
{
"family": "construction",
"item": "predicted/descriptive impact",
"classification": "IMPLEMENTED_TESTED",
"module": "src/rune/impact_preview.py; graph_projection",
"authority": "impact preview + frontier",
"evidence": "tests/test_impact_preview.py; test_v1_workspace_forge.py",
"gap": ""
},
{
"family": "construction",
"item": "migration",
"classification": "IMPLEMENTED_TESTED",
"module": "src/rune/structural_migration.py",
"authority": "consent-gated schema migration",
"evidence": "tests/test_structural_migration.py",
"gap": ""
},
{
"family": "construction",
"item": "supersession",
"classification": "IMPLEMENTED_TESTED",
"module": "src/rune/structural_store.py supersede; capability_ledger supersede",
"authority": "identity survives replacement",
"evidence": "tests/test_v19_ledgers.py",
"gap": ""
},
{
"family": "construction",
"item": "retirement",
"classification": "IMPLEMENTED_TESTED",
"module": "src/rune/capability_ledger.py retire",
"authority": "governed retirement lifecycle",
"evidence": "T17 retirement proof (complexity_scorer); tests",
"gap": ""
},
{
"family": "construction",
"item": "rollback (deploy generations)",
"classification": "IMPLEMENTED_LIVE",
"module": "scripts/rollback_generation.py",
"authority": "governed rollback_generation API",
"evidence": "T21.4 live B->A->B + refusals; tests/test_rollback_generation.py 10/10",
"gap": ""
},
{
"family": "continuity",
"item": "durable/resumable construction",
"classification": "IMPLEMENTED_TESTED",
"module": "construction_interface discover_active_contract; t14/t15 drivers",
"authority": "fresh-process resume from contract id",
"evidence": "tests/test_construction_*; T7 live subprocess resume",
"gap": ""
},
{
"family": "continuity",
"item": "event continuation",
"classification": "IMPLEMENTED_LIVE",
"module": "src/rune/event_wait.py; scripts/ci_event_wait.py",
"authority": "event=wake, authority=truth, single logical await",
"evidence": "T21.5 live workflows + tests/test_single_logical_await.py",
"gap": ""
},
{
"family": "continuity",
"item": "fresh-context reconstruction",
"classification": "IMPLEMENTED_LIVE",
"module": "store ledgers + context packets",
"authority": "no transcript needed",
"evidence": "T21.4 fresh-worker challenge 6/6; T21.5 fresh-process queries",
"gap": ""
},
{
"family": "continuity",
"item": "Registry (project discovery)",
"classification": "IMPLEMENTED_LIVE",
"module": "src/rune/project_registry.py",
"authority": "discovery/index authority",
"evidence": "tests/test_resolve_cwd.py etc.; T21.5 auto-registration",
"gap": ""
},
{
"family": "continuity",
"item": "living specs / ledgers",
"classification": "IMPLEMENTED_LIVE",
"module": "src/rune/capability_ledger.py (attach_spec/revise_spec/drift)",
"authority": "machinery-verified versions",
"evidence": "T21.4/T21.5 live revise_spec flows; tests/test_living_specs.py",
"gap": ""
},
{
"family": "continuity",
"item": "maintenance items",
"classification": "IMPLEMENTED_TESTED",
"module": "store/scaffold entities maintenance:*; scripts/file_drift_issue.py",
"authority": "deduped drift issues",
"evidence": "maintenance:contract-gen-affected-surface resolved (T5)",
"gap": ""
},
{
"family": "truth",
"item": "independent verification",
"classification": "IMPLEMENTED_TESTED",
"module": "construction verify + transition evidence gates",
"authority": "builder cannot self-certify",
"evidence": "tests/test_construction_interface.py",
"gap": ""
},
{
"family": "truth",
"item": "behaviour/test ledger",
"classification": "IMPLEMENTED_TESTED",
"module": "src/rune/behaviour_ledger.py",
"authority": "canonical behaviour specs",
"evidence": "tests/test_v19_ledgers.py; behaviour:deploy-refuses-red",
"gap": ""
},
{
"family": "truth",
"item": "quicksand/selective invalidation",
"classification": "IMPLEMENTED_TESTED",
"module": "src/rune/iteration.py; behaviour ledger harden state",
"authority": "selective retest obligations",
"evidence": "tests/test_v19_ledgers.py; T19 proofs",
"gap": ""
},
{
"family": "truth",
"item": "automated-green != user-confirmed",
"classification": "IMPLEMENTED_TESTED",
"module": "contract_quality + closure observed_evidence gates",
"authority": "green CI never self-certifies closure",
"evidence": "tests/test_contract_quality.py",
"gap": ""
},
{
"family": "truth",
"item": "structural/spec/version drift",
"classification": "IMPLEMENTED_LIVE",
"module": "src/rune/capability_ledger.py spec_drift_status",
"authority": "deterministic revision-based drift",
"evidence": "T21.4 live drift->revise cycle; tests/test_spec_drift_deterministic.py",
"gap": ""
},
{
"family": "truth",
"item": "schema migration consent",
"classification": "IMPLEMENTED_TESTED",
"module": "src/rune/structural_migration.py; project_registry.needs_migration",
"authority": "18I consent sweep",
"evidence": "tests/test_structural_migration.py",
"gap": ""
},
{
"family": "truth",
"item": "predicted-vs-observed outcome",
"classification": "IMPLEMENTED_TESTED",
"module": "src/rune/structural_store.py predicted_vs_observed",
"authority": "recorded mismatch as evidence",
"evidence": "tests + T21.4 contract_quality close",
"gap": ""
},
{
"family": "operations",
"item": "workspace/worktrees",
"classification": "IMPLEMENTED_TESTED",
"module": "src/rune/project_registry.py active_worktree precedence",
"authority": "worktree > repo",
"evidence": "tests/test_resolve_cwd.py; T21.4 live worktree proof",
"gap": ""
},
{
"family": "operations",
"item": "Git authority",
"classification": "IMPLEMENTED_LIVE",
"module": "repo git usage via ancestry/promotion",
"authority": "git ancestry = ordering authority",
"evidence": "scripts/deploy_promotion.py ancestry_of; live promotions",
"gap": ""
},
{
"family": "operations",
"item": "Forge/Gitea projection",
"classification": "IMPLEMENTED_TESTED",
"module": "src/rune/forge.py; forge_client.py; scripts/gitea_*",
"authority": "minimum stable forge boundary",
"evidence": "tests + live PR/issue automation",
"gap": ""
},
{
"family": "operations",
"item": "CI",
"classification": "IMPLEMENTED_LIVE",
"module": ".gitea/workflows/ci.yml; scripts/ci_event_wait.py",
"authority": "two-leg qualification; event-driven waits",
"evidence": "live runs 8700/8722 green; T21.5 single-await",
"gap": ""
},
{
"family": "operations",
"item": "release",
"classification": "IMPLEMENTED_LIVE",
"module": "scripts/create_release.py; semver_bump.py; auto_bump_pr.py",
"authority": "protected-branch bump flow",
"evidence": "releases v0.82.0-0.82.3 created live",
"gap": ""
},
{
"family": "operations",
"item": "deploy",
"classification": "IMPLEMENTED_LIVE",
"module": "scripts/deploy_stage/qualify/promotion.py",
"authority": "monotonic fail-closed promotion",
"evidence": "live deploys 40b3731..c3a0a48; stale-refusal fired in production",
"gap": ""
},
{
"family": "operations",
"item": "rollback",
"classification": "IMPLEMENTED_LIVE",
"module": "scripts/rollback_generation.py",
"authority": "governed regression API",
"evidence": "T21.4 live B->A->B",
"gap": ""
},
{
"family": "operations",
"item": "runtime generations",
"classification": "IMPLEMENTED_LIVE",
"module": "_build_identity.json authority; rune-code current/last-good",
"authority": "identity readback = runtime truth",
"evidence": "T21.4 rollback verification; deployed coherence checks",
"gap": ""
},
{
"family": "operations",
"item": "provider neutrality",
"classification": "IMPLEMENTED_TESTED",
"module": "src/rune/model_routing.py; provider_registry.py",
"authority": "one router, capability floor",
"evidence": "tests/test_model_routing*; canary_p1_06",
"gap": ""
},
{
"family": "operations",
"item": "secrets",
"classification": "IMPLEMENTED_TESTED",
"module": "scripts/gitea_auth.py credential ladder; henxels no-secrets gate",
"authority": "no tracked secrets",
"evidence": "henxels_checks.py CI gate; credential helper",
"gap": ""
},
{
"family": "self-awareness",
"item": "Spine authority",
"classification": "IMPLEMENTED_LIVE",
"module": "src/rune/spine.py",
"authority": "Rune structural self-model",
"evidence": "store/spine/current.json; deterministic builds",
"gap": ""
},
{
"family": "self-awareness",
"item": "Registry discovery",
"classification": "IMPLEMENTED_LIVE",
"module": "src/rune/project_registry.py",
"authority": "project index",
"evidence": "live registry reads across tranches",
"gap": ""
},
{
"family": "self-awareness",
"item": "Project Scaffold authority",
"classification": "IMPLEMENTED_LIVE",
"module": "src/rune/scaffold.py",
"authority": "project structural truth",
"evidence": "live project scaffolds",
"gap": ""
},
{
"family": "self-awareness",
"item": "Rune-wide meaningful structural coverage",
"classification": "IMPLEMENTED_LIVE",
"module": "scripts/structural_coverage_audit.py",
"authority": "A/B/C/D/E classification",
"evidence": "T21.4: 237 modules, 100% meaningful coverage, 0 unknowns",
"gap": ""
},
{
"family": "self-awareness",
"item": "automatic new-Scaffold awareness",
"classification": "IMPLEMENTED_LIVE",
"module": "src/rune/construction_activation.py _auto_structural_awareness",
"authority": "bootstrap auto-registers",
"evidence": "T21.5 live + tests/test_t215_awareness.py",
"gap": ""
},
{
"family": "self-awareness",
"item": "automatic living structural update",
"classification": "IMPLEMENTED_LIVE",
"module": "construction_interface close() -> _auto_material_change",
"authority": "material closure without manual sync",
"evidence": "T21.4/T21.5 live; tests/test_t214_material_closure.py",
"gap": ""
},
{
"family": "learning-seam",
"item": "candidate teachable-moment seam",
"classification": "IMPLEMENTED_TESTED",
"module": "scripts/research_memory_port.py mark_teachable_moment_candidate",
"authority": "CANDIDATE evidence via existing channels",
"evidence": "tests/test_memory_port.py",
"gap": ""
},
{
"family": "learning-seam",
"item": "Memory internal-experience port in INWARD",
"classification": "IMPLEMENTED_TESTED",
"module": "scripts/research_memory_port.py; research_inward_context.build_context",
"authority": "one bounded port of call",
"evidence": "tests/test_memory_port.py",
"gap": ""
},
{
"family": "learning-seam",
"item": "APPLICABILITY governs transfer",
"classification": "IMPLEMENTED_TESTED",
"module": "research_memory_port.applicability_filter + existing APPLICABILITY stage",
"authority": "irrelevant candidates rejected",
"evidence": "tests/test_memory_port.py::test_applicability_rejects_irrelevant_candidate",
"gap": ""
},
{
"family": "learning-seam",
"item": "zero-result fast path",
"classification": "IMPLEMENTED_TESTED",
"module": "research_memory_port.memory_port_of_call",
"authority": "[] -> next normal step",
"evidence": "tests/test_memory_port.py; ZERO_RESULT_EXTRA_MODEL_CALLS=0",
"gap": ""
},
{
"family": "learning-seam",
"item": "no competing memory authority",
"classification": "IMPLEMENTED_TESTED",
"module": "port design (provider delegation, no storage)",
"authority": "Memory stays single future authority",
"evidence": "port creates no store/db; provider contract only",
"gap": ""
},
{
"family": "deferred",
"item": "automatic Growth Delta pattern learning",
"classification": "DEFERRED_NON_V1",
"module": "src/rune/growth_delta.py (existing, non-V1 scope)",
"authority": "post-V1 learning loop",
"evidence": "V1 excludes Memory/Substrate/Growth Delta by tranche mandate; the T21.5 seam provides the harvest point without implementing the loop",
"gap": ""
},
{
"family": "deferred",
"item": "Memory V1 system itself",
"classification": "DEFERRED_NON_V1",
"module": "(future Memory T0 archaeology)",
"authority": "dedicated post-V1 phase",
"evidence": "explicitly excluded by T21.5 mandate \u00a70; only the INWARD port is V1",
"gap": ""
},
{
"family": "deferred",
"item": "Substrate",
"classification": "DEFERRED_NON_V1",
"module": "(future)",
"authority": "post-V1",
"evidence": "explicitly excluded by tranche mandate",
"gap": ""
},
{
"family": "deferred",
"item": "TUI redesign",
"classification": "DEFERRED_NON_V1",
"module": "(existing rune_tui.py adequate)",
"authority": "explicitly excluded by tranche mandate",
"evidence": "TUI work explicitly out of scope for V1 Scaffolding",
"gap": ""
}
],
"counts": {
"IMPLEMENTED_LIVE": 21,
"IMPLEMENTED_TESTED": 31,
"DEFERRED_NON_V1": 4
},
"blocking_items": []
}
+1039 -152
View File
File diff suppressed because it is too large. Load diff
+120
View File
@@ -0,0 +1,120 @@
"""test_auto_execution_context.py — T21.4 §21.4E: automatic project
execution context.
The model names the PROJECT; the execution layer resolves cwd through
ProjectRegistry.resolve_cwd (repo wins over scaffold; active worktree wins
over repo). Unknown projects are refused BEFORE execution. No manual `cd`.
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
import pytest
_SRC = str(Path(__file__).resolve().parent.parent / "src")
if _SRC not in sys.path:
sys.path.insert(0, _SRC)
from rune import tool_executor as te
@pytest.fixture
def two_projects(tmp_path, monkeypatch):
"""rune @ repo-a (with an active worktree seam) + ext @ repo-b."""
store = tmp_path / "store" / "scaffold"
store.mkdir(parents=True)
repo_a = tmp_path / "repo-a"
repo_b = tmp_path / "repo-b"
worktree_a = tmp_path / "wt-a"
for p in (repo_a, repo_b, worktree_a):
p.mkdir()
reg = {"schema_version": 1, "updated_at": "t", "projects": {
"rune": {
"project_id": "rune",
"scaffold_location": str(tmp_path / "scaffold-a"), # NOT the repo
"repo_identity": f"git:{repo_a}",
"active_worktree": "",
},
"ext": {
"project_id": "ext",
"scaffold_location": str(tmp_path / "scaffold-b"),
"repo_identity": f"git:{repo_b}",
"active_worktree": "",
},
}}
(store / "project_registry.json").write_text(json.dumps(reg))
monkeypatch.setenv("RUNE_SCAFFOLD_STORE", str(store))
return {"repo_a": repo_a, "repo_b": repo_b, "worktree_a": worktree_a,
"store": store, "reg": reg}
def _pwd(args: dict) -> dict:
# _run_command directly: the approval flow (prepare/approve) wraps it
# in production and is exercised by the tool-authority tests.
return te._run_command({"command": "pwd", **args})
# A. known project → executes in its repo automatically
def test_rune_auto_binding(two_projects):
r = _pwd({"project": "rune"})
assert r["success"] and r["cwd_source"] == "project_registry"
assert r["result"].strip() == str(two_projects["repo_a"])
# B. disposable external project → executes in ITS repo
def test_external_auto_binding(two_projects):
r = _pwd({"project": "ext"})
assert r["result"].strip() == str(two_projects["repo_b"])
# C. switch rune → ext → rune: no cwd leakage
def test_switch_no_leakage(two_projects):
r1 = _pwd({"project": "rune"})
r2 = _pwd({"project": "ext"})
r3 = _pwd({"project": "rune"})
assert r1["result"].strip() == str(two_projects["repo_a"])
assert r2["result"].strip() == str(two_projects["repo_b"])
assert r3["result"].strip() == str(two_projects["repo_a"])
# D. active worktree wins over repo root
def test_worktree_precedence(two_projects):
store = two_projects["store"]
reg = json.loads((store / "project_registry.json").read_text())
reg["projects"]["rune"]["active_worktree"] = str(two_projects["worktree_a"])
(store / "project_registry.json").write_text(json.dumps(reg))
r = _pwd({"project": "rune"})
assert r["result"].strip() == str(two_projects["worktree_a"])
# E. unknown project mutation refused BEFORE execution
def test_unknown_project_refused(two_projects):
r = te._run_command({
"command": "touch /tmp/pwned-marker-t214", "project": "ghost"})
assert not r["success"]
assert "refused" in r["error"].lower()
assert not Path("/tmp/pwned-marker-t214").exists()
# F. repo path wins over scaffold path (scaffold is never an execution cwd)
def test_repo_wins_over_scaffold(two_projects):
r = _pwd({"project": "rune"})
assert r["result"].strip() == str(two_projects["repo_a"])
assert r["result"].strip() != str(two_projects["reg"]["projects"]["rune"]["scaffold_location"])
# explicit governed workdir outranks project resolution
def test_explicit_workdir_precedence(two_projects):
other = two_projects["repo_a"].parent / "governed-dir"
other.mkdir()
r = _pwd({"workdir": str(other), "project": "rune"})
assert r["result"].strip() == str(other)
assert r["cwd_source"] == "explicit"
# project-less commands keep process cwd (nothing to bind)
def test_project_less_uses_process_cwd(two_projects):
r = te._run_command({"command": "pwd"})
assert r["success"] and r["cwd_source"] == "process"
+7 -5
View File
@@ -151,16 +151,18 @@ def test_16d_lost_event_with_no_verdict_fails_truthfully(
def test_16d_ci_alive_rearm_then_event(wr_daemon, monkeypatch):
mod = _load_ci_wait_module()
monkeypatch.setattr(mod, "authority_verdict", lambda: "success")
# first watchdog expiry (t=3s): CI runs still non-terminal → re-arm,
# not fail; the event arrives only AFTER that expiry (t≥4s)
# first watchdog expiry (t=3s): authority pending (lost event, no
# runs) → ONE bounded re-read, then truthful recovery; the event
# arrives only AFTER that expiry (t≥4s)
alive = {"n": 0}
def flaky_alive():
def flaky_alive(op_type, sha):
alive["n"] += 1
return alive["n"] <= 1 # alive on first expiry, gone after event
monkeypatch.setattr(mod, "ci_alive", flaky_alive)
monkeypatch.setattr(mod, "op_ci_alive", flaky_alive)
monkeypatch.setattr(mod, "op_authority_verdict",
lambda t, sha: "success")
def post_late():
deadline = time.time() + 10
+30 -13
View File
@@ -71,19 +71,36 @@ class Daemon:
WR_WATCH_SECS="30",
WR_TERM_GRACE_SECS="1",
RUNE_WR_ROOT=str(tmp_root))
self.proc = subprocess.Popen(
[sys.executable, str(WR / "daemon.py"), str(port)],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
text=True, env=env)
deadline = time.time() + 15
while time.time() < deadline:
try:
with urllib.request.urlopen(
f"http://127.0.0.1:{port}/health", timeout=2) as r:
if r.status == 200:
return
except Exception:
time.sleep(0.2)
# T21.5 hardening: under heavy parallel load the first start may
# lose the bind race or the health probe may exceed its window.
# Retry once on a FRESH ephemeral port before failing — the
# fixture must be robust, not flaky.
import socket as _socket
for attempt in range(2):
if attempt == 1:
with _socket.socket() as _s:
_s.bind(("127.0.0.1", 0))
self.port = _s.getsockname()[1]
self.proc = subprocess.Popen(
[sys.executable, str(WR / "daemon.py"), str(self.port)],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
text=True, env={**env, "WR_ROOT": str(tmp_root),
"RUNE_WR_ROOT": str(tmp_root)})
deadline = time.time() + 15
healthy = False
while time.time() < deadline:
try:
with urllib.request.urlopen(
f"http://127.0.0.1:{self.port}/health",
timeout=2) as r:
if r.status == 200:
healthy = True
break
except Exception:
time.sleep(0.2)
if healthy:
return
self.stop()
raise RuntimeError("daemon did not become healthy")
def url(self) -> str:
+7 -1
View File
@@ -5,7 +5,13 @@ import hashlib
from pathlib import Path
REPO = Path(__file__).resolve().parent.parent
CANONICAL = REPO / "config" / "mimo-hooks" / "event-wait-guard.ts"
INSTALLED = Path.home() / ".mimocode" / "hooks" / "event-wait-guard.ts"
# T21.3: the hook lives at the ACTUAL controller user's home, not the
# test runner's. Use an env override or skip when unavailable.
import os as _os
INSTALLED = Path(_os.environ.get(
"MIMOCODE_HOOK_TARGET",
str(Path(_os.environ.get("RUNE_HOOK_TARGET_HOME", "/home/nick"))
/ ".mimocode" / "hooks" / "event-wait-guard.ts")))
def _sha(p: Path) -> str:
+151
View File
@@ -0,0 +1,151 @@
"""test_memory_port.py — T21.5 §10-12 (G9/G10/G11).
G9 Memory port: real, wired into existing INWARD, replaceable/mockable.
G10 zero-result fast path: [] → next normal INWARD step directly;
ZERO_RESULT_EXTRA_MODEL_CALLS = 0.
G11 teachable-moment future seam: candidates only; APPLICABILITY governs
transfer; irrelevant candidates never contaminate SYNTHESIS.
"""
from __future__ import annotations
import sys
from pathlib import Path
import pytest
REPO = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(REPO / "scripts"))
sys.path.insert(0, str(REPO / "src"))
import research_memory_port as rmp # noqa: E402
import research_inward_context as ric # noqa: E402
@pytest.fixture(autouse=True)
def clean_provider():
rmp.set_memory_provider(None)
yield
rmp.set_memory_provider(None)
def _issue():
return {"title": "Widget export drops unicode",
"body": "widget_export.py fails on unicode names in bundles"}
def _provider_result(candidates):
def provider(query_text, keywords, *, limit=5):
return candidates[:limit]
return provider
# ── G9: port real, wired, mockable ─────────────────────────────────────────
def test_port_unbound_returns_empty_fast():
assert rmp.memory_port_of_call(_issue(), ["widget", "export"]) == []
def test_port_retrieves_candidates():
seen = {}
def provider(query_text, keywords, *, limit=5):
seen["query"] = query_text
seen["keywords"] = keywords
return [{"source": "memory", "kind": "construction_episode",
"summary": "widget_export unicode bug fixed before",
"relevance": 0.9, "episode_ref": "ep-1"}]
out = rmp.memory_port_of_call(_issue(), ["widget", "unicode"],
provider=provider)
assert len(out) == 1 and out[0]["episode_ref"] == "ep-1"
assert "widget" in seen["query"]
def test_port_wired_into_inward_context():
calls = {"n": 0}
def provider(query_text, keywords, *, limit=5):
calls["n"] += 1
return [{"source": "memory", "kind": "prior_failure",
"summary": "widget export unicode", "relevance": 0.8,
"episode_ref": "ep-2"}]
rmp.set_memory_provider(provider)
ctx = ric.build_context(_issue(), {"stages": ["INWARD"]})
assert calls["n"] == 1 # ONE port of call, no extra stages
assert ctx["memory_candidates"][0]["episode_ref"] == "ep-2"
def test_port_provider_crash_never_breaks_inward():
def provider(*a, **k):
raise RuntimeError("memory down")
out = rmp.memory_port_of_call(_issue(), ["widget"], provider=provider)
assert out == [] # degrades to the fast path
def test_low_relevance_candidates_filtered():
def provider(query_text, keywords, *, limit=5):
return [{"source": "memory", "kind": "prior_failure",
"summary": "unrelated", "relevance": 0.05,
"episode_ref": "ep-3"}]
assert rmp.memory_port_of_call(_issue(), ["widget"],
provider=provider) == []
# ── G10: zero-result fast path ──────────────────────────────────────────────
def test_zero_result_is_normal_success():
def provider(query_text, keywords, *, limit=5):
return []
out = rmp.memory_port_of_call(_issue(), ["widget"], provider=provider)
assert out == [] # no error, no ceremony — caller continues directly
def test_zero_result_inward_context_has_no_extra_fields_or_calls():
calls = {"n": 0}
def provider(query_text, keywords, *, limit=5):
calls["n"] += 1
return []
rmp.set_memory_provider(provider)
ctx = ric.build_context(_issue(), {"stages": ["INWARD"]})
assert calls["n"] == 1
assert ctx["memory_candidates"] == []
# ZERO_RESULT_EXTRA_MODEL_CALLS = 0: nothing in the context requests
# extra cognition for the empty result
assert not ctx.get("mimo_needed") or ctx["mimo_needed"] in (True, False)
# ── G11: teachable-moment future seam ──────────────────────────────────────
def test_mark_candidate_records_evidence():
emitted = []
rec = rmp.mark_teachable_moment_candidate(
"rollback:caa77f8a", trigger="rollback_executed",
summary="B->A rollback lived through; health gate caught lag",
emit=emitted.append)
assert rec["status"] == "CANDIDATE"
assert emitted and emitted[0]["episode_ref"] == "rollback:caa77f8a"
def test_unknown_trigger_refused():
import pytest
with pytest.raises(ValueError, match="unknown trigger"):
rmp.mark_teachable_moment_candidate("e", trigger="always_learn",
summary="x")
def test_applicability_rejects_irrelevant_candidate():
candidates = [
{"summary": "widget export unicode handling", "relevance": 0.9,
"keywords": ["widget", "unicode"]},
{"summary": "database connection pooling strategy", "relevance": 0.9,
"keywords": ["database", "pool"]},
]
applicable, rejected = rmp.applicability_filter(
candidates, ["widget", "unicode", "export"])
assert [c["summary"] for c in applicable] == \
["widget export unicode handling"]
assert [c["summary"] for c in rejected] == \
["database connection pooling strategy"]
# SYNTHESIS receives only applicable candidates — rejected never leak
synthesis_input = applicable
assert all("pool" not in c["summary"] for c in synthesis_input)
+169
View File
@@ -0,0 +1,169 @@
"""test_rollback_generation.py — T21.4 §21.4F: governed rollback API tests.
Refusals must occur BEFORE any mutation of current/last-good. The live
B→A→B proof runs against the real deploy root; these tests prove the
contract on synthetic deploy roots (same code path, no production risk).
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
import pytest
_SCRIPTS = str(Path(__file__).resolve().parent.parent / "scripts")
if _SCRIPTS not in sys.path:
sys.path.insert(0, _SCRIPTS)
import rollback_generation as rg
def make_generation(root: Path, name: str, commit: str) -> Path:
gen = root / name
pkg = gen / "src" / "rune"
pkg.mkdir(parents=True)
(pkg / "__init__.py").write_text('__version__ = "test"\n')
(gen / "src" / "rune" / "_build_identity.json").write_text(
json.dumps({"commit": commit}))
return gen
@pytest.fixture
def deploy_root(tmp_path, monkeypatch):
root = tmp_path / "rune-code"
root.mkdir()
gen_a = make_generation(root, "gen-a", "a" * 40)
gen_b = make_generation(root, "gen-b", "b" * 40)
(root / "current").symlink_to(gen_a)
(root / "last-good").symlink_to(gen_b)
history = tmp_path / "history.jsonl"
monkeypatch.setattr(rg.deploy_promotion, "ancestry_of",
lambda t, d, r: "ancestor")
return root, history, gen_a, gen_b
def readlink(p: Path) -> str:
return str(p.readlink())
# ── refusals: BEFORE mutation ──────────────────────────────────────────────
def test_refuse_no_reason(deploy_root):
root, history, gen_a, gen_b = deploy_root
outcome, detail = rg.rollback_generation("gen-b", "a" * 40, " ",
deploy_root=root,
history_path=history)
assert outcome == rg.REFUSED_NO_REASON
assert readlink(root / "current") == str(gen_a)
def test_refuse_expected_mismatch(deploy_root):
root, history, gen_a, gen_b = deploy_root
outcome, detail = rg.rollback_generation("gen-b", "f" * 40, "stale caller",
deploy_root=root,
history_path=history)
assert outcome == rg.REFUSED_EXPECTED_MISMATCH
assert readlink(root / "current") == str(gen_a)
assert readlink(root / "last-good") == str(gen_b)
def test_refuse_target_missing(deploy_root):
root, history, gen_a, gen_b = deploy_root
outcome, _ = rg.rollback_generation("gen-ghost", "a" * 40, "ghost target",
deploy_root=root, history_path=history)
assert outcome == rg.REFUSED_TARGET_MISSING
assert readlink(root / "current") == str(gen_a)
def test_refuse_target_unreadable(deploy_root):
root, history, gen_a, gen_b = deploy_root
bad = root / "gen-bad"
bad.mkdir()
outcome, _ = rg.rollback_generation("gen-bad", "a" * 40, "no identity",
deploy_root=root, history_path=history)
assert outcome == rg.REFUSED_TARGET_UNREADABLE
assert readlink(root / "current") == str(gen_a)
def test_refuse_not_eligible_divergent(deploy_root, monkeypatch):
root, history, gen_a, gen_b = deploy_root
monkeypatch.setattr(rg.deploy_promotion, "ancestry_of",
lambda t, d, r: "divergent")
outcome, detail = rg.rollback_generation("gen-b", "a" * 40, "divergent",
deploy_root=root,
history_path=history)
assert outcome == rg.REFUSED_NOT_ELIGIBLE
assert "divergent" in detail["error"]
assert readlink(root / "current") == str(gen_a)
# ── the operation itself ───────────────────────────────────────────────────
def test_rollback_success_switch(deploy_root):
root, history, gen_a, gen_b = deploy_root
outcome, detail = rg.rollback_generation("gen-b", "a" * 40, "regression",
deploy_root=root,
history_path=history)
assert outcome == rg.ROLLED_BACK, detail
assert readlink(root / "current") == str(gen_b)
assert readlink(root / "last-good") == str(gen_a)
events = [json.loads(l) for l in
history.read_text(encoding="utf-8").splitlines()]
assert events[0]["outcome"] == rg.ROLLED_BACK
assert events[0]["reason"] == "regression"
def test_noop_when_already_current(deploy_root):
root, history, gen_a, gen_b = deploy_root
outcome, _ = rg.rollback_generation("gen-a", "a" * 40, "same",
deploy_root=root, history_path=history)
assert outcome == rg.NOOP
def test_health_failure_reverts(deploy_root, monkeypatch):
root, history, gen_a, gen_b = deploy_root
monkeypatch.setattr(rg, "_health", lambda r, g, timeout=60:
(False, "import smoke failed: boom"))
outcome, detail = rg.rollback_generation("gen-b", "a" * 40, "bad health",
deploy_root=root,
history_path=history)
assert outcome == rg.ROLLED_BACK_REVERTED
assert readlink(root / "current") == str(gen_a)
events = [json.loads(l) for l in
history.read_text(encoding="utf-8").splitlines()]
assert events[0]["outcome"] == rg.ROLLED_BACK_REVERTED
def test_import_smoke_real(tmp_path):
"""Health check runs a REAL import against a generation tree."""
root = tmp_path / "rune-code"
root.mkdir()
make_generation(root, "gen-x", "x" * 40)
ok, detail = rg._health(root, "gen-x")
assert ok, detail
def test_lock_shared_with_promotion(deploy_root):
"""Rollback serialises on the SAME lock file promotion uses."""
root, history, gen_a, gen_b = deploy_root
lock = root / ".promotion.lock"
import fcntl
with open(lock, "w") as fd:
fcntl.flock(fd, fcntl.LOCK_EX)
import threading
results = {}
def try_rollback():
results["r"] = rg.rollback_generation(
"gen-b", "a" * 40, "contended", deploy_root=root,
history_path=history)
t = threading.Thread(target=try_rollback)
t.start()
import time
time.sleep(0.3)
assert "r" not in results # blocked on the shared lock
fcntl.flock(fd, fcntl.LOCK_UN)
t.join(timeout=10)
assert results["r"][0] == rg.ROLLED_BACK
+216
View File
@@ -0,0 +1,216 @@
"""test_single_logical_await.py — T21.5 §3/§4.
G2: one logical external wait = one run() invocation; every nonterminal
condition re-arms INTERNALLY; control returns to the caller only on a
terminal verdict or real deadline expiry.
G3: the wait verifier matches the operation — branch/PR ops are judged
by their own Actions runs; deploy qualification is never their authority
(wrong authority can never mean forever-pending).
"""
from __future__ import annotations
import importlib.util
import json
import sys
import threading
import time
from pathlib import Path
import pytest
REPO = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(REPO / "src"))
from rune import event_wait as ew # noqa: E402
from tests.test_event_wait import Daemon # noqa: E402
def _load():
spec = importlib.util.spec_from_file_location(
"ci_event_wait", str(REPO / "scripts" / "ci_event_wait.py"))
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
SHA = "b" * 40
OP = f"ci-{SHA}"
def test_unknown_op_type_refused():
mod = _load()
with pytest.raises(ValueError, match="wait verifier must match"):
mod.op_spec("nonsense_op")
def test_all_spec_fields_present():
mod = _load()
for name, spec in mod.OPERATION_SPECS.items():
assert set(spec) >= {"workflow_path", "head_branches", "authority",
"event_types", "terminal", "failure"}, name
assert spec["authority"] in ("ci_runs", "deploy_qualify")
def test_branch_ci_uses_own_runs_not_deploy_qualify(monkeypatch):
"""G3 core: a branch_ci op must NEVER consult deploy qualification."""
mod = _load()
calls = []
def boom():
calls.append("deploy_qualify")
return "pending"
monkeypatch.setattr(mod, "authority_verdict", boom)
monkeypatch.setattr(mod, "_ci_runs_verdict", lambda spec, sha: "success")
v = mod.op_authority_verdict("branch_ci", SHA)
assert v == "success"
assert calls == [] # deploy authority never consulted
def test_master_ci_uses_deploy_qualify(monkeypatch):
mod = _load()
monkeypatch.setattr(mod, "authority_verdict", lambda: "success")
assert mod.op_authority_verdict("master_ci", SHA) == "success"
def test_wrong_authority_never_forever_pending(monkeypatch):
"""If deploy qualification is blind to a SHA (e.g. branch op), the
op's own runs still resolve the verdict — no deadlock."""
mod = _load()
# deploy authority says pending forever; branch runs say success
monkeypatch.setattr(mod, "authority_verdict", lambda: "pending")
monkeypatch.setattr(mod, "_ci_runs_verdict", lambda spec, sha: "success")
assert mod.op_authority_verdict("branch_ci", SHA) == "success"
def _single_wait_scenario(wr_daemon, monkeypatch, verdict_sequence):
"""Register one run() call; the authority returns verdict_sequence[i]
on the i-th op_authority_verdict call; a wake event posts after each
nonterminal authority read. Returns (rc, out, authority_calls)."""
mod = _load()
seq = list(verdict_sequence)
calls = {"n": 0}
def authority(op_type, sha):
i = min(calls["n"], len(seq) - 1)
calls["n"] += 1
return seq[i]
monkeypatch.setattr(mod, "op_authority_verdict", authority)
monkeypatch.setattr(mod, "op_ci_alive", lambda t, sha: False)
def post_wakes():
# post a wake as soon as the wait record exists, then again after
# each nonterminal authority cycle (bounded; last is harmless)
for _ in range(len(seq) + 1):
deadline = time.time() + 10
posted = False
start = time.time()
while time.time() < deadline:
if ew.operation_wait(OP) is not None:
wr_daemon.post_event({"operation_id": OP,
"provider": "gitea",
"event_type": "ci_completed",
"sha": SHA, "exit_code": 0,
"completed_at": time.time()})
posted = True
break
time.sleep(0.05)
if not posted:
return
time.sleep(1.0) # next wake after the internal re-arm
threading.Thread(target=post_wakes, daemon=True).start()
rc = mod.run(sha=SHA, deadline_secs=30, watchdog_secs=3,
op_type="branch_ci", daemon_url=wr_daemon.url())
return rc, calls["n"]
def test_single_logical_wait_survives_nonterminal_wakes(wr_daemon, monkeypatch):
"""Two nonterminal wakes then terminal: ONE run() call returns the
terminal verdict; the model never saw the pending states."""
rc, n = _single_wait_scenario(wr_daemon, monkeypatch,
["pending", "pending", "success"])
assert rc == 0
assert n >= 3 # authority re-read internally after each wake
def test_pending_probe_failed_is_nonterminal(wr_daemon, monkeypatch):
"""§18 audit fix: "pending (probe failed)" is a pending-class verdict —
the tool must re-arm internally, never return it as terminal."""
mod = _load()
assert mod.normalize_verdict("pending (probe failed)") == "pending"
assert mod.normalize_verdict("pending") == "pending"
assert mod.normalize_verdict("success") == "success"
# live: authority alternates probe-failure and pending, then success;
# ONE run() call absorbs all of it
rc, n = _single_wait_scenario(
wr_daemon, monkeypatch,
["pending (probe failed)", "pending", "success"])
assert rc == 0
assert n >= 3
def test_not_started_operation_rearms_internally(wr_daemon, monkeypatch):
"""§18 audit fix: runs not visible yet is EXPECTED nonterminal —
internal re-arm, no model return."""
mod = _load()
seq = {"n": 0}
def authority(op_type, sha):
seq["n"] += 1
return "pending" if seq["n"] <= 2 else "success"
monkeypatch.setattr(mod, "op_authority_verdict", authority)
monkeypatch.setattr(mod, "op_ci_alive", lambda t, sha: False)
def post_wakes():
for _ in range(3):
deadline = time.time() + 10
while time.time() < deadline:
if ew.operation_wait(OP) is not None:
wr_daemon.post_event({"operation_id": OP,
"provider": "gitea",
"event_type": "ci_completed",
"sha": SHA, "exit_code": 0,
"completed_at": time.time()})
break
time.sleep(0.05)
time.sleep(1.0)
threading.Thread(target=post_wakes, daemon=True).start()
rc = mod.run(sha=SHA, deadline_secs=30, watchdog_secs=3,
op_type="branch_ci", daemon_url=wr_daemon.url())
assert rc == 0
assert seq["n"] >= 3
def test_deadline_is_the_only_model_return_for_pending(wr_daemon,
monkeypatch):
"""Authority stays pending the whole time: run() returns only at real
deadline expiry — not at first watchdog or first wake."""
mod = _load()
monkeypatch.setattr(mod, "op_authority_verdict",
lambda t, sha: "pending")
monkeypatch.setattr(mod, "op_ci_alive", lambda t, sha: True)
def post_wakes():
for _ in range(3):
deadline = time.time() + 10
while time.time() < deadline:
if ew.operation_wait(OP) is not None:
wr_daemon.post_event({"operation_id": OP,
"provider": "gitea",
"event_type": "ci_completed",
"sha": SHA, "exit_code": 0,
"completed_at": time.time()})
break
time.sleep(0.05)
time.sleep(1.0)
threading.Thread(target=post_wakes, daemon=True).start()
t0 = time.time()
rc = mod.run(sha=SHA, deadline_secs=8, watchdog_secs=3,
op_type="branch_ci", daemon_url=wr_daemon.url())
wall = time.time() - t0
assert rc == 1 # deadline pending
assert wall >= 7.0 # NOT returned at first watchdog (3s) or first wake
+168
View File
@@ -0,0 +1,168 @@
"""test_t214_material_closure.py — T21.4 §21.4H/§21.4G coverage.
Covers:
- construction_interface.close() AUTO material-change closure
(rule 19Q.2: governed construction cannot bypass the revision flow)
- spine.build() entry-point seeding (__main__ is a runtime path)
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
import pytest
_ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(_ROOT / "src"))
from rune.construction_interface import ConstructionInterface
from rune.capability_ledger import CapabilityLedger
from rune import spine
@pytest.fixture
def governed_store(tmp_path):
"""A store shaped like the Rune store: scaffold + rune_model ledgers."""
scaffold_dir = tmp_path / "store" / "scaffold"
scaffold_dir.mkdir(parents=True)
model_dir = tmp_path / "store" / "rune_model"
model_dir.mkdir(parents=True)
ledger = CapabilityLedger(model_dir)
ledger.define(capability_id="closure-target",
name="Closure target",
purpose="capability whose source file is in contract scope",
component="component:x", owning_project="t",
source_locations=["scripts/rollback_generation.py"])
ledger.save()
return tmp_path, scaffold_dir, model_dir, ledger
def test_close_auto_material_change(governed_store):
tmp_path, scaffold_dir, model_dir, ledger = governed_store
from rune import scaffold as sc
from rune import structural_store as ss
scaffold = sc.Scaffold(scaffold_dir, "t21.4-test")
doc = scaffold.init(purpose="t21.4 closure test")
ss.add_entity(doc, id="component:closure-target", kind="component",
identities={"source": "scripts/rollback_generation.py"},
maturity="EXERCISED")
ss.add_relationship(doc, type="provides", from_id="component:closure-target",
to_id="contract:closure-target")
scaffold.commit(doc, what="seed")
ci = ConstructionInterface("t21.4-test", project_store=str(scaffold_dir))
r = ci.submit_need(
objective="bounded change touching closure_target",
origin="operator",
affected_surface=["scripts/rollback_generation.py"],
constraints={"environment": {"existing_language": "python"},
"contracts": ["contract:closure-target"]},
evidence_refs=["tests/test_t214_material_closure.py"])
assert r["success"], r.get("errors")
cid = r["ids"]["contract_id"]
for phase in ("approved", "building", "verifying"):
t = ci.transition(cid, phase, evidence_ref=f"t21.4 test {phase}")
assert t["success"], t.get("errors")
c = ci.close(cid, observed_evidence=[{"kind": "test", "detail": "x"}],
scope_files=["scripts/rollback_generation.py"])
assert c["success"], c.get("errors")
closure = c["state"]["material_change_closure"]
assert closure["changed"] == ["capability:closure-target"]
assert closure["unmodeled"] == []
# revision incremented and durable
rec = CapabilityLedger(model_dir).get("closure-target")
assert rec["material_revision"] == 1
assert any(h["event"] == "material_change"
for h in json.loads(
(model_dir / "capability_ledger.json").read_text()
)["history"])
def test_close_unmodeled_files_reported(governed_store):
tmp_path, scaffold_dir, _, _ = governed_store
from rune import scaffold as sc
from rune import structural_store as ss
scaffold = sc.Scaffold(scaffold_dir, "t21.4-test")
doc = scaffold.init(purpose="t21.4 closure test")
ss.add_entity(doc, id="component:closure-target", kind="component",
identities={"source": "scripts/rollback_generation.py"},
maturity="EXERCISED")
ss.add_relationship(doc, type="provides", from_id="component:closure-target",
to_id="contract:closure-target")
scaffold.commit(doc, what="seed")
ci = ConstructionInterface("t21.4-test", project_store=str(scaffold_dir))
r = ci.submit_need(objective="scope with an unmodeled file",
origin="operator",
affected_surface=["scripts/rollback_generation.py",
"tests/test_t214_material_closure.py"],
constraints={"environment": {"existing_language": "python"},
"contracts": ["contract:closure-target"]},
evidence_refs=["tests/test_t214_material_closure.py"])
cid = r["ids"]["contract_id"]
for phase in ("approved", "building", "verifying"):
ci.transition(cid, phase, evidence_ref="t")
c = ci.close(cid, observed_evidence=[{"kind": "test", "detail": "x"}],
scope_files=["tests/test_t214_material_closure.py"])
closure = c["state"]["material_change_closure"]
assert closure["changed"] == []
assert closure["unmodeled"] == ["tests/test_t214_material_closure.py"]
def test_closure_error_does_not_break_close(governed_store, monkeypatch):
"""OSError in the ledger write degrades to a note — close() succeeds."""
tmp_path, scaffold_dir, model_dir, ledger = governed_store
from rune import scaffold as sc
from rune import structural_store as ss
scaffold = sc.Scaffold(scaffold_dir, "t21.4-test")
doc = scaffold.init(purpose="t21.4 closure test")
ss.add_entity(doc, id="component:closure-target", kind="component",
identities={"source": "scripts/rollback_generation.py"},
maturity="EXERCISED")
ss.add_relationship(doc, type="provides", from_id="component:closure-target",
to_id="contract:closure-target")
scaffold.commit(doc, what="seed")
monkeypatch.setattr(
"rune.capability_ledger.CapabilityLedger.save",
lambda self: (_ for _ in ()).throw(OSError("disk full")),
)
ci = ConstructionInterface("t21.4-test", project_store=str(scaffold_dir))
r = ci.submit_need(
objective="bounded change touching closure_target",
origin="operator",
affected_surface=["scripts/rollback_generation.py"],
constraints={"environment": {"existing_language": "python"},
"contracts": ["contract:closure-target"]},
evidence_refs=["tests/test_t214_material_closure.py"])
cid = r["ids"]["contract_id"]
for phase in ("approved", "building", "verifying"):
ci.transition(cid, phase, evidence_ref="t")
c = ci.close(cid, observed_evidence=[{"kind": "test", "detail": "x"}],
scope_files=["scripts/rollback_generation.py"])
assert c["success"], c.get("errors")
closure = c["state"]["material_change_closure"]
assert "closure error" in closure.get("note", "")
assert closure["changed"] == ["capability:closure-target"]
def test_spine_seeds_entry_point(tmp_path, monkeypatch):
"""__main__ is invisible to the import graph but is a real runtime
path — build() must seed it deterministically."""
src_dir = tmp_path / "src" / "rune"
src_dir.mkdir(parents=True)
(src_dir / "__main__.py").write_text("# entry point\n")
(src_dir / "seeded_mod.py").write_text("x = 1\n")
empty_store = tmp_path / "spine.json"
monkeypatch.setattr(spine, "SRC_DIR", src_dir)
monkeypatch.setattr(spine, "load", lambda path=None: None)
monkeypatch.setattr(spine, "save",
lambda doc, path=None: empty_store)
monkeypatch.setattr(spine, "_canary_registry", lambda: [])
monkeypatch.setattr(spine, "ingest_umbrellas", lambda doc, mp=None: 0)
monkeypatch.setattr(spine, "_test_backed_modules", lambda: set())
monkeypatch.setattr(spine, "DEPLOYED_LINK", tmp_path / "no-deploy")
doc = spine.build()
entry = spine.entity(doc, "module:__main__")
assert entry is not None
assert entry["identities"]["path"] == "src/rune/__main__.py"
+232
View File
@@ -0,0 +1,232 @@
"""test_t215_awareness.py — T21.5 §9: automatic new-Scaffold structural
awareness (G8).
NEW_SCAFFOLD_MANUAL_REGISTRATION_STEPS = 0
POST_CHANGE_MANUAL_MODEL_SYNC_STEPS = 0
"""
from __future__ import annotations
import json
import subprocess
import sys
from pathlib import Path
import pytest
REPO = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(REPO / "src"))
@pytest.fixture
def store(tmp_path):
from rune.scaffold import Scaffold
from rune import spine as spine_mod
store = tmp_path / "store" / "scaffold"
store.mkdir(parents=True)
spine_mod.save(spine_mod.new_doc(), tmp_path / "store" / "spine" / "current.json")
scaffold = Scaffold(store, "issue161-harness")
scaffold.init(purpose="t21.5 awareness test")
return tmp_path, store, scaffold
def _adopt(store, scaffold, project="widget-forge"):
from rune import construction_decision as cd
from rune import construction_activation as ca
from rune import spine as spine_mod
need = cd.Need(
need_id=f"need-adopt-{project}",
objective=f"Build {project} as a new standalone project with its "
"own repository: the capability",
origin="operator", project=project,
constraints={"environment": {"existing_language": "python"},
"contracts": [f"contract:{project}-export"]},
evidence_refs=["tests/test_t215_awareness.py"])
trace = cd.make_decision(need,
authorities={"project_scaffold": scaffold.load(),
"rune_spine": spine_mod.load(
store.parent / "spine" / "current.json")},
project_toolchain={"existing_language": "python"})
assert trace["construction_mode"]["mode"] == "PROJECT_BUILD"
return ca.dispatch_from_decision(trace, need=need, scaffold=scaffold,
builder="t21.5-test")
def test_adoption_auto_registers(store):
tmp, store, scaffold = store
from rune.project_registry import ProjectRegistry
from rune import spine as spine_mod
rec = _adopt(store, scaffold)
assert rec["activated"] is True
assert rec["structural_awareness"] == {"registry": "registered",
"spine": "recorded"}
# A. registry knows — no manual registration
reg = ProjectRegistry(store)
e = reg.get("widget-forge")
assert e is not None
assert e["scaffold_location"] == str(store / "projects" / "widget-forge")
# B. spine relationship — no manual bookkeeping
sdoc = spine_mod.load(tmp / "store" / "spine" / "current.json")
rels = [(r["type"], r["from"], r["to"]) for r in sdoc["relationships"]]
assert ("manages", "project:rune", "project:widget-forge") in rels
# C. project scaffold canonical; spine holds NO project modules
assert (store / "projects" / "widget-forge" / "current.json").is_file()
sdoc_ids = {x["id"] for x in sdoc["entities"]}
assert not any("widget" in i for i in sdoc_ids) # no duplication
def test_capability_discovery_finds_project_capability(store):
tmp, store, scaffold = store
from rune.scaffold import Scaffold
from rune import structural_store as ss
from rune.capability_fit import (discover_candidates, evaluate_fit,
CapabilityRequirement)
_adopt(store, scaffold)
ps = Scaffold(store / "projects" / "widget-forge", "widget-forge")
pdoc = ps.load()
ss.add_entity(pdoc, id="component:widget-export", kind="component",
identities={"source": "src/widget_export.py"},
maturity="DECLARED")
ss.add_relationship(pdoc, type="provides",
from_id="component:widget-export",
to_id="contract:widget-export")
ps.commit(pdoc, what="model capability")
req = CapabilityRequirement("widget export capability",
contracts=["contract:widget-export"],
environment={})
cands = discover_candidates(pdoc, req)
fits = [evaluate_fit(req, c) for c in cands]
assert any(c["id"] == "component:widget-export" and f["fit"] == "EXACT"
for c, f in zip(cands, fits))
def test_awareness_failure_tolerant(store, monkeypatch):
"""Registry/spine errors are recorded, never break the bootstrap."""
tmp, store, scaffold = store
import rune.construction_activation as ca
from rune.project_registry import ProjectRegistry
monkeypatch.setattr(ProjectRegistry, "save",
lambda self: (_ for _ in ()).throw(OSError("disk")))
rec = _adopt(store, scaffold)
assert rec["activated"] is True # bootstrap succeeded
aw = rec["structural_awareness"]
assert "error" in aw["registry"]
# spine edge still recorded (independent of registry failure)
assert aw["spine"] == "recorded"
def test_spine_error_recorded_not_raised(store, monkeypatch):
"""Spine failure degrades to an error note; bootstrap still succeeds."""
tmp, store, scaffold = store
import rune.construction_activation as ca
from rune import spine as spine_mod
monkeypatch.setattr(spine_mod, "save",
lambda doc, path=None: (_ for _ in ()).throw(
OSError("disk full")))
rec = _adopt(store, scaffold)
assert rec["activated"] is True
aw = rec["structural_awareness"]
assert "error" in aw["spine"]
def test_dispatch_handles_unknown_mode(store):
"""UNKNOWN mode flows through the reconstruction (probe path)."""
tmp, store, scaffold = store
from rune import construction_decision as cd
from rune import construction_activation as ca
from rune import spine as spine_mod
need = cd.Need(
need_id="n-unk", objective="ambiguous objective",
origin="operator", project="widget-forge",
constraints={"environment": {"existing_language": "python"},
"contracts": ["contract:widget-forge-export"]},
evidence_refs=["t"])
trace = cd.make_decision(need, authorities={
"project_scaffold": scaffold.load(),
"rune_spine": spine_mod.load(tmp / "store" / "spine" / "current.json")},
project_toolchain={"existing_language": "python"})
rec = ca.dispatch_from_decision(trace, need=need, scaffold=scaffold,
builder="t")
assert rec.get("mode") in ("UNKNOWN", "PROJECT_BUILD")
def test_dispatch_wraps_mode_decision_dict(store):
"""dispatch_from_decision reconstructs ConstructionMode from trace."""
tmp, store, scaffold = store
from rune import construction_decision as cd
from rune import construction_activation as ca
from rune import spine as spine_mod
need = cd.Need(
need_id="n-dispatch", objective="Build widget-forge as a new "
"standalone project with its own repository: the capability",
origin="operator", project="widget-forge",
constraints={"environment": {"existing_language": "python"},
"contracts": ["contract:widget-forge-export"]},
evidence_refs=["t"])
trace = cd.make_decision(need, authorities={
"project_scaffold": scaffold.load(),
"rune_spine": spine_mod.load(tmp / "store" / "spine" / "current.json")},
project_toolchain={"existing_language": "python"})
# UNKNOWN probe path: mode dict flows through the reconstruction
assert trace["construction_mode"]["mode"] in ("PROJECT_BUILD", "UNKNOWN")
rec = ca.dispatch_from_decision(trace, need=need, scaffold=scaffold,
builder="t")
assert "activated" in rec
def test_governed_change_updates_living_scaffold_automatically(store):
tmp, store, scaffold = store
from rune.scaffold import Scaffold
from rune.construction_interface import ConstructionInterface
from rune.capability_ledger import CapabilityLedger
_adopt(store, scaffold)
ps_store = store / "projects" / "widget-forge"
model_dir = tmp / "store" / "rune_model"
model_dir.mkdir(exist_ok=True)
ledger = CapabilityLedger(model_dir)
ledger.define(capability_id="widget-import", name="Widget import",
purpose="import widgets",
component="component:widget-import",
owning_project="widget-forge",
source_locations=["src/widget_import.py"])
ledger.save()
# the project already models widget-export (capability discovery
# target) — a governed EXTENSION need discovers it and proceeds
from rune import structural_store as ss
ps = Scaffold(ps_store, "widget-forge")
pdoc = ps.load()
ss.add_entity(pdoc, id="component:widget-export", kind="component",
identities={"source": "src/widget_export.py"},
maturity="DECLARED")
ss.add_relationship(pdoc, type="provides",
from_id="component:widget-export",
to_id="contract:widget-import")
ps.commit(pdoc, what="seed existing capability")
ci = ConstructionInterface("t21.5-test", project_store=str(ps_store),
project_id="widget-forge")
r = ci.submit_need(
objective="add the widget import capability to widget-forge "
"(bounded change; import widgets from external bundles)",
origin="operator",
affected_surface=["src/widget_import.py"],
constraints={"environment": {"existing_language": "python"},
"contracts": ["contract:widget-import"]},
evidence_refs=["tests/test_t215_awareness.py"])
cid = r["ids"]["contract_id"]
for phase in ("approved", "building", "verifying"):
assert ci.transition(cid, phase, evidence_ref="t").get("success")
c = ci.close(cid, observed_evidence=[{"kind": "test", "detail": "x"}],
scope_files=["src/widget_import.py"])
closure = c["state"]["material_change_closure"]
assert closure["changed"] == ["capability:widget-import"]
assert closure["unmodeled"] == []
# fresh process sees revision WITHOUT any manual sync step
code = (
"import sys, json\nfrom pathlib import Path\n"
f"sys.path.insert(0, {str(REPO / 'src')!r})\n"
"from rune.capability_ledger import CapabilityLedger\n"
f"led = CapabilityLedger(Path({str(model_dir)!r}))\n"
'sys.stdout.write(json.dumps({"rev": led.get("widget-import")["material_revision"]}))\n'
)
p = subprocess.run([sys.executable, "-c", code],
capture_output=True, text=True)
assert json.loads(p.stdout)["rev"] == 1
+166
View File
@@ -0,0 +1,166 @@
"""test_wr_root_authority.py — T21.5 §7: ONE wait/resume root authority.
WAIT_RESUME_ROOT_AUTHORITIES = 1, SPLIT_BRAIN_EVENT_STORES = 0.
"""
from __future__ import annotations
import importlib.util
import json
import sys
from pathlib import Path
import pytest
REPO = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(REPO / "src"))
def _load_resolver():
spec = importlib.util.spec_from_file_location(
"wr_root_resolver", str(REPO / "scripts" / "wr_root_resolver.py"))
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
@pytest.fixture
def isolated(monkeypatch, tmp_path):
mod = _load_resolver()
monkeypatch.setattr(mod, "CANONICAL_WR_ROOT", tmp_path / "canon")
monkeypatch.setattr(mod, "LEGACY_WR_ROOT", tmp_path / "legacy")
monkeypatch.delenv("RUNE_WR_ROOT", raising=False)
return mod, tmp_path
def _mk_store(root: Path, n_events: int = 2):
(root / "events").mkdir(parents=True)
for i in range(n_events):
(root / "events" / f"e{i}.json").write_text(
json.dumps({"event_id": f"e{i}", "event": {}}))
# resolution order
def test_env_pin_wins(isolated):
mod, tmp = isolated
pin = tmp / "pinned"
pin.mkdir()
import os
os.environ["RUNE_WR_ROOT"] = str(pin)
try:
assert mod.resolve_root() == pin
finally:
del os.environ["RUNE_WR_ROOT"]
def test_canonical_preferred_when_live(isolated):
mod, tmp = isolated
_mk_store(mod.CANONICAL_WR_ROOT)
_mk_store(mod.LEGACY_WR_ROOT, n_events=0) # dir but no events
assert mod.resolve_root() == mod.CANONICAL_WR_ROOT
def test_split_brain_fails_loudly(isolated):
mod, _ = isolated
_mk_store(mod.CANONICAL_WR_ROOT)
_mk_store(mod.LEGACY_WR_ROOT)
with pytest.raises(RuntimeError, match="SPLIT-BRAIN"):
mod.resolve_root()
def test_split_brain_resolved_by_pin(isolated):
mod, tmp = isolated
_mk_store(mod.CANONICAL_WR_ROOT)
_mk_store(mod.LEGACY_WR_ROOT)
import os
os.environ["RUNE_WR_ROOT"] = str(mod.CANONICAL_WR_ROOT)
try:
assert mod.resolve_root() == mod.CANONICAL_WR_ROOT
finally:
del os.environ["RUNE_WR_ROOT"]
def test_first_run_creates_canonical(isolated):
mod, _ = isolated
assert mod.resolve_root() == mod.CANONICAL_WR_ROOT # no dirs at all
# event_wait delegation
def test_event_wait_wr_root_delegates(isolated, monkeypatch):
mod, _ = isolated
_mk_store(mod.CANONICAL_WR_ROOT)
from rune import event_wait as ew
# point event_wait's resolver import at the isolated module
import importlib.util
resolver_path = REPO / "scripts" / "wr_root_resolver.py"
spec = importlib.util.spec_from_file_location(
"wr_root_resolver", str(resolver_path))
rmod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(rmod)
monkeypatch.setattr(rmod, "CANONICAL_WR_ROOT", mod.CANONICAL_WR_ROOT)
monkeypatch.setattr(rmod, "LEGACY_WR_ROOT", mod.LEGACY_WR_ROOT)
monkeypatch.delenv("RUNE_WR_ROOT", raising=False)
# patch the loader location used inside event_wait: it imports by
# path, so we monkeypatch resolve at the source module instead
monkeypatch.setattr(ew, "wr_root",
lambda: rmod.resolve_root())
assert ew.wr_root() == mod.CANONICAL_WR_ROOT
# daemon launcher uses the resolver (static check of the contract)
def test_event_wait_no_spec_raises_fallback(monkeypatch, tmp_path):
"""spec_from_file_location returning None (missing file) triggers
the documented fallback policy."""
from rune import event_wait as ew
import importlib.util
monkeypatch.setattr("importlib.util.spec_from_file_location",
lambda *a, **k: None)
monkeypatch.setattr(ew, "DEFAULT_WR_ROOT", tmp_path / "legacy-home")
monkeypatch.delenv("RUNE_WR_ROOT", raising=False)
import os
env = os.environ.get("RUNE_WR_ROOT")
try:
os.environ.pop("RUNE_WR_ROOT", None)
shared = Path("/mnt/workdrive/wr-bridge")
expected = shared if shared.exists() else tmp_path / "legacy-home"
assert ew.wr_root() == expected
finally:
if env is not None:
os.environ["RUNE_WR_ROOT"] = env
def test_daemon_launcher_resolves_root_once():
src = (REPO / "scripts" / "start_wr_daemon.py").read_text()
assert "resolve_root()" in src
assert "WR_ROOT=str(root)" in src
assert "RUNE_WR_ROOT=str(root)" in src
def test_reconcile_is_idempotent(isolated, capsys):
mod, _ = isolated
_mk_store(mod.CANONICAL_WR_ROOT, n_events=1)
_mk_store(mod.LEGACY_WR_ROOT, n_events=3)
import subprocess
# run the reconcile logic in-process against isolated roots
sys.path.insert(0, str(REPO / "scripts"))
import wr_root_reconcile as rec
rec_mod = rec
# patch module-level roots
monkeypatch_targets = rec_mod
import types
# simple: re-execute main with patched resolver attrs
old_canon, old_legacy = rec_mod.resolver.CANONICAL_WR_ROOT, \
rec_mod.resolver.LEGACY_WR_ROOT
rec_mod.resolver.CANONICAL_WR_ROOT = mod.CANONICAL_WR_ROOT
rec_mod.resolver.LEGACY_WR_ROOT = mod.LEGACY_WR_ROOT
try:
assert rec_mod.main() == 0
assert rec_mod.main() == 0 # second run: nothing left to move
finally:
rec_mod.resolver.CANONICAL_WR_ROOT = old_canon
rec_mod.resolver.LEGACY_WR_ROOT = old_legacy
events = sorted(p.name for p in
(mod.CANONICAL_WR_ROOT / "events").glob("*.json"))
assert len(events) == 3 # 1 original + 2 moved