feat: the extension adds Discord channels to an artist you pick, and its tests gate the XPI (milestone 429)
CI and images / lint (push) Successful in 2s
CI and images / extension-version (push) Successful in 2s
CI and images / extension-test (push) Successful in 20s
CI and images / frontend-build (push) Successful in 24s
CI and images / backend-lint-and-test (push) Successful in 32s
CI and images / integration (push) Successful in 2m22s
CI and images / build-agent (push) Successful in 5s
CI and images / sign-extension (push) Successful in 3m13s
CI and images / build-web (push) Successful in 1m42s
CI and images / smoke-web (push) Successful in 54s
CI and images / promote (push) Successful in 1s

Server (#4420)
- extension_service gains a Discord pattern (server or channel, jump links,
  ptb/canary; not DMs or threads), mirrored in platforms.js and pinned by
  the shared artist-url-samples.json.
- probe on a Discord URL matches the source by ids under any artist, reports
  a whole-server source as covering the channel, suggests the artist who owns
  another source on the same server, and names server/channel via the stored
  token (best-effort, bounded, no rate-limit waits).
- quick-add takes artist_id / artist_name; Discord URLs are stored canonical.

Extension (#4421, #4422)
- Content script on discord.com; SPA navigation by URL polling (the old
  pushState patch ran in the isolated world and never fired); stale probes
  are dropped.
- Discord chip opens an Add panel: this channel or the whole server, and the
  suggested artist / a search / a new name.
- Popup: sources show artist, platform and state; a Discord token export is
  verified by FC and the result shown. Token capture covers ptb/canary.
- Pure logic in lib/chip.js and lib/popup-format.js, with specs.

CI (#4423)
- extension.yml's lane (web-ext lint, vitest, XPI contents) moves into
  build.yml as extension-test and joins the needs of sign-extension,
  build-web and build-agent. As a separate workflow it gated nothing: a red
  extension suite still signed and shipped the XPI (rule 177).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVjrnpQjRgHdvq95rASoiR
This commit is contained in:
2026-09-24 23:31:38 -04:00
co-authored by Claude Opus 5.5
parent 97045279a3
commit 4c75dd0f88
23 changed files with 1176 additions and 209 deletions
+19 -2
View File
@@ -80,6 +80,17 @@ class FabledCuratorAPI {
getCredentials() {
return this.request('GET', '/credentials');
}
// Test the STORED credential against one of the platform's sources — the
// same check the web UI's Verify button runs. {valid: true|false|null, reason}.
verifyCredential(platform) {
return this.request('POST', `/credentials/${encodeURIComponent(platform)}/verify`);
}
// Artist search for the Discord Add panel — the web UI's autocomplete.
searchArtists(q, limit = 8) {
const qs = new URLSearchParams({ q, limit: String(limit) }).toString();
return this.request('GET', `/artists/autocomplete?${qs}`);
}
// FC-3a — sources.
listSources() {
@@ -90,8 +101,14 @@ class FabledCuratorAPI {
}
// FC-3g — extension-specific.
quickAddSource(url) {
return this.request('POST', '/extension/quick-add-source', { url });
// artistId connects the source to an existing artist, artistName to the
// artist of that name (created if new); with neither the server derives the
// artist from the URL. A Discord channel always sends one.
quickAddSource(url, { artistId = null, artistName = null } = {}) {
const body = { url };
if (artistId != null) body.artist_id = artistId;
else if (artistName) body.artist_name = artistName;
return this.request('POST', '/extension/quick-add-source', body);
}
probeSource(url) {
// Read-only existence check. Drives the content-script chip's