feat(catalog): screaming service layer + LLM integrations page (closes #71)#84
Merged
danielnaab merged 36 commits intomainfrom Apr 19, 2026
Merged
feat(catalog): screaming service layer + LLM integrations page (closes #71)#84danielnaab merged 36 commits intomainfrom
danielnaab merged 36 commits intomainfrom
Conversation
Captures the brainstormed design: service public-interface convention via services/<name>/index.ts, orphan-file migration plan, build-time commit SHA + githubPermalink helper, new catalog pages for LLM integrations and codebase navigation, and extended dependency-rule test to enforce cross-service import boundaries.
Thirteen bite-sized tasks covering: public-interface index.ts per service (Task 1), orphan file moves (2-5), build-info + permalink helper (6-7), markdown src: rewriter (8), NixOS BUILD_GIT_SHA wiring (9), import rewrites (10), dependency-rule test extension (11), and two new catalog pages (12-13).
Each service now exposes its public API via index.ts with a top-of-file comment stating that external imports must route through this file. Consumers are not yet rewritten — that happens in a later commit.
StrategyRegistry is a generic Map<key, factory> pattern with no domain content. It belongs in shared/ per the architecture principles.
…ce + form-project-repo) Form-project handling is a real domain boundary. Moving these two files into their own folder with an index.ts public interface makes the services/ directory read as a list of intents.
Resolves the git ref the app was built from so catalog permalinks can point at the exact deployed code. Prefers BUILD_GIT_SHA env var (set by deploy script), falls back to git rev-parse. Dirty worktrees use a 'dev-<branch>' marker.
Builds a GitHub blob URL pinned to the current build's git ref, with optional line number or range anchor.
Catalog markdown can now write [label](src:src/path/file.ts#L42-L88) and the renderer substitutes a GitHub permalink pinned to the current build's git ref at render time. Non-src: links pass through untouched. renderMarkdown now takes a RenderOptions argument; all 11 catalog route callers updated.
The deploy script captures the commit SHA of each deployed worktree and writes it into the per-branch .env, which the app service already reads via EnvironmentFile. The homepage service now also loads main's .env so getBuildInfo() can resolve the running commit without shelling out to git at runtime. Ignoring the file when absent keeps first-boot working via a leading '-' on the EnvironmentFile path.
These symbols are only consumed by tests. Tests now deep-import them directly. The public index.ts is reserved for symbols with non-test consumers.
These symbols are only consumed by tests. Tests now deep-import them directly. The public index.ts is reserved for symbols with non-test consumers.
These symbols are only consumed by tests. Tests now deep-import them directly. The public index.ts is reserved for symbols with non-test consumers.
Seven types had no callers anywhere; three more had only test consumers. The tests now deep-import these symbols. The public index.ts now reflects the evaluation service's real external surface.
…x.ts Task 10 routed runtime imports through each service's index.ts but missed type-only cross-service imports (the P2 test exempts them). The new service public interface rule in Task 11 is stricter — type-only imports are covered too, because the public interface is about intent visibility, not runtime coupling. Also exports `createToolUsePdfExtractor` from form-documents/index.ts so that extraction/registry.ts can import it through the public entrypoint.
Extends the dependency-rule test with a new rule: imports from outside service A into service A must resolve to A's index.ts (i.e. `services/A` or `services/A/index`). Intra-service imports are unrestricted. Type-only imports are included — the public interface is about intent visibility, not just runtime coupling.
Enumerates every LLM call site in the codebase — extraction, shaping, filling, evaluation, and a placeholder for future RAG — with src: permalinks that pin to the deployed commit. Acts as the 'where every LLM call lives' map for the codebase.
Navigation.md explains the service public interface convention with LLM integrations as the worked example. software-architecture.md gains a 'Service public interface' subsection under Structure, and system-overview.md links to navigation.md.
AppError and subclasses have no domain content — they're HTTP-status- coded error types used across all services. They belong in shared/ alongside the other pure utilities.
software-architecture.md, data-model.md, and CLAUDE.md's Project Structure list all carried references to paths that no longer exist (services/ingestion/, services/storage.ts, services/user-store.ts, services/form-project-repo.ts, services/errors.ts). Updated to match the current layout and name the new services and conventions.
Resolved conflicts:
- src/entrypoints/cli/commands/evaluate.ts: routed main's new evaluation
imports (fixtureProjectState, shapingIntentFixtures, shapingCommandsKind,
RunResult, FormShaper) through the public services/evaluation and
services/forms APIs; moved strategy-registry import to shared/ per this
branch's refactor.
- src/entrypoints/webhook/main.ts: kept main's teardownBranch import;
routed createGitHubClient through services/deployment.
- src/services/form-documents/extraction.ts: routed main's new
PolicyRetriever/PolicyChunk imports through services/rag; routed
ExtractionExemplar through services/extraction.
- src/services/form-documents/hybrid-extraction-prompt.ts: routed
ExtractionExemplar through services/extraction.
- src/design-system/components/flex-spec-{browser,diff-browser}/examples.tsx:
routed type imports through services/{data-collection,forms} public APIs.
- src/services/evaluation/index.ts: re-added fixtureProjectState,
shapingIntentFixtures, shapingCommandsKind, RunResult to the public
interface — they were removed earlier as test-only but main's new CLI
evaluation code uses them as genuine public API.
- test/evaluate-shaping-cli.test.ts: services/strategy-registry ->
shared/strategy-registry per earlier refactor.
- catalog/architecture/software-architecture.md: de-duplicated forms/
entry created by auto-merge.
…undle buildable
Problem: the forms service barrel re-exports SqliteFormSessionGateway,
SqliteSubmissionGateway, and BedrockFillingAgent, which import bun:sqlite
and @aws-sdk/credential-providers. When a design-system client.ts file
does 'import { executeBatch } from services/forms' at runtime, Bun's
browser-target build walks the whole barrel and errors on those server-
only imports before tree-shaking can drop the unused classes. This broke
CI's Build step after the merge.
Fix:
- client.ts files in src/design-system/components/ deep-import from
specific sub-modules (e.g. services/forms/shaping/commands) so the
browser bundle only pulls server-safe code. This is a physical
constraint of browser bundling, not an architecture failure — the
existing P2 rule already treats client.ts as entrypoint-level for the
same reason.
- dependency-rule.test.ts's cross-service rule now exempts client.ts
files explicitly, documenting the constraint in a comment.
- Removed an unused node:path import in webhook/recovery.ts (main merge
leftover) that was biome's error-level noise.
Browser bundle now builds clean. 1269 tests pass.
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.
Summary
src/services/<name>/now exposes a single public-interfaceindex.ts. External callers import fromservices/<name>only — never a deeper path. Enforced by an extendedtest/architecture/dependency-rule.test.tsthat also catches type-only imports.storage.ts→storage/,user-store.ts→auth/,project-service.ts+form-project-repo.ts→ newprojects/,strategy-registry.ts+errors.ts→shared/).src/services/now reads as 12 intent-named folders with no loose files.catalog/architecture/llm-integrations.mdenumerates every LLM call site (extraction, shaping, filling, evaluation, future RAG) withsrc:permalinks that resolve to GitHub at the exact deployed commit. Supporting infrastructure:src/shared/build-info.ts,src/services/content/github-permalink.ts, markdown-itsrc:URL rewriter, and NixOS deploy wiring forBUILD_GIT_SHA.catalog/architecture/navigation.mddocuments the convention withllm-integrations.mdas the worked example.software-architecture.md,data-model.md, andCLAUDE.mdrefreshed to match the new layout.Story
Closes #71
Acceptance Criteria
catalog/architecture/llm-integrations.mdservices/<name>/index.tsfilessoftware-architecture.mddescribes the "service public interface" convention — new subsection at lines 119-130test/architecture/dependency-rule.test.ts(includes type-only imports)src:scheme pinned to deployed commit SHAcatalog/architecture/navigation.mdbun run checkpasses — 1269 tests pass post-mergeTest Plan
bun run checkpasses (1269 tests, 0 fails)test/architecture/dependency-rule.test.tspasses, including the new cross-service rule/srv/forms-lab/main/.envcontainsBUILD_GIT_SHA=<sha>systemctl show forms-lab-homepage -p EnvironmentFilesshows/srv/forms-lab/main/.env/catalog/architecture/llm-integrationsand click severalsrc:links — each should open GitHub at the expected file + line range, pinned to the deployed commit/catalog/architecture/navigation— renders cleanly, cross-links resolveReview Notes
index.ts-per-service, per-target-service import rewrites, public-API trimming passes, permalink infrastructure, deploy wiring, dep-rule test extension, catalog pages, and a merge commit. Seenotes/story-71-llm-integrations-catalog/review.mdfor the full breakdown.index.tsfiles just so tests could import through the "public" API. Removed them; tests now deep-import internals. The convention is documented innavigation.md: production code goes throughindex.ts; tests unit-testing internals may deep-import.services/rag/service and shaping-CLI). Merged cleanly; conflicts in 3 files resolved to route main's new code through the public APIs. The dep-rule test caught two violations main introduced — fixed in the merge commit.dataCollectionSpecSchemamay belong indata-collection/rather thanform-documents/(P3);projects/project-service.tsis 822 lines with natural seams;storage/index.tscontains inline implementation rather than pure re-exports. None are blocking.Related
notes/story-71-llm-integrations-catalog/design.mdnotes/story-71-llm-integrations-catalog/plan.mdnotes/story-71-llm-integrations-catalog/review.md