Skip to content

Boards: list a report's public dump, count its private ones; a pinout is not always UART - #538

Merged
openipc-ai merged 2 commits into
masterfrom
board-dump-counts
Oct 30, 2026
Merged

openipc-ai merged 2 commits into
masterfrom
board-dump-counts

Conversation

@openipc-ai

Copy link
Copy Markdown
Collaborator

On /cameras/boards the stats line counted 11 factory flash dumps. That is only the OpenHisiIpCam archive: no owner report's backup has ever reached its board. Published reports hold 45 backups: 36 kept private by their senders, 5 shared but made private by a reviewer, and 4 public.

Dumps

  • A dump its sender shared publicly becomes a flash_dump file of the report's board unit. It is listed at the report's own address (board_artifacts.href) and never copied under BOARDS_ROOT. The report serves it no-store, so a reviewer making it private takes it down at once. A copy under /board-files/ is cached for a month, through the mirrors too, and would not come down.
  • A dump that isn't served (private by the sender or by a reviewer) is counted on its unit: board_units.private_dumps, migration 044. The API sums it per board as coverage.private_dumps, and the stats line adds "N more kept private, for maintainers".
  • PublishedTexts now also returns a published report that has only a private dump, so its count isn't lost.

Pinouts

  • The stat said "827 with UART pinout", but it counts every pinout file. The vendors' files are often connector diagrams: Xiongmai's interface drawings show VGA, HDMI and SATA, and cctvsp labels power, Ethernet and RS485. Neither names a UART. The files carry no text that could tell them apart, so the label now says "with a pinout" (en, ru, zh), and so does the catalogue lede.

The other numbers were checked against the database and are right: 1,397 boards, 349 devices, 29 named makers, and 919 boards still needing a pinout.

Tests

  • TestAReportsDumpIsLinkedNotCopiedAndItsPrivateOnesAreCounted: the dump is linked on both boards of a report and not on disk, the private count shows in coverage, a second refresh rewrites nothing, and making the dump private unlists and counts it.
  • TestAPublishedReportsDumpIsLinkedOrCounted: a public dump comes back as a link, a private one as a count, and a reviewer's private decision moves it from the first to the second.
  • A frontend stats test.
  • service/run.sh test passes in full; frontend lint, typecheck and tests pass.

Note for the Ruision donor work: its uncommitted 044_board_ruision.sql must become 045 if this lands first.

… is not always UART

The catalogue's "factory flash dumps" counted only the OpenHisiIpCam archive's
11: an owner report's backup never reached its board, shared or not.

- A dump its sender shared publicly is a flash_dump of the report's unit,
  listed at its report's address (board_artifacts.href) and never copied under
  BOARDS_ROOT: the report serves it no-store, so making it private takes it
  down at once, which a copy cached for a month under /board-files/ would not.
- A dump not served -- kept private by its sender or by a reviewer -- is
  counted on its unit (board_units.private_dumps, migration 044); the API
  sums it per board (coverage.private_dumps) and the stats line says how many
  more a maintainer has.
- "with UART pinout" counted every pinout, connector diagrams included (the
  vendors' interface drawings name no UART): it now says "with a pinout", in
  the lede too.
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

List public report dumps on boards and count private backups

✨ Enhancement 🐞 Bug fix 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• List public report backups on linked boards without creating cached copies that could outlive
 privacy changes.
• Count unserved backups separately and describe all pinout files without implying they contain UART
 pinouts.
• Add migration, API, frontend, and integration coverage for dump visibility and counts.
Diagram

graph TD
  R["Published reports"] --> D{"Dump served?"} -->|yes| H["Report file URL"] --> U["Board unit"] --> A["Board API"] --> S["Catalogue stats"]
  D -->|no| P["Private dump count"] --> U
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Compute report dump coverage at API read time
  • ➕ Avoids storing a derived private count on each board unit.
  • ➕ Reflects review decisions without waiting for a board refresh.
  • ➖ Couples catalogue reads to report and review tables.
  • ➖ Adds joins or aggregation to board requests and bypasses the existing contributed-unit refresh.
2. Copy public backups into board storage
  • ➕ Uses the existing board-file serving path without external artifact links.
  • ➖ Month-long board-file caching could keep a backup available after a reviewer makes it private.
  • ➖ Duplicates potentially large backups.

Recommendation: Keep the PR’s report-endpoint links and per-unit private counts. They fit the existing contribution pipeline while preserving the report endpoint’s immediate privacy behavior; the alternatives either complicate catalogue reads or weaken takedowns.

Files changed (22) +302 / -57

Enhancement (9) +111 / -38
Boards.tsxDisplay private-dump totals when present +1/-0

Display private-dump totals when present

• Adds a separate, conditional private-backup figure to the board catalogue statistics.

frontend/apps/site/src/components/boards/Boards.tsx

model.tsAggregate private backups in catalogue statistics +4/-1

Aggregate private backups in catalogue statistics

• Adds a private-dump total while keeping private backups outside the artifact-coverage key mapping.

frontend/apps/site/src/lib/boards/model.ts

types.tsType the private-dump coverage field +2/-0

Type the private-dump coverage field

• Allows board coverage to carry the count of unserved owner-report backups.

frontend/apps/site/src/lib/boards/types.ts

club.goProject report backups into contributed board units +10/-2

Project report backups into contributed board units

• Links served backups as flash-dump artifacts at their report URLs and passes unserved-backup counts to each linked board unit.

service/cmd/openipc/club.go

api.goExpose private coverage and report-hosted artifact URLs +16/-10

Expose private coverage and report-hosted artifact URLs

• Sums private dumps per board and uses an artifact’s report URL when present, retaining the board-file prefix for ordinary files.

service/internal/boards/api.go

contributions.goImport linked dumps without copying them +27/-8

Import linked dumps without copying them

• Carries report URL and file metadata through contributions, stores private counts on units, and skips filesystem reads for linked artifacts. The unchanged check includes private counts and linked-file checksums.

service/internal/boards/contributions.go

import.goPersist externally served board artifacts +10/-4

Persist externally served board artifacts

• Builds artifacts from supplied URLs, sizes, and checksums rather than reading linked files, and stores their URLs in the new href column.

service/internal/boards/import.go

parse.goRepresent linked files in board imports +8/-1

Represent linked files in board imports

• Extends the board file model with a URL, byte count, and checksum for report-hosted backups.

service/internal/boards/parse.go

club.goReturn public backup links and unserved counts from reports +33/-12

Return public backup links and unserved counts from reports

• Includes served backups and their metadata in published report text, counts backups without a public copy, and retains reports whose only contribution is a private backup.

service/internal/reports/club.go

Tests (3) +135 / -1
boards.test.tsTest separate public and private dump totals +7/-1

Test separate public and private dump totals

• Updates the expected statistics shape and verifies that private backups do not inflate the served-dump total.

frontend/apps/site/src/lib/boards/boards.test.ts

contributions_test.goTest linked dumps, private counts, and refresh behavior +77/-0

Test linked dumps, private counts, and refresh behavior

• Checks that a public dump appears on both linked boards without a disk copy, a repeated refresh preserves artifact rows, and privatizing it removes its link and increases the count.

service/internal/boards/contributions_test.go

api_test.goTest published backup visibility decisions +51/-0

Test published backup visibility decisions

• Verifies that a sender-shared backup yields a report-file link, a private backup yields only a count, and a reviewer’s private decision moves a backup from link to count.

service/internal/reports/api_test.go

Documentation (9) +43 / -18
boards.en.ymlCorrect English pinout wording and add private-dump statistic +6/-3

Correct English pinout wording and add private-dump statistic

• Removes the unsupported UART claim from the catalogue lede and pinout statistic. Adds text for backups kept private.

data/locales/boards.en.yml

boards.ru.ymlCorrect Russian pinout wording and add private-dump statistic +8/-3

Correct Russian pinout wording and add private-dump statistic

• Generalizes the lede and pinout statistic, and supplies plural forms for the private-backup count.

data/locales/boards.ru.yml

boards.zh.ymlCorrect Chinese pinout wording and add private-dump statistic +6/-3

Correct Chinese pinout wording and add private-dump statistic

• Removes the UART-specific claim and adds copy for backups held privately.

data/locales/boards.zh.yml

boards.en.jsonSync English board statistics translations +6/-2

Sync English board statistics translations

• Generalizes pinout wording and adds the private-dump statistic to the frontend translation bundle.

frontend/apps/site/src/i18n/boards.en.json

boards.ru.jsonSync Russian board statistics translations +8/-2

Sync Russian board statistics translations

• Generalizes pinout wording and adds pluralized private-dump text to the frontend translation bundle.

frontend/apps/site/src/i18n/boards.ru.json

boards.zh.jsonSync Chinese board statistics translations +6/-2

Sync Chinese board statistics translations

• Generalizes pinout wording and adds private-dump text to the frontend translation bundle.

frontend/apps/site/src/i18n/boards.zh.json

en.jsonGeneralize English catalogue lede +1/-1

Generalize English catalogue lede

• Describes pinouts without asserting that every file documents UART.

frontend/apps/site/src/i18n/en.json

ru.jsonGeneralize Russian catalogue lede +1/-1

Generalize Russian catalogue lede

• Describes pinouts without asserting that every file documents UART.

frontend/apps/site/src/i18n/ru.json

zh.jsonGeneralize Chinese catalogue lede +1/-1

Generalize Chinese catalogue lede

• Describes pinouts without asserting that every file documents UART.

frontend/apps/site/src/i18n/zh.json

Other (1) +13 / -0
044_board_unit_private_dumps.sqlAdd private-dump counts and external artifact links +13/-0

Add private-dump counts and external artifact links

• Adds a nonnegative private-dump count to board units and a constrained report-file href to board artifacts.

service/internal/db/migrations/044_board_unit_private_dumps.sql

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Catalogue totals count a report's dumps once per board ✓ Resolved
Description
stats() sums coverage.private_dumps (and flash_dumps, which now include report dumps) over
every board. refreshReportUnits gives a report one unit on each board it is linked to, and each
unit carries the report's full PrivateDumps and a copy of its public dump artifact. So a report
linked to two boards adds its dumps to the stats line twice, and the '45 backups' total the PR aims
to show comes out too high.
Code

frontend/apps/site/src/lib/boards/model.ts[346]

+    privateDumps: all.reduce((n, m) => n + (m.coverage.private_dumps ?? 0), 0),
Evidence
club.go builds one Contribution per (report, model) and gives each one `PrivateDumps:
t.PrivateDumps` plus the same backup file. The comment there says 'A report published on several
boards is a unit on each', and the PR's own test says the dump 'is linked on both boards of a
report'. api.go sums private_dumps per board, and model.ts then adds up all the per-board values.

service/cmd/openipc/club.go[79-95]
service/internal/reports/club.go[900-918]
frontend/apps/site/src/lib/boards/model.ts[339-349]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The catalogue stats line adds up per-board dump counts. A report linked to several boards becomes one unit per board, and each unit carries the same private count and the same public dump artifact. Those dumps are therefore counted once per board in the site-wide totals.
## Fix Focus Areas
- frontend/apps/site/src/lib/boards/model.ts[343-347]
- service/internal/boards/api.go[345-350]
- service/cmd/openipc/club.go[84-95]
## Recommended Fix
Computing the totals from per-board coverage cannot remove the duplicates. Have the API return catalogue-wide totals that count distinct dumps instead: distinct `sha256` for flash_dump artifacts, and for private dumps, the count taken once per report (for example, grouped by the report id in `source_ref`, not summed over units). Have `stats()` use those totals. Keep the per-board coverage values for each board's own page.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can route each severity your way: inline, summary, both, or drop

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Qodo's review of #538: a report published on two boards is a unit on each,
carrying its dumps, so summing the boards' coverage counted them twice. The
tree now says its totals itself -- distinct served dumps by sha256, and a
report's private ones once per report (by the receipt in its units'
reference) -- and the stats line uses them; each board keeps its own
coverage.
@openipc-ai
openipc-ai merged commit 6335953 into master Oct 30, 2026
4 checks passed
@openipc-ai
openipc-ai deleted the board-dump-counts branch October 30, 2026 14:57
openipc-ai added a commit that referenced this pull request Oct 30, 2026
#540)

* Web role: refresh the published reports' board units in the background

The web role refreshed every published report's board unit before it
listened. A change that makes each unit new -- #538 gave them a private-dump
count -- rewrites all of them on the first start, which took production past
openipc-deploy's 90-second /up gate twice; it rolled back, dev having too few
reports to show it. The refresh now starts once the routes are up, in the
background, and the role's stop waits for it before giving up the web lock.
A refresh cut short by a stop is finished by the next start: a unit whose
files are missing is rewritten.

* Board units: read the reports under the lock, stage a rewrite, bound the shutdown wait

Qodo's review of #540, now that the startup refresh runs while the site
serves:
- the published reports are read once the apply's lock is held
  (RefreshReportUnits), so a refresh started first cannot apply a list older
  than a later review's and put a report rejected meanwhile back on its board;
- a unit's rewrite stages its files under Root/.staging and swaps them in
  after its rows commit, so no page links a file not yet there, and a
  rewrite cut short leaves the unit as it was;
- the web role's stop waits ten seconds for the refresh, not forever: one
  queued behind a review's waits on a mutex that does not watch the context.
openipc-ai added a commit that referenced this pull request Oct 30, 2026
The web role refreshed every published report's board unit before it
listened. A change that makes each unit new -- #538 gave them a private-dump
count -- rewrites all of them on the first start, which took production past
openipc-deploy's 90-second /up gate twice; it rolled back, dev having too few
reports to show it. The refresh now starts once the routes are up, in the
background, and the role's stop waits for it before giving up the web lock.
A refresh cut short by a stop is finished by the next start: a unit whose
files are missing is rewritten.
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