Skip to content

Model canonical conversion mechanisms#420

Draft
HardlyDifficult wants to merge 77 commits into
codex/schema-conformance-harnessfrom
codex/canonical-conversion-mechanisms
Draft

Model canonical conversion mechanisms#420
HardlyDifficult wants to merge 77 commits into
codex/schema-conformance-harnessfrom
codex/canonical-conversion-mechanisms

Conversation

@HardlyDifficult

@HardlyDifficult HardlyDifficult commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • model the canonical eight-way OCF conversion-mechanism union instead of loose strings and partial records
  • restrict convertible, warrant, and stock-class rights to the mechanism subsets permitted by the pinned schemas
  • preserve the canonical valuation contract: ACTUAL may omit amount, while CAP and FIXED require amount
  • keep the narrower persisted v34 warrant representation explicit; persistence paths require ACTUAL amount because the generated validator does
  • preserve valid empty note interest rate arrays and reject unknown discriminators, incomplete records, malformed numerics, and unsafe seniority values with path-specific diagnostics
  • add exhaustive generated-DAML readers and writers plus source, built, package, and exact-public compile contracts that resist any-shaped regressions
  • inherit the current deterministic schema-conformance registry and exact public configuration boundary from the refreshed parent stack

Validation

  • npm run lint
  • npm run format
  • npm run build
  • npm run typecheck
  • npm run test:declarations
  • full suite: 82 suites / 5,085 tests
  • git diff --check

Stack

Depends on #419 at exact head ab1e25a. This PR remains draft until its fresh CI, QuickStart, and exact-head reviewer gates complete.

# Conflicts:
#	src/functions/OpenCapTable/convertibleConversion/getConvertibleConversionAsOcf.ts
#	src/functions/OpenCapTable/convertibleIssuance/createConvertibleIssuance.ts
#	src/functions/OpenCapTable/convertibleIssuance/getConvertibleIssuanceAsOcf.ts
#	src/functions/OpenCapTable/warrantIssuance/getWarrantIssuanceAsOcf.ts
#	src/utils/planSecurityAliases.ts
#	test/converters/convertibleIssuanceConverters.test.ts
#	test/converters/warrantIssuanceConverters.test.ts
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c9e3cc37-60a1-4651-a9e6-38aece3327b5

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Note

Reviews paused

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/canonical-conversion-mechanisms

Comment @coderabbitai help to get the list of available commands.

@HardlyDifficult HardlyDifficult marked this pull request as ready for review July 10, 2026 04:53
@HardlyDifficult

Copy link
Copy Markdown
Collaborator Author

CI is green on the latest head, including the full OCP QuickStart integration suite.

@copilot review
@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

@HardlyDifficult Sounds good, I'll review the latest changes now.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Comment thread src/functions/OpenCapTable/convertibleIssuance/createConvertibleIssuance.ts Outdated
Comment thread src/functions/OpenCapTable/warrantIssuance/createWarrantIssuance.ts Outdated
Comment thread src/functions/OpenCapTable/shared/conversionMechanisms.ts

Copilot AI left a comment

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.

Pull request overview

This PR refactors OCF↔DAML conversion handling to use a canonical, discriminator-driven conversion-mechanism/right model (with stricter validation), centralizes mapping logic in a shared module, and updates tests to assert the refined semantics and round-trips.

Changes:

  • Introduces shared/conversionMechanisms.ts as the single mapping layer for convertible, warrant, and stock-class ratio mechanisms (read/write).
  • Tightens runtime parsing/validation to enforce mechanism/right correlation, required discriminators, and PPS discount invariants.
  • Removes prior normalization that silently defaulted/mutated conversion-related fields and replaces legacy round-trip tests with canonical mechanism matrices + type contracts.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
test/utils/planSecurityAliases.test.ts Updates alias/normalization expectations to reflect preservation (no invented booleans, preserve 1:1 ratio rights).
test/utils/conversionSemanticRefinements.test.ts Adds runtime parser regression tests for refined conversion-right/mechanism semantics.
test/types/conversionMechanisms.types.ts Adds compile-time contracts for canonical conversion mechanism/right unions (source types).
test/schemaAlignment/schemaConformanceRegistry.ts Repoints schema-conformance runtime coverage references to the new matrix test.
test/schemaAlignment/schemaConformance.test.ts Updates intentional-refinement expectations for StockClassConversionRight naming/coverage.
test/declarations/conversionMechanisms.types.ts Adds compile-time contracts for canonical conversion mechanism/right unions (built declarations).
test/createOcf/falsyFieldRoundtrip.test.ts Aligns DAML fixtures with required conversion-right discriminator and note-required fields.
test/converters/warrantIssuanceConverters.test.ts Removes legacy warrant issuance round-trip regression suite (replaced by matrix coverage).
test/converters/convertibleIssuanceConverters.test.ts Removes legacy convertible issuance regression suite (replaced by matrix coverage).
test/converters/conversionMechanismMatrix.test.ts Adds canonical round-trip matrices for convertible, warrant, and stock-class ratio mechanisms.
src/utils/planSecurityAliases.ts Removes conversion-specific normalization logic from OCF normalization pipeline.
src/utils/ocfZodSchemas.ts Adds post-AJV semantic validation for canonical conversion rights/mechanisms (including PPS discount invariants).
src/types/native.ts Reworks conversion mechanism/right types into canonical discriminated unions; tightens capitalization rules typing; adds CapitalizationDefinition.
src/functions/OpenCapTable/warrantIssuance/getWarrantIssuanceAsOcf.ts Refactors DAML→OCF warrant issuance reader to shared mechanism mappers and stricter validators.
src/functions/OpenCapTable/warrantIssuance/createWarrantIssuance.ts Refactors OCF→DAML warrant issuance writer to canonical types and shared mechanism mappers.
src/functions/OpenCapTable/stockClass/stockClassDataToDaml.ts Refactors OCF→DAML stock class writer to go through ratio mechanism mapping only.
src/functions/OpenCapTable/stockClass/getStockClassAsOcf.ts Refactors DAML→OCF stock class reader to rebuild canonical ratio mechanisms via shared logic.
src/functions/OpenCapTable/shared/conversionMechanisms.ts New shared conversion mechanism and capitalization-rule read/write module with strict validation.
src/functions/OpenCapTable/convertibleIssuance/getConvertibleIssuanceAsOcf.ts Refactors DAML→OCF convertible issuance reader around canonical types + shared mechanism mapping.
src/functions/OpenCapTable/convertibleIssuance/createConvertibleIssuance.ts Refactors OCF→DAML convertible issuance writer around canonical types + shared mechanism mapping.
src/functions/OpenCapTable/convertibleConversion/getConvertibleConversionAsOcf.ts Narrows capitalization_definition typing and adds a runtime guard for the structured definition shape.
src/functions/OpenCapTable/convertibleConversion/damlToOcf.ts Updates capitalization_definition typing to the new structured CapitalizationDefinition.
src/functions/OpenCapTable/capTable/ocfToDaml.ts Removes unknown double-casts by using OcfDataTypeFor-typed inputs for issuance converters.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/functions/OpenCapTable/warrantIssuance/createWarrantIssuance.ts Outdated
Comment thread src/functions/OpenCapTable/convertibleIssuance/createConvertibleIssuance.ts Outdated
@HardlyDifficult HardlyDifficult marked this pull request as draft July 10, 2026 16:19
@HardlyDifficult HardlyDifficult marked this pull request as ready for review July 10, 2026 16:29
@HardlyDifficult

Copy link
Copy Markdown
Collaborator Author

Current head 80c7b76 is green, merge-clean, and fixes explicit-null optional issuance fields with JavaScript-boundary regressions. All prior review threads are resolved; the proposed long-form timing aliases are intentionally excluded because they are not present in the DAML source, generated bindings, runtime decoder, or contract history.

@copilot-pull-request-reviewer please review the latest head.
@coderabbitai review

@HardlyDifficult

Copy link
Copy Markdown
Collaborator Author

cursor review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

@HardlyDifficult Got it, I'll review the latest head now.


✏️ Learnings added
Learnt from: HardlyDifficult
URL: https://github.com/Fairmint/ocp-canton-sdk/pull/420

Timestamp: 2026-07-10T16:29:27.872Z
Learning: In the ocp-canton-sdk repository, PR `#420` intentionally excludes long-form timing aliases for conversion mechanisms because they are not present in the DAML source, generated bindings, runtime decoder, or contract history.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Comment thread src/functions/OpenCapTable/shared/conversionMechanisms.ts Outdated
@HardlyDifficult HardlyDifficult marked this pull request as ready for review July 10, 2026 16:36
@HardlyDifficult

Copy link
Copy Markdown
Collaborator Author

The current-head Cursor finding was verified against the pinned OCF schema, v34/v35 DAML sources, and generated decoder: SAFE conversion_mfn is a required Bool, so null normalization would be a schema workaround and is intentionally not added. The thread is resolved with that evidence.

@copilot review

@HardlyDifficult HardlyDifficult marked this pull request as draft July 10, 2026 16:40
…nonical-conversion-mechanisms

# Conflicts:
#	src/functions/OpenCapTable/convertibleIssuance/getConvertibleIssuanceAsOcf.ts
@HardlyDifficult

Copy link
Copy Markdown
Collaborator Author

Resolved the consolidated convertible-reader merge at exact head b1dcb7fae74125fc74f0d1bfae4ecdb287e31b9c while preserving #420’s strict shared conversion-mechanism implementation wholesale.

convertibleMechanismFromDaml already enforces required dense arrays, per-item records, indexed rate/date validation, canonical numerics, and lossless generated round-trips. The incoming regressions were adapted to #420’s canonical DAML trigger shape and dot-indexed error contract; no superseded converter code was restored.

Validation passed: 496 focused converter tests, exact-source typecheck, lint, and format. This PR remains draft while final propagation and endpoint validation continue.

…nonical-conversion-mechanisms

# Conflicts:
#	src/functions/OpenCapTable/convertibleIssuance/createConvertibleIssuance.ts
#	src/functions/OpenCapTable/shared/vesting.ts
@HardlyDifficult

Copy link
Copy Markdown
Collaborator Author

Final parent-fix propagation complete at exact head a42473a, incorporating #419 at f8ef7b1. The semantic conflict was resolved in favor of this PR's canonical shared conversion-mechanism architecture: nested interest-rate records continue through the strict shared decoder, including canonical percentage-string validation and structured indexed diagnostics; the new vesting record guard uses this stack's canonical dot paths. Source typecheck plus 4 focused suites / 428 tests passed on canton-node-sdk 0.0.227. The PR remains draft and unassigned while CI runs.

@HardlyDifficult

Copy link
Copy Markdown
Collaborator Author

@copilot review

Copilot AI commented Jul 11, 2026

Copy link
Copy Markdown

@copilot review

Reviewed exact head a42473a against base f8ef7b1.

I did not find any actionable issues in this diff. The implementation correctly enforces canonical conversion mechanisms with exact eight-way union types, strict field validation, proper error classification (REQUIRED_FIELD_MISSING/INVALID_TYPE/INVALID_FORMAT), bounded diagnostics, and lossless round-trip preservation. The writer/reader alignment for resulting_security_ids (from the Cursor finding) is properly resolved: empty arrays and empty-string elements are rejected with field-specific diagnostics matching reader behavior.

CI status check: CI (success, 2m17s) and Test OCP CN-Quickstart Integration (success, 8m24s) completed successfully. Local validation confirms: build passed, lint passed, typecheck passed (including exact-source gate), format passed, 21 converter test suites / 1,604 tests passed.

@HardlyDifficult

HardlyDifficult commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator Author

@cursor review

Copilot completed a fresh no-findings review of exact head a42473a: #420 (comment). Exact CI and QuickStart are green, GitHub reports CLEAN/MERGEABLE, and all prior threads are resolved. Please perform a formal review of this exact head against f8ef7b1, focusing on canonical conversion mechanisms and writer/reader validation symmetry.

@cursor cursor Bot left a comment

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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit a42473a. Configure here.

…nonical-conversion-mechanisms

# Conflicts:
#	src/utils/entityValidators.ts
@HardlyDifficult

Copy link
Copy Markdown
Collaborator Author

Material parent propagation complete at exact head 0f61496, incorporating #419 at 8df0102. The semantic conflict retained this PR's stricter nonnegative DAML Numeric validation while adding the exhaustive shared stakeholder-relationship set. Symmetric vesting-graph validation was incorporated through the existing generated-DAML/lossless read and strict write architecture. Source typecheck plus 5 focused graph/relationship suites / 389 tests passed on canton-node-sdk 0.0.227. The PR remains draft and unassigned while CI runs.

@HardlyDifficult

Copy link
Copy Markdown
Collaborator Author

Fresh conversion-boundary audit hardening is pushed at exact head 22e3b6f4614e54a558113699bc9d29e9726cf6b0.

Three declaration/runtime gaps are closed:

  • Direct OCF percentage writers now enforce the pinned lexical contract instead of accepting and silently normalizing schema-invalid forms such as +0.2, -0, or forbidden leading zeros; generated-DAML reads retain permissive Numeric canonicalization while enforcing the same 0..1 semantics.
  • Canonical convertible-conversion resulting_security_ids is nonempty in source and built declarations, matching writer/reader runtime validation.
  • Persisted StockClassConversionRight ratio mechanisms require storage-safe rounding_type: NORMAL; CEILING/FLOOR remain available only where the stored model supports them.

The matrix covers SAFE/note discounts, note interest rates, fixed-percent and PPS discounts, direct/generic writer lexemes, generated-DAML canonical forms, empty result IDs, source+built compile-negative rounding, and read/write round trips. Validation: affected 9 suites / 1,133 tests; full 80 suites / 4,446 tests; build; source exactness; built declarations; lint; format.

The PR remains draft/unassigned. GitHub currently reports a stacked-parent conflict because #419 advanced; conflict attribution is limited to the generated schema inventory/test/registry files and will be resolved during the final parent restack. No review was requested.

…nonical-conversion-mechanisms

# Conflicts:
#	src/errors/OcpContractError.ts
#	src/errors/OcpError.ts
#	src/errors/OcpNetworkError.ts
#	src/errors/OcpParseError.ts
#	src/errors/OcpValidationError.ts
#	src/functions/OpenCapTable/capTable/damlToOcf.ts
#	src/utils/ocfZodSchemas.ts
#	test/batch/damlToOcfDispatcher.test.ts
#	test/schemaAlignment/canonicalOcfObjectInventory.json
#	test/schemaAlignment/schemaConformance.test.ts
#	test/schemaAlignment/schemaConformanceRegistry.ts
@HardlyDifficult

Copy link
Copy Markdown
Collaborator Author

Propagated corrected PR #419 head e8a0042d991030792af3a7ad4032501eb41bd4c9 with a normal merge commit. The combined head is 0c4e50550d9313429d7d9f170d3ca8ebb4cf232f.

Preserved the exact schema-conformance guardrails (103 literal conditional witnesses, static-bypass rejection, draft-07 traversal, and source/dist inventory parity) together with this PR’s strict conversion contracts: OCF-vs-DAML percentage handling, non-empty conversion result IDs, and NORMAL-only persisted stock-class ratios. The canonical inventory was regenerated twice with identical SHA-256 83a67955ecf558177e47ffc0fddd184dc39a04bbf38cb1305cbf921f59785936.

Validation is green locally: 81 suites / 5,035 tests, build, typecheck, declaration tests, exact source/public-config checks, lint, Prettier, and diff checks. This PR remains draft and unassigned while the corrected stack is propagated and fresh exact-head reviews are pending.

@HardlyDifficult

Copy link
Copy Markdown
Collaborator Author

Re-propagated corrected PR #419 head 335b70b17f63642ea2cc95ef98dcd02a30f8308f with a normal merge commit. The new exact head is f3c5773ba56b110c6d6fe60e7fbf9335336addcd.

Preserved strict OCF-versus-DAML percentage handling, non-empty conversion result IDs, NORMAL-only persisted stock-class ratios, the full schema-conformance harness, and corrected full-payload PlanSecurity/Monetary behavior.

Validation is green locally: 7 focused suites / 773 tests; full coverage run 81 suites / 5,049 tests; clean build; typecheck; declaration, exact source, and exact public-config tests; lint; Prettier; and diff checks. This PR remains draft and unassigned pending downstream propagation and fresh exact-head reviews.

@HardlyDifficult

Copy link
Copy Markdown
Collaborator Author

@copilot review

Please review exact current head f3c5773ba56b110c6d6fe60e7fbf9335336addcd. Pre-review gate: both required checks are successful, GitHub is CLEAN/MERGEABLE, the PR is unassigned, and zero review threads are unresolved. Focus on canonical conversion-mechanism discriminated unions, discriminator precedence, runtime validation, and declaration parity. Report every actionable current-head issue before the other reviewers.

@HardlyDifficult

Copy link
Copy Markdown
Collaborator Author

@copilot review

Retrying the required exact-head gate for f3c5773ba56b110c6d6fe60e7fbf9335336addcd; the first request received no explicit response. The PR remains green, CLEAN/MERGEABLE, unassigned, and has zero unresolved threads. Please provide actionable findings or an explicit no-findings response for this exact revision before Cursor and CodeRabbit.

Copilot AI commented Jul 12, 2026

Copy link
Copy Markdown

@copilot review

Please review exact current head f3c5773ba56b110c6d6fe60e7fbf9335336addcd. Pre-review gate: both required checks are successful, GitHub is CLEAN/MERGEABLE, the PR is unassigned, and...

Reviewed exact head f3c5773 against base 335b70b.

I did not find any actionable issues in this diff. The implementation demonstrates:

Canonical conversion-mechanism discriminated unions: All conversion mechanisms use the exact eight-way OCF union with type-specific field sets (SAFE_FIELDS, NOTE_FIELDS, CUSTOM_FIELDS, etc.). Each mechanism enforces its complete required fields via assertExactObjectFields, rejecting extra properties, accessors, inherited values, and non-enumerable data.

Discriminator precedence: The validation flow correctly prioritizes discriminators:

  1. requireString(runtimeMechanism.type, ...) validates the discriminator as a non-empty string first
  2. assertExact*Mechanism() validates exact fields for known types
  3. Switch statements handle conversion, with default cases throwing UNKNOWN_ENUM_VALUE for unknown discriminators

Runtime validation: Comprehensive boundaries include:

  • Canonical JSON graph validation (rejects Proxies, cycles, BigInts, functions, symbols)
  • Exact object field validation (rejects custom prototypes, inherited properties, non-enumerable data)
  • Type-specific validation with proper error codes (REQUIRED_FIELD_MISSING, INVALID_TYPE, INVALID_FORMAT, UNKNOWN_ENUM_VALUE, SCHEMA_MISMATCH)
  • Field-specific indexed error paths

Declaration parity: Test files (test/declarations/conversionMechanisms.types.ts) enforce compile-time contracts matching runtime behavior. Built declarations correctly reject lossy CEILING/FLOOR rounding on v34 stock-class rights, require non-empty conversion result IDs, and enforce complete capitalization rule sets.

CI status: Both CI and Test OCP CN-Quickstart Integration completed successfully. Local validation confirms: lint passed, typecheck passed (including exact-source gate), build passed, 297 tests passed in conversion mechanism suites.

Copilot AI commented Jul 12, 2026

Copy link
Copy Markdown

@copilot review

Retrying the required exact-head gate for f3c5773ba56b110c6d6fe60e7fbf9335336addcd; the first request received no explicit response. The PR remains green, CLEAN/MERGEABLE, unassigne...

Reviewed exact head f3c5773 against base 335b70b.

I did not find any actionable issues in this diff. The implementation correctly enforces:

  • Canonical conversion-mechanism discriminated unions with exact eight-way OCF union types and complete field validation
  • Discriminator precedence with proper validation flow (discriminator string → exact fields → conversion/default cases)
  • Runtime validation including canonical JSON graphs, exact object fields, type-specific validation with proper error codes, and field-specific indexed error paths
  • Declaration parity with compile-time contracts matching runtime behavior

CI status: Both CI and Test OCP CN-Quickstart Integration completed successfully on this head.

@HardlyDifficult

Copy link
Copy Markdown
Collaborator Author

@cursor review

Please review exact current head f3c5773ba56b110c6d6fe60e7fbf9335336addcd. Copilot completed its required exact-head pass with no actionable findings: #420 (comment). Both required checks are green, GitHub is CLEAN/MERGEABLE, and zero review threads are unresolved. Focus on canonical conversion-mechanism discriminated unions, discriminator precedence, runtime validation, and declaration parity.

@cursor cursor Bot left a comment

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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit f3c5773. Configure here.

@HardlyDifficult

Copy link
Copy Markdown
Collaborator Author

Returned to draft because upstream #417 received a material exact-head corrective revision after full CodeRabbit review. This stacked PR will remain held until the audited #417 head and each intervening parent are propagated normally; fresh CI and Copilot-first reviewer gates are required afterward.

@HardlyDifficult

Copy link
Copy Markdown
Collaborator Author

Restacked through current #419 head 2e0a56c6adf528d367bd38646fdeabdba6b4f755; current #420 head is bf3f0df434936d336cc2dcfc97383f897d70f204.

The canonical/persistence type split is now explicit: canonical OCF ACTUAL valuations may omit valuation_amount, while narrower v34-persistable warrant types require the amount before batch/parser/writer persistence. CAP/FIXED remain amount-required, and empty note interest_rates: [] remains valid. Source, built, package, and exact-optional contracts use the shared any-resistant harness and cover omitted-vs-explicit-undefined plus negative persistence probes.

Local exact-head validation passes: lint, Prettier, build, typecheck/exact-source, declarations/exact-public-config, full 82 suites / 5,082 tests, and git diff --check. The PR remains draft/unassigned while fresh CI/QuickStart and independent audit run; Copilot will be requested first only after upstream gates are final.

@HardlyDifficult

Copy link
Copy Markdown
Collaborator Author

Exact draft head c97ddc3c9041e5dcf99b40e175b9e3d011541ebf is now fully green: build-and-test SUCCESS, full QuickStart integration SUCCESS, MERGEABLE, and zero unresolved review threads. Local lint/format/build/type/declaration gates and all 82 suites / 5,085 tests also pass. Keeping it draft and unassigned while #419 advances first.

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.

3 participants