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.
69 lines
1.9 KiB
Markdown
69 lines
1.9 KiB
Markdown
---
|
|
title: Entities Contain Only Enterprise Business Rules
|
|
impact: CRITICAL
|
|
impactDescription: enables reuse across applications, prevents coupling
|
|
tags: entity, business-rules, domain, purity
|
|
---
|
|
|
|
## Entities Contain Only Enterprise Business Rules
|
|
|
|
Entities encapsulate enterprise-wide business rules that would exist regardless of automation. They must not contain application-specific logic, persistence code, or UI concerns.
|
|
|
|
**Incorrect (entity mixed with infrastructure concerns):**
|
|
|
|
```typescript
|
|
class Invoice {
|
|
id: string
|
|
items: LineItem[]
|
|
status: InvoiceStatus
|
|
dueDate: Date
|
|
|
|
calculateTotal(): Money {
|
|
return this.items.reduce((sum, item) => sum.add(item.amount), Money.zero())
|
|
}
|
|
|
|
isOverdue(): boolean {
|
|
return this.status === InvoiceStatus.Unpaid && new Date() > this.dueDate
|
|
}
|
|
|
|
markPaid(): void {
|
|
this.status = InvoiceStatus.Paid
|
|
database.invoices.update(this) // Entity cannot be tested without database
|
|
emailService.send(this.customerEmail, 'Payment received') // Coupled to email system
|
|
}
|
|
}
|
|
```
|
|
|
|
**Correct (entity contains only business rules):**
|
|
|
|
```typescript
|
|
class Invoice {
|
|
id: string
|
|
items: LineItem[]
|
|
status: InvoiceStatus
|
|
dueDate: Date
|
|
|
|
calculateTotal(): Money {
|
|
return this.items.reduce((sum, item) => sum.add(item.amount), Money.zero())
|
|
}
|
|
|
|
isOverdue(): boolean {
|
|
return this.status === InvoiceStatus.Unpaid && new Date() > this.dueDate
|
|
}
|
|
|
|
markPaid(): void {
|
|
if (this.status !== InvoiceStatus.Unpaid) {
|
|
throw new InvalidOperationError('Invoice already processed')
|
|
}
|
|
this.status = InvoiceStatus.Paid
|
|
}
|
|
}
|
|
```
|
|
|
|
**Benefits:**
|
|
- Entity can be used in billing system, reporting system, mobile app
|
|
- Business rules tested without database or email setup
|
|
- Rules documented in one place, not scattered across application
|
|
|
|
Reference: [Clean Architecture - Entities](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)
|