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.
68 lines
2.4 KiB
Markdown
68 lines
2.4 KiB
Markdown
---
|
|
title: Avoid Side Effects
|
|
impact: HIGH
|
|
impactDescription: prevents hidden temporal couplings
|
|
tags: func, side-effects, purity, predictability
|
|
---
|
|
|
|
## Avoid Side Effects
|
|
|
|
Side effects are lies. Your function promises to do one thing, but it also does other hidden things. Avoid unexpected changes to class variables, globals, or passed arguments.
|
|
|
|
**Incorrect (hidden side effect):**
|
|
|
|
```java
|
|
public class UserValidator {
|
|
private Cryptographer cryptographer;
|
|
private Session session;
|
|
|
|
public boolean checkPassword(String userName, String password) {
|
|
User user = UserGateway.findByName(userName);
|
|
if (user != null) {
|
|
String codedPhrase = user.getPhraseEncodedByPassword();
|
|
String phrase = cryptographer.decrypt(codedPhrase, password);
|
|
if ("Valid Password".equals(phrase)) {
|
|
session.initialize(); // Hidden side effect!
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
// Calling checkPassword twice destroys the session unexpectedly
|
|
```
|
|
|
|
**Correct (explicit behavior):**
|
|
|
|
```java
|
|
public class UserValidator {
|
|
private Cryptographer cryptographer;
|
|
|
|
public boolean checkPassword(String userName, String password) {
|
|
User user = UserGateway.findByName(userName);
|
|
if (user == null) return false;
|
|
|
|
String codedPhrase = user.getPhraseEncodedByPassword();
|
|
String phrase = cryptographer.decrypt(codedPhrase, password);
|
|
return "Valid Password".equals(phrase);
|
|
}
|
|
}
|
|
|
|
// Separate function with clear name
|
|
public class SessionManager {
|
|
public void initializeSessionForUser(String userName, String password) {
|
|
if (userValidator.checkPassword(userName, password)) {
|
|
session.initialize();
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
If you must have a temporal coupling, make it explicit in the function name: `checkPasswordAndInitializeSession`.
|
|
|
|
**When side effects are expected:**
|
|
- I/O operations (file writes, network calls, database mutations) inherently have side effects — the principle here is about *hidden* side effects, not avoiding all mutation. A function named `saveUser()` clearly signals side effects.
|
|
- Event handlers, middleware, and lifecycle hooks are designed to produce side effects. Name them to make the effect obvious.
|
|
|
|
Reference: [Clean Code, Chapter 3: Functions](https://www.oreilly.com/library/view/clean-code-a/9780136083238/)
|