-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture Improvement Report
William Smith edited this page Jul 14, 2026
·
2 revisions
Scope: internal/* + cmd/axis. All statuses below are verified against
main source, not copied from the original proposals.
Legend: Problem → Cause → Verify → Shipped status.
Status: RESOLVED / merged in PR #225.
-
Problem:
internal/safetyandinternal/executionimported advisory-layer packages frominternal/knowledgeandinternal/events. -
Cause:
safety.Checkaccepted*knowledge.ClusterKnowledge; guarded execution emitted events directly and built execution context directly. -
Verify:
internal/safety/blocker.gonow importsinternal/models, andinternal/execution/guarded.gocontains noeventsorknowledgeimports.go list -deps ./internal/safetyand./internal/executionshow no such upward dependencies. -
Shipped:
safety.Checktakes*models.ClusterSnapshotdirectly. Execution defines the consumer-sideExecutionEventSinkinterface andExecutionContextBuilderfunction type onGuardedExecutionRequest. L5 callers (agent,api,daemon, andcmd/axis) injectevents.GuardedExecutionSink{}andknowledge.ExecutionContextJSON. This is intentionally not the originally proposed singleClusterViewinterface. -
reservationno longer importsevents; reservation lifecycle events use a package-neutralEventEmittercallback configured by the events package.
Status: RESOLVED / merged in PR #223.
- Problem: Listener callbacks previously created an unbounded goroutine fan-out during event bursts.
- Cause: Dispatch was performed independently for each listener/event.
-
Verify:
internal/events/events.godefinesdispatchWorkers = 8,dispatchQueueSize = 256, a buffereddispatchQueue, and fixed workers.enqueueDispatchdrops the oldest queued advisory job when full rather than blocking the event producer. - Shipped: The process-global compatibility bus remains, but listener dispatch now uses the bounded eight-worker pool and buffered queue. Listener callbacks remain advisory and are isolated from the execution critical path.
Status: RESOLVED / merged in PR #223.
- Problem: Snapshot and metadata readers could block behind refresh work protected by the daemon mutex.
-
Cause: Readers accessed mutable cached state under
d.mu. -
Verify:
internal/daemon/daemon.godefinesatomic.Pointer[models.ClusterSnapshot]and an atomic immutable metadata publication pointer.Snapshot()andMeta()load published state without acquiringd.mu; refresh writers continue to serialize state mutation. -
Shipped: Refresh publishes cloned snapshots and immutable metadata
atomically.
Snapshot()clones the loaded snapshot for caller isolation, whileMeta()composes metadata from atomic state plus concurrency-safe mesh, ledger, and pending-latency reads.
Status: RESOLVED / merged in PR #223.
- Problem: Reservation persistence could hold the in-memory ledger mutex while marshaling and writing the complete JSON file.
- Cause: The persistence critical section combined in-memory mutation, serialization, and disk I/O.
-
Verify:
internal/reservation/persist.gosnapshots entries underl.mu.RLock()and releases that mutex beforewriteSnapshot; the file lock still serializes persistence andpersist.WriteFileAtomicpreserves atomic replacement. -
Shipped: Reservation state is copied while protected, then marshaled
and atomically persisted under the file lock with the in-memory mutex
released. The narrowed lock scope is covered by
internal/reservation/persist_lock_test.go.
Status: RESOLVED / merged in PR #223.
- Problem: Fact-collector subprocesses lacked caller-context cancellation; streaming/provider HTTP operations needed explicit lifecycle handling; and user-influenced webhook/MCP URLs lacked basic outbound validation.
-
Verify: Fact collection uses
exec.CommandContextthroughout the local collector, and guarded stash restoration uses the caller context. Webhook and MCP URL setup callsnetutil.ValidateOutboundURL. -
Shipped: The streaming chat and Ollama clients intentionally retain
Timeout: 0; their request contexts control cancellation so a healthy long-running stream is not cut off by a short client timeout. The currentmainsource also uses a context-boundedhttp.Client{}for thellmrouterclassification request (Classifyapplies its configured context budget), rather than a client-level 30-second timeout. -
netutil.ValidateOutboundURLrequireshttporhttpsand a non-empty host. In the current shipped source it also resolves hosts and rejects loopback, private, link-local, and unspecified addresses unless explicitly allowlisted withnetutil.AllowInternalHost. This preserves explicit self-hosted/private deployment opt-in; it is not accurate to describe the shipped implementation as scheme/host-only. - The hardening remains intentionally minimal: no general outbound-host allowlist is imposed beyond the explicit internal-host opt-in.
-
L2-snapshot.pumlhad stale “single cache lock” wording; it now describes atomic snapshot/metadata publication and lock-free reads. -
L5-advisory.pumlhad stale “unbounded callback goroutines” wording; it now describes the fixed worker pool and bounded dispatch queue. -
Event-Bus-and-Webhooks.mdcontains no clear contradiction with the shipped bounded dispatch implementation.