Skip to content

docs(server): implementation plan for serving client-side knowledge rules (#582) - #583

Open
halcwb wants to merge 4 commits into
informedica:masterfrom
halcwb:docs/582-server-knowledge-rules
Open

docs(server): implementation plan for serving client-side knowledge rules (#582)#583
halcwb wants to merge 4 commits into
informedica:masterfrom
halcwb:docs/582-server-knowledge-rules

Conversation

@halcwb

@halcwb halcwb commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Description

Implementation plan for #582: the emergency list, continuous medication list, growth references and localization sheets that the client fetches straight from Google Sheets today are served through the server, so ReloadResources, GetResourceInfo and the #580 scope gate cover them like every other knowledge rule. Calculations stay in the browser; only the data path moves.

Key points:

  • A KnowledgeCmd command family (GetEmergencyList, GetContinuousMeds, GetNormalValues, GetLocalization) with matching responses; GetProducts is dropped because State.Products is never read.
  • The server extends Resources.defaultRegistry with its own keys and ofResultOrDefault loaders, so a failed or wrong emergency-list workbook is a warning in resource info, never a formulary outage. GenFORM itself gets one function, getCachedProviderWithRegistry, because GenFORM.Lib is Core and may not reference Shared.
  • GENPRES_EMERGENCY_URL_ID, defaulting to GENPRES_URL_ID when unset; verified against the public demo workbooks that the emergency tabs are not in the main workbook today while Localization is, and that Google returns the first tab for an unknown tab name, which the parsers' KeyNotFoundException turns into an error rather than wrong data.
  • KnowledgePort on AppEnv, four processCmd arms behind requireLoaded; Feature.ofCommand classification with a new Feature.Patient for normal values and localization.
  • Stated consequences for the reviewer: sheet edits now need a reload; a fatal formulary load failure also takes the emergency list down (a second provider is the named fallback); docs.google.com leaves the CSP once localization moves.

Six PRs, each under 200 lines. Depends on #580 (plan in #581) only for the scope classification; steps 1 to 3 change no behaviour until the client switches.

Author checklist

Reviewer checklist

  • The chosen approach is a good choice.
  • The sequence of steps is complete and sensible.
  • I have documented resources that may help with implementation.

🤖 Generated with Claude Code

Implementation plan for informedica#582: the emergency list, continuous medication
list, growth references and localization sheets that the client fetches
from Google Sheets today are served through the resource provider and a
new KnowledgeCmd command family, so that ReloadResources, GetResourceInfo
and the informedica#580 scope gate cover them. Calculations stay in the browser.

Refs informedica#582

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This documentation PR plans to move emergency medication, growth-reference, and localization data fetching from the browser to an independent, cached server provider.

  • Introduces three scoped knowledge commands and a separate localization remoting method.
  • Defines an independent KnowledgeProvider, startup loading, reload behavior, configuration, and client migration.
  • Revises reload fan-out and feature classification based on prior review feedback.

Confidence Score: 4/5

The plan should not be approved until a throwing knowledge loader can no longer prevent the independent formulary reload.

The previous findings were manually resolved and their documented corrections remain present. One blocking gap remains in the revised reload fan-out: the knowledge callback executes first without specified exception containment, so an unexpected loader exception can stop the GenORDER formulary reload.

Files Needing Attention: docs/implementation-plans/582-server-knowledge-rules.md

Important Files Changed

Filename Overview
docs/implementation-plans/582-server-knowledge-rules.md Plans the server-side knowledge-resource migration, but the reload sequence lacks exception isolation between the knowledge and formulary providers.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
    C[Client] -->|KnowledgeCmd| D[Command dispatcher and scope gate]
    C -->|getLocalization| A[Server API]
    D --> K[KnowledgeProvider]
    A --> K
    K --> E[Emergency workbook]
    K --> M[Main workbook localization]
    R[ReloadResources] --> O[OrderContextService]
    O --> K
    O --> F[GenFORM provider]
Loading

Fix all with Greploop Fix All in Claude Code

Reviews (3): Last reviewed commit: "docs(server): classify growth references..." | Re-trigger Greptile

Comment thread docs/implementation-plans/582-server-knowledge-rules.md Outdated
Comment thread docs/implementation-plans/582-server-knowledge-rules.md Outdated
Two availability regressions in the first draft, both caught in review:

- The emergency-list id defaulted to GENPRES_URL_ID, whose workbook has
  no emergency tabs, so an unconfigured upgrade would have shown an empty
  emergency list. The default is now the workbook the client reads today.
- The sheets were read through the formulary's CachedResourceProvider
  behind requireLoaded, so a fatal formulary load would have taken the
  emergency list down. They now have a small server-side provider over
  LoadEngine with its own cache and reload; KnowledgeCmd bypasses
  requireLoaded like GetDrugNames, and ReloadResources reloads both.
  GenFORM is no longer changed at all.

Refs informedica#582

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Comment thread docs/implementation-plans/582-server-knowledge-rules.md Outdated
Comment thread docs/implementation-plans/582-server-knowledge-rules.md Outdated
Maintainer decision: the growth references (GetNormalValues) are an MDR
function and map to a new Feature.Patient in the accredited list; the
UI text (Localization) is not, so it leaves the KnowledgeCmd family and
is served by a getLocalization remoting method outside processCmd, the
way informedica#580 serves getSettings. Also addresses the second review round:

- KnowledgeProvider.Warnings reads through the cache, and Host reads it
  right after construction, so a wrong workbook id is in the start-up
  log instead of first surfacing on a client request.
- The reload fan-out no longer waits for an Ok that Api.reloadCache
  never returns. It runs in OrderContextService.evaluate after the
  password guard, unconditionally, so the admin reload refreshes the
  emergency list whatever the formulary reload does.

Refs informedica#582

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Comment on lines +242 to +244
`OrderContextService.evaluate`, in the branch that runs after the password guard: it calls
`knowledge.Reload()` and logs its warnings before handing the command to GenORDER, so the
knowledge reload runs whenever the password was accepted, whatever the formulary then does.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Reloads Are Not Isolated

KnowledgeProvider calls LoadEngine.ForceAll directly, but ofResultOrDefault does not catch exceptions thrown by a loader. Because the plan calls knowledge.Reload() before handing the command to GenORDER, such an exception can stop the formulary reload from running. The reload path therefore does not preserve the intended independence between the knowledge and formulary providers. Add exception containment to KnowledgeProvider.Reload, or otherwise ensure that GenORDER's reload always executes.

Fix in Claude Code

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, fixed in db1fe79. ofResultOrDefault maps an Error from a loader, not an exception, and LoadEngine.ForceAll raises ResourceLoadError itself on a cycle or an unregistered key, so Reload() could have escaped and, running first, skipped the formulary reload.

Two nets now, both in the plan:

  • KnowledgeProvider wraps its load in try/with, the way loadAllResourcesWithRegistry wraps its own ForceAll. On an exception it caches a snapshot in which every key holds its empty value and Warnings carries one ErrorMsg with the exception, the same "empty state on error, cached so it is not retried per request" rule that CachedResourceProvider.loadFresh applies. Get, Reload and Warnings never propagate; that is the type's contract, pinned by a step-2 test (a raising loader yields the empty value and the warning, and a Reload after the loader is fixed serves data again).
  • The call site in OrderContextService.evaluate wraps knowledge.Reload() in a try/with of its own that logs and continues, so even a provider that broke the contract could not stop the GenORDER reload behind it. A step-3 service test with a stub reload that raises proves the command still reaches GenORDER.

Neither reload can prevent the other, in either direction.

Review round three: ofResultOrDefault maps an Error, not an exception,
so a loader that raised inside ForceAll would have escaped Reload() and,
because the knowledge reload runs first, skipped the formulary reload.
KnowledgeProvider now wraps its load in try/with and caches an empty
snapshot with an ErrorMsg, as CachedResourceProvider.loadFresh does, so
Get, Reload and Warnings never propagate; the service call site adds a
try/with of its own. Tests for both are added to steps 2 and 3.

Refs informedica#582

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant