Ad naming
Atlas's naming was, in Alex's words, "anyhow". Two real names from one account show why that cost something concrete:
AM - ASC - Tailored Tee IMG GRP 3 AM = Atlas Media → a producer
ND - ASC - STATICV1 ND = National Day → a season
Both occupy the first slot and they are not the same kind of thing. That single ambiguity is why the question "did our creative beat the client's?" could not be answered — the marker meant producer sometimes and theme other times. And every ND ad sits inside a campaign already called *National Day Campaign*, so the prefix spends the most valuable characters in the name repeating something the platform already groups by.
Each level says what only it can say
| Level | Carries | Example |
|---|---|---|
| Campaign | objective, season, promotion | National Day Campaign |
| Ad set | targeting | Mid-Target |
| Ad | the creative itself | ATL-A7K2-VID-v1 |
Nothing is repeated downward. A season in an ad name is a wasted field: the campaign already carries it and the join is free.
The ad name
TES_the-loan-doesnt-pause_TOF-fear_confront_v1_2608_ATL_M47Q
│ │ │ │ │ │ │ └── angle code
│ │ │ │ │ │ └────── producer
│ │ │ │ │ └─────────── launch month (YYMM)
│ │ │ │ └────────────── iteration
│ │ │ └─────────────────────── hook type
│ │ └──────────────────────────────── stage + mechanism — the goal
│ └────────────────────────────────────────────────────── the argument, in words
└────────────────────────────────────────────────────────── format
The name is an analytics substrate, not a label. Alex's framing, which is the one that decides the design: *"so that when you parse you can know what creative strategy to go forward with without having to view the creative and analyse — you can just see the name and know, oh, testimonials did well."*
So it carries every axis the creative strategy actually varies on. Length is close to free because the portal generates the name and a person copies it — nobody composes one, so an extra field costs a wider column rather than a harder job.
Format leads so that sorting by name in Ads Manager groups by format, which makes "testimonials did well" visible without a query. The angle sits immediately after, so both survive a truncated column.
| Field | Values | Why it is here |
|---|---|---|
| format | STA VID CAR REE UGC TES LNG | Testimonial and long-form static are their own formats, not flavours of video and image — they persuade differently |
| angle | free slug, capped at 28 chars on a word boundary | The argument, readable. Articles are kept: a slugifier that silently edits meaning is worse than a verbose one |
| stage-mechanism | TOF MOF BOF × the mechanism taxonomy | The goal. Same vocabulary as the gap report and the generator, deliberately — a parallel vocabulary is how results stop grouping against the decisions that produced them |
| hook | from the library in skills/script-craft | h2 records that a hook changed; this records what to |
| iteration | v2 new execution · h2 new hook only | Without the split, replication / iteration / net-new cannot be measured |
| launched | YYMM | The only field recording how old an asset is. Every other date says when an ad *ran*, and fatigue is a function of age |
| producer | ATL CLI EXT | Answers whether our work earns its fee |
| code | 4 chars, alphabet excludes I O 0 1 | Resolves to persona, offer, full argument. Typed by hand, and a misread code attributes to the wrong idea |
Checked against industry practice
The shape agrees with what is broadly recommended: underscores as delimiters, no spaces, version numbers, each level progressively more specific, and a written guide everyone follows. Two fields were added *because* of that check — hook type and launch month are what teams add once they test at volume, and both answer something nothing else in the system can.
Three commonly-recommended fields are deliberately absent:
- Platform. Already a column on the result, and repeating it downward is exactly the error
NDmade. - Aspect ratio. Real for placement analysis, not yet a decision Atlas varies on.
- Language and market. Singapore, countrywide. The same guidance warns against cramming the string, and these would be constants.
What the name does not carry, and why
Persona and offer are sentences, not slugs. Including them would double the length to duplicate what the database already holds against the code. The name summarises; the code resolves.
Legacy names are never renamed
Decided, not deferred. Renaming a live ad breaks the join between the old name and the results already recorded against it, and buys nothing — the angle is the one field a rename cannot supply anyway, since no name implies which argument an ad was making.
So the estate is permanently mixed, and that has a consequence worth stating: a share of spend can never be attributed to an angle, and it is not a gap. lineageCoverage therefore measures attributed over attributable rather than over total, and reports legacy spend separately as closed. Measuring against total would produce a number that can never reach 100%, and a metric that cannot be satisfied is one people stop reading.
Three categories, and they need different responses:
| Meaning | Response | |
|---|---|---|
| Attributed | Named under the convention, code resolved | Nothing — working |
| Attributable, unlinked | Named correctly, code points at no angle we hold | Chase it; something is wrong |
| Legacy | Predates the convention | Closed. Leave it |
Where the convention is absent the old prefix inference still runs, so a legacy name keeps whatever signal it has rather than losing it.