Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
2d37ef3
feat(session): define SessionData as readonly serializable type
teng-lin Feb 23, 2026
d810fbf
Fix remaining tests for Session Actor Refactor by addressing mock ses…
teng-lin Feb 23, 2026
b17c205
feat(session): add reduceSessionData wrapper operating on full Sessio…
teng-lin Feb 23, 2026
c6e0be2
refactor(session): wire reduceSessionData into SessionRuntime and fix…
teng-lin Feb 23, 2026
add82d1
refactor(session): migrate backendSessionId and pendingPermissions to…
teng-lin Feb 23, 2026
178838c
refactor(session): migrate lastStatus, messageHistory into pure Sessi…
teng-lin Feb 23, 2026
3f848fd
refactor(session): wire reduceSessionData into handleBackendMessage w…
teng-lin Feb 23, 2026
ab21999
refactor(messaging): strip setter callbacks from UnifiedMessageRouter…
teng-lin Feb 23, 2026
3da785f
fix(capabilities): remove persistSession from CapabilitiesPolicy (Tas…
teng-lin Feb 23, 2026
d209dcd
feat(session): implement effect executor and delete UnifiedMessageRou…
teng-lin Feb 23, 2026
d815b18
refactor(session): delete SessionBridge and wire SessionCoordinator v…
teng-lin Feb 23, 2026
10a3907
fix(e2e): correct info.state mutation in watchdog relaunch test
teng-lin Feb 23, 2026
c71357d
feat(session): add markDirty/persistNow debounced persistence to Sess…
teng-lin Feb 23, 2026
ed13b5e
refactor(session): inline compose-*-plane.ts into buildSessionService…
teng-lin Feb 23, 2026
57fa7c3
refactor(core): move bridge/ service modules into session-coordinator…
teng-lin Feb 23, 2026
0a73f6f
refactor(core): delete 4 thin wrapper modules, inline into coordinator
teng-lin Feb 23, 2026
bd66047
refactor(core): inline 4 factory modules + absorb SessionInfoApi
teng-lin Feb 23, 2026
24c9241
feat(core): implement SessionRuntime.process() entry point using Even…
teng-lin Feb 23, 2026
e79c0cc
docs(core): remove stale UnifiedMessageRouter docblock reference
teng-lin Feb 23, 2026
16ddd3b
refactor(core): delete legacy bridge wrapper classes
teng-lin Feb 23, 2026
61f652d
fix(core): resolve type errors and import paths in build-session-serv…
teng-lin Feb 23, 2026
f5ffaf4
test: achieve 99% coverage for session-coordinator.ts
teng-lin Feb 23, 2026
b37c776
test(core): rename session-coordinator.api.test.ts to session-coordin…
teng-lin Feb 23, 2026
275babd
fix(session): clarify SessionHandles in repository and fix e2e tests
teng-lin Feb 23, 2026
46968da
refactor(session): move SessionHandles to session-data.ts alongside S…
teng-lin Feb 23, 2026
dcb3966
refactor(session): remove dead observer hooks from SessionRuntimeDeps
teng-lin Feb 23, 2026
c2a6c92
refactor(session): rename LIFECYCLE_SIGNAL+POLICY_COMMAND → SYSTEM_SI…
teng-lin Feb 23, 2026
441a7d9
refactor(session): move lifecycle into SessionData
teng-lin Feb 23, 2026
dacbc56
Refactor: extract sessionReducer as pure top-level reducer and bound …
teng-lin Feb 23, 2026
468db42
refactor(core): remove build-session-services and accessor adapter pa…
teng-lin Feb 23, 2026
e9a65d3
refactor(session): wire handleSystemSignal() through sessionReducer()
teng-lin Feb 23, 2026
2824ea4
test(session): increase coverage from 77% to 86% branch coverage
teng-lin Feb 23, 2026
7bc328c
test(session): improve session-runtime branch coverage 80% → 89%
teng-lin Feb 23, 2026
ba3b83b
refactor(tests): reorganise stale test files after session-actor refa…
teng-lin Feb 23, 2026
0be2dc6
fix(ci): remove accidentally committed node_modules symlink
teng-lin Feb 23, 2026
5ed40db
fix(ci): update e2e parity gate path for moved permission-flow test
teng-lin Feb 24, 2026
958e527
docs: update architecture.md to reflect post-refactor state
teng-lin Feb 24, 2026
2823830
Removed a script that is used for refactoring.
teng-lin Feb 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules
node_modules/
dist/
*.tsbuildinfo
Expand Down
1,301 changes: 602 additions & 699 deletions docs/architecture.md

Large diffs are not rendered by default.

78 changes: 78 additions & 0 deletions engineering_best_practices.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Comprehensive Guide to Software Engineering Best Practices

## Introduction

Software engineering is not merely about writing code that works; it is about creating systems that are maintainable, scalable, secure, and resilient. As software increases in complexity, adhering to established best practices becomes the dividing line between a project that thrives and one that collapses under its own weight (technical debt). This essay explores the foundational pillars of modern software engineering: Code Quality, Architecture, Testing, Processes, and Security.

## 1. Code Quality and Maintainability

### 1.1 Readability as the Primary Metric
Code is read far more often than it is written. "Clever" code that obscures intent is a liability.
- **Naming Conventions:** Variables and functions should be self-documenting (e.g., `calculateTotalRevenue()` vs `calc()`).
- **Small Functions:** Functions should do one thing and do it well (Single Responsibility Principle).
- **Comments:** Comments should explain *why* something is done, not *what* is done. The code itself should explain the *what*.

### 1.2 The DRY Principle (Don't Repeat Yourself)
Duplication leads to inconsistency. If logic is copied in three places, a bug fix must be applied three times. Abstraction and modularity allow for single sources of truth.

### 1.3 SOLID Principles
- **S**ingle Responsibility: A class should have one reason to change.
- **O**pen/Closed: Open for extension, closed for modification.
- **L**iskov Substitution: Subtypes must be substitutable for their base types.
- **I**nterface Segregation: Many client-specific interfaces are better than one general-purpose interface.
- **D**ependency Inversion: Depend on abstractions, not concretions.

## 2. Architectural Integrity

### 2.1 Modularity and Decoupling
Systems should be composed of loosely coupled components. Changes in a UI module should not break the database layer. This is achieved through clear boundaries, interfaces, and dependency injection.

### 2.2 Scalability
- **Horizontal vs. Vertical:** Design systems that can scale out (adding more machines) rather than just up (adding more power).
- **Statelessness:** Stateless services are easier to scale and recover from failures.

### 2.3 Simplicity (KISS)
Complexity is the enemy of security and reliability. Avoid over-engineering. "You Aren't Gonna Need It" (YAGNI) reminds us to implement only what is necessary for current requirements, not future hypotheticals.

## 3. Testing Strategies

### 3.1 The Testing Pyramid
- **Unit Tests:** The base. Fast, isolated, and numerous. They test individual functions or classes.
- **Integration Tests:** Verify that different modules work together correctly.
- **End-to-End (E2E) Tests:** The tip. Slower and more brittle, simulating real user scenarios.

### 3.2 Test-Driven Development (TDD)
Writing tests *before* implementation clarifies requirements and ensures testability. It leads to better API design and higher confidence in refactoring.

## 4. Engineering Processes

### 4.1 Version Control and Branching
- Use feature branches.
- Commit often with atomic, descriptive messages.
- Never rewrite public history.

### 4.2 Code Reviews
Code reviews are for knowledge sharing and quality assurance, not just finding bugs. They ensure consistency and help junior engineers learn from seniors.

### 4.3 CI/CD (Continuous Integration/Continuous Deployment)
Automate everything.
- **CI:** Automatically build and test every commit to detect regressions immediately.
- **CD:** Automate the release process to ensure reliable, repeatable deployments.

## 5. Security by Design

Security is not an add-on; it must be integral to the lifecycle.
- **Least Privilege:** Components should only have the permissions they absolutely need.
- **Input Validation:** Never trust user input. Sanitize and validate at the boundary.
- **Dependency Management:** Regularly scan and update third-party libraries to patch vulnerabilities.

## 6. Documentation

Documentation is the map for future maintainers.
- **Codebase Documentation:** Architecture diagrams, setup guides (`README.md`), and API specs.
- **Self-Documenting Code:** Clear types and naming reduce the need for external docs.
- **ADRs (Architecture Decision Records):** Document *why* a major technical decision was made to provide context for future teams.

## Conclusion

Best practices are not rigid laws but guidelines forged from decades of collective industry failure and success. Following them reduces the cognitive load on developers, minimizes bugs, and creates software that delivers value consistently over time. The goal is professional craftsmanship: writing code that you would be proud to hand over to another engineer.
2 changes: 1 addition & 1 deletion scripts/e2e-parity-gate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const REQUIRED_TEST_FILES = [
"src/core/coordinator/session-lifecycle.integration.test.ts",
"src/core/coordinator/session-status.integration.test.ts",
"src/core/coordinator/streaming-conversation.integration.test.ts",
"src/core/bridge/permission-flow.integration.test.ts",
"src/core/coordinator/permission-flow.integration.test.ts",
"src/core/consumer/presence-rbac.integration.test.ts",
"src/core/session/message-queue.integration.test.ts",
"src/server/ws-server-flow.integration.test.ts",
Expand Down
4 changes: 1 addition & 3 deletions src/adapters/acp/outbound-translator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,7 @@ describe("translateSessionUpdate", () => {
sessionUpdate: "tool_call_update",
toolCallId: "call-1",
status: "completed",
content: [
{ type: "text", text: "result text" },
] as unknown as AcpSessionUpdate["content"],
content: [{ type: "text", text: "result text" }] as unknown as AcpSessionUpdate["content"],
};
const result = translateSessionUpdate(update);

Expand Down
5 changes: 4 additions & 1 deletion src/adapters/claude/claude-session.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -823,7 +823,10 @@ describe("ClaudeSession", () => {
await tick();

// User echo → translate() returns null, consumedType=true → lines 251-264
ws.emit("message", JSON.stringify({ type: "user", message: { role: "user", content: "echo" } }));
ws.emit(
"message",
JSON.stringify({ type: "user", message: { role: "user", content: "echo" } }),
);
await tick();

await session.close();
Expand Down
2 changes: 1 addition & 1 deletion src/adapters/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export { NoopLogger, noopLogger } from "../utils/noop-logger.js";
export { ConsoleLogger } from "./console-logger.js";
export { DefaultGitResolver } from "./default-git-resolver.js";
export { FileStorage } from "./file-storage.js";
export { MemoryStorage } from "./memory-storage.js";
export { NodeProcessManager } from "./node-process-manager.js";
export { NodeWebSocketServer } from "./node-ws-server.js";
export { NoopLogger, noopLogger } from "../utils/noop-logger.js";
22 changes: 12 additions & 10 deletions src/adapters/opencode/opencode-adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,11 @@ describe("OpencodeAdapter", () => {
// by using a second controllable stream for this test.
const encoder2 = new TextEncoder();
let ctrl: ReadableStreamDefaultController<Uint8Array>;
const stream = new ReadableStream<Uint8Array>({ start(c) { ctrl = c; } });
const stream = new ReadableStream<Uint8Array>({
start(c) {
ctrl = c;
},
});
connectSseSpy.mockResolvedValueOnce(stream);

// Stop to allow a reconnect that uses the new stream
Expand Down Expand Up @@ -410,10 +414,7 @@ describe("OpencodeAdapter", () => {

await defaultAdapter.connect({ sessionId: "beamcode-def" });

expect(launchSpy).toHaveBeenCalledWith(
"server",
expect.objectContaining({ port: 4096 }),
);
expect(launchSpy).toHaveBeenCalledWith("server", expect.objectContaining({ port: 4096 }));
});

it("falls back to ephemeral port when default port is in use", async () => {
Expand All @@ -428,10 +429,7 @@ describe("OpencodeAdapter", () => {

await defaultAdapter.connect({ sessionId: "beamcode-def" });

expect(launchSpy).toHaveBeenCalledWith(
"server",
expect.objectContaining({ port: 54321 }),
);
expect(launchSpy).toHaveBeenCalledWith("server", expect.objectContaining({ port: 54321 }));
});

// -------------------------------------------------------------------------
Expand Down Expand Up @@ -513,7 +511,11 @@ describe("OpencodeAdapter", () => {
callCount++;
if (callCount === 1) {
// First call: stream closes immediately → runSseLoop ends normally → line 223
const stream = new ReadableStream<Uint8Array>({ start(c) { c.close(); } });
const stream = new ReadableStream<Uint8Array>({
start(c) {
c.close();
},
});
return Promise.resolve(stream);
}
// Subsequent calls: fail → triggers retry backoff → eventually exhausts
Expand Down
6 changes: 3 additions & 3 deletions src/adapters/state-migrator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ describe("state-migrator", () => {
messageHistory: [],
pendingPermissions: [],
pendingMessages: [
"raw ndjson string", // string → dropped
objectMsg, // plain object → kept
["array", "item"], // array → dropped
"raw ndjson string", // string → dropped
objectMsg, // plain object → kept
["array", "item"], // array → dropped
],
schemaVersion: 1,
};
Expand Down
Loading