feat: classify gear and weapon mods
Validate and deploy production / validate (push) Successful in 24s
Validate and deploy staging / validate (push) Successful in 44s
Validate and deploy production / deploy (push) Failing after 29s
Validate and deploy staging / deploy (push) Failing after 6s

This commit is contained in:
jb
2026-07-31 21:54:47 -04:00
parent 1bd9facd3f
commit f48cc878aa
21 changed files with 595 additions and 39 deletions
+14 -2
View File
@@ -1,6 +1,6 @@
# Game Data Capture Contracts
RG-1 freezes contract version 1 for the taxonomy, workflow states, evidence model, archive manifest, and ISAC handoff boundary.
RG-1 freezes contract version 1 for the taxonomy, workflow states, evidence model, archive manifest, and ISAC handoff boundary. The approved GDC-001 mod-family change extends version 1 with `weapon_mod` and `gear_mod`.
## Files
@@ -8,11 +8,23 @@ RG-1 freezes contract version 1 for the taxonomy, workflow states, evidence mode
- `screenshot-classification.schema.json` validates one hierarchical screen classification.
- `evidence-archive-manifest-v1.schema.json` validates a private evidence archive manifest.
- `isac-handoff-v1.schema.json` validates a bounded draft handoff for authenticated ISAC intake.
- `examples/classification/` contains one valid and one invalid classification for each family, including explicit `unknown` abstention.
- `examples/classification/` contains one valid and one invalid classification for each family, including Weapon Mod, Gear Mod, and explicit `unknown` abstention.
- `examples/archive/` and `examples/handoff/` exercise each versioned document schema.
The schemas use JSON Schema Draft 2020-12, internal `$ref` values, standard formats, and no implementation-specific keywords. Python `jsonschema` and TypeScript validators such as Ajv can compile the files without model conversion. Consumers must reject a document unless `schema_version` equals the schema's `const` value. Consumers must also run semantic reference validation because JSON Schema cannot prove that an ID points to an item in another array.
## Mod Screen Semantics
Repository source data and editors establish `weapon_mod` and `gear_mod` as separate canonical aggregates. Each family currently supports only the `detail` view and its same-named record type. A Mod classification never sets the six-slot `gear_slot` field. The Weapon Mod slot, quality, compatibility, secondary-option, Mod Effect, exotic-attribute, and weapon-lock data and the Gear Mod quality and secondary-option data remain evidence-backed fields or relationships on the owning aggregate. They are not separate screen views or record types.
The `record_types` array still supports composite screens. No repository evidence establishes another independent canonical record type on a Weapon Mod or Gear Mod detail screen, so these two mappings each contain one record type. A classifier must use `unknown` with an abstention reason for another Mod view, an ambiguous family, or unsupported content.
## Version Decision
The approved additive taxonomy change remains schema version 1. The document shapes, trust boundary, bounds, and meanings of every existing value are unchanged, and all existing version 1 labels and documents remain valid. This utility and its payloads are prototype formats; no production-stable archive or handoff version has been declared. A version increment would force a document migration without representing a structural or semantic break.
Consumers must update their closed version 1 vocabularies before they accept Mod labels. Older consumers reject the new values, which keeps the boundary fail closed. Existing stored labels need validation against the revised vocabulary but no value rewrite. The capture-label UI, storage regression coverage, classifier, extraction dispatch, evidence export, and ISAC adapter mappings must consume both families and their exact `detail`/record-type pairs before their respective gates can pass. This contract change does not alter those downstream implementations.
## Evidence Rules
Capture workers store source text, a normalized value, confidence, engine identity, an image digest, a bounded region, and append-only correction history for each evidence value. A null normalized value requires a `missing_reason`. A present normalized value requires a null `missing_reason`. Each correction's `before` state must match the preceding `after` state. Timestamps must increase, and the final `after` state must match the current evidence value.
@@ -48,6 +48,18 @@
"rank_details": ["specialization_tree_rank"]
}
},
"weapon_mod": {
"gear_slot": false,
"views": {
"detail": ["weapon_mod"]
}
},
"gear_mod": {
"gear_slot": false,
"views": {
"detail": ["gear_mod"]
}
},
"unknown": {
"gear_slot": false,
"views": {"unknown": []}
@@ -40,13 +40,13 @@
"id": {"type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"},
"sha256": {"type": "string", "pattern": "^[a-f0-9]{64}$"},
"timestamp": {"type": "string", "format": "date-time", "maxLength": 40},
"family": {"enum": ["weapon", "gear", "skill_chip", "os_protocol", "specialization", "unknown"]},
"family": {"enum": ["weapon", "gear", "skill_chip", "os_protocol", "specialization", "weapon_mod", "gear_mod", "unknown"]},
"recordType": {"enum": [
"weapon", "weapon_talent", "weapon_augmentation_profile", "gear", "brand_set",
"gear_talent", "gear_augmentation_profile", "skill_chip_set", "skill_chip_set_bonus",
"skill_chip_roll_pool", "skill_chip_talent", "os_protocol", "os_protocol_secondary_profile",
"os_protocol_talent", "specialization", "specialization_focus", "specialization_ability",
"specialization_tree_node", "specialization_tree_rank"
"specialization_tree_node", "specialization_tree_rank", "weapon_mod", "gear_mod"
]},
"missingReason": {"enum": [
"not_visible", "occluded", "unreadable", "not_present_on_screen", "unsupported",
@@ -177,6 +177,8 @@
{"properties": {"family": {"const": "skill_chip"}, "view": {"enum": ["detail", "set_bonus", "talent"]}, "gear_slot": {"const": null}, "record_types": {"minItems": 1, "items": {"enum": ["skill_chip_set", "skill_chip_set_bonus", "skill_chip_roll_pool", "skill_chip_talent"]}}, "abstention_reason": {"const": null}}},
{"properties": {"family": {"const": "os_protocol"}, "view": {"enum": ["detail", "secondary_profile", "talent"]}, "gear_slot": {"const": null}, "record_types": {"minItems": 1, "items": {"enum": ["os_protocol", "os_protocol_secondary_profile", "os_protocol_talent"]}}, "abstention_reason": {"const": null}}},
{"properties": {"family": {"const": "specialization"}, "view": {"enum": ["overview", "details", "ability", "focus_details", "focus_tree", "rank_details"]}, "gear_slot": {"const": null}, "record_types": {"minItems": 1, "items": {"enum": ["specialization", "specialization_focus", "specialization_ability", "specialization_tree_node", "specialization_tree_rank"]}}, "abstention_reason": {"const": null}}},
{"properties": {"family": {"const": "weapon_mod"}, "view": {"const": "detail"}, "gear_slot": {"const": null}, "record_types": {"minItems": 1, "maxItems": 1, "items": {"const": "weapon_mod"}}, "abstention_reason": {"const": null}}},
{"properties": {"family": {"const": "gear_mod"}, "view": {"const": "detail"}, "gear_slot": {"const": null}, "record_types": {"minItems": 1, "maxItems": 1, "items": {"const": "gear_mod"}}, "abstention_reason": {"const": null}}},
{"properties": {"family": {"const": "unknown"}, "view": {"const": "unknown"}, "gear_slot": {"const": null}, "record_types": {"maxItems": 0}, "abstention_reason": {"enum": ["low_confidence", "ambiguous_family", "ambiguous_view", "unsupported_family", "unsupported_view", "insufficient_visual_evidence", "occluded", "corrupt_image", "resource_limit", "cancelled"]}}}
]
},
@@ -0,0 +1,8 @@
{
"family": "gear_mod",
"view": "detail",
"gear_slot": "Mask",
"record_types": ["gear_mod"],
"confidence": 0.95,
"abstention_reason": null
}
@@ -0,0 +1,8 @@
{
"family": "gear_mod",
"view": "detail",
"gear_slot": null,
"record_types": ["gear_mod"],
"confidence": 0.95,
"abstention_reason": null
}
@@ -0,0 +1,8 @@
{
"family": "weapon_mod",
"view": "effect",
"gear_slot": null,
"record_types": ["weapon_mod"],
"confidence": 0.96,
"abstention_reason": null
}
@@ -0,0 +1,8 @@
{
"family": "weapon_mod",
"view": "detail",
"gear_slot": null,
"record_types": ["weapon_mod"],
"confidence": 0.96,
"abstention_reason": null
}
@@ -44,7 +44,7 @@
"gear_talent", "gear_augmentation_profile", "skill_chip_set", "skill_chip_set_bonus",
"skill_chip_roll_pool", "skill_chip_talent", "os_protocol", "os_protocol_secondary_profile",
"os_protocol_talent", "specialization", "specialization_focus", "specialization_ability",
"specialization_tree_node", "specialization_tree_rank"
"specialization_tree_node", "specialization_tree_rank", "weapon_mod", "gear_mod"
]},
"missingReason": {"enum": [
"not_visible", "occluded", "unreadable", "not_present_on_screen", "unsupported",
@@ -6,7 +6,7 @@
"additionalProperties": false,
"required": ["family", "view", "gear_slot", "record_types", "confidence", "abstention_reason"],
"properties": {
"family": {"enum": ["weapon", "gear", "skill_chip", "os_protocol", "specialization", "unknown"]},
"family": {"enum": ["weapon", "gear", "skill_chip", "os_protocol", "specialization", "weapon_mod", "gear_mod", "unknown"]},
"view": {"type": "string"},
"gear_slot": {"type": ["string", "null"]},
"record_types": {"type": "array", "uniqueItems": true, "maxItems": 8, "items": {"type": "string"}},
@@ -59,6 +59,24 @@
"abstention_reason": {"const": null}
}
},
{
"properties": {
"family": {"const": "weapon_mod"},
"view": {"const": "detail"},
"gear_slot": {"const": null},
"record_types": {"minItems": 1, "maxItems": 1, "items": {"const": "weapon_mod"}},
"abstention_reason": {"const": null}
}
},
{
"properties": {
"family": {"const": "gear_mod"},
"view": {"const": "detail"},
"gear_slot": {"const": null},
"record_types": {"minItems": 1, "maxItems": 1, "items": {"const": "gear_mod"}},
"abstention_reason": {"const": null}
}
},
{
"properties": {
"family": {"const": "unknown"},