유대선
프로젝트로
·트러블슈팅·2

An ops Area that quietly rendered my own memory notes as CI failures to fix

돌리다(Run) listed agent retrospectives next to real GitHub CI failures. Root cause: cluster and source are orthogonal axes and the worklist only filtered on cluster. Fix verified against the real graph, not by eye.

The Run Area (돌리다) is supposed to be a worklist of real operational items — failed CI runs, broken deploys — that you act on. It started showing things like "Two startups rendered the same terminals" and "검증 삽질 회고" in the same list. Those are notes an agent wrote into the graph as memory, not CI failures. Mixed into the actual GitHub failures, the list lied about what needed doing.

Why it happened

Two things collide in the cluster router:

The screen filtered on one axis only — node_type === "issue" && clusterOf(n) === "run" — with no check on where the node came from. So evidence.source: "agent" memory sat in an operational worklist looking like a task.

The cluster (which Area a node shows in) and the source (an external work item vs the founder's own memory) are orthogonal. An ops lens needs both filters; it only had one.

The fix, and how I checked it

Added isConnectorSourced(node) (memory sources = agent | git | manual | capture | "") and applied it where the ops Areas build their lists. The Run screen and the generic cluster screens (벌다/만들다/ 지키다) now require a connector source; 조종하다(Steer) keeps memory on purpose, because decisions and strategy the agent recorded belong there.

I didn't trust the change by reading it. I counted the real on-disk graph for my active service before and after:

startup-…-ukhf:  36 run-cluster issues  →  27   (27 GitHub CI kept, exactly 9 agent-memory dropped)
two other services:  100% memory in run  →  0

tsc --noEmit clean. The remaining Biome warnings are pre-existing on a different useEffect, not from this change — verified by stashing my edits and re-running.

The lesson worth keeping

When a list is built from a single facet of a multi-axis model, the silent failure isn't an empty list — it's a plausible wrong list. A worklist that quietly mixes memory into to-dos is worse than one that's visibly broken, because you trust it. The check that mattered was re-running the same count with the filter on, against real data, not eyeballing the diff.