Use when the repo you found a bug in is a fork or port of another project and you are asking whether the original still has the same bug — or whether to report it there too. A port inherits its parent's bugs, so a defect found downstream is probably still live upstream, and vice versa. Check it, then report in both houses with each artifact cross-referencing its counterpart. But re-derive every anchor against the OTHER repo's own origin/main: line numbers drift, subcommands get deprecated, issue numbers autolink to the wrong project, label sets differ, and one house requires a DCO sign-off the other doesn't. Never copy a claim across a fork. Trigger terms: fork, port, upstream, downstream, vendored, same code, inherited bug, does upstream have this, does the original have this, is this fixed upstream, report it upstream too, cross-reference.
Port the report upstream (but re-verify every anchor)
Purpose
A port inherits its parent's bugs. Fixing one downstream and stopping there leaves
the defect live for the larger user base, and leaves the two projects diverging on a
file that was identical — which is a cost the downstream maintainer will pay at the
next sync. Reporting in both houses doubles the value of one investigation for near
zero extra investigation.
The trap is that it feels like copy-paste, and it is not. Everything about the code
differs even when the code doesn't: line numbers, the name of the subcommand you tell
people to run, which labels exist, whether commits need a sign-off, and — worst,
because it looks like it worked — issue numbers, which silently autolink to whatever
issue happens to have that number in the repo you're posting to. A ported report with
stale anchors is worse than no report: it tells the second maintainer you didn't
actually look at their code.
When to use
The repo you found a bug in is described as a port/fork of another project, or you
recognize the file structure.
You've landed (or are about to land) a downstream report and the offending file
looks unmodified from its origin.
Direction is symmetric: upstream→downstream is the same job.
When NOT to use
The two copies have genuinely diverged at the site you're reporting — then it's a
new investigation in the second repo, not a port. Diff first.
The upstream project is dead/archived, or the fork is a hard divergence nobody
syncs.
You haven't finished the downstream report yet. Land the evidence where you ran it
first; the second report cites the first.
The practice (checklist)
Diff the actual file, both from origin/main. Not from your working
branch — a clone you've been fixing in is checked out somewhere else.
git show origin/main:<path> on both sides, then diff. Quote the diff in the
report; "byte-identical apart from the import module name" is a claim a
reviewer can check in one command.
Reproduce it in the second repo by running it, not by reading it. The
defect existing in the source is a hypothesis until the second project's own
test run and CLI show it. Bring the same real-method-plus-control discipline.
Re-derive every anchor against the second repo's origin/main:
- line numbers — they drift; state each one only after reading it there;
- command/subcommand names — the one you used downstream may be deprecated
or renamed upstream, so your repro instructions would fail for a maintainer;
- issue/PR numbers in prose — safe only if they belong to the repo you're
posting in. Verify each resolves to the expected title, or use SHAs. A
number that autolinks to a nonexistent or unrelated issue in the target repo
is the most visible possible tell.
Done when every anchor in the draft has been re-read in the second repo —
count them and account for each, rather than checking the ones you remember.
Re-read the second house's contract, don't assume it matches:
- labels — an issue template can declare a label the repo doesn't have
(downstream) while the other repo does (upstream);
- sign-off — DCO Signed-off-by required in one house, license-by-
submission in the other;
- CONTRIBUTING order — issue-first vs PR-first can differ.
(This is sign-off-the-house-way and
read-the-houses-agent-file-first
applied a second time, from scratch. Two houses, two contracts.)
Cross-reference every artifact in both directions — each issue and PR names
its counterpart as "same code, found downstream/upstream", so either maintainer
sees the whole picture and can tell the fixes won't diverge.
Keep the numbering parallel if you can. Creating in the same defect order
in both repos so #A↔#X, #B↔#Y line up costs nothing and makes every
cross-reference readable. Worth more than any other ordering rationale.
Say where the provenance claim came from. "Inherited, not introduced by the
port" is load-bearing for the downstream reviewer — back it with the diff, and
if you earlier guessed the opposite, correct it in the open rather than
quietly.
Label CI evidence honestly per house. One project may run checks for
outside contributors immediately (green is observed); the other may hold
workflows at action_required pending maintainer approval (green is only your
local suite — say so). Per
verify-before-a-committer-comment.
Rationalizations
Shortcut
Why it fails
"Same file, so the same report applies."
The code is the same; nothing else is. The aggregate dispatch is line 1186 upstream and 1215 downstream — a 29-line offset in files whose relevant function is byte-identical.
"I'll reuse the repro command from the downstream report."
Downstream's subcommand was execute-test; upstream has it in DEPRECATED_SUBCOMMANDS and expects run. A maintainer following your instructions would hit a deprecation path, not the bug.
"The issue numbers I cited are fine, it's the same project family."
They autolink per-repo. Citing #882/#939 upstream resolved correctly; the same numbers downstream would have pointed at issues that don't exist in apache/solr-orbit. Use SHAs where numbers aren't native.
"The issue template lists the labels, so they exist."
Downstream's bug_template.yml declared untriaged; the repo has no such label. Upstream does. Check the label set, not the template.
"My commits are already fine, they passed downstream."
Upstream gates on DCO; downstream doesn't. Same commits, one house rejects them. Re-read the sign-off contract per repo.
"It's clearly port-specific, no need to check upstream."
I asserted exactly this about the unconditional blocker — a guess in a document meant to be evidence. Reading upstream's aggregator.py:169 refuted it: identical wrong attribute. The guess would have understated the blast radius by an entire user base.
"I'll just note the equivalence and let them port it."
You already have the fix and the test; the second maintainer has neither, plus the burden of translating. Porting the patch is minutes when the file is identical.
RECEIPT
MERGE-receipt — one investigation, two projects, 12 artifacts, and all six PRs
merged in both houses. Verified via gh 2026-08-02: downstream solr-orbit #59/#58/#60
merged 2026-07-27/29/30 by the author of the port, upstream OSB #1097/#1096/#1098 merged
07-28/29/30. Every issue closed. Upstream PRs carried 3 × SUCCESS checks (Code-Diff-
Analyzer, Code-Diff-Reviewer, DCO) throughout.
The provenance claim is what the merges settle. Reporting in both houses was justified
on the argument that the defect was inherited, not port-specific — and both sets of
maintainers accepted the fix in their own repo, independently. Had the diff-based claim
been wrong, one of them would have closed it.
Three aggregate defects were found downstream in apache/solr-orbit by finishing
a real benchmark campaign (use-the-tool-for-its-purpose),
then established as inherited — not introduced by the port — and reported and
fixed upstream in opensearch-project/opensearch-benchmark as well:
The provenance claim, checked not asserted — both files from origin/main,
295 lines each:
$ diff <(git -C solr-orbit show origin/main:solrorbit/aggregator.py) \
<(git -C opensearch-benchmark show origin/main:osbenchmark/aggregator.py)
6,8c6,8
< from solrorbit.metrics import FileTestRunStore, TestRun
---
> from osbenchmark.metrics import FileTestRunStore, TestRun
[...import module name only...]
Byte-identical apart from three import lines. So every downstream conclusion
transfers — and the fixes did too, applied with only the module name rewritten.
What re-verification caught before posting (the clone was sitting on a fix
branch, which is what prompted re-deriving everything against origin/main):
the aggregate dispatch line is 1186 upstream, 1215 downstream;
the subcommand is run upstream — downstream's execute-test is in upstream's
DEPRECATED_SUBCOMMANDS, so the ported repro instructions would have been wrong;
#882/#939/#638/#692 are safe to cite upstream (each verified to resolve
with the expected title) and would have autolinked to nonexistent issues
downstream — SHAs used there instead;
untriaged exists upstream, not downstream, despite downstream's template
declaring it;
and the provenance guess itself was wrong: I had written that the unconditional
blocker "appears port-specific." Upstream's aggregator.py:169 carries the
identical wrong attribute. Corrected in the open.
Also the same blind spot in both houses: the aggregator suite never constructs a
real test-run object, so 1099 tests downstream and 1422 upstream passed over a command
that could not run once.
Baseline discipline per repo: each fix is one commit off that repo's own origin/main
(edb3d03e / a5880562), full suite compared to that repo's own baseline
(1422 → 1423/1424 upstream), lint clean by that repo's own config.
Lifecycle
Signals it worked: both maintainers engage; neither asks "does this apply to
us?"; no correction of a line number, command name, or autolink in either thread.
What to log on a misfire: which anchor was stale, and whether it came from
copying the sibling report or from a clone that wasn't on main.
Record it in LEDGER.md.
Death criterion: if the two copies diverge enough that the diff is no longer
small, this stops being a port and each report is its own investigation.
1---2name: port-the-report-upstream3description: Use when the repo you found a bug in is a fork or port of another project and you are asking whether the original still has the same bug — or whether to report it there too. A port inherits its parent's bugs, so a defect found downstream is probably still live upstream, and vice versa. Check it, then report in both houses with each artifact cross-referencing its counterpart. But re-derive every anchor against the OTHER repo's own origin/main: line numbers drift, subcommands get deprecated, issue numbers autolink to the wrong project, label sets differ, and one house requires a DCO sign-off the other doesn't. Never copy a claim across a fork. Trigger terms: fork, port, upstream, downstream, vendored, same code, inherited bug, does upstream have this, does the original have this, is this fixed upstream, report it upstream too, cross-reference.4---56# Port the report upstream (but re-verify every anchor)78## Purpose910A port inherits its parent's bugs. Fixing one downstream and stopping there leaves11the defect live for the larger user base, and leaves the two projects diverging on a12file that was identical — which is a cost the downstream maintainer will pay at the13next sync. Reporting in both houses doubles the value of one investigation for near14zero extra investigation.1516The trap is that it feels like copy-paste, and it is not. Everything *about* the code17differs even when the code doesn't: line numbers, the name of the subcommand you tell18people to run, which labels exist, whether commits need a sign-off, and — worst,19because it looks like it worked — issue numbers, which silently autolink to whatever20issue happens to have that number in the repo you're posting to. A ported report with21stale anchors is worse than no report: it tells the second maintainer you didn't22actually look at their code.2324## When to use2526- The repo you found a bug in is described as a port/fork of another project, or you27 recognize the file structure.28- You've landed (or are about to land) a downstream report and the offending file29 looks unmodified from its origin.30- Direction is symmetric: upstream→downstream is the same job.3132## When NOT to use3334- The two copies have genuinely diverged at the site you're reporting — then it's a35 new investigation in the second repo, not a port. Diff first.36- The upstream project is dead/archived, or the fork is a hard divergence nobody37 syncs.38- You haven't finished the downstream report yet. Land the evidence where you ran it39 first; the second report cites the first.4041## The practice (checklist)4243- [ ] **Diff the actual file, both from `origin/main`.** Not from your working44 branch — a clone you've been fixing in is checked out somewhere else.45 `git show origin/main:<path>` on both sides, then `diff`. Quote the diff in the46 report; "byte-identical apart from the import module name" is a claim a47 reviewer can check in one command.48- [ ] **Reproduce it in the second repo by running it, not by reading it.** The49 defect existing in the source is a hypothesis until the second project's own50 test run and CLI show it. Bring the same real-method-plus-control discipline.51- [ ] **Re-derive every anchor against the second repo's `origin/main`:**52 - **line numbers** — they drift; state each one only after reading it there;53 - **command/subcommand names** — the one you used downstream may be deprecated54 or renamed upstream, so your repro instructions would fail for a maintainer;55 - **issue/PR numbers in prose** — safe only if they belong to the repo you're56 posting *in*. Verify each resolves to the expected title, or use SHAs. A57 number that autolinks to a nonexistent or unrelated issue in the target repo58 is the most visible possible tell.5960 *Done when* every anchor in the draft has been re-read in the second repo —61 count them and account for each, rather than checking the ones you remember.62- [ ] **Re-read the second house's contract**, don't assume it matches:63 - **labels** — an issue template can declare a label the repo doesn't have64 (downstream) while the other repo does (upstream);65 - **sign-off** — DCO `Signed-off-by` required in one house, license-by-66 submission in the other;67 - **CONTRIBUTING order** — issue-first vs PR-first can differ.68 (This is [sign-off-the-house-way](../sign-off-the-house-way/SKILL.md) and69 [read-the-houses-agent-file-first](../read-the-houses-agent-file-first/SKILL.md)70 applied a second time, from scratch. Two houses, two contracts.)71- [ ] **Cross-reference every artifact in both directions** — each issue and PR names72 its counterpart as "same code, found downstream/upstream", so either maintainer73 sees the whole picture and can tell the fixes won't diverge.74- [ ] **Keep the numbering parallel if you can.** Creating in the same defect order75 in both repos so #A↔#X, #B↔#Y line up costs nothing and makes every76 cross-reference readable. Worth more than any other ordering rationale.77- [ ] **Say where the provenance claim came from.** "Inherited, not introduced by the78 port" is load-bearing for the downstream reviewer — back it with the diff, and79 if you earlier guessed the opposite, correct it in the open rather than80 quietly.81- [ ] **Label CI evidence honestly per house.** One project may run checks for82 outside contributors immediately (green is *observed*); the other may hold83 workflows at `action_required` pending maintainer approval (green is only your84 *local* suite — say so). Per85 [verify-before-a-committer-comment](../verify-before-a-committer-comment/SKILL.md).8687## Rationalizations8889| Shortcut | Why it fails |90|---|---|91| "Same file, so the same report applies." | The *code* is the same; nothing else is. The `aggregate` dispatch is line **1186** upstream and **1215** downstream — a 29-line offset in files whose relevant function is byte-identical. |92| "I'll reuse the repro command from the downstream report." | Downstream's subcommand was `execute-test`; upstream has it in `DEPRECATED_SUBCOMMANDS` and expects `run`. A maintainer following your instructions would hit a deprecation path, not the bug. |93| "The issue numbers I cited are fine, it's the same project family." | They autolink per-repo. Citing `#882`/`#939` upstream resolved correctly; the same numbers downstream would have pointed at issues that don't exist in `apache/solr-orbit`. Use SHAs where numbers aren't native. |94| "The issue template lists the labels, so they exist." | Downstream's `bug_template.yml` declared `untriaged`; the repo has no such label. Upstream *does*. Check the label set, not the template. |95| "My commits are already fine, they passed downstream." | Upstream gates on DCO; downstream doesn't. Same commits, one house rejects them. Re-read the sign-off contract per repo. |96| "It's clearly port-specific, no need to check upstream." | I asserted exactly this about the unconditional blocker — a guess in a document meant to be evidence. Reading upstream's `aggregator.py:169` refuted it: identical wrong attribute. The guess would have understated the blast radius by an entire user base. |97| "I'll just note the equivalence and let them port it." | You already have the fix and the test; the second maintainer has neither, plus the burden of translating. Porting the patch is minutes when the file is identical. |9899## RECEIPT100101***MERGE-receipt*** — **one investigation, two projects, 12 artifacts, and all six PRs102merged in both houses.** Verified via `gh` 2026-08-02: downstream solr-orbit #59/#58/#60103merged 2026-07-27/29/30 by the author of the port, upstream OSB #1097/#1096/#1098 merged10407-28/29/30. Every issue closed. Upstream PRs carried 3 × SUCCESS checks (Code-Diff-105Analyzer, Code-Diff-Reviewer, DCO) throughout.106107**The provenance claim is what the merges settle.** Reporting in both houses was justified108on the argument that the defect was *inherited*, not port-specific — and both sets of109maintainers accepted the fix in their own repo, independently. Had the diff-based claim110been wrong, one of them would have closed it.111112Three `aggregate` defects were found downstream in **apache/solr-orbit** by finishing113a real benchmark campaign ([use-the-tool-for-its-purpose](../use-the-tool-for-its-purpose/SKILL.md)),114then established as **inherited** — not introduced by the port — and reported and115fixed upstream in **opensearch-project/opensearch-benchmark** as well:116117| defect | solr-orbit | OpenSearch Benchmark |118|---|---|---|119| null metrics crash metric reduction | [#55](https://github.com/apache/solr-orbit/issues/55) → [#58](https://github.com/apache/solr-orbit/pull/58) | [#1093](https://github.com/opensearch-project/opensearch-benchmark/issues/1093) → [#1096](https://github.com/opensearch-project/opensearch-benchmark/pull/1096) |120| unconditional failure — reads a test-run attribute that doesn't exist | [#56](https://github.com/apache/solr-orbit/issues/56) → [#59](https://github.com/apache/solr-orbit/pull/59) | [#1094](https://github.com/opensearch-project/opensearch-benchmark/issues/1094) → [#1097](https://github.com/opensearch-project/opensearch-benchmark/pull/1097) |121| `--workload-path` parity gap with `run` | [#57](https://github.com/apache/solr-orbit/issues/57) → [#60](https://github.com/apache/solr-orbit/pull/60) | [#1095](https://github.com/opensearch-project/opensearch-benchmark/issues/1095) → [#1098](https://github.com/opensearch-project/opensearch-benchmark/pull/1098) |122123**The provenance claim, checked not asserted** — both files from `origin/main`,124295 lines each:125126```console127$ diff <(git -C solr-orbit show origin/main:solrorbit/aggregator.py) \128 <(git -C opensearch-benchmark show origin/main:osbenchmark/aggregator.py)1296,8c6,8130< from solrorbit.metrics import FileTestRunStore, TestRun131---132> from osbenchmark.metrics import FileTestRunStore, TestRun133[...import module name only...]134```135136Byte-identical apart from three import lines. So every downstream conclusion137transfers — and the fixes did too, applied with only the module name rewritten.138139**What re-verification caught before posting** (the clone was sitting on a fix140branch, which is what prompted re-deriving everything against `origin/main`):141142- the `aggregate` dispatch line is **1186** upstream, **1215** downstream;143- the subcommand is **`run`** upstream — downstream's `execute-test` is in upstream's144 `DEPRECATED_SUBCOMMANDS`, so the ported repro instructions would have been wrong;145- `#882`/`#939`/`#638`/`#692` are safe to cite upstream (each verified to resolve146 with the expected title) and would have autolinked to nonexistent issues147 downstream — SHAs used there instead;148- `untriaged` exists upstream, not downstream, despite downstream's template149 declaring it;150- upstream requires DCO sign-off, downstream doesn't;151- and the provenance guess itself was wrong: I had written that the unconditional152 blocker "appears port-specific." Upstream's `aggregator.py:169` carries the153 identical wrong attribute. Corrected in the open.154155**Also the same blind spot in both houses:** the aggregator suite never constructs a156real test-run object, so 1099 tests downstream and 1422 upstream passed over a command157that could not run once.158159Baseline discipline per repo: each fix is one commit off that repo's own `origin/main`160(`edb3d03e` / `a5880562`), full suite compared to that repo's own baseline161(1422 → 1423/1424 upstream), lint clean by that repo's own config.162163## Lifecycle164165- **Signals it worked:** both maintainers engage; neither asks "does this apply to166 us?"; no correction of a line number, command name, or autolink in either thread.167- **What to log on a misfire:** which anchor was stale, and whether it came from168 copying the sibling report or from a clone that wasn't on `main`.169 Record it in [`LEDGER.md`](../../LEDGER.md).170- **Death criterion:** if the two copies diverge enough that the diff is no longer171 small, this stops being a port and each report is its own investigation.172- **Relates to:** sourced by173 [use-the-tool-for-its-purpose](../use-the-tool-for-its-purpose/SKILL.md);174 re-applies [sign-off-the-house-way](../sign-off-the-house-way/SKILL.md),175 [read-the-houses-agent-file-first](../read-the-houses-agent-file-first/SKILL.md)176 and [match-the-house-shape](../match-the-house-shape/SKILL.md) from scratch in the177 second house; the anchor re-derivation is178 [verify-before-a-committer-comment](../verify-before-a-committer-comment/SKILL.md)179 applied to your own ported text.
Run npx skillmds@latest add serhiy-bzhezytskyy/port-the-report-upstream in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Use when the repo you found a bug in is a fork or port of another project and you are asking whether the original still has the same bug — or whether to report it there too. A port inherits its parent's bugs, so a defect found downstream is probably still live upstream, and vice versa. Check it, then report in both houses with each artifact cross-referencing its counterpart. But re-derive every anchor against the OTHER repo's own origin/main: line numbers drift, subcommands get deprecated, issue numbers autolink to the wrong project, label sets differ, and one house requires a DCO sign-off the other doesn't. Never copy a claim across a fork. Trigger terms: fork, port, upstream, downstream, vendored, same code, inherited bug, does upstream have this, does the original have this, is this fixed upstream, report it upstream too, cross-reference. It is listed under Docs & Writing on SkillMD.
This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
serhiy-bzhezytskyy (@serhiy-bzhezytskyy) published this skill. Their other Agent Skills are listed on their SkillMD profile.