aboutsummaryrefslogtreecommitdiff
path: root/todo.org
diff options
context:
space:
mode:
Diffstat (limited to 'todo.org')
-rw-r--r--todo.org299
1 files changed, 283 insertions, 16 deletions
diff --git a/todo.org b/todo.org
index d50cb29..6750eed 100644
--- a/todo.org
+++ b/todo.org
@@ -39,6 +39,217 @@ Tags are assigned and refreshed by =task-audit=; =task-review= keeps them honest
* Rulesets Open Work
+** TODO [#B] Voice pattern #48 — corrective antithesis :feature:
+:PROPERTIES:
+:LAST_REVIEWED: 2026-07-31
+:END:
+Work proposed a new voice pattern for the corrective form of antithesis: "X
+rather than Y", "not X but Y", "X, not Y". Distinct from #9, which catches the
+additive form ("not only X but Y"). My ruling, quoted in the handoff: I don't
+mind it occasionally, but it has to be for emphasis and it can't be overused.
+So it's a frequency rule, not a ban. Proposed cap: about one per 150 words,
+never twice in adjacent sentences, mode tags prose + personal.
+
+Evidence is strong. A four-sentence Slack draft fired it four times in a
+hundred words, and I noticed it across messages, which is the tell that it had
+stopped being a device.
+
+Two additions I'd make before applying:
+
+1. Put it straight into the attestation high-recurrence set. That set is for
+ patterns with documented failure history, and this one arrived with its
+ failure history attached.
+
+2. The frequency rule needs a home outside the voice skill. I noticed this in
+ conversation, and the skill only runs on publish artifacts, so as proposed
+ it can't touch the surface where the complaint originated. Mirroring the cap
+ into =interaction.md= (always loaded) is what would make it bite in chat.
+ That second half is the part worth deciding on.
+
+Per the paired-files rule, any change lands in both =voice/SKILL.md= and
+=voice/references/voice-profile.org=. The handoff already names what the profile
+entry wants: problem statement, the verbatim ruling, the four-in-one-draft
+before/after, and a note that #9 is adjacent-but-different so the two don't get
+merged.
+
+Source: inbox/2026-07-31-1312-from-work-new-voice-pattern-proposal-48.org
+
+*** 2026-07-31 Fri @ 13:32:00 -0500 Craig approved the interaction.md mirror
+So the open half of this task is settled and the work is now fully specified.
+Both homes get the rule, with a cross-reference line in each so an edit to one
+prompts a look at the other. Work argued the duplication is right here because
+the two fire on different surfaces and neither subsumes the other, and I agree:
+a single home leaves one surface uncovered.
+
+Placement: a short subsection under =interaction.md='s existing chat-output
+rules, beside "No Reverse-Video Highlighting in Chat Output". Those rules
+constrain how the agent writes to Craig rather than what it decides, which is
+the same shape.
+
+Work sent suggested text for both files. It needs the em-dashes stripped before
+it lands, since it uses them freely while describing a rule that ships into a
+zero-tolerance context.
+
+Remaining work: the =voice/SKILL.md= rule line, the paired
+=voice/references/voice-profile.org= entry, the =interaction.md= subsection, the
+two cross-reference lines, and adding #48 to the attestation high-recurrence set.
+
+Source: inbox/2026-07-31-1328-from-work-craig-approved-mirroring-the-48.org
+
+** DOING [#B] Hostile subagent review before every agent commit :feature:
+:PROPERTIES:
+:LAST_REVIEWED: 2026-07-28
+:END:
+From the roam inbox, 2026-07-28: "code reviews must occur before every commit an agent does, and they should be hostile reviews from a subagent without the agent's context."
+
+Two asks, and only the second is new. The publish flow already mandates a review before every commit (Step 1). What changes is *who reviews*: today the reviewing agent is the one that wrote the change, so it inherits the author's mental model, and =review-code= only *suggests* subagent dispatch, and only "for substantive reviews on large diffs".
+
+Decisions settled with Craig, 2026-07-28, and shipped:
+- *Scope* — every commit. The reviewer's own Phase 0 rules a diff trivial and returns Skipped, which satisfies the gate; the author never rules on their own diff.
+- *Stance* — "adversarial", not "hostile" (Craig's call). An agent told to attack manufactures findings, so the stance carries a substantiation floor: a finding not substantiated against the diff is dropped.
+- *What the reviewer gets* — the diff, a one-line claim of what it does, and the requirement source (ticket, plan, task body) where one exists. Withheld: the conversation, the exploration, the author's rationale. The requirement source stays *in* because it is the only artifact that can contradict the author's claim; withholding it makes the claim self-certifying.
+- *Loop* — re-review until the reviewer approves, turning on blocking findings rather than the verdict token. Bounded at three rounds, and stopped early on a finding that recurs after being reported fixed. Both bounds hand the decision to Craig; the unattended callers park instead.
+- *Adjudication* — Craig, never the author overruling the reviewer.
+- *Home* — the =publish= skill Step 1, with =review-code= carrying the adversarial contract and re-review mode.
+- *The =subagents.md= tension* — resolved with an Isolation Override section: the size heuristics assume the main thread could do the task equally well, and they lapse when its own context is what makes its answer untrustworthy.
+
+Remaining: nothing on the design. The change shipped in this session.
+
+** TODO [#B] wrap-org-table splits logical rows, and lint drives it :bug:
+:PROPERTIES:
+:LAST_REVIEWED: 2026-07-29
+:END:
+Reported by work 2026-07-28 against =arch-00-deepsat-platform-spec-draft.org=. Reproduced here.
+
+*** Verified
+
+Each of these is a measurement, re-run under adversarial review. The analysis I built on top of them was wrong three times, so this section is deliberately separated from the open questions below.
+
+- *The defect.* =wrap-org-table.el= turns one logical table row into two or more, by writing a rule between its continuation lines. Content survives; structure does not.
+- *Root cause.* =wot--continuation-group-p= (=wrap-org-table.el:168=) requires every line past the first to carry at least one empty cell. When a row overflows in every column its continuation line is fully populated, the predicate rejects the group, and =wot--logical-rows= appends each physical line as its own row (=:197=).
+- *Controlled A/B.* Rules present in both, continuation line's middle cell the only variable: blank merges, populated splits.
+- *Idempotence is broken.* Running the tool twice on its own correct output corrupts it. Pass 1 emits a properly rule-delimited three-line row; pass 2 splits it into three rows. The docstring at =:203-204= asserts the opposite, and =wot-reformat-is-idempotent= passes because its fixture overflows only one column.
+- *lint doesn't just miss it, it causes it.* =lint-org.el:424= calls the same predicate. Given the tool's own correct output — a rule after every logical row — lint reports "missing rule between rows — wrap-org-table.el reflows it". Nothing is missing. Follow that advice and the tool splits the row; lint then reports the result 0 mechanical, 0 judgment. Control: a conformant table whose continuation keeps an empty cell returns 0 and 0. So the loop is not two independent green lights, it is the linter manufacturing a false violation and certifying the damage it caused.
+- *A second path.* With no hlines at all, =wot--logical-rows= short-circuits (=:184=) before the predicate is reached, so every physical line becomes a row.
+- *Nothing invokes the tool unattended.* =todo-cleanup.el= names it in comments only; the entry-script guard from the 2026-07-09 incident holds.
+- *rulesets is exposed*: =todo.org= carries a four-row attachment-sanitization table with no rules between rows. Don't reflow it until the tool is fixed — reflowing is the trigger.
+
+*** Two fixes that were verified to work
+
+- *Conformant round-trip.* Replacing the predicate body with =(> (length group) 1)= — pure rule-delimited grouping, no emitter marker, no new field — makes the corrupting pass-1 output round-trip cleanly.
+- *Telling a continuation group from two real rows at runtime.* Provenance isn't available (=wot-reformat-table-string= receives a string), but the test is cheap: merge the group, re-wrap at the allocated widths, compare to the group as given. A continuation group reproduces itself under merge-then-wrap; two genuinely distinct short rows don't, because merged they fit on one line.
+
+Start with a red test from the double-run repro — it needs no unusual input and falsifies the docstring and the passing idempotence test together. Emission is already correct (=:222-224= emits one rule per element of =rows=), so the repair is entirely in how =rows= is computed.
+
+*Prefer the round-trip check to any detector.* work implemented the predicate-based detection I recommended and demonstrated it cannot discriminate at any threshold (2026-07-29, worked examples from their repo). Run bare it flags 144 files — essentially every table — because an ordinary header-plus-body table is one multi-line fully-populated group and rejecting it is the predicate working correctly. Add a per-row-ruled precondition and it cuts to 9, but at 9 it still mixes a genuinely wrapped row (=arch-09:40=, a header row whose second line continues the sentence) with two distinct rows legitimately sharing a rule (their =todo.org:117=, where splitting is the *right* answer). Same structural signature, opposite correct verdicts. The difference is whether one line continues the other as prose, which is semantics and not in the parse.
+
+So =lint-org.el:424= cannot simply inherit the repair, and a checker keyed on the predicate would train people to ignore it. The idempotence property is the checkable one: reflow twice and diff. It sidesteps detection entirely, because round-tripping the tool's own output correctly is true by construction and needs nobody to decide what a group means.
+
+*** Open questions
+
+- *What should no-hline input do?* My "refuse to reflow" instruction was wrong: adding rules to a ruleless table is the tool's main job, and =test-wrap-org-table.el:147= asserts exactly that. The real danger is narrower — a no-hline table where some line carries an empty cell, so a physical line might be a continuation. Needs a decision on refuse, ask, or heuristic.
+- *Which path bit work?* One question settles it: did the =arch-00= Document Status table have rules between its rows before the reflow? I inferred "probably secondary" from a file-level scan, which can't resolve a table-level incident.
+- *How exposed is work?* Partly resolved by their own implementation, 2026-07-29. *Four secondary-path files are exact.* The primary-path number is 9 under a per-row-ruled precondition, which they correctly label a floor on a population they cannot cleanly define rather than a measurement — the precondition also drops the minimal =a_full= fixture, which has only two groups. My earlier "24 secondary-path files" certification was not sound: their original signature also matches every correctly-reflowed table, so it never partitioned.
+- *Does the grade go up?* Currently Major x most users frequently = P2 = [#B] (any table you reflow that overflows every column, which is what a width violation sends you to the tool to fix). The lint-drives-it finding arrived after that grading and may lift it.
+
+*** Provenance
+
+work reverted their table and left it over-budget at 134 columns, correctly: an over-wide table is cosmetic, a table that says something else is not. I hit this same failure on 2026-07-27, wrote "reflowed a table into a worse shape and lint-org then passed it" into a session summary, and never filed it — which is why work met it three weeks later. Their framing beats the apology: a correct observation recorded and then read as fine is the same failure as a green check on a corrupted table.
+
+Process note for whoever picks this up. This task bounded out of its review loop at three rounds. Every finding across all three landed on inference, never on a measurement — the reproductions held throughout while the reasoning built on them was refuted repeatedly. Trust the Verified section; re-derive anything else.
+
+
+** TODO [#B] Synced workflows link outside the synced set with ../../ :bug:
+:PROPERTIES:
+:LAST_REVIEWED: 2026-07-28
+:END:
+Seven link sites across four synced workflows, three distinct targets, all escaping the =.ai/= boundary into rulesets repo-root paths. From a consuming project's =.ai/workflows/=, =../../= is that project's root, where none of these exist:
+
+- → =../../claude-rules/todo-format.md= (five sites): =open-tasks.org:163=, =task-audit.org:84=, =task-review.org:60=, =task-review.org:64=, =task-review.org:99=
+- → =../../docs/design/task-review.org= (one site): =task-review.org:11=
+- → =../../flush/SKILL.md= (one site): =suspend.org:22=
+
+Verified dead in both home and =.emacs.d=; they resolve only in rulesets, which is why nobody noticed.
+
+Grading: Minor severity (a documented reference an agent can't follow, workaround is to search) x every user every time (every consuming project, every sync) = P2 = [#B]. Same grade as the =references/= link this came from, and the same mechanism — a synced file linking a path the sync doesn't deliver — with seven sites instead of one.
+
+Fix direction, two halves. Rewrite the seven as prose references naming the file, the same move taken for the credential paths: a link that resolves in one repo shouldn't be a link in a file that ships to two dozen. Then close the gate, or the eighth arrives unnoticed.
+
+The gate already exists and is nearly right. =scripts/lint.sh='s =check_md_links= was written for this exact class — its comment says so ("Validate cross-references to =claude-rules/= — the install-layout problem"). It misses these for two concrete reasons: it matches only markdown link syntax (=grep -oE '\[[^]]*\]\([^)]+\)'=), so org =[[file:...]]= links are invisible to it, and its driver loop only feeds it =claude-rules/*.md= and the language rule files, never =.ai/workflows/*.org=. Extending it on both axes is a smaller and more durable fix than a manual sweep.
+
+Found by the adversarial reviewer on the =references/= fix, 2026-07-28, as the sibling class the original report missed.
+
+** TODO [#C] start-work Phase 7 still summarizes the old publish flow :chore:solo:
+:PROPERTIES:
+:LAST_REVIEWED: 2026-07-28
+:END:
+=.claude/commands/start-work.md:336-339= hands off with "Follow =commits.md= exactly" and "Run =/review-code --staged= before each commit" — no isolated reviewer, no re-review loop. It was already stale before the 2026-07-28 review change, because =commits.md= moved the publish flow into the =publish= skill in an earlier commit, so the pointer names a file that no longer holds the flow.
+
+Grading: Cosmetic severity (a stale summary beside a correct canonical, and start-work is attended so the escalation target exists) x most users frequently = P3 = [#C].
+
+Fix is to point Phase 7 at the =publish= skill rather than restate the flow, which is what let it drift in the first place. Worth a sweep for other files that restate the flow instead of pointing at it.
+
+Found by the adversarial reviewer during the 2026-07-28 review-flow change, and correctly filed rather than fixed there: the line was untouched by that diff.
+
+** TODO [#C] Two lint defects at the template source :chore:
+:PROPERTIES:
+:LAST_REVIEWED: 2026-07-28
+:END:
+Both verified at the rulesets source, so every project seeded from them inherits the defect.
+
+=retrospectives/PRINCIPLES.org:38= violates the org-table standard (no closing rule). =lint-org= flags it as =org-table-standard=, and =wrap-org-table.el= reflows it mechanically. This half is purely tool-driven.
+
+=protocols.org= lints at 8 mechanical + 19 judgment =misplaced-heading= hits, all from Markdown =**bold**= in an org file, which org reads as a level-2 heading when it starts a line. 48 bold spans total, 14 of them line-initial. This half is not mechanical: converting to org =*bold*= is 48 edits, and =lint-org --fix= would rewrite the line-initial ones without knowing they are emphasis rather than headings.
+
+Grading: Cosmetic severity x every user every time = P3 = [#C]. It is most of the linter's noise on protocols.org, which is the real cost — noise that trains the reader to skip the report.
+
+Not =:solo:= — both files are synced templates.
+
+Source: winvm link-integrity pass, 2026-07-28.
+
+** DOING [#B] Finish context-engineering rightsizing :refactor:
+:PROPERTIES:
+:LAST_REVIEWED: 2026-07-27
+:END:
+Started 2026-07-27 from three Anthropic posts Craig supplied. Always-loaded rules went from ~57,800 tokens to ~28,949, plus 13,461 path-scoped. Shipped: =paths:= frontmatter on the three file-type rules, the per-project generic-rule de-duplication, =commits.md= split into a 2,804-token invariant core plus the =publish= skill, =testing.md= split into a 347-word directive plus the =testing-standards= skill, the approval-gate signal fixed from =.ai/=-tracking to remote host, and the first-person directive.
+
+*What remains is Craig's decisions, not execution.* Each of these needs him:
+
+1. *C1 — =verification.md= (3,388 tok).* The Opus 5 guide says explicit verification instructions cause over-verification and should be removed. His standing direction is never guess, always check. My read: the honesty core (don't claim a green suite you didn't run) stays and shortens, the process injection (green baseline, suite-as-its-own-step) moves to the publish skill. His call — and it's the rule closest to a preference he's stated outright.
+2. *=interaction.md= (3,828 tok, now the largest).* Carries genuinely universal output constraints (no popup menus, no reverse-video markup) plus the new peer-reasoning contract. Splitting it means deciding which parts must fire on every turn.
+3. *The TDD rationalization table.* Moved to =testing-standards= rather than cut. The posts argue that kind of over-argument is counterproductive on current models. Deleting his defense against me skipping TDD is his call.
+4. *D3 — the gate separation.* Which approval gates are preference (he wants to see what goes out under his name) versus guardrail (written when the worst case was worse). They read identically in the files; only he can tell them apart. Highest-leverage input remaining.
+
+*Do first:* the three docs in =working/context-engineering-rightsizing/= are one commit behind — they were corrected at d74d98d, before the two splits and the gate fix. Reconcile before deciding anything from them.
+
+Risk on the record: =testing.md='s margin is thinner than =commits.md='s. If =testing-standards= fails to trigger mid-test-writing the mocking-boundary rules are lost — a quality regression, visible in review, but a real bet where =commits.md='s moved half was purely procedural.
+
+** TODO [#B] Recurring-loop mechanics as a shared rule :feature:
+:PROPERTIES:
+:LAST_REVIEWED: 2026-07-27
+:END:
+Work proposes promoting the pattern behind its 2026-07-27 auto-mode triage-intake into a standing rule covering all recurring agent tasks: fixed interval via =CronCreate= rather than dynamic self-pacing, a subagent per firing so raw scan output never reaches the orchestrator, silence with no heartbeat when subagent-backed, and accumulate-don't-mutate between closes with explicit "close the X" / "stop the X" verbs. Likely touches =triage-intake.org= auto mode, =inbox.org= monitor mode, and a new short rule in =claude-rules/= so individual workflows point at one definition of cadence, isolation, and silence.
+
+Points 1, 2, and 4 mostly promote existing practice. Point 3 changes documented behavior and needs a real decision. Three findings from the skeptical review, all to resolve before this ships:
+
+1. *Point 1 is too absolute.* Fixed interval is the right default, but not the right universal. A loop waiting on unpredictable external state (a CI run, a deploy queue) should pace to how fast that state actually changes, which is what dynamic scheduling exists for. Write it as "default to a fixed interval; use dynamic pacing only when polling external state whose timing you can't predict."
+2. *Point 2 collides with a standing instruction.* Craig's harness prompt says not to spawn subagents unless he requested it. Making subagent-per-firing the standing pattern needs that reconciled explicitly — the honest reading is that asking for the loop *is* the request, and the rule should say so rather than leaving two instructions to fight. The isolation argument itself is sound and matches the Opus 5 guidance (delegate for genuinely independent, sizeable work).
+3. *Point 3 has a silent-failure hole.* Removing the heartbeat means a loop that died looks exactly like a loop quietly finding nothing. "The subagent completing is proof it ran" only holds if a *failed* subagent still surfaces. Either keep a rare heartbeat (hourly, not per-fire) or specify that failure always breaks silence. Suppressing success is fine; suppressing failure is not.
+
+Also underspecified: what counts as "signal" for a subagent-backed loop. And the silent-until-signal spec (=docs/specs/2026-07-20-silent-until-signal-monitors-spec.org=, IMPLEMENTED) documents the per-fire heartbeat, so it needs a superseding history line rather than a silent contradiction.
+
+*** 2026-07-27 Mon @ 16:55 Work accepted all three findings and sharpened point 3
+
+Work agreed without pushback and corrected my either/or on point 3, which was too weak. Both mechanisms are needed, not one: a failed or hung subagent breaking silence covers a *scan-level* failure, but only a periodic heartbeat catches the *scheduler* dying — because in that case nothing spawns at all and there is no failure to report. Heartbeat rare (hourly), not per-fire.
+
+The live consequence makes it urgent rather than theoretical: =CronCreate= auto-expires recurring jobs after seven days, so any fully-silent loop is *guaranteed* to die quietly. Work's auto-triage loop is running under exactly that contract right now.
+
+Work also added the reason that makes point 2's carve-out principled and should go in the rule text: the subagent exists to keep N sources' worth of raw scan output out of the orchestrator across a multi-hour loop, not to parallelize.
+
+Craig's call on point 3 is pending; work is surfacing it and will send the answer.
+
+Source: work handoff 2026-07-27 14:51, reply 16:55.
+
** TODO [#B] Sentry triage split — work mail and messengers in, personal mail out :bug:
:PROPERTIES:
:LAST_REVIEWED: 2026-07-27
@@ -90,26 +301,13 @@ The proposed Claude-memory migration auditor is related only by runtime portabil
No prepared diff: this is a shared configuration design decision, and the current local wrappers/configs remain machine-owned evidence rather than canonical source. Say "spec the MCP registry sync" to start the decisions walk.
-** VERIFY [#B] Parked: telegram source treats "down" as launch, not SCAN FAILED (from .emacs.d)
+** VERIFY [#B] A SCAN FAILED source must not advance its sentinel (engine, not the plugin)
:PROPERTIES:
:LAST_REVIEWED: 2026-07-24
:END:
-What arrived: a superseding handoff (the 17:23 setq-only version is retained as context but not the one to take). Both work and home independently hit the same failure — the telegram source, finding telega down or unloaded (its *normal* entry state), reported "SCAN FAILED: telegram — not loaded" or ran a blind sweep, instead of running Step 1 to launch it. Neither hit a segfault. The recovery IS Step 1: =(telega t)= both loads the package and starts the docker server.
-
-Root cause is wording, not code: the Quick Reference said "never skips because the server is down" but the closing paragraph said "if any lifecycle step fails ... reports SCAN FAILED", and two agents conflated "server is down" with "a lifecycle step failed" → blind sweep without ever attempting the launch.
-
-The fix (33-line diff, verified present): a directive after the Quick Reference that down/not-loaded is the *trigger* to launch, never a skip; the =:ENABLED:= guard tests whether telega is *installed* (=fboundp=), not whether the server is up; SCAN FAILED reserved for a launch that was attempted and didn't reach Ready; a note that a blind sweep is worse than a clean failure because it hides real unread behind a false all-clear. Also keeps the earlier =(setq telega-use-docker t)= segfault guard in Step 1 — both projects confirmed it's a real latent guard though not the cause here.
-
-Recommendation: accept the superseding version. The wording defect is real and reproduced by two projects, and a triage source silently reporting all-clear on a mailbox it never scanned is exactly the false-negative class that matters.
+Promoted to top-level 2026-07-28 when its parent closed — it is a separate engine question and would have been buried under a DONE parent.
-Prepared: [[file:working/triage-telegram-down-launch/proposed.diff]] and the full proposed file beside it.
-Say "approve the parked telegram fix" and it gets applied.
-
-*** VERIFY a SCAN FAILED source must not advance its sentinel (engine, not the plugin)
-:PROPERTIES:
-:LAST_REVIEWED: 2026-07-24
-:END:
-Secondary finding from the same handoff, flagged for your judgment because it's *engine* behavior in =triage-intake.org=, not the telegram plugin. work reported that after a SCAN FAILED, "the marker still advanced" — and telegram is =:ANCHOR: none=, so something advanced a cursor it shouldn't have. The compounding harm: a source that reports SCAN FAILED but advances its window means the next sweep believes it already covered that window, so the blind-sweep hole persists across sweeps rather than self-healing. Not touched .emacs.d-side; needs a look at the engine's per-source last-run/anchor advance.
+Secondary finding from the 2026-07-24 handoff, flagged for your judgment because it's *engine* behavior in =triage-intake.org=, not the telegram plugin. work reported that after a SCAN FAILED, "the marker still advanced" — and telegram is =:ANCHOR: none=, so something advanced a cursor it shouldn't have. The compounding harm: a source that reports SCAN FAILED but advances its window means the next sweep believes it already covered that window, so the blind-sweep hole persists across sweeps rather than self-healing. Not touched .emacs.d-side; needs a look at the engine's per-source last-run/anchor advance.
** TODO [#B] cj-remove-block still can delete the WRONG cj block :bug:
:PROPERTIES:
@@ -2121,3 +2319,72 @@ Fix direction (per home): while inside a =begin_example= / =begin_src= block, sk
Home answered it and I re-verified both halves here: =grep invalid-block= over =lint-org.el= returns nothing, and =org-lint--checkers= enumerates =invalid-block= in batch Emacs alongside =link-to-local-file=, =invalid-babel-call-block=, and =missing-language-in-src-block=. So this is the same shape as the =link-to-local-file= episode: suppress an =invalid-block= finding whose line falls inside a verbatim block, on our side of org-lint's output. No local checker to edit.
Regression fixture, ready to use: home's own =.ai/notes.org= PENDING DECISIONS example block (lines 386-398) holds an unescaped =** Feature Name or Topic= and trips both delimiters — findings at lines 386 and 398. Home is deliberately leaving its copy unescaped so it stays a fixture, and asked to be pinged when the filter lands so it can re-run. The filter should take those two findings to zero without touching the file.
+** DONE [#C] sentry.org calls one loop cycle a "fire" :chore:solo:
+CLOSED: [2026-07-28 Tue]
+:PROPERTIES:
+:LAST_REVIEWED: 2026-07-28
+:END:
+Done 2026-07-28. 72 noun-sense instances in =sentry.org= became "cycle"; the four verb-sense uses stayed. Three more lived outside the file — =wrap-it-up.org= ("a crashed fire"), =todo-cleanup.el= and its test ("every sentry fire") — all naming a sentry cycle, so they moved too. home's proposal was scoped to =sentry.org= alone, so the leak would have split the vocabulary across files.
+
+Craig read home's "nine fires" as nine emergencies and went looking for what was burning (2026-07-28): "I assume you mean nine crises, not nine loop cycles and I begin to get scared." The term reaches him directly — digest headings render as =** Fire 11 — 08:32 CDT= in the anchor he reads every morning. 35 instances in =sentry.org=.
+
+Grading: Minor severity (a user-facing artifact that miscommunicates, no data loss) x most users frequently (every digest, on every project running sentry) = P3 = [#C].
+
+*Take the problem, not home's proposed term.* home proposed "pass", reasoning that it already lives in the file's vocabulary. That is exactly what disqualifies it. =sentry.org= already uses "pass" as a precise numbered noun: "the pass list", "the Pass Runner", "eleven finding/hygiene passes", "pass 12" for the implementation pass. There are exactly eleven hygiene passes, so home's proposed digest heading =** Pass 11= collides with an existing real referent. Renaming would trade a term Craig misreads as urgent for one that is genuinely ambiguous.
+
+Counter-proposal: *cycle*. It appears zero times in =sentry.org=, so there is nothing to collide with. It is also Craig's own word from the very quote that surfaced this ("not nine loop cycles"). =** Cycle 11 — 08:32 CDT= reads cleanly. Checked and rejected: "sweep" (already used for hygiene and property sweeps) and "run" (already used as a noun, "first live run").
+
+Keep the verb sense of fire throughout ("the notify fires", "the path never fires") — only the noun meaning one loop cycle changes. Past session anchors are historical records and stay as written.
+
+Craig approved "cycle" on 2026-07-28. That settles the only judgment the task carried, so it is =:solo:= now: the surface is the 35 noun-sense instances in =sentry.org=, and the completion check is objective — zero noun-sense "fire" left, verb sense untouched, lint clean, suite green, canonical and mirror in sync.
+
+Source: home handoff, 2026-07-28.
+** DONE [#B] references/ is linked from protocols.org but never synced :bug:
+CLOSED: [2026-07-28 Tue]
+:PROPERTIES:
+:LAST_REVIEWED: 2026-07-28
+:END:
+Craig picked option 1 on 2026-07-28: drop the link, point at the calendar workflows instead. The four of them sync already and carry the MCP tool names, both account ids, the gcalcli fallback, and the conflict-check discipline — everything the reference was cited for.
+
+The adversarial review then returned =Needs Discussion= and widened the fix twice, both correctly:
+
+- My first replacement said credentials "live in the rulesets repo" without naming a file. The only calendar-named document there was =calendar-reference.org=, whose three credential paths have all been dead since the OAuth keys moved into the encrypted MCP bundle in May. So the prose sent a reader to a stale file, which fails more quietly than the dead link it replaced. Now names =mcp/README.org=, the real authority, verified to document =gcp-oauth.keys.json= as gitignored and regenerated at install.
+- =calendar-reference.org= was left orphaned by the link removal: zero live inbound references, two copies, and =references/= is not in =sync-check.sh='s gate (=paths=(protocols.org workflows scripts)=), so the copies could drift silently. Deleted both, and the empty =references/= directories with them. Its operational content is fully covered by the four workflows and its credential paths were all stale, so nothing live was lost.
+
+The review also found the same defect class at seven other sites, filed separately.
+
+One fact died with the file, dropped deliberately rather than by accident: that the Google Cloud app runs in production mode, so tokens don't expire after seven days. It's checkable in the console, and it was the last live line in a document whose other credential facts had all gone stale.
+
+=protocols.org:273= links to =references/calendar-reference.org=, but startup.org's rsync copies only =protocols.org=, =workflows/=, and =scripts/=. So the link is dead in every consuming project. Confirmed: the file exists at =claude-templates/.ai/references/=, and neither home nor =.emacs.d= has a =.ai/references/= at all.
+
+Grading: Minor severity (a documented reference an agent can't follow, workaround is to search or ask) x every user every time (every consuming project, every sync) = P2 = [#B].
+
+Pinned 2026-07-28 for Craig's decision. Analysis below is complete; only the choice is open.
+
+*Revised recommendation: drop the link, don't add the sync.* My first read was add =references/= to the rsync. Looking at what the directory actually holds reversed it:
+
+- =references/= holds exactly one file, and =protocols.org:273= is its only citation anywhere in the tree.
+- The four calendar workflows (=add-=, =edit-=, =delete-=, =read-calendar-event(s).org=) already sync to every project, and already carry the operational detail: the MCP server name, both account IDs, the gcalcli fallback, conflict-checking. None of them cites =calendar-reference.org=; they are self-sufficient.
+- What the reference uniquely adds is credential *file locations* (an OAuth keys path, a GPG-encrypted gcalcli secret) — no secret values. Re-auth is a rare operation Craig performs himself, not something an agent in another project needs a pointer to.
+
+So adding a synced directory carrying =--delete= semantics, to deliver one mostly-redundant file, is a poor trade. It also cuts against the rightsizing work: =protocols.org= is read into context every session, and a dead link is noise in a file being slimmed.
+
+Preferred fix: replace the link with a one-line pointer to the calendar workflows, which travel and are current. The reference file stays rulesets-only for the credential locations.
+
+Alternatives if Craig prefers: add =references/= to the rsync (the =--delete= hazard is not present today — work is the only project with a =.ai/references/= and its copy is byte-identical), or fold the credential locations into the calendar workflows and then drop the link (most complete, but spreads local absolute paths across four more synced files).
+
+Not =:solo:= — it changes a synced template, which needs Craig's approval by the inbox rule, and the sync-vs-drop choice is his.
+
+Source: winvm link-integrity pass, 2026-07-28.
+** DONE [#B] Parked: telegram source treats "down" as launch, not SCAN FAILED (from .emacs.d)
+CLOSED: [2026-07-28 Tue]
+:PROPERTIES:
+:LAST_REVIEWED: 2026-07-24
+:END:
+Applied 2026-07-28, merged with the segfault root-cause fix that arrived the same morning rather than applied alone.
+
+Merging was necessary, not tidiness. The parked proposed file still carried the bad =(telega--loadChats 'main)= call at its own lines 52 and 122, so applying it as-is would have shipped a file that fixed the wording defect while preserving the call that kills the server. Its third hunk also added prose citing "tdlib segfaults in native mode (SEGFAULT gotcha below)" — pointing at the section the new handoff rewrites to say those deaths were our own bad argument, not tdlib memory corruption.
+
+What landed: all three parked hunks (the down-is-launch directive, the SCAN-FAILED-only-after-launch-attempted rewording, the =(setq telega-use-docker t)= restored to the Step 1 code block), plus both corrected =loadChats= call sites and the rewritten gotcha. The native-mode prose was reconciled in two places so it no longer leans on the refuted story: the Step 1 comment now states plainly that docker mode and the loadChats bug are separate concerns (the deaths happened *in* docker mode, so docker mode is neither a defense against it nor evidence for it), and the Quick Reference line says "crashed in native mode (2026-06-09)" instead of "segfaults", with the same disambiguation.
+
+Verified: both live call sites use the TL object; the two remaining ='main= occurrences are inside the gotcha prose describing the bug. lint-org clean, mirror synced, suite green.