You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Rescoped 2026-08-22. Both of this ticket's deliverables have moved on. The real credential list landed as PR #101 — the 24 confirmed entries live in supabase/seed/credential-catalogue.json and load through supabase/seed.sql, kept honest by tests/db/credential-catalogue-seed.test.ts. The local fixtures half is split out to #110, which is afk and unblocked. What remains here, and what keeps this hitl, is the question the ticket always had underneath it: where the catalogue is permanently housed. The original text is kept below because its reasoning is still the reasoning; read it knowing the first two paragraphs of Two deliverables are now history rather than work.
Two details in that text are stale. credential_catalogue no longer has a source column — #103 split it into kind (certification | course) and platform, and the unique key is (kind, platform, label) across all three. And the blocker line names #52, which was closed as out of scope on 2026-08-21.
The catalogue table lands empty in #89. This is the ticket that puts real rows in it, plus the local development fixtures that were deferred alongside.
Do not start this until the data schema is final — #89 → #50 → #90 → #52. That is the whole reason it is a separate ticket: seed data is written against a shape, and rewriting it once per schema ticket teaches nobody anything on the way. See the decision comments on #89.
Why the table is empty in the first place
#89's text says the credential seed comes from a comment on it, and "if no comment carries one, stop and ask". Nobody ever wrote one, and there is no in-repo fallback: src/lib/practitioners.ts:281 is an empty credentialCatalogue. The only list anywhere is src/app/prototype/catalogue.ts, and its own header forbids exactly this use:
The contents below are invented and the real list has not been compiled. That is its own scope item… Read these as plausible placeholders for names Anthropic owns, and do not copy any of them into a migration.
Twenty-four entries, sized to make the spec's "2 of 23" worked example render. Not a candidate for anything but local fixtures.
Two deliverables, and they are not the same kind of thing
1. The real credential_catalogue contents — permanent, and Bluehex's to state
One row per Anthropic Academy course and per Claude Certification, per docs/spec/profile-and-credentials.md:106. The columns are source (one of exactly two values, Claude Certification or Anthropic Academy), label, sort_order and active, with unique (source, label) and no slug — the id is the reference.
sort_order matters and is not cosmetic: the Academy track has an order somebody works through, and alphabetical would scramble it.
This is the HITL part, and it is why the label is on this ticket. The contents are Anthropic's and Bluehex's to state. An agent must not compile them from a marketing page, from the prototype, or from its own training data — a wrong label here is a wrong credential name sitting behind the Verified badge, which is the only thing this directory sells. If the list is not on this ticket when you pick it up, stop and ask.
Related: Claude credentials are issued through Skilljar, and the tenant-scoped API belongs to Anthropic — so there is no integration to build here. This is a list somebody writes down once.
2. supabase/seed.sql — throwaway, local, and currently empty
Runs at the end of pnpm db:reset and never against the hosted project. Its header already states the rule:
Seed data is fake data, and this is the one place that is fine — it never leaves a developer's machine. The "real people only" rule in AGENTS.md is about the directory the public sees, not about local fixtures.
src/app/prototype/catalogue.tsis a fair source here — the one use its header does not forbid. Make it re-runnable (on conflict do nothing, per the file's second note).
Note this is fixtures for a developer, not for the tests. tests/db inserts what it needs as bluehex_admin, which proves the admin-only grant and supplies the fixture in one statement; a test passing against seeded data is the weaker assertion. Do not rewrite the tests to lean on this file.
The open question this ticket has to answer first
How does the real list actually arrive?#89 split the seed out, which leaves the mechanism undecided, and the two candidates are not equivalent:
A seed migration. Matches the spec's original reasoning — these rows are "the closed vocabulary the model is built on", not fixtures, and permanent reference data is a legitimate thing for migration history to hold. Keeps local and hosted identical by construction.
An admin INSERT against the hosted project. Matches the spec's correction path (docs/spec:744 — a renamed course is an admin operation, "or the history fills with Anthropic's release notes"), but the hosted project and a fresh pnpm db:reset then disagree about what exists, and nothing in version control says what the catalogue contains.
Recommendation: a seed migration, on the grounds that seeding and correcting are different acts and only the second one was ever argued to belong outside migration history. Settle it explicitly here rather than by whichever gets written first, and record the answer in the spec — it currently says both catalogues seed in their creating migration, which #89 already made untrue.
credential_catalogue holds the real entries after pnpm db:reset, with sort_order reflecting the real track order.
supabase/seed.sql is no longer empty and pnpm db:reset runs it clean twice in a row.
No invented credential label exists anywhere outside src/app/prototype/ and supabase/seed.sql.
pnpm test:db still passes, with no test rewritten to depend on seeded rows.
Blocked by: nothing — #89, #50, #90 and #103 are all merged and the schema is final. Split: #110 carries the local fixtures. Related: #78 · design: docs/spec/profile-and-credentials.md
What is actually left
The mechanism question, unchanged and still undecided: a seed migration, or an admin INSERT against the hosted project. The recommendation below still stands — a seed migration, on the grounds that seeding and correcting are different acts and only correcting was ever argued to belong outside migration history.
What has changed is that the cost of not deciding is now visible. supabase/seed.sql runs on a developer's machine and on the Schema runner and never against the hosted project, so the hosted credential_catalogue is empty and will stay empty until this is settled — while every local stack has 24 rows. The first practitioner who tries to claim a credential in production hits a catalogue with nothing in it.
That is not urgent, because there is no way to claim one until #14. It is the thing to settle before #14 rather than during it.
Done when: the mechanism is decided, written into docs/spec/profile-and-credentials.md in place of the seeding rule at lines 738/744 that #89 amended, and the hosted project holds the real entries by whichever route was chosen.
The catalogue table lands empty in #89. This is the ticket that puts real rows in it, plus the local development fixtures that were deferred alongside.
Do not start this until the data schema is final —
#89 → #50 → #90 → #52. That is the whole reason it is a separate ticket: seed data is written against a shape, and rewriting it once per schema ticket teaches nobody anything on the way. See the decision comments on #89.Why the table is empty in the first place
#89's text says the credential seed comes from a comment on it, and "if no comment carries one, stop and ask". Nobody ever wrote one, and there is no in-repo fallback:
src/lib/practitioners.ts:281is an emptycredentialCatalogue. The only list anywhere issrc/app/prototype/catalogue.ts, and its own header forbids exactly this use:Twenty-four entries, sized to make the spec's "2 of 23" worked example render. Not a candidate for anything but local fixtures.
Two deliverables, and they are not the same kind of thing
1. The real
credential_cataloguecontents — permanent, and Bluehex's to stateOne row per Anthropic Academy course and per Claude Certification, per
docs/spec/profile-and-credentials.md:106. The columns aresource(one of exactly two values,Claude CertificationorAnthropic Academy),label,sort_orderandactive, withunique (source, label)and no slug — theidis the reference.sort_ordermatters and is not cosmetic: the Academy track has an order somebody works through, and alphabetical would scramble it.This is the HITL part, and it is why the label is on this ticket. The contents are Anthropic's and Bluehex's to state. An agent must not compile them from a marketing page, from the prototype, or from its own training data — a wrong
labelhere is a wrong credential name sitting behind the Verified badge, which is the only thing this directory sells. If the list is not on this ticket when you pick it up, stop and ask.Related: Claude credentials are issued through Skilljar, and the tenant-scoped API belongs to Anthropic — so there is no integration to build here. This is a list somebody writes down once.
2.
supabase/seed.sql— throwaway, local, and currently emptyRuns at the end of
pnpm db:resetand never against the hosted project. Its header already states the rule:src/app/prototype/catalogue.tsis a fair source here — the one use its header does not forbid. Make it re-runnable (on conflict do nothing, per the file's second note).Note this is fixtures for a developer, not for the tests.
tests/dbinserts what it needs asbluehex_admin, which proves the admin-only grant and supplies the fixture in one statement; a test passing against seeded data is the weaker assertion. Do not rewrite the tests to lean on this file.The open question this ticket has to answer first
How does the real list actually arrive? #89 split the seed out, which leaves the mechanism undecided, and the two candidates are not equivalent:
INSERTagainst the hosted project. Matches the spec's correction path (docs/spec:744— a renamed course is an admin operation, "or the history fills with Anthropic's release notes"), but the hosted project and a freshpnpm db:resetthen disagree about what exists, and nothing in version control says what the catalogue contains.Recommendation: a seed migration, on the grounds that seeding and correcting are different acts and only the second one was ever argued to belong outside migration history. Settle it explicitly here rather than by whichever gets written first, and record the answer in the spec — it currently says both catalogues seed in their creating migration, which #89 already made untrue.
Done when
docs/spec/profile-and-credentials.md, replacing the seeding rule at lines 738/744 that The two Bluehex-owned catalogues: credential_catalogue and service_catalogue #89 amended.credential_catalogueholds the real entries afterpnpm db:reset, withsort_orderreflecting the real track order.supabase/seed.sqlis no longer empty andpnpm db:resetruns it clean twice in a row.src/app/prototype/andsupabase/seed.sql.pnpm test:dbstill passes, with no test rewritten to depend on seeded rows.Blocked by: nothing — #89, #50, #90 and #103 are all merged and the schema is final. Split: #110 carries the local fixtures. Related: #78 · design:
docs/spec/profile-and-credentials.mdWhat is actually left
The mechanism question, unchanged and still undecided: a seed migration, or an admin
INSERTagainst the hosted project. The recommendation below still stands — a seed migration, on the grounds that seeding and correcting are different acts and only correcting was ever argued to belong outside migration history.What has changed is that the cost of not deciding is now visible.
supabase/seed.sqlruns on a developer's machine and on theSchemarunner and never against the hosted project, so the hostedcredential_catalogueis empty and will stay empty until this is settled — while every local stack has 24 rows. The first practitioner who tries to claim a credential in production hits a catalogue with nothing in it.That is not urgent, because there is no way to claim one until #14. It is the thing to settle before #14 rather than during it.
Done when: the mechanism is decided, written into
docs/spec/profile-and-credentials.mdin place of the seeding rule at lines 738/744 that #89 amended, and the hosted project holds the real entries by whichever route was chosen.