Skip to content

Commit 0a848de

Browse files
TMHSDigitalclaude
andcommitted
ai-assets: a dated Steam quote, what an ai-generated entry obliges, and a complete list
docs/ai-assets.md said only "check current Valve guidelines". It now quotes the Steamworks Content Survey's generative-AI section (read 2026-11-03), says what shipping an ai-generated entry means for that survey, and lists every entry tagged ai-generated, with a link to the live search. Soundza gains the tag: its Notes say some library sounds are AI-generated. V37 keeps this true: an entry whose body says content is or are AI-generated must carry the tag, and the guide must link every tagged entry. Closes #180 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LpkeXRUqm7oDedPDk3QWtK
1 parent ee25fe1 commit 0a848de

6 files changed

Lines changed: 86 additions & 3 deletions

File tree

‎CONTRIBUTING.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ To add a starter stack (one pick per need for a kind of game), follow [`stacks/R
5454
- `subcategories`: lower-case kebab-case (`base-meshes`, `field-recordings`). Reuse a value already in the catalog before inventing one, and do not add a variant of an existing value that differs only in plural or spelling. Where both forms were in use, the more common one was kept (`characters`, `environment`, `interior`, `tileset`, `vectors`, `pixel`, `impulse-responses`, `base-meshes`). Retired spellings are listed in [`site/value-aliases.json`](site/value-aliases.json), which the validator enforces (V19); add a line there when you merge two values.
5555
- `formats`: what you actually get, in one of four kinds. **File formats**, as commonly written: usually upper case (`PNG`, `FBX`, `JSON`, `VOX`), tool-specific ones as the tool writes them (`gdshader`, `tmx`, `ktx2`). **Engine or language targets**, as the product writes them (`glTF`, `Godot`, `Unity`, `Python`, `React`). **Delivery types** for software with no file format of its own, lower-case kebab-case (`godot-addon`, `blender-extension`, `npm`, `cli`, `library`, `desktop-app`, `mobile-app`, `middleware`, `model`). And `various` for an aggregator whose files come in too many formats to list. Platforms (`windows`, `macos`, `ios`) and descriptions (`heightfield`, `examples`) are `tags`, not formats. The allowed values are the closed list in [`site/format-vocabulary.json`](site/format-vocabulary.json), which the validator enforces (V20): reuse a listed spelling, and add a genuinely new format there in the same change that first uses it. Its `groups` drive the site's Format filter, so a new format that belongs to a group (another glTF or font container) goes into that group too. A format outside every group is still searchable and shown on the card, but the Format filter cannot select it.
5656
- `tags`: lower-case kebab-case words describing the content (`low-poly`, `modular`, `platformer`). Do not restate another field: the licence (`cc0`, `mit`, `ofl`, `open-source`, `royalty-free`), the credit requirement (`no-attribution`) or the entry's own `publisher` (`kenney`) are rejected (V19 via `site/value-aliases.json`, V21), because search and the filters read those fields directly and a copied tag drifts. A `varies` entry lists its licence mix in the body, not in tags.
57+
- `ai-generated` tag: required when the source says any of its content is AI-generated (V37; the body sentence "some sounds are AI-generated" without the tag fails). Add the entry to the list in [`docs/ai-assets.md`](docs/ai-assets.md) in the same change; the validator checks that list too.
5758
- Frontmatter is one `key: value` per line; lists are `[a, b]` or indented `- item` lines. A key given twice is an error. Entry files may not contain emoji.
5859
- `grid_dimensions` / `camera_perspective` / `hardware_tags`: optional metadata for 2D and UI entries. `3d` and `characters` entries leave `camera_perspective` out: it describes a 2D camera, and those entries have none.
5960
- `maintenance`: optional, `archived` | `inactive` (V22). Set `archived` when the source repository is archived, and `inactive` when it has had no push for over three years, with a dated `- Maintenance:` line in Notes saying what you read. Leave it out otherwise. The weekly link check reports a GitHub source whose state no longer matches.

‎catalog/audio/soundza.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ license: custom
88
commercial: true
99
attribution_required: false
1010
formats: [WAV, MP3]
11-
tags: [loops, one-shots, api]
11+
tags: [loops, one-shots, api, ai-generated]
1212
added: 2026-10-26
1313
verified: 2026-10-26
1414
status: needs-review

‎docs/ai-assets.md‎

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# AI-generated assets — catalog policy
22

3-
Last reviewed 2026-10-23. Court rulings and platform rules on AI content change quickly: **re-verify** dates and rulings before relying on them.
3+
Last reviewed 2026-11-03. Court rulings and platform rules on AI content change quickly: **re-verify** dates and rulings before relying on them.
44

55
## Practical catalog rules
66

@@ -12,14 +12,45 @@ Last reviewed 2026-10-23. Court rulings and platform rules on AI content change
1212

1313
## Platform notes (verify live)
1414

15-
- **Steam**: research notes player-facing generative assets may require store disclosure; internal coding assistants may not. Check current Valve guidelines.
15+
- **Steam**: the Content Survey you fill in before review has a section on generative AI.
16+
The live [Content Survey page](https://partner.steamgames.com/doc/gettingstarted/contentsurvey)
17+
(read 2026-11-03) says it "is concerned with the use of AI in creating content that ships
18+
with your game, and is consumed by players. This includes content such as artwork, sound,
19+
narrative, localization, etc.", and that AI tools used only for development efficiency
20+
are "not the focus of this section". It splits AI content into "Pre-Generated: Any kind of
21+
content that ships with your game and is consumed by players that is created with the
22+
help of AI tools during development" and "Live-Generated", content made while the game
23+
runs, for which "you'll need to tell us what kind of guardrails you're putting on your
24+
AI to ensure it's not generating illegal content."
1625
- **US copyright**: research cites human-authorship requirements for registration; pure machine output may be unprotected — that does **not** clear third-party training-data or likeness claims.
1726
- **Voice/likeness**: generating a real performer’s voice without rights is high risk regardless of model license.
1827

28+
## What an `ai-generated` entry obliges you to disclose
29+
30+
An entry tagged `ai-generated` holds content that its source says was made with AI tools,
31+
in whole or in part. If you ship any of it, it is content "created with the help of AI
32+
tools" that players consume, so on Steam it belongs in the Pre-Generated part of the
33+
Content Survey, whoever ran the generator. Describe which assets and where they came
34+
from. The same holds for a library that mixes human and AI files, such as Soundza, if a
35+
file you use is labelled AI on its page. Other stores have their own rules; read them
36+
before you submit.
37+
1938
## Mixed catalogs (human CC0 vs generator)
2039

2140
[3DTexel](../catalog/shaders-vfx/3dtexel-decals.md) ships a **human-authored CC0 decal library** on the same site as paid premium files and AI generators. The catalog entry is the human CC0 set only. Footer (2026-08-29): free assets CC0; premium and AI assets use a separate 3DTexel License. Do not treat generator output as that CC0 grant.
2241

42+
## Entries tagged ai-generated
43+
44+
These entries carry the `ai-generated` tag; the validator keeps this list complete (V37).
45+
The [live search](https://tmhsdigital.github.io/Free-Game-Dev-Assets/?q=ai-generated) shows
46+
the same set.
47+
48+
- [BitNuker Fantasy Environments Sampler](../catalog/2d/bitnuker-fantasy-environments-sampler.md)
49+
- [BitNuker Workshop Sampler](../catalog/2d/bitnuker-workshop-sampler.md)
50+
- [BudgetPixel Sound Effects](../catalog/audio/budgetpixel-sfx.md)
51+
- [SFXMint](../catalog/audio/sfxmint.md)
52+
- [Soundza](../catalog/audio/soundza.md)
53+
2354
## Related entries
2455

2556
- [`catalog/shaders-vfx/3dtexel-decals.md`](../catalog/shaders-vfx/3dtexel-decals.md)

‎site/checks.mjs‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,36 @@ export function checkPreviewVocabulary(vocab, allow = PREVIEW_LICENSES, credits
360360
return errors;
361361
}
362362

363+
/* ----------------------------------------------------------------- V37 */
364+
/**
365+
* AI-generated content stays labelled. An entry whose body says some of its
366+
* content "is" or "are" AI-generated carries the `ai-generated` tag, and
367+
* docs/ai-assets.md links every tagged entry, since Steam's Content Survey
368+
* asks a developer to disclose AI content they ship. "No generative AI" and
369+
* "not AI-generated" do not count.
370+
*/
371+
const AI_CLAIM_RE = /\b(?:is|are)\s+(?:partly\s+|partially\s+|mostly\s+)?AI[- ]generated\b/i;
372+
373+
export function checkAiTag(rel, meta, body) {
374+
const tags = Array.isArray(meta.tags) ? meta.tags.map(String) : [];
375+
if (tags.includes("ai-generated")) return [];
376+
for (const sentence of String(body).split(/(?<=[.!?])\s+|\n/)) {
377+
if (AI_CLAIM_RE.test(sentence) && !/\b(?:not|no|never)\b/i.test(sentence)) {
378+
return [`${rel} says content is AI-generated ("${sentence.trim().slice(0, 80)}") but has no ai-generated tag`];
379+
}
380+
}
381+
return [];
382+
}
383+
384+
/** docs/ai-assets.md links every entry tagged ai-generated (repo paths relative to docs/). */
385+
export function checkAiGuide(guideText, taggedRels) {
386+
const errors = [];
387+
for (const rel of taggedRels) {
388+
if (!String(guideText).includes(`(../${rel})`)) errors.push(`docs/ai-assets.md does not link ${rel}, which is tagged ai-generated`);
389+
}
390+
return errors;
391+
}
392+
363393
/* ----------------------------------------------------------------- V36 */
364394
/**
365395
* `limits`: a short line for a restriction the licence fields cannot carry,

‎site/checks.test.mjs‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ import {
3838
checkTitleMatchesName,
3939
checkValueAliases,
4040
checkValueSpellings,
41+
checkAiGuide,
42+
checkAiTag,
4143
checkCreditPlaceholders,
4244
checkLimits,
4345
checkKnownKeys,
@@ -901,6 +903,15 @@ rejects("V34 rejects an allowlisted licence missing from the vocabulary", checkP
901903
rejects("V34 rejects a required licence the caption would not credit", checkPreviewVocabulary({ licenses: { "OGA-BY-3.0": { attribution: "required" } } }, ["OGA-BY-3.0"], () => false), "would lack the creator credit");
902904
rejects("V34 rejects a credit on a licence that does not ask for one", checkPreviewVocabulary({ licenses: { MIT: { attribution: "notice" } } }, ["MIT"], () => true), "carry a credit the licence does not ask for");
903905

906+
/* V37: AI-generated content stays tagged and listed ------------------------ */
907+
accepts("V37 accepts a tagged entry", checkAiTag("ok.md", { tags: ["ai-generated"] }, "Some sounds are AI-generated."));
908+
accepts("V37 ignores a denial", checkAiTag("ok.md", { tags: [] }, "itch metadata: No generative AI was used. The audio is not AI-generated."));
909+
accepts("V37 ignores a mention that is not a claim", checkAiTag("ok.md", { tags: [] }, "Built for AI-generated pixel art that is off-grid."));
910+
rejects("V37 rejects an untagged claim", checkAiTag("bad.md", { tags: ["loops"] }, "- Some library sounds are AI-generated and labelled."), "has no ai-generated tag");
911+
rejects("V37 rejects a partly-AI claim", checkAiTag("bad.md", {}, "The pack is partly AI-generated."), "has no ai-generated tag");
912+
accepts("V37 accepts a guide that links every tagged entry", checkAiGuide("- [S](../catalog/audio/s.md)", ["catalog/audio/s.md"]));
913+
rejects("V37 rejects a guide missing a tagged entry", checkAiGuide("nothing", ["catalog/audio/s.md"]), "does not link catalog/audio/s.md");
914+
904915
/* V36: limits is one short plain line ------------------------------------- */
905916
accepts("V36 accepts a short plain line", checkLimits("ok.md", { limits: "Tree packs are Unreal-only; no standalone redistribution" }));
906917
accepts("V36 ignores a missing field", checkLimits("ok.md", {}));

‎site/validate.mjs‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
checkEntryBasics,
1111
checkPreviewVocabulary,
1212
checkFeatured,
13+
checkAiGuide,
14+
checkAiTag,
1315
checkCreditPlaceholders,
1416
checkLimits,
1517
checkKnownKeys,
@@ -313,6 +315,7 @@ function main() {
313315
errors.push(...checkKnownKeys(rel, meta));
314316
errors.push(...checkCreditPlaceholders(rel, meta));
315317
errors.push(...checkLimits(rel, meta));
318+
errors.push(...checkAiTag(rel, meta, body));
316319
// V24 types commercial and attribution_required (a quoted "true" is text).
317320
errors.push(...checkFieldTypes(rel, meta));
318321
errors.push(...checkEntryUrl(rel, meta));
@@ -371,6 +374,13 @@ function main() {
371374
errors.push(...checkComparisonLicences(cat, readText(readme), entriesByRel));
372375
}
373376

377+
errors.push(
378+
...checkAiGuide(
379+
readText(path.join(ROOT, "docs", "ai-assets.md")),
380+
records.filter(({ meta }) => Array.isArray(meta.tags) && meta.tags.map(String).includes("ai-generated")).map(({ rel }) => rel)
381+
)
382+
);
383+
374384
const measured = {};
375385
for (const { meta } of records) {
376386
const cat = String(meta.category);

0 commit comments

Comments
 (0)