arch 0001 target topology - Capsize-Games/spikeforge GitHub Wiki
ARCH-0001 target topology
Status: accepted โ implemented.
Date: 2026-09-11 ยท Issue: ARCH-0001 Phased repo split: core library, deploy targets, dashboard
Owner: Capsize Games (maintainer) ยท Depends on: plans/arch-0001-adr-repo-topology.md
Decision
Adopt the following distribution-to-import-root mapping. No two distributions
ship into the same top-level namespace. The plan's PEP 420 namespace-package
escape hatch (plans/repo_topology_plan.md ยง4) is deliberately not used.
| Distribution | Import root | Source of truth today | Owner phase |
|---|---|---|---|
spikeforge |
spikeforge |
spikeforge/ (minus any extracted subpackages) |
Phase 1 |
spikeforge-server |
server (kept) |
server/ |
Phase 1 |
spikeforge-targets |
spikeforge_targets |
spikeforge_targets, spikeforge_targets.backends, spikeforge_targets.energy, spikeforge_targets.event_runtime |
Phase 3 |
spikeforge-hub |
spikeforge_hub |
spikeforge_hub |
Phase 4 (go) |
spikeforge-dashboard (npm spikeforge-dashboard, private) |
n/a โ npm | client/ |
Phase 2 |
Core import root
The core distribution owns the import root spikeforge. The project rename
landed: the bring-up project was renamed to spikeforge and moved under the
capsize-games organization, so the core
distribution, its import root, and its repository all read spikeforge. No
alias, shim, or namespace package is used for core.
Server import root
The spikeforge-server distribution keeps the existing top-level package
server as its import root (end state: distribution spikeforge-server, import
root server). Rationale: server/ is already a top-level package and every one
of its library-importing files imports spikeforge, so keeping server avoids
churn in CI, Docker, and the WS entry point. The spikeforge-server repository
is a deliberate no-go (trigger T4 did not fire), so the server stays in this
repository as a separate distribution; no spikeforge_server/ rename is planned
and no alias package is used.
Targets, hub, and dashboard import roots
spikeforge-targetsownsspikeforge_targetsand absorbstargets,targets/backends,energy, andevent_runtime. These four subpackages move together becausetargets/backendsdepends onenergy/event_runtimeand all carry the volatile backend SDK surface.spikeforge-hubownsspikeforge_huband absorbshub.- The dashboard keeps the npm package name
spikeforge-dashboardeven though its GitHub repository and product name becomespikeforge-dashboard. The npm package staysprivate: true.
GitHub repository names
| Repository | Phase | Status |
|---|---|---|
capsize-games/spikeforge-dashboard |
Phase 2 | extracted (T1 fired) |
capsize-games/spikeforge-targets |
Phase 3 | extracted (T2 fired) |
capsize-games/spikeforge-hub |
Phase 4 | extracted (T3 fired) |
capsize-games/spikeforge-server |
Phase 4 | no-go โ T4 did not fire |
capsize-games/spikeforge |
โ | the core repository (renamed to spikeforge) |
All repositories live under the capsize-games
organization and use hyphenated spikeforge* names; the core repository is
capsize-games/spikeforge.
Dependency direction
The invariant is dependency arrows point from a component to what it
requires, and there is no cycle. A satellite may depend on spikeforge;
spikeforge never depends on a satellite.
flowchart TB
dashboard[spikeforge-dashboard React plus Vite]
server[spikeforge-server server package]
hub[spikeforge-hub spikeforge_hub]
targets[spikeforge-targets spikeforge_targets]
core[spikeforge spikeforge]
protocol[protocol JSON Schema contract]
targets --> core
hub --> core
server --> core
server --> targets
server --> hub
server --> protocol
dashboard --> protocol
core --> protocol
Reading the edges as "depends on": spikeforge-targets, spikeforge-hub, and
spikeforge-server each depend on core; the server additionally depends on
spikeforge_targets and spikeforge_hub because its files import those
capability roots; and both the server and the dashboard depend on the
protocol/ contract, never on each other's code.
Why this direction is stable
- Core is the floor. It has no incoming packaging dependency on a
satellite, which is what makes a headless
pip install spikeforgeprovably free of the forbidden list inplans/arch-0001-core-boundary.md. - The protocol is a leaf, not a library. Because
protocol/is data (JSON Schema), the dashboard and the server can both depend on it without depending on each other โ which is what makes the Phase 2 dashboard extraction safe. - Capabilities are siblings.
spikeforge-targetsandspikeforge-hubdo not depend on each other, so either can be extracted without ordering constraints beyond core.
Layout mechanism
Each distribution is defined by its own PEP 621
pyproject.toml under a top-level
packages/ workspace directory. The distribution's build config selects its
import root via package-dir; the extraction is done, so each import root
now lives at its own top-level path in this repository and in its satellite.
packages/
spikeforge/
pyproject.toml # distribution spikeforge, import root spikeforge
spikeforge-server/
pyproject.toml # distribution spikeforge-server, import root server
spikeforge-targets/
pyproject.toml # distribution spikeforge-targets, import root spikeforge_targets
spikeforge-hub/
pyproject.toml # distribution spikeforge-hub, import root spikeforge_hub
The core distribution's find configuration explicitly excludes the roots it
does not own, so server/ can no longer leak into the core wheel:
[tool.setuptools.packages.find]
where = ["../.."]
include = ["spikeforge*"]
exclude = ["server*", "tests*", "spikeforge_targets*", "spikeforge_hub*"]
This is the direct fix for the verified fact that
setup.py currently packages server/ into spikeforge.
Related decisions
plans/arch-0001-adr-repo-topology.mdโ why one repo, multiple distributions.plans/arch-0001-core-boundary.mdโ the forbidden-import contract for core.plans/arch-0001-packaging-versioning.mdโ names, extras, scripts, pinning.plans/arch-0001-migration-plan.mdโ the reversible move sequence.plans/repo_topology_plan.mdโ the source analysis.