Files
AgentCompany/builtin-skills/software-engineer/clean-code/references/obj-avoid-hybrids.md
T
ZacharyZhang-NY e74dafcd36 Silence render-disposed spam, unbreak bundled skills, drop board wake
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.
2026-04-20 08:13:48 -04:00

70 lines
1.9 KiB
Markdown

---
title: Avoid Hybrid Data-Object Structures
impact: MEDIUM-HIGH
impactDescription: prevents worst-of-both-worlds maintenance burden
tags: 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):**
```java
// 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):**
```java
// 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](https://www.oreilly.com/library/view/clean-code-a/9780136083238/)