Feature 4 — Deep Dive

Admin Vertical Tagging

How internal tagging of buyer projects feeds UpStage’s AI category match-scoring engine — and connects talent to the right projects.

1. Vertical Tagging — The Foundation

Every buyer project on UpStage belongs to a content vertical. The platform’s matching engine rewards projects that land in the bucket most relevant to the talent’s declared experience — and the operator is the one who locks that bucket in. Vertical tagging is the operator-side queue at /admin/tags, backed by a Postgres project_vertical enum.

Without vertical tagging, matching is purely semantic: profile embeddings vs project embeddings. Tagging adds an explicit, human-curated axis on top of that — one row, one enum value, one operator decision per project.

2. The Four Vertical Buckets

The project_vertical enum is a single-valued, “primary-axis” column on buyer_projects. Legacy rows default to short-film; new projects default the same way unless the creator explicitly picks another value via POST /projects/new.

Vertical • default
Short-Film
Indie Film — shorts, festival bound, narrative statement pieces.
short-film
Vertical • branded
Commercial
Branded Content — paid ads, brand spots, agency work.
commercial
Vertical • live
Theater
Stage — live performance, run-of-show, on-stage credits.
theater
Vertical • emerging
New-Media
Vertical Drama — episodic streaming-first, mobile-first formats.
new-media

Each value maps to a market category the talent operator cares about — and each is its own “axis of relevance” in the matching engine. Pick one per project.

3. Tagging → Scoring → Matching

The end-to-end workflow that takes a project from “just posted” to “ranked on a talent card” flows through three stages:

Stage 1
Admin Tags
Operator hits /admin/tags, chooses a vertical, hits Save.
Stage 2
Vertical Write
Row updated via setProjectVertical(id, vertical) in db/buyer-projects.js.
Stage 3
Match Score
Engine adds a 5% verticalBoost inside the 40% skills weight at services/matching.js:69–86.

Admin hits /admin/tags → selects the vertical enum → on POST /admin/tags/vertical/:id the row gets vertical written via setProjectVertical → matching engine re-reads on the next cron cycle, the verticals multi-select array cross-references talent project_types, and the score surfaces as the category sub-score on the talent card.

4. What the Admin Sees

The tagger UI at /admin/tags is a deliberately thin admin surface — it lists every buyer_projects row and exposes a dropdown + Save pair per row, backed by the project_vertical enum.

upstageplatform.com/admin/tags
Project #1284 current: short-film sarah.k · posted Jun 28
vertical: Save
Project #1291 current: new-media mateo.r · posted Jun 30
vertical: Save
Project #1303 current: short-film nina.w · posted Jul 03
vertical: Save

The dropdown submits via POST /admin/tags/vertical/:id — gated by requireAdminSecret at routes/admin.js:1663 so only the operator (with the secret in the URL or the Authorization header) can alter a project’s vertical.

5. Buyer Brief Tags vs. Talent Profile Tags

UpStage carries two parallel “tag” surfaces on the brief and on the talent profile. Both feed the same category match logic but the operator only ever touches the brief-side one.

Buyer sets
Buyer brief tags
Single, enum-valued vertical on buyer_projects (Postgres enum, default short-film), plus a multi-select verticals TEXT[] array the buyer can fill at POST /projects/new. Talent matching reads verticals for the explicit overlap cue.
Talent sets
Talent profile tags
Multi-select project_types TEXT[] array on seller_profiles the talent fills when editing their profile. The match boost fires when at least one of the project’s verticals labels overlaps a talent project_types label (case-insensitive).

The two arrays meet inside computeMatchScore() at services/matching.js:69–86 — and the overlap yields the 5% explicit verticalBoost that sharpens the existing 40% skills weight without changing its headroom.

6. Closing the Loop — Cron & Category Score

A vertical tag doesn’t take effect the moment it’s saved — it takes effect when the AI match engine re-reads the row. The daily ai-talent-match-check cron (referenced in the CLAUDE.md changelog for 2026-08-12) re-runs the matching every cron run, while the 3-day cadence ensures notifications fire on the match_run_counter % 3 == 0 rhythm (corrected in the 2026-07-15 fix).

In practice: an operator hits Save on the tagger screen, the next cron cycle re-evaluates the project against every talent profile, and the affected talents see the new category score on their match card — no manual re-scoring required, no separate “re-tag” pipeline to maintain.