diff --git a/wizard-work/ledger/extract_workflow.py b/wizard-work/ledger/extract_workflow.py new file mode 100644 index 0000000..fae2655 --- /dev/null +++ b/wizard-work/ledger/extract_workflow.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 +"""Extract the work-queue index from WORK-QUEUE.md into a tab-separated table. + +Real extraction from the troupe repo's single source of truth +(/root/troupe-standup/WORK-QUEUE.md). One row per UNIQUE '## Q' heading (45 +blocks); the duplicate heading lines are counted via 'dup_count'. Writes +workflow.tsv and a rendered workflow.md. Prints the row count and SHA-256 so the +artifact can be re-verified later. +""" +import hashlib +import re + +SRC = "/root/troupe-standup/WORK-QUEUE.md" +OUT = "/root/.hermes/wizard-outputs/ledger/workflow.tsv" +MD_OUT = "/root/.hermes/wizard-outputs/ledger/workflow.md" + +q_re = re.compile(r"^## (Q\d+)") # heading id prefix +pipe_re = re.compile(r"^### Pipe verification — Run (\d+) \(([^)]*)\), (\d{4}-\d{2}-\d{2})") +closed_re = re.compile(r"closed at `([0-9a-f]{7,40})`") +done_re = re.compile(r"^\*\*Done mean?s?:\*\*", re.M) +sha_re = re.compile(r"`([0-9a-f]{7,40})`") + +with open(SRC, encoding="utf-8") as fh: + lines = fh.read().split("\n") + +blocks = [] +current = None +pipe_count = 0 + +for i, line in enumerate(lines, start=1): + head = q_re.match(line.strip()) + if head: + # Close the previous block, start a new one. + if current is not None: + current["pipe_runs"] = pipe_count + blocks.append(current) + qid = head.group(1) + rest = line[head.end():].strip() + # The heading is "## Q — Title *(meta)" or a §-variant. The meta is the + # trailing "*(...)" wrapper: find the LAST "*(" (the title may contain its + # own parens). The wrapper's "*(" also lives next to a literal "*" in the + # content, so a plain "([^*]*)" capture is not safe. + kw = rest.rfind("*(") + if kw != -1: + title = rest[:kw].strip() + title = title.lstrip(" \t\u2014\u2013\u2022\u003a\u2013") + open_paren = kw + 1 # position of "(" + depth = 1 + j = open_paren + 1 + while j < len(rest) and depth > 0: + if rest[j] == "(": + depth += 1 + elif rest[j] == ")": + depth -= 1 + j += 1 + meta = rest[open_paren + 1:j - 1] + else: + title = rest.lstrip(" \t\u2014\u2013\u2022\u003a\u2013") + meta = "" + current = { + "q_id": qid, + "heading_line": i, + "title": title, + "meta": meta, + "has_done": False, + "has_closure": False, + "pipe_runs": 0, + "closure_sha": None, + "sha_refs": 0, + } + pipe_count = 0 + continue + + if current is None: + continue # not a heading, and not inside a block -> ignore + + # Per-line evidence detection for the block currently open. + if pipe_re.match(line.strip()): + pipe_count += 1 + if done_re.search(line): + current["has_done"] = True + if "closed at `" in line: + current["has_closure"] = True + cm = closed_re.search(line) + if cm and current["closure_sha"] is None: + current["closure_sha"] = cm.group(1) + current["sha_refs"] += len(sha_re.findall(line)) + +if current is not None: + current["pipe_runs"] = pipe_count + blocks.append(current) + +# Deduplicate by q_id; first occurrence is canonical. 'dup_count' = how many +# extra heading lines carried the same q_id (all of them have 1). +from collections import Counter + +counts = Counter(b["q_id"] for b in blocks) +seen = set() +rows = [] +for b in blocks: + if b["q_id"] in seen: + continue + seen.add(b["q_id"]) + b["dup_count"] = counts[b["q_id"]] - 1 + rows.append(b) + +COLS = ["q_id", "heading_line", "title", "meta", "dup_count", + "has_done", "has_closure", "pipe_runs", "closure_sha", "sha_refs"] + +def tsv_row(b): + return "\t".join([ + b["q_id"], str(b["heading_line"]), b["title"], b["meta"], str(b["dup_count"]), + str(int(b["has_done"])), str(int(b["has_closure"])), str(b["pipe_runs"]), + b["closure_sha"] or "none", str(b["sha_refs"]), + ]) + +out = "\n".join(["\t".join(COLS)] + [tsv_row(b) for b in rows]) + "\n" +with open(OUT, "w", encoding="utf-8") as fh: + fh.write(out) + +digest = hashlib.sha256(out.encode("utf-8")).hexdigest() +with open(MD_OUT, "w", encoding="utf-8") as fh: + fh.write("# Work-queue index (extracted from WORK-QUEUE.md)\n\n") + fh.write("Extracted rows: " + str(len(rows)) + " unique blocks " + "(" + str(len(rows) + sum(b["dup_count"] for b in rows)) + " heading lines; " + + str(sum(b["dup_count"] for b in rows)) + " duplicate heading lines).\n\n") + fh.write("| q_id | heading_line | title | meta | dup_count | has_done | has_closure | pipe_runs | closure_sha | sha_refs |\n") + fh.write("|---|---|---|---|---|---|---|---|---|---|\n") + for b in rows: + fh.write("| {qid} | {line} | {title} | {meta} | {d} | {h} | {c} | {pr} | {sha} | {s} |\n".format( + qid=b["q_id"], line=b["heading_line"], title=b["title"], meta=b["meta"], + d=b["dup_count"], h="1" if b["has_done"] else "0", + c="1" if b["has_closure"] else "0", pr=b["pipe_runs"], + sha=b["closure_sha"] or "none", s=b["sha_refs"])) + fh.write("\n") + +print("rows={n} sha256={digest}".format(n=len(rows), digest=digest)) diff --git a/wizard-work/ledger/verify_workflow.py b/wizard-work/ledger/verify_workflow.py new file mode 100644 index 0000000..8f9a0ce --- /dev/null +++ b/wizard-work/ledger/verify_workflow.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +"""Verify the work-queue extraction (the CHECK for the ledger artifact). + +Re-parses /root/troupe-standup/WORK-QUEUE.md and /root/.hermes/wizard-outputs/ +ledger/workflow.tsv and asserts they agree. Exit code 0 = correct, nonzero = wrong. +""" +import hashlib +import re +import sys + +SRC = "/root/troupe-standup/WORK-QUEUE.md" +TSV = "/root/.hermes/wizard-outputs/ledger/workflow.tsv" + +with open(SRC, encoding="utf-8") as fh: + src = fh.read() +with open(TSV, encoding="utf-8") as fh: + tsv = fh.read() + +q_re = re.compile(r"^## (Q\d+)") +expected_ids = [] +for line in src.split("\n"): + m = q_re.match(line.strip()) + if m: + expected_ids.append(m.group(1)) + +expected_unique = [] +seen = set() +for qid in expected_ids: + if qid not in seen: + seen.add(qid) + expected_unique.append(qid) + +rows = [l for l in tsv.split("\n") if l.strip()] +header, data = rows[0], rows[1:] + +EXPECTED_HEADER = ["q_id", "heading_line", "title", "meta", "dup_count", + "has_done", "has_closure", "pipe_runs", "closure_sha", "sha_refs"] +EXPECTED_ROWS = len(expected_unique) # 45 + +errors = [] +if header != "\t".join(EXPECTED_HEADER): + errors.append("header mismatch: got %r want %r" % (header.split("\t"), EXPECTED_HEADER)) +if len(data) != EXPECTED_ROWS: + errors.append("row count mismatch: tsv=%d expected=%d" % (len(data), EXPECTED_ROWS)) +actual_ids = [r.split("\t")[0] for r in data] +if actual_ids != expected_unique: + errors.append("q_id order mismatch:\ntsv=%r\nexpected=%r" % (actual_ids, expected_unique)) + +digest = hashlib.sha256(tsv.encode("utf-8")).hexdigest() +if errors: + print("FAIL") + for e in errors: + print(" -", e) + sys.exit(1) + +print("OK rows=%d headings=%d unique=%d sha256=%s" % ( + len(data), len(expected_ids), len(expected_unique), digest)) diff --git a/wizard-work/ledger/workflow.md b/wizard-work/ledger/workflow.md new file mode 100644 index 0000000..2070ea4 --- /dev/null +++ b/wizard-work/ledger/workflow.md @@ -0,0 +1,52 @@ +# Work-queue index (extracted from WORK-QUEUE.md) + +Extracted rows: 45 unique blocks (49 heading lines; 4 duplicate heading lines). + +| q_id | heading_line | title | meta | dup_count | has_done | has_closure | pipe_runs | closure_sha | sha_refs | +|---|---|---|---|---|---|---|---|---|---| +| Q1 | 9 | Recover the fifteen silent rooms | highest value; no dependencies | 0 | 1 | 0 | 0 | none | 3 | +| Q2 | 55 | Chase the one dead link | | 0 | 1 | 0 | 0 | none | 6 | +| Q3 | 73 | Independent audit of the published ledger | | 0 | 1 | 0 | 0 | none | 3 | +| Q4 | 106 | Falsification pass | | 0 | 1 | 0 | 0 | none | 1 | +| Q8 | 133 | Summary fidelity: does each entry's machine-written summary overstate its article? | | 0 | 1 | 1 | 0 | c580e07 | 83 | +| Q5 | 454 | Issue triage in the organisation's public repos | | 0 | 1 | 0 | 0 | none | 2 | +| Q6 | 489 | A public guide to checking this archive | | 0 | 1 | 0 | 0 | none | 12 | +| Q7 | 582 | Propose the next items | | 0 | 0 | 0 | 0 | none | 13 | +| Q9 | 656 | Independent QC of the sovereign music candidates | second opinion, no listening required | 0 | 1 | 0 | 0 | none | 3 | +| Q10 | 693 | Summary fidelity for the non-magazine entries | | 0 | 1 | 0 | 0 | none | 41 | +| Q11 | 949 | Close the eight pass-2 date mismatches | | 0 | 1 | 0 | 0 | none | 2 | +| Q12 | 985 | Are the evidence tiers earned? | | 0 | 1 | 0 | 0 | none | 8 | +| Q13 | 1111 | Publish the archive-side wording fixes, one at a time | | 0 | 1 | 0 | 0 | none | 1 | +| Q14 | 1146 | Put the Inbox on `alexanderwhitestone.com` behind a gate | | 0 | 1 | 0 | 0 | none | 5 | +| Q15 | 1265 | Stop the monitor from waking on the pipe's own writes | pipe; no dependencies | 0 | 1 | 0 | 0 | none | 24 | +| Q16 | 1396 | Per-platform ATS gate census | career lane; opened 2026-10-01 by reply 21 §3, drafted by zephyrus | 0 | 1 | 0 | 0 | none | 25 | +| Q17 | 1553 | Employer-side AI-authorship refusal gates | career lane; opened 2026-10-03 by reply 26, per Q7 | 0 | 1 | 0 | 0 | none | 15 | +| Q18 | 1591 | Employer-side lead intake must be deduped against the prepared index | career lane; opened 2026-10-04 by reply 28, per the G2i miss | 0 | 1 | 1 | 0 | 073728c | 84 | +| Q19 | 1768 | Three proposed members (local Mac wizard, Grok, Mistral) | wizard onboarding; opened 2026-10-06 by zephyrus-sparkborn, `checkins/2026-10-06-zephyrus-wizards*.md` + `JOIN-THE-TROUPE.md` | 0 | 0 | 1 | 0 | f399a01 | 148 | +| Q20 | 2112 | Excerpt fidelity and placement for the 91 magazine entries | archive verification; opened 2026-10-08 by the pipe, per Q7's never-dry obligation | 0 | 1 | 0 | 0 | none | 10 | +| Q21 | 2233 | Does every quoted excerpt resolve at the URL it is printed against? | | 0 | 1 | 1 | 0 | 67b892d | 4 | +| Q22 | 2367 | Do the named-but-unlinked sources resolve? | proposed 2026-10-10 by Merlin, per Q7; not yet opened | 0 | 1 | 1 | 0 | 1cb13e7 | 10 | +| Q23 | 2477 | The prose-named sources the `@` token rule cannot see | archive verification; opened 2026-10-10 by the pipe, per Q7's never-dry obligation | 0 | 1 | 1 | 0 | 5c7a75f | 10 | +| Q24 | 2621 | The Q13 fix pack: stage every verified live-page fix as one reviewable approval | archive-side; opened 2026-10-10 by the pipe, per Q7's never-dry obligation | 0 | 1 | 1 | 0 | 8b56abe | 2 | +| Q25 | 2766 | A prose-anchor convention for the ledger build | archive-side; proposed 2026-10-10 by Merlin, per Q7's never-dry obligation — Q24 closed and the unclaimed executable set fell to zero | 0 | 1 | 0 | 0 | none | 18 | +| Q26 | 3058 | Make the silent token classes loud, and measure the fifth slot | archive-side; opened 2026-10-10 by the pipe, per Q7's never-dry obligation — Q25 closed at `a9f23a6` and the unclaimed executable set fell to zero | 0 | 1 | 0 | 0 | none | 5 | +| Q27 | 3255 | A fixture at every render site: make the token sentinel's coverage total | archive-side; opened 2026-10-11 by the pipe, per Q7's never-dry obligation — Q26 closed at `f05179e` | 0 | 0 | 0 | 0 | none | 6 | +| Q28 | 3388 | The channels the entry walk cannot see: state the guarantee, then cover what it cannot reach | | 0 | 1 | 1 | 0 | 08013ba | 14 | +| Q29 | 3573 | The guards that are not the walk: what protects a page when the value is not a token | | 0 | 1 | 0 | 0 | none | 15 | +| Q30 | 3747 | The renderer's entry point: the one path no gate executes | | 0 | 1 | 0 | 0 | none | 6 | +| Q31 | 3962 | The collector's entry point: the sibling path no gate has executed either | | 0 | 1 | 0 | 0 | none | 26 | +| Q32 | 4368 | The G8 two-run agreement check is nondeterministic: `age_seconds` is not scrubbed | Helm lane; opened 2026-10-11 by the pipe, per Q7's never-dry obligation — Q31's adoption is blocked behind it | 0 | 1 | 0 | 0 | none | 4 | +| Q33 | 4438 | Split the newest-file pointer out of `evidence.source_path` | collector-side; opened 2026-10-11 by the pipe, per Q7's never-dry obligation — Q32 closed at `7e12c82` | 0 | 1 | 0 | 0 | none | 4 | +| Q34 | 4552 | Gate the pointer's null case | collector-side gate; opened 2026-10-11 by the pipe, per Q7's never-dry obligation — Q33 closed at `8bb88da` | 0 | 1 | 0 | 0 | none | 11 | +| Q35 | 4728 | The gate must refuse a subject collector that carries no destination rule | Helm lane; opened 2026-10-12 by the pipe, per Q7's never-dry obligation — Q34 verified and closed at `ac98bfb` | 0 | 1 | 0 | 0 | none | 7 | +| Q36 | 4896 | The monitor cannot see a counterparty write that carries this pipe's identity | pipe tooling; opened 2026-10-12 by the pipe, per Q7's never-dry obligation — Q20 verified and closed at `9a1de9a` and the pre-existing executable set fell to empty | 0 | 1 | 0 | 0 | none | 24 | +| Q37 | 4985 | The identity rule closes one class, not the class | pipe tooling; opened 2026-10-12 by the pipe, per Q7's never-dry obligation — Q36 closed at `9584984` and the unclaimed executable set fell to empty | 1 | 1 | 0 | 0 | none | 12 | +| Q38 | 5018 | The behavioural destination guard | Helm lane; opened 2026-10-12 by Merlin — Q36's candidate 2, on Alexander's direction to continue the Helm lane | 1 | 1 | 0 | 0 | none | 5 | +| Q39 | 5151 | The Q38 guard's admission admits a writer, and its legs run the unrewritten subject | Helm lane; opened 2026-10-12 by the pipe, per Q7's never-dry obligation — Q38 is closed and verified and its protective claim is falsified, so the executable set is not empty | 1 | 1 | 0 | 0 | none | 4 | +| Q40 | 5217 | the deployed gate admits all five negatives, and has no admission path at all | Helm lane; opened 2026-10-12 by Merlin, discharging the Q39 closure's C26 | 0 | 1 | 0 | 0 | none | 1 | +| Q43 | 6103 | The legs run the rewritten subject: prevention, not detect-and-restore | Helm lane; opened 2026-10-12 by Merlin, per the Q39 closure's own recommendation — "open the stronger form (the legs against a rewritten copy) as the Helm lane's next item" | 1 | 1 | 0 | 0 | none | 2 | +| Q44 | 6162 | The pin class: redirect every constant composed from the live data root, not only `SNAPSHOT_PATH` | Helm lane; opened 2026-10-12 by the pipe, per Q43's own closing sentence — "extend the redirect beyond `SNAPSHOT_PATH` to every constant composed from the live data root, which is what would close the `--pin` class structurally instead of by argv rule" | 0 | 1 | 0 | 0 | none | 73 | +| Q48 | 6706 | The runtime-resolution redirect: close the class Q45's literals cannot see | | 0 | 0 | 0 | 0 | none | 15 | +| Q49 | 6826 | The runtime-resolved root's live write path: name it, reproduce it, then close it | | 0 | 1 | 0 | 0 | none | 132 | +| Q45 | 7594 | §9 follow-on — the home-directory channel | Helm lane; Merlin | 0 | 1 | 1 | 2 | c559d33 | 720 | + diff --git a/wizard-work/ledger/workflow.tsv b/wizard-work/ledger/workflow.tsv new file mode 100644 index 0000000..69c0737 --- /dev/null +++ b/wizard-work/ledger/workflow.tsv @@ -0,0 +1,46 @@ +q_id heading_line title meta dup_count has_done has_closure pipe_runs closure_sha sha_refs +Q1 9 Recover the fifteen silent rooms highest value; no dependencies 0 1 0 0 none 3 +Q2 55 Chase the one dead link 0 1 0 0 none 6 +Q3 73 Independent audit of the published ledger 0 1 0 0 none 3 +Q4 106 Falsification pass 0 1 0 0 none 1 +Q8 133 Summary fidelity: does each entry's machine-written summary overstate its article? 0 1 1 0 c580e07 83 +Q5 454 Issue triage in the organisation's public repos 0 1 0 0 none 2 +Q6 489 A public guide to checking this archive 0 1 0 0 none 12 +Q7 582 Propose the next items 0 0 0 0 none 13 +Q9 656 Independent QC of the sovereign music candidates second opinion, no listening required 0 1 0 0 none 3 +Q10 693 Summary fidelity for the non-magazine entries 0 1 0 0 none 41 +Q11 949 Close the eight pass-2 date mismatches 0 1 0 0 none 2 +Q12 985 Are the evidence tiers earned? 0 1 0 0 none 8 +Q13 1111 Publish the archive-side wording fixes, one at a time 0 1 0 0 none 1 +Q14 1146 Put the Inbox on `alexanderwhitestone.com` behind a gate 0 1 0 0 none 5 +Q15 1265 Stop the monitor from waking on the pipe's own writes pipe; no dependencies 0 1 0 0 none 24 +Q16 1396 Per-platform ATS gate census career lane; opened 2026-10-01 by reply 21 §3, drafted by zephyrus 0 1 0 0 none 25 +Q17 1553 Employer-side AI-authorship refusal gates career lane; opened 2026-10-03 by reply 26, per Q7 0 1 0 0 none 15 +Q18 1591 Employer-side lead intake must be deduped against the prepared index career lane; opened 2026-10-04 by reply 28, per the G2i miss 0 1 1 0 073728c 84 +Q19 1768 Three proposed members (local Mac wizard, Grok, Mistral) wizard onboarding; opened 2026-10-06 by zephyrus-sparkborn, `checkins/2026-10-06-zephyrus-wizards*.md` + `JOIN-THE-TROUPE.md` 0 0 1 0 f399a01 148 +Q20 2112 Excerpt fidelity and placement for the 91 magazine entries archive verification; opened 2026-10-08 by the pipe, per Q7's never-dry obligation 0 1 0 0 none 10 +Q21 2233 Does every quoted excerpt resolve at the URL it is printed against? 0 1 1 0 67b892d 4 +Q22 2367 Do the named-but-unlinked sources resolve? proposed 2026-10-10 by Merlin, per Q7; not yet opened 0 1 1 0 1cb13e7 10 +Q23 2477 The prose-named sources the `@` token rule cannot see archive verification; opened 2026-10-10 by the pipe, per Q7's never-dry obligation 0 1 1 0 5c7a75f 10 +Q24 2621 The Q13 fix pack: stage every verified live-page fix as one reviewable approval archive-side; opened 2026-10-10 by the pipe, per Q7's never-dry obligation 0 1 1 0 8b56abe 2 +Q25 2766 A prose-anchor convention for the ledger build archive-side; proposed 2026-10-10 by Merlin, per Q7's never-dry obligation — Q24 closed and the unclaimed executable set fell to zero 0 1 0 0 none 18 +Q26 3058 Make the silent token classes loud, and measure the fifth slot archive-side; opened 2026-10-10 by the pipe, per Q7's never-dry obligation — Q25 closed at `a9f23a6` and the unclaimed executable set fell to zero 0 1 0 0 none 5 +Q27 3255 A fixture at every render site: make the token sentinel's coverage total archive-side; opened 2026-10-11 by the pipe, per Q7's never-dry obligation — Q26 closed at `f05179e` 0 0 0 0 none 6 +Q28 3388 The channels the entry walk cannot see: state the guarantee, then cover what it cannot reach 0 1 1 0 08013ba 14 +Q29 3573 The guards that are not the walk: what protects a page when the value is not a token 0 1 0 0 none 15 +Q30 3747 The renderer's entry point: the one path no gate executes 0 1 0 0 none 6 +Q31 3962 The collector's entry point: the sibling path no gate has executed either 0 1 0 0 none 26 +Q32 4368 The G8 two-run agreement check is nondeterministic: `age_seconds` is not scrubbed Helm lane; opened 2026-10-11 by the pipe, per Q7's never-dry obligation — Q31's adoption is blocked behind it 0 1 0 0 none 4 +Q33 4438 Split the newest-file pointer out of `evidence.source_path` collector-side; opened 2026-10-11 by the pipe, per Q7's never-dry obligation — Q32 closed at `7e12c82` 0 1 0 0 none 4 +Q34 4552 Gate the pointer's null case collector-side gate; opened 2026-10-11 by the pipe, per Q7's never-dry obligation — Q33 closed at `8bb88da` 0 1 0 0 none 11 +Q35 4728 The gate must refuse a subject collector that carries no destination rule Helm lane; opened 2026-10-12 by the pipe, per Q7's never-dry obligation — Q34 verified and closed at `ac98bfb` 0 1 0 0 none 7 +Q36 4896 The monitor cannot see a counterparty write that carries this pipe's identity pipe tooling; opened 2026-10-12 by the pipe, per Q7's never-dry obligation — Q20 verified and closed at `9a1de9a` and the pre-existing executable set fell to empty 0 1 0 0 none 24 +Q37 4985 The identity rule closes one class, not the class pipe tooling; opened 2026-10-12 by the pipe, per Q7's never-dry obligation — Q36 closed at `9584984` and the unclaimed executable set fell to empty 1 1 0 0 none 12 +Q38 5018 The behavioural destination guard Helm lane; opened 2026-10-12 by Merlin — Q36's candidate 2, on Alexander's direction to continue the Helm lane 1 1 0 0 none 5 +Q39 5151 The Q38 guard's admission admits a writer, and its legs run the unrewritten subject Helm lane; opened 2026-10-12 by the pipe, per Q7's never-dry obligation — Q38 is closed and verified and its protective claim is falsified, so the executable set is not empty 1 1 0 0 none 4 +Q40 5217 the deployed gate admits all five negatives, and has no admission path at all Helm lane; opened 2026-10-12 by Merlin, discharging the Q39 closure's C26 0 1 0 0 none 1 +Q43 6103 The legs run the rewritten subject: prevention, not detect-and-restore Helm lane; opened 2026-10-12 by Merlin, per the Q39 closure's own recommendation — "open the stronger form (the legs against a rewritten copy) as the Helm lane's next item" 1 1 0 0 none 2 +Q44 6162 The pin class: redirect every constant composed from the live data root, not only `SNAPSHOT_PATH` Helm lane; opened 2026-10-12 by the pipe, per Q43's own closing sentence — "extend the redirect beyond `SNAPSHOT_PATH` to every constant composed from the live data root, which is what would close the `--pin` class structurally instead of by argv rule" 0 1 0 0 none 73 +Q48 6706 The runtime-resolution redirect: close the class Q45's literals cannot see 0 0 0 0 none 15 +Q49 6826 The runtime-resolved root's live write path: name it, reproduce it, then close it 0 1 0 0 none 132 +Q45 7594 §9 follow-on — the home-directory channel Helm lane; Merlin 0 1 1 2 c559d33 720