Skip to content

docs: bring TECHNICAL_DESIGN.md back in line with the code - #99

Merged
serkanalgur merged 4 commits into
mainfrom
docs/fix-technical-design-architecture
Sep 28, 2026
Merged

serkanalgur merged 4 commits into
mainfrom
docs/fix-technical-design-architecture

Conversation

@serkanalgur

@serkanalgur serkanalgur commented Sep 28, 2026 •

Copy link
Copy Markdown
Owner

Brings TECHNICAL_DESIGN.md back in line with the code. The document was
written before implementation against a "Design Phase" v1.0 target, and had
been touched exactly once since — a single deleted line. An audit of 40
verifiable claims found 20 wrong, 18 correct and 2 unverifiable.

Roadmap, Benchmarks and the solution-comparison appendix were removed in #98.
This covers the rest.

§9 Configuration — the dangerous part

The example config told users to set three blocks that were deliberately
deleted from NexusConfig, and src/types.ts carries a 25-line comment on
each explaining why. Anyone who followed the doc and wrote
memory.enabled: false had it silently ignored while the doc said otherwise.

memory, security and communication are now listed under "Blocks that do
not exist" with the reason for each removal, rather than presented as
settings. Also dropped: agents.defaultRole, agents.spawnDelay and
selfHealing.backoffMultiplier, each with its reason. Added the five blocks
that were missing entirely: models, notifications, gitFlow, effort
and customRoles. taskTemplates and agentRoles are gone — the real
counterparts are customRoles (an array) and TEMPLATES in
src/templates.ts.

A new §9.3 collects the constructor-only settings, which previously had
nowhere to live.

§4 Core Concepts — six interfaces that never existed

MessageBroker, CostRouter, SelfHealingManager, SharedMemoryStore,
ComplexityAnalyzer and AgentSpawner are not in the codebase. §4 now
describes what is there: MessageRouter + MessageStore +
StateBroadcaster, orchestrator.selectBestModel() + CostForecaster,
handleFailure() + HealthMonitor, PersistentMemoryStore,
analyzeComplexity(), spawnAgent(). PriorityQueue<DAGNode> does not
exist either. DAG.estimateTotalCost() is not a method; the eighth one is
isComplete().

The parts that were already right are untouched: DAGNode, DAG, MessageType,
MemoryScope, MessageMetadata, BudgetConstraint, ComplexityScore,
MemoryEntry.

§5, §6, §8 — architecture, modules, API

The .use(ParallelExecution({...})).use(...) chaining and the static
NexusOrchestrator.preset() were never implemented. The real surface is
moduleRegistry.register({ name, description, version, setup, teardown, tools, hooks }), with presets reached through configManager.applyPreset()
or the separate preset tool. NexusModule now matches src/modules.ts,
including that setup receives a ModuleContext and hooks are
{ event, handler } pairs. All six class X implements NexusModule blocks
in §6 were fiction and are replaced with the real method bodies.

§8.2 listed 10 tools under nexus_* names; all 10 were wrong, and 3 of them
(nexus_send, nexus_broadcast, nexus_request_review) never existed. It
now lists all 48 registered tools by their literal editor.add() names.
nexus_status and friends are not registered names — the plugin is in the
nexus namespace and the tools are status, agents, costs and so on.

§8.3 mixed two different command sets. The server's handleCommand accepts 6
cases; the TUI has its own 7. They are now separate, and the 4 phantom
commands (cancel, retry, memory, help) are gone.

§5.3 also gains the 14 subsystems that were missing: teams, goals, todos,
history, roles, forecast, astgrep, performance, git-flow, custom roles,
skills install, config hot-reload and the TUI.

The riskLevel divergence this surfaced — fixed in the same PR

Correcting §4 created a visible disagreement: §4 said low | high, §7 said
three levels, and the code backed §7. So the doc was corrected by changing
the code.

src/types.ts now types riskLevel as 'low' | 'high', and
src/templates.ts defaults to 'low'. The removed 'medium' was read by
nothing: the only consumer of Task.complexity is forecastTask, which uses
overall alone, and the one place a riskLevel is formatted into a string
reads the score analyzeComplexity just returned rather than the template's.
A template task declared itself medium risk and it moved no model choice and
no cost estimate.

test/effort-selection.test.ts pinned that claim and is rewritten to assert
the surviving range — the mapping is a function of overall and not an input
at either end, which is what the original was written to protect.

§8.2 is now pinned to the registry

The table is correct but nothing held it there, and README is already pinned
by an existing test, so §8.2 could drift apart from it independently. A new
test compares it 1:1 against registeredTools() in both directions and
rejects the nexus_ spelling specifically — the original table used it
throughout, and a count comparison would have passed on a table of wrong
names. Mutation-checked both ways.

Version 2.13.2 for the public type narrowing.

Verification

typecheck and lint clean. bun test 1335 → 1337. Claims were checked against the source rather than recalled:
the tool table is extracted from src/index.ts and matches 48/48 with no
duplicates and nothing missing on either side; removed blocks are confirmed
absent from NexusConfig; the six replaced interfaces are confirmed present
under their real names.

…uced

The type admitted 'medium' and `src/templates.ts` emitted it, but nothing
read it. `analyzeComplexity` assigns only 'low' or 'high', and the sole
reader of a `riskLevel` value anywhere in `src/` is the spawn tool's
display string, which formats the score `analyzeComplexity` just returned
rather than the template's. `Task.complexity` reaches exactly one
consumer, `forecastTask`, which uses `overall` alone.

So a template task declared itself medium risk and it changed no model
choice and no cost estimate. #89 removed the 15-point arm that would have
consumed it and left the type three-wide, which is what left the doc and
the code disagreeing.

`riskLevel` is now 'low' | 'high' and the template default is 'low'. The
`effort-selection` test that pinned the old claim is rewritten to assert
the surviving range: the mapping is a function of `overall` and not an
input at either end, which is the property the original test was written
to protect.
The §8.2 table is the one a reader following the API section lands on, and
it is now correct — but nothing held it there. README is pinned to the
registry by an existing test; §8.2 is a second table listing the same 48
tools by their bare names, and the two could drift apart independently.

The comparison strips the `nexus` namespace off the registered names
because §8.2 documents the literal `editor.add()` `name` values, which are
unprefixed. A second assertion rejects the `nexus_` spelling specifically:
the original table used it throughout, and a count comparison would have
passed on a table of wrong names.

Mutation-checked: deleting one row from §8.2 fails the 1:1 test, and
restoring a `nexus_status` row fails both it and the prefix assertion.

Version 2.13.2 for the public `ComplexityScore.riskLevel` narrowing in the
previous commit.
@serkanalgur
serkanalgur merged commit 6223f2c into main Sep 28, 2026
4 checks passed
@serkanalgur
serkanalgur deleted the docs/fix-technical-design-architecture branch September 28, 2026 06:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant