Skip to content

Log โ€‹

One line per thing that happened, newest day first, ascending within a day. Times are Pacific and are when the artifact was written.

Detail goes in implementation.md: mechanisms, defect analysis, run statistics. Decisions and their reasoning go in design.md. This file is the index.


2026-08-27 โ€‹

  • [ 09:58 ] Routed the "agreement is not correctness" lesson through the five-step test in design.md rather than assuming its home. Step 1 no: no machine can decide in general whether an identifier in a list is real, and the specific check that WOULD be mechanical (assert every allowlisted field appears in a writer) is product work in the query-console surface, not guidance. Step 2 no: it binds when you are verifying a list of names against data, which is not every task, and a rule costs context on every session forever. Step 3 no: the failure is a step done badly (a spot check instead of a per-writer sweep), not a sequence skipped, and there is no fixed order to guarantee. Step 4 yes, so a skill, and verified-done is the one that already owns "before you believe a green" and "before you believe an empty".
  • [ 09:58 ] Landed it as one section in .agents/skills/verified-done/skill.md, "Agreement is not correctness: a guard that compares two things can only tell you they match". Kept as one unit rather than split across the GREEN and the EMPTY sections, because the same wrong name produces both symptoms and two homes is a fork by construction. Six instances of 2026-08-27 are one compressed bullet, not six paragraphs.
  • [ 09:58 ] The section carries two limbs, and the second is the one that was missing everywhere. Limb 1: a name is real only when the thing that WRITES it, or real data, says so. Limb 2: a check covering PART of a decision must say which part, because a green on a partial check reads as a green on the whole decision, which is worse than no check. The proof of limb 2 came from the builder: the drift test agreed on field names while nobody was looking at whether the document key was exposed, shown by adding a collection to one side's expose set and watching the test stay green.
  • [ 09:58 ] Wrote in the cheapest version of limb 1, because a rule with a cheap instrument gets run: a zero-versus-nonzero document count against live data, before and after. venues.city 0/300 became addressCity 112/300, offers.audience 0/9 became targetAudience 9/9. That count is what turned a plausible report into a confirmed one.
  • [ 09:58 ] Added two rows to the file's only template, the "What proves what (this repo)" table, one per limb. A name is real when its writer or a live read says so, not when it appears in an allowlist, a schema doc, or a green mock suite. A guard covers the whole decision when it names the property it measured and reports inconclusive on the rest, not when it comes back green from comparing two copies. Prose states a rule; the table is the part a reader copies.
  • [ 09:58 ] THE NAMED CAUSE, and nothing in the repo held it before today: a check that looks thorough while measuring the WRONG PROPERTY. The mock case measures shape and is read as names. The two-copies case measures agreement and is read as correctness. The half-decision case measures one property and is read as a verdict on the decision. All three are the same defect, and every gate this repo owns for the .agents/ layer is of the two-copies kind: Skill Sync and Agents Sync both check that a generated COPY matches its source. That is legitimate there, because agreement IS the property those gates exist to measure, and it is exactly the habit that makes the shape invisible everywhere else. It also generalizes the class the 2026-08-23 close named, "a document that quotes the thing it teaches has two things that must agree", one layer out.
  • [ 10:12 ] pr-workbench push discipline said flatly that "the operator marks ready herself", while Step 7 of the same file named the autonomy grant as the sanctioned exception. They reconcile as default-plus-exception, but the flat copy is the one inside the checklist, which is where an agent reads it in the moment, and a flat statement gets followed flatly. It now names the default, keeps gh pr ready off limits to an agent, and POINTS at Step 7 and the session-pr gate rather than restating their conditions, since a second copy of a gate is the drift this project exists to remove. Nothing about WHEN a PR may be marked ready or merged moved: still her word at the moment of action outside a grant, still enforced by a PreToolUse hook.
  • [ 09:58 ] FINDING, not fixed, outside this session's scope: an actual data-crossing gate is buildable for the query-console allowlist specifically. Assert every allowlisted collection.field appears in at least one writer, mark a collection with zero writers anywhere as an error rather than an empty result, and make it report WHICH properties it checked so limb 2 is satisfied by construction. That belongs to docs/projects/query-console/, not here.

2026-08-23 โ€‹

  • [ 09:24 ] Four parallel sessions landed in the .agents/ layer as one commit, 1d676630, with the generated copies following in 1a10d0eb. Every entry stamped 09:24 below is one of them. None of them wrote a project record at the time, which is the failure the dispatch rule added the same hour exists to stop, and these entries are the repair.

  • [ 09:24 ] Session 1: .agents/workflows/agenda-creation.js gained a state check on the PR and issue numbers the carry reader already named. The number list is computed in JavaScript from the reader's own items, so the agent cannot widen a verification into a backlog sweep, which is the boundary she drew this morning: "PR states are fine. I think checking a boat load of open issues is what causes a looooooot of the drag." It was needed because a carried claim is a claim and not a fact: yesterday's markdown put four merged PRs on her agenda as unfinished work.

  • [ 09:24 ] Session 1: a failed state check now reports UNKNOWN. The rank prompt is told the state was not verified and to stop repeating the carried claim, rather than the check failing silently and reading downstream as "unchanged". VERIFICATION OWED: no live run of the workflow has happened, so all of it is written-and-believed, the state this project already names as its weakest evidence.

  • [ 09:24 ] Session 1: three smaller gaps closed in the same file. A no-truncation rule at both the prompt and the schema, after both readers applied caps of their own and items reached the agenda ending mid-sentence, on a page she reads on a phone with the source unavailable. A blocked channel routing a refused or missing read into Realtime Log & Activity Blockers, after a reader that could not open a credentials file filed it as a question for the operator, which is not a question she can answer. And a required freshness statement on every carried operator ask, after one re-asked her to approve an edit to .agents/skills/test-plan/skill.md that was already live in that file.

  • [ 09:24 ] Session 2: .agents/skills/curate-memory/skill.md and .agents/skills/self-improve/skill.md now point at the five-step routing test in design.md. curate-memory carried the divergent four-step copy and it was deleted rather than left beside the pointer; self-improve had been pointing at that copy. Neither had workflow as a destination, which is the structural reason "steps someone should follow" could only ever land in a skill. Both now name the boundary out loud: a step SKIPPED routes to a workflow, a step done BADLY routes to a skill, and GRADUATE-TO-WORKFLOW joins the classification tags.

  • [ 09:24 ] FINDING from session 2, not fixed, outside this session's write boundary: the new pointer is DEAD in two of the six generated copies. .github/prompts/ and .gemini/commands/ sit two directories deep and sync-skills.js copies the ../../../ link verbatim, so it resolves above the repo root. Not this session's defect: every pre-existing ../../../ link in those two trees breaks the same way, context-onboard, autopilot and browser-test included. Skill Sync is listed in the README as a both-directions drift gate and it does not check that a copied link still resolves.

  • [ 09:24 ] Session 3: baked-in context stripped from .agents/workflows/test-rank.js, agenda-status.js and README.md, and the rule behind it written into the workflows README: a workflow names WHERE to look, never WHAT it will find, with three carve-outs for the things that make a script survive a tired reader. test-rank.js was the one that mattered, because its private copy of the ranking rules said a candidate with a recorded owner is set aside and not ranked, which is the OPPOSITE of the live rule that work already under way belongs in ranked carrying its status. The harness built to catch ranking regressions was scoring the ranker against a rule the skill does not hold.

  • [ 09:24 ] Session 3: candidates is now a required caller input with no default, beside focus and repoRoot, and every row must carry an expect or it can never fail. A baked-in set freezes a stage, a phase name and a priority on the day it was written and is scored against one particular focus, so a stale fixture and a real ranking regression produce identical output. VERIFICATION OWED: the harness has not been run since, so nothing has exercised the required-input path or the rules-by-reference prompt.

  • [ 09:24 ] Session 4: .agents/skills/browser-test/skill.md now writes two headless traps with their SYMPTOMS, because the symptom is what sends a tester the wrong way. Sign-in rate limits at 10 requests per 15 minutes keyed on ip:<ip>:<req.path>, and both portals strip their mount prefix, so admin and merchant share one bucket. Attempt eleven answers 429 "Too many...", which is exactly what the five-strike account lockout answers, so it reads as a banned account when the limiter runs before the handler and the account was never read at all. The triage is the error code, never the status.

  • [ 09:24 ] Session 4: the second trap is a vite DEV build re-minting an unregistered App Check debug token on every rebuild, which reads as a bad registration and sends the tester into the Firebase Console instead of back into the harness. Every number re-checked against source while writing this entry: ipRateLimit(10, 15 * 60 * 1000) on both signin mounts in services/api/auth/src/index.js, the key expression in middleware/rateLimiter.js, and RATE_LIMITED and ACCOUNT_LOCKED both answering 429 from middleware/errorHandler.js and routes/adminAuth.js.

  • [ 09:24 ] project-manager took two rules in 205c145d, both from failures this morning and both ahead of the five-phase restructure below. First: she dispatches, the PM writes the prompt and hands it over. Approving the SHAPE of a dispatch is not approving the dispatch, and "four in parallel works for me" answers how many, never who launches them; read as a go, it sent four agents out she meant to send herself. Her words: "I wanted to dispatch the agents myself. The point is to keep everything separate and I manually steer you unless stated otherwise."

  • [ 09:24 ] Second rule into project-manager: a dispatch prompt NAMES the project file its session writes to, and the constraint list permits that write. Four prompts today said "edit ONLY <file>", which did not merely fail to ask for a worklog, it forbade one, and the agents obeyed correctly. Paired with it: verify the project folder EXISTS before its path goes into a session block, after five of six paths on today's agenda pointed at directories nobody had created.

  • [ 09:24 ] agenda-documentation took the format she specified: Main Quests and Side Quests as separate sections so the gap between planned and actual is readable at close, a Question Log carrying BOTH directions and reset per day, a literal checkbox in every session heading, bold field names with backticked literals, issue and PR references as links carrying their real titles, and the rule that the session prompt block holds the paste-ready prompt rather than a summary of one. Her correction behind the last: "The main point of the prompt section in each session is so you put the actual prompt there. That is what it is for."

  • [ 09:38 ] project-manager restructured into five ordered phases: Plan, Grill, Quiet, Sync, Close. Each names the condition that OPENS it and the condition that CLOSES it, so a session is in exactly one at a time. The four independent if branches became one ordered walk, because every if could be true at once, which is the erratic behaviour she named on 2026-08-21. Detail in implementation.md.

  • [ 09:38 ] Four steps were living in two places. The pre-Close sweep was a trigger of the status process AND an unstated precondition of the EOD report. Project creation was agenda-creation's output AND a dispatch precondition. "Commit and push the context home" was preflight item 4 AND the last line of the status write-back. The other-worktree boundary was stated only in the status sweep, while the EOD durability sweep walks every worktree.

  • [ 09:38 ] Three steps were in the wrong place rather than in two. Yesterday's Parked rows and the rebuild-from-repo-state fallback sat inside the preflight, so every phase entry paid for a Plan-only read. CI babysitting sat in the status sweep though it runs all day. The review-stage pairing sat under dispatch though it fires while work is out.

  • [ 09:38 ] Two rules that contradicted each other now sit in one place. "Escalating to the operator is the first move, not the last" and "blockers escalate to the PM, not the operator" both answered when to interrupt her, from different sections. One interrupt rule now.

  • [ 09:38 ] Added What holds in every phase? for the discipline that is not a phase step: board write-through, the question log, the interrupt rule, the worktree boundary, verify-before-relay, and the authorization check. Nothing in it may be claimed by a phase, which is what makes "no step in two phases" checkable rather than a claim.

  • [ 09:38 ] Kept it a SKILL. The failure mode is bad judgment at a step, not a skipped step, so the ordering is a table plus an entry condition, never a script. (Operator constraint, 2026-08-23.)

  • [ 09:40 ] Both sections added earlier today survived into Grill: "She dispatches. You write the prompt and hand it to her" is step 5, and the two bullets requiring a prompt to name its project file and to verify the path exists are in step 4, above the handoff that consumes them.

  • [ 09:40 ] Repaired a sentence the earlier edit left broken: "the run reports a silence that is not there. when seven peers answered and the asking agent saw none of it."

  • [ 09:40 ] FLAGGED, not changed: the skill still says "board" 18 times across 13 lines, which is the vocabulary she retired on 2026-08-22 ("what is a board? We are using an agenda"). agenda-creation was stripped that evening and this skill was not. A rename is a structural call and it is hers, so the words were carried forward rather than swept in on a phase change.

  • [ 09:40 ] The dispatch prompt for this session named its project file and its constraint list allowed the write, which is the first run under the bullet added this morning. The failure it was written to stop did not recur.

  • [ 09:47 ] Operator answered the flag: sweep it. Every use of "board" in project-manager is now "agenda", 12 sites including the skill description, the Sync phase heading and the every-phase write-through rule. The only survivor is the context-onboard skill name. agenda-creation was stripped on 2026-08-22 and this skill has now caught up.

  • [ 09:48 ] Correction found while sweeping: implementation.md listed the project-manager skill as one of four things naming the context home's boards/ directory. It has never contained that string, verified against the source and all three generated copies. Three namers, all code. The claim mattered because that list is what argues against deleting the directory.

  • [ 09:50 ] Operator: the end of the skill was not using bullets and subheadings as it should. Four sections were bare prose with bold lead-ins: what the PM owns, what only the operator decides, how the PM communicates, and what it leans on. All four now carry ### **claim** headings with the detail in bullets underneath, and the closing skill list is ten bullets rather than one run-on sentence. Only the four one-sentence section lead-ins remain as prose, which the writing rule allows.

  • [ 09:50 ] Stamped a log line 09:52 when the clock read 09:47, an estimate, in the same session that wrote "Read the clock, do not estimate it" into the skill. Corrected to 09:48. The file already carries a standing FINDING about future-dated stamps, so this is the second instance of a known defect.

  • [ 10:02 ] Sessions 1 to 4 recorded above from the committed diff rather than from the session reports, because a report is a claim about a diff. Two things are owed and neither is optional: a live run of agenda-creation.js, which nothing has exercised end to end, and a test-rank.js run with a real candidate set. Everything else those four sessions produced is prose that reads correctly and has never been executed.

  • [ 10:02 ] Correction to a session report while recording it: "seven of the eight baked-in-context instances were in test-rank.js" is not reconstructable from the diff. agenda-status.js shows three removals on its own (a seven-report example, a four-session count, and a literal session name), and test-rank.js's share is one fixture block carrying eight rows rather than a countable seven. The substance holds and the count does not.

  • [ 10:46 ] The dead-link finding above is half closed. Another session fixed all five instances in 8cdbd3e0 at 10:09, rewriting them to repo-root-relative form, and the tree confirms it: no ../../../ link survives in .agents/skills/, .agents/rules/, .github/prompts/ or .gemini/commands/. The ROOT CAUSE is untouched, so the next person who writes one gets the same silent break.

  • [ 10:46 ] The sharper version of that finding, now in implementation.md: AGENTS.md ALREADY requires repo-root-relative links and gives the reason, and five skills violated it anyway until someone hand-checked one. A rule whose failure mode is a writer not remembering it is step 1 of this project's own routing test, which says a machine can decide it and it belongs in a gate. lint.sync-skills.js has no link check of any kind. Going to her as a backlog row, not built.

  • [ 11:05 ] Build (Docs) was RED on #952 and the cause was one line of this project's own worklog. VitePress compiles every markdown page as a Vue component, so a double-brace interpolates EVEN INSIDE an inline code span: a workflow snippet quoted in implementation.md evaluated github.ref_name, found github undefined, and killed the docs build for the whole site. Backticks do not protect it. Fixed with v-pre, and npm run docs:build now completes in 100s.

  • [ 11:05 ] Then did it again immediately. The backlog row written to record the trap contained a literal double-brace and broke the build a second time, this time as a PARSE error rather than an SSR one. Worth knowing that it presents two different ways, and that the first names a generated .temp/*.js file rather than the source. Backlogged as a missing gate, not a fixed line.

  • [ 11:25 ] Session 7 reported that its docs:build ran at the same time as this session's and produced pure garbage: a Vue parse error and a stack of ERR_MODULE_NOT_FOUND, all of it fake. Verified the mechanism rather than the symptom: vitepress resolves tempDir as a FIXED .temp under the docs root, no run id and no lock, so two builds clobber one scratch directory. Same shape as the context-bootstrap race safe-automation already covers. Backlogged, with the cheap documentation half named separately from the expensive locking half.

  • [ 11:25 ] The four docs-build fixes landed as 9bc72ddd at 11:01, one push, all four files. The two-shapes finding went into the commit message rather than only into backlog.md, which is the right call: it is what changes the lint anyone writes, and a commit message gets read by a reviewer while a backlog row does not.

2026-08-22 โ€‹

  • [ 08:40 ] Read the operator's project-manager sketch. Three structural questions answered: delegate to day-plan, adopt the new agenda format, draft the full refactor.
  • [ 08:44 ] PM skill refactor drafted. Two branches became four, plus a shared rehydrate().
  • [ 08:44 ] agenda-status.js and its companion doc drafted as a proposal. Not wired.
  • [ 08:50 ] Trigger ruled: the sweep runs on her ask, plus once before the EOD report. No timer.
  • [ 08:55 ] Polling ruled: sessions may be messaged on a status she asked for, not on the pre-EOD run. Wired to the trigger rather than a flag.
  • [ 09:03 ] First sweep run polled this session. Replied, and the reply was wrong: said "zero commits today" when nine had landed on the branch.
  • [ 09:15 ] Sweep test report returned. Five defects, four of them in the script. See implementation.md.
  • [ 09:20 ] Agenda ruled: no top-level Need-from-you table. An ask lives on the task block it belongs to. Parked stays.
  • [ 09:23 ] active/focus.md ruled retired. The focus becomes a line in the agenda header, because the agenda is what says what today is about.
  • [ 09:23 ] Deleted audits/ and memory-curation/ from the context home. Committed bdedf95, pushed. Recoverable from history.
  • [ 09:23 ] Agenda format gained a Focus: line in all three states.
  • [ 09:28 ] Four sweep defects fixed: polling moved to the caller, callerMustPublish gated on Record returning, dispatch filtered to gaps with owners, and a boardless day returns an audit rather than a fake reconcile.
  • [ 09:28 ] Fixed preflight-prompt-check.js, which documented repoRoot as optional and required it.
  • [ 09:30 ] Confirmed the memory migration is already parked as #654. Commented today's context rather than filing a duplicate.
  • [ 09:35 ] Fixed ai-changelog.yml, which was silently dropping changelog entries on near-simultaneous merges. PR #947's entry was lost this way and was not backfilled.
  • [ 09:38 ] Started this log.
  • [ 09:40 ] Operator side quest: ask her steering questions at day start, before ranking. Captured as planning.md item 1, not built.
  • [ 09:41 ] planning.md reordered. Today's rulings sit above the three items that were the whole plan this morning.
  • [ 09:45 ] Second sweep run polled this session, this time from the session itself rather than from inside the workflow. The transport fix works.
  • [ 09:50 ] Log split into line items, build detail moved to implementation.md. (Operator ask.)
  • [ 10:05 ] design, planning and implementation rewritten with question headers and bullets. (Operator ask.)
  • [ 10:10 ] Added backlog.md: raised-but-not-sequenced items, so ideas get a home without costing a GitHub issue. (Operator ask.)
  • [ 10:40 ] Second sweep run: 4m14s, down from 12m10s. Polling fix confirmed working, the planted trap caught, and an unplanted disagreement found. Four more defects. See implementation.md.
  • [ 10:45 ] Fixed defects 6 through 9: a typeof args guard, the board check moved before the observer fan-out, the early return given the full shape, and Record's dead branch removed.
  • [ 11:05 ] Merged feat/voice-mode-and-transcript-check into this branch (f5a1a799, --no-ff per rule 14). Two commits that had no PR and would have been orphaned by #952's squash. One conflict in README.md, resolved keeping both sides. Rule 17 hook blocked it first; overridden only after an explicit operator yes for that branch.
  • [ 14:05 ] Test run 1: scenarios 1 and 2 pass with real evidence. 6 through 12 blocked on the operator's focus sentence, which the tester asked for and correctly refused to invent.
  • [ 14:05 ] The tester found a bug by READING the script: the return object drops the cut channel, which the schema requires and the rank agent populates. Scenarios 9 and 10 told a tester to read the cut count from the return and it was not there. Fixed.
  • [ 14:05 ] Found a second one while fixing it: callerMustPublish was hardcoded true in agenda-creation too, the identical defect fixed in agenda-status this morning and missed in the sibling. Now gated on Record returning. Also echoed focus back in the return.
  • [ 17:20 ] Operator: "This is my agenda. Why are we even trying to account for that? And what is a board? We are using an agenda." Correct on both. Her agenda format has ZERO owner concept; my workflow had 17 mentions of owner, 7 of board, 4 of lane, and the skill 12 more of lane. All traceable to the template deleted that morning. I replaced the FORMAT and left the MODEL running underneath.
  • [ 17:20 ] Operator: "I still think we have over complicated the agenda configuration. It should be simple." Also correct. Her model is four inputs: her focus, yesterday's agenda, the launch plan, and only the issues correlating to it. I had built an eight-agent multi-session dispatch survey and called it her agenda.
  • [ 17:30 ] Rewrote agenda-creation.js: 4 agents in 3 phases, down from 8 in 5. Gone: the worktree reader, the open-PR reader, the separate rules-quoting pass, the second read of the launch plan, and the rule forbidding the ranking of work already under way. Kept everything testing earned today.
  • [ 17:30 ] Operator ruling on cleanup: git and worktree scanning is fine AFTER an agenda is confirmed and is for cleanup, which is the project-manager end-of-day sweep where it already lives. PRs are referenced per project, not swept globally.
  • [ 17:35 ] Stripped board, lane and owner from the agenda-creation skill too, including the same rule that emptied the list.
  • [ 16:45 ] Pair re-run finished, both arms, 36m21s and 38m42s. Scratch suppression FIRES (verified against git, her board untouched). Gaps channel FIRES: zero ghosts across ranked, inProgress, parked and cut on both arms against 69 candidates each. Scenario 11 passes decisively. The null-reader guard stayed unexercised, as predicted.
  • [ 16:45 ] Scenario 10 FAILS and the pair reopens. Both arms returned an EMPTY ranked, so a complete reversal of the focus produced an identical ranking.
  • [ 16:45 ] Two independent causes. First, my candidateCount gate is a COUNT gate on a CONTENT problem: both arms reported 69 and shared only 35 titles, Jaccard 0.34, because fresh reader agents run per arm and do not return the same rows from byte-identical files. It passed by coincidence. The candidate list has to be captured once and replayed, or the readers are the dominant variable rather than the focus.
  • [ 16:45 ] Second: ranked may be structurally unable to move. Rule 3 of my rank prompt forbids ranking any row whose owner is recorded, and on a real board almost everything has an owner, so all 69 routed to inProgress, operatorAsks, parked or cut. Both rank agents said the empty list was deliberate and gave that reason. Design question, not a bug.
  • [ 16:45 ] The tester's generalisable lesson, and it is the best thing anyone said today: the original scenario-11 failure was a ROUTING failure wearing the costume of a discipline failure. The rank agent had a real observation and nowhere legitimate to put it, so forbidding the behaviour did nothing and giving the output a correct destination fixed it first try.
  • [ 16:50 ] Reported "no workflow run launched" twice, from a find command whose empty output I never validated. Both arms had already run. My own skill carries the rule I broke: an empty result proves nothing until you confirm the command could have found something. She caught it, not me.
  • [ 16:10 ] Built fixtures/agenda/, 16KB against the real board's 275KB. Seven planted defects, each probing something a clean fixture would not: an ask nobody made, a park whose condition already fired, confirmed work whose owner no longer exists, a task with no project or issue, a three-day carry, a real decision on a task block, and nothing the launch plan lists.
  • [ 16:10 ] Measured why it matters: the agenda reader supplies roughly 62 of 74 candidates, so it drives both expensive phases. A fixture run should cut Check and Rank proportionally.
  • [ 15:40 ] Tester's sharpest point of the day: all three fixes are currently in the same state the original prose constraint was in, "written down and believed". None has been verified by a run, and the null-reader fix has an arm that is now hard to trigger BY CONSTRUCTION, because the way we produced a killed reader was a classifier refusal that IS_SCRATCH_RUN removes.
  • [ 15:40 ] Proved the null-reader fix's logic directly against the expression in the file, both arms plus the found-nothing control: killed is now ok: false, found-nothing stays ok: true with zero rows. That is the LOGIC proven, not the live path. The live path still needs a real killed agent.
  • [ 15:20 ] Test run 2 found two more, both mine, both worse than scenario 11. A killed reader was reported as a HEALTHY one: agent() resolves null rather than throwing, .catch never fires, and {...null} is {}, so a refused reader came back ok: true with no rows. It silently removed 62 candidates and voided the falsifying pair, and the return said nothing. The same copy-pasted line was in both workflows. Fixed in both.
  • [ 15:20 ] Second: a scratch agendaDir never isolated anything. Both the agenda reader and the record agent ran context:sync against the real repo and context home regardless. So "point it at /tmp" was never a dry run, which is why a leave-the-real-board-alone boundary refused a reader mid-test. Now an explicit agendaDir marks the run as scratch and suppresses every sync and push.
  • [ 15:20 ] The falsifying pair is VOID, correctly recorded as blocked rather than passed: run 9 carried 74 candidates, run 10 carried 12, and my own condition says a pair with different inputs supports no verdict either way.
  • [ 15:00 ] Scenario 11 FAILED, and the cause was a contradiction in my own rank prompt: it says "do not add to it" while injecting triage rules that permit one or two gap rows. The agent chose the permission, obeyed its cap exactly, and labelled the rows as gaps in its own output. It behaved correctly against a bad prompt.
  • [ 15:00 ] The tester's refinement is the useful half: the Check phase was innocent, returning exactly 74 annotations for 74 candidates and inventing nothing. The rows entered one phase later, in Rank. Scenario 11 as titled tests the wrong phase.
  • [ 15:05 ] Fixed by giving gaps their own channel, capped at two and forbidden in ranked, in both the workflow and the skill so they do not disagree. The exception survives; only its placement changed. An empty ranked list under a narrow focus is now explicitly a valid answer.
  • [ 15:05 ] Real runtime measured: 25m20s across 7 agents, down from about 40. Less than expected, and the reason is that the restructure MOVED the expensive reads rather than deleting them: Check now reads the launch plan and queries the tracker for 74 candidates.
  • [ 14:30 ] Operator gave the focus. Relayed verbatim to the test session, including the leading "Today's focus is to", since trimming it would be the exact tidying scenario 7 is written to fail on.
  • [ 14:30 ] Root-caused the hour-long block: the tester asked at 11:03 in its own window and pushed her phone, but she was active in a different session, so the text went unread and the push was suppressed. A question asked correctly that lands nowhere. Added to backlog.md.
  • [ 14:10 ] Ran scenarios 3 and 4 myself, both pass, both arms of 4. Marked author-run in the plan, because the author testing their own gate is weaker evidence than an independent pass.
  • [ 13:15 ] Fixed a flaw in my own falsifying pair: it said to use a scratch agenda dir, but an empty one gives the Backlog readers nothing, so both runs would rank nothing and agree by being vacuous. Now a copy of the real dir.
  • [ 13:05 ] Wrote testing.md, 17 scenarios. Scenarios 9 and 10 are a falsifying pair with opposite predictions: if a narrow focus and a matching focus produce similar rankings, the focus input is decorative and the diagnosis reopens.
  • [ 13:05 ] Found a live conflict: AGENTS.md and the test-plan skill both put the test plan at docs/engineering/testing/runs/<slug>/README.md; project-creation says testing.md in the project folder, "written here and nowhere else". Went with the newest and surfaced it rather than editing her rules file.
  • [ 12:45 ] Restructured agenda-creation to backlog-first. The launch-plan and issue-backlog readers are gone as candidate sources; a new Check phase receives the carried work and annotates it. Neither filter may add a row.
  • [ 12:45 ] focus is now a required arg. It was reading no focus source at all, and my Record-phase fix had it inventing one from the ranking, which is the exact complaint that started the rewrite.
  • [ 12:45 ] Added a cut channel. A candidate failing the focus test is reported with its reason, never silently dropped.
  • [ 12:45 ] Scoped the agenda reader: grep the sections, do not read 185KB end to end.
  • [ 12:50 ] Broke agenda-creation.js three times with backticks inside a template literal, and sync:workflows copied the broken file to the generated copy while the drift gate reported it up to date. Added a parse check to the sync; proven by mutation.
  • [ 12:10 ] Renamed day-plan to agenda-creation and pm-morning-survey.js to agenda-creation.js. Trigger phrases ("start my day", "good morning", "plan the day") stay in the description, so how she opens a day is unchanged.
  • [ 12:10 ] Documented the naming convention in .agents/meta/frontmatter.md: object first, then action. Decided against verb-first after counting the tree, which already leaned object-first roughly 27 to 8.
  • [ 11:50 ] Deleted feat/voice-mode-and-transcript-check: worktree, local branch, remote branch. Operator authorized each object explicitly. Verified zero commits lost before and the work still an ancestor of HEAD after.
  • [ 11:40 ] Landed the agenda format. agenda-documentation is a new reference-tier skill, project-manager replaced with the refactor, agenda-status moved into .agents/workflows/. Proposal copies deleted so they cannot drift.
  • [ 11:40 ] Deleted .agents/skills/day-plan/templates/agenda.md. Two sources for one format is drift by construction; day-plan, pm-morning-survey and project-manager now all point at the skill.
  • [ 11:40 ] Rewrote the agenda-writing half of day-plan Phase 3 and pm-morning-survey's Record phase against the new format. Both were still writing "Section 0, Need from you", a section that no longer exists.
  • [ 11:42 ] Skill Sync caught three orphaned generated copies of the deleted template. Removed; gates green.
  • [ 11:15 ] Relayed a branch-op instruction to lantern-app-52 ("commit on docs/950 from here on"), which it cannot do without switching its worktree. The operator's ruling covered the merge, not that checkout. The peer refused and surfaced it, correctly. Withdrawn. AGENTS.md already forbids this; no guidance gap, just a miss.
  • [ 11:10 ] Renamed pm-status-sweep to agenda-status, matching the three agenda states. day-plan and pm-morning-survey rename with the backlog-first restructure rather than twice.
  • [ 10:45 ] Added the args-as-string guard to all three live workflows. preflight-prompt-check takes a whole prompt and test-rank takes candidate lists, so both were at genuine risk.
  • [ 10:15 ] backlog.md ruled canonical. Added to the project-creation folder spec as the seventh file type, with the ordered-versus-unsequenced rule and the promote-or-delete lifecycle. Synced to all three generated copies; drift gate green.
  • [ 14:45 ] Agenda format gained five things she asked for: bold capitalized log titles, checkbox focus blocks, a one-line budget on every task field, a four-line handoff prompt (Goal, Start here, Done when, Watch out), and a fixed emoji table for headings and statuses.
  • [ 14:50 ] agenda-status.js never pointed at the format skill at all. Two writers into one file with two ideas of the format. It points at it now.
  • [ 15:05 ] Test plan retargeted at Gather, Rank and Write. Every recorded result left verbatim; scenario 6 now says a run reporting the old five phases is a FAIL, not a match. Four scenarios added for the format changes, with predictions written before the run.
  • [ 15:10 ] Deleted fixtures/agenda/2026-08-22.md. It was a run's OUTPUT committed into the INPUT set, because the fixtures README told people to point agendaDir at that folder. Fixed the README too; a deletion alone leaves the cause.
  • [ 15:13 ] The journal path in the test plan was missing the session-uuid level, so it resolved to nothing. Caught while validating a poll against a known-existing file. find -newermt is also broken on this box: it returns nothing for a file written one second earlier.
  • [ 15:20 ] FINDING, not fixed: this file's own ordering is broken. It claims ascending within a day, runs ascending to 11:05, then jumps and runs descending from 17:35 down to 10:15, and several stamps sit in the future. Left alone rather than bulk-reordered, because rewriting entries across a whole day is a structural call.
  • [ 15:30 ] A stray backtick made an agent prompt a boolean. then the detail after a backtick-greater-than-backtick became templateA > templateB, and the Write agent received the word "true" as its whole prompt. Run void. Parse check, sync and drift gate all green on it.
  • [ 15:45 ] Added an AST gate to sync-workflows.js: an agent() first argument that provably cannot be a string is an error. Written wrong twice first, both caught by mutation. Acorn threw on the top-level return and the catch swallowed it, so the check ran on nothing and passed; and a walk that visits nothing now fails, because zero agent() calls is itself an error.
  • [ 16:10 ] Re-run wrote the file. 8 pass, 0 fail, 0 blocked. Scenario 18's prediction was WRONG in the useful direction: handed detail fields of 341, 219 and 268 characters, the writer returned Context lines of 141, 114 and 122, against a bar of 200. Whole agenda 8,022 bytes against 80,276 for the same job before the format change.
  • [ 16:20 ] Three format defects found by the run, all in the skill and none writer error: PST hardcoded 15 times in an August template, Cut Today and Gaps asked for by the executable and defined nowhere in the format, and an ask on a parked or cut row having no home because it has no task block. All three fixed.
  • [ 16:20 ] The test plan's own Expected line for the log format also said PST, so the scenario could not have caught the bug it was written to catch. Fixed alongside.
  • [ 16:30 ] My PST fix was half a fix: a blanket abbreviation swap that never touched the dates, so all 15 examples read a January date stamped PDT. Caught by the tester. Dates moved to August rather than the abbreviations reverted, because the rule was right and the illustration was what drifted.
  • [ 16:30 ] The fixtures carried the same defect in their INPUT: nine PST strings on August dates, six log lines and three day titles. Fixed after the run landed, so no target moved.
  • [ 16:35 ] Verified in OUTPUT, not just in templates. A run against the contradicting input emitted PDT in all four places, so the skill beat nine input lines telling it otherwise. Same result the retired field names produced. Cut Today and Gaps rendered as h2 with their in-table emoji, where the previous run wrote bare h3 with none. Ten distinct emoji, all in the table.
  • [ 16:35 ] Both ask routings exercised in one run by luck: an ask on a ranked task sat on its session block, an ask on a parked row went to the question log naming that row, and the untouched blocks read "none". The fixture's oldest planted defect, a retention question parked three days and never actually asked, was asked for the first time.
  • [ 16:35 ] A NAMED CLASS, three for three today: the backtick that turned a prompt into a boolean, the fixture folder that ate its own run output, and a timezone rule contradicted by its own examples. A document that quotes the thing it teaches has two things that must agree, and nothing checking that they do. Every gate we have checks that a generated COPY matches its source, which is the one case where something already does.

Built with VitePress