refactor(core): deepen architecture — rules registry, enricher pipeline, export composition#62
Conversation
There was a problem hiding this comment.
Note
Clean refactor. One behavioral change to be aware of: client_id= was removed from the inline-params check in par.ts — this is a correct bugfix per RFC 9126.
TL;DR — Extracts 7 diagnosis rules into a registry, replaces the ad-hoc enricher merge with a pipeline, moves export helpers into core, simplifies the EventStore interface, and narrows the barrel. ~800 lines changed, ~800 removed. Zero behavioral regressions.
Key changes
- Rules registry —
FlowRule[]array replaces 7 hardcodedcollect*calls inrunFlowRules. Adding a diagnostic is one file + one array entry. - Enricher pipeline —
OidcEnrichertype +enrichersarray replaces the object-spread merge inenrichWithOidcSemantics. - Export composition into core — 3 call-sites (extension panel, standalone IPC, MCP tools) now import
exportAsJson/exportAsMarkdownfrom@wolfcola/devtools-coreinstead of duplicating the redact→diagnose→render recipe. - EventStore interface simplification —
setLastOidcEventIdremoved.updateSummarynow auto-trackslastOidcEventIdviaevent.oidcSemantics. All adapters get OIDC causal linking for free. - Barrel narrowed — 14 internal symbols removed from public index. Reorganized into labeled sections.
par.tsbugfix —client_id=removed from inline-params-with-request_uricheck. RFC 9126 requiresclient_idalongsiderequest_uri, so flagging it was a false positive.
Summary | 19 files | 1 commit | base: main ← refactor/deepen-core-architecture
Rules registry
Before: 7
collect*functions defined inline in a 774-line diagnosis engine module
After: Each rule is a separate file indiagnosis/rules/, registered in aFlowRule[]array
The extraction is pure — no behavior changed in any rule. The mergeByDedupKey helper stays in diagnosis-engine.ts since it's the engine's concern, not a per-rule concern. The FlowRule type signature is (events: readonly AuthEvent[]) => IssueCandidate[], which is exactly what each extracted function already was.
diagnosis-engine.ts · rules/types.ts · rules/index.ts
Enricher pipeline
Before: Three inline variables (
semantics,dpop,par) merged via ad-hoc object spread with a cast
After:OidcEnrichertype withenrichers.reduce, same contract
The OidcEnricher type matches the existing convention of the file — the import()-style inline type for OidcConfig is already used in enrichWithOidcSemantics itself, so this is consistent.
Export composition
Before:
panel.ts,ipc-bridge.ts, andmcp/tools.tseach implemented redact→diagnose→render inline
After:exportAsJsonandexportAsMarkdownlive in@wolfcola/devtools-core, all three callers import from there
The standalone export-helpers.ts module is gone — its contents are now in packages/devtools-core/src/export/export-transforms.ts. The envelope construction (version, exportedAt, redacted) is centralized.
export-transforms.ts · panel.ts · ipc-bridge.ts
EventStore simplification
Before:
setLastOidcEventIdin interface, manualyield* store.setLastOidcEventId(id)in handler
After:updateSummaryauto-trackslastOidcEventId— no interface method, no manual calls
This is a textbook "don't ask for state you can derive" improvement. The lastOidcEventId was always derivable from the event being appended; the manual call was a leaky abstraction that every adapter implementor had to remember.
event-store.service.ts · message-handler.ts · event-store-chrome.ts
…ne, export composition Five architectural improvements to devtools-core: 1. Extract diagnosis rules into registry (diagnosis/rules/) - 846-line monolith → 160-line engine + 7 focused rule modules - FlowRule type defines internal seam; flowRules array is the registry - Adding a diagnostic category = one file + one array entry 2. Simplify EventStore interface - Remove setLastOidcEventId from service shape - Track lastOidcEventId in updateSummary alongside lastSdkEventId - Every adapter gets OIDC tracking automatically via append 3. Move export composition into core - exportAsJson/exportAsMarkdown now in devtools-core/src/export/ - Delete standalone export-helpers.ts; all consumers import from core - Eliminates duplicated redact→diagnose→render recipe 4. Refactor detector pipeline with enricher pattern - OidcEnricher type + enrichers array replaces hardcoded merge - annotateOidc stays as base gate; DPoP/PAR are enrichers - Adding a detector = one function + one array entry 5. Narrow barrel exports - Remove 14 internal-only symbols from public barrel - ~20 organized exports vs 30+ flat exports Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
48f3203 to
aa938a3
Compare
There was a problem hiding this comment.
No new issues. Reviewed the following changes:
- Added
.changeset/fix-par-inline-params.mddocumenting the PAR bugfix - Added test coverage verifying
client_idalone withrequest_uridoes not trigger the inline-params false positive

Summary
collect*functions from 846-line monolith intodiagnosis/rules/modules withFlowRuletype and array-based registry. Adding a diagnostic = one file + one array entry.setLastOidcEventIdmethod — tracking now happens automatically inupdateSummary. All adapters get OIDC event linking for free.exportAsJson/exportAsMarkdownfrom standalone intodevtools-core/src/export/. Eliminates duplicated redact→diagnose→render recipe across 3 call sites.enrichWithOidcSemanticswithOidcEnrichertype +enrichersarray. Adding a detector = one function + one entry.Test plan
pnpm test)pnpm --filter @wolfcola/devtools-core buildsucceedsexport-json,export-markdown) work🤖 Generated with Claude Code