Three independently ugly bugs that all fed the same log flood:
* Electron's `WebFrameMain.send` writes every "Render frame was disposed"
to stderr via `console.error`. When the worker fans a second
agent-event past a reloading renderer, we produced thousands of
identical lines and stalled the main loop on stderr I/O. Added a
`mainFrame.detached` guard so the send is short-circuited before
Electron's own catch block fires, and installed a targeted filter in
`suppress-warnings.ts` (now actually imported from the entry point)
so the specific `console.error("Error sending from webFrameMain:"…)`
noise never reaches the terminal. Any other payload still surfaces.
* `dispatchAgentMessage` accepted the human-reader sentinel as a wake
target when an agent replied to `__board__` — `wakeAgentIfPossible`
then called `db.getAgent("__board__")`, threw, and the dispatcher
kept repeating on every send. Strip `HUMAN_SENDER_ID` at
`resolveMessageWakeTargets`, and keep a defense-in-depth guard in the
heartbeat so a stray caller (future automation fan-out) cannot
reintroduce the crash.
* Pi's skills loader rejects any SKILL.md whose frontmatter `name`
diverges from its parent directory. The four bundled `my-writing-style`
copies declared `name: writing-style-skill`, so every install emitted
a warning and dropped the skill. They also lived as orphan gitlinks
(mode 160000 without .gitmodules), and the global `*.md` gitignore
hid every other SKILL.md from version control. Fixed the frontmatter
to match the dir, converted the dangling submodules to plain trees,
and allowlisted `builtin-skills/**/*.md` so a fresh clone carries the
catalogue instead of silently losing it.
1.9 KiB
1.9 KiB
title, impact, impactDescription, tags
| title | impact | impactDescription | tags |
|---|---|---|---|
| Avoid Hybrid Data-Object Structures | MEDIUM-HIGH | prevents worst-of-both-worlds maintenance burden | obj, hybrid, structure, design |
Avoid Hybrid Data-Object Structures
Hybrids are half object, half data structure. They have functions that do significant things, AND they have public variables or accessors that expose internal structure. Avoid creating these monstrosities.
Incorrect (hybrid structure):
// Half object, half data structure
public class Employee {
// Public data like a data structure
public String name;
public double salary;
public List<String> skills;
// Behavior like an object
public void promote() {
this.salary *= 1.1;
notifyHR();
}
public void addSkill(String skill) {
this.skills.add(skill);
updateTrainingRecord();
}
}
// Client code can bypass behavior
employee.salary = employee.salary * 1.5; // Skips notifyHR()
employee.skills.clear(); // Skips updateTrainingRecord()
Correct (choose one approach):
// Pure object - hide data, expose behavior
public class Employee {
private String name;
private Money salary;
private SkillSet skills;
public void promote(PromotionDetails details) {
this.salary = salary.increase(details.getRaisePercentage());
notifyHR(details);
}
public void addSkill(Skill skill) {
this.skills.add(skill);
updateTrainingRecord(skill);
}
public EmployeeReport getReport() {
return new EmployeeReport(name, salary.getAmount(), skills.list());
}
}
// OR pure data structure - no behavior, expose data
public record EmployeeData(String name, double salary, List<String> skills) {}
Hybrids make it hard to add new functions AND hard to add new data structures.
Reference: Clean Code, Chapter 6: Objects and Data Structures