Skip to content

Add screener import and export - #540

Merged
prestoncabe merged 14 commits into
mainfrom
538-importexport-of-screeners
Oct 6, 2026
Merged

prestoncabe merged 14 commits into
mainfrom
538-importexport-of-screeners

Conversation

@prestoncabe

Copy link
Copy Markdown
Collaborator

Summary

Analysts can share a saved screener as one .bdt.json file and import it into another account as an unpublished, editable screener. The file preserves benefits, configured check versions, parameters, aliases, custom-check rules, and the form.

  • Add export to each screener card's menu and an import dialog with a visible file-picker button, a prefilled editable name, and readable validation errors.
  • Require unique screener names per user on creation, rename, and import, ignoring case and surrounding spaces. Ordinary benefit edits and publishing remain available for legacy screeners with duplicate names.
  • Reuse custom checks by original identity and immutable published version, preserve recipient draft edits, and add missing versions. Reject conflicting rules or parameter definitions; unrelated checks with matching names stay separate.
  • Export only the custom-check versions the screener uses. Validate imported DMN models and derive their inputs before writes, resolve library references against the receiving server (including internal composition checks), and clean up newly created artifacts when an import fails.
  • Distinguish a missing saved form from a failed storage read, preserve the editor's benefit order, and return rejected imports with their appropriate HTTP status.

Closes #538.

Verification

  • Builder API: mvn -o test — 202 tests passed.
  • Builder frontend: npm test — 147 tests passed.
  • Builder frontend: npm run build — passed.
  • Documentation: npm run build — passed.
  • Playwright: exampleScreenerTransfer.spec.ts and screenerTransfer.spec.ts — passed against an isolated local demo stack. Covers bundled-example sharing, internal library checks, name collisions, concurrent imports, cross-account re-export, preserved draft edits, conflicting versions, and imported screener evaluation.
  • git diff --check origin/main...HEAD — passed.

Documentation impact

  • Updated the user guide and frontend README with sharing instructions, name uniqueness, check reuse, and conflict behavior.

prestoncabe and others added 13 commits October 5, 2026 20:35
Benefit edits, benefit deletes, and publishing saved the working screener
through the unique-name transaction. Accounts that already had two
screeners with the same name could no longer edit or delete benefits, and
a failed delete left the screener listing a removed benefit. Every save
also queried all of the owner's screeners and serialized their writes.

Plain updates now write the document directly. The PATCH endpoint uses
the transactional rename only when the normalized name changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Creating and deleting a benefit writes the benefit document and the
screener's benefit list separately, so a partial failure can leave them
out of step. Export then failed with a generic 500 the user could not
fix. It now exports the benefits the editor lists, skipping list entries
with no stored benefit and stored benefits the list no longer includes,
and logs what it skipped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Export also bundled the author's current working draft of each check.
A draft that was mid-edit could fail import validation and reject a file
whose published versions were fine, and a successful import shared the
author's unpublished edits as the recipient's draft. The file now holds
only the referenced versions; import already seeds a new family's draft
from the latest version it receives.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Export read the form through getFormSchemaFromStorage, which logs and
returns null on any storage error, so a transient failure produced a
"complete" file with no form. Export now uses a read that distinguishes
a screener with no saved form from a failed read and lets the failure
reach the caller.

Export also now reads the screener document once: it authorizes with the
metadata and reads the form directly instead of fetching the screener a
second time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Import only checked that each model parsed and had a decision named
after the check, and trusted the file's inputDefinition. A hand-edited
or corrupted file could create check versions that fail at evaluation,
or ask the form for the wrong inputs. Import now compiles each model as
publishing does and replaces the file's inputDefinition with the one
derived from the model. Each distinct model is compiled once, after
the cheaper checks of the file and the screener name.

Reuse no longer compares input definitions: they are derived from the
model, which is already compared.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The import endpoint turned every WebApplicationException other than a
bad request into a generic "please try again" 500, so ownership and
not-found rejections looked retryable. It now returns their response,
as export already does.

Planning an import also threw when one of the account's stored checks
had an ID that did not match its version. Such checks are now left out
of matching and logged, instead of failing every import.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The import's early duplicate-name check compared stripped names with
equalsIgnoreCase, while saving compares strip().toLowerCase(Locale.ROOT).
For names such as "İncome" and "income" the two disagree, so the early
check could reject a name the save would accept, or the reverse, after
all checks and benefits had already been written. Both now use
Screener.normalizeName.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Screener transfer had its own copies of the rules for a configuration's
source check ID and for library check IDs, and the frontend had a second
server-error parser beside apiErrorFrom. Those rules now live once on
CheckConfig, used by both example and transfer import, and the screener
API calls use apiErrorFrom, which also carries the response status.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The import dialog parsed the chosen file to prefill its name, then
importScreener read and parsed the same file again on submit, for files
of up to 10 MB. The dialog now keeps the parsed object and importScreener
uploads it, applying the chosen name to a copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Exports no longer include the author's working draft, so the shared
file holds just the published version the benefit references.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@prestoncabe prestoncabe linked an issue Oct 6, 2026 that may be closed by this pull request
This info is in the user guide docs
@prestoncabe
prestoncabe merged commit 3bd52be into main Oct 6, 2026
5 checks passed
@prestoncabe
prestoncabe deleted the 538-importexport-of-screeners branch October 6, 2026 17:09
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.

Import/Export of screeners

1 participant