Extract every config mutation into pure core operations - #57
Closed
jellologic wants to merge 1 commit into
Closed
Conversation
Configuration editing used to live in whichever front end you were looking at.
The Ink wizard minted account+setup+profile in its own `finish()`; shape
parsing, reference checking and state mutation lived in adapters/web/api.ts.
Two editors, two implementations of the same rules, and no way to tell they
agreed.
src/core/operations.ts is now the single home: state in, state out, no I/O.
The web API's remaining job is HTTP — routing, revision conflicts, redaction
and status codes — and it went from 628 lines to 433.
The shape follows bindPath/unbindPath in core/binding.ts, which already worked
this way. Refusals carry a `kind` of 'invalid' or 'missing' rather than a
status code, so core/ stays ignorant of HTTP: the adapter maps those to 400 and
404, and a terminal caller can map them to exit codes.
Provider validation takes `reservedIds`, `knownCompatFlags` and
`credentialEnvs` as parameters rather than importing them. They are Claude
Code's, core/ may not name a CLAUDE_CODE_ or ANTHROPIC_ identifier, and the
adapter that owns them supplies them.
This is a pure extraction. Every rule, comment and refusal message is carried
across unchanged, which is why the 802 existing tests pass untouched. Two
places where I had started to improve behaviour were reverted to match the
original exactly, because a refactor that quietly changes semantics cannot be
reviewed:
- deleting the default profile clears it to null; it does not promote a
survivor
- binding pruning keeps its string-only comparison
That second one is a real latent bug — a BindingValue is
`string | {profile, overrides}` and the comparison only prunes the string form,
so an object-form binding outlives the profile it names. Preserved here and
filed separately.
test/core/operations.test.ts drives a whole lifecycle — account, setup,
profile, edit, default, delete, plus every refusal path — importing nothing
from adapters/ or web/. That is the point of the change: what makes a second
front end cheap is not shared UI, it is that neither front end owns the logic.
813 tests green.
Refs #38
Signed-off-by: jellologic <31935831+jellologic@users.noreply.github.com>
jellologic
force-pushed
the
feat/core-operations
branch
from
September 3, 2026 22:25
a3e188e to
1e6d930
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #38. Stacked on #55 — review that first; the base will retarget to
mainonce it merges.Configuration editing used to live in whichever front end you were looking at: the wizard minted account+setup+profile in its own
finish(), while shape parsing, reference checking and mutation lived inweb/api.ts. Two editors, two implementations, no way to tell they agreed.src/core/operations.tsis now the single home.api.tswent 628 → 433 lines, and its remaining job is HTTP: routing, revision conflicts, redaction, status codes.Why this is the highest-leverage issue in #52
It is the hedge behind dropping the CLI editor (#35). What makes a second front end cheap is not shared UI — it is that neither front end owns the logic.
test/core/operations.test.tsproves it by driving a full lifecycle importing nothing fromadapters/orweb/.Design notes
bindPath/unbindPathincore/binding.ts, which already returned{ok, state, reason}.kind: 'invalid' | 'missing'rather than a status code, so core stays ignorant of HTTP. The adapter maps them to 400/404; a terminal caller can map them to exit codes.reservedIds,knownCompatFlags,credentialEnvs) are parameters, because they are Claude Code's and core may not name aCLAUDE_CODE_/ANTHROPIC_identifier.This is a pure extraction
Every rule, comment and message carried across unchanged — which is why the 802 existing tests pass untouched. I reverted two places where I had started to improve behaviour, because a refactor that quietly changes semantics can't be reviewed:
null; no promoting a survivorThat second one is a real latent bug and is filed separately:
BindingValueisstring | {profile, overrides}, so an object-form binding outlives the profile it names.813 tests green.