You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Prerequisite:#927 (LP-008) — the layer rules are not satisfiable until the tool package is split.
Summary
Record the decision not to split TARS into tars-app / tars-cli / tars-console repositories at this time, and get the discipline that split would have provided from a CI-enforced layering rule instead.
Problem
The motivation for splitting is real: keep the library clean, and stop application changes from breaking external consumers. But the evidence says a split addresses less than it appears to.
The vertical boundary already holds.go list -deps ./pkg/... contains no internal/tarsserver. The server does not leak into the core in the import direction. The actual coupling problem is horizontal — one oversized internal/tool package — which a repository split does not touch. #927 does.
There is no separable desktop application. No Tauri, no Electron. What is described as the desktop app is golang.design/x/hotkey, internal/tarsserver/notify.go, and the same binary serving an embedded console. There is no third codebase to extract.
The costs are concrete.frontend/console is 41k LOC embedded via go:embed; splitting it requires a new artifact or npm pipeline just to keep make build working. CI already runs security, windows-build, windows-test, pr-diff, three CodeQL analyses, and SonarCloud. For a single maintainer, every change crossing a new repo boundary becomes a multi-repo PR — and provider work currently spans internal/llm, internal/config, internal/tarsserver, and frontend/console routinely.
The problem a split really solves is API stability, and #928 plus #929 solve that directly and reversibly. A layering rule is cheap to add and cheap to remove; a repository split is neither.
Scope
Write an ADR under docs/ recording the decision, the evidence above, and the criteria for revisiting it.
Enforce with a depguard rule in .golangci.yml or a make arch-check target, alongside the existing static-analysis guards.
Fail CI on a reverse import.
Document the layer map in CLAUDE.md and .analysis/AI_CONTEXT.md so future work respects it by default.
State the re-evaluation criteria explicitly: two or more external consumers, plus two consecutive quarters with no breaking change to pkg/*, plus a release pipeline that no longer needs the console embedded at build time.
Acceptance Criteria
A reverse import from a core package to an app package fails CI with a message naming both packages.
The current tree passes the rule without exceptions, or every exception is listed with a reason and an owner.
The ADR is committed and linked from the README architecture section.
The re-evaluation criteria are specific enough to be checked without reopening the debate.
Validation
Add a deliberate reverse import locally and confirm CI rejects it
make test
make lint
make lint-diff
make ci-static-analysis-check
make build
Out of Scope
Moving any package to another repository.
Splitting the Go module. If pkg/* ever becomes its own module, that is a separate decision made against the criteria in this ADR.
Renaming packages beyond what the layer map requires.
Part of #919. Phase 3. LP-011.
Prerequisite: #927 (LP-008) — the layer rules are not satisfiable until the tool package is split.
Summary
Record the decision not to split TARS into
tars-app/tars-cli/tars-consolerepositories at this time, and get the discipline that split would have provided from a CI-enforced layering rule instead.Problem
The motivation for splitting is real: keep the library clean, and stop application changes from breaking external consumers. But the evidence says a split addresses less than it appears to.
The vertical boundary already holds.
go list -deps ./pkg/...contains nointernal/tarsserver. The server does not leak into the core in the import direction. The actual coupling problem is horizontal — one oversizedinternal/toolpackage — which a repository split does not touch. #927 does.There is no separable desktop application. No Tauri, no Electron. What is described as the desktop app is
golang.design/x/hotkey,internal/tarsserver/notify.go, and the same binary serving an embedded console. There is no third codebase to extract.The costs are concrete.
frontend/consoleis 41k LOC embedded viago:embed; splitting it requires a new artifact or npm pipeline just to keepmake buildworking. CI already runs security, windows-build, windows-test, pr-diff, three CodeQL analyses, and SonarCloud. For a single maintainer, every change crossing a new repo boundary becomes a multi-repo PR — and provider work currently spansinternal/llm,internal/config,internal/tarsserver, andfrontend/consoleroutinely.The problem a split really solves is API stability, and #928 plus #929 solve that directly and reversibly. A layering rule is cheap to add and cheap to remove; a repository split is neither.
Scope
docs/recording the decision, the evidence above, and the criteria for revisiting it.cmd/→internal/app/*(tarsserver, cron, pulse, reflection, ops) →internal/core/*(llm, tool-core, session, memory, skill, mcp, prompt) →pkg/.depguardrule in.golangci.ymlor amake arch-checktarget, alongside the existing static-analysis guards.CLAUDE.mdand.analysis/AI_CONTEXT.mdso future work respects it by default.pkg/*, plus a release pipeline that no longer needs the console embedded at build time.Acceptance Criteria
Validation
make testmake lintmake lint-diffmake ci-static-analysis-checkmake buildOut of Scope
pkg/*ever becomes its own module, that is a separate decision made against the criteria in this ADR.