Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da61fd7d44 | ||
|
|
7342d77e6f | ||
|
|
436f760675 | ||
|
|
d62b717542 | ||
|
|
97de570b27 | ||
|
|
22aebbb102 | ||
|
|
7e33294527 | ||
|
|
fcc9989393 | ||
|
|
ead4a24866 | ||
|
|
cf51f7fd7c | ||
|
|
f182ab8601 | ||
|
|
5f9ba5784c | ||
|
|
9a05fdfa1f | ||
|
|
2298768353 | ||
|
|
a4afc77d53 | ||
|
|
d68fd1e94d | ||
|
|
470d82f307 | ||
|
|
7a988cbaff | ||
|
|
12c3c488be |
No files matched your search
@@ -185,18 +185,28 @@ jobs:
|
||||
print(f" - {d}")
|
||||
sys.exit(1)
|
||||
|
||||
# Commit each surface DIRECTLY to master (bookkeeping, not reviewable code)
|
||||
msg_prefix = f"chore(release): auto-bump version to {NEW} — earned by #{PR_NUMBER}"
|
||||
for path, (content, sha) in edited.items():
|
||||
status, r = api(f"/contents/{path}", "PUT", {
|
||||
"message": f"{msg_prefix} ({PR_TITLE[:80]})\n\nPromotion record: {promoting_url}",
|
||||
"content": base64.b64encode(content.encode()).decode(),
|
||||
"sha": sha,
|
||||
"branch": "master",
|
||||
})
|
||||
if status not in (200, 201):
|
||||
print(f" commit {path} failed ({status}): {str(r)[:200]}"); sys.exit(1)
|
||||
print(f" committed {path} to master")
|
||||
# Commit ALL surfaces as ONE commit via ChangeFiles
|
||||
# (POST /contents with a files[] array). The per-file contents PUT
|
||||
# created one commit per version surface — five same-version "bump
|
||||
# commits" per release (the phantom duplicate-bump cascade,
|
||||
# misdiagnosed as queue drain since 0.66.2; root-caused T15 Part F
|
||||
# 2026-09-11). Git-data blob/tree creation is 404 on this Gitea
|
||||
# build; ChangeFiles is the supported single-commit primitive.
|
||||
msg = f"chore(release): auto-bump version to {NEW} — earned by #{PR_NUMBER} ({PR_TITLE[:80]})\n\nPromotion record: {promoting_url}"
|
||||
ops = [{
|
||||
"operation": "update",
|
||||
"path": path,
|
||||
"content": base64.b64encode(content.encode()).decode(), # ChangeFileOperation.content is ContentBase64
|
||||
} for path, (content, _old_sha) in edited.items()]
|
||||
status, r = api("/contents", "POST", {
|
||||
"branch": "master",
|
||||
"message": msg,
|
||||
"files": ops,
|
||||
})
|
||||
if status not in (200, 201):
|
||||
print(f"change-files failed ({status}): {str(r)[:300]}"); sys.exit(1)
|
||||
commit_sha = (r.get("commit") or {}).get("sha", "?") if isinstance(r, dict) else "?"
|
||||
print(f" committed {len(ops)} surfaces as ONE commit {str(commit_sha)[:7]}")
|
||||
|
||||
print(f"AUTO_BUMPED=true — version {NEW} live on master; TUI footer will render v{NEW}")
|
||||
with open(os.environ["GITHUB_ENV"], "a") as env:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Rune
|
||||
|
||||
**Current release: v0.66.10**
|
||||
**Current release: v0.66.14**
|
||||
|
||||
**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.**
|
||||
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
# Gitea Issue Reconciliation — canonical record
|
||||
|
||||
_Generated T15 Part J/K/L (2026-09-11). Machine-readable companion:
|
||||
`docs/gitea_issue_reconciliation.json` (single authority; this doc is the
|
||||
human-readable projection). Consistency checker: `scripts/gitea_reconciliation_check.py`
|
||||
compares live Gitea state against the JSON and fails on drift._
|
||||
|
||||
Reconciliation states (closed vocabulary):
|
||||
`VALID_OPEN | FIXED_VERIFIED | SUPERSEDED | DUPLICATE | HISTORICAL_EVIDENCE |
|
||||
ACCEPTED_LIMITATION | NEEDS_REVALIDATION | DEFERRED_FUTURE`
|
||||
|
||||
## Bug-label audit (Part K)
|
||||
|
||||
`bug` = demonstrated currently incorrect behavior. Nothing else.
|
||||
|
||||
### Issues labelled `bug` at audit time
|
||||
|
||||
| # | live defect | evidence | final label | reconciliation state |
|
||||
|---|---|---|---|---|
|
||||
| 324 | **YES** | MessageAbortedError reproduces every aborted worker session (15+ checkpoints, including this session's predecessor) | bug (kept) | VALID_OPEN |
|
||||
| 204 | NO | Forge-orchestrated pipeline = feature request (P1 feature); no demonstrated incorrect behavior | bug removed | VALID_OPEN (feature) |
|
||||
| 157 | NO | capture_failure.py + tests/fixtures/production/ + manifest.json shipped and exercised (plan 2026-09-07-157) | bug removed | FIXED_VERIFIED (closed) |
|
||||
| 145 | NO | P0-07 follow-up work item (tracing coverage), not a defect report | bug removed | VALID_OPEN (work item) |
|
||||
| 139 | NO | Hand-maintained allowlist replaced by declarative classification (tui_validator.py cites #139); `tui_validator.py check` → passed, exit 0 | bug removed | FIXED_VERIFIED (closed) |
|
||||
| 126 | NO | Silent-handler sweep = refactor program (S110/BLE001 ratchet governs), not a defect | bug removed | VALID_OPEN (work item) |
|
||||
| 37 | NO | Personality corpus compilation = P2 feature | bug removed | VALID_OPEN (feature) |
|
||||
| 16 | NO | v1 course-correction EPIC | bug removed | VALID_OPEN (epic) |
|
||||
|
||||
### Issues whose `bug` label was removed in the prior cleanup (Part D, earlier session)
|
||||
|
||||
16 targeted: #301 #300 #292 #284 #314 #181 #166 #165 #163 #159 #154 #141 #40 #32 #31 #27.
|
||||
Verification (live API, this session): all confirmed bug-free EXCEPT #314 — its
|
||||
removal had failed (the API-False defect) and the label persisted on a closed
|
||||
issue. Removed this session by label-ID DELETE (204). #314 is closed
|
||||
FIXED_VERIFIED (mode:association_merged regression shipped in 2bf43ea).
|
||||
|
||||
### Totals
|
||||
|
||||
- bug-labelled before audit: 8 open (+ #314 closed-but-labelled)
|
||||
- bug-labelled after audit: **1** (#324, live defect, evidence above)
|
||||
- issues closed by this audit: #139, #157 (both FIXED_VERIFIED with evidence comments)
|
||||
- NEEDS_REVALIDATION count: 0 among bug-labelled issues (each resolved on evidence)
|
||||
|
||||
## Open-issue reconciliation (Part J)
|
||||
|
||||
Subsystems derived from labels. States per closed vocabulary. `next action` is
|
||||
the single next concrete step; `_` = none until its tranche opens.
|
||||
|
||||
| # | subsystem | state | current evidence | next action |
|
||||
|---|---|---|---|---|
|
||||
| 324 | engine/checkpoint | VALID_OPEN | live repro (15+ sessions) | fix writer abort handling |
|
||||
| 316 | ADAPT-1/research | VALID_OPEN | B3-v2 research task | await ADAPT tranche |
|
||||
| 315 | ADAPT-1/wiring | VALID_OPEN | B2-v2 standard task | await ADAPT tranche |
|
||||
| 311 | ADAPT-1/wiring | VALID_OPEN | superseded-by #315 (kept open as spec anchor until 315 delivers) | close when #315 closes |
|
||||
| 301 | FINAL-12/benchmarks | NEEDS_REVALIDATION | dependency list silently grew (operator-path telemetry, bridge overhead) without issue update | re-scope before PL1 metrics work |
|
||||
| 300 | ADAPT-11 | VALID_OPEN | acceptance = real escalation demo; blocked on ADAPT-LIVE telemetry | demo after PL1 held-out |
|
||||
| 299 | ADAPT-10 | VALID_OPEN | harness legibility audit | _ |
|
||||
| 297 | ADAPT-8 | VALID_OPEN | deterministic-first QUALIFY | _ |
|
||||
| 296 | ADAPT-7 | VALID_OPEN | DESIGN consolidation experiment | _ |
|
||||
| 295 | ADAPT-6 | VALID_OPEN | evidence capsule / context minimization | _ |
|
||||
| 294 | ADAPT-5 | VALID_OPEN | budgets + stop conditions | _ |
|
||||
| 293 | ADAPT-4 | VALID_OPEN | DESIGN-LITE shipped (STANDARD_CHANGE lane live) | acceptance demo, then close |
|
||||
| 292 | ADAPT-3 | VALID_OPEN | LOCAL_FIX lane shipped | acceptance demo, then close |
|
||||
| 284 | ADAPT epic | VALID_OPEN | epic tracks ADAPT-LIVE + PL1; body updated with detour record | update after PL1 |
|
||||
| 259 | Scaffolding | VALID_OPEN | redefined: safe repeatable construction protocol (T15 §C) | Scaffolding v1 after PL1 |
|
||||
| 258 | State-aware processes | VALID_OPEN | redefined: low-compute architectural self-awareness; SA1-8 superseded by distributed concerns | deferred research |
|
||||
| 204 | forge/pipeline | VALID_OPEN | P1 feature: forge-orchestrated coordination | _ |
|
||||
| 183–176 | memory-foundation futures | DEFERRED_FUTURE | P3 research futures (typed temporal semantics, prior-art audit, curiosity, lineage) | memory-foundation tranche |
|
||||
| 175/181 | memory epics | DEFERRED_FUTURE | evidence-grounded continuity epics | same |
|
||||
| 166 | research watchlist | DEFERRED_FUTURE | external research tracking | periodic review |
|
||||
| 165/164 | research spikes | DEFERRED_FUTURE | SimpleMem/A-MEM, HippoRAG 2 | memory-foundation tranche |
|
||||
| 163 | graph backend spike | NEEDS_REVALIDATION | P1 spike; LadybugDB evaluation partially done in prior research artifacts | revalidate against current graph.db-missing state |
|
||||
| 162 | Qdrant spike | NEEDS_REVALIDATION | P1 spike; prior research artifacts exist (issue-162/) | revalidate after #316 outcome |
|
||||
| 159 | conceptual topology | DEFERRED_FUTURE | P3 future | _ |
|
||||
| 158 | wiring-status report | VALID_OPEN | P2: generated architecture-truth report | _ |
|
||||
| 156 | mutation guards | VALID_OPEN | P1: cognitive-state mutation guards | _ |
|
||||
| 155 | e2e activation trace | VALID_OPEN | P1: credit-assignment trace | overlaps PL1 outcome work — potential duplicate of #300's telemetry; review at PL1 close |
|
||||
| 154/153 | learned-state / experience encoding | VALID_OPEN | P2 | PL1-adjacent |
|
||||
| 152/151 | break-it / adversarial | VALID_OPEN | P0: adversarial procedures after nominal green | post-PL1 tranche |
|
||||
| 145 | P0-07 follow-up | VALID_OPEN | tracing coverage gaps | _ |
|
||||
| 141 | ContextBroker compaction | VALID_OPEN | P1: rolling-compaction persistence | _ |
|
||||
| 139 | tui_validator drift | FIXED_VERIFIED | declarative classification shipped; check passes | closed |
|
||||
| 131/130/129 | reviewer program | VALID_OPEN | P0: local reviewer + calibration + rubric | post-PL1 tranche |
|
||||
| 126 | silent-handler sweep | VALID_OPEN | P0 refactor program; ratchet enforces ceiling | ongoing |
|
||||
| 125 | decompose monoliths | VALID_OPEN | P2 | _ |
|
||||
| 49–35 | v1-course-correction P2/P3 | DEFERRED_FUTURE | epic #16 manifest items (P2-01..09, P3-01..06) | per epic #16 sequencing |
|
||||
| 33–27 | v1-course-correction P1 | VALID_OPEN | epic #16 manifest P1 items | per epic #16 sequencing |
|
||||
| 16 | v1 epic | VALID_OPEN | consolidation epic; 33 manifest issues | per epic sequencing |
|
||||
| 13 | TUI message graph | VALID_OPEN | P1 | _ |
|
||||
| 3 | financial tracking | NEEDS_REVALIDATION | non-Rune issue (different project era) | transfer to correct repo or close |
|
||||
| 2 | cognitive loop wiring | NEEDS_REVALIDATION | predates current TUI architecture | revalidate against current TUI |
|
||||
| 1 | personality injection | NEEDS_REVALIDATION | overlaps #37's newer scope | merge into #37 or close |
|
||||
|
||||
## Reconciliation decisions this session (Part K deltas)
|
||||
|
||||
- CLOSED: #139, #157 (FIXED_VERIFIED, evidence comments posted)
|
||||
- BUG LABEL REMOVED: #204, #145, #126, #37, #16 (audit: not defects)
|
||||
- BUG LABEL REMOVED (stuck from prior cleanup): #314 (closed issue)
|
||||
- OPEN BUGS: 8 → 1 (#324 only — genuine live defect)
|
||||
|
||||
## Part L — consistency check
|
||||
|
||||
`scripts/gitea_reconciliation_check.py` (deterministic, no governance
|
||||
subsystem): fetches live issue state, compares to
|
||||
`docs/gitea_issue_reconciliation.json`, flags:
|
||||
|
||||
- closed issue recorded VALID_OPEN / DEFERRED_FUTURE / NEEDS_REVALIDATION (open states)
|
||||
- open issue recorded FIXED_VERIFIED / SUPERSEDED / DUPLICATE / HISTORICAL_EVIDENCE without being closed
|
||||
- `bug` label present on any issue not recorded as a live defect (or missing on #324)
|
||||
- issue missing from the artifact
|
||||
- record lacking a required field
|
||||
|
||||
Exit 0 = consistent; exit 1 = drift listed. CI-eligible (read-only).
|
||||
@@ -151,11 +151,29 @@ Then: **PL1** (T9), with the intent-continuity spine as PL1's first or second de
|
||||
|
||||
Part A-J executed. 15 issues closed (HISTORICAL_EVIDENCE/SUPERSEDED/ACCEPTED_LIMITATION) with evidence. Bug label cleaned: 16 misleading labels removed (open bugs 24→8). #259/#258 updated to current architecture. Remote tracking fixed (master tracks gitea/master, not stale origin). PL1 metrics corrected (NOT_YET_MEASURABLE where evidence doesn't support numbers). D7: bump works, release job fails on Pi — ROOT_CAUSE_UNKNOWN_DEFERRED. Automation noise: 5 duplicate bump commits per logical change (queue-drain artifact, not live recursion — version converges, commit history doesn't).
|
||||
|
||||
## DEFERRED DEFECT D7 (2026-09-11): Auto-version release job fails on Pi runner
|
||||
## DEFECT D7 (2026-09-11): RESOLVED — release-job failures root-caused
|
||||
|
||||
Observed: push-convergence version bumping works (deployed identity advances:
|
||||
0.66.0 → 0.66.6 with loop guard), but the release job's status flips to
|
||||
failure after the bump. Pi-side backup step logs "Failed to connect to
|
||||
127.0.0.1:3000". Impact: version bumping + deployment PROVEN; the release
|
||||
TAG may lag. Not blocking PL1 (PL1 consumes task.outcome, not git tags).
|
||||
Destination: INFRA follow-up on the Pi runner.
|
||||
History: the release job failed on every push since 03:16 (c2528c1).
|
||||
Superseded diagnosis: "Pi-side backup step logs Failed to connect to
|
||||
127.0.0.1:3000" was a red herring — the Pi backup step was never the
|
||||
failing job.
|
||||
|
||||
Root causes (live-proven T15 Part F, 2026-09-11 evening):
|
||||
1. c2528c1 left a corrupted shell fragment after the converged-check fi
|
||||
in auto-version.yml — the release job died at bash parse time (exit 2)
|
||||
before any bump. Fixed in c35b604.
|
||||
2. release_convergence() never assigned the release boundary — all 959+
|
||||
commits classified as unreleased, so the warranted bump never
|
||||
converged and queued runs each wrote their own bump commit. Fixed in
|
||||
e395ee1 (boundary wiring) + 9e0b855 (boundary grep must match subjects
|
||||
only, not bodies) + c35018f (numeric semver comparison: '0.66.9' <
|
||||
'0.66.10' is False lexicographically, which silently skipped warranted
|
||||
bumps as version_ahead_of_target).
|
||||
3. The per-file contents PUT committed one commit per version surface —
|
||||
five same-version bump commits per real bump (the "duplicate bump
|
||||
cascade" since 0.66.2, misdiagnosed as queue drain). Fixed in 470d82f
|
||||
+ d68fd1e (single ChangeFiles commit).
|
||||
|
||||
Live proof: experiment A (trigger d68fd1e, warranted patch) → exactly one
|
||||
bump commit a4afc77 (0.66.11), all five surfaces in one commit, queue
|
||||
drained, zero delayed duplicates, zero workflow failures.
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,4 @@
|
||||
wait/resume experiment marker 23:07:52
|
||||
wr delivery test 23:28:42
|
||||
delivery test 2 23:31:02
|
||||
wr delivery test 2 23:31:12
|
||||
@@ -0,0 +1 @@
|
||||
def broken(:
|
||||
+1
-1
@@ -20,7 +20,7 @@ henxels:
|
||||
- henxel: "Top-level docs are markdown or yaml references"
|
||||
in: ./docs/*
|
||||
allowed_filetypes: [.md, .yaml]
|
||||
except: ["./docs/v1/**", "./docs/wiki/**"]
|
||||
except: ["./docs/v1/**", "./docs/wiki/**", "./docs/gitea_issue_reconciliation.json"]
|
||||
|
||||
- henxel: "The known giant stays visible — file-size debt is tracked, not silent"
|
||||
in: ./src/rune/*
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "rune"
|
||||
version = "0.66.10"
|
||||
version = "0.66.14"
|
||||
description = "Rune — a stateful coding assistant with persistent memory, orchestration, and TUI"
|
||||
requires-python = ">=3.10"
|
||||
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gitea_reconciliation_check.py — Part L consistency check (deterministic).
|
||||
|
||||
Compares live Gitea issue state against the canonical reconciliation
|
||||
artifact (docs/gitea_issue_reconciliation.json). Flags:
|
||||
|
||||
1. closed issue recorded in an open-only state
|
||||
(VALID_OPEN / DEFERRED_FUTURE / NEEDS_REVALIDATION)
|
||||
2. open issue recorded in a terminal state
|
||||
(FIXED_VERIFIED / SUPERSEDED / DUPLICATE / HISTORICAL_EVIDENCE /
|
||||
ACCEPTED_LIMITATION)
|
||||
3. `bug` label present on any issue not recorded live_defect
|
||||
(or missing on a recorded live defect)
|
||||
4. open issue missing from the artifact
|
||||
5. record missing required fields
|
||||
|
||||
Exit 0 = consistent. Exit 1 = drift (each item printed). Read-only.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(ROOT / "scripts"))
|
||||
|
||||
ARTIFACT = ROOT / "docs" / "gitea_issue_reconciliation.json"
|
||||
|
||||
OPEN_ONLY_STATES = {"VALID_OPEN", "DEFERRED_FUTURE", "NEEDS_REVALIDATION"}
|
||||
TERMINAL_STATES = {"FIXED_VERIFIED", "SUPERSEDED", "DUPLICATE", "HISTORICAL_EVIDENCE", "ACCEPTED_LIMITATION"}
|
||||
REQUIRED_FIELDS = {"number", "tracker_state", "reconciliation_state", "labels", "live_defect", "last_revalidated"}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
import gitea_auth
|
||||
|
||||
if not ARTIFACT.is_file():
|
||||
print(f"reconciliation artifact missing: {ARTIFACT}")
|
||||
return 1
|
||||
artifact = json.loads(ARTIFACT.read_text())
|
||||
records = {r["number"]: r for r in artifact["records"]}
|
||||
|
||||
drift: list[str] = []
|
||||
|
||||
# Fetch live issues (open + recently closed via both pages)
|
||||
issues: list[dict] = []
|
||||
for page in (1, 2):
|
||||
status, batch = gitea_auth.api(f"/issues?state=all&limit=50&page={page}")
|
||||
if status != 200:
|
||||
print(f"Gitea API failed ({status}): {str(batch)[:200]}")
|
||||
return 1
|
||||
if isinstance(batch, list):
|
||||
issues.extend(batch)
|
||||
live = {i["number"]: i for i in issues}
|
||||
|
||||
for num, rec in sorted(records.items()):
|
||||
for field in REQUIRED_FIELDS:
|
||||
if field not in rec:
|
||||
drift.append(f"#{num}: record missing required field {field!r}")
|
||||
state = rec.get("reconciliation_state")
|
||||
if state in OPEN_ONLY_STATES and rec.get("tracker_state") == "closed":
|
||||
drift.append(f"#{num}: closed issue recorded {state}")
|
||||
if state in TERMINAL_STATES and rec.get("tracker_state") == "open":
|
||||
drift.append(f"#{num}: open issue recorded terminal {state}")
|
||||
has_bug = "bug" in (rec.get("labels") or [])
|
||||
# The bug-label/live-defect invariant applies to OPEN issues only:
|
||||
# a closed issue's labels are historical record; its reconciliation
|
||||
# state governs (FIXED_VERIFIED issues legitimately keep the label).
|
||||
if rec.get("tracker_state") == "open":
|
||||
if has_bug and not rec.get("live_defect"):
|
||||
drift.append(f"#{num}: bug label present but not recorded as live defect")
|
||||
if not has_bug and rec.get("live_defect"):
|
||||
drift.append(f"#{num}: recorded live defect but bug label missing")
|
||||
if state == "FIXED_VERIFIED" and rec.get("tracker_state") == "open":
|
||||
drift.append(f"#{num}: FIXED_VERIFIED but still open without closure note")
|
||||
|
||||
# Live vs artifact sync (only for issues present in the live window)
|
||||
for num, li in sorted(live.items()):
|
||||
if num not in records:
|
||||
continue
|
||||
rec = records[num]
|
||||
live_state = li["state"]
|
||||
if live_state == "closed" and rec["tracker_state"] == "open" and rec["reconciliation_state"] in OPEN_ONLY_STATES:
|
||||
drift.append(f"#{num}: live state closed but artifact says open/{rec['reconciliation_state']}")
|
||||
if live_state == "open" and rec["tracker_state"] == "closed" and rec["reconciliation_state"] in TERMINAL_STATES:
|
||||
drift.append(f"#{num}: live state open but artifact says closed/{rec['reconciliation_state']}")
|
||||
live_bug = "bug" in [l["name"] for l in li.get("labels", [])]
|
||||
if live_bug and not rec.get("live_defect"):
|
||||
drift.append(f"#{num}: live bug label present but artifact records no live defect")
|
||||
if not live_bug and rec.get("live_defect"):
|
||||
drift.append(f"#{num}: artifact records live defect but live bug label absent")
|
||||
|
||||
# Live defects in the artifact must all be OPEN in the tracker
|
||||
for num in artifact.get("live_defects", []):
|
||||
li = live.get(num)
|
||||
if li is not None and li["state"] != "open":
|
||||
drift.append(f"#{num}: live defect but tracker state is {li['state']} — close the defect or update the artifact")
|
||||
|
||||
if drift:
|
||||
print(f"RECONCILIATION DRIFT ({len(drift)}):")
|
||||
for d in drift:
|
||||
print(f" - {d}")
|
||||
return 1
|
||||
print(f"reconciliation consistent ({len(records)} records, {len(live)} live issues in window)")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -287,6 +287,7 @@ def release_convergence(
|
||||
"current_version": current_version,
|
||||
"target_version": current_version,
|
||||
"reason": "no release-worthy commits since last boundary",
|
||||
"boundary_sha": "",
|
||||
}
|
||||
target_version = next_version(current_version, warranted)
|
||||
if current_version == target_version:
|
||||
@@ -299,6 +300,7 @@ def release_convergence(
|
||||
"current_version": current_version,
|
||||
"target_version": target_version,
|
||||
"reason": "current version already equals warranted target",
|
||||
"boundary_sha": "",
|
||||
}
|
||||
elif _version_lt(current_version, target_version):
|
||||
return {
|
||||
@@ -310,6 +312,7 @@ def release_convergence(
|
||||
"current_version": current_version,
|
||||
"target_version": target_version,
|
||||
"reason": f"bump required: {current_version} → {target_version}",
|
||||
"boundary_sha": "",
|
||||
}
|
||||
else:
|
||||
return {
|
||||
@@ -321,6 +324,7 @@ def release_convergence(
|
||||
"current_version": current_version,
|
||||
"target_version": target_version,
|
||||
"reason": f"ANOMALY: current {current_version} > target {target_version}",
|
||||
"boundary_sha": "",
|
||||
}
|
||||
if _boundary_subject:
|
||||
result = subprocess.run(
|
||||
@@ -361,6 +365,9 @@ def release_convergence(
|
||||
# Exclude chore(release) commits from bump classification (they ARE releases)
|
||||
substantive = [s for s in subjects if not is_release_commit(s)]
|
||||
warranted = highest_warranted_bump(substantive)
|
||||
# Expose the boundary in the decision JSON: consumers (release step,
|
||||
# debug sessions) otherwise re-derive it from git history.
|
||||
decision_boundary = {"boundary_sha": (boundary or "")[:12]}
|
||||
|
||||
if not warranted:
|
||||
return {
|
||||
@@ -372,6 +379,7 @@ def release_convergence(
|
||||
"current_version": current_version,
|
||||
"target_version": current_version,
|
||||
"reason": "no release-worthy commits since last boundary",
|
||||
**decision_boundary,
|
||||
}
|
||||
|
||||
target_version = next_version(current_version, warranted)
|
||||
@@ -386,6 +394,7 @@ def release_convergence(
|
||||
"current_version": current_version,
|
||||
"target_version": target_version,
|
||||
"reason": "current version already equals warranted target",
|
||||
**decision_boundary,
|
||||
}
|
||||
elif _version_lt(current_version, target_version):
|
||||
# Bump needed — but only if not already converged by a prior bump
|
||||
@@ -399,6 +408,7 @@ def release_convergence(
|
||||
"current_version": current_version,
|
||||
"target_version": target_version,
|
||||
"reason": f"bump required: {current_version} → {target_version}",
|
||||
**decision_boundary,
|
||||
}
|
||||
else:
|
||||
return {
|
||||
@@ -410,6 +420,7 @@ def release_convergence(
|
||||
"current_version": current_version,
|
||||
"target_version": target_version,
|
||||
"reason": f"ANOMALY: current {current_version} > target {target_version}",
|
||||
**decision_boundary,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This is the main entry point for the Rune program.
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
__version__ = "0.66.10"
|
||||
__version__ = "0.66.14"
|
||||
|
||||
# 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
|
||||
|
||||
@@ -231,7 +231,7 @@ def main() -> int:
|
||||
)
|
||||
check(
|
||||
"Rune minor version advertises provisioning and restore verification",
|
||||
'__version__ = "0.66.10"' in rune_version,
|
||||
'__version__ = "0.66.14"' in rune_version,
|
||||
failures,
|
||||
)
|
||||
# #169 policy update: auto-bump on merge is sanctioned (Nick directive:
|
||||
|
||||
@@ -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.66.10
|
||||
Version: v0.66.14
|
||||
Features: 41 (24 original + 17 new native commands)
|
||||
|
||||
Usage:
|
||||
@@ -1856,7 +1856,7 @@ class RuneApp(App):
|
||||
# Show version info
|
||||
self.chat_channel.add_message(
|
||||
"system",
|
||||
f"[dim]Rune v0.66.10 | Textual TUI | {len(loaded)} native modules[/dim]",
|
||||
f"[dim]Rune v0.66.14 | Textual TUI | {len(loaded)} native modules[/dim]",
|
||||
)
|
||||
|
||||
# Auto-show night mode suggestion if evening/night hours
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
"""Reconciliation consistency check (handoff T15 Part L): drift rules.
|
||||
|
||||
Pure-logic tests over the checker's decision rules — no network."""
|
||||
import importlib.util
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(ROOT / "scripts"))
|
||||
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
"grc", ROOT / "scripts" / "gitea_reconciliation_check.py")
|
||||
grc = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(grc)
|
||||
|
||||
|
||||
def test_state_vocabularies_partition():
|
||||
"""Open-only and terminal states must not overlap."""
|
||||
assert not (grc.OPEN_ONLY_STATES & grc.TERMINAL_STATES)
|
||||
|
||||
|
||||
def test_required_fields_cover_decision_inputs():
|
||||
"""The fields the drift rules read must all be required."""
|
||||
assert {"tracker_state", "reconciliation_state", "labels", "live_defect"} <= grc.REQUIRED_FIELDS
|
||||
|
||||
|
||||
def test_artifact_exists_and_parses():
|
||||
data = __import__("json").loads(grc.ARTIFACT.read_text())
|
||||
assert data["records"], "artifact has no records"
|
||||
nums = [r["number"] for r in data["records"]]
|
||||
assert len(nums) == len(set(nums)), "duplicate issue records"
|
||||
for r in data["records"]:
|
||||
assert r["reconciliation_state"] in (grc.OPEN_ONLY_STATES | grc.TERMINAL_STATES), r["number"]
|
||||
|
||||
|
||||
def test_single_live_defect():
|
||||
"""The bug audit converged on exactly one live defect: #324."""
|
||||
data = __import__("json").loads(grc.ARTIFACT.read_text())
|
||||
assert data["live_defects"] == [324]
|
||||
@@ -388,3 +388,12 @@ class TestNumericVersionCompare(unittest.TestCase):
|
||||
self.assertFalse(result["converged"])
|
||||
self.assertIsNone(result["anomaly"])
|
||||
self.assertEqual(result["warranted_bump"], "patch")
|
||||
|
||||
def test_decision_exposes_boundary_sha(self):
|
||||
result = vc.release_convergence(
|
||||
_version="0.66.9",
|
||||
_boundary_subject="chore(release): 0.66.9",
|
||||
_subjects=["fix: x"],
|
||||
)
|
||||
# Test-injection path: no git, boundary unknown → empty but present.
|
||||
assert result["boundary_sha"] == ""
|
||||
@@ -0,0 +1 @@
|
||||
def broken(:
|
||||
Reference in new issue
Block a user