feat: add community publishing and news syndication
Validate and deploy production / validate (push) Successful in 23s
Validate and deploy staging / validate (push) Successful in 50s
Validate and deploy production / deploy (push) Failing after 40s
Validate and deploy staging / deploy (push) Failing after 7s

This commit is contained in:
jb
2026-08-01 21:42:09 -04:00
parent b47e2588e9
commit dd84dff055
288 changed files with 83254 additions and 38351 deletions
+426 -323
View File
@@ -6,285 +6,388 @@ import type { AiMechanicAttribute } from './mechanic-authoring-context';
export const AI_MECHANIC_PROMPT_VERSION = 7;
const mechanicsGrammar = {
version: 1,
simulationReadiness: ['ready', 'requires_assumptions', 'captured_only'],
scenarioInput: {
required: ['key', 'label', 'kind'],
kind: ['toggle', 'slider', 'number'],
readiness: ['ready', 'requires_assumption'],
optional: ['description', 'defaultValue', 'bestCaseValue', 'min', 'max']
},
pool: {
required: ['key', 'label', 'stateKind', 'scope', 'initial'],
stateKind: ['flag', 'count', 'stack', 'duration', 'scalar', 'target_mark', 'resource'],
scope: ['agent', 'target'],
optional: ['min', 'max', 'bestCaseValue', 'expiresAfterSeconds', 'expiresAfterSecondsVariants'],
expirationSemantics:
'expiresAfterSeconds is one timer per pool entity. Use expiresAfterSecondsVariants instead when each source variant has a different duration; define every requested variant exactly once. Every write resets that entity timer, and expiry resets only that entity to the pool initial value.',
targetScopeSemantics:
'A target-scoped pool stores one independent value and expiry timer per resolved entity. Use mutation.entity event_target when a weapon-hit mechanic binds state to the enemy that was hit.'
},
clause: {
required: ['key', 'event', 'if', 'reducer', 'mutations', 'outcomes'],
optional: ['label', 'recipeKey', 'cooldownSeconds', 'weight']
},
predicateKinds: ['true', 'exists', 'compare', 'all', 'any', 'not'],
compareOperators: ['eq', 'ne', 'gt', 'gte', 'lt', 'lte'],
reducerKinds: [
'none',
'count',
'modulo',
'distinct_count',
'consecutive_count',
'sliding_count',
'duration',
'periodic',
'derived_scalar'
],
mutationKinds: ['none', 'add', 'set', 'consume', 'derive', 'clear', 'transfer'],
entitySelectors: ['agent', 'event_actor', 'event_target', 'event_field'],
valueReferences: ['event', 'scenario', 'combat', 'equipment', 'attribute', 'reducer', 'pool'],
combatReferences: MECHANIC_COMBAT_REFERENCE_REGISTRY,
numericExpressionOperators: ['add', 'subtract', 'multiply', 'divide', 'min', 'max', 'clamp', 'floor'],
referenceShapes: [
{ ref: { source: 'event', path: 'values.distanceMeters' } },
{ ref: { source: 'scenario', key: 'input-key' } },
{ ref: { source: 'combat', path: 'currentHealth', timing: 'current' } },
{ ref: { source: 'combat', path: 'currentHealthPercent', timing: 'current' } },
{ ref: { source: 'equipment', path: 'protocolTalentUpgrades' } },
{ ref: { source: 'attribute', attributeId: 'replace-with-an-exact-catalog-attribute-id' } },
{ ref: { source: 'reducer' } },
{ ref: { source: 'pool', pool: 'pool-key', entity: { kind: 'agent' } } }
],
expressionShapes: [
{ op: 'multiply', left: 3, right: { ref: { source: 'pool', pool: 'stacks' } } },
{ op: 'min', left: { ref: { source: 'reducer' } }, right: 5 },
{ op: 'clamp', value: { ref: { source: 'event', path: 'values.enemyCount' } }, min: 0, max: 5 },
{ op: 'floor', value: { op: 'divide', left: { ref: { source: 'event', path: 'values.distanceMeters' } }, right: 5 } }
],
predicateShapes: [
{ kind: 'true' },
{ kind: 'exists', value: { source: 'event', path: 'targetId' } },
{
kind: 'compare',
left: { source: 'event', path: 'values.distanceMeters' },
operator: 'lte',
right: 10
},
{ kind: 'all', predicates: [{ kind: 'true' }] },
{ kind: 'any', predicates: [{ kind: 'true' }] },
{ kind: 'not', predicate: { kind: 'true' } }
],
reducerShapes: [
{ kind: 'none' },
{ kind: 'count', threshold: 9, reset: 'zero', groupBy: { source: 'event', path: 'targetId' } },
{ kind: 'modulo', every: 5 },
{
kind: 'distinct_count',
threshold: 3,
windowSeconds: 5,
distinctBy: { source: 'event', path: 'targetId' }
},
{ kind: 'consecutive_count', threshold: 9, groupBy: { source: 'event', path: 'targetId' } },
{
kind: 'sliding_count',
threshold: 9,
windowSeconds: 3,
reset: 'zero',
groupBy: { source: 'event', path: 'targetId' }
},
{ kind: 'duration', seconds: 3, repeat: false },
{ kind: 'periodic', intervalSeconds: 1, immediate: false },
{ kind: 'derived_scalar', value: { ref: { source: 'event', path: 'values.enemyCount' } } }
],
mutationShapes: [
{ kind: 'none' },
{ kind: 'add', pool: 'stacks', value: 1 },
{
kind: 'add',
pool: 'target-stacks',
entity: { kind: 'event_target' },
value: 1
},
{ kind: 'set', pool: 'active', value: 1 },
{ kind: 'consume', pool: 'stacks', value: 1 },
{ kind: 'derive', pool: 'nearby-enemies', value: { ref: { source: 'reducer' } } },
{ kind: 'clear', pool: 'active' },
{
kind: 'transfer',
pool: 'mark',
from: { kind: 'event_target' },
to: { kind: 'event_field', path: 'values.transferTargetId' },
value: 'all',
clearSource: true
}
],
outcome: CONDITIONAL_MECHANIC_OUTCOME_CAPABILITIES,
continuousRestorationOutcomeShape: {
key: 'restore-health',
label: 'Restore Health',
phase: 'after_event',
kind: 'restoration',
scope: 'self',
basis: 'flat',
resource: 'health',
delivery: 'continuous',
durationSeconds: 3,
variants: {
'replace-with-each-required-variant-key': {
value: 240,
expression: {
op: 'multiply',
left: 240,
right: {
op: 'divide',
left: {
ref: {
source: 'attribute',
attributeId: 'replace-with-an-exact-catalog-attribute-id'
}
},
right: 100
}
}
}
}
},
preciseTraumaShape: {
version: 1,
simulationReadiness: 'ready',
scenarioInputs: [],
pools: [{
key: 'trauma',
label: 'Trauma',
stateKind: 'stack',
scope: 'target',
initial: 0,
min: 0,
max: 10,
bestCaseValue: 10,
expiresAfterSeconds: 1
}],
clauses: [
{
key: 'body-shot-trauma',
label: 'Body shot adds one Trauma stack',
recipeKey: 'body_shot',
event: 'weapon_hit',
if: {
kind: 'all',
predicates: [
{ kind: 'exists', value: { source: 'event', path: 'targetId' } },
{ kind: 'exists', value: { source: 'event', path: 'values.headshot' } },
{
kind: 'compare',
left: { source: 'event', path: 'values.headshot' },
operator: 'eq',
right: false
}
]
},
reducer: { kind: 'none' },
mutations: [{
kind: 'add',
pool: 'trauma',
entity: { kind: 'event_target' },
value: 1
}],
outcomes: [{
key: 'trauma-skill-damage',
label: 'Trauma Skill Damage',
phase: 'on_exit',
kind: 'direct_damage',
scope: 'target',
basis: 'flat',
statePool: 'trauma',
perStack: true,
variants: {
'replace-with-each-required-variant-key': {
value: 0,
expression: {
ref: {
source: 'attribute',
attributeId: 'replace-with-the-exact-skill-damage-catalog-id'
}
}
}
}
}]
},
{
key: 'headshot-trauma',
label: 'Headshot adds two Trauma stacks',
recipeKey: 'headshot',
event: 'weapon_hit',
if: {
kind: 'all',
predicates: [
{ kind: 'exists', value: { source: 'event', path: 'targetId' } },
{ kind: 'exists', value: { source: 'event', path: 'values.headshot' } },
{
kind: 'compare',
left: { source: 'event', path: 'values.headshot' },
operator: 'eq',
right: true
}
]
},
reducer: { kind: 'none' },
mutations: [{
kind: 'add',
pool: 'trauma',
entity: { kind: 'event_target' },
value: 2
}],
outcomes: []
}
]
},
variantValue: {
required: ['value'],
optional: ['expression']
},
completeDraftShape: {
version: 1,
simulationReadiness: 'ready',
scenarioInputs: [],
pools: [{
key: 'stacks',
label: 'Stacks',
stateKind: 'stack',
scope: 'agent',
initial: 0,
min: 0,
max: 10,
bestCaseValue: 10,
expiresAfterSeconds: 10
}],
clauses: [{
key: 'gain-stack',
label: 'Gain one stack',
event: 'weapon_hit',
if: { kind: 'true' },
reducer: { kind: 'count', threshold: 9, reset: 'zero' },
mutations: [{ kind: 'add', pool: 'stacks', value: 1 }],
outcomes: [{
key: 'weapon-damage',
label: 'Weapon Damage',
phase: 'while_active',
kind: 'attribute_modifier',
scope: 'self',
basis: 'flat',
attributeId: 'replace-with-an-exact-catalog-attribute-id',
statePool: 'stacks',
perStack: true,
descriptionSlot: 1,
variants: {
'replace-with-each-required-variant-key': { value: 3 }
}
}]
}]
}
version: 1,
simulationReadiness: ['ready', 'requires_assumptions', 'captured_only'],
scenarioInput: {
required: ['key', 'label', 'kind'],
kind: ['toggle', 'slider', 'number'],
readiness: ['ready', 'requires_assumption'],
optional: ['description', 'defaultValue', 'bestCaseValue', 'min', 'max'],
},
pool: {
required: ['key', 'label', 'stateKind', 'scope', 'initial'],
stateKind: [
'flag',
'count',
'stack',
'duration',
'scalar',
'target_mark',
'resource',
],
scope: ['agent', 'target'],
optional: [
'min',
'max',
'bestCaseValue',
'expiresAfterSeconds',
'expiresAfterSecondsVariants',
],
expirationSemantics:
'expiresAfterSeconds is one timer per pool entity. Use expiresAfterSecondsVariants instead when each source variant has a different duration; define every requested variant exactly once. Every write resets that entity timer, and expiry resets only that entity to the pool initial value.',
targetScopeSemantics:
'A target-scoped pool stores one independent value and expiry timer per resolved entity. Use mutation.entity event_target when a weapon-hit mechanic binds state to the enemy that was hit.',
},
clause: {
required: ['key', 'event', 'if', 'reducer', 'mutations', 'outcomes'],
optional: ['label', 'recipeKey', 'cooldownSeconds', 'weight'],
},
predicateKinds: ['true', 'exists', 'compare', 'all', 'any', 'not'],
compareOperators: ['eq', 'ne', 'gt', 'gte', 'lt', 'lte'],
reducerKinds: [
'none',
'count',
'modulo',
'distinct_count',
'consecutive_count',
'sliding_count',
'duration',
'periodic',
'derived_scalar',
],
mutationKinds: [
'none',
'add',
'set',
'consume',
'derive',
'clear',
'transfer',
],
entitySelectors: ['agent', 'event_actor', 'event_target', 'event_field'],
valueReferences: [
'event',
'scenario',
'combat',
'equipment',
'attribute',
'reducer',
'pool',
],
combatReferences: MECHANIC_COMBAT_REFERENCE_REGISTRY,
numericExpressionOperators: [
'add',
'subtract',
'multiply',
'divide',
'min',
'max',
'clamp',
'floor',
],
referenceShapes: [
{ ref: { source: 'event', path: 'values.distanceMeters' } },
{ ref: { source: 'scenario', key: 'input-key' } },
{ ref: { source: 'combat', path: 'currentHealth', timing: 'current' } },
{
ref: {
source: 'combat',
path: 'currentHealthPercent',
timing: 'current',
},
},
{ ref: { source: 'equipment', path: 'protocolTalentUpgrades' } },
{
ref: {
source: 'attribute',
attributeId: 'replace-with-an-exact-catalog-attribute-id',
},
},
{ ref: { source: 'reducer' } },
{ ref: { source: 'pool', pool: 'pool-key', entity: { kind: 'agent' } } },
],
expressionShapes: [
{
op: 'multiply',
left: 3,
right: { ref: { source: 'pool', pool: 'stacks' } },
},
{ op: 'min', left: { ref: { source: 'reducer' } }, right: 5 },
{
op: 'clamp',
value: { ref: { source: 'event', path: 'values.enemyCount' } },
min: 0,
max: 5,
},
{
op: 'floor',
value: {
op: 'divide',
left: { ref: { source: 'event', path: 'values.distanceMeters' } },
right: 5,
},
},
],
predicateShapes: [
{ kind: 'true' },
{ kind: 'exists', value: { source: 'event', path: 'targetId' } },
{
kind: 'compare',
left: { source: 'event', path: 'values.distanceMeters' },
operator: 'lte',
right: 10,
},
{ kind: 'all', predicates: [{ kind: 'true' }] },
{ kind: 'any', predicates: [{ kind: 'true' }] },
{ kind: 'not', predicate: { kind: 'true' } },
],
reducerShapes: [
{ kind: 'none' },
{
kind: 'count',
threshold: 9,
reset: 'zero',
groupBy: { source: 'event', path: 'targetId' },
},
{ kind: 'modulo', every: 5 },
{
kind: 'distinct_count',
threshold: 3,
windowSeconds: 5,
distinctBy: { source: 'event', path: 'targetId' },
},
{
kind: 'consecutive_count',
threshold: 9,
groupBy: { source: 'event', path: 'targetId' },
},
{
kind: 'sliding_count',
threshold: 9,
windowSeconds: 3,
reset: 'zero',
groupBy: { source: 'event', path: 'targetId' },
},
{ kind: 'duration', seconds: 3, repeat: false },
{ kind: 'periodic', intervalSeconds: 1, immediate: false },
{
kind: 'derived_scalar',
value: { ref: { source: 'event', path: 'values.enemyCount' } },
},
],
mutationShapes: [
{ kind: 'none' },
{ kind: 'add', pool: 'stacks', value: 1 },
{
kind: 'add',
pool: 'target-stacks',
entity: { kind: 'event_target' },
value: 1,
},
{ kind: 'set', pool: 'active', value: 1 },
{ kind: 'consume', pool: 'stacks', value: 1 },
{
kind: 'derive',
pool: 'nearby-enemies',
value: { ref: { source: 'reducer' } },
},
{ kind: 'clear', pool: 'active' },
{
kind: 'transfer',
pool: 'mark',
from: { kind: 'event_target' },
to: { kind: 'event_field', path: 'values.transferTargetId' },
value: 'all',
clearSource: true,
},
],
outcome: CONDITIONAL_MECHANIC_OUTCOME_CAPABILITIES,
continuousRestorationOutcomeShape: {
key: 'restore-health',
label: 'Restore Health',
phase: 'after_event',
kind: 'restoration',
scope: 'self',
basis: 'flat',
resource: 'health',
delivery: 'continuous',
durationSeconds: 3,
variants: {
'replace-with-each-required-variant-key': {
value: 240,
expression: {
op: 'multiply',
left: 240,
right: {
op: 'divide',
left: {
ref: {
source: 'attribute',
attributeId: 'replace-with-an-exact-catalog-attribute-id',
},
},
right: 100,
},
},
},
},
},
preciseTraumaShape: {
version: 1,
simulationReadiness: 'ready',
scenarioInputs: [],
pools: [
{
key: 'trauma',
label: 'Trauma',
stateKind: 'stack',
scope: 'target',
initial: 0,
min: 0,
max: 10,
bestCaseValue: 10,
expiresAfterSeconds: 1,
},
],
clauses: [
{
key: 'body-shot-trauma',
label: 'Body shot adds one Trauma stack',
recipeKey: 'body_shot',
event: 'weapon_hit',
if: {
kind: 'all',
predicates: [
{ kind: 'exists', value: { source: 'event', path: 'targetId' } },
{
kind: 'exists',
value: { source: 'event', path: 'values.headshot' },
},
{
kind: 'compare',
left: { source: 'event', path: 'values.headshot' },
operator: 'eq',
right: false,
},
],
},
reducer: { kind: 'none' },
mutations: [
{
kind: 'add',
pool: 'trauma',
entity: { kind: 'event_target' },
value: 1,
},
],
outcomes: [
{
key: 'trauma-skill-damage',
label: 'Trauma Skill Damage',
phase: 'on_exit',
kind: 'direct_damage',
scope: 'target',
basis: 'flat',
statePool: 'trauma',
perStack: true,
variants: {
'replace-with-each-required-variant-key': {
value: 0,
expression: {
ref: {
source: 'attribute',
attributeId:
'replace-with-the-exact-skill-damage-catalog-id',
},
},
},
},
},
],
},
{
key: 'headshot-trauma',
label: 'Headshot adds two Trauma stacks',
recipeKey: 'headshot',
event: 'weapon_hit',
if: {
kind: 'all',
predicates: [
{ kind: 'exists', value: { source: 'event', path: 'targetId' } },
{
kind: 'exists',
value: { source: 'event', path: 'values.headshot' },
},
{
kind: 'compare',
left: { source: 'event', path: 'values.headshot' },
operator: 'eq',
right: true,
},
],
},
reducer: { kind: 'none' },
mutations: [
{
kind: 'add',
pool: 'trauma',
entity: { kind: 'event_target' },
value: 2,
},
],
outcomes: [],
},
],
},
variantValue: {
required: ['value'],
optional: ['expression'],
},
completeDraftShape: {
version: 1,
simulationReadiness: 'ready',
scenarioInputs: [],
pools: [
{
key: 'stacks',
label: 'Stacks',
stateKind: 'stack',
scope: 'agent',
initial: 0,
min: 0,
max: 10,
bestCaseValue: 10,
expiresAfterSeconds: 10,
},
],
clauses: [
{
key: 'gain-stack',
label: 'Gain one stack',
event: 'weapon_hit',
if: { kind: 'true' },
reducer: { kind: 'count', threshold: 9, reset: 'zero' },
mutations: [{ kind: 'add', pool: 'stacks', value: 1 }],
outcomes: [
{
key: 'weapon-damage',
label: 'Weapon Damage',
phase: 'while_active',
kind: 'attribute_modifier',
scope: 'self',
basis: 'flat',
attributeId: 'replace-with-an-exact-catalog-attribute-id',
statePool: 'stacks',
perStack: true,
descriptionSlot: 1,
variants: {
'replace-with-each-required-variant-key': { value: 3 },
},
},
],
},
],
},
};
export const AI_MECHANIC_AUTHORING_INSTRUCTIONS = `You are the SHD mechanic authoring assistant for The Division Resurgence.
@@ -314,49 +417,49 @@ Response rules:
The response is machine-validated after generation. Invalid or unsupported drafts are rejected and are never saved.`;
export function buildAiMechanicAuthoringInput(input: {
request: AiMechanicAuthoringRequest;
attributes: AiMechanicAttribute[];
events: unknown[];
actions: unknown[];
recipeKeys: string[];
request: AiMechanicAuthoringRequest;
attributes: AiMechanicAttribute[];
events: unknown[];
actions: unknown[];
recipeKeys: string[];
}) {
const { request } = input;
return JSON.stringify({
task: 'Author or revise a mechanic draft from the exact in-game wording.',
source: request.context,
sourcePolicy: {
variantKeys: request.valueColumns.map((column) => column.variantKey),
valueColumns: request.valueColumns,
allowAssignedAbility: request.allowAssignedAbility,
allowProtocolUpgradeScaling: request.allowProtocolUpgradeScaling
},
clarificationTranscript: request.transcript,
currentDraft: request.currentDraft,
canonicalCatalog: {
grammar: mechanicsGrammar,
events: input.events,
actions: input.actions,
recipeKeys: input.recipeKeys,
attributes: input.attributes
},
examplesAndRules: [
'Percent values use the numeric percentage shown in game, such as 3 for +3%, rather than 0.03.',
'Live Attribute references must use {source:"attribute",attributeId:"<exact supplied catalog ID>"}. Never use an Attribute name or invent an ID.',
'An Attribute whose catalog unit is % resolves to its displayed percentage number, such as 20 for 20%. Divide that reference by 100 when wording applies it as a percentage multiplier, such as 240 × 20%.',
'combat.currentHealthPercent with timing:"current" resolves authoritative current Health divided by resolved maximum Health on a 0–100 scale. Use operator:"lt" for exact “below” wording.',
'Health threshold bands must be mutually exclusive. For thresholds 25, 50, and 75, use <25, >=25 and <50, and >=50 and <75; exact threshold values enter the next weaker band.',
'Continuous Health restoration is supported. Use kind:"restoration", resource:"health", delivery:"continuous", and a positive durationSeconds. The variant expression resolves the total restoration across that duration, not a per-second value. Do not replace continuous delivery with a periodic reducer.',
'Each continuous-restoration activation creates an independent constant-rate stream. Overlapping streams add, and restoration above the resource maximum is lost when it occurs.',
'A target-scoped pool owns an independent value and expiry timer for each entity. Bind hit-created target state with entity:{kind:"event_target"}; do not collapse different enemies into one Agent-scoped pool.',
'Writing a target-scoped pool refreshes only that target entity timer. An on_exit outcome runs when that entity expires and uses the entity previous state for stack bands and perStack multiplication, even though the stored pool value has already reset.',
'When an outcome value is exactly a live Attribute amount, use the Attribute reference directly as the variant expression. Divide by 100 only when a displayed percentage is being used as a multiplier.',
'Precise Trauma is representable as one target-scoped stack pool with a one-second expiry: body shots add one stack, headshots add two, and one on_exit direct-damage outcome uses the expiring target previous stack count. This models the stated one-second debuff without inventing generic repeated target ticks.',
'OS Protocol upgrade scaling may use equipment.protocolTalentUpgrades only when allowProtocolUpgradeScaling is true.',
'Assigned-ability scope may be used only when allowAssignedAbility is true.',
'If cadence is not stated, ask rather than assuming a one-second interval.',
'If wording contains separate actions such as restoration on a periodic snapshot and a health cost on every shot, represent them as separate clauses.',
'A pool with expiresAfterSeconds refreshes the shared expiry for every current stack whenever any mutation writes that pool. Use it for mechanics where gaining a stack refreshes the duration of the full stack pool.',
'Manual editing remains authoritative. Preserve a valid current draft only when it agrees with the supplied wording and answers.'
]
});
const { request } = input;
return JSON.stringify({
task: 'Author or revise a mechanic draft from the exact in-game wording.',
source: request.context,
sourcePolicy: {
variantKeys: request.valueColumns.map((column) => column.variantKey),
valueColumns: request.valueColumns,
allowAssignedAbility: request.allowAssignedAbility,
allowProtocolUpgradeScaling: request.allowProtocolUpgradeScaling,
},
clarificationTranscript: request.transcript,
currentDraft: request.currentDraft,
canonicalCatalog: {
grammar: mechanicsGrammar,
events: input.events,
actions: input.actions,
recipeKeys: input.recipeKeys,
attributes: input.attributes,
},
examplesAndRules: [
'Percent values use the numeric percentage shown in game, such as 3 for +3%, rather than 0.03.',
'Live Attribute references must use {source:"attribute",attributeId:"<exact supplied catalog ID>"}. Never use an Attribute name or invent an ID.',
'An Attribute whose catalog unit is % resolves to its displayed percentage number, such as 20 for 20%. Divide that reference by 100 when wording applies it as a percentage multiplier, such as 240 × 20%.',
'combat.currentHealthPercent with timing:"current" resolves authoritative current Health divided by resolved maximum Health on a 0–100 scale. Use operator:"lt" for exact “below” wording.',
'Health threshold bands must be mutually exclusive. For thresholds 25, 50, and 75, use <25, >=25 and <50, and >=50 and <75; exact threshold values enter the next weaker band.',
'Continuous Health restoration is supported. Use kind:"restoration", resource:"health", delivery:"continuous", and a positive durationSeconds. The variant expression resolves the total restoration across that duration, not a per-second value. Do not replace continuous delivery with a periodic reducer.',
'Each continuous-restoration activation creates an independent constant-rate stream. Overlapping streams add, and restoration above the resource maximum is lost when it occurs.',
'A target-scoped pool owns an independent value and expiry timer for each entity. Bind hit-created target state with entity:{kind:"event_target"}; do not collapse different enemies into one Agent-scoped pool.',
'Writing a target-scoped pool refreshes only that target entity timer. An on_exit outcome runs when that entity expires and uses the entity previous state for stack bands and perStack multiplication, even though the stored pool value has already reset.',
'When an outcome value is exactly a live Attribute amount, use the Attribute reference directly as the variant expression. Divide by 100 only when a displayed percentage is being used as a multiplier.',
'Precise Trauma is representable as one target-scoped stack pool with a one-second expiry: body shots add one stack, headshots add two, and one on_exit direct-damage outcome uses the expiring target previous stack count. This models the stated one-second debuff without inventing generic repeated target ticks.',
'OS Protocol upgrade scaling may use equipment.protocolTalentUpgrades only when allowProtocolUpgradeScaling is true.',
'Assigned-ability scope may be used only when allowAssignedAbility is true.',
'If cadence is not stated, ask rather than assuming a one-second interval.',
'If wording contains separate actions such as restoration on a periodic snapshot and a health cost on every shot, represent them as separate clauses.',
'A pool with expiresAfterSeconds refreshes the shared expiry for every current stack whenever any mutation writes that pool. Use it for mechanics where gaining a stack refreshes the duration of the full stack pool.',
'Manual editing remains authoritative. Preserve a valid current draft only when it agrees with the supplied wording and answers.',
],
});
}