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.
52 lines
1.8 KiB
Markdown
52 lines
1.8 KiB
Markdown
---
|
|
title: Hide Data Behind Abstractions
|
|
impact: MEDIUM-HIGH
|
|
impactDescription: enables implementation changes without ripple effects
|
|
tags: obj, abstraction, encapsulation, data-hiding
|
|
---
|
|
|
|
## Hide Data Behind Abstractions
|
|
|
|
Objects hide their data behind abstractions and expose functions that operate on that data. Data structures expose their data and have no meaningful functions. Do not mix these concepts.
|
|
|
|
**Incorrect (exposing implementation):**
|
|
|
|
```java
|
|
// Exposes internal representation - clients depend on Cartesian coordinates
|
|
public class Point {
|
|
public double x;
|
|
public double y;
|
|
}
|
|
|
|
// Using it forces clients to know the implementation
|
|
double distance = Math.sqrt(point.x * point.x + point.y * point.y);
|
|
```
|
|
|
|
**Correct (hiding behind abstraction — immutable):**
|
|
|
|
```java
|
|
// Hides representation - could be Cartesian, polar, or something else
|
|
public interface Point {
|
|
double getX();
|
|
double getY();
|
|
double getR();
|
|
double getTheta();
|
|
|
|
static Point fromCartesian(double x, double y) {
|
|
return new CartesianPoint(x, y);
|
|
}
|
|
|
|
static Point fromPolar(double r, double theta) {
|
|
return new PolarPoint(r, theta);
|
|
}
|
|
}
|
|
|
|
// Clients work with the abstraction
|
|
double distance = point.getR(); // Works regardless of internal representation
|
|
Point moved = Point.fromCartesian(point.getX() + dx, point.getY() + dy);
|
|
```
|
|
|
|
**Key insight:** The abstraction is not just about using getters/setters. It is about hiding the form of the data and exposing operations that work with the abstract concept. Prefer immutable objects with factory methods over mutable objects with setters — immutable designs are easier to reason about, thread-safe by default, and less prone to bugs.
|
|
|
|
Reference: [Clean Code, Chapter 6: Objects and Data Structures](https://www.oreilly.com/library/view/clean-code-a/9780136083238/)
|