From 73e8c01fa49f48766a283e2db933e38951f656ab Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Wed, 29 Jul 2026 06:38:34 -0500 Subject: chore: archive completed work and refresh the lint follow-up queue Three finished tasks moved to Resolved: the sentry cycle rename, the references/ link removal, and the parked telegram down-is-launch fix. The lint sweep re-ran and its judgment items were refreshed. --- ...-adversarial-review-flow-and-telegram-fixes.org | 514 +++++++++++++++++++++ inbox/lint-followups.org | 31 +- todo.org | 141 +++--- 3 files changed, 599 insertions(+), 87 deletions(-) create mode 100644 .ai/sessions/2026-07-29-06-36-adversarial-review-flow-and-telegram-fixes.org 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 =