Skip to content

chore: sync development to main#2774

Merged
maintainerr-automation[bot] merged 11 commits into
mainfrom
development
Apr 26, 2026
Merged

chore: sync development to main#2774
maintainerr-automation[bot] merged 11 commits into
mainfrom
development

Conversation

@maintainerr-automation

@maintainerr-automation maintainerr-automation Bot commented Apr 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Promotes development to main for release. Squash-merge when approved; release automation continues on approval.

Changes

  • Merge branch 'main' into development
  • fix(notifications): dedupe sibling-rule media events within a batch
  • ci(docs-drift): flag user-facing fix commits and default to latest release
  • fix: stop cross-rule contamination for same-titled automatic collections (fix: stop cross-rule contamination for same-titled automatic collections #2766)
  • feat: add ntfy support (feat: add ntfy support #2769)
  • feat(fider): replace cc:@user comment prefix with Discord notifications
  • fix(fider-stale): create the 'stale' tag on first run + share ensureTags
  • ci(fider): add automated triage, invitation, re-eval, and stale workflows
  • ci(release): restore version header and add New Contributors section

maintainerr-automation Bot and others added 10 commits April 24, 2026 22:44
Two gaps since we swapped @semantic-release/release-notes-generator for
the custom AI-backed tool:

- No top-level "# [X.Y.Z](compare-url) (YYYY-MM-DD)" header. Now
  prepended in all emit paths (model, fallback, empty).

- No "New Contributors" section. Now fetched from GitHub's
  POST /releases/generate-notes endpoint and appended if present.
…lows

Five workflows under a unified Fider - * naming scheme that operate the
features.maintainerr.info Fider instance from CI. Tags + comments only —
no auto-closes from the triage path. Maintainers always have the final
call on possibly-* posts; the stale sweep is the one place the bot will
auto-decline (after a 30-day warning + grace window).

## Workflows

  fider-triage.yml              Fider - Triage              cron daily 06:00 UTC
  fider-re-evaluate.yml         Fider - Re-evaluate         cron monthly + manual
  fider-pre-existing-scan.yml   Fider - Pre-existing scan   manual only
  fider-stale.yml               Fider - Stale sweep         cron weekly Sunday
  fider-invite-codeowners.yml   Fider - Invite CODEOWNERS   cron weekly Sunday

## Tags applied by the bot

  triage-checked         every processed post (idempotency gate)
  possibly-completed     a recent merged PR clearly delivers the request
  possibly-duplicate     this post duplicates an earlier open post
  possibly-pre-existing  feature already shipped before the post was filed
                         (lower confidence than possibly-completed)
  stale                  open >1.5 years with <3 votes; warned for review

## Triage pipeline (per post)

1. Skip if already triage-checked, unless FORCE_REEVAL=true
2. Extract keywords from title + first 400 chars of description, drop
   stopwords, keep top 4
3. Search merged PRs one keyword at a time via gh pr list, union and
   dedupe (AND'ing keywords misses real matches)
4. Filter candidates: feat/fix/feature prefix only, mergedAt > createdAt
5. Ask the model (openai/gpt-4.1-mini) with a strict JSON rubric that
   requires a verbatim quote from a candidate PR; verify the quote
   actually appears in what we sent (defence against hallucination)
6. If status=completed + confidence=high: tag possibly-completed and
   upsert a comment citing the evidence URL and verbatim quote (PUT
   to update on re-eval if a better PR is now cited)
7. (CHECK_PRE_EXISTING=true) Re-run the search with mergedAt < createdAt
   and a stricter rubric to surface "user didn't know feature exists"
8. Compare against other open posts (Jaccard >= 0.4 on keywords) for
   duplicate detection; the cited original_number must be in the
   candidate list AND the quote must appear in its title/description

## Stale sweep (two-phase)

Phase 1: post older than 1.5 years with <3 votes and no exempt tag →
         apply 'stale' tag, post a "still relevant?" warn comment
         carrying the same cc:@maintainers prefix as triage comments
Phase 2: 30 days later, if no engagement (any comment newer than the
         warn) → PUT /api/v1/posts/{n}/status to declined with
         explanation
Exempt tags: never-stale, planned, started, bug, enhancement,
possibly-completed

## Invite-CODEOWNERS sweep

Weekly: parse .github/CODEOWNERS, look up each user's public GitHub
email, skip those already Collaborator/Administrator on Fider, send
remaining recipients a single POST /api/v1/invitations/send. Users
with private email visibility are logged and skipped (no automatic
fallback — invite manually if needed).

## Safety guards (triage)

- Per-comment-type hidden HTML markers prevent double-commenting on
  re-runs (completed / duplicate / pre-existing each independent);
  upsertBotComment edits in place if the body changed
- 5-second throttle between model calls (free-tier 15 req/min cap)
- Per-call retry on 429/5xx: 60s/120s/240s backoff, honour Retry-After
  but cap at 5 minutes — anything beyond (e.g. 23-hour daily quota
  reset) becomes a hard failure so the run aborts cleanly instead of
  sleeping inside CI
- Abort run after 3 consecutive model failures (~15s on quota-exhausted
  days, ~21min worst case during real outages) — remaining posts get
  picked up on the next scheduled run
- Rate-limit headers logged via an explicit allowlist; trace IDs and
  infra identifiers are deliberately NOT logged
- CODEOWNERS guard on workflow_dispatch
- models: read permission only; no actions write, repo write, PR write

## Maintainer @mentions on every bot comment

Resolves a "cc:" prefix once per run from GET /api/v1/users (works for
Collaborator OR Administrator per docs.fider.io/api/users) or falls
back to FIDER_MENTION_USERS_FALLBACK env var. The bot's own user is
auto-excluded via FIDER_BOT_USERNAME (no /me endpoint exists in
Fider's public API). If neither source yields anyone, the comment
posts without the cc line.

## Shared helpers

tools/fider-shared.mjs provides createFider, postHasTag,
buildMentionToken, and buildMentionPrefix to all four entry-point
scripts so the API client, role filtering, and cc-prefix logic stay
in one place.

## Bot identity

  GitHub user:  maintainerr-fider-bot (Read collaborator on the repo)
  Fider role:   Collaborator (with one-time Administrator promotion
                to create the five private tags)
  Secret:       FIDER_API_KEY (personal access token)
The first live stale-sweep run failed with 404 on tag application
because the 'stale' tag didn't exist on Fider yet — the script never
called the equivalent of fider-triage's ensureTags(). Both scripts
now use a single shared helper:

- New export ensureTags({ fider, log, dryRun, host, tags }) in
  tools/fider-shared.mjs that idempotently creates a list of tags
  and converts the 403-on-Collaborator failure into the same
  promote-then-demote instructional error message both scripts
  used to inline.
- fider-stale.mjs now calls it for the 'stale' tag (color e74c3c).
- fider-triage.mjs's local ensureTags shrinks from 36 lines to a
  10-line invocation passing its four tag definitions.

Net: -23 lines despite adding tag creation to fider-stale.
The cc: prefix was cosmetic — Fider doesn't send notifications on
@mentions, so maintainers never saw the bot's output until they opened
the post directly. Two visible bugs in the live runs (bot included
itself as @maintainerr-fider-bot, ydkmlt84 listed twice because of
two Fider accounts under the same display name) were also confusing.

Replace it with a Discord webhook integration.

## What changed

- New notifyDiscord helper in fider-shared.mjs. Posts a coloured embed
  to a webhook URL on each meaningful bot action: possibly-completed,
  possibly-pre-existing, possibly-duplicate, stale-warned, and
  stale-declined. Embed contains the post title (clickable), the cited
  PR / quote / vote count, and a colour matching the Fider tag.
- Optional DISCORD_PING_ROLE_ID — when set, the message also includes
  <@&ROLE_ID> in the content field so a Discord role gets pinged
  (embeds alone never trigger Discord notifications). allowed_mentions
  is scoped to that single role so it can never accidentally @everyone.
- All buildMentionPrefix / withMentionPrefix / FIDER_BOT_USERNAME /
  FIDER_MENTION_USERS_FALLBACK code removed from triage and stale
  scripts. Comments now post without a cc line.
- buildMentionPrefix and buildMentionToken removed from
  tools/fider-shared.mjs (they were the only consumers).

## Workflow plumbing

All four runtime workflows (fider-triage, fider-re-evaluate,
fider-pre-existing-scan, fider-stale) pass two new env vars:
  DISCORD_FIDER_BOT_WEBHOOK  ← repo secret
  DISCORD_PING_ROLE_ID       ← repo secret (treated as sensitive too)
Both default to empty: webhook empty = no notifications,
role ID empty = embed-only with no @-ping. The bot's Fider work always
happens; Discord is best-effort and never throws on failure.

## Setup required by maintainers

1. Create a Discord webhook in the target channel
   (Server Settings → Integrations → Webhooks → New Webhook),
   copy the URL, store as repo secret DISCORD_FIDER_BOT_WEBHOOK.
2. (Optional) Create or pick a "maintainers" role in the Discord
   server, copy its numeric snowflake ID (Developer Mode →
   right-click role → Copy ID), store as repo secret
   DISCORD_PING_ROLE_ID. Members of that role get pinged on every
   bot action.
…ons (#2766)

* fix: stop cross-rule contamination for same-titled automatic collections

Two automatic rule groups with the same title end up linked to the same
media server collection because checkAutomaticMediaServerLink relinks by
name. The rule executor's manual-import path then imported the sibling
rule's items as manual into the wrong rule, subjecting them to the
wrong deleteAfterDays. Whichever rule emptied first would also delete
the shared media server collection in removeFromCollection /
checkAutomaticMediaServerLink, taking the sibling rule's items with it.

Skip the manual import for items already rule-owned by a sibling
collection sharing the same mediaServerId, and guard the two empty-
collection deletes against shared links by unlinking locally instead.

Refs #2765

* fix: resync rule-owned items + close sibling-lookup gaps

Adds three corrections on top of the same-title automatic collection
contamination fix:

- Partial drift: checkAutomaticMediaServerLink now diffs server children
  against local rule-owned items for shared automatic collections (not
  just empty ones) and pushes any missing back via addBatchToCollection.
  Fixes the "Plex shows 1 of 5 items, Maintainerr DB shows 5" case where
  the rule executor's local-DB-only delta can't recover server drift.
- Sibling lookup error path: getSiblingRuleOwnedMediaServerIds no longer
  swallows DB errors into an empty Set. The rule executor catches the
  throw and skips manual child import, since a silent empty would let
  sibling-owned children be imported as manual into the wrong rule.
- Cross-type sharing: isMediaServerCollectionShared and
  getSiblingRuleOwnedMediaServerIds now filter siblings by
  manualCollection so a manual collection sharing a mediaServerId can no
  longer be treated as a sibling automatic rule group.
…lease

- Adds a "Behavioral fixes worth reviewing" section that scans fix:
  commits and surfaces the ones touching user-visible surfaces (UI,
  settings, notifications, collections, rule executor, controllers,
  README) so behavior-changing fixes don't slip past the drift report.
  Each entry lists the touched files for cheap triage.
- workflow_dispatch base_ref is now a small choice dropdown (latest /
  origin/main / origin/development) defaulting to 'latest', which the
  resolve step expands to the most recent GitHub release tag at run
  time. No per-version maintenance burden.
When two automatic rule groups share a media server collection (same
title in the same library), each emits its own CollectionMedia_Added /
_Removed for the same item, so the user sees N identical notifications
for what is one user-visible change. Dedupe these within a single
rule-executor batch (one processQueue() pass), keyed on
(event, collectionName, mediaServerId).

Notifications across separate batches (e.g. scheduled runs at different
times) are never collapsed — those are genuinely distinct events.
Manual / user-triggered notifications are unaffected because dedupe
lives in the @onevent handlers, not in handleNotification itself.

Reuses the existing RuleHandlerQueue_StatusUpdated event for batch
lifecycle so no new contracts surface is added. Adds a defensive
emitStatusUpdate() to processQueue's outer finally so listeners can
observe the processingQueue=true→false transition reliably (previously
no event fired on drain). Verified safe under stop/abort: the same
finally path runs and clears dedupe state.
@maintainerr-automation maintainerr-automation Bot added the release:docker-build Build release candidate Docker image label Apr 26, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved by release automation (CODEOWNER approval was verified by Release 2).

@maintainerr-automation

Copy link
Copy Markdown
Contributor Author

‼️ Release 2.5 stopped: PR #2774 is behind main (mergeable_state=behind). Update the branch and re-approve.

@github-actions

Copy link
Copy Markdown
Contributor

‼️ Release 2.5 - Execute Push PR To Main failed during squash-merge after approval.

  • Merge result: failure

See the earlier blocker comment on this PR for specifics.

@enoch85 enoch85 self-requested a review April 26, 2026 10:59

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved by release automation (CODEOWNER approval was verified by Release 2).

@maintainerr-automation maintainerr-automation Bot merged commit 934f5d0 into main Apr 26, 2026
24 checks passed
@maintainerr-automation

Copy link
Copy Markdown
Contributor Author

📚 Docs drift report

Comparing origin/mainHEAD against Maintainerr_docs. Informational only — maintainers decide what needs doc updates before release.

Rule glossary parity

  • Code rule keys (rules.constants.ts): 160
  • Documented keys (docs/Glossary.md): 160

Glossary is in sync with the code.

New migrations on this branch

No new migrations.

Rule constants

No changes to rules.constants.ts.

Public contracts (@maintainerr/contracts)

No contract changes.

New HTTP controllers

No new controllers.

feat: commits on this branch

Behavioral fixes worth reviewing

  • 9d723cf fix(notifications): dedupe sibling-rule media events within a batch
    • touched: apps/server/src/modules/notifications/notifications.service.spec.ts, apps/server/src/modules/notifications/notifications.service.ts, apps/server/src/modules/rules/tasks/rule-executor-job-manager.service.spec.ts, apps/server/src/modules/rules/tasks/rule-executor-job-manager.service.ts
  • 98b96e8 fix: stop cross-rule contamination for same-titled automatic collections (fix: stop cross-rule contamination for same-titled automatic collections #2766)
    • touched: apps/server/src/modules/collections/collections.service.spec.ts, apps/server/src/modules/collections/collections.service.ts, apps/server/src/modules/rules/tasks/rule-executor.service.spec.ts, apps/server/src/modules/rules/tasks/rule-executor.service.ts

fix: commits that touched user-facing surfaces (UI, settings, notifications, collections, rule executor, controllers, README). Worth scanning to decide whether observable behavior changed enough to warrant a docs note.

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Release 2.5 - Execute Push PR To Main completed after approval.

  • PR squash-merged into main
  • Sync back: success
  • Build Main: success

@enoch85 enoch85 added this to the 3.9.0 milestone Apr 28, 2026 — with GitHub Codespaces
@maintainerr-automation

Copy link
Copy Markdown
Contributor Author

🎉 This PR is included in version 3.9.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release:docker-build Build release candidate Docker image released

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants