aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ai/sessions/2026-07-29-06-36-adversarial-review-flow-and-telegram-fixes.org514
-rw-r--r--inbox/lint-followups.org31
-rw-r--r--todo.org141
3 files changed, 599 insertions, 87 deletions
diff --git a/.ai/sessions/2026-07-29-06-36-adversarial-review-flow-and-telegram-fixes.org b/.ai/sessions/2026-07-29-06-36-adversarial-review-flow-and-telegram-fixes.org
new file mode 100644
index 0000000..12cbe3d
--- /dev/null
+++ b/.ai/sessions/2026-07-29-06-36-adversarial-review-flow-and-telegram-fixes.org
@@ -0,0 +1,514 @@
+#+TITLE: Session Context — 2026-07-28
+#+AUTHOR: Craig Jennings
+#+DATE: 2026-07-28
+
+* Summary
+
+** Active Goal
+
+Started as inbox triage on a telegram-plugin bug report and became two things: shipping the cross-project fixes that arrived overnight, then designing and dogfooding a mandatory isolated adversarial review before every commit — which immediately found real defects in its own design and in everything filed afterward.
+
+** Decisions
+
+- *Merge colliding fixes rather than sequence them.* The parked down-is-launch diff still carried the bad =loadChats= call and cited the segfault gotcha the other fix rewrites. Applying either alone would have shipped a file arguing against itself.
+- *"Adversarial", not "hostile"* (Craig). An agent told to attack manufactures findings, so the stance carries a substantiation floor: a finding not substantiated against the diff is dropped.
+- *Re-review until the reviewer approves* (Craig's addition, the thing I had missed). Same reviewer continued, not a fresh one — a fresh reviewer can't tell an addressed finding from one that never existed. Bounded at three rounds or first recurrence.
+- *Dispatch on every commit*, with the reviewer's own Phase 0 ruling triviality. A floor written as "small" or "mechanical" puts the judgment back with the author, whose judgment is the thing being checked.
+- *Pass the requirement source, withhold the rationale.* A ticket is not the author's model; it was written first and by someone else, so it's the only input that can contradict the author's claim.
+- *=cycle=, not =pass=, for one sentry loop* (Craig). home proposed =pass=; =sentry.org= already uses it as a numbered noun for the eleven hygiene passes, so =Pass 11= would have collided with =pass 12=.
+- *Drop the =references/= link rather than sync the directory.* The four calendar workflows already travel and already carry the recipes.
+- *Strip the wrap-org-table task to Verified / Open questions* after its review loop bounded out. The measurements were never what failed.
+
+** Data Collected / Findings
+
+- *The telegram bug.* =(telega--loadChats 'main)= sends a bare symbol on the wire; =tdat_plist_value= (=telega-dat.c=) accepts only =(=, =[=, ="=, =-=, digit, =t=, =:=, =n= and calls =assert(false)= on =m=. Verified against telega's source at four points rather than trusting the handoff. Exposure was manual triage only — sentry excludes messengers, so home's eleven overnight cycles never loaded the plugin.
+- *=wrap-org-table.el= splits logical rows*, and =lint-org= doesn't merely miss it — it *causes* it. =lint-org.el:424= calls the same broken predicate, so it reports the tool's own correct output as "missing rule between rows — wrap-org-table.el reflows it" when nothing is missing, then reports the corrupted result clean. Idempotence is broken: the tool corrupts its own output on a second run.
+- *The isolated reviewer earned its keep on its first four uses*, finding: that withholding the ticket made my claim self-certifying; that my =subagents.md= override reaffirmed the Prompt Contract field that would destroy the isolation; a fourth verdict (=Needs Discussion=) I'd asserted didn't exist; and four successive wrong root-cause analyses on the table bug.
+- *rulesets is itself exposed* to the table bug: =todo.org='s four-row attachment-sanitization table. Don't reflow until fixed.
+- *Seven =../../= link sites* across four synced workflows resolve only in rulesets. =scripts/lint.sh='s =check_md_links= was built for that class and misses them because it matches markdown syntax only.
+
+** Files Modified
+
+Seven commits, all pushed, velox synced after each. =bff0138= merged telegram fixes. =43a4cf7= post-load liveness check. =614e3b1= removed finished working dirs. =ca508a1= filed three handoff findings. =f3f5bfd= the =fire= → =cycle= rename (72 sites plus three that had leaked outside =sentry.org=). =3a933a2= the =references/= analysis. =5999f88= dropped the dead link and deleted the stale file behind it. =ecd5d7b= filed the wrap-org-table bug.
+
+Rules changed: =publish/SKILL.md= Step 1 rewritten (dispatch contract, four defined verdicts, the loop, bounds), =review-code/SKILL.md= (two levels of dispatch, adversarial contract, re-review mode), =claude-rules/subagents.md= (Isolation Override; Prompt Contract field 2 inverts), and the three unattended callers taught to park.
+
+** Next Steps
+
+- *Three of the four items Craig queued are untouched*: the winvm =[#C]= lint defects, the context-engineering rightsizing (needs his four decisions), and the sentry spec deep read (two weeks old).
+- *Rule gap found by using the rule*: =Needs Discussion= exits to the user, but nothing says what happens after the user answers — whether the round counter resets. I treated it as a fresh review; that judgment isn't written down.
+- *Item 2 is now qualified*: its fix says "run =wrap-org-table.el=", and that tool has a live corruption bug. The specific table is safe, but verify the output rather than trust it.
+- Four =[#B]= bugs filed tonight and unstarted: the table splitter, the =../../= links, plus the two carried in.
+
+KB: promoted 1 / consulted no
+
+* Session Log
+
+** 11:55 — Startup
+
+Ran startup. Rulesets already current, project repo clean and current, =make
+install= had nothing new to link, =.ai/= synced from templates. No crash anchor
+— previous session (context-engineering rightsizing, 2026-07-27 17:02) wrapped
+cleanly.
+
+Findings: 6 tasks unreviewed >7 days; roam inbox holds 4 items; KB at 108
+=:agent:= nodes with nothing matching this project. Spec-sort and host-identity
+probes silent. Language-bundle check silent.
+
+** 12:05 — Inbox: the telegram segfault root cause
+
+Four new inbox files from =.emacs.d=, two pairs: a 06:15 intro note + plugin
+file, then a 07:21 correction + superseding plugin file. The correction retracts
+one secondary claim from the 06:15 write-up (that the "19 of ~50 chats" reading
+was truncation caused by the bug — it wasn't; 19 is the real account size,
+measured by work at the wire level). Root cause and fix unchanged.
+
+The proposal: =triage-intake.telegram.org= Step 1 calls =(telega--loadChats
+'main)=, and that bare symbol kills =telega-server= outright.
+
+I verified the whole chain against telega's own source rather than taking the
+handoff's word for it (=elpa/telega-20260706.2147/=):
+
+- =telega--loadChats= (telega-tdlib.el:2190) drops its argument straight into
+ the request as =:chat_list= with no conversion. Confirmed.
+- The C parser =tdat_plist_value= (server/telega-dat.c:466) accepts only =(=,
+ =[=, ="=, =-=, a digit, =t=, =:=, or =n= to start a value; anything else
+ prints "Unexpected char '%c' in plist value" and calls =assert(false)=.
+ =main= starts with =m=. Confirmed, and the accepted-char list in the handoff
+ is exactly right.
+- telega's own callers all pass the object: telega.el:290, telega.el:295,
+ telega-tdlib-events.el:516. Confirmed.
+- The symbol shorthand lives in a different layer — telega-filter.el:472 and
+ telega-folders.el:99 convert =(eq cl-fspec 'main)= to ='(:@type
+ "chatListMain")=. Confirmed.
+
+Every claim holds. Grepped all workflows for other bare-symbol arguments to
+=telega--*= wrappers: only the two sites this diff already fixes.
+
+*The complication I found.* There is already a parked =[#B]= VERIFY (todo.org:136,
+from =.emacs.d= 2026-07-24) proposing a different fix to this same file — the
+down-is-launch fix, with a prepared diff in
+=working/triage-telegram-down-launch/=. Its proposed file still carries the bad
+=loadChats= call at its lines 52 and 122, and its third hunk adds prose citing
+"tdlib segfaults in native mode (SEGFAULT gotcha below)" — pointing at the very
+section this new handoff rewrites to say the crashes were our bug, not tdlib's.
+Applying either one alone leaves the file half-right. They need merging, not
+sequencing.
+
+Also: home and work both declare =telegram= in =:TRIAGE_SOURCES:=, so both have
+been killing telega-server on every sweep. Work already patched locally and
+verified at the wire level. Home has not been told.
+
+** 11:58 — Staged the batch, closed the loops
+
+The Stop hook counted the four files still sitting in =inbox/=. The triage was
+done; what remained was Craig's merge decision, not mine. So I staged all four
+into =working/triage-telegram-segfault-rootcause/= (the 07:21 pair as
+=.proposed= + correction note, the 06:15 pair kept as superseded context — its
+blast-radius and follow-up sections aren't repeated in the correction). Inbox
+back to clean, =inbox-status= rc=0.
+
+Left =working/triage-telegram-down-launch/= untouched: the parked VERIFY's body
+links its =proposed.diff= by path, and moving it would break the link before
+Craig has decided anything. If he takes the merge, that consolidates then.
+
+Replied to =.emacs.d= — confirmed the verification, and told them the one thing
+they couldn't see: their own 2026-07-24 parked diff collides with this, both in
+the stale =loadChats= call it still carries and in the native-mode prose it adds
+citing the gotcha this rewrites. Also declined their suggested bare-symbol lint:
+one consumer file, two call sites both fixed, so the stated rule is cheaper than
+a checker with a single input.
+
+Notified home directly with the mechanism and the two-site fix, told it to patch
+locally rather than wait on the canonical, and carried the correction forward
+explicitly so it doesn't inherit the retracted truncation story.
+
+Nothing applied to the canonical. That still waits on Craig.
+
+** 12:10 — Craig picked the merge. Applied and committed.
+
+Merged both fixes into one version rather than sequencing them. Base was the
+segfault-corrected file, then the three parked hunks on top: the down-is-launch
+directive, the SCAN-FAILED-only-after-launch-attempted rewording, and =(setq
+telega-use-docker t)= restored to the Step 1 code block.
+
+The reconcile that made merging worth doing. The parked hunk's new comment said
+"tdlib segfaults in native mode (SEGFAULT gotcha below)", pointing at the
+section the other fix rewrites to say those deaths were our own bad argument.
+Left alone the file would have argued against itself. I changed the Step 1
+comment to state plainly that the two are separate concerns (the deaths happened
+*in* docker mode, so docker mode is neither a defense against the loadChats bug
+nor evidence for itself), and reworded the Quick Reference line from "tdlib
+segfaults outside docker mode" to "crashed in native mode (2026-06-09)" with the
+same disambiguation.
+
+That reword also removed a host-identity violation I hadn't gone looking for.
+The original asserted "Craig's daemon currently has telega-use-docker nil" — a
+mutable machine fact stated as fixed in a synced doc. I checked the actual
+default (=telega-customize.el:514=, =defcustom telega-use-docker nil=) and wrote
+the durable claim instead.
+
+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 on the
+changed file, mirror synced, =make test= green before (exit 0) and after (exit
+0).
+
+todo.org: closed the parked =**= VERIFY as =DONE= + =CLOSED:= per todo-format.md
+with the merge rationale in the body. Promoted its =***= engine child (SCAN
+FAILED must not advance the sentinel) to top-level =**= VERIFY so it doesn't get
+buried under a DONE parent. Kept its =:LAST_REVIEWED: 2026-07-24= rather than
+stamping today — I moved it and judged it separate, but nobody re-derived its
+content, so the older date keeps it honest.
+
+Review: Approve, no Critical or Important. Two Minor, both surfaced rather than
+fixed. The gotcha now advises a post-load =(process-live-p ...)= check that the
+Step 1 recipe doesn't actually do, and adding it would extend the recipe past the
+two fixes Craig approved.
+
+Committed =bff0138=. Not pushed — that's a separate confirmation.
+
+** 12:20 — home replied, and corrected my urgency read
+
+home accepted, patched both call sites locally, and re-verified the diagnosis
+independently rather than trusting it. Useful correction back: home declares
+telegram in =:TRIAGE_SOURCES:=, but sentry excludes messengers from triage
+intake, so the plugin never loaded on a sentry fire. Eleven overnight fires ran
+clean against the broken file. I had assumed the sweeps were affected; the real
+exposure is manual triage intake only.
+
+Told home its stopgap won't be reverted into a broken state — the next rsync
+replaces it with canonical content carrying the same fix. On this machine that
+lands as soon as its next startup runs, since the rsync reads the local rulesets
+working tree. velox needs the push.
+
+Inbox back to zero.
+
+** 14:10 — Pushed, and closed the velox gap
+
+Pushed =1675613..bff0138= to origin after the pre-push reconcile (still 1 ahead,
+0 behind).
+
+=.emacs.d= replied while the push was in flight. It had already verified bff0138
+here, re-run its own workflows rsync, and confirmed the corrected form landed.
+It withdrew the bare-symbol lint suggestion, conceded the home omission, and
+flagged one real gap: bff0138 was committed but not pushed, so velox stayed
+exposed. That was true when written and stale by the time it arrived.
+
+Checked velox over tailscale (this host is ratio, per =uname -n=). It was 1
+behind / 0 ahead and sync-safe, so I fast-forwarded it through the same
+=git-worktree-gate sync-safe= check startup uses. Velox is now at bff0138 and its
+workflow file carries the corrected call, with the only bare ='main= occurrences
+inside the gotcha prose. Both daily drivers covered.
+
+Corrected read carried into both replies: the exposure was manual triage intake
+only, not the automated sweeps, because sentry excludes messengers.
+
+** Open follow-ups (surfaced to Craig, not acted on)
+
+1. The gotcha tells callers to check =(process-live-p (telega-server--proc))=
+ after a load, but the Step 1 recipe doesn't do it. Now that the corrected call
+ shouldn't kill the server, that check is what would catch a regression. Left
+ out deliberately as scope creep past the two approved fixes.
+2. Both =working/triage-telegram-*= dirs are completed-task artifacts and want
+ filing per working-files.md. Revised read after checking: delete both
+ outright. Every file is tracked (=b19d420= and =bff0138=), so git holds them
+ permanently and a copy in =assets/= would only duplicate history. Nothing
+ links to them.
+
+** 15:00 — Second inbox round: .emacs.d self-correction + winvm lint findings
+
+=.emacs.d= wrote back to say it had overcorrected on home: it accepted "home was
+in the blast radius" and then recorded that home "had been killing telega-server
+on every sweep too", which home's own sentry data refutes. It fixed its task
+record rather than leaving it. I told it the pattern wasn't one-sided — I made
+the same move this morning, estimating home's blast radius instead of measuring
+it, and home's data is what corrected me. It also offered the emacs-side half of
+a completed-vs-truncated signal, which it has filed as =[#C]=, if the
+=process-live-p= recipe change lands.
+
+=winvm= sent a link-integrity pass with three findings, all reproduced against
+the rulesets source rather than only its local copy. I verified all three:
+
+1. =protocols.org:273= links =references/calendar-reference.org=, but the rsync
+ set is only =protocols.org=, =workflows/=, =scripts/=. Dead link in every
+ consuming project. home and =.emacs.d= have no =.ai/references/= at all.
+2. =retrospectives/PRINCIPLES.org:38= violates the org-table standard.
+ =lint-org= confirms, checker =org-table-standard=.
+3. =protocols.org= lints 8 mechanical + 19 judgment =misplaced-heading=, all
+ from Markdown =**bold**= in an org file. 48 bold spans, 14 line-initial.
+
+The check winvm couldn't run: adding =references/= to the sync brings =--delete=
+with it, which would destroy project-owned files there. work is the only project
+with a =.ai/references/= and its copy is byte-identical to the source, so nothing
+would be lost. That clears the path to sync-it over drop-the-link.
+
+One refinement back to winvm on finding 3: the 8 mechanical hits are would-fix,
+and the 14 line-initial bold spans are exactly what =lint-org --fix= would
+rewrite without knowing they're emphasis rather than headings. So that half is
+not safe to run mechanically even though the count suggests it is.
+
+Filed as =[#B] :bug:= (the dead link, Minor x every-project-every-sync = P2) and
+=[#C] :chore:= (the two cosmetic defects, P3), both stamped =:LAST_REVIEWED:=
+today since I graded them today. Verified todo.org's lint counts are unchanged
+from the pre-edit baseline (mechanical=2, judgment=12, both pre-existing and
+already in =inbox/lint-followups.org=).
+
+Inbox back to zero. todo.org is left uncommitted deliberately — Craig has a
+pending decision that will likely touch it again, so batching beats a churn
+commit.
+
+** 18:45 — Craig took both follow-ups. Liveness check staged.
+
+Added the liveness check to the Step 1 load call. It now returns ='server-died=
+instead of an unconditional ='loaded=, and new prose routes that into the
+existing SCAN FAILED definition rather than inventing a category: a server that
+dies during load leaves a thin hash, which reads exactly like a quiet account,
+which is the same false all-clear the down/not-loaded rule already prevents.
+
+Self-review caught a real defect in my own first draft. I called
+=(telega-server-live-p)= bare, but Step 0 guards the same predicate with
+=fboundp= because telega may not be loaded. A launch that failed outright would
+have signalled void-function instead of returning the clean contract. Added the
+guard, matching Step 0's idiom. Verified the predicate is exactly the
+=process-live-p= expression the gotcha names (=telega-server.el:221=).
+
+Verified: parens balance at depth 0, lint-org 0/0, mirror identical, =make test=
+green (exit 0) on the final state.
+
+Not committed — waiting on the approval gate.
+
+** 18:48 — Third inbox round
+
+=.emacs.d= sent a closing FYI marked no-action, agreeing my framing of the shared
+failure (blast radius estimated rather than measured) named the trigger rather
+than the failure. Deleted without reply, since replying to "nothing owed back"
+is noise.
+
+home proposed renaming sentry.org's noun-sense "fire" to "pass", after Craig read
+its "nine fires" as nine emergencies: "I assume you mean nine crises, not nine
+loop cycles and I begin to get scared." The problem is real, well-evidenced, and
+reaches Craig directly through digest headings.
+
+But the proposed term is wrong, and the reason home gave for it is the
+disqualifier. =sentry.org= already uses "pass" as a precise numbered noun — the
+pass list, the Pass Runner, "eleven finding/hygiene passes", "pass 12". With
+exactly eleven hygiene passes, home's proposed =** Pass 11= heading collides with
+an existing referent. That trades a term Craig misreads as urgent for one that is
+genuinely ambiguous.
+
+Counter-proposed *cycle*: zero occurrences in the file, and Craig's own word in
+the quote home cited. Checked and rejected "sweep" (3 uses) and "run" (used as a
+noun). Filed =[#C] :chore:= with the grading and the collision analysis; replied
+to home with the counter-proposal.
+
+** 18:50 — Three commits, and the collision confirmed from live evidence
+
+Craig approved both follow-ups and the =cycle= term. Three commits:
+
+- =43a4cf7= the liveness check.
+- =614e3b1= removed both telegram working dirs. Filing by deletion, since every
+ file was already in git via =b19d420= and =bff0138= and an =assets/= copy would
+ only duplicate history.
+- =ca508a1= filed the three handoff findings with their gradings.
+
+home wrote back confirming the "pass" collision was real, and that it had already
+walked into it: its anchor now carries =** Pass 11= meaning the eleventh cycle,
+three lines from =pass 12 (solo-task implementation)= meaning the twelfth item in
+the pass list. Same file, two referents, introduced by its own normalization an
+hour earlier. It found that in live evidence faster than reading the file would
+have caught it.
+
+It was blocked on Craig's confirmation and had written a memory saying "pass", so
+I sent the confirmation immediately. The memory was the urgent half — a stale one
+teaches every future home session the ambiguity, where the anchor is one file.
+
+Recording Craig's approval flipped the sentry task to =:solo:=. The term was the
+only judgment it carried, and the completion check is objective, so it can ride a
+backlog run rather than waiting for someone to touch =sentry.org=.
+
+Pushed =bff0138..ca508a1=, velox fast-forwarded to match.
+
+** 19:45 — The cycle rename, and the leak home's scope missed
+
+home did its side first: renormalized its anchor by restoring the
+pre-normalization backup and re-running fire→cycle from clean, rather than
+reverse-mapping pass→cycle. That was the right call — reverse-mapping would have
+needed a judgment on every instance to separate its own conversions from genuine
+pass-list references, where re-running from clean makes it structural. It also
+corrected its memory and handed the canonical back.
+
+Did the canonical rename with a script rather than by eye: protect the verb sites
+by explicit pattern, assert zero unclassified =-ed/-ing= forms survive, then
+substitute. 72 noun instances converted, 4 verb sites untouched (=/loop= fires
+again, two "fires on approval", "record of what fired").
+
+*The leak home's scope missed.* The term wasn't confined to =sentry.org=.
+=wrap-it-up.org= said "a crashed fire", and =todo-cleanup.el= and its test both
+said "every sentry fire" — all three naming a sentry cycle. Renaming only
+=sentry.org= would have split the vocabulary across files. Found by grepping
+every file that mentions sentry, then re-grepping without a context window after
+the first pass truncated short-line matches and hid them.
+
+Verified: exactly 3 verb instances left in sentry.org, no placeholder leaked, the
+digest commit template now reads =<date> <time> cycle=, capitalized plurals
+handled, lint 0/0 on sentry.org, suite green (exit 0 — load-bearing here, since
+=todo-cleanup.el= and its test are under test). The two =wrap-it-up.org= lint
+findings are pre-existing and identical at HEAD.
+
+Committed =f3f5bfd=, pushed, velox fast-forwarded and verified. Notified home
+(with the leak it hadn't seen) and =.emacs.d=. Closed the task =DONE=.
+
+Four commits this session, all pushed, both daily drivers current, inbox at zero.
+
+** 20:00 — Roam inbox zero, then the adversarial-review design
+
+Roam scan: 3 items, 1 claimed (=rulesets:= prefix), 2 unowned gear links left
+for Craig. Filed the claimed one, removed it from roam under capture-guard +
+roam-write lock, triggered =roam-sync=. A local =.emacs.d= FYI also cleared.
+
+The claimed item: "code reviews must occur before every commit an agent does,
+and they should be hostile reviews from a subagent without the agent's context."
+
+Craig's decisions: *adversarial* rather than hostile (he took my push-back that
+an agent told to attack manufactures findings), plus a requirement I had missed —
+a re-review loop that runs until the reviewer approves — and that the rules must
+not contradict afterward. Unopposed recommendations I proceeded on: dispatch on
+every commit with the reviewer's own gate deciding triviality, and the flow lands
+in the =publish= skill.
+
+Wrote it into three files: =publish/SKILL.md= Step 1 (dispatch contract,
+adversarial-with-substantiation, the loop, bounds), =review-code/SKILL.md= (the
+two levels of dispatch, the adversarial contract, re-review mode), and
+=claude-rules/subagents.md= (a new Isolation Override section, since three
+separate size rules there said don't dispatch small work).
+
+** 20:30 — Dogfooded it, and the reviewer found nine things
+
+Ran the new flow on its own diff: dispatched an isolated adversarial reviewer
+with the diff plus a one-line claim, withholding everything else. It returned
+REQUEST CHANGES with seven Important and two Minor, every one substantiated. I
+checked each against the files rather than accepting them, and all nine were
+real:
+
+1. =Skipped= from Phase 0 is not =Approve=, so trivial diffs dead-ended at a gate
+ with no defined pass.
+2. =no-approvals.org= line 73 (the actual execution step) still described the old
+ inline unbounded flow; I had only updated the preamble at line 49.
+3. =work-the-backlog.org= and =sentry.org= — the unattended callers — had no
+ receiver for "stop and surface to the user". The speedrun routes to
+ work-the-backlog, not the file I updated.
+4. The Step 2 exception still said the review runs "when it applies", which my
+ rewrite had made false.
+5. *The sharpest one.* Withholding the ticket/plan makes the author's claim
+ self-certifying and strands =review-code='s Intent-vs-Delivery criterion — the
+ one aimed at exactly the inherited-scope error this gate exists to catch. A
+ ticket is not the author's model; it is the independent record of what was
+ asked. I had not considered this.
+6. The loop turned on the verdict token, so a single Minor could burn all three
+ rounds and escalate.
+7. My override said it "doesn't relax the Prompt Contract" while field 2 of that
+ contract says paste your context verbatim — which would destroy the isolation
+ the whole change is built on.
+8. todo.org carried an unrelated =references/= rewrite, and the new task body
+ listed as open the questions the same commit answered.
+9. =subagents.md= still says subagent output is a claim to verify, unreconciled
+ with "approval is the reviewer's to give".
+
+All nine fixed. The =references/= hunk is split out as =3a933a2=. Round 2 sent
+back to the same reviewer, which is the loop working as designed.
+
+** 21:30 — The loop closed at three rounds
+
+Round 2 (three findings): =review-code='s adversarial contract still said two
+inputs, so the round-1 fix landed in =publish= but not in the text the reviewer
+reads — the two files disagreed at the one seam the change was about. A
+count/list regression my own fix introduced ("exactly two things" over a
+three-item list). And a fourth verdict I had missed entirely: =review-code=
+emits =Needs Discussion=, which is exactly what an adversarial reviewer reaches
+for on an architectural objection, and the flow had no handling for it.
+
+Fixing =Needs Discussion= reproduced round 1's finding 3 one level down, which I
+caught myself: all four unattended parking clauses keyed on "the loop hits its
+bound", and a first-round =Needs Discussion= is not a bound hit. Rewrote all four
+to trigger on "the review can't reach approval" with the three causes named, and
+asked the reviewer to verify that independently rather than take it from me.
+
+Round 3: APPROVE, with one Minor — a recurrence of the count/list mismatch, since
+my fourth-verdict fix left the lead-in saying "three outcomes" above four
+bullets. Fixed the numeral. The committed diff therefore differs from the
+approved one by exactly that word, which the flow's own Minor-only rule permits
+rather than spending a fourth round.
+
+The reviewer also verified things I had not asked about and would not have
+checked: that =failed= is already a legal outcome slug in work-the-backlog's
+metrics table, that the three workflow mirrors carry identical blob hashes rather
+than merely similar text, and that no fifth verdict token exists anywhere in
+=review-code=. It filed one follow-up correctly rather than fixing it in-diff —
+=start-work.md= Phase 7 still summarizes the publish flow instead of pointing at
+it, and was stale before today. Filed =[#C] :chore:solo:=.
+
+Convergence shape across the three rounds: nine findings, three, one Minor. The
+residue in later rounds was integration error from the previous round's fixes
+rather than new design problems, which is the shape the bound is calibrated for.
+
+Committed =8062460=, pushed, velox fast-forwarded and verified.
+
+Six commits this session, all pushed, both daily drivers current, inbox at zero,
+tree clean.
+
+** 2026-07-29 — Walking the remaining items, and a long lesson
+
+*Item 1, the =references/= dead link* (=5999f88=). Craig picked drop-the-link.
+The adversarial review returned =Needs Discussion= — the fourth verdict, on its
+first real use — and widened the fix twice, both correctly. My replacement prose
+said credentials "live in the rulesets repo" without naming a file, which would
+have sent readers to =calendar-reference.org=, whose three paths had been dead
+since May. Now names =mcp/README.org=. And the file itself was orphaned by the
+link removal, so both copies and the empty =references/= dirs are gone. Round 2
+approved with three Low findings, all against the task record: my count was wrong
+(seven sites, not five) and =scripts/lint.sh='s =check_md_links= already exists
+for that class, missing them only because it matches markdown syntax.
+
+*Rule gap found by using the rule.* =Needs Discussion= exits to the user, but I
+never wrote what happens after the user answers. Treated it as a fresh review on
+the reasoning that Craig adjudicated and the scope changed. Flagged to Craig; not
+yet written into the skill.
+
+*The wrap-org-table bug* (=ecd5d7b=). work reported that the tool splits a logical
+row and lint passes the result. Everything I *measured* held. Everything I
+*inferred* on top was refuted, four times:
+
+1. Root cause "absence of rules in the input" — refuted by the double-run repro
+ (the tool corrupts its own correct, rule-delimited output).
+2. "Tested and killed the empty-cell hypothesis" — the fixture was confounded.
+ With no hlines the code short-circuits at =:184= before the predicate is
+ reached, so I varied the empty cell while the path that reads it was switched
+ off, got a negative, and wrote it down as settled.
+3. "The reporter's row-below observation discriminates between the paths" — it
+ doesn't; both produce that signature. Claimed twice.
+4. "A static scan can't see primary-path exposure, needs simulation" — wrong, and
+ worse, I sent it to work, who built on it. Then my *corrected* advice (run the
+ predicate over rule-delimited groups) was also wrong: work implemented it and
+ showed it can't discriminate at any threshold, with worked examples where the
+ same structural signature has opposite correct verdicts.
+
+Three corrections sent to work, plus a fourth acknowledging their disproof. The
+review loop *bounded out* at three rounds — the first bound-out under the new
+rule, working as designed. Craig adjudicated by stripping the task to Verified /
+Two-fixes-that-work / Open-questions, which is the right shape: the measurements
+were never the problem.
+
+The fix that survived is work's read, not mine: check idempotence (reflow twice,
+diff) rather than build a detector. It's true by construction and needs nobody to
+decide what a group means.
+
+*The lesson, stated plainly.* Every refutation across three rounds landed on
+inference, never on a measurement. I ran experiments and then over-read them,
+repeatedly, at full confidence. work — after I'd sent them three wrong analyses —
+labelled their own uncertain number as a floor on a population they couldn't
+cleanly define, unprompted. That discipline is the thing to copy.
+
+I also found the 2026-07-27 note where I'd already observed this exact failure
+and left it in a session summary instead of filing it. work's framing: a correct
+observation recorded and then read as fine is the same failure as a green check
+on a corrupted table.
diff --git a/inbox/lint-followups.org b/inbox/lint-followups.org
index f63470c..9d2bd8f 100644
--- a/inbox/lint-followups.org
+++ b/inbox/lint-followups.org
@@ -1,17 +1,18 @@
* 2026-07-20 Mon — Task-review health: 1 top-level [#A]/[#B]/[#C] tasks unreviewed for >30 days (daily review may have slipped)
-* lint-org follow-ups — todo.org (2026-07-25)
-** TODO link-to-local-file — Link to non-existent local file "working/hook-fail-open/validate-el.diff" (line 2066)
-** TODO misplaced-planning-info — Misplaced planning info line (line 2055)
-** TODO link-to-local-file — Link to non-existent local file "working/hook-fail-open/pre-commit.diff" (line 2050)
-** TODO misplaced-planning-info — Misplaced planning info line (line 2035)
-** TODO org-table-standard — table violates the org-table standard: no closing rule; missing rule between rows — wrap-org-table.el reflows it (line 141)
-** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 299)
-** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 302)
-** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 309)
-** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 317)
-** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 320)
-** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 329)
-** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 427)
-** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 586)
-** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 595)
+* lint-org follow-ups — todo.org (2026-07-29)
+** TODO misplaced-heading — Possibly misplaced heading line (line 2263)
+** TODO link-to-local-file — Link to non-existent local file "working/hook-fail-open/validate-el.diff" (line 2239)
+** TODO misplaced-planning-info — Misplaced planning info line (line 2228)
+** TODO link-to-local-file — Link to non-existent local file "working/hook-fail-open/pre-commit.diff" (line 2223)
+** TODO misplaced-planning-info — Misplaced planning info line (line 2208)
+** TODO org-table-standard — table violates the org-table standard: no closing rule; missing rule between rows — wrap-org-table.el reflows it (line 314)
+** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 472)
+** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 475)
+** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 482)
+** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 490)
+** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 493)
+** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 502)
+** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 600)
+** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 759)
+** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 768)
diff --git a/todo.org b/todo.org
index d666170..9d47566 100644
--- a/todo.org
+++ b/todo.org
@@ -134,65 +134,6 @@ Fix is to point Phase 7 at the =publish= skill rather than restate the flow, whi
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.
-** 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.
-
** TODO [#C] Two lint defects at the template source :chore:
:PROPERTIES:
:LAST_REVIEWED: 2026-07-28
@@ -303,19 +244,6 @@ 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.
-** 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.
-
** VERIFY [#B] A SCAN FAILED source must not advance its sentinel (engine, not the plugin)
:PROPERTIES:
:LAST_REVIEWED: 2026-07-24
@@ -2334,3 +2262,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.