Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
25 KiB
25 KiB
SCRATCHPAD — Invoice Workflow Expression Binding Unification
- Plan slug:
invoice-workflow-expression-unification - Created:
2026-03-04
What This Is
Working notes for unifying invoice designer bindings and Workflow v2 expression authoring on a shared expression foundation while preserving each runtime/storage contract.
Decisions
- (2026-03-04) Use a shared abstraction centered on typed context paths + expression modes (
path-only,template,expression) instead of forcing one persisted expression format across products. - (2026-03-04) Shared primitives module location:
shared/workflow/expression-authoring/*so both invoice (packages/billing) and workflow designer (ee/server) can import through@shared/workflow/.... - (2026-03-04) Shared context contract includes explicit
SharedExpressionContextRootandSharedExpressionPathOptioninterfaces plus deterministic root serialization to stabilize cross-editor behavior/tests. - (2026-03-04) Shared path discovery contract uses deterministic lexical traversal with explicit array item marker segments (
[]) and additional-property wildcard (*) to produce stable picker options. - (2026-03-04) Shared insertion helper is split into pure value insertion (
insertTextIntoValue) plus environment-specific adapters (insertTextIntoDomControl,insertTextIntoMonacoEditor) with explicit no-op reasons. - (2026-03-04) Shared validation contract (
SharedExpressionValidationResult) normalizes severity ordering and preserves path/root/range attribution for downstream UI rendering. - (2026-03-04) Invoice adapter roots stay domain-native (
invoice,customer,tenant,item) for this migration to preserve existing author mental model and template bindings. - (2026-03-04) Workflow adapter includes dynamic loop roots (
itemVar,indexVar) so forEach-scoped expressions can use the same shared path options as global roots. - (2026-03-04) Invoice palette fields now derive from
buildInvoiceExpressionPathOptions(...); static catalog import path is no longer used in rendering/search. - (2026-03-04) Invoice FIELDS panel grouping now maps directly to shared root keys (
invoice/customer/tenant/item) and only renders leaf path options from the shared model. - (2026-03-04)
DesignerShellnow usesinsertTextIntoDomControland target-path mode inference: inspector fields ending inbindingKeyreceive raw path insertion (path-only). - (2026-03-04) Invoice text fallback insertion path also uses shared insertion primitives (
insertTextIntoValue) and enforces template token formatting for text/label nodes. - (2026-03-04) Workspace AST export now parses moustache text edits into dynamic expressions: full-token moustache becomes
path, mixed literal+moustache becomestemplatewith deterministic arg keys. - (2026-03-04) Existing roundtrip suites (
workspaceAst.roundtrip.*) remain green after adapter/insertion changes, confirming deterministic export/import behavior. - (2026-03-04) Shared path validator (
validateSourcePaths) now powers invoice insertion feedback; unknown paths emit informational diagnostics without breaking insertion flow. - (2026-03-04) Workflow step config validation now derives shared path options from
DataContextviabuildWorkflowExpressionPathOptions(...)and usesvalidateSourcePaths(...)in place of legacy${...}scanners. - (2026-03-04) Workflow step validation UI now consumes shared diagnostic objects directly (
severity,code,message,path) and renders grouped Error/Warning/Info cards from the shared contract. - (2026-03-04) Runtime function allowlist is now centralized in
shared/workflow/runtime/expressionFunctions.tsand consumed by both runtime validation and Monaco completion generation. - (2026-03-04) Workflow function completion/snippet suggestions are now constrained to runtime-allowlisted helpers (
$nowIso,$coalesce,$len,$toString,$append). - (2026-03-04) Workflow diagnostics now validate
$function(...)tokens against runtime-allowlisted helpers only; non-allowlisted JSONata built-ins are flagged as unknown in-editor. - (2026-03-04) Workflow persisted expression shape remains
{ $expr: string }; migration changes were limited to authoring/validation paths and retainedensureExpr+ runtime schema guards. - (2026-03-04) Added focused runtime guardrail tests (
expressionEngine.test.ts) to lock function allowlist rejection, timeout enforcement, JSON-serializable result checks, and max output size checks. - (2026-03-04) Shared schema/context-aware path checks (
validateSourcePaths) are now used in both designers where author-time validation exists:- invoice insertion feedback (
DesignerShell) - workflow step config validation (
WorkflowDesigner)
- invoice insertion feedback (
- (2026-03-04) Workflow Monaco insertion paths now route through shared
insertTextIntoMonacoEditorfor both programmatic insert and drag/drop insert, with existing$0placeholder stripping preserved. - (2026-03-04) Invoice input/textarea insertion remains shared-helper based (
insertTextIntoDomControl+insertTextIntoValue) for selection-aware replacement and repeated insert behavior. - (2026-03-04) Invoice preview interpolation semantics remain intact post-migration (resolved known tokens + visibly marked unresolved tokens), validated by preview-mode suite.
- (2026-03-04) Workflow runtime expression suites (
expressionEngine,mappingResolver,mappingValidator) remain green after migration; contracts for{ $expr }evaluation/validation are preserved. - (2026-03-04) Deprecated invoice static variable catalog path is removed; field discovery now only flows through shared adapter-driven options.
- (2026-03-04) Deprecated workflow
${...}scanner helpers are removed from step validation flow; only shared-path validation remains. - (2026-03-04) Test implementation standards are codified in
PRD.mdand operationalized as explicit meta-test backlog items (T041,T043,T044) for checklist enforcement. - (2026-03-04) Added shared expression-authoring foundational unit suites:
shared/workflow/expression-authoring/__tests__/coreContracts.test.tsshared/workflow/expression-authoring/__tests__/insertionDom.test.tsThese cover T001-T012 contract behaviors.
- (2026-03-04) T002 verified by
coreContracts.test.tsroundtrip serialization/deserialization assertions for shared context roots. - (2026-03-04) T003 verified by deterministic path ordering assertions in
coreContracts.test.ts(buildPathOptionsFromContextRootsnested schema flattening). - (2026-03-04) T004 verified by array marker segment assertions in
coreContracts.test.ts(payload.items[]andpayload.items[].id). - (2026-03-04) T005 verified in
insertionDom.test.tswith start/middle/end insertion coverage forinsertTextIntoValue. - (2026-03-04) T006 verified in
insertionDom.test.tsby replacing an active range ({{invoice.total}}->{{customer.name}}). - (2026-03-04) T007 verified in
insertionDom.test.tswith explicit readonly/disabled/unfocused no-op assertions forinsertTextIntoDomControl. - (2026-03-04) T008 verified via
coreContracts.test.tsvalidation-result normalization assertions (severity fallback + deterministic diagnostic ordering). - (2026-03-04) T009 verified via
coreContracts.test.tsinvoice adapter assertions (roots + canonical paths for invoice/customer/tenant/item). - (2026-03-04) T010 verified via
coreContracts.test.tsstable label assertions for known invoice adapter roots/paths. - (2026-03-04) T011 verified via
coreContracts.test.tsworkflow adapter root emission assertions (payload/vars/meta/error + forEach vars). - (2026-03-04) T012 verified via
coreContracts.test.tsrecursive vars-path assertions (vars.previous.account.id). - (2026-03-04) T013 validated with
ComponentPalette.fields.integration.test.tsxby exercising FIELDS tab insertion path callback using adapter-derived field options. - (2026-03-04) T014 validated with
ComponentPalette.fields.integration.test.tsxsearch assertions (query filters adapter-derived labels/paths). - (2026-03-04) Preserve persisted contracts:
- Invoice keeps AST value expressions (
literal|binding|path|template). - Workflow keeps
{ $expr: string }.
- Invoice keeps AST value expressions (
- (2026-03-04) Migrate incrementally via adapters; no big-bang rewrite.
- (2026-03-04) Treat runtime allowlist as source of truth for workflow function capability; editor metadata must conform to it.
- (2026-03-04) Keep plan scope focused on logic/functionality and correctness; exclude metrics/observability/production-readiness additions.
- (2026-03-04) Replace doc-only feature/test entries with explicit test implementation standards and enforceable meta-tests (framework, selector strategy, anti-tautology, feature-test traceability).
Discoveries / Constraints
- (2026-03-04) This branch already had uncommitted invoice-designer WIP focused on static
templateVariableCatalogand local insertion logic; foundational shared abstractions were still missing. - (2026-03-04) Invoice currently uses multiple authoring paths:
metadata.bindingKey(path string)- moustache text in
metadata.text - AST conversion in
workspaceAst.ts.
- (2026-03-04) Invoice preview now resolves moustache interpolation in canvas preview path but this is distinct from export compilation.
- (2026-03-04) Workflow currently has duplicated expression validation paths:
- Monaco diagnostics provider
- Legacy
${...}extraction/validation inWorkflowDesigner.tsx.
- (2026-03-04)
WorkflowDesigner.tsxno longer definesextractExpressionPaths/validateExpressionPath; step validation now emits issues from shared validation diagnostics. - (2026-03-04) Editor metadata previously exposed broad JSONata helpers; completion hints were not runtime-aligned until runtime allowlist filtering was introduced.
- (2026-03-04) Workflow editor function metadata includes many JSONata functions, while runtime currently allowlists only a small helper set in
shared/workflow/runtime/expressionEngine.ts. - (2026-03-04) Removing drift between workflow editor and runtime is required to avoid misleading UX.
Commands / Runbooks
- Implement F001 shared mode contract:
shared/workflow/expression-authoring/modes.ts(EXPRESSION_MODES,ExpressionMode,isExpressionMode)
- Implement F002 context and path option contracts:
shared/workflow/expression-authoring/context.ts
- Implement F003 path discovery helper:
shared/workflow/expression-authoring/pathDiscovery.ts
- Implement F004 insertion helper API:
shared/workflow/expression-authoring/insertion.ts
- Implement F005 validation result contract:
shared/workflow/expression-authoring/validation.ts
- Implement F006 invoice adapter:
shared/workflow/expression-authoring/adapters/invoiceContextAdapter.ts
- Implement F007 workflow adapter:
shared/workflow/expression-authoring/adapters/workflowContextAdapter.ts
- Validate invoice field palette integration after adapter wiring:
cd server && npx vitest run ../packages/billing/src/components/invoice-designer/palette/ComponentPalette.fields.integration.test.tsx
- Validate invoice shell + fields insertion wiring:
cd server && npx vitest run ../packages/billing/src/components/invoice-designer/palette/ComponentPalette.fields.integration.test.tsx ../packages/billing/src/components/invoice-designer/DesignerShell.selectedContext.integration.test.tsx
- Validate moustache-to-AST behavior:
cd server && npx vitest run ../packages/billing/src/components/invoice-designer/ast/workspaceAst.test.ts
- Validate roundtrip determinism:
cd server && npx vitest run ../packages/billing/src/components/invoice-designer/ast/workspaceAst.roundtrip.templates.test.ts ../packages/billing/src/components/invoice-designer/ast/workspaceAst.roundtrip.nodes.test.ts ../packages/billing/src/components/invoice-designer/ast/workspaceAst.roundtrip.styles.test.ts
- Validate invoice insertion + validation integration:
cd server && npx vitest run ../packages/billing/src/components/invoice-designer/ast/workspaceAst.test.ts ../packages/billing/src/components/invoice-designer/palette/ComponentPalette.fields.integration.test.tsx ../packages/billing/src/components/invoice-designer/DesignerShell.selectedContext.integration.test.tsx
- Validate workflow editor test baseline after validation migration:
cd ee/server && npx vitest run src/components/workflow-designer/expression-editor/__tests__/completionProvider.test.ts src/components/workflow-designer/expression-editor/__tests__/diagnosticsProvider.test.ts
- Validate workflow designer type safety after shared validation UI refactor:
cd ee/server && npm run -s typecheck
- Validate allowlisted function completions and diagnostics baseline:
cd ee/server && npx vitest run src/components/workflow-designer/expression-editor/__tests__/completionProvider.test.ts src/components/workflow-designer/expression-editor/__tests__/diagnosticsProvider.test.ts
- Validate diagnostics allowlist behavior and type safety:
cd ee/server && npx vitest run src/components/workflow-designer/expression-editor/__tests__/diagnosticsProvider.test.tscd ee/server && npm run -s typecheck
- Audit persisted expression contract usage:
rg -n '\\$expr' ee/server/src/components/workflow-designer/WorkflowDesigner.tsx shared/workflow/runtime -g'*.ts*'
- Validate runtime expression guardrails:
npx vitest run --config shared/vitest.config.ts shared/workflow/runtime/__tests__/expressionEngine.test.ts shared/workflow/runtime/__tests__/mappingResolver.test.ts
- Audit shared path-validator usage in both designers:
rg -n "validateSourcePaths\\(" packages/billing/src/components/invoice-designer ee/server/src/components/workflow-designer -g'*.ts*'
- Validate workflow expression editor insertion refactor:
cd ee/server && npx vitest run src/components/workflow-designer/expression-editor/__tests__/completionProvider.test.ts src/components/workflow-designer/expression-editor/__tests__/diagnosticsProvider.test.ts && npm run -s typecheck
- Validate invoice preview interpolation behavior:
cd server && npx vitest run ../packages/billing/src/components/invoice-designer/canvas/DesignCanvas.previewMode.test.tsx
- Validate workflow runtime expression behavior:
npx vitest run --config shared/vitest.config.ts shared/workflow/runtime/__tests__/expressionEngine.test.ts shared/workflow/runtime/__tests__/mappingResolver.test.ts shared/workflow/runtime/__tests__/mappingValidator.test.ts
- Audit for deprecated invoice static catalog references:
rg -n "templateVariableCatalog|TEMPLATE_VARIABLE_OPTIONS" packages/billing/src/components/invoice-designer -g'*.ts*'
- Audit for deprecated workflow scanner helpers:
rg -n "extractExpressionPaths|validateExpressionPath|\\$\\{\\[^\\}\\]\\+\\}" ee/server/src/components/workflow-designer -g'*.ts*'
- Run shared expression-authoring foundational tests:
npx vitest run --config shared/vitest.config.ts shared/workflow/expression-authoring/__tests__/coreContracts.test.ts shared/workflow/expression-authoring/__tests__/insertionDom.test.ts
- Compare invoice/workflow binding/expression surfaces:
rg -n "binding|template|\{\{|\$expr|validateExpressionSource" packages/billing/src/components/invoice-designer ee/server/src/components/workflow-designer shared/workflow/runtime -g"*.ts*"
- Review workflow designer expression sections:
sed -n '820,980p' ee/server/src/components/workflow-designer/WorkflowDesigner.tsxsed -n '6060,6665p' ee/server/src/components/workflow-designer/WorkflowDesigner.tsx
- Review workflow runtime constraints:
sed -n '1,220p' shared/workflow/runtime/expressionEngine.ts
- Review invoice AST compile/render paths:
sed -n '104,340p' packages/billing/src/components/invoice-designer/ast/workspaceAst.tssed -n '210,290p' packages/billing/src/lib/invoice-template-ast/react-renderer.tsx
Links / References
- Workflow designer:
ee/server/src/components/workflow-designer/WorkflowDesigner.tsxee/server/src/components/workflow-designer/expression-editor/
- Workflow runtime:
shared/workflow/runtime/expressionEngine.tsshared/workflow/runtime/types.ts
- Invoice designer:
packages/billing/src/components/invoice-designer/DesignerShell.tsxpackages/billing/src/components/invoice-designer/palette/ComponentPalette.tsxpackages/billing/src/components/invoice-designer/ast/workspaceAst.ts
- Invoice AST renderer:
packages/billing/src/lib/invoice-template-ast/react-renderer.tsx
Open Questions
- Should invoice roots stay domain-native (
invoice/customer/tenant/item) or converge on workflow-style roots for long-term consistency? - Should workflow function metadata be hard-pruned immediately to runtime allowlist, or staged with compatibility warnings first?
- Should shared validation include a true parser abstraction now, or remain an interface with domain-specific parser implementations?
- Is invoice drag/drop path insertion in scope for this plan, or deferred after click-insert parity?
- (2026-03-04) T015 validated by
DesignerShell.insertion.integration.test.tsx: bindingKey-target insertions write rawinvoice.totalpath tokens without moustache wrapping. - (2026-03-04) T016 validated by
DesignerShell.insertion.integration.test.tsx: non-binding text targets insert moustache-wrapped tokens in template mode (Before {{invoice.total}}). - (2026-03-04) T017 validated by
DesignerShell.insertion.integration.test.tsx: when no insert target is focused, insertion falls back to appending template token into selected text-nodemetadata.text. - (2026-03-04) T018 validated by
workspaceAst.test.ts(compiles edited moustache text into a dynamic path expression):{{invoice.total}}compiles to ASTcontent: { type: 'path', path: 'total' }. - (2026-03-04) T019 validated by
workspaceAst.test.ts(compiles mixed literal + moustache text into a template expression): mixed text exports as ASTtemplatewith args includinginvoiceNumberanddueDatepaths. - (2026-03-04) T020 validated by
workspaceAst.test.ts(preserves expression and format semantics when importing existing AST templates): import/export with unchanged imported content preserves original expression kinds/bindings (non-destructive text preview path). - (2026-03-04) T021 validated by
workspaceAst.test.ts(roundtrips exported AST deterministically (export -> import -> export)): repeated export/import cycles are byte-stable for AST shape. - (2026-03-04) Added
DesignerShell.insertion.integration.test.tsxcoverage for T022: insertinginvoice.missingFieldsurfaces shared-validator feedback (Unknown path ...) and subsequent replacement withinvoice.totalsucceeds without UI crash. - (2026-03-04) Introduced
workflow-designer/expressionValidation.tsand test coverage provingWorkflowDesigner.tsxno longer references legacyextractExpressionPaths/validateExpressionPath; step config validation uses shared validator wiring. - (2026-03-04) T024 covered in
expressionValidation.test.ts:partitionStepExpressionValidationsconsumes shared diagnostics contract (severity/message/code) and splits panel-facing error/warning/info groups deterministically. - (2026-03-04) T025 covered in
expressionValidation.test.ts:validateStepExpressions(...)now flagsunknown.rootandvars.missing.idvia shared validator diagnostics while accepting knownvars.previous.id. - (2026-03-04) T026 validated by
expression-editor/__tests__/completionProvider.test.ts: function completion items are constrained to runtime allowlisted helpers only. - (2026-03-04) T027 validated by
expression-editor/__tests__/diagnosticsProvider.test.ts: disallowed/non-allowlisted functions are diagnosed and runtime-allowlisted helpers pass. - (2026-03-04) Added
shared/workflow/runtime/__tests__/types.exprPersistence.test.tsfor T028: step schemas persist expressions as{ $expr: string }objects (and reject bare string Expr inputs). - (2026-03-04) T029 validated by
shared/workflow/runtime/__tests__/expressionEngine.test.ts: runtime validation rejects disallowed functions (e.g.$sum(...)). - (2026-03-04) T030 validated by
expressionEngine.test.ts: evaluation timeout and max output-size guardrails remain enforced post-migration. - (2026-03-04) T031 added to
coreContracts.test.ts: sharedvalidateSourcePaths(...)returnsinfo/unknown-pathdiagnostic for unresolved schema-aware path references (payload.user.missing). - (2026-03-04) T032 covered by
shared/workflow/expression-authoring/__tests__/insertionDom.test.ts: Monaco-style insertion now verifies shared helper replacement semantics and cursor/selection placement at the inserted text end. - (2026-03-04) T033 validated via
expression-editor/__tests__/insertionText.test.ts: trailing snippet placeholder ($0) is stripped before insertion, while non-trailing text remains unchanged. - (2026-03-04) T034 added to
DesignerShell.insertion.integration.test.tsx: repeated template insertions with manual typing in between remain cursor-accurate and produce expected final text. - (2026-03-04) T035 validated by
DesignCanvas.previewMode.test.tsx: preview rendering resolves known moustache tokens and leaves unresolved tokens visibly marked. - (2026-03-04) T036 validated by
DesignCanvas.previewMode.test.tsxregression run (17 passing tests), covering field/table/totals preview scaffolds. - (2026-03-04) T037 validated by
server/src/test/unit/workflowRuntimeV2.unit.test.ts(33 passing tests): workflow runtime v2 expression compile/evaluate/resolve unit suite remains green. - (2026-03-04) T038 validated by
server/src/test/integration/workflowRuntimeV2.control.integration.test.ts+workflowRuntimeV2.eventTrigger.integration.test.ts(86 passing tests total). Required fixes:- improved
@alga-psa/authtest mock inserver/src/test/setup.tsto provide tenant-awarewithAuth/withOptionalAuthcontext (using mocked db tenant where available), - added
@alga-psa/corealias coverage inserver/vitest.config.tsto resolve document dependency imports in server Vitest runs, - updated integration test tenant mocks to use
mockImplementation(() => tenantId)for mutable tenant scenarios, - updated event-trigger tests to use trigger
sourcePayloadSchemaRefand schema-aware invalid-payload expectations aligned with current runtime contract.
- improved
- (2026-03-04) T039 validated by new repo-guard test
templateVariableCatalog.removal.test.ts: static catalog fileconstants/templateVariableCatalog.tsis absent and no source files under invoice-designer reference legacyconstants/templateVariableCatalog/TEMPLATE_VARIABLE_OPTIONS. - (2026-03-04) T040 validated by
ee/server/src/components/workflow-designer/__tests__/expressionValidation.test.ts(3 passing tests), including explicit source assertions thatWorkflowDesigner.tsxno longer references legacyextractExpressionPathsorvalidateExpressionPathhelpers. - (2026-03-04) T041 validated by new meta-test
testImplementationStandards.meta.test.ts: scoped React UI tests are enforced to import Vitest + Testing Library and, unless explicitly marked render-semantics-only, include role/automation-based selector signals. - (2026-03-04) T042 validated in one local session by running invoice + workflow smoke insertion tests:
cd server && npx vitest run ../packages/billing/src/components/invoice-designer/DesignerShell.insertion.integration.test.tsx(5 passing tests),cd ee/server && npx vitest run src/components/workflow-designer/expression-editor/__tests__/ExpressionEditor.smoke.test.tsx(1 passing test). This proves both designers render and accept at least one inserted binding path without runtime errors.
- (2026-03-04) T043 validated by new meta-guard
packages/billing/src/components/invoice-designer/nonTautologyAssertions.meta.test.ts:- requires semantic assertion signals across scoped tests for AST behavior (
workspaceAst.test.tspath/template checks), - model/feedback behavior (
DesignerShell.insertion.integration.test.tsxstore mutation, diagnostics, cursor assertions), - shared workflow diagnostics behavior (
ee/server/.../expressionValidation.test.tspath attribution + severity partitioning). Verification run: cd server && npx vitest run ../packages/billing/src/components/invoice-designer/nonTautologyAssertions.meta.test.ts ../packages/billing/src/components/invoice-designer/DesignerShell.insertion.integration.test.tsx ../packages/billing/src/components/invoice-designer/ast/workspaceAst.test.tscd ee/server && npx vitest run src/components/workflow-designer/__tests__/expressionValidation.test.ts
- requires semantic assertion signals across scoped tests for AST behavior (
- (2026-03-04) T044 validated by new meta-test
packages/billing/src/components/invoice-designer/featureTraceability.meta.test.ts:- loads plan
features.json+tests.json, - asserts every
Fxxxin features has at least one reference in testsfeatureIds, - asserts no tests reference unknown feature IDs. Verification run:
cd server && npx vitest run ../packages/billing/src/components/invoice-designer/featureTraceability.meta.test.ts
- loads plan