Skip to content

feat(example): expose project source priority + cross-project compare in demo - #414

Merged
thewrz merged 3 commits into
mainfrom
feat/issue-413
Jul 8, 2026
Merged

feat(example): expose project source priority + cross-project compare in demo#414
thewrz merged 3 commits into
mainfrom
feat/issue-413

Conversation

@thewrz

@thewrz thewrz commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Why

Uploading a DOCX through the demo's Project Spec Map could silently yield a project copy whose content is not the uploaded file. Live repro: two different versions of section 08 11 13 uploaded for projects A100 and B100 produced identical project copies, so the Compare view showed no differences. Diagnosis in #413: the backend is working as designed (priority-ordered project_sources resolution, ADR-015, with source/shadowed reported back); the demo hid the chain, hardcoded the company master as upload target, rewrote priorities clients-first from unrelated flows, and discarded the resolution provenance. The demo also had no working way to delete fixtures once loaded.

What

Demo-only (examples/web_ui_demo/) — no src/, no API, no openapi.yaml changes:

Source priority made explicit

  • Project Settings gains an ordered SOURCE LIBRARIES editor (reorder/remove/add; priority = list order) wired to the existing PUT /projects/:id/sources.
  • Spec Map upload asks which source library to onboard into (default = the project's priority-1 source).
  • Resolution provenance surfaced: after a section joins a project, the demo names the winning library and warns when the targeted library was shadowed (js/source-order.mjs, pure + unit-tested).
  • Order-preserving scope sync: TOC/library flows can add or drop sources but never reprioritize an explicit chain.
  • Cross-project Compare: pickers now list other projects' copies labeled by project name — two projects' versions of one section compare directly (verified live: 329 aligned ¶, 16 differing).

Fixture deletion tiers

  • Library masters (client + company): Withdraw button → DELETE /specs/:id (ADR-030 soft tombstone) with an Undo toast → POST /specs/:id/restore. The stale deleteSpec helper is renamed withdrawSpec to match real server semantics; the never-enabled specDelete flag becomes specWithdraw: true.
  • Admin full reset (re-parse from scratch): per-master Re-parse button → library-targeted re-import; the server upsert hard-deletes all parsed paragraphs/references and rebuilds them with a fresh inference pass (content_version bumps, id + clone lineage survive).
  • Project copies: removal walks the admin force path — 409s are classified (js/spec-removal.mjs, pure + unit-tested) so an edited copy offers "Force delete (admin)" (?force=true) while a package-pinned copy explains itself and never offers force.

Testing

  • Unit tests pass — node --test in examples/web_ui_demo/: 90 pass, 0 fail (15 new across source-order.test.mjs + spec-removal.test.mjs)
  • Integration tests pass — n/a (no src/ change)
  • Manual verification: drove every flow in the browser — settings reorder → save; cross-project compare shows the real deltas; withdraw → Undo round-trip; re-parse bumped content_version 2→3 rebuilding 330 paragraphs; force-remove of an edited copy logged force: true
  • CI green

🤖 Co-authored by Claude Fable 5. Closes #413.

Summary by CodeRabbit

  • New Features

    • Project settings now support ordered source libraries, with clearer guidance on priority and saving changes.
    • Added compare catalog updates so versions from other projects can appear in comparison views.
    • Library items now include a Re-parse action, and toast messages can include actionable buttons.
  • Bug Fixes

    • Spec and library removal now uses safer withdraw/restore behavior instead of immediate deletion.
    • Improved handling for removing edited project copies, including clearer conflict retries and force-removal prompts.

… in demo

Uploading a spec through the Project Spec Map could silently produce a
project copy of DIFFERENT content than the uploaded file: the demo
hardcoded the company master as the onboarding target, joined by section
number only, and several flows rewrote the project's source chain
clients-first — so a client master holding the same section shadowed the
upload with no visible signal (#413). The backend was never at fault:
POST /projects/:id/specs resolves by project_sources priority and reports
source + shadowed; the demo discarded both.

- Project Settings: ordered source-library editor (reorder / remove / add,
  priority = list order) saved via PUT /projects/:id/sources.
- Spec Map upload: pick the destination source library (priority-1 default)
  instead of a hardcoded company master.
- Join provenance: surface which library won resolution and warn when the
  targeted library was shadowed (new pure module source-order.mjs, tested).
- syncProjectSourcesToTocScope now merges order-preservingly — scope changes
  append or drop sources, never reprioritize.
- Compare pickers now include other projects' TOC copies labeled by project
  name, so two projects' versions of one section compare directly.

Closes #413

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 8 minutes

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

How can I continue?

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

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

How do review limits work?

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

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

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e2e0a25f-1296-43e3-bb92-58bdee0e265b

📥 Commits

Reviewing files that changed from the base of the PR and between 560e52d and 936638b.

📒 Files selected for processing (1)
  • examples/web_ui_demo/js/app.js
📝 Walkthrough

Walkthrough

This PR reworks the web UI demo's project source-library management from unordered checkboxes to an explicit ordered priority chain, wires it into settings, upload, and TOC-scope flows, and replaces hard-delete spec removal with withdraw/restore semantics plus guarded conflict-retry logic, alongside cross-project compare catalog refresh, supporting CSS/HTML updates, and new unit tests.

Changes

Web UI demo: ordered source priority and safe spec removal

Layer / File(s) Summary
Source-order resolution module and tests
examples/web_ui_demo/js/source-order.mjs, examples/web_ui_demo/source-order.test.mjs
New pure module adds mergeSourcesWithScope, moveSource, and resolutionNotice, with a full unit test suite covering ordering, immutability, and notice output.
Settings source-draft UI and persistence
examples/web_ui_demo/js/app.js, examples/web_ui_demo/index.html, examples/web_ui_demo/css/app.css
Replaces checkbox-based source selection with an ordered draft model (sourceDraft, priority move/remove controls), persisted via saveSourceDraft/syncProjectSourcesToTocScope; index.html and CSS updated to describe and style priority-order resolution.
Upload source-library selection and resolution notices
examples/web_ui_demo/js/app.js
joinProject and map upload flow accept an uploaded library id, prompt via chooseUploadSourceLibrary, and toast resolutionNotice messages for shadowed/mismatched resolutions.
Withdraw/restore replaces hard delete
examples/web_ui_demo/js/api.js, examples/web_ui_demo/js/features.js, examples/web_ui_demo/js/app.js, examples/web_ui_demo/css/app.css
deleteSpec is replaced by withdrawSpec/restoreSpec; removeSpecFromProject adds a force option; API_FEATURES.specWithdraw replaces specDelete; UI adds Withdraw/Re-parse actions and an Undo toast.
Guarded project-copy removal and conflict classification
examples/web_ui_demo/js/spec-removal.mjs, examples/web_ui_demo/spec-removal.test.mjs, examples/web_ui_demo/js/app.js
New classifyRemovalConflict categorizes 409 errors; removeProjectCopyGuarded retries removal with force on conflict; removeTargetSpecs no longer hard-deletes library specs.
Cross-project compare catalog refresh
examples/web_ui_demo/js/app.js
refreshCrossProjectSpecs fetches other projects' TOC entries into state and merges them into buildCompareCatalog, refreshed on view show and workspace load.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant SettingsUI as Settings UI (app.js)
  participant SourceOrder as source-order.mjs
  participant API as api.js

  User->>SettingsUI: reorder/remove source rows
  SettingsUI->>SettingsUI: setSourceDraftOrder(sourceDraft)
  User->>SettingsUI: click Save Settings
  SettingsUI->>SourceOrder: mergeSourcesWithScope(currentSources, scope, companyId)
  SourceOrder-->>SettingsUI: merged ordered list
  SettingsUI->>API: setProjectSources(projectId, order)
  API-->>SettingsUI: updated project.sources
Loading
sequenceDiagram
  participant Admin
  participant AppJS as app.js
  participant API as api.js
  participant SpecRemoval as spec-removal.mjs

  Admin->>AppJS: click Withdraw on library spec
  AppJS->>API: withdrawSpec(specId)
  API-->>AppJS: tombstoned
  AppJS-->>Admin: toastWithAction "Undo"
  Admin->>AppJS: click Undo
  AppJS->>API: restoreSpec(specId)
  API-->>AppJS: restored

  Admin->>AppJS: remove project copy
  AppJS->>API: removeSpecFromProject(projectId, specId)
  API-->>AppJS: 409 conflict
  AppJS->>SpecRemoval: classifyRemovalConflict(err)
  SpecRemoval-->>AppJS: 'force-retry'
  AppJS->>API: removeSpecFromProject(projectId, specId, {force:true})
  API-->>AppJS: removed
Loading

Possibly related PRs

  • wrzonance/SpecR#313: Introduces the same withdraw/restore (tombstone + restore) replacement for deleteSpec with matching API route and UI conflict-handling updates.
  • wrzonance/SpecR#236: Modifies the same examples/web_ui_demo/js/features.js API_FEATURES capability flag map alongside this PR's specDeletespecWithdraw change.
  • wrzonance/SpecR#386: Touches the same Compare feature and catalog-building code in examples/web_ui_demo/js/app.js that this PR extends with cross-project refresh.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR also adds cross-project compare and fixture deletion/withdrawal flows, which are unrelated to linked issue #413. Split the compare and deletion workflow changes into separate PRs, or clarify and link the issues covering that additional scope.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is clear and matches the main demo changes around source priority, with cross-project compare as a secondary but present detail.
Linked Issues check ✅ Passed The demo now exposes ordered source priority, preserves ordering, lets uploads choose a source library, and surfaces resolution details as requested in #413.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-413

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

… re-parse

Give the demo the full deletion story for fixtures, using only landed API
surface (#413):

- Library masters (client + company): a Withdraw button soft-deletes via
  DELETE /specs/:id (ADR-030 tombstone) with an Undo toast wired to
  POST /specs/:id/restore. The stale deleteSpec helper ("hard-deletes") is
  renamed to withdrawSpec to match what the server actually does, and the
  never-enabled specDelete flag becomes specWithdraw: true.
- Admin full reset: a Re-parse button on each master explains, then reuses
  the library-targeted picker — POST /libraries/:id/import upserts onto
  (section, source, library), hard-deleting every parsed paragraph and
  reference server-side and rebuilding them with a fresh inference pass
  (content_version bumps, id and clone lineage survive).
- Project copies: removal now walks the admin force path — a 409 is
  classified (new pure module spec-removal.mjs, tested) so an EDITED copy
  offers "Force delete (admin)" (?force=true) while a package-pinned copy
  explains itself and never offers force.
- removeTargetSpecs drops its dead specDelete purge block: the REST
  contract has no hard spec delete by design.

Verified live: withdraw→undo round-trip, re-parse content_version 2→3 with
330 paragraphs rebuilt, force-remove of an edited copy logged force:true.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@thewrz
thewrz marked this pull request as ready for review July 7, 2026 22:59

@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)
examples/web_ui_demo/js/app.js (2)

1501-1552: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Same partial-failure pattern in withdrawSpecFromLibrary.

withdrawSpec and the subsequent UI refreshes (refreshSelectedLibrarySpecs, refreshTocLibrarySpecs, refreshCoordination, etc.) share one try/catch. If withdrawSpec succeeds but a later refresh call throws, the catch reports `withdraw failed: ${err.message}` even though the withdraw itself succeeded server-side — an admin could then retry the withdraw and hit a confusing conflict on an already-withdrawn spec.

🤖 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 `@examples/web_ui_demo/js/app.js` around lines 1501 - 1552,
`withdrawSpecFromLibrary` is treating post-withdraw UI refresh failures as
withdraw failures, which can misreport a successful server-side withdraw as an
error. Split the `withdrawSpec(spec.specId)` call from the subsequent
refresh/render steps in `app.js`, and keep the undo/toast flow tied only to the
actual withdraw result. Use the `withdrawSpecFromLibrary` function and its
`refreshSelectedLibrarySpecs`, `refreshTocLibrarySpecs`, `refreshCoordination`,
and `refreshOpenComments` calls to update the UI, while ensuring only the real
withdraw call is wrapped by the withdraw-specific error handling.

471-498: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Partial-failure messaging in saveProjectSettings.

patchProject and saveSourceDraft are wrapped in one try/catch. If patchProject succeeds but saveSourceDraft (or any later refresh) throws, the user only sees `settings save failed: ${err.message}` even though the name/format change was already persisted — masking a partial success as a total failure.

💡 Proposed fix: isolate the source-draft save so failures are reported distinctly
   try {
     await patchProject(activeProjectId, { name, sectionNumberFormat });
-    await saveSourceDraft();
+    try {
+      await saveSourceDraft();
+    } catch (err) {
+      toast(`settings saved, but source order failed: ${err.message}`, 'warn');
+    }
     await refreshProjectList(activeProjectId);
🤖 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 `@examples/web_ui_demo/js/app.js` around lines 471 - 498, The
`saveProjectSettings` flow currently treats `patchProject`, `saveSourceDraft`,
and all refresh calls as one failure path, so a later exception can make a
successful `patchProject` look like a total save failure. Split the source-draft
save and the subsequent refresh/render work into separate handling inside
`saveProjectSettings`, so the user gets a distinct message when
`saveSourceDraft` (or refreshes like
`refreshProjectList`/`refreshTocClientScope`) fail after the project update has
already succeeded. Use the existing `patchProject`, `saveSourceDraft`, and
`toast` logic to preserve the persisted name/section format change while
reporting post-save failures accurately.
🧹 Nitpick comments (1)
examples/web_ui_demo/js/spec-removal.mjs (1)

10-16: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Message-string classification is fragile but intentionally fail-closed.

classifyRemovalConflict matches on substrings of the server's error message ('force=true', 'package'). If backend wording changes, this silently degrades to 'other' rather than breaking loudly — which per the file's own comment is the intended fail-safe behavior (never force-retry blindly). Consider whether the backend could instead return a structured error code/reason field, which would be less brittle than string matching, though this would require a backend contract change outside this demo-only PR's scope.

🤖 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 `@examples/web_ui_demo/js/spec-removal.mjs` around lines 10 - 16,
`classifyRemovalConflict` in `spec-removal.mjs` uses fragile substring matching
on `err.message`, but the review indicates this is intentionally fail-closed, so
keep the current fallback-to-'other' behavior unchanged. If you do need to
improve it, prefer switching the `409` handling to a structured backend-provided
reason/code field in `classifyRemovalConflict` rather than expanding string
checks, but that contract change is outside this demo-only PR’s scope.
🤖 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 `@examples/web_ui_demo/js/app.js`:
- Around line 2155-2178: The refreshCrossProjectSpecs workflow currently lets
one failed getProject(project.id) call wipe out the entire cross-project catalog
because Promise.all rejects and the catch resets crossProjectSpecs to an empty
array. Update refreshCrossProjectSpecs to tolerate per-project failures by
handling each project independently, using allSettled (or equivalent per-item
error handling) so successful results still populate crossProjectSpecs. Keep the
existing getProject, crossProjectSpecs, and refreshCrossProjectSpecs symbols,
and make sure the catch no longer discards already loaded entries.

---

Outside diff comments:
In `@examples/web_ui_demo/js/app.js`:
- Around line 1501-1552: `withdrawSpecFromLibrary` is treating post-withdraw UI
refresh failures as withdraw failures, which can misreport a successful
server-side withdraw as an error. Split the `withdrawSpec(spec.specId)` call
from the subsequent refresh/render steps in `app.js`, and keep the undo/toast
flow tied only to the actual withdraw result. Use the `withdrawSpecFromLibrary`
function and its `refreshSelectedLibrarySpecs`, `refreshTocLibrarySpecs`,
`refreshCoordination`, and `refreshOpenComments` calls to update the UI, while
ensuring only the real withdraw call is wrapped by the withdraw-specific error
handling.
- Around line 471-498: The `saveProjectSettings` flow currently treats
`patchProject`, `saveSourceDraft`, and all refresh calls as one failure path, so
a later exception can make a successful `patchProject` look like a total save
failure. Split the source-draft save and the subsequent refresh/render work into
separate handling inside `saveProjectSettings`, so the user gets a distinct
message when `saveSourceDraft` (or refreshes like
`refreshProjectList`/`refreshTocClientScope`) fail after the project update has
already succeeded. Use the existing `patchProject`, `saveSourceDraft`, and
`toast` logic to preserve the persisted name/section format change while
reporting post-save failures accurately.

---

Nitpick comments:
In `@examples/web_ui_demo/js/spec-removal.mjs`:
- Around line 10-16: `classifyRemovalConflict` in `spec-removal.mjs` uses
fragile substring matching on `err.message`, but the review indicates this is
intentionally fail-closed, so keep the current fallback-to-'other' behavior
unchanged. If you do need to improve it, prefer switching the `409` handling to
a structured backend-provided reason/code field in `classifyRemovalConflict`
rather than expanding string checks, but that contract change is outside this
demo-only PR’s scope.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bf25c762-9299-4afe-b5e3-09431986a341

📥 Commits

Reviewing files that changed from the base of the PR and between cae7521 and 560e52d.

📒 Files selected for processing (9)
  • examples/web_ui_demo/css/app.css
  • examples/web_ui_demo/index.html
  • examples/web_ui_demo/js/api.js
  • examples/web_ui_demo/js/app.js
  • examples/web_ui_demo/js/features.js
  • examples/web_ui_demo/js/source-order.mjs
  • examples/web_ui_demo/js/spec-removal.mjs
  • examples/web_ui_demo/source-order.test.mjs
  • examples/web_ui_demo/spec-removal.test.mjs

Comment thread examples/web_ui_demo/js/app.js
…lure reporting

Review findings on the #413 demo source-editor / deletion-tier work
(CodeRabbit + Codex second-reviewer pass):

- refreshCrossProjectSpecs: Promise.allSettled so one project's fetch failure
  no longer discards the whole cross-project compare catalog.
- loadActiveProjectWorkspace: clear the Settings source-draft cached before
  project details load, so a stale empty draft can't hide the real chain or
  overwrite it on save.
- refreshTocLibrarySpecs: only surface the company master when it is still in
  the saved source chain, so the TOC never offers specs the project cannot
  resolve after a source is removed.
- withdrawSpecFromLibrary / saveProjectSettings: isolate the persisting call
  from later view refreshes so a refresh failure is not misreported as a failed
  operation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@thewrz

thewrz commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

CodeRabbit body / outside-diff findings — dispositions

Fixed in 936638b:

  • withdrawSpecFromLibrary partial-failure (Major, outside-diff). The withdraw call and the subsequent UI refreshes shared one try/catch, so a post-withdraw refresh failure was reported as withdraw failed even though the soft delete had persisted server-side (inviting a confusing retry). Split so only the actual withdrawSpec failure reports "withdraw failed"; a refresh failure now reports distinctly ("withdrawn, but the view failed to refresh"), and the undo affordance is tied to the real withdraw result.

  • saveProjectSettings partial-failure (Major, outside-diff). patchProject and saveSourceDraft shared one catch, so a source-order failure masked an already-persisted name/format change as a total save failure. saveSourceDraft is now wrapped independently and reports "settings saved, but source order failed" so the persisted project patch isn't misrepresented.

Declined:

  • spec-removal.mjs classifyRemovalConflict substring matching (Trivial nitpick). Keeping as-is. As the review itself notes, the message-substring classification is intentionally fail-closed — anything unrecognized degrades to 'other' (never a blind force-retry), which the file's own header comment documents. The structured error-code alternative is a backend contract change explicitly out of scope for this demo-only PR.

@thewrz

thewrz commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

Codex (GPT-5.5, xhigh) second-reviewer pass — additional eyes

CodeRabbit reviewed this PR normally; Codex ran as a second adversarial reviewer. It surfaced two [P2] findings on the #413 ordered-source-editor, both verified against the code and fixed in 936638b:

  • [P2] Stale empty source-draft can overwrite the chain. On boot, ensureActiveProject()refreshProjectList() renders Settings (via renderProjectSettingscurrentSourceDraft) before getProject() has populated activeProject. currentSourceDraft then caches an empty [] draft keyed to the real activeProjectId ([] is truthy, so the guard reuses it). Once loadActiveProjectWorkspace() loaded the real sources, the draft was never invalidated — the Settings panel showed zero sources / all libraries "available", and adding one + Save could setProjectSources the chain down to only the newly clicked source. Fix: loadActiveProjectWorkspace now clears sourceDraft/sourceDraftProjectId so the panel rebuilds from the freshly loaded chain.

  • [P2] TOC catalog ignored a chain that removed the company master. The new remove-source affordance lets a project save a chain without the company master, but refreshTocLibrarySpecs built its catalog from projectClientLibraryIds plus an unconditional company master — so TOC / Add-from-TOC still offered company specs that addSpecToProject cannot resolve from the saved chain. Fix: the company master is now surfaced only when it is still in activeProject.sources (empty-sources projects keep the legacy include, preserving existing behavior).

Codex reported no other issues. Full transcript verdict retained locally.

@thewrz
thewrz merged commit 15deddf into main Jul 8, 2026
9 checks passed
@thewrz
thewrz deleted the feat/issue-413 branch July 8, 2026 05:08
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.

demo: project source-library priority is invisible and silently rewritten — uploads shadowed by higher-priority sources

1 participant