Skip to content

Restore fresh install, harden CI, fix doc drift and audit findings - #22

Open
koriym wants to merge 19 commits into
1.xfrom
fix-fresh-install
Open

koriym wants to merge 19 commits into
1.xfrom
fix-fresh-install

Conversation

@koriym

@koriym koriym commented Jul 17, 2026 •

Copy link
Copy Markdown
Contributor

Why

A fresh composer install in every demo has been failing since 2026-06-10:

Problem 1
  - Root composer.json requires be-framework/psalm-plugin 1.x-dev -> satisfiable by be-framework/psalm-plugin[1.x-dev].
  - be-framework/psalm-plugin 1.x-dev requires vimeo/psalm ^7.0@dev -> found vimeo/psalm[dev-master, 7.0.0-beta1, ...] but it does not match your minimum-stability.

psalm-plugin's Psalm 7 support (be-framework/psalm-plugin#3) switched its requirement to vimeo/psalm ^7.0@dev. Composer ignores stability flags in transitive constraints — only the root package may allow non-stable versions — and Psalm 7 has no stable release, so resolution fails on every fresh clone. CI never noticed because the last run predates the upstream change and CI only fires on push/PR.

While fixing that, a full audit of the catalog (docs-vs-code, CLAUDE.md invariants, test coverage, code quality) surfaced further issues, fixed in the follow-up commits.

What

Repo health

  • composer.json (x8): declare vimeo/psalm: ^7.0@beta in require-dev so the root permits the beta line (resolves to 7.0.0-beta19). Verified: all eight demos install from scratch and pass.
  • tests.yml: add weekly schedule + workflow_dispatch — the demos depend on dev branches, so upstream changes can break a fresh install without any push here.
  • .gitignore: ignore generated var/log/semantic-dev-*.json dev logs.
  • composer.json metadata: add 4 missing descriptions and MIT license everywhere; composer validate --strict now passes in all demos.

Documentation accuracy (this repo is an AI-assistant contract)

  • CLAUDE.md §6 told agents to run composer test at the root and ./demos/vendor/bin/phpunit — neither path exists. Now documents the real per-demo workflow.
  • CLAUDE.md §3-8 required interfaces for clock/randomness Reasons, which the demos consistently don't do; the rule now documents the observed boundary (external systems → interface; in-process generators/policies → concrete), continuing e9e9c35.
  • README/README.ja: PHP 8.2+ → 8.3+ (composer requires ^8.3); the test-coverage claim now matches reality; the insurance-claim entry no longer claims a $being type-matching mechanism that only medical-triage implements.
  • PHILOSOPHY.md / After_BeFramework.php showed a fictitious #[Moment] attribute — copying it verbatim fatals. Replaced with the real #[Inject].
  • MomentTemplate now notes the qualifier-attribute convention its cited canonical example actually uses.
  • blog-publishing composer description described unwired classes; now names the demo's real pattern.

Bugs

  • CardExpiry: createFromFormat('m/y') fills day/time from "now", so on the 29th–31st an expiry in a shorter month overflowed into the next month (expired card accepted), and a card expiring in the current month was rejected on the month's last day. Now uses !Y-m + first-of-month comparison, the semantics CardValidator already had. Added the missing CardExpiryTest.
  • IncomePolicy: $this->lastDti ?: 0.25 silently replaced a legitimately computed DTI of 0.0 (huge income, small loan) with the fallback. Now null-tracked with ??, plus a regression test.
  • PasswordHasher: removed the unreachable password_hash() === false branch (impossible since PHP 8.0) and its generic RuntimeException.

Invariant violations

  • Constructor parameter order (invariant 4): LoanApproved and MetadataResolved interleaved #[Input]/#[Inject]. Reordered; hand-built test constructions switched to named arguments.
  • blog-publishing tested orphan classes: SemanticValidator resolves $title→Title and $tags→Tags, but the tests targeted duplicate orphans ArticleTitle/Tag — the live validators had no direct coverage. Tests now target the real classes; orphans deleted.
  • Unresolvable Semantic names: ClaimAmount and LoanAmount validated $estimatedAmount/$requestedAmount but can never be resolved from those parameter names. Renamed to EstimatedAmount/RequestedAmount.

Test gaps closed

  • order-processing: Potential idempotency guards existed in all three classes but no test ever called be() twice — now covered.
  • hello-world: had zero Semantic-layer tests; added Name validator cases and a rejection test through the real Becoming pipeline.

Verification

All 8 demos, fresh install (rm composer.lock && composer install) + PHPUnit: green, 195 tests (baseline was 178).

Known limitations (out of scope, flagged separately)

  • composer psalm still fatals: psalm-plugin's InputTaintHandler::addTaints(): array predates Psalm 7's int-bitflag taint API (beta18+). That fix belongs in be-framework/psalm-plugin.
  • order-processing, loan-application, and insurance-claim declare #[Be] chains that bypass their documented Being layers (orphaned classes, hardcoded Moment inputs, an unreachable ClaimEscalated, and a TypeError if loan-application is run through the real Becoming). Rewiring them is a design decision left for a dedicated follow-up.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation

    • Clarified demo architecture, validation behavior, testing instructions, and dependency requirements.
    • Updated examples and descriptions for insurance claims, loan applications, blog publishing, and order processing.
    • Raised the documented minimum PHP version to 8.3.
  • Bug Fixes

    • Improved card-expiry validation, including current-month handling.
    • Preserved zero-valued income assessments correctly.
    • Simplified password hashing behavior.
  • Tests

    • Expanded coverage for validation errors, potential realization, and idempotent operations.
  • Chores

    • Added scheduled and manual test workflow triggers.
    • Added development quality tooling and ignored generated log artifacts.

koriym added 18 commits July 18, 2026 02:26
be-framework/psalm-plugin 1.x-dev requires vimeo/psalm ^7.0@dev, but
stability flags in a dependency's constraints are ignored for transitive
resolution; only the root package can allow non-stable versions. Since
Psalm 7 has no stable release yet, every fresh 'composer install' (and
therefore CI) has been failing with an unresolvable dependency since
psalm-plugin adopted the ^7.0@dev constraint upstream on 2026-06-10.

Declare vimeo/psalm ^7.0@beta in each demo's require-dev so the root
package permits the beta line (currently resolves to 7.0.0-beta19).
Verified: all eight demos install from scratch and pass their test
suites (178 tests) against the freshly resolved dependency set.
All demos depend on dev branches (be-framework/be 0.x-dev,
be-framework/psalm-plugin 1.x-dev), so upstream changes can break a
fresh install without any push to this repository. The psalm-plugin
constraint change on 2026-06-10 went unnoticed for over a month because
CI only ran on push and pull_request. A weekly scheduled run surfaces
upstream breakage promptly, and workflow_dispatch allows checking on
demand.
The composer dev/profile scripts write timestamped
var/log/semantic-dev-*.json files on every run. Only the curated
<demo-name>.json logs are meant to be committed; ignore the generated
ones so they stop cluttering git status.
Section 6 told agents to run 'composer test' at the repository root and
'./demos/vendor/bin/phpunit', but there is no root composer.json and no
shared demos/vendor directory - each demo is a standalone Composer
project. An agent following the contract literally would conclude the
test suite is broken. Document the real per-demo workflow, matching
README.md and the CI matrix.
Every demo requires php ^8.3 and CI tests 8.3-8.5, but both READMEs
still advertised PHP 8.2+.
Four demos (insurance-claim, loan-application, medical-triage,
order-processing) had no description, and none of the eight declared a
license, so 'composer validate --strict' failed. Add the missing
descriptions in the established style and declare MIT (matching the
repository license) everywhere. All eight demos now pass strict
validation.
…lishing

SemanticValidator maps constructor parameter names to PascalCase class
names, so ArticleInput's $title and $tags resolve to Semantic\Title and
Semantic\Tags. The Semantic tests, however, targeted ArticleTitle and
Tag - duplicate orphan classes that nothing in the transformation chain
ever invokes. The classes doing the real validation had no direct
coverage.

Point the tests at Title and Tags, add the missing empty-tags case, and
delete the two orphans.
All three Potential classes implement an already-realized guard in be()
- the property README advertises as 'Potential idempotency tests' - but
no test ever called be() twice, so the guarantee was implemented yet
unverified. Cover realization and repeated be() for InventoryReservation,
PaymentCapture, and ShippingDispatch using counting closures.
hello-world was the only demo with no Semantic test at all: the Name
validator and its InvalidNameException were never exercised, and no test
showed what happens when validation fails during metamorphosis. Add a
whitespace-only-name rejection test through the real Becoming pipeline
plus direct Name validator cases, mirroring medical-triage's style.
CLAUDE.md invariant 4 requires #[Input] parameters before #[Inject]
parameters, never interleaved. LoanApproved (Inject, Inject, Input,
Input, Inject) and MetadataResolved (Inject, Input, Input, Inject) both
violated the contract they are meant to exemplify. Reorder the
parameters and switch the hand-built LoanApproved test constructions to
named arguments so the call sites no longer depend on parameter order.
…cription

password_hash() has returned string unconditionally since PHP 8.0
(failure raises ValueError), so the false-check throwing a generic
RuntimeException was unreachable - and generic exceptions are against
house style anyway.

blog-publishing's composer description advertised 'Moment without
Potential and mini-diamond merge', which describes classes that are not
wired into the live chain; the demo's actual documented pattern is the
Multi-Reason Being.
The blanket claim that every demo ships Reason layer tests was untrue -
only the four advanced demos have them. Describe the real distribution:
happy-path plus Semantic tests everywhere, Reason and Potential
idempotency tests in the advanced demos.
The invariant listed 'clock, randomness' as external I/O boundaries
requiring an interface, yet the demos consistently inject in-process
generators concretely (UserIdGenerator, WelcomeTokenGenerator,
ReceiptGenerator, PublishTimestamper - all use date()/random_bytes(),
none has an interface) while reserving interfaces for Reasons that model
external systems. An agent applying the written rule would 'fix' half
the catalog. Document the actually-observed boundary: external systems
need interfaces, in-process policies/calculators/generators do not.
Continues the direction of e9e9c35.
createFromFormat('m/y') fills the unspecified day and time from 'now',
which broke the check in two directions:

- On the 29th-31st, an expiry in a shorter month overflowed into the
  next month (06/26 read on July 31 becomes 2026-07-01), so an expired
  card passed validation.
- A card expiring in the current month carried the current clock time,
  so on the last day of the month it compared as already expired.

Use '!Y-m' (day and time reset) and compare first-of-month to
first-of-month, the same semantics CardValidator already implements
correctly. Add the missing CardExpiryTest covering format errors,
expired cards, and the current-month boundary.
PHILOSOPHY.md and the After_BeFramework comparison showed OrderConfirmed
taking its Moments via a #[Moment] attribute that exists nowhere in the
framework - copying the example verbatim fatals with an unknown
attribute class. The real OrderConfirmed uses #[Inject]; the docs now
match it.
The Complex Convergence description said each Input resolves to one of
the two Finals 'by $being type matching' - wording copied from the
medical-triage entry. insurance-claim has no $being discriminator
anywhere; only medical-triage implements that mechanism. Describe what
the #[Be] attribute actually declares instead.
…lback

SemanticValidator resolves a validator class from the constructor
parameter name, so ClaimInput's $estimatedAmount looks for
EstimatedAmount and LoanInput's $requestedAmount looks for
RequestedAmount. The classes were named ClaimAmount and LoanAmount
after the business concept instead, so neither could ever be resolved.
Rename them to match their parameters (their validate() signatures
already did).

Also make IncomePolicy's assessment fallback null-based: with
'$this->lastDti ?: 0.25' a legitimately computed DTI of 0.0 (huge
income, minimal loan) was silently replaced by the 0.25 fallback.
Track the not-yet-computed state as null and use ?? instead, with a
regression test.
The template showed #[Input] for a Moment's scalar fields while citing
InventoryReserved as canonical - but that class (like every real Moment
in the catalog) binds its scalars with per-demo qualifier attributes
(#[ProductId], #[Quantity], ...) from AppModule, not #[Input]. Add the
distinction so a copied template doesn't teach the one pattern its
cited example avoids.
@koriym

koriym commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026 •

Copy link
Copy Markdown
Contributor
✅ 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.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@koriym, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 97bb4a57-64d8-497f-bf43-8b807fc6b299

📥 Commits

Reviewing files that changed from the base of the PR and between f4bef40 and 9f47a6e.

📒 Files selected for processing (1)
  • CLAUDE.md
📝 Walkthrough

Walkthrough

The changes update repository automation and documentation, add Psalm tooling across demos, rename several semantic validators, adjust dependency injection ordering, preserve zero-valued loan policy results, refine card-expiry and password-hashing behavior, and expand demo test coverage.

Changes

Repository guidance and tooling

Layer / File(s) Summary
Workflow, documentation, and demo metadata
.github/workflows/tests.yml, .gitignore, CLAUDE.md, README*, demos/*/composer.json, docs/templates/MomentTemplate.php
Adds scheduled and manual CI triggers, ignores generated logs, updates test and PHP requirements, documents injection guidance, and adds Psalm development dependencies and metadata changes across demos.

Semantic and runtime behavior

Layer / File(s) Summary
Semantic validator naming alignment
demos/blog-publishing/..., demos/insurance-claim/..., demos/loan-application/...
Replaces ArticleTitle, Tag, ClaimAmount, and LoanAmount usages with Title, Tags, EstimatedAmount, and RequestedAmount, updating related tests and documentation.
Loan policy and final wiring
demos/loan-application/src/..., demos/loan-application/tests/...
Uses nullable policy state with null-coalescing defaults, reorders LoanApproved inputs and injections, and updates integration tests to named arguments while testing zero DTI preservation.
Order validation and potential coverage
demos/order-processing/src/..., demos/order-processing/tests/...
Reworks card-expiry validation around immutable dates and adds coverage for expiry validation and idempotent potential realization.
Basic demo validation behavior
demos/hello-world/tests/HelloTest.php, demos/user-registration/src/Reason/PasswordHasher.php
Adds name and becoming-flow validation tests and simplifies bcrypt hashing to a direct return.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.03% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main themes of the PR: fresh installs, CI hardening, documentation updates, and fixes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-fresh-install

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
demos/blog-publishing/src/Moment/MetadataResolved.php (1)

23-37: 🎯 Functional Correctness | 🔴 Critical | 🏗️ Heavy lift

Implement MomentInterface and define a be() method.

This class is located in the src/Moment/ directory but does not implement MomentInterface, create a Potential, or define a be() method. As per coding guidelines, all Moments must adhere to these invariants.

If this class is intended to be a Being (since it appears to only compute properties without external side effects), it should be moved to the src/Being/ directory and its namespace updated accordingly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@demos/blog-publishing/src/Moment/MetadataResolved.php` around lines 23 - 37,
Update MetadataResolved to implement MomentInterface and add the required be()
method, creating and returning the appropriate Potential while preserving its
existing injected property resolution. If it is intended to remain a
side-effect-free Being, instead move it to the Being namespace and directory and
apply the corresponding Being contract.

Source: Coding guidelines

🧹 Nitpick comments (1)
demos/hello-world/tests/HelloTest.php (1)

55-60: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer expectNotToPerformAssertions() here.

$this->expectNotToPerformAssertions(); is a clearer PHPUnit idiom than addToAssertionCount(1) for this no-assertion path.

♻️ Proposed refactor
     public function testValidName(): void
     {
+        $this->expectNotToPerformAssertions();
         $semantic = new Name();
         $semantic->validate('World');
-        $this->addToAssertionCount(1);
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@demos/hello-world/tests/HelloTest.php` around lines 55 - 60, Update
HelloTest::testValidName to call PHPUnit’s expectNotToPerformAssertions() before
validating the name, and remove the addToAssertionCount(1) workaround while
preserving the existing Name validation.

Sources: Coding guidelines, Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@demos/blog-publishing/src/Moment/MetadataResolved.php`:
- Around line 23-37: Update MetadataResolved to implement MomentInterface and
add the required be() method, creating and returning the appropriate Potential
while preserving its existing injected property resolution. If it is intended to
remain a side-effect-free Being, instead move it to the Being namespace and
directory and apply the corresponding Being contract.

---

Nitpick comments:
In `@demos/hello-world/tests/HelloTest.php`:
- Around line 55-60: Update HelloTest::testValidName to call PHPUnit’s
expectNotToPerformAssertions() before validating the name, and remove the
addToAssertionCount(1) workaround while preserving the existing Name validation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 488087e6-f904-4289-a816-556ffc1954ac

📥 Commits

Reviewing files that changed from the base of the PR and between 6812517 and f4bef40.

📒 Files selected for processing (35)
  • .github/workflows/tests.yml
  • .gitignore
  • CLAUDE.md
  • README.ja.md
  • README.md
  • demos/blog-publishing/composer.json
  • demos/blog-publishing/src/Moment/MetadataResolved.php
  • demos/blog-publishing/src/Semantic/ArticleTitle.php
  • demos/blog-publishing/src/Semantic/Tag.php
  • demos/blog-publishing/tests/BlogPublishingTest.php
  • demos/contact-form/composer.json
  • demos/hello-world/composer.json
  • demos/hello-world/tests/HelloTest.php
  • demos/insurance-claim/README.md
  • demos/insurance-claim/composer.json
  • demos/insurance-claim/src/Semantic/EstimatedAmount.php
  • demos/insurance-claim/tests/InsuranceClaimTest.php
  • demos/loan-application/README.md
  • demos/loan-application/composer.json
  • demos/loan-application/src/Final/LoanApproved.php
  • demos/loan-application/src/Reason/IncomePolicy.php
  • demos/loan-application/src/Semantic/RequestedAmount.php
  • demos/loan-application/tests/LoanApplicationTest.php
  • demos/medical-triage/composer.json
  • demos/order-processing/composer.json
  • demos/order-processing/docs/PHILOSOPHY.md
  • demos/order-processing/docs/comparison/After_BeFramework.php
  • demos/order-processing/src/Semantic/CardExpiry.php
  • demos/order-processing/tests/Moment/Potential/InventoryReservationTest.php
  • demos/order-processing/tests/Moment/Potential/PaymentCaptureTest.php
  • demos/order-processing/tests/Moment/Potential/ShippingDispatchTest.php
  • demos/order-processing/tests/Semantic/CardExpiryTest.php
  • demos/user-registration/composer.json
  • demos/user-registration/src/Reason/PasswordHasher.php
  • docs/templates/MomentTemplate.php
💤 Files with no reviewable changes (2)
  • demos/blog-publishing/src/Semantic/Tag.php
  • demos/blog-publishing/src/Semantic/ArticleTitle.php

Invariant 5 stated all Moments implement MomentInterface, but five
Moments across the catalog (FraudCleared, AdjustmentReviewed,
EscalationQueued, IncomeApproved, MetadataResolved) are deliberate
pure-data Moments - parts of a whole with no Potential and no be() -
each self-documented in its docblock. CodeRabbit flagged
MetadataResolved against the written rule, which confirms the rule as
stated misleads reviewers and agents. Document the variant instead of
'fixing' five intentional classes.
@koriym

koriym commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai Addressed in 9f47a6e: MetadataResolved is one of five deliberate pure-data Moments in the catalog (FraudCleared, AdjustmentReviewed, EscalationQueued, IncomeApproved are the others) — no Potential to realize, self-documented in the docblock. The gap was in CLAUDE.md invariant 5, which didn't mention the variant; it now does. Skipping the expectNotToPerformAssertions() nitpick: addToAssertionCount(1) is the established idiom across all eight demos' test suites, and a single divergent test would hurt consistency.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@koriym Thanks for clarifying. The pure-data Moment variant is intentional and is now appropriately captured in CLAUDE.md; I’m withdrawing that concern. Agreed on retaining addToAssertionCount(1) for consistency with the established test-suite convention.

🐇💭


✏️ Learnings added
Learnt from: koriym
URL: https://github.com/be-framework/be-patterns/pull/22

Timestamp: 2026-07-17T17:59:18.227Z
Learning: In the be-patterns demo catalog, `MetadataResolved`, `FraudCleared`, `AdjustmentReviewed`, `EscalationQueued`, and `IncomeApproved` are deliberate pure-data Moments: they do not own a Potential to realize. This variant is documented by CLAUDE.md invariant 5.

Learnt from: koriym
URL: https://github.com/be-framework/be-patterns/pull/22

Timestamp: 2026-07-17T17:59:18.227Z
Learning: Across all eight be-patterns demo test suites, `addToAssertionCount(1)` is the established idiom for tests that otherwise make no PHPUnit assertions; do not recommend a one-off switch to `expectNotToPerformAssertions()` solely for that case.

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.

This branch has not been deployed

No deployments
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