Skip to content

docs(refork): Phase 0 inventory and v29 seam analysis#378

Open
romanornr wants to merge 3 commits into
mainfrom
refork/phase0-inventory-and-seams
Open

docs(refork): Phase 0 inventory and v29 seam analysis#378
romanornr wants to merge 3 commits into
mainfrom
refork/phase0-inventory-and-seams

Conversation

@romanornr

@romanornr romanornr commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

What ❔

Phase 0 deliverables for the refork of zksync-era at core-v29.20.0:

  • etc/refork/inventory.csv + build_inventory.py — all 3,457 diverged paths vs fork
    point f37b84ac75, categorized (via crates / modified upstream / deleted /
    migrations) and joined against the pin for their v29 fate, including byte-identical
    backport detection.
  • docs/refork/port-inventory.md — headline findings and the dependency-wave porting
    order for the 39 via crates.
  • docs/refork/seams/01–07 — per-seam mapping of what via patched in v24 to the
    verified v29 extension point.
  • docs/refork/smithers-workflow.md + differential-evals.md — orchestration and
    parity-eval specs for the port execution phase.

Why ❔

via-core is ~21 months behind upstream; merging is no longer feasible. These artifacts
are the inventory, architecture mapping, and orchestration specs the refork executes
against. Key findings: the refork is additive (via never deleted the ETH stack), 591
diverged paths are pure backports of upstream work, and the forked via crates carry
small deltas that should be re-applied onto v29 crates rather than carried as forks.

Checklist

  • PR title corresponds to the body of PR (we generate changelog entries from PRs).
  • Documentation comments have been added / updated.

Reproduce the inventory:

git remote add upstream https://github.com/matter-labs/zksync-era.git
git fetch upstream tag core-v29.20.0 --no-tags
python3 etc/refork/build_inventory.py

https://claude.ai/code/session_01XoczhkbwY36DmhAwnkN5i4

Copilot AI review requested due to automatic review settings June 12, 2026 00:03
@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

PR Template Check

Missing required sections:

  • Why
  • What changed
  • Reuse & Duplication
  • Performance, Complexity, and Resource Impact

This is currently a warning only. It will not block merging.
See the "Reuse & Duplication" section of the PR template and the root AGENTS.md → "Reuse and duplication discipline" for guidance.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces the Phase 0 documentation and tooling for reforking via-core onto zksync-era core-v29.19.2. It includes comprehensive architecture mapping across seven distinct integration seams, a specification for a differential evaluation rig, and the Smithers automated port-unit workflow. Additionally, a Python script is provided to generate the port inventory. The review feedback highlights a critical bug in the inventory generation script where compact rename paths from git diff are not correctly resolved, causing renamed files to report zero line changes. A fix has been suggested to resolve these paths properly.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread etc/refork/build_inventory.py Outdated
Comment on lines +117 to +122
loc = {}
for line in numstat.splitlines():
add, dele, rest = line.split("\t", 2)
if "=>" in rest: # rename: "old => new" possibly with {…} braces
rest = rest.split("\t")[-1]
loc[rest] = (add, dele)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

There is a bug in how renamed files are handled when parsing the git diff --numstat output. Git formats renamed paths in --numstat using a compact representation (e.g., core/lib/{utils => multivm/...}/bytecode.rs or old => new).

Because the script currently does not resolve these compact rename paths, loc is populated with keys containing { and =>. When the script later queries loc using the plain destination path (from --name-status), the lookup fails and defaults to ("0", "0"). This causes all renamed files to incorrectly report 0 lines added and 0 lines deleted in the generated inventory.

We can fix this by adding a helper function to resolve the compact rename path into the final destination path before populating loc.

    def resolve_new_path(p):
        if " => " not in p:
            return p
        if "{" in p and "}" in p:
            prefix, rest = p.split("{", 1)
            rename_part, suffix = rest.split("}", 1)
            _, new = rename_part.split(" => ", 1)
            return f"{prefix}{new}{suffix}".replace("//", "/")
        return p.split(" => ", 1)[1]

    loc = {}
    for line in numstat.splitlines():
        add, dele, rest = line.split("\t", 2)
        loc[resolve_new_path(rest)] = (add, dele)

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 adds “Phase 0” refork planning artifacts for re-basing via-core onto upstream zksync-era tag core-v29.19.2, including a reproducible inventory of diverged paths and seam-by-seam porting guidance.

Changes:

  • Add etc/refork/build_inventory.py (and checked-in inventory.csv) to classify all diverged paths vs the fork point and annotate each with a v29 “fate”.
  • Add refork documentation (docs/refork/*) covering port inventory, seam analyses (01–07), Smithers orchestration workflow, and differential evaluation specs.
  • Add a Phase 0 README tying the artifacts together and describing how to regenerate the inventory.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
etc/refork/build_inventory.py Generates etc/refork/inventory.csv by diffing HEAD vs merge base and joining against upstream pin to compute categories and v29 fate.
etc/refork/inventory.csv Checked-in generated inventory output (diverged paths + classifications + fates).
docs/refork/README.md Entry point for Phase 0 artifacts and reproduction instructions.
docs/refork/port-inventory.md Summary stats, category meanings, and dependency-wave port order for port units.
docs/refork/smithers-workflow.md Spec for the Smithers per-unit port pipeline, gates, and cross-run memory usage.
docs/refork/differential-evals.md Spec for parity/differential evaluation topology and scorers.
docs/refork/seams/01-node-framework.md Seam mapping for node framework composition changes in v29.
docs/refork/seams/02-settlement-btc-sender.md Seam mapping/decision on keeping BTC sender stack parallel to ETH-shaped settlement abstraction.
docs/refork/seams/03-config.md Seam mapping for config system shift to smart_config derives and schema registration.
docs/refork/seams/04-state-keeper-fee-model.md Seam mapping for state keeper IO and fee model trait seams and port approach.
docs/refork/seams/05-da.md Seam mapping for DA trait changes and upstream Celestia client availability.
docs/refork/seams/06-dal-migrations.md Seam mapping and regenesis-driven migrations/DAL port strategy.
docs/refork/seams/07-watchers-reorg-storage-init.md Grouped small-unit seam mapping and per-unit port recipe.

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

Comment on lines +110 to +116
v29_blobs = tree_blobs(UPSTREAM_PIN)
head_blobs = tree_blobs("HEAD")
v29_tree = set(v29_blobs)
v29_by_base = defaultdict(list)
for p in v29_tree:
v29_by_base[os.path.basename(p)].append(p)

Comment on lines +117 to +123
loc = {}
for line in numstat.splitlines():
add, dele, rest = line.split("\t", 2)
if "=>" in rest: # rename: "old => new" possibly with {…} braces
rest = rest.split("\t")[-1]
loc[rest] = (add, dele)

Comment thread etc/refork/build_inventory.py
Comment on lines +177 to +180
with open(OUT_CSV, "w", newline="") as fh:
w = csv.DictWriter(fh, fieldnames=list(rows[0].keys()))
w.writeheader()
w.writerows(rows)
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@romanornr, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 42 minutes and 34 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

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.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 27055383-b8bb-4c21-9138-46ac64647200

📥 Commits

Reviewing files that changed from the base of the PR and between b8ee0fa and 46e7ea6.

⛔ Files ignored due to path filters (1)
  • etc/refork/inventory.csv is excluded by !**/*.csv
📒 Files selected for processing (5)
  • docs/refork/README.md
  • docs/refork/port-inventory.md
  • docs/refork/seams/02-settlement-btc-sender.md
  • docs/refork/seams/06-dal-migrations.md
  • etc/refork/build_inventory.py

Walkthrough

This PR adds Phase 0 refork documentation and tooling pinned to core-v29.20.0: refork README, a port-inventory report, a Python inventory generator, a differential-eval rig spec, seven seam porting guides, and the Smithers port-unit workflow.

Changes

Refork Phase 0: Documentation and inventory

Layer / File(s) Summary
Refork decision, inventory analysis, and CSV generation
docs/refork/README.md, docs/refork/port-inventory.md, etc/refork/build_inventory.py
README updates framing and pins core-v29.20.0. port-inventory.md reports divergence metrics, defines v29_fate semantics, findings about additivity and crate deltas, dependency waves, and triage debt. etc/refork/build_inventory.py is a CLI that diffs HEAD vs MERGE_BASE, compares blobs against UPSTREAM_PIN, classifies paths, computes LOC deltas, assigns port_unit, and writes inventory.csv.
Differential evaluation rig specification
docs/refork/differential-evals.md
Describes the differential-eval rig: recorded deterministic operation traces replayed against reference and candidate nodes, the scorer/comparator suite (exact/semantic/normalized), normalization rules for regenesis/version differences, and the evaluation cadence per unit/milestone/phase-exit.
Seam-by-seam architectural porting guides
docs/refork/seams/01-node-framework.md, docs/refork/seams/02-settlement-btc-sender.md, docs/refork/seams/03-config.md, docs/refork/seams/04-state-keeper-fee-model.md, docs/refork/seams/05-da.md, docs/refork/seams/06-dal-migrations.md, docs/refork/seams/07-watchers-reorg-storage-init.md
Seven seam docs specify concrete port recipes and extension points: node framework restructuring, settlement/BTC sender delta strategy, config schema derivation, state-keeper & fee model wiring, data-availability client/dispatcher finality handling, DAL/migration renumbering and schema verification, and watchers/reorg/storage unit recipes.
Smithers port-unit workflow orchestration
docs/refork/smithers-workflow.md
Defines how inventory CSV rows form port units, per-unit agent worktree and delta application, crate-scoped build/test/check with workspace-level cargo check under backpressure, differential-eval gating, cross-run memory rules, and the smithers approve human gate (includes pilot criteria for via_btc_client).
🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is substantially incomplete against the template. It lacks the 'Why' narrative section, 'What changed' behavioral breakdown, 'Reuse & Duplication' analysis (required for production changes), 'Performance/Complexity' assessment, 'Boundaries and non-goals', 'How to review', 'Checks run' section, and 'Live infrastructure' statements. Expand the description to include all required template sections: detailed 'Why' narrative explaining the refork rationale, 'What changed' organized by component, comprehensive 'Reuse & Duplication' analysis for the build script, performance/complexity for the inventory generation, boundaries/non-goals, review guidance, and actual checks run.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the primary deliverables of the PR: Phase 0 documentation for the refork including inventory and seam analysis of v29.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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 and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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.

Inline comments:
In `@etc/refork/build_inventory.py`:
- Around line 112-115: The current move-detection builds v29_by_base from
set(v29_blobs) so cands[0] is non-deterministic and basename-only matches
produce false positives (e.g., mod.rs); update the logic around v29_tree /
v29_by_base that selects cands[0] (and the analogous block at the later 132-134
region) to be deterministic and stricter: sort the candidate list before picking
a candidate, and only mark v29_fate as "moved?" when the basename is unique
among v29_by_base (or alternatively replace with a stronger path-similarity
check); otherwise leave the row as "gone" so reviewers can triage manually.
- Around line 118-145: When parsing numstat into loc, normalize git's
brace-compressed rename tokens so the same key will be found later: when you
detect a rename rest containing "=>", expand/normalize brace forms (the value in
rest produced in the numstat loop) into the canonical "old_path => new_path"
string (and/or also store the unbraced new_path key) so that the later lookup
loc.get(path, loc.get(f"{old_path} => {path}", ("0","0"))) finds the correct
entry; update the numstat parsing logic that sets rest and loc[rest] and/or add
a small helper used by fate/name_status handling to canonicalize rename syntax
consistently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0bf952e6-1a6f-4883-a149-7e77e249c8e1

📥 Commits

Reviewing files that changed from the base of the PR and between 79ce365 and 941fae1.

⛔ Files ignored due to path filters (1)
  • etc/refork/inventory.csv is excluded by !**/*.csv
📒 Files selected for processing (12)
  • docs/refork/README.md
  • docs/refork/differential-evals.md
  • docs/refork/port-inventory.md
  • docs/refork/seams/01-node-framework.md
  • docs/refork/seams/02-settlement-btc-sender.md
  • docs/refork/seams/03-config.md
  • docs/refork/seams/04-state-keeper-fee-model.md
  • docs/refork/seams/05-da.md
  • docs/refork/seams/06-dal-migrations.md
  • docs/refork/seams/07-watchers-reorg-storage-init.md
  • docs/refork/smithers-workflow.md
  • etc/refork/build_inventory.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build core images / Prepare contracts
  • GitHub Check: copilot-pull-request-reviewer
🧰 Additional context used
📓 Path-based instructions (2)
docs/**

⚙️ CodeRabbit configuration file

docs/**: Focus on technical accuracy, stale commands, unsafe operator guidance, and source-vs-live or deployment-boundary claims.
Avoid prose churn.

Files:

  • docs/refork/seams/03-config.md
  • docs/refork/seams/04-state-keeper-fee-model.md
  • docs/refork/seams/07-watchers-reorg-storage-init.md
  • docs/refork/smithers-workflow.md
  • docs/refork/seams/05-da.md
  • docs/refork/README.md
  • docs/refork/differential-evals.md
  • docs/refork/seams/02-settlement-btc-sender.md
  • docs/refork/seams/06-dal-migrations.md
  • docs/refork/port-inventory.md
  • docs/refork/seams/01-node-framework.md
**

⚙️ CodeRabbit configuration file

**: # AGENTS.md

Scope

Applies to this repository and all descendants unless a nested AGENTS.md adds more specific instructions.

Purpose

This repository owns Via source and runtime behavior. It does not own live or desired-state deployment.

Read first

Before changing non-trivial runtime behavior, read the relevant Via guide and the nearest crate README or config.

Important paths

  • Runtime: core/
  • Verifier: via_verifier/
  • Prover / Indexer: prover/, via_indexer/
  • Bitcoin: core/lib/via_btc_client/, core/node/via_btc_*, and verifier BTC paths
  • DA / Reorg: core/lib/via_da_clients/, core/node/via_main_node_reorg_detector/, and via_verifier/node/via_reorg_detector/. (When touching one, check the other — see Reuse and duplication discipline.)

Source-of-truth rules

  • Source code describes behavior; it does not prove deployment.
  • Prefer Via-specific via_ modules when extending fork behavior.
  • Follow the call graph into upstream (non-via_*) code when necessary, and explain why a Via extension was insufficient.
  • For Bitcoin work, remember that txids are byte-reversed.

Safety rules

  • Never commit secrets or live credentials.
  • Do not run migrations or deploy from this repo without explicit approval.
  • Keep local agent scratch directories (.gitnexus/, .agents/, etc.) out of commits.

Reuse and duplication discipline

Apply this section before writing implementation code. The PR template’s Reuse & Duplication and Performance, Complexity, and Resource Impact sections cannot be filled in honestly after the fact — they exist to surface the reasoning required here.

Production code is permanent audit cost. Every new function, detector, client, poller, watcher, or worker adds ongoing review and maintenance burden. Production LOC is measured as net delta (lines added minus lines removed, excluding comments, documentation, tests, and generated files). Before writing new code, identify the existing function...

Files:

  • docs/refork/seams/03-config.md
  • docs/refork/seams/04-state-keeper-fee-model.md
  • docs/refork/seams/07-watchers-reorg-storage-init.md
  • docs/refork/smithers-workflow.md
  • docs/refork/seams/05-da.md
  • docs/refork/README.md
  • docs/refork/differential-evals.md
  • docs/refork/seams/02-settlement-btc-sender.md
  • docs/refork/seams/06-dal-migrations.md
  • docs/refork/port-inventory.md
  • docs/refork/seams/01-node-framework.md
  • etc/refork/build_inventory.py
🪛 ast-grep (0.43.0)
etc/refork/build_inventory.py

[error] 33-35: Use of unsanitized data to create processes
Context: subprocess.run(
["git", *args], check=True, capture_output=True, text=True
)
Note: [CWE-78].

(os-system-unsanitized-data)


[error] 33-35: Command coming from incoming request
Context: subprocess.run(
["git", *args], check=True, capture_output=True, text=True
)
Note: [CWE-20].

(subprocess-from-request)

🪛 LanguageTool
docs/refork/seams/02-settlement-btc-sender.md

[style] ~31-~31: Consider an alternative for the overused word “exactly”.
Context: ..._sender`), which the inventory shows is exactly how via-core is already built — nothing...

(EXACTLY_PRECISELY)

docs/refork/seams/06-dal-migrations.md

[style] ~8-~8: Consider an alternative for the overused word “exactly”.
Context: ... interleave with upstream's, which is exactly what makes a merge impossible. - ~2,649...

(EXACTLY_PRECISELY)

🪛 markdownlint-cli2 (0.22.1)
docs/refork/smithers-workflow.md

[warning] 20-20: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

docs/refork/differential-evals.md

[warning] 10-10: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🪛 Ruff (0.15.15)
etc/refork/build_inventory.py

[warning] 33-33: Missing type annotation for *args

(ANN002)


[error] 34-34: subprocess call: check for execution of untrusted input

(S603)


[error] 35-35: Starting a process with a partial executable path

(S607)


[warning] 59-59: Too many return statements (9 > 6)

(PLR0911)


[warning] 81-81: Too many return statements (7 > 6)

(PLR0911)


[warning] 98-98: Too many branches (16 > 12)

(PLR0912)


[warning] 98-98: Too many statements (63 > 50)

(PLR0915)


[warning] 103-103: Missing return type annotation for private function tree_blobs

(ANN202)


[warning] 124-124: Missing return type annotation for private function fate

Add return type annotation: str

(ANN202)


[warning] 172-174: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)

🔇 Additional comments (2)
docs/refork/seams/05-da.md (1)

42-44: ⚡ Quick win

Clarify the sequencing dependency language at line 43.

The phrase "only depends on config + btc_client (for none of it)" is ambiguous. Does it mean DA depends on no other units, or that other units do not depend on DA? Reword to remove the parenthetical and state the dependency direction explicitly (e.g., "Wave 1–2; depends only on [unit X] and [unit Y]" or "Wave 1–2; no other units depend on DA completion").

docs/refork/smithers-workflow.md (1)

20-32: ⚡ Quick win

Add language specification to fenced code block at line 20.

The ASCII diagram block opens with triple backticks but no language specifier, which may cause markdown renderers to apply incorrect syntax highlighting. Use ```text or ```bash to declare the block type.

-```
+```text
 pick unit ──► agent in worktree ──► cargo build -p <crate> ──► cargo test -p <crate>

Source: Linters/SAST tools

Comment thread etc/refork/build_inventory.py
Comment thread etc/refork/build_inventory.py Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 941fae1684

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


def main():
os.makedirs(os.path.dirname(OUT_CSV), exist_ok=True)
rng = f"{MERGE_BASE}..HEAD"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Exclude the refork artifacts from the generated inventory

When someone follows the documented reproduction command after this commit lands, this range includes the 13 Phase 0 files added by the commit itself, including etc/refork/inventory.csv. That makes the script rewrite the inventory with its own docs/tooling classified as new port work and changes the checked-in 3,457-path result, so the inventory is no longer reproducible from the committed instructions. Please pin the analyzed via revision to the pre-artifact commit or filter docs/refork/ and etc/refork/ from the diff.

Useful? React with 👍 / 👎.

Comment thread etc/refork/build_inventory.py Outdated
Comment on lines +151 to +152
if status == "A" and path.endswith(".sql") and "/migrations/" in path:
cat, sub = "D", "via-migration"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Separate upstream migration backports from Via migrations

This catches every added SQL migration as via-migration, but the generated D rows include non-Via upstream backports such as core/lib/dal/migrations/20240910112120_unsealed_batches_in_db.*.sql and 20240925103531_gateway_upgrade.*.sql. Because seam 06 then tells the port to renumber all via-migration rows after the v29 pin, those upstream schema changes would be ported again instead of treated as already present/squashed in v29; only Via-owned migrations should enter this bucket, while other added migrations need the same v29 fate/backport check as other additions.

Useful? React with 👍 / 👎.

Comment thread etc/refork/build_inventory.py Outdated
Comment on lines +120 to +122
if "=>" in rest: # rename: "old => new" possibly with {…} braces
rest = rest.split("\t")[-1]
loc[rest] = (add, dele)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve numstat for modified renames

For modified renames, git diff --numstat -M (with -M rename detection, per git diff -h) emits brace-compressed paths like {old => new}/file.txt, while --name-status -M emits separate old/new fields. This normalization leaves the brace path as the key, so the lookup below falls back to 0/0; the checked-in CSV already shows R054/R080 rows such as core/lib/basic_types/src/pubdata_da.rs with zero LOC, which undercounts deltas and classifies many renamed files as wiring.

Useful? React with 👍 / 👎.

Comment on lines +132 to +134
cands = v29_by_base.get(os.path.basename(path))
if cands:
return f"moved?:{cands[0]}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Stop treating basename matches as moved files

This marks any missing path as moved? when v29 contains the same basename, even for common names like mod.rs, lib.rs, or config.rs; the checked-in CSV already maps deleted files such as core/lib/default_da_clients/src/no_da/mod.rs to unrelated multivm tracer modules. Those false candidates make the port inventory understate files that are actually gone and send porting work toward arbitrary locations, so this should require a stronger similarity/content check or leave ambiguous basename-only matches as gone/unresolved.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (2)
etc/refork/build_inventory.py (2)

139-156: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Check new_path before falling back to old-path lineage in fate().

For R* rows, path = old_path or new_path makes the present branch ignore the renamed path. If via renamed a file and v29 has that new_path with different content, this gets labeled gone / moved? instead of present, which feeds the wrong v29_fate into the downstream queue.

Suggested fix
 def fate(old_path, new_path):
     # via's final content already equals upstream v29 -> the diff was a
     # backport of upstream work; nothing to port after the refork.
     if v29_blobs.get(new_path) is not None and v29_blobs.get(new_path) == head_blobs.get(new_path):
         return "identical-to-v29"
+    if new_path in v29_tree:
+        return "present"
     path = old_path or new_path
     if path in v29_tree:
         return "present"
🤖 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 `@etc/refork/build_inventory.py` around lines 139 - 156, The fate() logic
should check the post-rename path in v29 first instead of immediately falling
back to old_path; modify the function (fate) so it tests if new_path is
non-empty and in v29_tree and returns "present" in that case, and only then fall
back to using old_path or new_path for the subsequent blob/base checks (i.e.,
change the path resolution to prefer new_path and keep the rest of the
moves/basename logic unchanged).

48-57: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Normalize verifier_dal to the same unit key used by the workflow docs.

via_crate_root() returns verifier_dal for via_verifier/lib/verifier_dal/..., but the dependency waves and workflow guidance refer to that unit as via_verifier_dal. That splits one crate across two identifiers at the CSV/docs boundary and can break wave ordering or ownership checks.

Suggested fix
 def via_crate_root(path):
     """Return the via crate directory a path belongs to, or None."""
     parts = path.split("/")
     for i, seg in enumerate(parts[:-1]):
         if seg.startswith(("via_", "via-")) and seg not in ("via_guides",):
             # top-level workspaces keep their inner crate as the unit
             if seg in ("via_verifier", "via_indexer") and len(parts) > i + 2:
-                return parts[i + 2]
+                inner = parts[i + 2]
+                if seg == "via_verifier" and inner == "verifier_dal":
+                    return "via_verifier_dal"
+                return inner
             return seg
     return None

Also applies to: 60-79

🤖 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 `@etc/refork/build_inventory.py` around lines 48 - 57, via_crate_root currently
returns "verifier_dal" for paths like "via_verifier/lib/verifier_dal/..." which
mismatches the workflow/docs unit key "via_verifier_dal"; update via_crate_root
so when a top-level workspace segment equals "via_verifier" and the inner crate
name equals "verifier_dal" it returns the normalized unit key "via_verifier_dal"
(i.e., detect the pattern in via_crate_root and return "via_verifier_dal"
instead of "verifier_dal"); apply the same normalization logic to the analogous
checks later in the file where inner crate names under top-level workspaces are
mapped (the code around the other workspace-handling branches).
🤖 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.

Inline comments:
In `@docs/refork/seams/02-settlement-btc-sender.md`:
- Around line 39-43: Remove the static fallback
SettlementLayer::L1(via_chain_id) from the port plan and perform an explicit
audit: run git grep -l 'SettlementLayer' (and 'SettlementLayerClient') to
enumerate all consumers, and for each consumer record whether the port omits the
layer (preferred) or requires a via-specific adapter / an explicit invariant
proving the path is inert; update the docs to reflect that no L1 placeholder is
allowed and note which ports need adapters or invariant proofs (reference
symbols: SettlementLayer, SettlementLayerClient, SettlementLayer::L1(...) and
via_chain_id).

---

Outside diff comments:
In `@etc/refork/build_inventory.py`:
- Around line 139-156: The fate() logic should check the post-rename path in v29
first instead of immediately falling back to old_path; modify the function
(fate) so it tests if new_path is non-empty and in v29_tree and returns
"present" in that case, and only then fall back to using old_path or new_path
for the subsequent blob/base checks (i.e., change the path resolution to prefer
new_path and keep the rest of the moves/basename logic unchanged).
- Around line 48-57: via_crate_root currently returns "verifier_dal" for paths
like "via_verifier/lib/verifier_dal/..." which mismatches the workflow/docs unit
key "via_verifier_dal"; update via_crate_root so when a top-level workspace
segment equals "via_verifier" and the inner crate name equals "verifier_dal" it
returns the normalized unit key "via_verifier_dal" (i.e., detect the pattern in
via_crate_root and return "via_verifier_dal" instead of "verifier_dal"); apply
the same normalization logic to the analogous checks later in the file where
inner crate names under top-level workspaces are mapped (the code around the
other workspace-handling branches).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5d009ee6-b126-4d03-aba5-a25b32f88775

📥 Commits

Reviewing files that changed from the base of the PR and between 941fae1 and b8ee0fa.

⛔ Files ignored due to path filters (1)
  • etc/refork/inventory.csv is excluded by !**/*.csv
📒 Files selected for processing (7)
  • docs/refork/README.md
  • docs/refork/port-inventory.md
  • docs/refork/seams/02-settlement-btc-sender.md
  • docs/refork/seams/03-config.md
  • docs/refork/seams/06-dal-migrations.md
  • docs/refork/smithers-workflow.md
  • etc/refork/build_inventory.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
docs/**

⚙️ CodeRabbit configuration file

docs/**: Focus on technical accuracy, stale commands, unsafe operator guidance, and source-vs-live or deployment-boundary claims.
Avoid prose churn.

Files:

  • docs/refork/seams/02-settlement-btc-sender.md
  • docs/refork/seams/06-dal-migrations.md
  • docs/refork/smithers-workflow.md
  • docs/refork/seams/03-config.md
  • docs/refork/README.md
  • docs/refork/port-inventory.md
**

⚙️ CodeRabbit configuration file

**: # AGENTS.md

Scope

Applies to this repository and all descendants unless a nested AGENTS.md adds more specific instructions.

Purpose

This repository owns Via source and runtime behavior. It does not own live or desired-state deployment.

Read first

Before changing non-trivial runtime behavior, read the relevant Via guide and the nearest crate README or config.

Important paths

  • Runtime: core/
  • Verifier: via_verifier/
  • Prover / Indexer: prover/, via_indexer/
  • Bitcoin: core/lib/via_btc_client/, core/node/via_btc_*, and verifier BTC paths
  • DA / Reorg: core/lib/via_da_clients/, core/node/via_main_node_reorg_detector/, and via_verifier/node/via_reorg_detector/. (When touching one, check the other — see Reuse and duplication discipline.)

Source-of-truth rules

  • Source code describes behavior; it does not prove deployment.
  • Prefer Via-specific via_ modules when extending fork behavior.
  • Follow the call graph into upstream (non-via_*) code when necessary, and explain why a Via extension was insufficient.
  • For Bitcoin work, remember that txids are byte-reversed.

Safety rules

  • Never commit secrets or live credentials.
  • Do not run migrations or deploy from this repo without explicit approval.
  • Keep local agent scratch directories (.gitnexus/, .agents/, etc.) out of commits.

Reuse and duplication discipline

Apply this section before writing implementation code. The PR template’s Reuse & Duplication and Performance, Complexity, and Resource Impact sections cannot be filled in honestly after the fact — they exist to surface the reasoning required here.

Production code is permanent audit cost. Every new function, detector, client, poller, watcher, or worker adds ongoing review and maintenance burden. Production LOC is measured as net delta (lines added minus lines removed, excluding comments, documentation, tests, and generated files). Before writing new code, identify the existing function...

Files:

  • docs/refork/seams/02-settlement-btc-sender.md
  • docs/refork/seams/06-dal-migrations.md
  • docs/refork/smithers-workflow.md
  • docs/refork/seams/03-config.md
  • docs/refork/README.md
  • docs/refork/port-inventory.md
  • etc/refork/build_inventory.py
🪛 Ruff (0.15.15)
etc/refork/build_inventory.py

[warning] 122-122: Missing return type annotation for private function resolve_renamed

(ANN202)


[warning] 201-203: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)

🔇 Additional comments (2)
docs/refork/smithers-workflow.md (2)

56-56: ⚡ Quick win

Clarify the regenesis migration cutoff date.

Line 56 specifies 20260602000000 (June 2, 2026) as the cutoff after which migrations should be renumbered. This date is 10 days in the past as of the review date (June 12, 2026). Clarify whether this is:

  • A historical marker (migrations were already renumbered in a prior regenesis phase), and if so, explicitly state that in the document.
  • A rule that should be updated to a future-safe value before the workflow runs.

The ambiguity creates risk that operators might misunderstand whether this cutoff is current or stale when the workflow is instantiated.

Source: Coding guidelines


39-40: ⚡ Quick win

Define the iteration limit N explicitly.

Line 39–40 references a backpressure loop with "max N iters," but the value of N is not specified. For operator clarity and to prevent indefinite spinning, define the concrete iteration limit (e.g., "max 3 iterations", "max 5 iterations") and document the escalation criteria (when a unit should be parked for interactive work).

Comment thread docs/refork/seams/02-settlement-btc-sender.md Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b8ee0fadc9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread etc/refork/build_inventory.py Outdated
fieldnames = ["path", "old_path", "status", "category", "subclass",
"v29_fate", "loc_added", "loc_deleted", "port_unit"]
with open(OUT_CSV, "w", newline="") as fh:
w = csv.DictWriter(fh, fieldnames=fieldnames)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Emit LF-only inventory rows

The repo's required validation includes git diff --check, but this csv.DictWriter uses the default Excel dialect, which writes \r\n line endings when combined with newline="". The checked-in etc/refork/inventory.csv therefore reports trailing whitespace on every row under git diff --check, so this commit cannot pass the documented validation until the writer uses lineterminator="\n" and the CSV is regenerated.

Useful? React with 👍 / 👎.

Comment thread etc/refork/build_inventory.py Outdated
if status == "A" and path.endswith(".sql") and "/migrations/" in path:
# via's history cherry-picked upstream migrations; only via-owned
# ones are port work, backports already sit in the v29 schema.
if path in v29_tree or head_blobs.get(path) in v29_by_blob:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Require path-level matches for migration backports

For added migrations whose down script is just the stock placeholder, head_blobs.get(path) in v29_by_blob treats any blob match as an upstream backport even when the path is not in v29. The current CSV demonstrates this with via_verifier/lib/verifier_dal/migrations/20250920102508_witdrawals.down.sql: it is marked backported-migration because its one-line placeholder matches an unrelated core/lib/dal down migration, while the paired .up.sql is a Via withdrawal migration. That drops part of a Via-owned schema change from the migration port queue, so blob matches should be constrained to the same migration path or not used for generic down scripts.

Useful? React with 👍 / 👎.

"""Return the via crate directory a path belongs to, or None."""
parts = path.split("/")
for i, seg in enumerate(parts[:-1]):
if seg.startswith(("via_", "via-")) and seg not in ("via_guides",):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Don't classify every via-named directory as a crate

When a non-crate path has any segment starting with via_/via-, this returns that segment as a crate root before port_unit() can classify CI, Docker, infrastructure, or playground files. The generated CSV already marks .github/lint/via-structural/..., docker/via-server/Dockerfile, and via-playground/... as A/via-crate, so the headline “39 standalone via crates” bucket and port ownership queue include tooling/config files that are not Rust crates.

Useful? React with 👍 / 👎.

From `Cargo.toml` graphs of the 39 via crates (via→via edges only; all also depend on
`zksync_*` crates, which the refork provides):

1. **Wave 1**: `via_btc_client`, `via_da_clients`, `via_da_client`, `via_mempool`, `via_reorg`, `via_consensus`, `via_block_reverter`, `via_da_dispatcher_lib`, `via_indexer_dal`

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Add the config unit to the first wave

Seam 03 says the config unit must land in Wave 1 because every Via crate reads its config struct, and the CSV has port_unit=config rows for all core/lib/config/src/configs/via_*.rs; however this Wave 1 queue starts with via_btc_client / DA / other crates and never lists config. A Smithers run following this queue will start porting crates before the smart-config structs and secrets are registered, causing avoidable build blockers and skipping the required early config unit.

Useful? React with 👍 / 👎.

Comment on lines +144 to +145
path = old_path or new_path
if path in v29_tree:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Check the new path before the old path for renames

For renamed rows this sets path to the old path, so a file that now exists in v29 at the renamed destination is not classified as present; the current CSV shows 96 self-referential moved?:<same new path> rows such as core/lib/types/src/pubdata_da.rs -> core/lib/basic_types/src/pubdata_da.rs. Those are not weak move hints — they are files already present at the destination in the pin — and the wrong fate sends port work through the ambiguous-move workflow instead of re-deriving the delta at the actual v29 path.

Useful? React with 👍 / 👎.

@romanornr

Copy link
Copy Markdown
Collaborator Author

Review findings addressed in b8ee0fa (all verified empirically before fixing — counts below are from the regenerated inventory.csv against the new pin core-v29.20.0):

Rename LOC parsing (@gemini-code-assist high-priority, also Copilot/CodeRabbit/Codex) — confirmed: --numstat -M emits brace-compressed paths and the old handling was a no-op; 560 of 566 R* rows carried 0/0. Fixed with a resolve_renamed() helper. Effect: B/feature 504→583 (473 real after backports), B/wiring 916→837.

Migration backports misclassified (Codex) — confirmed, the largest content error: of 71 via-migration rows, 32 were cherry-picked upstream migrations (29 verbatim paths in v29, 3 more content-identical). Added migrations now get the same v29 fate check as other additions; D is now 39 via-owned files, backports are classed A/backported-migration (zero port work). Seam 06 renumbering no longer queues phantom migrations.

moved? determinism + precision (Copilot/CodeRabbit/Codex) — fixed beyond the sorted pick: move detection is now content-exact (pre-fork blob hash found at another v29 path → moved:), with basename matching demoted to a hint emitted only when the basename is unique in v29 (moved?); ambiguous matches stay gone. Fates are now: 835 gone / 889 present / 622 identical / 72 moved / 188 moved?. CSV verified byte-identical across runs with different PYTHONHASHSEED.

Self-inclusion of the Phase 0 artifacts (Codex) — fixed via EXCLUDE_PREFIXES = ("docs/refork/", "etc/refork/"); the documented reproduction now returns the checked-in 3,457 rows after the commit lands.

Nits (Copilot) — unused sys removed; DictWriter uses an explicit fieldname list.

Docs citing the affected numbers (port-inventory.md, README.md, seams 03/06) were reconciled in the same commit, and the pin was bumped core-v29.19.2core-v29.20.0 (newest core tag; a fate-diff between the two showed zero classification changes, documented in the README's "Pin choice" section).

@romanornr

Copy link
Copy Markdown
Collaborator Author

Second review round addressed in 46e7ea6 — all eight findings verified against the data first, and all eight were valid:

fate() ignored the post-rename path (CodeRabbit, and Codex's self-referential moved? rows are the same root cause) — confirmed and larger than reported: 208 R* rows whose new path exists in v29 were misfated gone/moved? (96 of them self-referentially pointing at their own path). Fixed by checking new_path in v29_tree before old-path lineage. Fates now: 726 gone / 1,097 present / 622 identical / 69 moved / 92 moved?.

Migration backport false positive (Codex) — confirmed: witdrawals.down.sql's one-line placeholder blob-matched an unrelated upstream down-migration. Backport detection is now path-level only; the pair is back in the via queue (D = 40, backported = 31).

via-crate over-matching (Codex) — confirmed: 74 tooling rows (via-playground, .github lint, docker dirs, protocol-upgrade scripts) sat in the via-crate bucket. A crate now requires a Cargo.toml with a package name under its directory; via-crate = 403 rows across exactly 39 distinct units — the headline count is now true by construction.

verifier_dal vs via_verifier_dal unit-key mismatch (CodeRabbit) — fixed as a by-product of the above: unit keys are now the Cargo package names, so every wave name in port-inventory.md resolves to a CSV unit (via_verifier_dal, via_verifier_btc_sender, …).

CRLF in the checked-in CSV (Codex) — confirmed (all 3,458 lines); writer now uses lineterminator="\n" and git diff --check passes on the new commit.

config missing from the wave queue (Codex) — added as Wave 0 in port-inventory.md: via's smart-config structs/secrets land before any crate.

SettlementLayer::L1(...) placeholder (CodeRabbit) — removed from seam 02; the audit is now binary per consumer: omit the layer, or a via-specific adapter with an explicit invariant that the ETH path stays inert.

Docs citing affected counts (port-inventory.md table, README, seam 06) were reconciled in the same commit. CSV remains 3,457 rows and byte-identical across runs with different PYTHONHASHSEED.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 46e7ea6dd2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +165 to +167
hits = v29_by_blob.get(base_blobs.get(path) or head_blobs.get(new_path))
if hits:
return f"moved:{min(hits)}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid confirming moves from empty/common blobs

For files whose blob is empty or a stock placeholder, this records an arbitrary v29 path as a confirmed moved: fate even though there is no lineage. The checked-in CSV demonstrates this with core/tests/via_loadnext/src/sdk/abi/IL1Bridge.json (empty) being mapped to CHANGELOG.md, and with the verifier withdrawal down migration mapped to an unrelated DAL down script. Since moved is documented as a confirmed reorganization that should be re-derived at the target path, these rows send the refork work queue to edit unrelated files; common/empty blobs need to be ignored or constrained by path similarity.

Useful? React with 👍 / 👎.

Comment on lines +107 to +109
3. **Wave 3**: `via_state_keeper`, `via_btc_watch`, `via_btc_sender`†, `via_external_node`, `via_verifier_dal`, `via_withdrawal_client`, `via_indexer`
4. **Wave 4**: `via_musig2`, `via_verifier_btc_sender`, `via_verifier_btc_watch`, `via_verifier_reorg_detector`, `via_verifier_state`, `via_verifier_storage_init`
5. **Wave 5**: `via_verifier_coordinator`, `via_zk_verifier`

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Schedule every CSV port unit in the waves

The workflow groups work by the CSV port_unit, but this wave list omits several package-name units that the generated inventory contains, including via_indexer_bin, via_indexer_storage_init, via_block_reverter_cli, via_verifier, and via_verifier_block_reverter. A Smithers run following this ordering has no wave assignment for those rows, so whole binaries/storage-init/reverter units can be skipped or parked indefinitely; either list them explicitly or make the documented grouping match the CSV units.

Useful? React with 👍 / 👎.

Comment on lines +73 to +74
if p[0] == "core" and len(p) > 2 and p[1] in ("lib", "node", "bin", "tests"):
return p[2]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Use cargo package names for upstream units

For upstream crates this stores the directory segment as port_unit (for example config, state_keeper, or basic_types) rather than the actual Cargo package names (zksync_config, zksync_state_keeper, zksync_basic_types). The Smithers workflow consumes this CSV as the ownership map and runs cargo build -p <crate>, so those units fail validation or require an undocumented manual mapping before any port work can be tested; the inventory needs a build package column or package-name units for Cargo crates.

Useful? React with 👍 / 👎.

`etc/refork/inventory.csv`, grouped by `port_unit`, ordered by the dependency waves in
`port-inventory.md`. A unit's work item bundles:

- its Category A rows (crate files / embedded additions),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Filter zero-work Category A rows from the queue

This queues every Category A row for a unit, but Category A also includes the 262 generated .sqlx rows and the 31 backported-migration rows that the inventory and seam 06 explicitly say are regenerated or already present in v29. A Smithers run following this work-queue spec can waste time copying stale SQLx metadata or reapplying upstream migrations that should be zero port work; the queue should exclude generated and backported-migration subclasses (and any A rows marked identical-to-v29) instead of taking all Category A rows.

Useful? React with 👍 / 👎.

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.

2 participants