Moving from late-exploration to early production quality. The pattern is settled; this roadmap fills in the gaps that were consciously deferred.
delete businesses intentionally omitted.
The API has grown to 73 paths / 159 schemas. Currently covered: businesses, accounts, contacts, documents (list/update). Not yet covered:
- Transactions (read-only streaming, useful for reports)
- Accounting periods
- VAT returns
- Attachments (multipart upload — needs special handling)
- Balance sheet / P&L report folds (a
Reports.fsstub exists)
Prioritise based on user need, not completeness for its own sake.
Phase 1: Http errors → Unit tests → New repo
Phase 2: Dry-run → Config profiles
Phase 3: Self-contained binary → CI
Phase 4: Hawaii → Missing commands → Logging
Phase 5: Broader API coverage
Phases 1–3 are the path to "early production quality tool". Phases 4–5 are "mature tool".
A "super-Hawaii" generator was considered: a tool that reads an OpenAPI spec and emits a complete F# CLI with CSV import/export for all entity types, replacing most of the hand-written code in this repo. Several architectures were evaluated:
- Extend Hawaii directly — single tool, but pollutes the generator with two concerns; makes upstreaming patches harder;
- Pipeline (Hawaii + second-pass CLI generator) — clean separation; Hawaii unchanged;
new generator emits
Streaming.fs,Arguments.fs,Dispatch.fsper entity - Full standalone generator — maximum control, but reimplements everything Hawaii already handles (nullable types, enum robustness, JSON quirks)
- Template-based rendering — good for simple scaffolding, awkward for type-relationship inference; best as a rendering layer within one of the above, not a primary architecture
- Runtime interpretation — no code generation, dynamic CLI from config; loses Argu's compile-time type safety and makes debugging harder
The pipeline approach (Option B) was the strongest technically: Hawaii handles the hard API-layer problems; a new tool handles CLI generation; static runtime files (Http.fs, AsyncSeq.fs, Streams.fs, PatchShape.fs, Csv.fs) are checked in and never generated. An annotation file alongside the OpenAPI spec would capture what OpenAPI cannot express (tenant scope parameter, identity keys, CSV exclusions, custom type converters).
The generator is only worth building if it will be applied to multiple different APIs. For a NoCFO-specific tool the upfront cost outweighs the ongoing savings — especially since the hand-written code is already small and well-structured.
The codebase should evolve toward maximally generic F# using SRTP and inline functions,
so that adding a new entity type costs almost nothing — just a type definition and a few
lines of wiring. paginateByPageSRTP in AsyncSeq.fs is the established proof of concept.
Target end state:
Http.fs,AsyncSeq.fs,Streams.fs,PatchShape.fs,Csv.fs— fully generic, no entity-specific codeDomain.fs— type definitions and the minimal entity-specific logic that SRTP cannot absorb (e.g. semantic field interpretation)Arguments.fs— NoCFO-specific CLI structureProgram.fs— small orchestrator
Next candidates for genericisation (in rough priority order):
- Per-entity streaming wiring currently duplicated across entities in
Domain.fs - CSV field mapping in
Csv.fs PatchShape.fs— reflection is acceptable to keep; SRTP for record fields is verbose enough that the reflection approach remains more readable
This approach delivers most of the code-generation benefit (new entity = trivial addition) without building or maintaining a generator.