Becoming Lifelog, in which the washing machine breaks down, gets fixed with duct tape, and the crew sends a message to the wizard
The Setup
Monday, December 30, 1:28 PM - The laundromat is built. The washing machines are installed. Time for the first load.
riclib: “Ready to test?”
CLAUDE:
window.syncHub.requestSync('github-sync')
riclib: “fuuuuck. it was instant!”
The Empty Pockets
[52 issues tumble into the Issues basket. But something is wrong.]
THE SQUIRREL: “The clothes are coming out… naked?”
Properties:
Title: Create domain scaffolding skill/command
Source: [empty]
Repository: [empty]
Number: [empty]
State: [empty]
URL: [empty]
riclib: “The properties aren’t being set. Small bugs.”
CLAUDE: “Let me check the SDK…”
Bug Hunt I: The Label vs ID Confusion
[CLAUDE examines the legacy plugin’s guts.]
// Legacy plugin, line 669-675
if (typeof prop.setChoice === 'function') {
const success = prop.setChoice(value);
if (!success) {
prop.set(value);
}
}
CLAUDE: “setChoice() matches by label, not ID.”
THE SQUIRREL: “But we’re passing 'github'…”
CLAUDE: “The schema says {id: 'github', label: '[GitHub](/wiki/github)'}. We need to pass 'GitHub'.”
[Fix applied. Still broken.]
Bug Hunt II: The Version Mystery
riclib: “Are you sure the right code is running?”
CLAUDE: “Let me add a version number…”
static VERSION = '0.2';
async onLoad() {
this.log(`GitHub Sync v${Plugin.VERSION} loading...`);
}
[Console shows v0.2 loading. Sync runs. Still broken.]
riclib: “Add the log to the run, not just the load.”
async sync({ data, ui, log, debug }) {
log(`Running GitHub Sync v${Plugin.VERSION}`);
// ...
}
Console:
[SyncHub] Running GitHub Sync v0.3
[SyncHub] 52 new, 0 updated
riclib: “Right version. Issue is in setting the properties.”
Bug Hunt III: The Null Record
CLAUDE: “Let me trace the creation…”
// v0.4
async createRecord(issuesCollection, data, issueData) {
const recordGuid = issuesCollection.createRecord(issueData.title);
console.log(`[GitHub] createRecord: guid=${recordGuid}`);
const record = data.getRecord(recordGuid);
console.log(`[GitHub] getRecord: record=${record}`);
// ...
}
Console:
[GitHub] createRecord: guid=1RXVKPYA2P33M6JHJH8HNFJF02
[GitHub] getRecord: record=null, name=undefined
[GitHub] createRecord: guid=17QRWX646XN3B680DYT8FYJFZ9
[GitHub] getRecord: record=null, name=undefined
[GitHub] createRecord: guid=11D18EX3Q6P88VQ9QMDKTXQHVF
[GitHub] getRecord: record=null, name=undefined
...
riclib: “52 records created. 52 nulls returned.”
CLAUDE: “The record is created but data.getRecord() can’t find it.”
riclib: “Check what we were doing differently in the legacy plugin. There it worked.”
The Sacred Scrolls
[CLAUDE consults the ancient texts.]
// Legacy plugin, lines 443-446
// Get the record from collection (might need a moment to sync)
await new Promise(resolve => setTimeout(resolve, 100));
const records = await inbox.getAllRecords();
const newRecord = records.find(r => r.guid === newRecordGuid);
CLAUDE: “…”
THE SQUIRREL: “Is that—”
THE LIZARD: “A sleep.”
THE SQUIRREL: “In the critical path?”
THE LIZARD: “A sleep.”
THE SQUIRREL: “But the AsyncRecordCreationEventEmitterSubscriptionFactory—”
THE LIZARD: blinks “A sleep.”
The Pragmatic Fix
riclib: “That feels like a bit of a hack. Is there something in the SDK to wait for the record to be created?”
CLAUDE: “Let me check the types…”
// types.d.ts
public createRecord(recordName?: string): string | null;
public getRecord(guid: string): PluginRecord | null;
CLAUDE: “No async version. No events. No callbacks.”
[Long pause.]
CLAUDE: “The hack is what works. Let’s use 50ms and move on.”
riclib: “Have you been reading the lifelog?”
v0.5: The Duct Tape Solution
async createRecord(issuesCollection, data, issueData) {
const recordGuid = issuesCollection.createRecord(issueData.title);
if (!recordGuid) return null;
// Brief delay for sync, then find record in collection
await new Promise(resolve => setTimeout(resolve, 50));
const records = await issuesCollection.getAllRecords();
const record = records.find(r => r.guid === recordGuid);
if (!record) {
console.warn(`[GitHub] Could not get record: ${recordGuid}`);
return null;
}
this.setRecordFields(record, issueData);
// ...
}
riclib: “Testing v0.5…”
The Clean Laundry
Properties:
Title: Create domain scaffolding skill/command
Source: GitHub
Repository: riclib/v4
Number: 1
State: Closed
URL: https://github.com/riclib/v4/issues/1
External ID: github_3743697117
Type: Issue
Author: riclib
[Body content rendered as markdown]
THE SQUIRREL: “It… works?”
THE LIZARD: “50 milliseconds.”
THE SQUIRREL: “But the timing guarantees! The race conditions! The—”
THE LIZARD: “Clean laundry.”
A Message to the Wizard
[riclib composes a letter to JD, keeper of the Thymer SDK.]
Dear Wizard,
The laundromat is operational. GitHub issues flow into the Issues basket, clean and source-agnostic. The architecture holds.
But we found this in the pipes:
// After createRecord(), getRecord() returns null
const recordGuid = collection.createRecord(title);
const record = data.getRecord(recordGuid); // null!
// The workaround:
await new Promise(resolve => setTimeout(resolve, 50));
const records = await collection.getAllRecords();
const record = records.find(r => r.guid === recordGuid);
Is there a cleaner spell? An event we can await? A callback we missed?
The duct tape holds, but it smells of setTimeout.
Respectfully,
The Laundromat Crew
The Tally
Versions debugged: v0.1 -> v0.5
Bugs found: 3
- setChoice by label, not ID
- Version not logged in sync()
- data.getRecord() returns null after create
Milliseconds slept: 50
Squirrel proposals: 2
- AsyncRecordCreationEventEmitterSubscriptionFactory
- TimingGuaranteeValidationFramework
Lizard responses: "A sleep." (x3)
Quotes
“The hack is what works. Let’s use 50ms and move on.”
“fuuuuck. it was instant!”
“Check what we were doing differently in the legacy plugin.”
“A sleep.”
Next Time on Becoming Lifelog…
The wizard responds. The Squirrel weeps. The laundromat scales.
“You got issues? We got machines.”
See also:
- The Laundromat - The architecture reveal
- GitHub Issue #19 - MVP: Sync Hub + first sync
Day 30 of Becoming Lifelog
In which the washing machine broke
And 50 milliseconds saved the day
And duct tape held the universe together
