feat: restore functional option pattern for New() #28

Merged
Virgil merged 725 commits from feat/service-options into dev 2026-03-24 22:09:20 +00:00
Member

Summary

  • New() returns Result, accepts CoreOption functionals
  • WithService(factory func(*Core) Result) — service factory receives Core during construction
  • WithOptions(Options) — key-value configuration
  • WithServiceLock() — immutable conclave after construction

Restores the v0.3.3 service registration contract adapted for the dev branch DTO pattern. Services registered in New() form the application conclave with shared IPC access. Each Core instance has its own bus scope.

Test plan

  • go build ./... passes
  • go test ./... passes (all 24 files updated)
  • New tests: TestNew_WithService_Good, TestNew_WithServiceLock_Good

🤖 Generated with Claude Code
Co-Authored-By: Virgil virgil@lethean.io

## Summary - `New()` returns `Result`, accepts `CoreOption` functionals - `WithService(factory func(*Core) Result)` — service factory receives Core during construction - `WithOptions(Options)` — key-value configuration - `WithServiceLock()` — immutable conclave after construction Restores the v0.3.3 service registration contract adapted for the dev branch DTO pattern. Services registered in `New()` form the application conclave with shared IPC access. Each Core instance has its own bus scope. ## Test plan - [x] `go build ./...` passes - [x] `go test ./...` passes (all 24 files updated) - [x] New tests: `TestNew_WithService_Good`, `TestNew_WithServiceLock_Good` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Virgil <virgil@lethean.io>
Virgil added 1 commit 2026-03-24 16:46:08 +00:00
New() returns Result, accepts CoreOption functionals.
Restores v0.3.3 service registration contract:
- WithService(factory func(*Core) Result) — service factory receives Core
- WithOptions(Options) — key-value configuration
- WithServiceLock() — immutable after construction

Services registered in New() form the application conclave with
shared IPC access. Each Core instance has its own bus scope.

Co-Authored-By: Virgil <virgil@lethean.io>
Virgil added 1 commit 2026-03-24 16:59:35 +00:00
- WithOptions copies the Options slice (constructor isolation regression)
- WithService auto-discovers service name from package path via reflect
- WithService auto-registers HandleIPCEvents if present (v0.3.3 parity)
- Add test for failing option short-circuit in New()

Co-Authored-By: Virgil <virgil@lethean.io>
Virgil added 1 commit 2026-03-24 17:14:53 +00:00
- HandleIPCEvents only auto-registered for services the factory didn't
  register itself (prevents double handler registration)
- Auto-discovery only creates Service{} placeholder when factory didn't
  call c.Service() — factories that register themselves keep full lifecycle

Addresses Codex review findings 1 and 2 from third pass.

Co-Authored-By: Virgil <virgil@lethean.io>
Virgil added 1 commit 2026-03-24 17:24:53 +00:00
WithService is now a simple factory call — no reflect, no auto-registration.
New() calls discoverHandlers() after all opts run, scanning Config for
service instances that implement HandleIPCEvents.

This eliminates both double-registration and empty-placeholder issues:
- Factories wire their own lifecycle via c.Service()
- HandleIPCEvents discovered once, after all services are registered
- No tension between factory-registered and auto-discovered paths

Co-Authored-By: Virgil <virgil@lethean.io>
Virgil added 1 commit 2026-03-24 17:38:00 +00:00
Restores v0.3.3 service manager capabilities:
- RegisterService(name, instance) stores the raw instance
- Auto-discovers Startable/Stoppable interfaces → wires lifecycle
- Auto-discovers HandleIPCEvents → wires to IPC bus
- ServiceFor[T](c, name) for typed instance retrieval
- Service DTO gains Instance field for instance tracking

WithService is a simple factory call — no reflect, no magic.
discoverHandlers removed — RegisterService handles it inline.
No double-registration: IPC wired once at registration time.

Co-Authored-By: Virgil <virgil@lethean.io>
Virgil added 1 commit 2026-03-24 19:17:15 +00:00
Options is now a proper struct with New(), Set(), Get(), typed accessors.
Result gains New(), Result(), Get() methods on the struct.
WithOption("key", value) convenience for core.New().

options_test.go: 22 tests passing against the new contract.
Other test files mechanically updated for compilation.

Co-Authored-By: Virgil <virgil@lethean.io>
Virgil added 1 commit 2026-03-24 19:19:02 +00:00
App.New() creates from Options. App.Find() locates programs on PATH.
Both are struct methods — no package-level functions.
8 tests passing.

Co-Authored-By: Virgil <virgil@lethean.io>
Virgil added 1 commit 2026-03-24 19:24:20 +00:00
Cli struct unchanged — already conforms.
Tests use WithOption() convenience. 9 tests passing.

Co-Authored-By: Virgil <virgil@lethean.io>
Virgil added 1 commit 2026-03-24 19:26:47 +00:00
Cli{}.New(c) replaces &Cli{core: c} in contract.go.
9 tests passing.

Co-Authored-By: Virgil <virgil@lethean.io>
Virgil added 1 commit 2026-03-24 19:48:15 +00:00
Cli as service with ServiceRuntime, incomplete.
Need to properly port v0.3.3 service_manager, message_bus,
WithService with full name/IPC discovery.

Co-Authored-By: Virgil <virgil@lethean.io>
Virgil added 7 commits 2026-03-24 20:15:37 +00:00
- WithService now calls factory, discovers service name from package path via
  reflect/runtime (last path segment, _test suffix stripped, lowercased), and
  calls RegisterService — which handles Startable/Stoppable/HandleIPCEvents
- If factory returns nil Value (self-registered), WithService returns OK without
  a second registration
- Add contract_test.go with _Good/_Bad tests covering all three code paths
- Fix core.go Cli() accessor: use ServiceFor[*Cli](c, "cli") (was cli.New())
- Fix pre-existing })) → }}) syntax errors in command_test, service_test, lock_test
- Fix pre-existing Options{...} → NewOptions(...) in core_test, data_test,
  drive_test, i18n_test (Options is a struct, not a slice)

Co-Authored-By: Virgil <virgil@lethean.io>
WithService now: calls factory, discovers service name from instance's
package path via reflect.TypeOf, discovers HandleIPCEvents method,
calls RegisterService. If factory returns nil Value, assumes self-registered.

Also fixes: Cli() accessor uses ServiceFor, test files updated for Options struct.

Co-Authored-By: Virgil <virgil@lethean.io>
Co-Authored-By: Virgil <virgil@lethean.io>
Co-Authored-By: Virgil <virgil@lethean.io>
Co-Authored-By: Virgil <virgil@lethean.io>
Co-Authored-By: Virgil <virgil@lethean.io>
WithService: full name discovery + IPC handler auto-registration via reflect
WithName: explicit service naming
RegisterService: Startable/Stoppable/HandleIPCEvents auto-discovery
MustServiceFor[T]: panics if not found
WithServiceLock: enable/apply split (v0.3.3 parity)
Cli: registered as service via CliRegister, accessed via ServiceFor

@TODO Codex: Fix data_test.go and embed_test.go — embed path resolution
after Options changed from []Option to struct. Mount paths need updating.

Co-Authored-By: Virgil <virgil@lethean.io>
Virgil added 1 commit 2026-03-24 20:29:58 +00:00
Root cause: Result.New didn't mark single-value results as OK=true,
breaking Mount/ReadDir/fs helpers that used Result{}.New(value, err).

Also: data_test.go and embed_test.go updated for Options struct,
doc comments updated across data.go, drive.go, command.go, contract.go.

All tests green. Coverage 82.2%.

Co-Authored-By: Virgil <virgil@lethean.io>
Virgil added 1 commit 2026-03-24 20:40:36 +00:00
Config.New() initialises ConfigOptions.
Fs.New(root) sets sandbox root.
ErrorLog uses Default() fallback — no explicit init needed.
contract.go uses constructors instead of struct literals.

All tests green.

Co-Authored-By: Virgil <virgil@lethean.io>
Virgil added 1 commit 2026-03-24 21:16:04 +00:00
Virgil added 1 commit 2026-03-24 21:32:52 +00:00
Virgil added 1 commit 2026-03-24 21:36:14 +00:00
Co-Authored-By: Virgil <virgil@lethean.io>
Virgil added 1 commit 2026-03-24 21:50:07 +00:00
Virgil force-pushed feat/service-options from d2412459f5 to 5362a9965c 2026-03-24 22:09:13 +00:00 Compare
Virgil merged commit f72c5782fd into dev 2026-03-24 22:09:20 +00:00
Snider deleted branch feat/service-options 2026-03-24 22:10:42 +00:00
Sign in to join this conversation.
No description provided.