Skip to content

[Fix] Upstream fix Part 1 - #161

Merged
UmbiMax merged 27 commits into
ArcaneSS14:masterfrom
ReWAFFlution:fieeexx
Aug 1, 2026
Merged

[Fix] Upstream fix Part 1#161
UmbiMax merged 27 commits into
ArcaneSS14:masterfrom
ReWAFFlution:fieeexx

Conversation

@ReWAFFlution

@ReWAFFlution ReWAFFlution commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Описание PR

Фиксы взяли отсюда:
space-syndicate/Goob-Station#991
space-syndicate/Goob-Station#1013
space-syndicate/Goob-Station#1022

  • машина клоуна больше не требует доп ключ

  • борги могут брать материалы и вырываться из граба.

  • в ItemSlotRendererSystem если слот ссылается на удаленную сущность , то вместо того чтобы отрисовать удаленный энтити , будет использоваться пустая текстура

  • Закуп антагонистов в манифесте теперь отображается

  • Транспорт больше не может ездить по космосу

  • Если сломать 1 разлом дракона, то ломаются все

  • Теперь можно поставить голобарьер(проектор) на тайл где есть лужа

  • Сабля ГСБ теперь заряжается

  • Борги могут брать органы

  • У умного корги вновь две руки

  • Сингулярность и тесла больше не ломают барьеры(наверное)

  • Улучшения для латов теперь можно вставлять

  • Вору убран пацифизм

  • Взаимодействие через E с наэлектризованными ентити бьют током

  • У дрона корректно работает т-лучевой сканер

  • При использовании разных стимуляторов, увеличивающих скорость, кукла игрока больше не лагает

  • Теперь наборов для ушибов/мазей/пакетов с кровью/бинтов реально 15

  • Живая сущность в инвентаре больше не мешает поднять щит через хотбар

Также отдельно были пофикшены тесты. Ыыы.

Тип PR

  • Feature
  • Fix
  • Tweak
  • Balance
  • Refactor
  • Port
  • Translate
  • Resprite

🆑 ReWAFFlution, Umbi_Max, shimorio

  • fix: борги могут брать материалы и вырываться из граба
  • fix: Закуп антагонистов в манифесте теперь отображается
  • fix: Сабля ГСБ теперь заряжается
  • fix: Вору убран пацифизм
  • fix: Теперь наборов для ушибов/мазей/пакетов с кровью/бинтов реально 15
  • fix: Взаимодействие через E с наэлектризованными ентити бьют током
  • fix: Ныне запускаются абсолютно все карты

@ReWAFFlution

Copy link
Copy Markdown
Contributor Author

Это то что я нашёл от Шимори в ПРах. Мб ещё найду какие-то фиксы от Диммона но уже в коммитах.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 64b5847d-e483-4d4a-90de-6ac56fb1c6dc

📥 Commits

Reviewing files that changed from the base of the PR and between 3472b63 and bb2e79a.

📒 Files selected for processing (1)
  • Resources/Maps/_Goobstation/Shuttles/consul.yml
📜 Recent review details
⏰ Context from checks skipped due to timeout. (5)
  • GitHub Check: Test Packaging
  • GitHub Check: YAML map schema validator
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: YAML Linter
  • GitHub Check: build (ubuntu-latest)
🧰 Additional context used
📓 Path-based instructions (4)
Resources/**/*.{yml,ftl,json}

📄 CodeRabbit inference engine (.cursor/rules/ss14-resources.mdc)

Resources/**/*.{yml,ftl,json}: Follow SS14 naming conventions when editing prototype, localization, and resource files
Follow SS14 upstream maintenance practices when editing resource files

Files:

  • Resources/Maps/_Goobstation/Shuttles/consul.yml
Resources/**/*.yml

📄 CodeRabbit inference engine (.cursor/rules/ss14-resources.mdc)

Resources/**/*.yml: Follow SS14 ECS prototype patterns and conventions
Follow SS14 graphics generic visualizer appearance conventions and patterns

Resources/**/*.yml: Load ss14-naming-conventions documentation for Resources work
Load ss14-ecs-prototypes documentation for Resources work
Load ss14-upstream-maintenance documentation for Resources work
Load ss14-prototypes-locale documentation for Resources work
Load ss14-localization-strings documentation for Resources work
Load ss14-sprite-overlays-shaders documentation when working with RSI metadata, textures, sprite layers, overlays, or shader resources
Validate YAML after edits

Run dotnet run --project Content.YAMLLinter/Content.YAMLLinter.csproj -c DebugOpt to validate YAML and resource edits.

Files:

  • Resources/Maps/_Goobstation/Shuttles/consul.yml
Resources/**/*.{yml,ftl}

📄 CodeRabbit inference engine (.cursor/rules/ss14-resources.mdc)

Follow SS14 prototype locale integration rules and practices

Files:

  • Resources/Maps/_Goobstation/Shuttles/consul.yml
**/*.{cs,xaml,yml,yaml}

📄 CodeRabbit inference engine (AGENTS.md)

If the task adds tests or you need to choose the right test layer, load skill ss14-tests-authoring.

Files:

  • Resources/Maps/_Goobstation/Shuttles/consul.yml
🧠 Learnings (2)
📚 Learning: 2026-07-07T17:55:50.395Z
Learnt from: UmbiMax
Repo: ArcaneSS14/arcane-old PR: 101
File: Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs:192-194
Timestamp: 2026-07-07T17:55:50.395Z
Learning: When reviewing ArcaneSS14/arcane-old changes related to `MobState` thresholds/visuals/actions, treat the `Critical` enum value as requiring explicit mappings (it no longer implicitly behaves as an alias of `SoftCritical`). If any YAML prototype `thresholds:` (for `MobThresholdsComponent.Thresholds`) uses the bare `Critical` key (e.g., `MobThresholdsComponent.Thresholds: { Critical: ... }` for entities like `MobMouseCancer`), ensure the prototype (or referenced config) provides corresponding explicit `StateAlertDict` entries and any required `MobStateActions.actions` and `DamageStateVisuals.states` mappings for `Critical`. Otherwise, review should expect failures because the default `StateAlertDict` in `MobThresholdsComponent.cs` typically maps only `Alive`, `SoftCritical`, `HardCritical`, and `Dead`—not `Critical`.

Applied to files:

  • Resources/Maps/_Goobstation/Shuttles/consul.yml
📚 Learning: 2026-07-28T15:44:38.498Z
Learnt from: UmbiMax
Repo: ArcaneSS14/arcane PR: 152
File: Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/pistol.yml:69-72
Timestamp: 2026-07-28T15:44:38.498Z
Learning: В ArcaneSS14/arcane не выполняйте форматирующие перестановки полей (в т.ч. изменение порядка полей) в upstream-файлах: делайте это только если правка добавляет Arcane-специфичную сущность или напрямую требуется для функциональности. Иначе сохраняйте upstream-порядок полей, даже если он противоречит локальным рекомендациям, чтобы не создавать лишние diff и конфликты при upstream-синхронизации. Исключение: допустимо переупорядочивание/форматирование в `Resources/Prototypes/_Arcane` (и только там).

Applied to files:

  • Resources/Maps/_Goobstation/Shuttles/consul.yml
🔇 Additional comments (1)
Resources/Maps/_Goobstation/Shuttles/consul.yml (1)

3208-3208: 🎯 Functional Correctness

Проверьте YAML штатным линтером перед слиянием.

dotnet run --project Content.YAMLLinter/Content.YAMLLinter.csproj -c DebugOpt

Ожидаемый результат: ошибки синтаксиса и схемы отсутствуют.

Also applies to: 3286-3286, 3305-3305, 3324-3324, 3667-3667, 3686-3686, 3705-3705, 3733-3805, 9778-9786, 9853-9853, 10878-10878, 10897-10897, 10916-10916, 10935-10935, 10954-10954, 10973-10973, 10992-10992, 11011-11011, 11030-11030, 11049-11049, 11068-11068, 11087-11087


Главное изменение

  • Портированы upstream-исправления для транспорта, драконьих рифтов, электрифицированных сущностей, голознаков, блокирования и движения.
  • Исправлена обработка удалённых сущностей в ItemSlotRenderer.
  • Исправлено получение JobPrototype и расчёт зарплаты для ролей без корректного job ID.
  • Обновлены взаимодействия с батареями, медицинскими предметами, киборгами и smart corgi.
  • Thief и OopsAllThieves больше не получают Pacified.
  • Латы получили BaseSiloUtilizer. Старые варианты AutolatheHyperConvection и ProtolatheHyperConvection перенаправляются на стандартные латы.
  • Добавлены точки появления ролей на шести картах. В PostMapInitTest добавлены Aspid и WonderBox.
  • Исправлены конфигурации consul.yml, box.yml, FigurinesQM, SmartFridgeCircuitboard и связанные integration-тесты.

Prediction / networking

  • SharedVehicleSystem добавляет транспортам GravityAffectedComponent и обрабатывает RefreshWeightlessModifiersEvent. Это широкая upstream-правка с риском рассинхронизации состояния невесомости.
  • MovementModStatusSystem теперь вызывает Dirty(status) после изменения модификаторов скорости.
  • SharedTrayScannerSystem обновляет visibility mask при подключении игрока.
  • Другие изменения не добавляют сетевые сообщения и не переносят игровую логику между слоями.

Resources / localization

  • Обновлены прототипы smart corgi, киборгов, материалов, сингулярности, Tesla, clown car, медицинских стеков и holopad.
  • Для DrinkKvassGlass отключены fill-level визуализации в руке.
  • Добавлено состояние open_unlit для syndicate hatch.
  • Удалена UI-разметка из русских хирургических статусов.
  • Параметр локализации зарядного устройства изменён с chargePercentage на chargePercent.
  • Breaking prototype ID не переименовывались. Миграция добавлена для устаревших вариантов лат.
  • Изменения зимней куртки уменьшают материалы с 125/50 до 3/3. Это может изменить баланс крафта.
  • Player-facing изменения затрагивают русскую локализацию. Обновления других локалей не показаны.

Validation / risk

  • Исправлены известные причины integration-test failures: отсутствующие map prototypes и spawn points, неверный DeviceNetwork, устаревшие Stack overrides, количество FigurinesQM и невалидные job prototypes.
  • Полный набор тестов и runtime-проверки в изменениях не указаны.
  • Для транспорта требуется проверить prediction и состояние невесомости.
  • Для лат требуется проверить assembly placement, silo-интеграцию, рецепты и запуск карт.
  • Изменения прототипов киборгов, smart corgi, holopad и медицинских стеков требуют проверки загрузки прототипов и поведения предметов в игре.

Walkthrough

PR изменяет игровые системы, прототипы сущностей, правила, локализацию, машинные конфигурации и карты. Добавлены проверки состояний, точки появления и безопасная обработка отсутствующих прототипов.

Changes

Игровые системы и контент

Layer / File(s) Summary
Игровые системы и проверки
Content.Client/..., Content.Server/..., Content.Shared/...
Обновлены гравитация транспорта, активация электрифицированных сущностей, расчёт заряда, физические проверки, видимость Tray Scanner, блокирование, движение, рифты и обработка должностей.
Прототипы сущностей и компонентов
Resources/Prototypes/..., Resources/Textures/...
Добавлены слоты SmartCorgi, компоненты и теги, ограничения стеков, whitelist органов, параметры отображения и состояние текстуры шлюза.
Правила, локализация и совместимость
Resources/Prototypes/GameRules/..., Resources/Locale/..., Content.Shared/CCVar/..., Resources/migration.yml
Отключён Pacified, изменён fallback-пресет, обновлены локализованные параметры и добавлены миграции прототипов.
Машинные и шаттловые конфигурации
Resources/Prototypes/Entities/..., Resources/Maps/_Goobstation/Shuttles/..., Resources/Maps/_Goobstation/box.yml
Машины получили BaseSiloUtilizer. Обновлены платы и типы стеков. Удалён недействительный DeviceNetwork.
Карты и интеграционные проверки
Resources/Maps/_Goobstation/*.yml, Content.IntegrationTests/Tests/PostMapInitTest.cs
На шесть карт добавлены три точки появления. Обновлены счётчики сущностей и список проверяемых карт.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested reviewers: puroslavking

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive Название связано с портированием исправлений, но не указывает, какие основные проблемы исправлены. Укажите ключевое изменение в названии, например: «Порт исправлений для транспорта, боргов и карт».
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed Описание перечисляет исправления и связывает их с портированными upstream-изменениями, поэтому соответствует содержанию PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ReWAFFlution ReWAFFlution changed the title [Fix] Upstream fix Part 1 [Fix] Upstream fix Part 1 [WIP] Jul 31, 2026
@ReWAFFlution

Copy link
Copy Markdown
Contributor Author

Короче че ещё найду то и сюда закину

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Content.Server/Dragon/DragonSystem.cs`:
- Line 290: Update the Dragon shutdown cleanup flow around DeleteRifts so
cleanup or round-end does not reset CarpRiftsConditionComponent.RiftsCharged.
Preserve progress when RiftDestroyed is triggered by ComponentShutdown, and use
an explicit cause distinguishing crew destruction from cleanup/round-end instead
of relying on the already-cleared comp.Rifts or TryComp lookup.

In `@Content.Server/Electrocution/ElectrocutionSystem.cs`:
- Line 92: Переименуйте ElectrifiedComponent.OnHandInteract в общее имя,
отражающее активацию как через InteractHandEvent, так и через
ActivateInWorldEvent, и обновите проверки в OnElectrifiedActivate и связанных
обработчиках. Проверьте взаимодействие подписок ActivateInWorldEvent и
InteractHandEvent, чтобы одна активация предмета не вызывала повторный удар.

In `@Content.Shared/_DV/Construction/UpgradeKitSystem.cs`:
- Line 83: Replace the trailing comment on the IsWhitelistPass condition in
UpgradeKitSystem with the standard // Arcane-Edit fork marker; leave the
condition and its blacklist behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5291193a-f5c5-43d5-975d-b3433e5cd975

📥 Commits

Reviewing files that changed from the base of the PR and between fc3a12d and a353a61.

📒 Files selected for processing (23)
  • Content.Client/RoundEnd/RoundEndSummaryWindow.cs
  • Content.Client/_White/ItemSlotRenderer/ItemSlotRendererSystem.cs
  • Content.Goobstation.Shared/Vehicles/SharedVehicleSystem.cs
  • Content.Server/Dragon/DragonSystem.cs
  • Content.Server/Electrocution/ElectrocutionSystem.cs
  • Content.Server/Holosign/HolosignSystem.cs
  • Content.Server/_Shitmed/ItemSwitch/ItemSwitchSystem.cs
  • Content.Shared/Blocking/BlockingSystem.cs
  • Content.Shared/Movement/Systems/MovementModStatusSystem.cs
  • Content.Shared/SubFloor/SharedTrayScannerSystem.cs
  • Content.Shared/Throwing/ThrowingSystem.cs
  • Content.Shared/_DV/Construction/UpgradeKitSystem.cs
  • Resources/Locale/ru-RU/_Shitmed/surgery/status.ftl
  • Resources/Prototypes/Body/Prototypes/Specific/smartcorgi.yml
  • Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml
  • Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml
  • Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml
  • Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml
  • Resources/Prototypes/GameRules/subgamemodes.yml
  • Resources/Prototypes/Stacks/Specific/medical.yml
  • Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Robotics/borg_modules.yml
  • Resources/Prototypes/_Goobstation/Entities/Objects/Vehicles/clowncar.yml
  • Resources/Prototypes/_Goobstation/GameRules/roundstart.yml
📜 Review details
⏰ Context from checks skipped due to timeout. (5)
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: YAML map schema validator
  • GitHub Check: YAML Linter
  • GitHub Check: Test Packaging
🧰 Additional context used
📓 Path-based instructions (21)
Resources/**/*.{yml,ftl,json}

📄 CodeRabbit inference engine (.cursor/rules/ss14-resources.mdc)

Resources/**/*.{yml,ftl,json}: Follow SS14 naming conventions when editing prototype, localization, and resource files
Follow SS14 upstream maintenance practices when editing resource files

Files:

  • Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml
  • Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml
  • Resources/Prototypes/GameRules/subgamemodes.yml
  • Resources/Prototypes/_Goobstation/GameRules/roundstart.yml
  • Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml
  • Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml
  • Resources/Prototypes/_Goobstation/Entities/Objects/Vehicles/clowncar.yml
  • Resources/Prototypes/Stacks/Specific/medical.yml
  • Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Robotics/borg_modules.yml
  • Resources/Prototypes/Body/Prototypes/Specific/smartcorgi.yml
  • Resources/Locale/ru-RU/_Shitmed/surgery/status.ftl
Resources/**/*.yml

📄 CodeRabbit inference engine (.cursor/rules/ss14-resources.mdc)

Resources/**/*.yml: Follow SS14 ECS prototype patterns and conventions
Follow SS14 graphics generic visualizer appearance conventions and patterns

Resources/**/*.yml: Load ss14-naming-conventions documentation for Resources work
Load ss14-ecs-prototypes documentation for Resources work
Load ss14-upstream-maintenance documentation for Resources work
Load ss14-prototypes-locale documentation for Resources work
Load ss14-localization-strings documentation for Resources work
Load ss14-sprite-overlays-shaders documentation when working with RSI metadata, textures, sprite layers, overlays, or shader resources
Validate YAML after edits

Run dotnet run --project Content.YAMLLinter/Content.YAMLLinter.csproj -c DebugOpt to validate YAML and resource edits.

Files:

  • Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml
  • Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml
  • Resources/Prototypes/GameRules/subgamemodes.yml
  • Resources/Prototypes/_Goobstation/GameRules/roundstart.yml
  • Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml
  • Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml
  • Resources/Prototypes/_Goobstation/Entities/Objects/Vehicles/clowncar.yml
  • Resources/Prototypes/Stacks/Specific/medical.yml
  • Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Robotics/borg_modules.yml
  • Resources/Prototypes/Body/Prototypes/Specific/smartcorgi.yml
Resources/**/*.{yml,ftl}

📄 CodeRabbit inference engine (.cursor/rules/ss14-resources.mdc)

Follow SS14 prototype locale integration rules and practices

Files:

  • Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml
  • Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml
  • Resources/Prototypes/GameRules/subgamemodes.yml
  • Resources/Prototypes/_Goobstation/GameRules/roundstart.yml
  • Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml
  • Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml
  • Resources/Prototypes/_Goobstation/Entities/Objects/Vehicles/clowncar.yml
  • Resources/Prototypes/Stacks/Specific/medical.yml
  • Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Robotics/borg_modules.yml
  • Resources/Prototypes/Body/Prototypes/Specific/smartcorgi.yml
  • Resources/Locale/ru-RU/_Shitmed/surgery/status.ftl
**/*.{cs,xaml,yml,yaml}

📄 CodeRabbit inference engine (AGENTS.md)

If the task adds tests or you need to choose the right test layer, load skill ss14-tests-authoring.

Files:

  • Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml
  • Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml
  • Resources/Prototypes/GameRules/subgamemodes.yml
  • Resources/Prototypes/_Goobstation/GameRules/roundstart.yml
  • Content.Server/Holosign/HolosignSystem.cs
  • Content.Shared/Throwing/ThrowingSystem.cs
  • Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml
  • Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml
  • Content.Shared/Movement/Systems/MovementModStatusSystem.cs
  • Resources/Prototypes/_Goobstation/Entities/Objects/Vehicles/clowncar.yml
  • Resources/Prototypes/Stacks/Specific/medical.yml
  • Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Robotics/borg_modules.yml
  • Content.Client/_White/ItemSlotRenderer/ItemSlotRendererSystem.cs
  • Content.Shared/_DV/Construction/UpgradeKitSystem.cs
  • Resources/Prototypes/Body/Prototypes/Specific/smartcorgi.yml
  • Content.Shared/SubFloor/SharedTrayScannerSystem.cs
  • Content.Server/Electrocution/ElectrocutionSystem.cs
  • Content.Client/RoundEnd/RoundEndSummaryWindow.cs
  • Content.Shared/Blocking/BlockingSystem.cs
  • Content.Goobstation.Shared/Vehicles/SharedVehicleSystem.cs
  • Content.Server/_Shitmed/ItemSwitch/ItemSwitchSystem.cs
  • Content.Server/Dragon/DragonSystem.cs
Resources/Prototypes/**/*.yml

📄 CodeRabbit inference engine (AGENTS.md)

Resources/Prototypes/**/*.yml: Put prototypes under the most specific existing subtree in Resources/Prototypes/. If you introduce a new prototype parent tree, put parent prototypes in base.yml and variants in sibling files.
Keep entity prototype field order as type, abstract, parent, id, categories, name, suffix, description, components. Do not insert blank lines between - type: entries inside a components: list. Separate prototype blocks with one blank line.
Prefer suffix for spawn-menu distinctions instead of changing prototype name.

Files:

  • Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml
  • Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml
  • Resources/Prototypes/GameRules/subgamemodes.yml
  • Resources/Prototypes/_Goobstation/GameRules/roundstart.yml
  • Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml
  • Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml
  • Resources/Prototypes/_Goobstation/Entities/Objects/Vehicles/clowncar.yml
  • Resources/Prototypes/Stacks/Specific/medical.yml
  • Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Robotics/borg_modules.yml
  • Resources/Prototypes/Body/Prototypes/Specific/smartcorgi.yml

⚙️ CodeRabbit configuration file

Resources/Prototypes/**/*.yml: Review SS14 prototypes carefully:

  • Use the narrowest existing subtree and avoid duplicating nearby prototype files or parent trees.
  • Keep entity field order as: type, abstract, parent, id, categories, name, suffix, description, components.
  • Prefer base.yml for parent trees and sibling files for variants.
  • Do not add blank lines inside components lists.
  • Call out broken inheritance, risky prototype ID changes, missing paired locale updates, and Appearance / GenericVisualizer mismatches.

Files:

  • Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml
  • Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml
  • Resources/Prototypes/GameRules/subgamemodes.yml
  • Resources/Prototypes/_Goobstation/GameRules/roundstart.yml
  • Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml
  • Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml
  • Resources/Prototypes/_Goobstation/Entities/Objects/Vehicles/clowncar.yml
  • Resources/Prototypes/Stacks/Specific/medical.yml
  • Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Robotics/borg_modules.yml
  • Resources/Prototypes/Body/Prototypes/Specific/smartcorgi.yml
**/*.cs

📄 CodeRabbit inference engine (GEMINI.md)

**/*.cs: Keep components data-only and behavior in systems
Use On... -> Try... -> Can... -> Do... pattern for gameplay actions
Prefer Entity<T?>, ProtoId<T>, EntProtoId, and localized strings for type usage

**/*.cs: When writing or editing C# gameplay code, load skills: ss14-ecs-components, ss14-ecs-entities, ss14-ecs-prototypes, ss14-ecs-systems, ss14-events, and ss14-prediction.
If the C# change is large or the task needs reviewable notes, load skill ss14-documentation-writing.
If the code touches hot paths, Update(), or frequently raised events, load skill ss14-standard-optimizations.
If the task adds or changes player-facing text, load skills ss14-localization-strings and ss14-localization-code when the change also touches Loc.GetString(...), LocId, popup text, or localized component fields.
If the task touches network events, NetEntity, replicated state routing, or shared/server/client message flow, load skill ss14-netcode.
If the task touches Appearance, GenericVisualizer, visual state enums, or sprite-layer toggles, load skill ss14-graphics-generic-visualizer-appearance.
If the task touches sprites, RSI metadata, overlays, shaders, or custom client visual effects, load skill ss14-sprite-overlays-shaders.
If the task is about learning or explaining SS14 architecture, first features, or where code belongs, load skills ss14-prototype-basics, ss14-ecs-basics, and ss14-client-server-shared.
If the task is about bug hunting, VV, logs, breakpoints, or runtime inspection, load skills ss14-debugging-workflow and ss14-common-api-patterns.
If the task touches common gameplay helpers such as entity-system methods, spawning, prototypes, audio, popups, or random, load skills ss14-common-api-patterns and ss14-audio when the work changes audio routing, sound assets, sound collections, or predicted sound feedback.
If the task ports code or assets from another repository, or needs license or attribution guidance,...

Files:

  • Content.Server/Holosign/HolosignSystem.cs
  • Content.Shared/Throwing/ThrowingSystem.cs
  • Content.Shared/Movement/Systems/MovementModStatusSystem.cs
  • Content.Client/_White/ItemSlotRenderer/ItemSlotRendererSystem.cs
  • Content.Shared/_DV/Construction/UpgradeKitSystem.cs
  • Content.Shared/SubFloor/SharedTrayScannerSystem.cs
  • Content.Server/Electrocution/ElectrocutionSystem.cs
  • Content.Client/RoundEnd/RoundEndSummaryWindow.cs
  • Content.Shared/Blocking/BlockingSystem.cs
  • Content.Goobstation.Shared/Vehicles/SharedVehicleSystem.cs
  • Content.Server/_Shitmed/ItemSwitch/ItemSwitchSystem.cs
  • Content.Server/Dragon/DragonSystem.cs
+(Content.Shared|Content.Server|Content.Client|Content.Goobstation.Shared|Content.Goobstation.Server|Content.Goobstation.Client|Content.Goobstation.Common|Content.Goobstation.Maths|Content.Goobstation.UIKit|Content.Server.Database|Content.Shared.Database|Content.Tests|Content.IntegrationTests)/**/*.cs

📄 CodeRabbit inference engine (.cursor/rules/ss14-csharp.mdc)

+(Content.Shared|Content.Server|Content.Client|Content.Goobstation.Shared|Content.Goobstation.Server|Content.Goobstation.Client|Content.Goobstation.Common|Content.Goobstation.Maths|Content.Goobstation.UIKit|Content.Server.Database|Content.Shared.Database|Content.Tests|Content.IntegrationTests)/**/*.cs: Apply C# ECS style rules from ss14-interaction-flow.md
Apply ss14-csharp-style.md rules for C# code formatting and conventions
Follow ss14-naming-conventions for variable, method, and type naming in C# gameplay code

Files:

  • Content.Server/Holosign/HolosignSystem.cs
  • Content.Shared/Throwing/ThrowingSystem.cs
  • Content.Shared/Movement/Systems/MovementModStatusSystem.cs
  • Content.Client/_White/ItemSlotRenderer/ItemSlotRendererSystem.cs
  • Content.Shared/_DV/Construction/UpgradeKitSystem.cs
  • Content.Shared/SubFloor/SharedTrayScannerSystem.cs
  • Content.Server/Electrocution/ElectrocutionSystem.cs
  • Content.Client/RoundEnd/RoundEndSummaryWindow.cs
  • Content.Shared/Blocking/BlockingSystem.cs
  • Content.Goobstation.Shared/Vehicles/SharedVehicleSystem.cs
  • Content.Server/_Shitmed/ItemSwitch/ItemSwitchSystem.cs
  • Content.Server/Dragon/DragonSystem.cs
{Content.Server,Content.Goobstation.Server}/**/*.cs

📄 CodeRabbit inference engine (AGENTS.md)

Put main server-only authority and non-predicted server simulation in Content.Server/; use Content.Goobstation.Server/ for features already rooted in that module.

Files:

  • Content.Server/Holosign/HolosignSystem.cs
  • Content.Server/Electrocution/ElectrocutionSystem.cs
  • Content.Server/_Shitmed/ItemSwitch/ItemSwitchSystem.cs
  • Content.Server/Dragon/DragonSystem.cs
**/*System.cs

📄 CodeRabbit inference engine (AGENTS.md)

Public entity-system APIs that operate on entities should usually take Entity<T?> or EntityUid first and call Resolve(...) early.

Files:

  • Content.Server/Holosign/HolosignSystem.cs
  • Content.Shared/Throwing/ThrowingSystem.cs
  • Content.Shared/Movement/Systems/MovementModStatusSystem.cs
  • Content.Client/_White/ItemSlotRenderer/ItemSlotRendererSystem.cs
  • Content.Shared/_DV/Construction/UpgradeKitSystem.cs
  • Content.Shared/SubFloor/SharedTrayScannerSystem.cs
  • Content.Server/Electrocution/ElectrocutionSystem.cs
  • Content.Shared/Blocking/BlockingSystem.cs
  • Content.Goobstation.Shared/Vehicles/SharedVehicleSystem.cs
  • Content.Server/_Shitmed/ItemSwitch/ItemSwitchSystem.cs
  • Content.Server/Dragon/DragonSystem.cs
Content.Server/**/*.cs

⚙️ CodeRabbit configuration file

Content.Server/**/*.cs: Review Content.Server for authority and fork-maintenance discipline:

  • Server owns authority, persistence, round logic, and non-predicted side effects.
  • If a player action should feel immediate, call out missing shared prediction rather than accepting a server-only path.
  • Keep upstream diffs narrow and prefer fork-scoped extensions over broad edits to upstream behavior.
  • When player-visible behavior changes, check whether matching prototype and locale updates are missing.

Files:

  • Content.Server/Holosign/HolosignSystem.cs
  • Content.Server/Electrocution/ElectrocutionSystem.cs
  • Content.Server/_Shitmed/ItemSwitch/ItemSwitchSystem.cs
  • Content.Server/Dragon/DragonSystem.cs
Content.Shared/**

📄 CodeRabbit inference engine (Content.Shared/AGENTS.md)

Content.Shared/**: Load ss14-naming-conventions agent notes for Content.Shared work
Load ss14-ecs-prototypes, ss14-ecs-components, ss14-ecs-entities, and ss14-ecs-systems agent notes for Content.Shared work
Load ss14-upstream-maintenance agent notes for Content.Shared work
Load ss14-events agent notes for Content.Shared work
Load ss14-prediction and ss14-netcode agent notes for Content.Shared work
Load ss14-localization-code agent notes when shared code emits player text or stores LocId
Load ss14-graphics-generic-visualizer-appearance agent notes when shared gameplay state drives Appearance or GenericVisualizer
Load ss14-audio agent notes when shared components or events carry sound specifiers or predicted audio intent
Load ss14-atmos agent notes when shared components, UI messages, or enums belong to atmos features
Load ss14-transform-physics agent notes for shared coordinates, movement, collision, anchoring, or physics contracts
Load ss14-pvs agent notes for PVS-sensitive shared/network contracts
Load ss14-npc-ai agent notes for shared NPC, HTN, steering, pathfinding, or debug contracts
Shared code must own replicated state, shared events, and prediction-aware logic without adding direct client-only or server-only dependencies

Files:

  • Content.Shared/Throwing/ThrowingSystem.cs
  • Content.Shared/Movement/Systems/MovementModStatusSystem.cs
  • Content.Shared/_DV/Construction/UpgradeKitSystem.cs
  • Content.Shared/SubFloor/SharedTrayScannerSystem.cs
  • Content.Shared/Blocking/BlockingSystem.cs
{Content.Shared,Content.Goobstation.Shared,Content.Goobstation.Common,Content.Goobstation.Maths,Content.Shared.Database}/**/*.cs

📄 CodeRabbit inference engine (.cursor/rules/ss14-shared.mdc)

{Content.Shared,Content.Goobstation.Shared,Content.Goobstation.Common,Content.Goobstation.Maths,Content.Shared.Database}/**/*.cs: Apply SS14 C# style guidelines from @.agents/rules/ss14-csharp-style.md
Follow SS14 ECS component patterns as defined in @.agents/skills/ss14-ecs-components/SKILL.md
Follow SS14 ECS entity patterns as defined in @.agents/skills/ss14-ecs-entities/SKILL.md
Follow SS14 ECS system patterns as defined in @.agents/skills/ss14-ecs-systems/SKILL.md
Follow SS14 event patterns as defined in @.agents/skills/ss14-events/SKILL.md
Follow SS14 prediction-aware gameplay code patterns as defined in @.agents/skills/ss14-prediction/SKILL.md
Follow SS14 netcode patterns as defined in @.agents/skills/ss14-netcode/SKILL.md
Follow SS14 localization identifier patterns as defined in @.agents/skills/ss14-localization-code/SKILL.md

Files:

  • Content.Shared/Throwing/ThrowingSystem.cs
  • Content.Shared/Movement/Systems/MovementModStatusSystem.cs
  • Content.Shared/_DV/Construction/UpgradeKitSystem.cs
  • Content.Shared/SubFloor/SharedTrayScannerSystem.cs
  • Content.Shared/Blocking/BlockingSystem.cs
  • Content.Goobstation.Shared/Vehicles/SharedVehicleSystem.cs
{Content.Shared,Content.Goobstation.Shared}/**/*.cs

📄 CodeRabbit inference engine (AGENTS.md)

{Content.Shared,Content.Goobstation.Shared}/**/*.cs: Put main shared data, shared events, networked state, and predicted logic in Content.Shared/; use Content.Goobstation.Shared/ only for code that belongs to that existing module path.
Do not make shared projects depend on client-only or server-only projects.
Predicted systems and their relevant components belong in Content.Shared/. Shared predicted components should use NetworkedComponent, AutoGenerateComponentState, and AutoNetworkedField where appropriate.

Files:

  • Content.Shared/Throwing/ThrowingSystem.cs
  • Content.Shared/Movement/Systems/MovementModStatusSystem.cs
  • Content.Shared/_DV/Construction/UpgradeKitSystem.cs
  • Content.Shared/SubFloor/SharedTrayScannerSystem.cs
  • Content.Shared/Blocking/BlockingSystem.cs
  • Content.Goobstation.Shared/Vehicles/SharedVehicleSystem.cs
Content.Shared/**/*.cs

⚙️ CodeRabbit configuration file

Content.Shared/**/*.cs: Review Content.Shared as shared gameplay and prediction code:

  • Shared owns replicated state, shared events, prediction, and data both client and server must understand.
  • Flag client-only or server-only dependencies in Shared.
  • For local-player actions, verify prediction support instead of accepting delayed server-only UX.
  • When state is networked, check NetworkedComponent, AutoGenerateComponentState, AutoNetworkedField, and Dirty/DirtyField usage.
  • Prefer entity-system APIs and Entity<T?> / Resolve patterns over ad-hoc tuples or old-style access.
  • Flag EntityUid.Invalid used as a "missing" sentinel.

Files:

  • Content.Shared/Throwing/ThrowingSystem.cs
  • Content.Shared/Movement/Systems/MovementModStatusSystem.cs
  • Content.Shared/_DV/Construction/UpgradeKitSystem.cs
  • Content.Shared/SubFloor/SharedTrayScannerSystem.cs
  • Content.Shared/Blocking/BlockingSystem.cs
{Content.Client,Content.Goobstation.Client,Content.Goobstation.UIKit}/**/*.{cs,xaml}

📄 CodeRabbit inference engine (.cursor/rules/ss14-ui.mdc)

{Content.Client,Content.Goobstation.Client,Content.Goobstation.UIKit}/**/*.{cs,xaml}: Follow SS14 naming conventions when editing UI, XAML, and client-state files
Maintain upstream compatibility when editing SS14 client code
Follow SS14 UI and BUI conventions when implementing UI components and behavior-driven user interfaces

Files:

  • Content.Client/_White/ItemSlotRenderer/ItemSlotRendererSystem.cs
  • Content.Client/RoundEnd/RoundEndSummaryWindow.cs
{Content.Client,Content.Goobstation.Client,Content.Goobstation.UIKit}/**/*.cs

📄 CodeRabbit inference engine (.cursor/rules/ss14-ui.mdc)

{Content.Client,Content.Goobstation.Client,Content.Goobstation.UIKit}/**/*.cs: Apply SS14 prediction patterns when working with predicted client-state in SS14 client code
Follow SS14 localization code practices in UI and client-state implementations

Put main client-only visuals, overlays, XAML, and BUI front-ends in Content.Client/; use Content.Goobstation.Client/ or Content.Goobstation.UIKit/ only when matching existing references and ownership.

Files:

  • Content.Client/_White/ItemSlotRenderer/ItemSlotRendererSystem.cs
  • Content.Client/RoundEnd/RoundEndSummaryWindow.cs
{**/_Orion,**/_Goobstation,**/_EinsteinEngines,**/_Shitmed,**/_DV,**/_NF,**/_Mono,**/_RMC14,**/_White}/**/*.cs

📄 CodeRabbit inference engine (AGENTS.md)

For Orion-only behavior, prefer existing _Orion folders when they exist. This repository also contains inherited/vendor-specific trees such as _Goobstation, _EinsteinEngines, _Shitmed, _DV, _NF, _Mono, _RMC14, _White, and others; extend the tree that already owns the feature instead of assuming all fork code belongs under _Orion.

Files:

  • Content.Client/_White/ItemSlotRenderer/ItemSlotRendererSystem.cs
  • Content.Shared/_DV/Construction/UpgradeKitSystem.cs
  • Content.Server/_Shitmed/ItemSwitch/ItemSwitchSystem.cs
Content.Client/**/*.cs

⚙️ CodeRabbit configuration file

Content.Client/**/*.cs: Review Content.Client for presentation-only behavior:

  • Keep authority out of the client.
  • Prefer reading already-networked component state instead of duplicating BUI state unless the pattern requires it.
  • Flag missing localization for player-visible text.
  • Prefer nearby established UI and visual patterns over custom one-off structures.

Files:

  • Content.Client/_White/ItemSlotRenderer/ItemSlotRendererSystem.cs
  • Content.Client/RoundEnd/RoundEndSummaryWindow.cs
Resources/**/*.ftl

📄 CodeRabbit inference engine (.cursor/rules/ss14-resources.mdc)

Follow SS14 localization string standards and formatting rules

Files:

  • Resources/Locale/ru-RU/_Shitmed/surgery/status.ftl
**/*.ftl

📄 CodeRabbit inference engine (AGENTS.md)

Use specific kebab-case localization IDs.

Files:

  • Resources/Locale/ru-RU/_Shitmed/surgery/status.ftl
Resources/Locale/**/*.ftl

📄 CodeRabbit inference engine (AGENTS.md)

Resources/Locale/**/*.ftl: Add or update FTL entries under Resources/Locale/, starting with en-US; add/update matching ru-RU entries when the same feature already maintains Russian locale or the change is Orion-facing and you can do so without guessing translations.
Use specific kebab-case localization IDs.

Files:

  • Resources/Locale/ru-RU/_Shitmed/surgery/status.ftl

⚙️ CodeRabbit configuration file

Resources/Locale/**/*.ftl: Treat localization as mandatory:

  • Every player-facing string should be localized.
  • Prefer specific kebab-case keys.
  • Flag raw strings in code or gameplay/resource changes that are missing locale updates.
  • Check consistency when prototype names, descriptions, markings, or UI text changed.

Files:

  • Resources/Locale/ru-RU/_Shitmed/surgery/status.ftl
🧠 Learnings (11)
📚 Learning: 2026-06-29T05:34:31.137Z
Learnt from: Alazell
Repo: ArcaneSS14/arcane-station PR: 99
File: Resources/Prototypes/Entities/Clothing/Belt/belts.yml:973-974
Timestamp: 2026-06-29T05:34:31.137Z
Learning: В SS14 YAML-прототипах для компонента `Storage` поле `grid` задаёт **включительные** координаты прямоугольника. При подсчёте вместимости учитывайте оффсет-инклюзивность: прямоугольник `x1,y1,x2,y2` означает размеры `width = (x2 - x1 + 1)` и `height = (y2 - y1 + 1)`, а число ячеек `width * height`. Например, `0,0,0,0` = 1 ячейка, а `0,0,6,2` = (6-0+1)*(2-0+1)=7*3=21. При ревью файлов наподобие `Resources/Prototypes/Entities/Clothing/Belt/belts.yml` не интерпретируйте `x2/y2` как эксклюзивные (чтобы не ошибиться на 1 в ширине/высоте).

Applied to files:

  • Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml
  • Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml
  • Resources/Prototypes/GameRules/subgamemodes.yml
  • Resources/Prototypes/_Goobstation/GameRules/roundstart.yml
  • Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml
  • Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml
  • Resources/Prototypes/_Goobstation/Entities/Objects/Vehicles/clowncar.yml
  • Resources/Prototypes/Stacks/Specific/medical.yml
  • Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Robotics/borg_modules.yml
  • Resources/Prototypes/Body/Prototypes/Specific/smartcorgi.yml
📚 Learning: 2026-07-29T08:12:37.068Z
Learnt from: UmbiMax
Repo: ArcaneSS14/arcane PR: 153
File: Resources/Prototypes/_Orion/Entities/Objects/Tools/energy_dome.yml:137-137
Timestamp: 2026-07-29T08:12:37.068Z
Learning: In ArcaneSS14 prototype YAMLs, the YAML field that disables synchronization for `Content.Shared.Power.Components.BatteryComponent` is spelled exactly `netsync` (all lowercase), not `netSync`. For prototypes derived from `PowerNetworkBattery` (including `BaseAPC`, `CoreSubstation`, `BaseSMES`, `TeslaCoil`), set `netsync: false`; otherwise `Content.Server.Power.EntitySystems.BatterySystem.OnStartup` can throw `DebugAssertException` for a predicted battery connected to a power net. For `Resources/Prototypes/_Orion/Entities/Objects/Tools/energy_dome.yml`, ensure `EnergyDomeWiredTest` uses `netsync: false`.

Applied to files:

  • Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml
  • Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml
  • Resources/Prototypes/GameRules/subgamemodes.yml
  • Resources/Prototypes/_Goobstation/GameRules/roundstart.yml
  • Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml
  • Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml
  • Resources/Prototypes/_Goobstation/Entities/Objects/Vehicles/clowncar.yml
  • Resources/Prototypes/Stacks/Specific/medical.yml
  • Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Robotics/borg_modules.yml
  • Resources/Prototypes/Body/Prototypes/Specific/smartcorgi.yml
📚 Learning: 2026-07-07T17:55:50.395Z
Learnt from: UmbiMax
Repo: ArcaneSS14/arcane-old PR: 101
File: Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs:192-194
Timestamp: 2026-07-07T17:55:50.395Z
Learning: When reviewing ArcaneSS14/arcane-old changes related to `MobState` thresholds/visuals/actions, treat the `Critical` enum value as requiring explicit mappings (it no longer implicitly behaves as an alias of `SoftCritical`). If any YAML prototype `thresholds:` (for `MobThresholdsComponent.Thresholds`) uses the bare `Critical` key (e.g., `MobThresholdsComponent.Thresholds: { Critical: ... }` for entities like `MobMouseCancer`), ensure the prototype (or referenced config) provides corresponding explicit `StateAlertDict` entries and any required `MobStateActions.actions` and `DamageStateVisuals.states` mappings for `Critical`. Otherwise, review should expect failures because the default `StateAlertDict` in `MobThresholdsComponent.cs` typically maps only `Alive`, `SoftCritical`, `HardCritical`, and `Dead`—not `Critical`.

Applied to files:

  • Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml
  • Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml
  • Resources/Prototypes/GameRules/subgamemodes.yml
  • Resources/Prototypes/_Goobstation/GameRules/roundstart.yml
  • Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml
  • Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml
  • Resources/Prototypes/_Goobstation/Entities/Objects/Vehicles/clowncar.yml
  • Resources/Prototypes/Stacks/Specific/medical.yml
  • Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Robotics/borg_modules.yml
  • Resources/Prototypes/Body/Prototypes/Specific/smartcorgi.yml
📚 Learning: 2026-07-28T15:44:38.498Z
Learnt from: UmbiMax
Repo: ArcaneSS14/arcane PR: 152
File: Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/pistol.yml:69-72
Timestamp: 2026-07-28T15:44:38.498Z
Learning: В ArcaneSS14/arcane не выполняйте форматирующие перестановки полей (в т.ч. изменение порядка полей) в upstream-файлах: делайте это только если правка добавляет Arcane-специфичную сущность или напрямую требуется для функциональности. Иначе сохраняйте upstream-порядок полей, даже если он противоречит локальным рекомендациям, чтобы не создавать лишние diff и конфликты при upstream-синхронизации. Исключение: допустимо переупорядочивание/форматирование в `Resources/Prototypes/_Arcane` (и только там).

Applied to files:

  • Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml
  • Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml
  • Resources/Prototypes/GameRules/subgamemodes.yml
  • Resources/Prototypes/_Goobstation/GameRules/roundstart.yml
  • Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml
  • Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml
  • Resources/Prototypes/_Goobstation/Entities/Objects/Vehicles/clowncar.yml
  • Resources/Prototypes/Stacks/Specific/medical.yml
  • Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Robotics/borg_modules.yml
  • Resources/Prototypes/Body/Prototypes/Specific/smartcorgi.yml
📚 Learning: 2026-06-04T21:17:36.815Z
Learnt from: MataVsn
Repo: ArcaneSS14/arcane-station PR: 31
File: Content.Client/Fluids/PuddleSystem.cs:41-41
Timestamp: 2026-06-04T21:17:36.815Z
Learning: In this repo (ArcaneSS14/arcane-station), reviewers should use the Arcane inline fork-edit marker naming for single-line edits outside `_Arcane` paths: prefer `// Arcane` or `// Arcane-edit` and do not use `// Orion` for this purpose. Repo-specific AGENTS guidance that mentions `// Orion` does not apply to these single-line changes; only `_Arcane` paths follow the applicable different convention.

Applied to files:

  • Content.Server/Holosign/HolosignSystem.cs
  • Content.Shared/Throwing/ThrowingSystem.cs
  • Content.Shared/Movement/Systems/MovementModStatusSystem.cs
  • Content.Client/_White/ItemSlotRenderer/ItemSlotRendererSystem.cs
  • Content.Shared/_DV/Construction/UpgradeKitSystem.cs
  • Content.Shared/SubFloor/SharedTrayScannerSystem.cs
  • Content.Server/Electrocution/ElectrocutionSystem.cs
  • Content.Client/RoundEnd/RoundEndSummaryWindow.cs
  • Content.Shared/Blocking/BlockingSystem.cs
  • Content.Goobstation.Shared/Vehicles/SharedVehicleSystem.cs
  • Content.Server/_Shitmed/ItemSwitch/ItemSwitchSystem.cs
  • Content.Server/Dragon/DragonSystem.cs
📚 Learning: 2026-06-28T19:37:26.732Z
Learnt from: MataVsn
Repo: ArcaneSS14/arcane-station PR: 96
File: Content.Client/IoC/ClientContentIoC.cs:151-151
Timestamp: 2026-06-28T19:37:26.732Z
Learning: For ArcaneSS14/arcane-station C# files whose path is outside any `_Arcane` directories, avoid adding an inline fork marker (e.g., `// Arcane` or `// Arcane-edit`) to `using` directives when the imported namespace already clearly indicates the `_Arcane` fork. For example, if the `using` namespace itself contains `_Arcane` (such as `using Content.Client._Arcane.ERP.Preferences;`), the `// Arcane`/`// Arcane-edit` marker is unnecessary.

Applied to files:

  • Content.Server/Holosign/HolosignSystem.cs
  • Content.Shared/Throwing/ThrowingSystem.cs
  • Content.Shared/Movement/Systems/MovementModStatusSystem.cs
  • Content.Client/_White/ItemSlotRenderer/ItemSlotRendererSystem.cs
  • Content.Shared/_DV/Construction/UpgradeKitSystem.cs
  • Content.Shared/SubFloor/SharedTrayScannerSystem.cs
  • Content.Server/Electrocution/ElectrocutionSystem.cs
  • Content.Client/RoundEnd/RoundEndSummaryWindow.cs
  • Content.Shared/Blocking/BlockingSystem.cs
  • Content.Goobstation.Shared/Vehicles/SharedVehicleSystem.cs
  • Content.Server/_Shitmed/ItemSwitch/ItemSwitchSystem.cs
  • Content.Server/Dragon/DragonSystem.cs
📚 Learning: 2026-07-04T03:37:47.373Z
Learnt from: UmbiMax
Repo: ArcaneSS14/arcane-station PR: 101
File: Content.Goobstation.Server/Chemistry/EntitySystems/EnergyReagentDispenserSystem.cs:59-59
Timestamp: 2026-07-04T03:37:47.373Z
Learning: In ArcaneSS14/arcane-station, C# source files must never have fork-edit markers (e.g., comments like `// Arcane`, `// Arcane-edit`, `// Orion`) on `using` directives. During code review, do not flag existing markers on any `using` line and do not suggest adding/removing them based on `_Arcane` folder placement or whether the imported namespace contains `_Arcane`—this rule applies universally to all `using` statements.

Applied to files:

  • Content.Server/Holosign/HolosignSystem.cs
  • Content.Shared/Throwing/ThrowingSystem.cs
  • Content.Shared/Movement/Systems/MovementModStatusSystem.cs
  • Content.Client/_White/ItemSlotRenderer/ItemSlotRendererSystem.cs
  • Content.Shared/_DV/Construction/UpgradeKitSystem.cs
  • Content.Shared/SubFloor/SharedTrayScannerSystem.cs
  • Content.Server/Electrocution/ElectrocutionSystem.cs
  • Content.Client/RoundEnd/RoundEndSummaryWindow.cs
  • Content.Shared/Blocking/BlockingSystem.cs
  • Content.Goobstation.Shared/Vehicles/SharedVehicleSystem.cs
  • Content.Server/_Shitmed/ItemSwitch/ItemSwitchSystem.cs
  • Content.Server/Dragon/DragonSystem.cs
📚 Learning: 2026-07-28T16:30:37.419Z
Learnt from: UmbiMax
Repo: ArcaneSS14/arcane PR: 133
File: Content.Server/Speech/EntitySystems/StutteringSystem.cs:28-29
Timestamp: 2026-07-28T16:30:37.419Z
Learning: При внесении одиночных fork-правок в репозитории ArcaneSS14/arcane вне путей `_Arcane` добавляйте в конец каждой изменённой строки комментарий-маркер `// Arcane-Edit`. Это нужно для упрощения последующего поиска/обновления подобных правок при код-ревью и синхронизациях.

Applied to files:

  • Content.Server/Holosign/HolosignSystem.cs
  • Content.Shared/Throwing/ThrowingSystem.cs
  • Content.Shared/Movement/Systems/MovementModStatusSystem.cs
  • Content.Client/_White/ItemSlotRenderer/ItemSlotRendererSystem.cs
  • Content.Shared/_DV/Construction/UpgradeKitSystem.cs
  • Content.Shared/SubFloor/SharedTrayScannerSystem.cs
  • Content.Server/Electrocution/ElectrocutionSystem.cs
  • Content.Client/RoundEnd/RoundEndSummaryWindow.cs
  • Content.Shared/Blocking/BlockingSystem.cs
  • Content.Goobstation.Shared/Vehicles/SharedVehicleSystem.cs
  • Content.Server/_Shitmed/ItemSwitch/ItemSwitchSystem.cs
  • Content.Server/Dragon/DragonSystem.cs
📚 Learning: 2026-07-01T17:19:52.100Z
Learnt from: UmbiMax
Repo: ArcaneSS14/arcane-station PR: 101
File: Content.Shared/Mobs/MobState.cs:0-0
Timestamp: 2026-07-01T17:19:52.100Z
Learning: When reviewing ArcaneSS14/arcane-station C# code, ensure all usages of the `MobState` enum (`MobState.Dead`, `MobState.SoftCritical`, `MobState.HardCritical`, `MobState.Critical`) reference the enum members symbolically rather than via hardcoded numeric literals or casts from numeric values (e.g., avoid patterns like `(MobState)4` or `MobState` comparisons to `byte`/`int` literals). Treat direct numeric usage as a code-level dependency that should be flagged. Also avoid persisting `MobState` numeric values in `Content.Server.Database` or `Content.Replay`; if persistence/replay of the raw byte value is introduced, renumbering the `[NetSerializable] MobState : byte` enum becomes a data-compatibility risk. Renumbering the enum is compile-safe as long as there are no numeric literal dependencies, but the remaining risk is network wire-protocol mismatch if mixed old/new client-server builds are running during a rolling update—coordinate deployment to maintain net compatibility.

Applied to files:

  • Content.Server/Holosign/HolosignSystem.cs
  • Content.Shared/Throwing/ThrowingSystem.cs
  • Content.Shared/Movement/Systems/MovementModStatusSystem.cs
  • Content.Client/_White/ItemSlotRenderer/ItemSlotRendererSystem.cs
  • Content.Shared/_DV/Construction/UpgradeKitSystem.cs
  • Content.Shared/SubFloor/SharedTrayScannerSystem.cs
  • Content.Server/Electrocution/ElectrocutionSystem.cs
  • Content.Client/RoundEnd/RoundEndSummaryWindow.cs
  • Content.Shared/Blocking/BlockingSystem.cs
  • Content.Goobstation.Shared/Vehicles/SharedVehicleSystem.cs
  • Content.Server/_Shitmed/ItemSwitch/ItemSwitchSystem.cs
  • Content.Server/Dragon/DragonSystem.cs
📚 Learning: 2026-07-03T18:41:44.286Z
Learnt from: UmbiMax
Repo: ArcaneSS14/arcane-station PR: 101
File: Content.Shared/_Orion/EntityEffects/Effects/Special/ChemConvermol.cs:0-0
Timestamp: 2026-07-03T18:41:44.286Z
Learning: In ArcaneSS14/arcane-station, the hardcoded 1.2f base used by RefreshPartsEvent.GetLinearMultiplier(tier, step, min, max) (Math.Clamp(1.2f - tier * step, min, max)) is an intentional repo-wide balance convention: callers typically pass max: 1.2f, so a tier-1 part yields a 10% penalty (e.g., GetLinearMultiplier(tier: 1, step: 0.1f, min: 0.5f, max: 1.2f) => 1.1), while tier-2 is the neutral baseline. Do not flag the hardcoded 1.2f as a bug when reviewing shared multiplier/linear scaling usage. If a specific system needs a tier-1 baseline that produces 0% penalty instead, implement a locally-scoped formula in that system (e.g., adjust the constant to match the desired neutral baseline) rather than changing the shared helper, since modifying it would affect all callers’ balance.

Applied to files:

  • Content.Server/Holosign/HolosignSystem.cs
  • Content.Shared/Throwing/ThrowingSystem.cs
  • Content.Shared/Movement/Systems/MovementModStatusSystem.cs
  • Content.Shared/_DV/Construction/UpgradeKitSystem.cs
  • Content.Shared/SubFloor/SharedTrayScannerSystem.cs
  • Content.Server/Electrocution/ElectrocutionSystem.cs
  • Content.Shared/Blocking/BlockingSystem.cs
  • Content.Server/_Shitmed/ItemSwitch/ItemSwitchSystem.cs
  • Content.Server/Dragon/DragonSystem.cs
📚 Learning: 2026-06-28T12:09:12.409Z
Learnt from: MataVsn
Repo: ArcaneSS14/arcane-station PR: 96
File: Content.Shared/Humanoid/HumanoidVisualLayers.cs:65-67
Timestamp: 2026-06-28T12:09:12.409Z
Learning: In ArcaneSS14/arcane-station, for any C# enum annotated with `[NetSerializable]` in shared code, treat the enum member order/implicit underlying numeric values as part of the network wire-protocol. During code review, disallow inserting new enum members in the middle (or otherwise changing existing members’ numeric values), since it breaks network compatibility. Instead, add new members only at the end or explicitly pin stable values by assigning numeric literals to members.

Applied to files:

  • Content.Shared/Throwing/ThrowingSystem.cs
  • Content.Shared/Movement/Systems/MovementModStatusSystem.cs
  • Content.Shared/_DV/Construction/UpgradeKitSystem.cs
  • Content.Shared/SubFloor/SharedTrayScannerSystem.cs
  • Content.Shared/Blocking/BlockingSystem.cs
🔇 Additional comments (20)
Content.Shared/Movement/Systems/MovementModStatusSystem.cs (1)

182-182: LGTM!

Resources/Prototypes/GameRules/subgamemodes.yml (1)

34-34: LGTM!

Resources/Prototypes/_Goobstation/GameRules/roundstart.yml (1)

424-424: LGTM!

Resources/Prototypes/_Goobstation/Entities/Objects/Vehicles/clowncar.yml (1)

36-36: LGTM!

Content.Client/RoundEnd/RoundEndSummaryWindow.cs (1)

89-89: LGTM!

Resources/Locale/ru-RU/_Shitmed/surgery/status.ftl (1)

1-3: LGTM!

Resources/Prototypes/Stacks/Specific/medical.yml (2)

11-11: LGTM!

Also applies to: 19-19, 27-27, 35-35, 43-43, 51-51, 59-59


11-59: 🎯 Functional Correctness

Проверьте оба YAML-файла перед слиянием.

  • Resources/Prototypes/Stacks/Specific/medical.yml#L11-L59: запустите YAML-линер.
  • Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Robotics/borg_modules.yml#L89-L115: запустите YAML-линер.

Команда: dotnet run --project Content.YAMLLinter/Content.YAMLLinter.csproj -c DebugOpt.

As per path instructions, изменённые YAML-ресурсы необходимо валидировать указанным линтером.

Source: Path instructions

Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Robotics/borg_modules.yml (1)

89-89: LGTM!

Also applies to: 115-115

Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml (1)

252-252: 🎯 Functional Correctness

Проверьте область наследования Grabbable.

Компонент добавлен в BaseBorgChassisNotIonStormable. Поэтому его получают BaseBorgChassis, BaseXenoborgChassis и их наследники. Если grabbable-поведение требуется только обычным borg, перенесите компонент в более узкий прототип. Если область намеренная, добавьте проверку взаимодействий с xenoborg.

Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml (1)

83-83: LGTM!

Resources/Prototypes/Body/Prototypes/Specific/smartcorgi.yml (1)

10-22: 🗄️ Data Integrity & Integration

Определения связанных частей тела уже есть.

LeftHandSmartCorgi и RightHandSmartCorgi определены в Resources/Prototypes/Body/Parts/animal.yml и используют стандартные родителские прототипы BaseLeftHand / BaseRightHand, поэтому ссылки из SmartCorgi корректны.

Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml (1)

18-18: 🎯 Functional Correctness

Проверьте, подключён ли TileFrictionController к прототипам.

TileFrictionModifier зарегистрирован в shared-сборке, значение по умолчанию — 0f, а TileFrictionController применит его к динамическим телам и KinematicControllers. Осталось проверить подключение этого контроллера к Singularity и BaseEnergyBall, если оно не реализовано в родительских/стартовых прототипах.

Content.Goobstation.Shared/Vehicles/SharedVehicleSystem.cs (1)

59-77: LGTM!

Content.Client/_White/ItemSlotRenderer/ItemSlotRendererSystem.cs (1)

118-128: LGTM!

Content.Server/Holosign/HolosignSystem.cs (1)

76-76: LGTM!

Content.Shared/Blocking/BlockingSystem.cs (1)

172-186: LGTM!

Content.Shared/Throwing/ThrowingSystem.cs (1)

155-156: LGTM!

Content.Shared/SubFloor/SharedTrayScannerSystem.cs (1)

36-42: LGTM!

Content.Server/_Shitmed/ItemSwitch/ItemSwitchSystem.cs (1)

47-47: 🗄️ Data Integrity & Integration

Замечание не нужно.

Кортеж (uid, component) соответствует Entity<BatteryComponent?>, а GetRemainingUses((uid, battery), cost) возвращает GetCharge((uid, battery)) / cost до округления.

Comment thread Content.Server/Dragon/DragonSystem.cs
Comment thread Content.Server/Electrocution/ElectrocutionSystem.cs
Comment thread Content.Shared/_DV/Construction/UpgradeKitSystem.cs Outdated
@UmbiMax

UmbiMax commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Уже так и думал сделать. Можешь отдельно заносить каждый PR с фиксами сюда, если на губы не заносят пачкой.

@ReWAFFlution

Copy link
Copy Markdown
Contributor Author

Ладно я не нашёл...
По факту можно мерджить

@ReWAFFlution ReWAFFlution changed the title [Fix] Upstream fix Part 1 [WIP] [Fix] Upstream fix Part 1 Jul 31, 2026
@UmbiMax

UmbiMax commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

@coderabbitai Подробно напиши все причины провала теста интегралок с разбором.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ArcaneSS14/arcane"
pr="161"

echo '=== PR checks ==='
gh pr checks "$pr" --repo "$repo" --required=false || true

echo '=== PR workflow runs ==='
gh run list --repo "$repo" --branch "$(gh pr view "$pr" --repo "$repo" --json headRefName --jq .headRefName)" --limit 20 \
  --json databaseId,name,status,conclusion,event,headSha,createdAt,updatedAt,url

echo '=== Failed jobs and logs for relevant failed runs ==='
gh run list --repo "$repo" --branch "$(gh pr view "$pr" --repo "$repo" --json headRefName --jq .headRefName)" --limit 20 \
  --json databaseId,name,conclusion \
  --jq '.[] | select(.conclusion == "failure") | [.databaseId, .name] | `@tsv`' |
while IFS=$'\t' read -r run_id run_name; do
  echo "----- RUN ${run_id}: ${run_name} -----"
  gh run view "$run_id" --repo "$repo" --json jobs \
    --jq '.jobs[] | select(.conclusion == "failure") | {name, databaseId, startedAt, completedAt, steps}'
  gh run view "$run_id" --repo "$repo" --log-failed || true
done

Length of output: 10893


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ArcaneSS14/arcane"
run_id="30629301734"
job_id="91151709493"

echo '=== Run metadata ==='
gh api "repos/$repo/actions/runs/$run_id" \
  --jq '{status, conclusion, event, run_attempt, created_at, updated_at, run_started_at, head_sha, actor: .actor.login, triggering_actor: .triggering_actor.login, cancel_url, workflow_url}'

echo '=== Job metadata ==='
gh api "repos/$repo/actions/jobs/$job_id" \
  --jq '{name, status, conclusion, started_at, completed_at, check_run_url, steps}'

echo '=== Job log archive listing and matching excerpts ==='
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT
if gh api "repos/$repo/actions/jobs/$job_id/logs" > "$tmp/job-logs.zip"; then
  unzip -l "$tmp/job-logs.zip" || true
  unzip -p "$tmp/job-logs.zip" > "$tmp/job.log" || true
  rg -n -i -C 8 'Run Content\.IntegrationTests|error|failed|fail:|exception|timeout|cancel|killed|hang' "$tmp/job.log" || true
else
  echo 'Could not retrieve job log archive.'
fi

echo '=== Workflow files with integration-test or timeout configuration ==='
fd -t f -e yml -e yaml .github . 2>/dev/null | while read -r file; do
  if rg -q -i 'Content\.IntegrationTests|timeout-minutes|concurrency' "$file"; then
    echo "----- $file -----"
    rg -n -i -C 4 'Content\.IntegrationTests|timeout-minutes|concurrency|cancel-in-progress' "$file"
  fi
done

echo '=== Integration test project and current PR changes that reference it ==='
fd -t f '.*IntegrationTests.*\.(csproj|cs)$' . 2>/dev/null | head -80
git diff --name-status "$(git merge-base origin/master HEAD)" HEAD

Length of output: 7076


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ArcaneSS14/arcane"
pr="161"
run_id="30629301734"
job_id="91151709493"

echo '=== Response to job-log request ==='
gh api -i "repos/$repo/actions/jobs/$job_id/logs" | sed -n '1,80p'

echo '=== Workflow definition at the PR head ==='
gh api "repos/$repo/actions/workflows/275074948" \
  --jq '{name, path, state, created_at, updated_at}'
workflow_path=$(gh api "repos/$repo/actions/workflows/275074948" --jq .path)
echo "Workflow path: $workflow_path"
git show "HEAD:$workflow_path" 2>&1 || true

echo '=== All repository files that configure or invoke integration tests ==='
rg --hidden -n -i -C 4 \
  'Content\.IntegrationTests|Run Content\.IntegrationTests|timeout-minutes|dotnet test.*Integration' \
  .github Content.IntegrationTests 2>/dev/null || true

echo '=== Integration test project ==='
cat -n Content.IntegrationTests/Content.IntegrationTests.csproj

echo '=== All PR commits and associated check suites ==='
gh pr view "$pr" --repo "$repo" --json commits \
  --jq '.commits[] | [.oid, .committedDate, .messageHeadline] | `@tsv`'

echo '=== Debug workflow runs for all PR commits ==='
gh pr view "$pr" --repo "$repo" --json commits --jq '.commits[].oid' |
while read -r sha; do
  echo "----- $sha -----"
  gh api "repos/$repo/actions/runs?head_sha=$sha&event=pull_request&per_page=100" \
    --jq '.workflow_runs[] | select(.name == "Build & Test Debug") | {id, status, conclusion, created_at, updated_at, run_attempt, url}'
done

echo '=== Failed check annotations ==='
gh api "repos/$repo/check-runs/$job_id/annotations?per_page=100" \
  --jq '.[] | {path, start_line, end_line, level, title, message}' || true

Length of output: 236926


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ArcaneSS14/arcane"
current_job="91151709493"
previous_run="30619953289"

dump_job_log() {
  local job_id="$1"
  local output="$2"
  gh api "repos/$repo/actions/jobs/$job_id/logs" > "$output"
  echo "=== $output: test step start/end and cancellation markers ==="
  rg -n -i -C 12 \
    'Run Content\.IntegrationTests|Starting test execution|Passed!|Failed!|Total tests|The operation was canceled|cancelled|canceling|SIGINT|SIGTERM|Process completed|Error:' \
    "$output" || true
  echo '=== Final 160 lines ==='
  tail -n 160 "$output"
}

tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT

dump_job_log "$current_job" "$tmp/current.log"

echo '=== Previous failed workflow jobs ==='
gh api "repos/$repo/actions/runs/$previous_run/jobs?per_page=100" \
  --jq '.jobs[] | {id, name, conclusion, started_at, completed_at, steps}'

gh api "repos/$repo/actions/runs/$previous_run/jobs?per_page=100" \
  --jq '.jobs[] | select(.name == "build (ubuntu-latest)") | .id' |
while read -r job_id; do
  dump_job_log "$job_id" "$tmp/previous.log"
done

Length of output: 54555


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ArcaneSS14/arcane"
job_id="91151709493"

tmp="$(mktemp)"
trap 'rm -f "$tmp"' EXIT
gh api "repos/$repo/actions/jobs/$job_id/logs" > "$tmp"

echo '=== Current integration test failures ==='
rg -n -i -C 5 '^\S+.*\sFailed\s|^.*Failed [A-Za-z_].*\[[0-9<]|^.*Error Message:|^.*Expected:|^.*But was:' "$tmp" || true

echo '=== Current test-host/process termination markers ==='
rg -n -i -C 4 'test host|process crashed|process terminated|test run aborted|death report|shutdown signal|operation was canceled' "$tmp" || true

echo '=== PR paths that could affect the known failed assets ==='
git diff --name-only "$(git merge-base origin/master HEAD)" HEAD |
rg -i 'figurines|smartfridge|locale|locali[sz]|machine.*board|board.*machine|dataset' || true

echo '=== Definitions and recent last-change commits for known failed assets ==='
rg -n -i -C 2 'FigurinesQM|figurines-qm-9|SmartFridgeCircuitboard|SmartFridgeMachineCircuitboard' \
  Resources Content.IntegrationTests 2>/dev/null || true
git log -1 --format='%H%n%ad%n%s' --date=iso-strict -- \
  Resources Content.IntegrationTests || true

Length of output: 51067


@UmbiMax, причина failure состоит из шести ошибок интегралок и внешней остановки GitHub runner.

Что прошло

  • Build Project прошёл.
  • Content.Tests прошёл: 372 passed, 1 skipped.
  • Ошибок компиляции нет.
  • Ошибка Node.js 20 — предупреждение. Она не остановила job.

Ошибки Content.IntegrationTests

1. FillLevelSpritesExist

DrinkKvassGlass задаёт InHandsMaxFillLevels = 3.

RSI Textures/_Orion/Objects/Consumable/Drinks/kvass.rsi не содержит шесть обязательных состояний:

  • inhand-left-fill-1
  • inhand-right-fill-1
  • inhand-left-fill-2
  • inhand-right-fill-2
  • inhand-left-fill-3
  • inhand-right-fill-3

Причина: конфигурация SolutionContainerVisualsComponent требует fill-level sprite states, которых нет в RSI.

Это ошибка прототипа или RSI. PR не меняет kvass-прототип или этот RSI.


2. FailAndStartTest

При загрузке карты отсутствует прототип:

HellfireFreezerMachineCircuitBoard

Лог:

entity_deserializer: Missing prototype for map: HellfireFreezerMachineCircuitBoard

Причина: карта содержит ссылку на machine board, но соответствующий entity prototype отсутствует или был переименован.

Это блокирует корректный запуск тестового preset. PR не меняет Hellfire freezer prototype, карту или machine board с этим ID.


3. TryStopNukeOpsFromConstantlyFailing

Во время запуска NukeOps тест фиксирует ошибки контента. Одна из них:

AirlockHatchSyndicate's state 'open_unlit' does not exist in RSI
Textures/Structures/Doors/Airlocks/Standard/hatch_syndicate.rsi

Причина: door prototype или клиентская door-логика ожидает sprite state open_unlit, но RSI его не содержит.

В том же тесте есть предупреждения при удалении ERP-органов из контейнеров. Они идут через:

EroticOrganSpawnSystem.RemoveEroticOrgans
SharedBodySystem.RemoveOrgan
SharedContainerSystem.Remove
SharedTransformSystem.AttachToGridOrMap

Эти сообщения показывают, что органы пытаются присоединиться к map/grid после удаления из контейнера. В integration environment это считается проблемой логирования и делает тест нестабильным.

PR добавляет Organ в whitelist borg-хирургических модулей, но лог не доказывает связь этой правки с ошибками NukeOps. Нужен отдельный воспроизводимый запуск после исправления базовых контентных ошибок.


4. TestAllConcurrent

Лог:

entity_deserializer: Missing prototype for map: AutolatheHyperConvection

Причина: карта или preset ссылается на AutolatheHyperConvection, но entity prototype отсутствует.

Это отдельная ошибка prototype/map consistency. PR не меняет файл с данным prototype или карту, которая его запрашивает.


5. LocalizedDatasetPrototypeTest.ValidProtoIdsTest

FigurinesQM объявляет восемь строк, но локализация содержит девятую:

LocalizedDataset FigurinesQM with prefix "figurines-qm-" specifies 8 entries,
but a localized string exists with ID figurines-qm-9

Фактические данные:

  • Resources/Prototypes/Datasets/figurines.yml: count равен 8.
  • Resources/Locale/ru-RU/datasets/figurines.ftl: существует figurines-qm-9.

Причина: count в localized dataset не синхронизирован с Fluent-локализацией.

Эта ошибка была и в предыдущем запуске PR. PR меняет только Resources/Locale/ru-RU/_Shitmed/surgery/status.ftl, а не figurines localization.


6. MachineBoardTest.TestMachineBoardHasValidMachine

Лог:

Machine SmartFridge's BoardPrototype is not equal to its corresponding machine board,
SmartFridgeMachineCircuitboard

Expected: "SmartFridgeMachineCircuitboard"
But was: SmartFridgeCircuitboard

Фактические данные:

  • Resources/Prototypes/Entities/Structures/Machines/smartfridge.yml задаёт:
    board: SmartFridgeCircuitboard
  • Есть также _Orion prototype:
    SmartFridgeMachineCircuitboard
    

Причина: существуют два несовпадающих ID machine board. Machine prototype и соответствующий board prototype используют разные ID.

Эта ошибка также была в предыдущем запуске PR. PR не меняет SmartFridge machine или machine board prototypes.

Причина остановки job

В 12:46:41 UTC GitHub Actions записал:

The runner has received a shutdown signal.
The operation was canceled.

Это не NUnit assertion и не timeout теста.

В .github/workflows/build-test-debug.yml нет timeout-minutes. Стандартный job timeout GitHub Actions также не соответствует остановке через 41 минуту.

Workflow содержит:

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

Этот параметр может остановить более старый запуск при появлении нового запуска в той же группе. В доступной истории не видно более нового запуска для final SHA. Поэтому по журналу нельзя однозначно определить источник shutdown. Возможны ручная отмена или внешняя остановка runner.

Итог

  • Текущий статус CI является корректным: integration tests уже содержали реальные ошибки.
  • Runner shutdown дополнительно отменил незавершённый тестовый прогон.
  • Финальный результат тестов неполный. После 12:25:45 UTC dotnet test уже не вывел итоговую сводку.
  • Две ошибки точно существовали до последнего коммита PR: FigurinesQM и SmartFridge.
  • Остальные ошибки не затронуты напрямую изменёнными путями PR. Лог не подтверждает их причинную связь с upstream-правками из этого PR.

You are interacting with an AI system.

@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

RSI Diff Bot; head commit bb2e79a merging into d0a9177
This PR makes changes to 1 or more RSIs. Here is a summary of all changes:

Resources/Textures/Structures/Doors/Airlocks/Standard/hatch_syndicate.rsi

State Old New Status
open_unlit Added

Edit: diff updated after bb2e79a

@UmbiMax

UmbiMax commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

@coderabbitai Как я могу запустить тест Build & Test Debug / build (ubuntu-latest) на своём ПК?

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Build & Test Debug workflow ==='
cat -n .github/workflows/build-test-debug.yml

echo '=== SDK and build configuration files ==='
fd -HI -t f '^(global\.json|.*\.(sln|slnx)|Directory\.(Build|Packages)\.(props|targets))$' . \
  -x sh -c 'echo "----- $1 -----"; cat -n "$1"' sh {}

echo '=== Test project definitions ==='
fd -HI -t f '.*(Tests|IntegrationTests).*\.csproj$' . \
  -x sh -c 'echo "----- $1 -----"; cat -n "$1"' sh {}

Length of output: 18669


@UmbiMax, для наиболее близкого воспроизведения используйте Ubuntu или WSL2 Ubuntu. Workflow запускается на ubuntu-latest, использует .NET SDK 10 и конфигурацию DebugOpt.

  1. Перейдите на commit PR, который проверяет GitHub Actions.
  2. Установите .NET SDK 10.0.100 или более новый SDK 10.0 feature band. global.json разрешает latestFeature.
  3. Выполните команды из корня репозитория:
git submodule update --init --recursive

# Обновляет engine dependency так же, как action space-wizards/submodule-dependency.
# Если submodule уже соответствует PR, команда изменений не внесёт.
git submodule update --init --recursive

cd RobustToolbox
git submodule update --init --recursive
cd ..

dotnet --info
dotnet restore
dotnet build --configuration DebugOpt --no-restore /m

dotnet test --no-build --configuration DebugOpt \
  Content.Tests/Content.Tests.csproj \
  -- NUnit.ConsoleOut=0

DOTNET_gcServer=1 dotnet test --no-build --configuration DebugOpt \
  Content.IntegrationTests/Content.IntegrationTests.csproj \
  -- NUnit.ConsoleOut=0 NUnit.MapWarningTo=Failed

Последняя команда воспроизводит этап Run Content.IntegrationTests, который завершился ошибкой в CI.

На Windows PowerShell задайте переменную окружения так:

$env:DOTNET_gcServer=1
dotnet test --no-build --configuration DebugOpt `
  Content.IntegrationTests/Content.IntegrationTests.csproj `
  -- NUnit.ConsoleOut=0 NUnit.MapWarningTo=Failed

Для сохранения полного лога добавьте перенаправление:

DOTNET_gcServer=1 dotnet test --no-build --configuration DebugOpt \
  Content.IntegrationTests/Content.IntegrationTests.csproj \
  -- NUnit.ConsoleOut=0 NUnit.MapWarningTo=Failed \
  2>&1 | tee integration-tests.log

Важно: workflow также выполняет action space-wizards/submodule-dependency@v0.1.5 на шаге Pull engine updates. Обычный git submodule update не полностью заменяет этот action, если он переключает RobustToolbox на специальный commit. Перед запуском проверьте, что локальный RobustToolbox совпадает с состоянием PR/CI.

You are interacting with an AI system.

@UmbiMax

UmbiMax commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

@coderabbitai Пофиксили. Какие ещё недоработки остались, которые могут провалить тесты?

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Oops, something went wrong! Please try again later. 🐰 💔

@UmbiMax UmbiMax left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Если и решать, то в основном последнее


if (!_physicsQuery.TryComp(entity, out var physics))
if (!_physicsQuery.TryComp(entity, out var physics)
|| !physics.CanCollide || !physics.Hard) // Arcane-Add

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Просто // Arcane


status.Comp.SprintSpeedModifier = sprintSpeedModifier;
status.Comp.WalkSpeedModifier = walkSpeedModifier;
Dirty(status); // Arcane-Edit

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Просто // Arcane

requireAll: true
tags:
- VehicleKey
#- VehicleKey # CorvaxGoob

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тут Arcane-Edit
и решётка в самом начале строчки

@UmbiMax

UmbiMax commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

@coderabbitai В тесте жалуется на inhand спрайты кваса, но имеется лишь icon. Посмотри на другие напитки. Как лучше и быстрее решить данную проблему?

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Kvass references ==='
rg -n -i -C 8 'DrinkKvassGlass|kvass\.rsi|InHandsMaxFillLevels|inhand-left-fill' \
  Resources Content Content.IntegrationTests 2>/dev/null || true

echo '=== Candidate drink prototypes with solution visuals and in-hand configuration ==='
rg -n -i -C 6 'SolutionContainerVisuals|InHandsMaxFillLevels|inhandVisuals|inhand.*fill' \
  Resources/Prototypes 2>/dev/null | head -160 || true

echo '=== Kvass RSI files ==='
fd -HI -t f . Resources | rg -i 'kvass(\.rsi|/)' || true

kvass_meta="$(fd -HI -t f 'meta\.json$' Resources | while read -r f; do
  case "$f" in *'/kvass.rsi/meta.json') echo "$f";; esac
done | head -1)"

if [ -n "$kvass_meta" ]; then
  echo "=== $kvass_meta ==="
  cat -n "$kvass_meta"
fi

echo '=== Metadata of comparable drink RSIs that declare in-hand fill states ==='
fd -HI -t f 'meta\.json$' Resources | while read -r f; do
  if rg -q '"inhand-(left|right)-fill-[1-3]"' "$f"; then
    echo "----- $f -----"
    rg -n '"(icon|inhand-(left|right)-fill-[1-3])"' "$f" | head -24
  fi
done | head -160

Length of output: 1164


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ArcaneSS14/arcane"
pr="161"
sha="$(gh pr view "$pr" --repo "$repo" --json headRefOid --jq '.headRefOid')"
tree="$(mktemp)"
trap 'rm -f "$tree"' EXIT

gh api "repos/$repo/git/trees/$sha?recursive=1" > "$tree"

echo '=== Kvass-related paths ==='
jq -r '.tree[] | select(.type == "blob") | .path' "$tree" | rg -i 'kvass|drink' | head -250

echo '=== Prototype files containing DrinkKvassGlass ==='
jq -r '.tree[] | select(.type == "blob") | .path' "$tree" |
while read -r path; do
  case "$path" in
    Resources/Prototypes/*.yml|Resources/Prototypes/*.yaml)
      if gh api -H 'Accept: application/vnd.github.raw' "repos/$repo/contents/$path?ref=$sha" 2>/dev/null |
          rg -q 'DrinkKvassGlass'; then
        echo "----- $path -----"
        gh api -H 'Accept: application/vnd.github.raw' "repos/$repo/contents/$path?ref=$sha" |
          rg -n -i -C 12 'DrinkKvassGlass|kvass\.rsi|InHandsMaxFillLevels|SolutionContainerVisuals'
      fi
      ;;
  esac
done

echo '=== kvass.rsi metadata ==='
jq -r '.tree[] | select(.type == "blob") | .path' "$tree" |
rg -i '(^|/)kvass\.rsi/meta\.json$' |
while read -r path; do
  echo "----- $path -----"
  gh api -H 'Accept: application/vnd.github.raw' "repos/$repo/contents/$path?ref=$sha" | jq .
done

echo '=== Example drink RSI metadata with in-hand fill states ==='
jq -r '.tree[] | select(.type == "blob") | .path' "$tree" |
rg -i '(^|/)(beer|wine|glass|drink).*\.rsi/meta\.json$' |
head -80 |
while read -r path; do
  raw="$(gh api -H 'Accept: application/vnd.github.raw' "repos/$repo/contents/$path?ref=$sha" 2>/dev/null || true)"
  if printf '%s' "$raw" | rg -q '"inhand-(left|right)-fill-[1-3]"'; then
    echo "----- $path -----"
    printf '%s' "$raw" | jq -r '.states[]?.name' | rg '^(icon|inhand-(left|right)-fill-[1-3])$' || true
    break
  fi
done

Length of output: 1923


@UmbiMax Быстрый и безопасный фикс: отключить fill-level спрайты у кваса, а не рисовать шесть новых состояний.

Ошибка возникает только потому, что у DrinkKvassGlass задано inHandsMaxFillLevels: 3. При таком значении тест требует состояния:

inhand-left-fill-1..3
inhand-right-fill-1..3

Если у kvass.rsi есть только icon, у предмета уже нет визуальной поддержки уровня жидкости в руках. Для такого RSI корректно установить:

- type: SolutionContainerVisuals
  inHandsMaxFillLevels: 0

Либо удалите inHandsMaxFillLevels, если значение 3 задано непосредственно в компоненте SolutionContainerVisuals у DrinkKvassGlass. Если значение наследуется от базового стакана, задайте 0 в прототипе кваса, чтобы переопределить наследование.

Результат:

  • интеграционный тест перестанет ожидать отсутствующие состояния;
  • квас в руках будет показываться одним icon;
  • изменение не требует PNG-спрайтов и не создаёт риск ошибок в meta.json.

Добавлять шесть inhand-*-fill-* состояний стоит только если нужен видимый уровень кваса в руках. Тогда надо сделать две ориентации для каждого из трёх уровней и добавить их в kvass.rsi/meta.json. Это дольше и требует проверки вида предмета в руках.

Проверка репозитория через sandbox дважды не выполнилась, поэтому я не смог подтвердить точный файл и источник наследования. После восстановления доступа проверьте, где задан inHandsMaxFillLevels, и измените именно это место.

You are interacting with an AI system.

@UmbiMax

UmbiMax commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

@coderabbitai Смотри на тест. Сообщи об оставшихся ошибках с вариантами фиксов.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ArcaneSS14/arcane"
pr="161"
sha="$(gh pr view "$pr" --repo "$repo" --json headRefOid --jq .headRefOid)"

echo "=== PR head ==="
echo "$sha"

echo "=== Checks for current PR head ==="
gh pr checks "$pr" --repo "$repo" --required=false || true

echo "=== Build & Test Debug runs for PR head ==="
gh api "repos/$repo/actions/runs?head_sha=$sha&event=pull_request&per_page=100" \
  --jq '.workflow_runs[]
    | select(.name == "Build & Test Debug")
    | {id, status, conclusion, run_attempt, created_at, updated_at, html_url}'

run_id="$(gh api "repos/$repo/actions/runs?head_sha=$sha&event=pull_request&per_page=100" \
  --jq '[.workflow_runs[] | select(.name == "Build & Test Debug")] | sort_by(.created_at) | last | .id // empty')"

if [ -z "$run_id" ]; then
  echo "No Build & Test Debug workflow run found for the current PR head."
  exit 0
fi

echo "=== Jobs for run $run_id ==="
gh api "repos/$repo/actions/runs/$run_id/jobs?per_page=100" \
  --jq '.jobs[] | {id, name, status, conclusion, started_at, completed_at, steps}'

job_id="$(gh api "repos/$repo/actions/runs/$run_id/jobs?per_page=100" \
  --jq '.jobs[]
    | select(.name == "build (ubuntu-latest)")
    | .id' | tail -1)"

if [ -z "$job_id" ]; then
  echo "No build (ubuntu-latest) job found."
  exit 0
fi

tmp="$(mktemp)"
trap 'rm -f "$tmp"' EXIT
gh api "repos/$repo/actions/jobs/$job_id/logs" > "$tmp"

echo "=== Test failure blocks ==="
rg -n -i -C 10 \
  'Failed [A-Za-z0-9_.]+|Error Message:|Expected:|But was:|Missing prototype|does not exist in RSI|specifies [0-9]+ entries|Test Run Failed|Total tests|The runner has received a shutdown signal|operation was canceled' \
  "$tmp" || true

echo "=== Final test summary ==="
rg -n -i -C 5 \
  'Passed!|Failed!|Total tests|Test Run Aborted|Run Content\.IntegrationTests|Process completed' \
  "$tmp" || true

echo "=== Related content definitions in the PR checkout ==="
rg -n -i -C 3 \
  'DrinkKvassGlass|HellfireFreezerMachineCircuitBoard|AirlockHatchSyndicate|AutolatheHyperConvection|FigurinesQM|figurines-qm-9|SmartFridge(Machine)?Circuitboard' \
  Resources Content.IntegrationTests 2>/dev/null || true

Length of output: 60706


@UmbiMax, в текущем запуске осталось 3 ошибки. Content.Tests проходит полностью. Интеграционные тесты завершились с Failed: 3, Passed: 320.

1. FillLevelSpritesExist: квас

DrinkKvassGlass задаёт InHandsMaxFillLevels = 3, но kvass.rsi не содержит состояний:

inhand-left-fill-1..3
inhand-right-fill-1..3

Быстрый фикс: в Resources/Prototypes/_Orion/Entities/Objects/Consumable/Drinks/drinks.yml переопределить inHandsMaxFillLevels: 0 для DrinkKvassGlass либо удалить это поле, если оно задано непосредственно в прототипе.

Это отключит визуальное изменение уровня жидкости в руках. icon продолжит отображаться.

Полный визуальный фикс: добавить шесть PNG-состояний и их описания в Textures/_Orion/Objects/Consumable/Drinks/kvass.rsi/meta.json. Он нужен только если уровень кваса в руках должен быть видимым.

2. TestAllConcurrent: отсутствует ProtolatheHyperConvection

Карта Resources/Maps/_Goobstation/Nonstations/dm01-entryway.yml содержит:

- proto: ProtolatheHyperConvection

Но prototype ProtolatheHyperConvection отсутствует. В Resources/Prototypes/Entities/Structures/Machines/lathe.yml он закомментирован как redundant.

Resources/migration.yml содержит отображение для AutolatheHyperConvection, но не для ProtolatheHyperConvection. Даже добавление mapping не заменяет исправление исходной map YAML для этого теста.

Рекомендуемый фикс: заменить ProtolatheHyperConvection на существующий Protolathe в Resources/Maps/_Goobstation/Nonstations/dm01-entryway.yml.

Нужно также проверить дубликат Resources/Maps/Nonstations/dm01-entryway.yml: он содержит AutolatheHyperConvection, который тоже отсутствует. Текущий тест загрузил _Goobstation-карту, но второй файл создаёт аналогичный риск для карты без префикса.

Альтернатива: вернуть prototype ProtolatheHyperConvection и его machine board. Это больше не соответствует пометкам Orion-Edit: Redundant и вернёт удалённый контент.

3. LocalizedDatasetPrototypeTest.ValidProtoIdsTest: FigurinesQM

В Resources/Prototypes/Datasets/figurines.yml:

id: FigurinesQM
values:
  prefix: figurines-qm-
  count: 8

В Resources/Locale/ru-RU/datasets/figurines.ftl есть дополнительная строка:

figurines-qm-9 = 99% азартных игроков ...

Рекомендуемый фикс: изменить count с 8 на 9.

Это сохраняет существующую русскую реплику и делает её доступной для набора FigurinesQM.

Альтернатива: удалить figurines-qm-9 из всех локалей, если девятая реплика не должна существовать. По текущим данным это менее предпочтительно.

Что исправлено с прошлого запуска

В текущем логе больше нет ошибок для:

  • HellfireFreezerMachineCircuitBoard;
  • AirlockHatchSyndicate и state open_unlit;
  • SmartFridgeCircuitboard / SmartFridgeMachineCircuitboard.

После этих трёх исправлений нужен новый запуск интеграционных тестов. Текущий запуск помечен Test Run Aborted, поэтому он не доказывает отсутствие следующих ошибок после снятия этих блокеров.

You are interacting with an AI system.

@UmbiMax

UmbiMax commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

@coderabbitai На каких картах ещё нет данных ролей. Сразу с готовой вставкой отпиши мне

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Candidate map files ==='
fd -HI -t f -e yml -e yaml . Resources/Maps | sort

echo '=== Required spawn point prototypes ==='
rg -n -A8 -B3 \
  '^\s*id:\s*(SpawnPointAdjutant|SpawnPointCommandMaid|SpawnPointIAA)\s*$' \
  Resources/Prototypes --glob '*.{yml,yaml}' || true

echo '=== Per-map role spawner and bridge beacon audit ==='
while IFS= read -r map; do
  case "$map" in
    *.yml|*.yaml) ;;
    *) continue ;;
  esac

  if ! rg -q '^\s*entities:\s*$' "$map"; then
    continue
  fi

  adj="$(rg -c '^- proto: SpawnPointAdjutant$' "$map" || true)"
  maid="$(rg -c '^- proto: SpawnPointCommandMaid$' "$map" || true)"
  iaa="$(rg -c '^- proto: SpawnPointIAA$' "$map" || true)"
  beacon="$(rg -c '^- proto: DefaultStationBeaconBridge$' "$map" || true)"
  count="$(rg -n '^\s*entityCount:\s*[0-9]+' "$map" | head -1 || true)"
  max_uid="$(rg -o '^\s*- uid: [0-9]+$' "$map" | sed -E 's/.*: //' | sort -n | tail -1 || true)"

  printf '\nMAP=%s\nadjutant=%s command_maid=%s iaa=%s bridge_beacons=%s\n%s\nmax_uid=%s\n' \
    "$map" "$adj" "$maid" "$iaa" "$beacon" "$count" "${max_uid:-none}"

  if [ "$adj" -eq 0 ] || [ "$maid" -eq 0 ] || [ "$iaa" -eq 0 ]; then
    echo '--- bridge beacon blocks ---'
    rg -n -A14 -B2 '^- proto: DefaultStationBeaconBridge$' "$map" || true
    echo '--- existing target spawn points ---'
    rg -n -A12 -B2 '^- proto: SpawnPoint(Adjutant|CommandMaid|IAA)$' "$map" || true
    echo '--- duplicate UIDs ---'
    rg -o '^\s*- uid: [0-9]+$' "$map" |
      sed -E 's/.*: //' | sort -n | uniq -d | head -20 || true
  fi
done < <(fd -HI -t f -e yml -e yaml . Resources/Maps | sort)

Length of output: 19362


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Station-map audit: required spawn points and bridge beacons ==='
python3 - <<'PY'
from pathlib import Path
import re

root = Path("Resources/Maps")
targets = ("SpawnPointAdjutant", "SpawnPointCommandMaid", "SpawnPointIAA")

for path in sorted(root.rglob("*.yml")):
    parts = set(path.parts)
    if parts & {"Dungeon", "Lavaland", "Ruins", "Salvage", "Shuttles", "Test", "Misc", "Nonstations", "InfinityDorms", "Bitrun"}:
        continue

    text = path.read_text(encoding="utf-8")
    if not re.search(r"(?m)^- proto: DefaultStationBeaconBridge$", text):
        continue

    present = {target: bool(re.search(rf"(?m)^- proto: {re.escape(target)}$", text)) for target in targets}
    missing = [target for target, exists in present.items() if not exists]
    if not missing:
        continue

    entity_count = re.search(r"(?m)^\s*entityCount:\s*(\d+)\s*$", text)
    uids = [int(value) for value in re.findall(r"(?m)^\s*- uid: (\d+)\s*$", text)]
    print(f"MAP={path}")
    print(f"MISSING={','.join(missing)}")
    print(f"ENTITY_COUNT={entity_count.group(1) if entity_count else 'none'}")
    print(f"MAX_UID={max(uids) if uids else 'none'}")
    print()
PY

echo '=== Bridge-beacon blocks and insertion context for every incomplete station map ==='
python3 - <<'PY' > /tmp/incomplete_station_maps.txt
from pathlib import Path
import re

root = Path("Resources/Maps")
targets = ("SpawnPointAdjutant", "SpawnPointCommandMaid", "SpawnPointIAA")

for path in sorted(root.rglob("*.yml")):
    parts = set(path.parts)
    if parts & {"Dungeon", "Lavaland", "Ruins", "Salvage", "Shuttles", "Test", "Misc", "Nonstations", "InfinityDorms", "Bitrun"}:
        continue
    text = path.read_text(encoding="utf-8")
    if not re.search(r"(?m)^- proto: DefaultStationBeaconBridge$", text):
        continue
    if any(not re.search(rf"(?m)^- proto: {re.escape(target)}$", text) for target in targets):
        print(path)
PY

while IFS= read -r map; do
  [ -n "$map" ] || continue
  echo "===== $map ====="
  rg -n -A16 -B2 '^- proto: DefaultStationBeaconBridge$' "$map"
  echo '--- header ---'
  sed -n '1,40p' "$map"
  echo '--- existing target spawners ---'
  rg -n -A10 -B2 '^- proto: SpawnPoint(Adjutant|CommandMaid|IAA)$' "$map" || true
  echo '--- highest UIDs ---'
  rg -o '^\s*- uid: [0-9]+$' "$map" | sed -E 's/.*: //' | sort -n | tail -5
done < /tmp/incomplete_station_maps.txt

echo '=== Job spawn test map list and current CI errors ==='
rg -n -C 8 \
  'SpawnPointAdjutant|SpawnPointCommandMaid|SpawnPointIAA|missing.*spawn|spawn.*point|GameMaps' \
  Content.IntegrationTests Resources/Prototypes --glob '*.{cs,yml,yaml}' || true

Length of output: 96125


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Active map IDs in PostMapInitTest ==='
sed -n '110,225p' Content.IntegrationTests/Tests/PostMapInitTest.cs

echo '=== GameMapPrototype definitions for active-map path resolution ==='
rg -n -A18 -B3 \
  '^\s*id:\s*(Aspid|Atlas|Bagel|Barratry|Box|CentComm|Chloris|Cluster|Cog|Core|Delta|Elkridge|Europa|Fland|Kettle|Lambda|Leonid|Loop|Marathon|Meta|Oasis|Omega|Origin|Packed|Plasma|Reach|Relic|Saltern|Serpentcrest|Snowball|Train|WonderBox)\s*$|^\s*mapPath:' \
  Resources/Prototypes --glob '*.{yml,yaml}' || true

echo '=== Ready insertions for incomplete active maps ==='
python3 - <<'PY'
from pathlib import Path
import re

# IDs currently exercised by GameMapsInCurrentPool. The map files are resolved
# from the active _Goobstation/Arcane map variants in this PR checkout.
active = {
    "Amber": Path("Resources/Maps/_Goobstation/amber.yml"),
    "Box": Path("Resources/Maps/_Goobstation/box.yml"),
    "Chloris": Path("Resources/Maps/_Goobstation/chloris.yml"),
    "Core": Path("Resources/Maps/_Goobstation/core.yml"),
    "Delta": Path("Resources/Maps/_Goobstation/delta.yml"),
    "Kettle": Path("Resources/Maps/_Goobstation/kettle.yml"),
    "Lambda": Path("Resources/Maps/_Goobstation/lambda.yml"),
    "Leonid": Path("Resources/Maps/_Goobstation/leonid.yml"),
    "Loop": Path("Resources/Maps/_Goobstation/loop.yml"),
    "Meta": Path("Resources/Maps/_Goobstation/meta.yml"),
    "Omega": Path("Resources/Maps/_Goobstation/omega.yml"),
    "Reach": Path("Resources/Maps/_Goobstation/reach.yml"),
    "Saltern": Path("Resources/Maps/_Goobstation/saltern.yml"),
    "Serpentcrest": Path("Resources/Maps/_Goobstation/serpentcrest.yml"),
    "Train": Path("Resources/Maps/_Goobstation/train.yml"),
}
targets = ("SpawnPointAdjutant", "SpawnPointCommandMaid", "SpawnPointIAA")

for name, path in active.items():
    if not path.exists():
        print(f"SKIP {name}: {path} does not exist")
        continue

    text = path.read_text(encoding="utf-8")
    missing = [x for x in targets if not re.search(rf"(?m)^- proto: {x}$", text)]
    if not missing:
        continue

    match = re.search(
        r"(?m)^- proto: DefaultStationBeaconBridge$\n  entities:\n"
        r"  - uid: \d+\n    components:\n    - type: Transform\n"
        r"(?:      rot: .*\n)?"
        r"      pos: ([^ \n]+)\n      parent: (\d+)",
        text,
    )
    if not match:
        print(f"ERROR {name}: cannot parse first bridge beacon")
        continue

    count_match = re.search(r"(?m)^  entityCount: (\d+)$", text)
    uids = {int(x) for x in re.findall(r"(?m)^  - uid: (\d+)$", text)}
    next_uid = max(uids) + 1
    new_uids = []
    while len(new_uids) < len(missing):
        if next_uid not in uids:
            new_uids.append(next_uid)
        next_uid += 1

    print(f"MAP={name}")
    print(f"PATH={path}")
    print(f"ENTITY_COUNT={count_match.group(1) if count_match else 'none'} -> "
          f"{int(count_match.group(1)) + len(missing) if count_match else 'none'}")
    print(f"MISSING={','.join(missing)}")
    print(f"POS={match.group(1)} PARENT={match.group(2)}")
    print("INSERT:")
    for proto, uid in zip(missing, new_uids):
        print(f"- proto: {proto}")
        print("  entities:")
        print(f"  - uid: {uid}")
        print("    components:")
        print("    - type: Transform")
        print(f"      pos: {match.group(1)}")
        print(f"      parent: {match.group(2)}")
    print()
PY

Length of output: 136660


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

wanted_maps = {
    "Atlas", "Bagel", "Chloris", "Cluster", "Cog", "Delta", "Exo",
    "FlandHighPop", "Kettle", "Leonid", "Loop", "Marathon", "Meta",
    "OasisHighPop", "Omega", "Origin", "Packed", "Reach", "Saltern",
    "Serpentcrest",
}
wanted_jobs = {
    "Adjutant": "SpawnPointAdjutant",
    "CommandMaid": "SpawnPointCommandMaid",
    "IAA": "SpawnPointIAA",
}

# Split every prototype file into YAML entity blocks. All relevant GameMapPrototype
# blocks are flat enough for extracting id, mapPath, and indented availableJobs.
game_maps = {}
for proto_file in Path("Resources/Prototypes").rglob("*.yml"):
    text = proto_file.read_text(encoding="utf-8")
    blocks = re.split(r"(?m)^- type: gameMap\s*$", text)[1:]
    for block in blocks:
        block = block.split("\n- type:", 1)[0]
        id_match = re.search(r"(?m)^  id: ([^\s#]+)", block)
        path_match = re.search(r"(?m)^  mapPath: ([^\s#]+)", block)
        if not id_match or not path_match:
            continue
        map_id = id_match.group(1)
        if map_id not in wanted_maps:
            continue

        jobs = set()
        available = re.search(
            r"(?ms)^        - type: StationJobs\s*\n\s+availableJobs:\s*\n(.*?)(?=^        - type:|\Z)",
            block,
        )
        if available:
            for job in wanted_jobs:
                if re.search(rf"(?m)^\s+{job}:\s*\[", available.group(1)):
                    jobs.add(job)
        game_maps[map_id] = (proto_file, path_match.group(1), jobs)

print("=== Exact active game maps with requested jobs ===")
for map_id in sorted(game_maps):
    proto_file, map_path, jobs = game_maps[map_id]
    print(f"{map_id}: prototype={proto_file}, mapPath={map_path}, jobs={','.join(sorted(jobs)) or 'none'}")

print("\n=== Required map insertions ===")
for map_id in sorted(game_maps):
    proto_file, res_path, jobs = game_maps[map_id]
    if not jobs:
        continue

    path = Path("Resources") / res_path.lstrip("/")
    if not path.exists():
        print(f"ERROR {map_id}: missing map file {path}")
        continue
    text = path.read_text(encoding="utf-8")
    needed = [wanted_jobs[job] for job in sorted(jobs) if not re.search(rf"(?m)^- proto: {wanted_jobs[job]}$", text)]
    if not needed:
        continue

    beacon = re.search(
        r"(?m)^- proto: DefaultStationBeaconBridge$\n  entities:\n"
        r"  - uid: \d+\n    components:\n    - type: Transform\n"
        r"(?:      rot: .*\n)?"
        r"      pos: ([^ \n]+)\n      parent: (\d+)",
        text,
    )
    if not beacon:
        print(f"ERROR {map_id}: missing or unsupported bridge beacon in {path}")
        continue

    count = re.search(r"(?m)^  entityCount: (\d+)$", text)
    uids = {int(uid) for uid in re.findall(r"(?m)^  - uid: (\d+)$", text)}
    uid = max(uids) + 1
    new_uids = []
    while len(new_uids) < len(needed):
        if uid not in uids:
            new_uids.append(uid)
        uid += 1

    print(f"\nMAP={map_id}")
    print(f"PATH={path}")
    print(f"ENTITY_COUNT={count.group(1) if count else 'no entityCount'}"
          + (f" -> {int(count.group(1)) + len(needed)}" if count else ""))
    print(f"BRIDGE_POS={beacon.group(1)} PARENT={beacon.group(2)}")
    print("MISSING=" + ",".join(needed))
    for proto, new_uid in zip(needed, new_uids):
        print(f"- proto: {proto}")
        print("  entities:")
        print(f"  - uid: {new_uid}")
        print("    components:")
        print("    - type: Transform")
        print(f"      pos: {beacon.group(1)}")
        print(f"      parent: {beacon.group(2)}")
PY

Length of output: 8126


@UmbiMax, в текущем GameMapsInCurrentPool остаются три карты. На них доступны все три роли, но отсутствуют их spawn point.

Остальные активные карты либо уже имеют нужные spawn point, либо не включают эти роли в availableJobs. Они не должны провалить проверку PostMapInitTest по этим ролям.

Kettle

Файл: Resources/Maps/_Goobstation/kettle.yml

Измени entityCount:

-  entityCount: 26994
+  entityCount: 26997

Добавь после DefaultStationBeaconBridge:

- proto: SpawnPointAdjutant
  entities:
  - uid: 600001
    components:
    - type: Transform
      pos: 7.5,-11.5
      parent: 2
- proto: SpawnPointCommandMaid
  entities:
  - uid: 600002
    components:
    - type: Transform
      pos: 7.5,-11.5
      parent: 2
- proto: SpawnPointIAA
  entities:
  - uid: 600003
    components:
    - type: Transform
      pos: 7.5,-11.5
      parent: 2

600000 уже занят в карте. Поэтому используются UID 600001..600003.

Leonid

Файл: Resources/Maps/_Goobstation/leonid.yml

Измени entityCount:

-  entityCount: 35968
+  entityCount: 35971

Добавь после DefaultStationBeaconBridge:

- proto: SpawnPointAdjutant
  entities:
  - uid: 35969
    components:
    - type: Transform
      pos: -16.5,12.5
      parent: 2
- proto: SpawnPointCommandMaid
  entities:
  - uid: 35970
    components:
    - type: Transform
      pos: -16.5,12.5
      parent: 2
- proto: SpawnPointIAA
  entities:
  - uid: 35971
    components:
    - type: Transform
      pos: -16.5,12.5
      parent: 2

Loop

Файл: Resources/Maps/_Goobstation/loop.yml

Измени entityCount:

-  entityCount: 18076
+  entityCount: 18079

Добавь после DefaultStationBeaconBridge:

- proto: SpawnPointAdjutant
  entities:
  - uid: 18077
    components:
    - type: Transform
      pos: -22.5,72.5
      parent: 2
- proto: SpawnPointCommandMaid
  entities:
  - uid: 18078
    components:
    - type: Transform
      pos: -22.5,72.5
      parent: 2
- proto: SpawnPointIAA
  entities:
  - uid: 18079
    components:
    - type: Transform
      pos: -22.5,72.5
      parent: 2

You are interacting with an AI system.

@UmbiMax

UmbiMax commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

@coderabbitai Узнай по какой причине не работает ТТС, если кто-то говорит по рации, а также эффекты рации, которые ранее добавили в Content.Shared/Radio/RadioChannelPrototype.cs и Content.Shared/Radio/RadioChannelPrototype.cs + Resources/Prototypes/radio_channels.yml

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Radio and TTS candidate files ==='
fd -HI -t f . Content Resources \
  | rg '(^|/)(RadioChannelPrototype|.*Radio.*System|.*Radio.*Event|.*TTS.*|.*TextToSpeech.*)\.(cs|yml|yaml)$|radio_channels\.yml$' \
  | sort

echo '=== Prototype and radio-channel definitions ==='
for file in \
  Content.Shared/Radio/RadioChannelPrototype.cs \
  Resources/Prototypes/radio_channels.yml
do
  if [ -f "$file" ]; then
    echo "----- $file -----"
    ast-grep outline "$file" --items all --view expanded 2>/dev/null || true
    cat -n "$file"
  fi
done

echo '=== Definitions and uses of RadioChannelPrototype fields ==='
rg -n -i -C 10 \
  'RadioChannelPrototype|radio.*channel|channel.*(sound|effect|color|tts)|TTS|TextToSpeech|Speak.*Radio|Radio.*Speak' \
  Content \
  --glob '*.cs' || true

echo '=== Radio network-event declarations and client handlers ==='
rg -n -i -C 14 \
  '(NetEvent|EntityEvent).*Radio|Radio.*(NetEvent|EntityEvent)|Broadcast.*Radio|Raise.*Radio|Subscribe.*Radio|On.*Radio' \
  Content \
  --glob '*.cs' || true

echo '=== PR changes and recent commits involving radio/TTS ==='
git diff --name-status "$(git merge-base origin/master HEAD)" HEAD -- \
  Content.Shared/Radio Resources/Prototypes/radio_channels.yml Content.Client Content.Server || true
git log --all --oneline -G 'RadioChannelPrototype|radio_channels|TTS|TextToSpeech' -- \
  Content.Shared/Radio Resources/Prototypes/radio_channels.yml Content.Client Content.Server \
  | head -80 || true

Length of output: 1588


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ArcaneSS14/arcane"
pr="161"
sha="$(gh pr view "$pr" --repo "$repo" --json headRefOid --jq .headRefOid)"
echo "PR_HEAD=$sha"

echo '=== Changed files related to radio or TTS ==='
gh api "repos/$repo/pulls/$pr/files?per_page=100" --paginate \
  --jq '.[] | select(.filename | test("(?i)(radio|tts|texttospeech)")) | {filename, status, additions, deletions, patch}'

echo '=== Required files at PR head ==='
for path in \
  Content.Shared/Radio/RadioChannelPrototype.cs \
  Resources/Prototypes/radio_channels.yml
do
  echo "----- $path -----"
  gh api -H 'Accept: application/vnd.github.raw' \
    "repos/$repo/contents/$path?ref=$sha" 2>/dev/null || echo "NOT FOUND"
done

echo '=== GitHub code search: declarations and uses ==='
for query in \
  'repo:ArcaneSS14/arcane RadioChannelPrototype language:C#' \
  'repo:ArcaneSS14/arcane "RadioChannelPrototype" language:C#' \
  'repo:ArcaneSS14/arcane "TextToSpeech" language:C#' \
  'repo:ArcaneSS14/arcane "TTS" language:C#' \
  'repo:ArcaneSS14/arcane "radio_channels.yml"'
do
  echo "----- $query -----"
  gh api "search/code?q=$(printf '%s' "$query" | jq -sRr `@uri`)&per_page=100" \
    --jq '.items[] | {path, html_url}' 2>/dev/null || true
done

echo '=== History of the target files ==='
for path in \
  Content.Shared/Radio/RadioChannelPrototype.cs \
  Resources/Prototypes/radio_channels.yml
do
  echo "----- $path -----"
  gh api "repos/$repo/commits?path=$(printf '%s' "$path" | jq -sRr `@uri`)&sha=$sha&per_page=15" \
    --jq '.[] | {sha: .sha[0:12], date: .commit.author.date, message: .commit.message}' \
    2>/dev/null || true
done

Length of output: 16436


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ArcaneSS14/arcane"
sha="ca579fba181b0069e8bb7f0cf8253240d441bd2c"
radio_commit="a5391bcca954"

echo '=== Files changed by Radio 2.0 ==='
gh api "repos/$repo/commits/$radio_commit" \
  --jq '.files[] | {filename, status, patch}'

echo '=== Radio/TTS-related paths at PR head ==='
tree="$(mktemp)"
trap 'rm -f "$tree"' EXIT
gh api "repos/$repo/git/trees/$sha?recursive=1" > "$tree"

jq -r '.tree[] | select(.type == "blob") | .path' "$tree" \
  | rg -i '(^|/)(radio|headset|tts|texttospeech|speech|chat).*\.(cs|yml|yaml)$|/(radio|headset|tts|texttospeech)/' \
  | sort

echo '=== Read source files changed by Radio 2.0 that still exist at PR head ==='
gh api "repos/$repo/commits/$radio_commit" --jq -r '.files[].filename' \
  | while IFS= read -r path; do
      case "$path" in
        *.cs)
          echo "----- $path -----"
          gh api -H 'Accept: application/vnd.github.raw' \
            "repos/$repo/contents/$path?ref=$sha" 2>/dev/null \
            | nl -ba \
            | sed -n '1,420p' || true
          ;;
      esac
    done

echo '=== Commit metadata and parent diff for the later volume change ==='
gh api "repos/$repo/commits/3c703150fd3a" \
  --jq '{sha, message: .commit.message, files: [.files[] | {filename, patch}]}'

Length of output: 30133


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ArcaneSS14/arcane"
sha="ca579fba181b0069e8bb7f0cf8253240d441bd2c"
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT

paths=(
  "Content.Server/Radio/EntitySystems/HeadsetSystem.cs"
  "Content.Client/_Orion/Radio/RadioBarkAudioSystem.cs"
  "Content.Shared/_Orion/Radio/PlayRadioBarkEvent.cs"
  "Content.Shared/_Art/TTS/TTSRadioPlayEvent.cs"
  "Content.Client/_Art/TTS/TTSSystem.cs"
  "Content.Server/_Art/TTS/TTSSystem.cs"
  "Content.Server/_Art/TTS/TTSManager.cs"
  "Content.Server/Chat/Systems/ChatSystem.cs"
  "Content.Server/Radio/EntitySystems/RadioSystem.cs"
  "Content.Shared/Radio/EntitySystems/SharedHeadsetSystem.cs"
)

for path in "${paths[@]}"; do
  output="$tmp/$(basename "$path")"
  if gh api -H 'Accept: application/vnd.github.raw' \
      "/repos/$repo/contents/$path?ref=$sha" > "$output" 2>/dev/null; then
    echo "===== $path ====="
    rg -n -i -C 18 \
      'OnEntitySpoke|EntitySpokeEvent|OnSendSound|PlayEntity|TTSRadioPlayEvent|PlayRadioBarkEvent|RadioReceiveEvent|Raise.*Event|Subscribe.*Event|Speak|radio' \
      "$output" || true
  else
    echo "===== $path: NOT AVAILABLE ====="
  fi
done

Length of output: 92495


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ArcaneSS14/arcane"
sha="ca579fba181b0069e8bb7f0cf8253240d441bd2c"
tree="$(mktemp)"
trap 'rm -f "$tree"' EXIT
gh api "repos/$repo/git/trees/$sha?recursive=1" > "$tree"

echo '=== Files that can reference radio TTS/bark events or OnSendSound ==='
jq -r '.tree[] | select(.type == "blob") | .path' "$tree" \
  | rg '\.cs$' \
  | while IFS= read -r path; do
      content="$(gh api -H 'Accept: application/vnd.github.raw' \
        "/repos/$repo/contents/$path?ref=$sha" 2>/dev/null || true)"
      if printf '%s' "$content" | rg -q \
        'TTSRadioPlayEvent|PlayRadioBarkEvent|OnSendSound|RadioReceiveRelayEvent|HeadsetRadioReceiveRelayEvent'; then
        echo "===== $path ====="
        printf '%s\n' "$content" \
          | rg -n -C 12 \
            'TTSRadioPlayEvent|PlayRadioBarkEvent|OnSendSound|RadioReceiveRelayEvent|HeadsetRadioReceiveRelayEvent'
      fi
    done

echo '=== Current HeadsetSystem versus Radio 2.0 implementation ==='
for ref in "$sha" "a5391bcca954^" "a5391bcca954"; do
  echo "----- ref=$ref -----"
  gh api -H 'Accept: application/vnd.github.raw' \
    "/repos/$repo/contents/Content.Server/Radio/EntitySystems/HeadsetSystem.cs?ref=$ref" \
    2>/dev/null \
    | rg -n -C 10 'SharedAudioSystem|OnSendSound|DefaultOnSound|OnSpeak|OnEntitySpoke|PlayEntity' \
    || true
done

Length of output: 1424


@UmbiMax, причины две. Они независимы.

1. TTS не передаётся получателям радио

В Content.Server/Radio/EntitySystems/HeadsetSystem.cs метод OnSpeak делает следующее:

_radio.SendRadioMessage(uid, args.Message, args.Channel, component.Headset);
args.Channel = null;

В Content.Server/_Art/TTS/TTSSystem.cs обработчик OnEntitySpoke запускается после RadioSystem и HeadsetSystem:

SubscribeLocalEvent<TTSComponent, EntitySpokeEvent>(
    OnEntitySpoke,
    after: [typeof(RadioSystem), typeof(HeadsetSystem)]);

Он проверяет канал так:

if (args.Channel != null)
    return;

Но HeadsetSystem уже сбросил args.Channel. Поэтому TTSSystem считает радиосообщение обычной локальной репликой и вызывает HandleSay. HandleSay отправляет звук только в Filter.Pvs(uid), то есть рядом с говорящим. Получатели сообщения по рации TTS не получают.

При этом отдельный путь для радио существует:

TTSRadioPlayEventTTSSystem.OnTTSRadioPlayEvent
→ HandleReceiveRadio(...)

Но в подтверждённой цепочке RadioReceiveEvent этот TTSRadioPlayEvent не создаётся.

Нужный фикс

Создавать TTSRadioPlayEvent для каждого получателя, который успешно прошёл RadioReceiveAttemptEvent. Это надо делать в серверном пути доставки радио, рядом с обработкой RadioReceiveEvent.

При этом обработчик OnEntitySpoke не должен запускать локальный HandleSay для уже переданного радиосообщения. Надёжный вариант: сохранить факт радиопередачи в EntitySpokeEvent или обработать TTS до очистки args.Channel, но отправить звук через HandleReceiveRadio, а не через HandleSay.

2. Эффекты рации не вызываются

Content.Shared/Radio/RadioChannelPrototype.cs содержит поле:

[DataField]
public SoundSpecifier? OnSendSound = null;

Resources/Prototypes/radio_channels.yml задаёт onSendSound для каналов.

Но текущий Content.Server/Radio/EntitySystems/HeadsetSystem.cs не содержит:

  • зависимости SharedAudioSystem;
  • чтения args.Channel.OnSendSound;
  • вызова _audio.PlayEntity(...).

Следовательно, onSendSound сейчас является неиспользуемыми данными прототипа.

Ранее Radio 2.0 добавлял код вида:

var sound = args.Channel.OnSendSound ?? DefaultOnSound;
_audio.PlayEntity(sound, uid, uid);

Этот код отсутствует на текущем HEAD. Вероятно, его потеряли при merge/rebase либо перенесли не полностью.

Content.Client/_Orion/Radio/RadioBarkAudioSystem.cs сам по себе проблему не решает. Он воспроизводит звук только после сетевого PlayRadioBarkEvent. Наличие класса не означает, что событие отправляется.

Итог

  • TTS: радио очищает args.Channel раньше обработчика TTS. TTS идёт в локальный PVS вместо получателей рации.
  • Эффекты: OnSendSound определён в прототипах, но текущая серверная система его не использует.
  • Это не регрессия из PR #161. PR не меняет radio/TTS-файлы.

Последняя широкая проверка всех вызовов не выполнилась из-за ошибки доступа к репозиторию. Однако причины выше подтверждаются текущими реализациями HeadsetSystem и TTSSystem.

You are interacting with an AI system.

@ReWAFFlution

Copy link
Copy Markdown
Contributor Author

Уже занимаюсь фиксом ттса в другом пре чтобы макароны не делать

@UmbiMax

UmbiMax commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

@coderabbitai Как будем фиксить проблему? Звук само собой должен быть персональным для каждой сущности. Наушники не динамики.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Radio and audio source maps ==='
for file in \
  Content.Server/Radio/EntitySystems/HeadsetSystem.cs \
  Content.Shared/Radio/RadioChannelPrototype.cs \
  Content.Client/_Orion/Radio/RadioBarkAudioSystem.cs \
  Content.Shared/_Orion/Radio/PlayRadioBarkEvent.cs \
  Content.Shared/_Art/TTS/TTSRadioPlayEvent.cs \
  Content.Client/_Art/TTS/TTSSystem.cs \
  Content.Server/_Art/TTS/TTSSystem.cs
do
  if [ -f "$file" ]; then
    echo "----- OUTLINE: $file -----"
    ast-grep outline "$file" --items all --type class,method --view expanded || true
    echo "----- RELEVANT CONTEXT: $file -----"
    rg -n -C 18 \
      'OnSpeak|OnSendSound|PlayEntity|PlayGlobal|Play.*Event|Raise.*Event|TTSRadioPlayEvent|PlayRadioBarkEvent|RadioReceive|EntitySpokeEvent|Filter|Session' \
      "$file" || true
  else
    echo "MISSING: $file"
  fi
done

echo '=== All uses of the existing radio audio/TTS events ==='
rg -n -C 12 \
  'TTSRadioPlayEvent|PlayRadioBarkEvent|OnSendSound|DefaultOnSound|RadioReceiveAttemptEvent|RadioReceiveEvent' \
  Content --glob '*.cs' || true

echo '=== Examples of client-targeted sound events ==='
rg -n -C 10 \
  'RaiseNetworkEvent\(.*Filter|RaiseNetworkEvent\(.*session|Filter\.SinglePlayer|Filter\.Empty\(\)\.AddPlayer|PlayEntity\(.*Filter|PlayGlobal\(.*Filter' \
  Content.Server Content.Shared --glob '*.cs' | head -500 || true

Length of output: 75257


@UmbiMax, фикс должен быть server-authoritative и адресным по ActorComponent.PlayerSession. Не используем Filter.Pvs, Filter.Broadcast или звук от сущности в мире.

1. Звук отправки канала

RadioChannelPrototype.OnSendSound — это звук отправителю. В HeadsetSystem.OnSpeak надо проигрывать его только игроку, который носит component.Headset.

Порядок:

  1. Проверить ключ и SendWhitelist.
  2. Вызвать _radio.SendRadioMessage(...).
  3. Получить ActorComponent у uid.
  4. Проиграть args.Channel.OnSendSound через Filter.SinglePlayer(actor.PlayerSession).

Схема:

if (TryComp(uid, out ActorComponent? actor)
    && args.Channel.OnSendSound is { } sound)
{
    _audio.PlayGlobal(sound, Filter.SinglePlayer(actor.PlayerSession), false);
}

Такой звук слышит только отправитель. Игроки рядом не услышат его без наушников.

2. Звук приёма рации

Приёмный звук надо отправлять в OnHeadsetReceive, после успешной доставки RadioReceiveEvent.

Уже есть нужная точка:

if (TryComp(parent, out ActorComponent? actor))

Она означает, что данный headset принадлежит конкретному игроку. Сервер должен послать PlayRadioBarkEvent только в actor.PlayerSession.

RadioBarkAudioSystem уже проигрывает событие через:

_audio.PlayGlobal(..., Filter.Local(), false, audioParams);

Это правильно на клиенте. Filter.Local() здесь означает «только клиент, который получил сетевое событие», а не PVS вокруг рации.

Нужно отправлять событие так:

RaiseNetworkEvent(
    new PlayRadioBarkEvent
    {
        Path = "...",
        Params = AudioParams.Default,
        Source = GetNetEntity(uid),
    },
    actor.PlayerSession);

Не отправлять его через Filter.Pvs(uid).

3. Не смешивать с TTS

Фикс TTS должен использовать тот же принцип: сервер создаёт TTSRadioPlayEvent для каждого ActorComponent, который получил RadioReceiveEvent.

Но этот PR должен чинить только radio bark и OnSendSound. Иначе появится риск:

  • двойного радио-звука;
  • двойной генерации TTS;
  • TTS у отправителя как локальной речи и как рации.

Проверка

Проверить минимум четыре случая:

  1. Игрок без гарнитуры рядом с отправителем не слышит отправной звук.
  2. Отправитель с гарнитурой слышит OnSendSound.
  3. Получатель с гарнитурой слышит приёмный звук независимо от расстояния.
  4. Игрок рядом с получателем, но без его гарнитуры, не слышит приёмный звук.
  • Добавить адресное воспроизведение RadioChannelPrototype.OnSendSound для отправителя в Content.Server/Radio/EntitySystems/HeadsetSystem.cs.
  • Отправлять PlayRadioBarkEvent только владельцу принимающей гарнитуры в Content.Server/Radio/EntitySystems/HeadsetSystem.cs.
  • Не включать TTS в эту правку. TTS должен оставаться в отдельном PR.

Failed to handle agent chat message. Please try again.

You are interacting with an AI system.

@UmbiMax

UmbiMax commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

@coderabbitai По какой причине нельзя надеть наушник на второй слот для него?

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Candidate headset, slot, and equipment files ==='
fd -HI -t f . Content Resources |
  rg -i '(^|/)(.*(headset|earpiece|slot|inventory|clothing|equipment).*\.(cs|yml|yaml))$' |
  head -300

echo '=== Headset prototypes and slot definitions ==='
rg -n -i -C 8 \
  'Headset|Radio|earpiece|ears|ear slot|slotId|slots:|SlotContainer|Clothing' \
  Resources/Prototypes \
  --glob '*.{yml,yaml}' | head -1200 || true

echo '=== Equipment validation and headset-specific code ==='
rg -n -i -C 10 \
  'Headset|Radio|CanEquip|CanUnequip|Equip.*Slot|Slot.*Equip|slotId|SlotContainer|ClothingSlot' \
  Content \
  --glob '*.cs' | head -1400 || true

echo '=== Changed files in this PR that may affect equipment ==='
git diff --name-only "$(git merge-base origin/master HEAD)" HEAD |
  rg -i '(headset|radio|slot|inventory|clothing|equipment)' || true

Length of output: 115530


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Human inventory ear slots ==='
sed -n '/- name: ears$/,/^    - name:/p' \
  Resources/Prototypes/InventoryTemplates/human_inventory_template.yml
sed -n '/- name: earsright$/,/^    - name:/p' \
  Resources/Prototypes/InventoryTemplates/human_inventory_template.yml

echo '=== Base headset prototypes and their Clothing slot flags ==='
for file in \
  Resources/Prototypes/Entities/Clothing/Ears/headsets.yml \
  Resources/Prototypes/Entities/Clothing/Ears/headsets_alt.yml \
  Resources/Prototypes/_Orion/Entities/Clothing/Ears/headsets.yml \
  Resources/Prototypes/_Orion/Entities/Clothing/Ears/headsets_alt.yml \
  Resources/Prototypes/_Arcane/Entities/Clothing/Ears/headsets.yml \
  Resources/Prototypes/_Shitmed/Entities/Clothing/Ears/headsets_alt.yml
do
  [ -f "$file" ] || continue
  echo "----- $file -----"
  rg -n -C 8 'BaseHeadset|Headset|type: Clothing|slots:|EARS|EARSRIGHT' "$file" || true
done

echo '=== Slot-flag declaration and equipment compatibility checks ==='
rg -n -i -C 12 \
  'EARSRIGHT|\bEARS\b|SlotFlags|CanEquip.*slot|slotFlags.*Clothing|Clothing.*Slots' \
  Content.Shared Content.Server Content.Client RobustToolbox \
  --glob '*.cs' 2>/dev/null | head -1200 || true

Length of output: 137823


@UmbiMax, причина в несовпадении флагов слота.

В Resources/Prototypes/InventoryTemplates/human_inventory_template.yml:

  • ears использует slotFlags: EARS;
  • earsright использует slotFlags: EARSRIGHT.

Это разные флаги. Второй слот не является вторым экземпляром EARS.

Базовый прототип ClothingHeadset в Resources/Prototypes/Entities/Clothing/Ears/headsets.yml разрешён только для первого слота:

- type: Clothing
  slots:
  - ears

Поэтому проверка экипировки отклоняет гарнитуру для earsright.

Минимальная правка должна быть в базовом прототипе, чтобы она применялась ко всем наследникам:

- type: Clothing
  slots:
  - ears
  - earsright

После этого две гарнитуры можно будет надеть одновременно. Нужно отдельно проверить визуальный слой правого уха. У гарнитур может не быть отдельного спрайта для EARSRIGHT, но это не должно блокировать экипировку.

You are interacting with an AI system.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Content.Server/Radio/EntitySystems/HeadsetSystem.cs`:
- Around line 75-88: Replace the Orion markers surrounding the inventory
examination block in Content.Server/Radio/EntitySystems/HeadsetSystem.cs lines
75-88, the active headset selection block at lines 111-140, the speech
transmission block at lines 142-171, and the radio sound block at lines 208-211:
use Orion-Edit-Start and Orion-Edit-End consistently instead of Orion-Start and
Orion-End.
- Around line 36-37: Update the OnEntitySpoke event subscription in
HeadsetSystem to use InventoryComponent instead of ActorComponent, so speaking
entities with an equipped headset are handled regardless of whether they have a
game session. Preserve the existing OnEntitySpoke behavior and the
OnInventoryExamined subscription.
- Around line 147-169: В цикле обработки гарнитур вокруг HeadsetSystem и вызовов
_radio.SendRadioMessage обеспечьте отправку args.Channel только один раз: после
первой успешной отправки немедленно завершайте перебор, сохраняя все
существующие проверки совместимости гарнитуры и канала.
- Around line 241-245: Update the PlayRadioBarkEvent construction in the headset
radio flow to set its Source field to parent before raising the event, so the
client receives the speaking entity. Do not modify the client-side
OnPlayRadioBark handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ad0b30b6-7e0d-4666-a873-cf178dda9c9e

📥 Commits

Reviewing files that changed from the base of the PR and between ca579fb and e2bdced.

📒 Files selected for processing (2)
  • Content.Server/Radio/EntitySystems/HeadsetSystem.cs
  • Resources/Prototypes/Entities/Clothing/Ears/headsets.yml
📜 Review details
⏰ Context from checks skipped due to timeout. (5)
  • GitHub Check: Test Packaging
  • GitHub Check: YAML Linter
  • GitHub Check: YAML map schema validator
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: build (ubuntu-latest)
🧰 Additional context used
📓 Path-based instructions (10)
Resources/**/*.{yml,ftl,json}

📄 CodeRabbit inference engine (.cursor/rules/ss14-resources.mdc)

Resources/**/*.{yml,ftl,json}: Follow SS14 naming conventions when editing prototype, localization, and resource files
Follow SS14 upstream maintenance practices when editing resource files

Files:

  • Resources/Prototypes/Entities/Clothing/Ears/headsets.yml
Resources/**/*.yml

📄 CodeRabbit inference engine (.cursor/rules/ss14-resources.mdc)

Resources/**/*.yml: Follow SS14 ECS prototype patterns and conventions
Follow SS14 graphics generic visualizer appearance conventions and patterns

Resources/**/*.yml: Load ss14-naming-conventions documentation for Resources work
Load ss14-ecs-prototypes documentation for Resources work
Load ss14-upstream-maintenance documentation for Resources work
Load ss14-prototypes-locale documentation for Resources work
Load ss14-localization-strings documentation for Resources work
Load ss14-sprite-overlays-shaders documentation when working with RSI metadata, textures, sprite layers, overlays, or shader resources
Validate YAML after edits

Run dotnet run --project Content.YAMLLinter/Content.YAMLLinter.csproj -c DebugOpt to validate YAML and resource edits.

Files:

  • Resources/Prototypes/Entities/Clothing/Ears/headsets.yml
Resources/**/*.{yml,ftl}

📄 CodeRabbit inference engine (.cursor/rules/ss14-resources.mdc)

Follow SS14 prototype locale integration rules and practices

Files:

  • Resources/Prototypes/Entities/Clothing/Ears/headsets.yml
**/*.{cs,xaml,yml,yaml}

📄 CodeRabbit inference engine (AGENTS.md)

If the task adds tests or you need to choose the right test layer, load skill ss14-tests-authoring.

Files:

  • Resources/Prototypes/Entities/Clothing/Ears/headsets.yml
  • Content.Server/Radio/EntitySystems/HeadsetSystem.cs
Resources/Prototypes/**/*.yml

📄 CodeRabbit inference engine (AGENTS.md)

Resources/Prototypes/**/*.yml: Put prototypes under the most specific existing subtree in Resources/Prototypes/. If you introduce a new prototype parent tree, put parent prototypes in base.yml and variants in sibling files.
Keep entity prototype field order as type, abstract, parent, id, categories, name, suffix, description, components. Do not insert blank lines between - type: entries inside a components: list. Separate prototype blocks with one blank line.
Prefer suffix for spawn-menu distinctions instead of changing prototype name.

Files:

  • Resources/Prototypes/Entities/Clothing/Ears/headsets.yml

⚙️ CodeRabbit configuration file

Resources/Prototypes/**/*.yml: Review SS14 prototypes carefully:

  • Use the narrowest existing subtree and avoid duplicating nearby prototype files or parent trees.
  • Keep entity field order as: type, abstract, parent, id, categories, name, suffix, description, components.
  • Prefer base.yml for parent trees and sibling files for variants.
  • Do not add blank lines inside components lists.
  • Call out broken inheritance, risky prototype ID changes, missing paired locale updates, and Appearance / GenericVisualizer mismatches.

Files:

  • Resources/Prototypes/Entities/Clothing/Ears/headsets.yml
**/*.cs

📄 CodeRabbit inference engine (GEMINI.md)

**/*.cs: Keep components data-only and behavior in systems
Use On... -> Try... -> Can... -> Do... pattern for gameplay actions
Prefer Entity<T?>, ProtoId<T>, EntProtoId, and localized strings for type usage

**/*.cs: When writing or editing C# gameplay code, load skills: ss14-ecs-components, ss14-ecs-entities, ss14-ecs-prototypes, ss14-ecs-systems, ss14-events, and ss14-prediction.
If the C# change is large or the task needs reviewable notes, load skill ss14-documentation-writing.
If the code touches hot paths, Update(), or frequently raised events, load skill ss14-standard-optimizations.
If the task adds or changes player-facing text, load skills ss14-localization-strings and ss14-localization-code when the change also touches Loc.GetString(...), LocId, popup text, or localized component fields.
If the task touches network events, NetEntity, replicated state routing, or shared/server/client message flow, load skill ss14-netcode.
If the task touches Appearance, GenericVisualizer, visual state enums, or sprite-layer toggles, load skill ss14-graphics-generic-visualizer-appearance.
If the task touches sprites, RSI metadata, overlays, shaders, or custom client visual effects, load skill ss14-sprite-overlays-shaders.
If the task is about learning or explaining SS14 architecture, first features, or where code belongs, load skills ss14-prototype-basics, ss14-ecs-basics, and ss14-client-server-shared.
If the task is about bug hunting, VV, logs, breakpoints, or runtime inspection, load skills ss14-debugging-workflow and ss14-common-api-patterns.
If the task touches common gameplay helpers such as entity-system methods, spawning, prototypes, audio, popups, or random, load skills ss14-common-api-patterns and ss14-audio when the work changes audio routing, sound assets, sound collections, or predicted sound feedback.
If the task ports code or assets from another repository, or needs license or attribution guidance,...

Files:

  • Content.Server/Radio/EntitySystems/HeadsetSystem.cs
+(Content.Shared|Content.Server|Content.Client|Content.Goobstation.Shared|Content.Goobstation.Server|Content.Goobstation.Client|Content.Goobstation.Common|Content.Goobstation.Maths|Content.Goobstation.UIKit|Content.Server.Database|Content.Shared.Database|Content.Tests|Content.IntegrationTests)/**/*.cs

📄 CodeRabbit inference engine (.cursor/rules/ss14-csharp.mdc)

+(Content.Shared|Content.Server|Content.Client|Content.Goobstation.Shared|Content.Goobstation.Server|Content.Goobstation.Client|Content.Goobstation.Common|Content.Goobstation.Maths|Content.Goobstation.UIKit|Content.Server.Database|Content.Shared.Database|Content.Tests|Content.IntegrationTests)/**/*.cs: Apply C# ECS style rules from ss14-interaction-flow.md
Apply ss14-csharp-style.md rules for C# code formatting and conventions
Follow ss14-naming-conventions for variable, method, and type naming in C# gameplay code

Files:

  • Content.Server/Radio/EntitySystems/HeadsetSystem.cs
{Content.Server,Content.Goobstation.Server}/**/*.cs

📄 CodeRabbit inference engine (AGENTS.md)

Put main server-only authority and non-predicted server simulation in Content.Server/; use Content.Goobstation.Server/ for features already rooted in that module.

Files:

  • Content.Server/Radio/EntitySystems/HeadsetSystem.cs
**/*System.cs

📄 CodeRabbit inference engine (AGENTS.md)

Public entity-system APIs that operate on entities should usually take Entity<T?> or EntityUid first and call Resolve(...) early.

Files:

  • Content.Server/Radio/EntitySystems/HeadsetSystem.cs
Content.Server/**/*.cs

⚙️ CodeRabbit configuration file

Content.Server/**/*.cs: Review Content.Server for authority and fork-maintenance discipline:

  • Server owns authority, persistence, round logic, and non-predicted side effects.
  • If a player action should feel immediate, call out missing shared prediction rather than accepting a server-only path.
  • Keep upstream diffs narrow and prefer fork-scoped extensions over broad edits to upstream behavior.
  • When player-visible behavior changes, check whether matching prototype and locale updates are missing.

Files:

  • Content.Server/Radio/EntitySystems/HeadsetSystem.cs
🧠 Learnings (11)
📚 Learning: 2026-06-28T12:10:10.819Z
Learnt from: MataVsn
Repo: ArcaneSS14/arcane-station PR: 96
File: Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml:170-174
Timestamp: 2026-06-28T12:10:10.819Z
Learning: В YAML-прототипах для компонента `HideLayerClothing` предпочитайте поле `layers:` вместо устаревшего `slots:`. Из-за того, что в `FoldableClothingSystem.OnMapInit` перезаписывается `Slots`, наличие `slots: []` может стать артефактом и приводить к падениям/ложным срабатываниям проверок (например, `UninitializedSaveTest`). Использование `layers` не затрагивается этой перезаписью и безопаснее для существующих проверок.

Applied to files:

  • Resources/Prototypes/Entities/Clothing/Ears/headsets.yml
📚 Learning: 2026-06-29T05:34:31.137Z
Learnt from: Alazell
Repo: ArcaneSS14/arcane-station PR: 99
File: Resources/Prototypes/Entities/Clothing/Belt/belts.yml:973-974
Timestamp: 2026-06-29T05:34:31.137Z
Learning: В SS14 YAML-прототипах для компонента `Storage` поле `grid` задаёт **включительные** координаты прямоугольника. При подсчёте вместимости учитывайте оффсет-инклюзивность: прямоугольник `x1,y1,x2,y2` означает размеры `width = (x2 - x1 + 1)` и `height = (y2 - y1 + 1)`, а число ячеек `width * height`. Например, `0,0,0,0` = 1 ячейка, а `0,0,6,2` = (6-0+1)*(2-0+1)=7*3=21. При ревью файлов наподобие `Resources/Prototypes/Entities/Clothing/Belt/belts.yml` не интерпретируйте `x2/y2` как эксклюзивные (чтобы не ошибиться на 1 в ширине/высоте).

Applied to files:

  • Resources/Prototypes/Entities/Clothing/Ears/headsets.yml
📚 Learning: 2026-07-29T08:12:37.068Z
Learnt from: UmbiMax
Repo: ArcaneSS14/arcane PR: 153
File: Resources/Prototypes/_Orion/Entities/Objects/Tools/energy_dome.yml:137-137
Timestamp: 2026-07-29T08:12:37.068Z
Learning: In ArcaneSS14 prototype YAMLs, the YAML field that disables synchronization for `Content.Shared.Power.Components.BatteryComponent` is spelled exactly `netsync` (all lowercase), not `netSync`. For prototypes derived from `PowerNetworkBattery` (including `BaseAPC`, `CoreSubstation`, `BaseSMES`, `TeslaCoil`), set `netsync: false`; otherwise `Content.Server.Power.EntitySystems.BatterySystem.OnStartup` can throw `DebugAssertException` for a predicted battery connected to a power net. For `Resources/Prototypes/_Orion/Entities/Objects/Tools/energy_dome.yml`, ensure `EnergyDomeWiredTest` uses `netsync: false`.

Applied to files:

  • Resources/Prototypes/Entities/Clothing/Ears/headsets.yml
📚 Learning: 2026-07-07T17:55:50.395Z
Learnt from: UmbiMax
Repo: ArcaneSS14/arcane-old PR: 101
File: Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs:192-194
Timestamp: 2026-07-07T17:55:50.395Z
Learning: When reviewing ArcaneSS14/arcane-old changes related to `MobState` thresholds/visuals/actions, treat the `Critical` enum value as requiring explicit mappings (it no longer implicitly behaves as an alias of `SoftCritical`). If any YAML prototype `thresholds:` (for `MobThresholdsComponent.Thresholds`) uses the bare `Critical` key (e.g., `MobThresholdsComponent.Thresholds: { Critical: ... }` for entities like `MobMouseCancer`), ensure the prototype (or referenced config) provides corresponding explicit `StateAlertDict` entries and any required `MobStateActions.actions` and `DamageStateVisuals.states` mappings for `Critical`. Otherwise, review should expect failures because the default `StateAlertDict` in `MobThresholdsComponent.cs` typically maps only `Alive`, `SoftCritical`, `HardCritical`, and `Dead`—not `Critical`.

Applied to files:

  • Resources/Prototypes/Entities/Clothing/Ears/headsets.yml
📚 Learning: 2026-07-28T15:44:38.498Z
Learnt from: UmbiMax
Repo: ArcaneSS14/arcane PR: 152
File: Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/pistol.yml:69-72
Timestamp: 2026-07-28T15:44:38.498Z
Learning: В ArcaneSS14/arcane не выполняйте форматирующие перестановки полей (в т.ч. изменение порядка полей) в upstream-файлах: делайте это только если правка добавляет Arcane-специфичную сущность или напрямую требуется для функциональности. Иначе сохраняйте upstream-порядок полей, даже если он противоречит локальным рекомендациям, чтобы не создавать лишние diff и конфликты при upstream-синхронизации. Исключение: допустимо переупорядочивание/форматирование в `Resources/Prototypes/_Arcane` (и только там).

Applied to files:

  • Resources/Prototypes/Entities/Clothing/Ears/headsets.yml
📚 Learning: 2026-06-04T21:17:36.815Z
Learnt from: MataVsn
Repo: ArcaneSS14/arcane-station PR: 31
File: Content.Client/Fluids/PuddleSystem.cs:41-41
Timestamp: 2026-06-04T21:17:36.815Z
Learning: In this repo (ArcaneSS14/arcane-station), reviewers should use the Arcane inline fork-edit marker naming for single-line edits outside `_Arcane` paths: prefer `// Arcane` or `// Arcane-edit` and do not use `// Orion` for this purpose. Repo-specific AGENTS guidance that mentions `// Orion` does not apply to these single-line changes; only `_Arcane` paths follow the applicable different convention.

Applied to files:

  • Content.Server/Radio/EntitySystems/HeadsetSystem.cs
📚 Learning: 2026-06-28T19:37:26.732Z
Learnt from: MataVsn
Repo: ArcaneSS14/arcane-station PR: 96
File: Content.Client/IoC/ClientContentIoC.cs:151-151
Timestamp: 2026-06-28T19:37:26.732Z
Learning: For ArcaneSS14/arcane-station C# files whose path is outside any `_Arcane` directories, avoid adding an inline fork marker (e.g., `// Arcane` or `// Arcane-edit`) to `using` directives when the imported namespace already clearly indicates the `_Arcane` fork. For example, if the `using` namespace itself contains `_Arcane` (such as `using Content.Client._Arcane.ERP.Preferences;`), the `// Arcane`/`// Arcane-edit` marker is unnecessary.

Applied to files:

  • Content.Server/Radio/EntitySystems/HeadsetSystem.cs
📚 Learning: 2026-07-04T03:37:47.373Z
Learnt from: UmbiMax
Repo: ArcaneSS14/arcane-station PR: 101
File: Content.Goobstation.Server/Chemistry/EntitySystems/EnergyReagentDispenserSystem.cs:59-59
Timestamp: 2026-07-04T03:37:47.373Z
Learning: In ArcaneSS14/arcane-station, C# source files must never have fork-edit markers (e.g., comments like `// Arcane`, `// Arcane-edit`, `// Orion`) on `using` directives. During code review, do not flag existing markers on any `using` line and do not suggest adding/removing them based on `_Arcane` folder placement or whether the imported namespace contains `_Arcane`—this rule applies universally to all `using` statements.

Applied to files:

  • Content.Server/Radio/EntitySystems/HeadsetSystem.cs
📚 Learning: 2026-07-28T16:30:37.419Z
Learnt from: UmbiMax
Repo: ArcaneSS14/arcane PR: 133
File: Content.Server/Speech/EntitySystems/StutteringSystem.cs:28-29
Timestamp: 2026-07-28T16:30:37.419Z
Learning: При внесении одиночных fork-правок в репозитории ArcaneSS14/arcane вне путей `_Arcane` добавляйте в конец каждой изменённой строки комментарий-маркер `// Arcane-Edit`. Это нужно для упрощения последующего поиска/обновления подобных правок при код-ревью и синхронизациях.

Applied to files:

  • Content.Server/Radio/EntitySystems/HeadsetSystem.cs
📚 Learning: 2026-07-01T17:19:52.100Z
Learnt from: UmbiMax
Repo: ArcaneSS14/arcane-station PR: 101
File: Content.Shared/Mobs/MobState.cs:0-0
Timestamp: 2026-07-01T17:19:52.100Z
Learning: When reviewing ArcaneSS14/arcane-station C# code, ensure all usages of the `MobState` enum (`MobState.Dead`, `MobState.SoftCritical`, `MobState.HardCritical`, `MobState.Critical`) reference the enum members symbolically rather than via hardcoded numeric literals or casts from numeric values (e.g., avoid patterns like `(MobState)4` or `MobState` comparisons to `byte`/`int` literals). Treat direct numeric usage as a code-level dependency that should be flagged. Also avoid persisting `MobState` numeric values in `Content.Server.Database` or `Content.Replay`; if persistence/replay of the raw byte value is introduced, renumbering the `[NetSerializable] MobState : byte` enum becomes a data-compatibility risk. Renumbering the enum is compile-safe as long as there are no numeric literal dependencies, but the remaining risk is network wire-protocol mismatch if mixed old/new client-server builds are running during a rolling update—coordinate deployment to maintain net compatibility.

Applied to files:

  • Content.Server/Radio/EntitySystems/HeadsetSystem.cs
📚 Learning: 2026-07-03T18:41:44.286Z
Learnt from: UmbiMax
Repo: ArcaneSS14/arcane-station PR: 101
File: Content.Shared/_Orion/EntityEffects/Effects/Special/ChemConvermol.cs:0-0
Timestamp: 2026-07-03T18:41:44.286Z
Learning: In ArcaneSS14/arcane-station, the hardcoded 1.2f base used by RefreshPartsEvent.GetLinearMultiplier(tier, step, min, max) (Math.Clamp(1.2f - tier * step, min, max)) is an intentional repo-wide balance convention: callers typically pass max: 1.2f, so a tier-1 part yields a 10% penalty (e.g., GetLinearMultiplier(tier: 1, step: 0.1f, min: 0.5f, max: 1.2f) => 1.1), while tier-2 is the neutral baseline. Do not flag the hardcoded 1.2f as a bug when reviewing shared multiplier/linear scaling usage. If a specific system needs a tier-1 baseline that produces 0% penalty instead, implement a locally-scoped formula in that system (e.g., adjust the constant to match the desired neutral baseline) rather than changing the shared helper, since modifying it would affect all callers’ balance.

Applied to files:

  • Content.Server/Radio/EntitySystems/HeadsetSystem.cs
🔇 Additional comments (6)
Resources/Prototypes/Entities/Clothing/Ears/headsets.yml (1)

24-28: LGTM!

Content.Server/Radio/EntitySystems/HeadsetSystem.cs (5)

6-14: LGTM!

Also applies to: 26-28


61-73: LGTM!


90-109: LGTM!


183-206: LGTM!


247-250: 🎯 Functional Correctness

Оставьте предупреждение как есть.

Текущие радиоканалы не используют SoundCollectionSpecifier в onSendSound, поэтому этот путь не блокирует ни одну существующую радиопередачу.

Comment on lines +36 to +37
SubscribeLocalEvent<ActorComponent, EntitySpokeEvent>(OnEntitySpoke); // Orion-Edit
SubscribeLocalEvent<InventoryComponent, ExaminedEvent>(OnInventoryExamined); // Orion

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Не ограничивайте передачу радио ActorComponent.

ActorComponent есть только у сущностей с игровой сессией. Говорящие NPC и другие сущности без этого компонента больше не вызовут OnEntitySpoke, хотя прежний обработчик работал через WearingHeadsetComponent.

Подпишитесь на InventoryComponent. Сущность с экипированной гарнитурой уже имеет инвентарь.

Предлагаемое исправление
- SubscribeLocalEvent<ActorComponent, EntitySpokeEvent>(OnEntitySpoke);
+ SubscribeLocalEvent<InventoryComponent, EntitySpokeEvent>(OnEntitySpoke);

- private void OnEntitySpoke(EntityUid uid, ActorComponent component, EntitySpokeEvent args)
+ private void OnEntitySpoke(EntityUid uid, InventoryComponent component, EntitySpokeEvent args)

Also applies to: 142-142

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Content.Server/Radio/EntitySystems/HeadsetSystem.cs` around lines 36 - 37,
Update the OnEntitySpoke event subscription in HeadsetSystem to use
InventoryComponent instead of ActorComponent, so speaking entities with an
equipped headset are handled regardless of whether they have a game session.
Preserve the existing OnEntitySpoke behavior and the OnInventoryExamined
subscription.

Comment thread Content.Server/Radio/EntitySystems/HeadsetSystem.cs Outdated
Comment thread Content.Server/Radio/EntitySystems/HeadsetSystem.cs Outdated
Comment on lines +241 to +245
RaiseNetworkEvent(new PlayRadioBarkEvent
{
Path = sps.Path.ToString(),
Params = sps.Params,
}, actor.PlayerSession.Channel);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Найти создание и обработчики сетевого события.
ast-grep run --lang csharp \
  --pattern 'new PlayRadioBarkEvent { $$$ }' \
  Content.Server Content.Shared Content.Client

rg -n -C 6 --glob '*.cs' \
  '\bPlayRadioBarkEvent\b|Subscribe.*PlayRadioBark|Raise.*PlayRadioBark' .

Repository: ArcaneSS14/arcane

Length of output: 5282


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== HeadsetSystem context =="
sed -n '200,270p' Content.Server/Radio/EntitySystems/HeadsetSystem.cs

echo
echo "== PlayRadioBarkEvent =="
cat -n Content.Shared/_Orion/Radio/PlayRadioBarkEvent.cs

echo
echo "== Client handler context =="
sed -n '1,60p' Content.Client/_Orion/Radio/RadioBarkAudioSystem.cs

echo
echo "== Nearby PlayGlobal/PlayFiltered patterns =="
rg -n --glob '*.cs' '\bPlay(Filtered|Global|Entity)\(' Content.Client/_Orion/Radio Content.Shared/_Orion Content.Server/Radio Content.Client Content.Shared Content.Server | head -120

Repository: ArcaneSS14/arcane

Length of output: 17926


Передавайте Source в PlayRadioBarkEvent.

Создание сетевого события не задаёт Source, поэтому клиент получает default NetEntity. На текущий момент OnPlayRadioBark() игнорирует ev.Source и вызывает PlayGlobal(..., Filter.Local(), ...), поэтому звук не привязывается к говорящему. Передайте Source: parent сервером и игнорируйте лишнее исправление на клиенте.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Content.Server/Radio/EntitySystems/HeadsetSystem.cs` around lines 241 - 245,
Update the PlayRadioBarkEvent construction in the headset radio flow to set its
Source field to parent before raising the event, so the client receives the
speaking entity. Do not modify the client-side OnPlayRadioBark handling.

@UmbiMax

UmbiMax commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

@coderabbitai Глянь на причины провала тестов у коммита Roles 3
Опять фиксить

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ArcaneSS14/arcane"
sha="ca579fba181b0069e8bb7f0cf8253240d441bd2c"

echo "=== Workflow runs for commit ==="
gh api "repos/$repo/actions/runs?head_sha=$sha&per_page=100" \
  --jq '.workflow_runs[] | {id, name, event, status, conclusion, run_attempt, created_at, updated_at, html_url}'

echo "=== Pull-request checks for commit ==="
gh pr checks 161 --repo "$repo" --json name,state,link,workflow,bucket 2>/dev/null || true

run_id="$(
  gh api "repos/$repo/actions/runs?head_sha=$sha&per_page=100" \
    --jq '[.workflow_runs[]
      | select(.name == "Build & Test Debug" and .status == "completed")
      | {id, created_at}] | sort_by(.created_at) | last.id // empty'
)"

if [ -z "$run_id" ]; then
  echo "No completed Build & Test Debug run exists for this commit."
  exit 0
fi

echo "=== Jobs for Build & Test Debug run $run_id ==="
gh api "repos/$repo/actions/runs/$run_id/jobs?per_page=100" \
  --jq '.jobs[] | {id, name, status, conclusion, started_at, completed_at, steps}'

job_id="$(
  gh api "repos/$repo/actions/runs/$run_id/jobs?per_page=100" \
    --jq '.jobs[] | select(.name == "build (ubuntu-latest)") | .id' | tail -1
)"

if [ -z "$job_id" ]; then
  echo "No build (ubuntu-latest) job exists in this run."
  exit 0
fi

log="$(mktemp)"
trap 'rm -f "$log"' EXIT
gh api "repos/$repo/actions/jobs/$job_id/logs" > "$log"

echo "=== First test error and stack trace ==="
rg -n -i -m 1 -B 35 -A 130 \
  'Error Message:|Failed [A-Za-z0-9_.]+|Unhandled exception|Exception:|Test Run Aborted|Attempted to resolve invalid ProtoId|Missing prototype|does not exist in RSI|Expected:|But was:' \
  "$log" || true

echo "=== All test failure headers ==="
rg -n -i -C 12 \
  '^\s*Failed [A-Za-z0-9_.]+|Error Message:|Attempted to resolve invalid ProtoId|Missing prototype|does not exist in RSI|Expected:|But was:|Test Run Aborted|Failed!|Passed!' \
  "$log" || true

echo "=== Integration-test summary ==="
rg -n -i -C 8 \
  'Run Content\.IntegrationTests|Total tests|Failed!|Passed!|Test Run Aborted|Process completed' \
  "$log" || true

echo "=== Relevant current source ==="
for file in \
  Content.Shared/Roles/Jobs/SharedJobSystem.cs \
  Content.Server/_Orion/Economy/Systems/PayrollSystem.cs \
  Content.Server/Radio/EntitySystems/HeadsetSystem.cs \
  Content.IntegrationTests/Tests/Minds/MindTests.cs \
  Content.IntegrationTests/Tests/PostMapInitTest.cs
do
  if [ -f "$file" ]; then
    echo "----- $file -----"
    rg -n -C 12 \
      'MindTryGetJob|TryIndex|GetPayrollData|JobPrototype|TestAddRemoveHasRoles|SpawnPoint|Error' \
      "$file" || true
  fi
done

Length of output: 129271


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sha="ca579fba181b0069e8bb7f0cf8253240d441bd2c"
repo="ArcaneSS14/arcane"
job_id="91234704946"
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT

echo "=== All failed test names and pool-abort context ==="
gh api "repos/$repo/actions/jobs/$job_id/logs" > "$tmp/ci.log"
rg -n -i -C 30 \
  'Failed [A-Za-z0-9_.]+|`#0`: ResearchTest\.DisciplineValidTierPrerequesitesTest|FailFast|Pool manager|pool.*(error|fail|abort)|Test Run Aborted' \
  "$tmp/ci.log" || true

echo "=== Entity 27273 in maps at tested commit ==="
git grep -n -C 18 'uid: 27273' "$sha" -- 'Resources/Maps/**/*.yml' || true

echo "=== DeviceNetwork references near entity 27273 ==="
git grep -n -C 8 '27273' "$sha" -- 'Resources/Maps/**/*.yml' || true

echo "=== consul.yml: Stack components that do not define stackType ==="
git show "$sha:Resources/Maps/_Goobstation/Shuttles/consul.yml" > "$tmp/consul.yml"
python3 - "$tmp/consul.yml" <<'PY'
import re
import sys

text = open(sys.argv[1], encoding="utf-8").read()
blocks = re.split(r'(?m)^- proto: ', text)
for block in blocks[1:]:
    proto, _, body = block.partition('\n')
    if re.search(r'(?m)^    - type: Stack\s*$', body) and not re.search(r'(?m)^      stackType:\s*\S+', body):
        uid = re.search(r'(?m)^  - uid: (\d+)\s*$', body)
        print(f"proto={proto}; uid={uid.group(1) if uid else 'unknown'}")
        lines = body.splitlines()
        for i, line in enumerate(lines):
            if line == '    - type: Stack':
                print('\n'.join(lines[max(0,i-8):i+12]))
                print('---')
PY

echo "=== DeviceLink and DeviceNetwork blocks in consul.yml ==="
rg -n -C 14 'type: (DeviceNetwork|DeviceLinkSource|DeviceLinkSink|Stack)|stackType:|links:|devices:' "$tmp/consul.yml" || true

echo "=== Research and lathe definitions for the first failing recipes at tested commit ==="
for needle in \
  'id: Bloodpack' \
  'id: SawElectric' \
  'id: ClothingNeckStethoscope' \
  'id: MedicalEquipment' \
  'id: SmartFridgeCircuitboard'
do
  echo "--- $needle ---"
  git grep -n -C 10 -F "$needle" "$sha" -- 'Resources/Prototypes/**/*.yml' || true
done

echo "=== Changes introduced by Roles 3 commit ==="
git diff --stat "${sha}^" "$sha"
git diff --name-status "${sha}^" "$sha"

Length of output: 241864


@UmbiMax, в этом запуске JobPrototype уже не ломает тесты. Content.Tests проходят: 372/372.

Roles 3 меняет только kettle.yml, leonid.yml и loop.yml. Текущие ошибки находятся в других файлах.

1. NonGameMapsLoadableTest: неверная ссылка в box.yml

Файл: Resources/Maps/_Goobstation/box.yml.

Сущность uid: 27273 имеет:

- type: DeviceNetwork
  configurators:
  - invalid

configurators содержит EntityUid. Строка invalid не является UID. Десериализатор не может прочитать компонент DeviceNetwork.

Исправление: удалить override DeviceNetwork у этой сущности, если configurator не нужен. Если он нужен, заменить invalid на UID существующего configurator на этой карте.

-    - type: DeviceNetwork
-      configurators:
-      - invalid

Это первый map error в логе.

2. NonGameMapsLoadableTest: устаревшие Stack-компоненты в consul.yml

Файл: Resources/Maps/_Goobstation/Shuttles/consul.yml.

Map overrides задают Stack.count, но не задают обязательный Stack.stackType. Примеры затронутых прототипов:

  • CableApcStack1;
  • CableHVStack1;
  • CableMVStack1;
  • CapacitorStockPart;
  • MatterBinStockPart;
  • MicroManipulatorStockPart;
  • SheetSteel1.

Первый сбой возникает на CableApcStack1, UID 176.

- type: Stack
  count: 3

Исправление: пересохранить consul.yml в актуальном editor. Это безопаснее ручного обновления старого сериализованного YAML.

Если править вручную, добавьте правильный stackType во все такие component overrides. Например:

- type: Stack
  stackType: Cable
  count: 3

Нельзя добавлять один stackType только к UID 176. После этого тест дойдёт до следующей старой stack-сущности.

Два сообщения про invalid DeviceLink у shuttle airlock — каскад при очистке частично загруженной карты после этой ошибки.

3. AllTechPrintableTest: research не совпадает с lathe recipes

Тест нашёл массовую рассинхронизацию между:

  • technology.recipeUnlocks;
  • latheRecipe;
  • наборами рецептов доступных lathe.

Примеры:

  • Bloodpack, SawElectric, ClothingNeckStethoscope разблокируются MedicalEquipment, но ни один lathe не может их напечатать;
  • MedicalBeamGunSyndicate, medical circuitboards и ряд других рецептов имеют ту же проблему;
  • SmartFridgeCircuitboard dynamic на lathe, но research его не разблокирует;
  • VoiceSensor, BoxFolderClipboardEmpty, MaterialDurathread и много других рецептов dynamic, но не принадлежат ни одной technology.

Это не точечная ошибка трёх первых рецептов. Здесь сломана консистентность данных research/lathe после upstream-изменений.

Нужная правка:

  1. Для каждого recipe из technology.recipeUnlocks добавить его в доступные recipes соответствующего lathe.
  2. Для каждого dynamic recipe добавить technology unlock.
  3. Если рецепт не должен быть research-gated, убрать его из dynamic recipes lathe.
  4. Отдельно проверить Resources/Prototypes/_Orion/Research/Nodes/*.yml, Resources/Prototypes/_Orion/Recipes/Lathes/*.yml и prototype lathe с dynamicRecipes/staticRecipes.

Не стоит просто удалить проверки из ResearchTest. Они выявляют реальные рецепты, которые игроки не смогут разблокировать или напечатать.

4. Test Run Aborted

После двух registered failures runner завершается через Environment.FailFast во время:

ResearchTest.DisciplineValidTierPrerequesitesTest

Лог не содержит NUnit assertion этой проверки. Поэтому это не подтверждённая третья content-ошибка. После исправления пунктов 1–3 надо повторить CI. Новый запуск покажет точную причину abort и следующие ошибки, если они есть.

You are interacting with an AI system.

@UmbiMax

UmbiMax commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

@coderabbitai Где рецепты у латов? В каком файле?

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Files with lathe recipe definitions ==='
rg -l -g '*.yml' -g '*.yaml' '^\s*- type: latheRecipe\s*$' Resources/Prototypes | sort

echo '=== Files that assign static or dynamic recipes to lathe prototypes ==='
rg -l -g '*.yml' -g '*.yaml' '^\s*(staticRecipes|dynamicRecipes):\s*$' Resources/Prototypes | sort

echo '=== Representative context ==='
rg -n -C 4 -g '*.yml' -g '*.yaml' \
  '^\s*- type: latheRecipe\s*$|^\s*(staticRecipes|dynamicRecipes):\s*$' \
  Resources/Prototypes | head -220

Length of output: 6149


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Definitions of recipe IDs from the CI failure ==='
rg -n -C 8 -g '*.yml' -g '*.yaml' \
  '^\s*id:\s*(Bloodpack|SawElectric|ClothingNeckStethoscope|SmartFridgeCircuitboard)\s*$' \
  Resources/Prototypes

echo '=== Lathe prototype recipe-list fields ==='
rg -n -i -C 8 -g '*.yml' -g '*.yaml' \
  'static.*recipe|dynamic.*recipe|type:\s*Lathe|type:\s*MaterialStorage' \
  Resources/Prototypes/Entities Resources/Prototypes/_Orion Resources/Prototypes/DeltaV \
  || true

Length of output: 201041


@UmbiMax, для текущих ошибок:

  • BloodpackResources/Prototypes/_Orion/Recipes/Lathes/medical.yml, Line 2.
  • ClothingNeckStethoscope — тот же файл, Line 11.
  • SawElectric — тот же файл, Line 20.

Сами рецепты определяются через - type: latheRecipe.

Списки пакетов, доступных конкретному лату, находятся в:

Resources/Prototypes/Entities/Structures/Machines/lathe.yml

Там у компонента Lathe используются:

staticPacks:
dynamicPacks:

Например, MedicalTechFab начинается около Line 592. Для него список пакетов задан сразу ниже в dynamicPacks.

То есть при фиксе надо проверить два уровня:

  1. Resources/Prototypes/_Orion/Recipes/Lathes/medical.yml — определение рецепта.
  2. Resources/Prototypes/Entities/Structures/Machines/lathe.yml — пакет должен быть назначен нужному лату через dynamicPacks или staticPacks.

Пакеты рецептов имеют тип latheRecipePack и обычно лежат рядом с рецептами в Resources/Prototypes/**/Recipes/Lathes/*.yml.

You are interacting with an AI system.

@UmbiMax

UmbiMax commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

@coderabbitai На консуле всего 32 - type: Stack. Можешь подобрать для каждого вставку?

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

map='Resources/Maps/_Goobstation/Shuttles/consul.yml'

echo '=== Map Stack overrides ==='
python3 - "$map" <<'PY'
from pathlib import Path
import re, sys

text = Path(sys.argv[1]).read_text(encoding='utf-8')
for m in re.finditer(r'(?ms)^- proto: ([^\s#]+)\s*\n(.*?)(?=^- proto:|\Z)', text):
    proto, body = m.group(1), m.group(2)
    stack = re.search(r'(?m)^    - type: Stack\s*\n((?:      .*(?:\n|$))*)', body)
    if stack:
        stack_body = stack.group(1)
        uid = re.search(r'(?m)^  - uid: (\d+)\s*$', body)
        print(f'proto={proto}\tuid={uid.group(1) if uid else "unknown"}\t'
              f'has_stackType={"stackType:" in stack_body}\t'
              f'fields={stack_body.strip().replace(chr(10), " | ")}')
PY

echo '=== Prototype definitions and inherited Stack components ==='
rg -n -C 8 \
  '^\s*id:\s*(CableApcStack1|CableHVStack1|CableMVStack1|CapacitorStockPart|MatterBinStockPart|MicroManipulatorStockPart|SheetSteel1)\s*$|^\s*stackType:\s*' \
  Resources/Prototypes --glob '*.{yml,yaml}' || true

echo '=== All candidate Stack prototype blocks for Consul entity prototypes ==='
python3 - "$map" <<'PY'
from pathlib import Path
import re, sys

map_text = Path(sys.argv[1]).read_text(encoding='utf-8')
protos = []
for m in re.finditer(r'(?ms)^- proto: ([^\s#]+)\s*\n(.*?)(?=^- proto:|\Z)', map_text):
    if re.search(r'(?m)^    - type: Stack\s*$', m.group(2)):
        protos.append(m.group(1))

print('\n'.join(sorted(set(protos))))
PY

Length of output: 202031


🏁 Script executed:

#!/bin/bash
set -euo pipefail

map='Resources/Maps/_Goobstation/Shuttles/consul.yml'
targets='CableApcStack1|CableHVStack1|CableMVStack1|CapacitorStockPart|MatterBinStockPart|MicroManipulatorStockPart|SheetSteel1'

echo '=== Все map override Stack: proto, UID, count ==='
python3 - "$map" <<'PY'
from pathlib import Path
import re, sys

text = Path(sys.argv[1]).read_text(encoding="utf-8")
n = 0
for m in re.finditer(r'(?ms)^- proto: ([^\s#]+)\s*\n(.*?)(?=^- proto:|\Z)', text):
    proto, body = m.group(1), m.group(2)
    stack = re.search(r'(?m)^    - type: Stack\s*\n((?:      .*(?:\n|$))*)', body)
    if not stack or 'stackType:' in stack.group(1):
        continue
    uid = re.search(r'(?m)^  - uid: (\d+)\s*$', body)
    count = re.search(r'(?m)^      count: (\d+)\s*$', stack.group(1))
    n += 1
    print(f'{n:02d}. proto={proto}; uid={uid.group(1) if uid else "unknown"}; '
          f'count={count.group(1) if count else "default"}')
print(f'TOTAL={n}')
PY

echo '=== Точные объявления этих прототипов ==='
rg -n -A35 -B4 \
  "^[[:space:]]*id:[[:space:]]*(${targets})[[:space:]]*$" \
  Resources/Prototypes --glob '*.{yml,yaml}' || true

echo '=== Точные родительские цепочки ==='
rg -n -A4 -B3 \
  "^[[:space:]]*(id|parent):[[:space:]]*(${targets}|CableStack|CableHVStack|CableMVStack|StockPart|SheetSteel)[[:space:]]*$" \
  Resources/Prototypes --glob '*.{yml,yaml}' || true

Length of output: 44475


🏁 Script executed:

#!/bin/bash
set -euo pipefail

map='Resources/Maps/_Goobstation/Shuttles/consul.yml'

echo '=== Все Stack override по UID ==='
python3 - "$map" <<'PY'
from pathlib import Path
import re, sys

text = Path(sys.argv[1]).read_text(encoding="utf-8")
rows = []

# Map format: a proto block contains one or more "  - uid:" entity instances.
for proto_match in re.finditer(r'(?ms)^- proto: ([^\s#]+)\s*\n(.*?)(?=^- proto:|\Z)', text):
    proto, proto_body = proto_match.group(1), proto_match.group(2)
    for entity_match in re.finditer(r'(?ms)^  - uid: (\d+)\s*\n(.*?)(?=^  - uid:|\Z)', proto_body):
        uid, entity_body = entity_match.group(1), entity_match.group(2)
        stack = re.search(r'(?m)^    - type: Stack\s*\n((?:      .*(?:\n|$))*)', entity_body)
        if not stack:
            continue
        stack_body = stack.group(1)
        if re.search(r'(?m)^      stackType:', stack_body):
            continue
        count = re.search(r'(?m)^      count: ([^\s#]+)', stack_body)
        rows.append((proto, uid, count.group(1) if count else 'default'))

for index, (proto, uid, count) in enumerate(rows, 1):
    print(f'{index:02d}. proto={proto}; uid={uid}; count={count}')
print(f'TOTAL={len(rows)}')
PY

echo '=== Базовые Stack-компоненты для оставшихся типов ==='
rg -n -A38 -B3 \
  '^\s*id:\s*(CableStack|CableApcStack|BaseStockPart|MicroManipulatorStockPart)\s*$' \
  Resources/Prototypes --glob '*.{yml,yaml}' || true

echo '=== Все stackType в cable_coils.yml и machines_parts.yml ==='
rg -n -C2 '^\s*stackType:\s*' \
  Resources/Prototypes/Entities/Objects/Tools/cable_coils.yml \
  Resources/Prototypes/_Orion/Entities/Objects/Misc/machines_parts.yml \
  2>/dev/null || true

Length of output: 21806


🏁 Script executed:

#!/bin/bash
set -euo pipefail

map='Resources/Maps/_Goobstation/Shuttles/consul.yml'

python3 - "$map" <<'PY'
from pathlib import Path
import re, sys

path = Path(sys.argv[1])
text = path.read_text(encoding="utf-8")

stack_types = {
    "CableApcStack1": "Cable",
    "CableHVStack1": "CableHV",
    "CableMVStack1": "CableMV",
    "SheetSteel1": "Steel",
}
remove_stack = {
    "CapacitorStockPart",
    "MatterBinStockPart",
    "MicroManipulatorStockPart",
}

out = []
pos = 0
changes = []

for proto_match in re.finditer(r'(?ms)^- proto: ([^\s#]+)\s*\n(.*?)(?=^- proto:|\Z)', text):
    proto, proto_body = proto_match.group(1), proto_match.group(2)
    block_start = proto_match.start(2)

    for entity_match in re.finditer(r'(?ms)^  - uid: (\d+)\s*\n(.*?)(?=^  - uid:|\Z)', proto_body):
        uid, entity_body = entity_match.group(1), entity_match.group(2)
        entity_start = block_start + entity_match.start(2)

        stack = re.search(r'(?m)^    - type: Stack\s*\n((?:      .*(?:\n|$))*)', entity_body)
        if not stack or re.search(r'(?m)^      stackType:', stack.group(1)):
            continue

        absolute_start = entity_start + stack.start()
        absolute_end = entity_start + stack.end()
        original = text[absolute_start:absolute_end]

        if proto in remove_stack:
            replacement = ""
            action = "remove"
        elif proto in stack_types:
            replacement = original.replace(
                "    - type: Stack\n",
                f"    - type: Stack\n      stackType: {stack_types[proto]}\n",
                1,
            )
            action = f"add stackType: {stack_types[proto]}"
        else:
            raise SystemExit(f"Unhandled prototype: {proto}, uid={uid}")

        changes.append((absolute_start, absolute_end, replacement, proto, uid, action))

for start, end, replacement, *_ in sorted(changes, reverse=True):
    text = text[:start] + replacement + text[end:]

print("=== Изменения ===")
for _, _, _, proto, uid, action in changes:
    print(f"uid {uid}: {proto} -> {action}")
print(f"TOTAL={len(changes)}")

print("\n=== Полный diff ===")
import difflib
original = path.read_text(encoding="utf-8").splitlines(keepends=True)
modified = text.splitlines(keepends=True)
print("".join(difflib.unified_diff(
    original, modified,
    fromfile=str(path),
    tofile=str(path),
)))

print("=== Проверка результата ===")
missing = 0
for proto_match in re.finditer(r'(?ms)^- proto: ([^\s#]+)\s*\n(.*?)(?=^- proto:|\Z)', text):
    proto, proto_body = proto_match.group(1), proto_match.group(2)
    for entity_match in re.finditer(r'(?ms)^  - uid: (\d+)\s*\n(.*?)(?=^  - uid:|\Z)', proto_body):
        uid, entity_body = entity_match.group(1), entity_match.group(2)
        stack = re.search(r'(?m)^    - type: Stack\s*\n((?:      .*(?:\n|$))*)', entity_body)
        if stack and not re.search(r'(?m)^      stackType:', stack.group(1)):
            missing += 1
            print(f"MISSING stackType: proto={proto}; uid={uid}")
print(f"Stack components without stackType after patch: {missing}")
PY

echo '=== Stock part definitions: Stack должен отсутствовать ==='
rg -n -A34 -B3 \
  '^\s*id:\s*(BaseStockPart|CapacitorStockPart|MatterBinStockPart|MicroManipulatorStockPart)\s*$|^\s*#.*type: Stack' \
  Resources/Prototypes/Entities/Objects/Misc/machine_parts.yml \
  Resources/Prototypes/_Orion/Entities/Objects/Misc/machines_parts.yml \
  2>/dev/null || true

Length of output: 22413


@UmbiMax, да. Не всем 32 нужен stackType.

  • Для 19 сущностей добавь stackType.
  • Для 13 stock part удали весь map override Stack. Эти предметы больше не stackable. Добавление выдуманного stackType вернёт устаревшую механику и может создать новые ошибки.

Добавить stackType

# uid 186, CableApcStack1
     - type: Stack
+      stackType: Cable
       count: 3
# uid 55, 64, 88 — CableHVStack1
     - type: Stack
+      stackType: CableHV
       count: 5
# uid 53, 62, 86 — CableMVStack1
     - type: Stack
+      stackType: CableMV
       count: 5
# uid 3, 8, 13, 18, 23, 28, 33, 38, 43, 48 — SheetSteel1
     - type: Stack
+      stackType: Steel
       count: 5
# uid 125 — SheetSteel1
     - type: Stack
+      stackType: Steel
       count: 3
# uid 188 — SheetSteel1
     - type: Stack
+      stackType: Steel
       count: 2

Удалить устаревший Stack

Удаляй эти два поля целиком.

# uid 5, 10, 15, 20, 25, 30, 35, 40, 45, 50 — CapacitorStockPart
-    - type: Stack
-      count: 4
# uid 99 — MatterBinStockPart
-    - type: Stack
-      count: 3
# uid 107 — MatterBinStockPart
-    - type: Stack
-      count: 2
# uid 108 — MicroManipulatorStockPart
-    - type: Stack
-      count: 2

Проверка модели изменений показала: после этой правки в Resources/Maps/_Goobstation/Shuttles/consul.yml не остаётся ни одного map Stack без stackType.

You are interacting with an AI system.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Resources/Maps/_Goobstation/Shuttles/consul.yml (1)

3286-3324: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Исправьте скопированное значение stackType: Cable.

Один тип стека не подходит для разных материалов. Неверный тип нарушает stacking и взаимодействия с объектами карты.

  • Resources/Maps/_Goobstation/Shuttles/consul.yml#L3286-L3324: замените Cable на CableHV.
  • Resources/Maps/_Goobstation/Shuttles/consul.yml#L3667-L3705: замените Cable на CableMV.
  • Resources/Maps/_Goobstation/Shuttles/consul.yml#L10878-L11087: замените Cable на Steel или удалите override, если тип уже задан базовым prototype.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Resources/Maps/_Goobstation/Shuttles/consul.yml` around lines 3286 - 3324,
Исправьте скопированные значения stackType в
Resources/Maps/_Goobstation/Shuttles/consul.yml: в диапазоне 3286-3324 замените
Cable на CableHV, в диапазоне 3667-3705 замените Cable на CableMV, а в диапазоне
10878-11087 замените Cable на Steel либо удалите override, если тип уже задан
базовым prototype.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@Resources/Maps/_Goobstation/Shuttles/consul.yml`:
- Around line 3286-3324: Исправьте скопированные значения stackType в
Resources/Maps/_Goobstation/Shuttles/consul.yml: в диапазоне 3286-3324 замените
Cable на CableHV, в диапазоне 3667-3705 замените Cable на CableMV, а в диапазоне
10878-11087 замените Cable на Steel либо удалите override, если тип уже задан
базовым prototype.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: cdd151a5-4549-476e-a230-98d1b9739fc0

📥 Commits

Reviewing files that changed from the base of the PR and between e2bdced and 3472b63.

📒 Files selected for processing (3)
  • Resources/Maps/_Goobstation/Shuttles/consul.yml
  • Resources/Maps/_Goobstation/box.yml
  • Resources/Prototypes/Entities/Structures/Machines/lathe.yml
💤 Files with no reviewable changes (1)
  • Resources/Maps/_Goobstation/box.yml
📜 Review details
⏰ Context from checks skipped due to timeout. (5)
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: YAML Linter
  • GitHub Check: Test Packaging
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: YAML map schema validator
🧰 Additional context used
📓 Path-based instructions (5)
Resources/**/*.{yml,ftl,json}

📄 CodeRabbit inference engine (.cursor/rules/ss14-resources.mdc)

Resources/**/*.{yml,ftl,json}: Follow SS14 naming conventions when editing prototype, localization, and resource files
Follow SS14 upstream maintenance practices when editing resource files

Files:

  • Resources/Maps/_Goobstation/Shuttles/consul.yml
  • Resources/Prototypes/Entities/Structures/Machines/lathe.yml
Resources/**/*.yml

📄 CodeRabbit inference engine (.cursor/rules/ss14-resources.mdc)

Resources/**/*.yml: Follow SS14 ECS prototype patterns and conventions
Follow SS14 graphics generic visualizer appearance conventions and patterns

Resources/**/*.yml: Load ss14-naming-conventions documentation for Resources work
Load ss14-ecs-prototypes documentation for Resources work
Load ss14-upstream-maintenance documentation for Resources work
Load ss14-prototypes-locale documentation for Resources work
Load ss14-localization-strings documentation for Resources work
Load ss14-sprite-overlays-shaders documentation when working with RSI metadata, textures, sprite layers, overlays, or shader resources
Validate YAML after edits

Run dotnet run --project Content.YAMLLinter/Content.YAMLLinter.csproj -c DebugOpt to validate YAML and resource edits.

Files:

  • Resources/Maps/_Goobstation/Shuttles/consul.yml
  • Resources/Prototypes/Entities/Structures/Machines/lathe.yml
Resources/**/*.{yml,ftl}

📄 CodeRabbit inference engine (.cursor/rules/ss14-resources.mdc)

Follow SS14 prototype locale integration rules and practices

Files:

  • Resources/Maps/_Goobstation/Shuttles/consul.yml
  • Resources/Prototypes/Entities/Structures/Machines/lathe.yml
**/*.{cs,xaml,yml,yaml}

📄 CodeRabbit inference engine (AGENTS.md)

If the task adds tests or you need to choose the right test layer, load skill ss14-tests-authoring.

Files:

  • Resources/Maps/_Goobstation/Shuttles/consul.yml
  • Resources/Prototypes/Entities/Structures/Machines/lathe.yml
Resources/Prototypes/**/*.yml

📄 CodeRabbit inference engine (AGENTS.md)

Resources/Prototypes/**/*.yml: Put prototypes under the most specific existing subtree in Resources/Prototypes/. If you introduce a new prototype parent tree, put parent prototypes in base.yml and variants in sibling files.
Keep entity prototype field order as type, abstract, parent, id, categories, name, suffix, description, components. Do not insert blank lines between - type: entries inside a components: list. Separate prototype blocks with one blank line.
Prefer suffix for spawn-menu distinctions instead of changing prototype name.

Files:

  • Resources/Prototypes/Entities/Structures/Machines/lathe.yml

⚙️ CodeRabbit configuration file

Resources/Prototypes/**/*.yml: Review SS14 prototypes carefully:

  • Use the narrowest existing subtree and avoid duplicating nearby prototype files or parent trees.
  • Keep entity field order as: type, abstract, parent, id, categories, name, suffix, description, components.
  • Prefer base.yml for parent trees and sibling files for variants.
  • Do not add blank lines inside components lists.
  • Call out broken inheritance, risky prototype ID changes, missing paired locale updates, and Appearance / GenericVisualizer mismatches.

Files:

  • Resources/Prototypes/Entities/Structures/Machines/lathe.yml
🧠 Learnings (4)
📚 Learning: 2026-07-07T17:55:50.395Z
Learnt from: UmbiMax
Repo: ArcaneSS14/arcane-old PR: 101
File: Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs:192-194
Timestamp: 2026-07-07T17:55:50.395Z
Learning: When reviewing ArcaneSS14/arcane-old changes related to `MobState` thresholds/visuals/actions, treat the `Critical` enum value as requiring explicit mappings (it no longer implicitly behaves as an alias of `SoftCritical`). If any YAML prototype `thresholds:` (for `MobThresholdsComponent.Thresholds`) uses the bare `Critical` key (e.g., `MobThresholdsComponent.Thresholds: { Critical: ... }` for entities like `MobMouseCancer`), ensure the prototype (or referenced config) provides corresponding explicit `StateAlertDict` entries and any required `MobStateActions.actions` and `DamageStateVisuals.states` mappings for `Critical`. Otherwise, review should expect failures because the default `StateAlertDict` in `MobThresholdsComponent.cs` typically maps only `Alive`, `SoftCritical`, `HardCritical`, and `Dead`—not `Critical`.

Applied to files:

  • Resources/Maps/_Goobstation/Shuttles/consul.yml
  • Resources/Prototypes/Entities/Structures/Machines/lathe.yml
📚 Learning: 2026-07-28T15:44:38.498Z
Learnt from: UmbiMax
Repo: ArcaneSS14/arcane PR: 152
File: Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/pistol.yml:69-72
Timestamp: 2026-07-28T15:44:38.498Z
Learning: В ArcaneSS14/arcane не выполняйте форматирующие перестановки полей (в т.ч. изменение порядка полей) в upstream-файлах: делайте это только если правка добавляет Arcane-специфичную сущность или напрямую требуется для функциональности. Иначе сохраняйте upstream-порядок полей, даже если он противоречит локальным рекомендациям, чтобы не создавать лишние diff и конфликты при upstream-синхронизации. Исключение: допустимо переупорядочивание/форматирование в `Resources/Prototypes/_Arcane` (и только там).

Applied to files:

  • Resources/Maps/_Goobstation/Shuttles/consul.yml
  • Resources/Prototypes/Entities/Structures/Machines/lathe.yml
📚 Learning: 2026-06-29T05:34:31.137Z
Learnt from: Alazell
Repo: ArcaneSS14/arcane-station PR: 99
File: Resources/Prototypes/Entities/Clothing/Belt/belts.yml:973-974
Timestamp: 2026-06-29T05:34:31.137Z
Learning: В SS14 YAML-прототипах для компонента `Storage` поле `grid` задаёт **включительные** координаты прямоугольника. При подсчёте вместимости учитывайте оффсет-инклюзивность: прямоугольник `x1,y1,x2,y2` означает размеры `width = (x2 - x1 + 1)` и `height = (y2 - y1 + 1)`, а число ячеек `width * height`. Например, `0,0,0,0` = 1 ячейка, а `0,0,6,2` = (6-0+1)*(2-0+1)=7*3=21. При ревью файлов наподобие `Resources/Prototypes/Entities/Clothing/Belt/belts.yml` не интерпретируйте `x2/y2` как эксклюзивные (чтобы не ошибиться на 1 в ширине/высоте).

Applied to files:

  • Resources/Prototypes/Entities/Structures/Machines/lathe.yml
📚 Learning: 2026-07-29T08:12:37.068Z
Learnt from: UmbiMax
Repo: ArcaneSS14/arcane PR: 153
File: Resources/Prototypes/_Orion/Entities/Objects/Tools/energy_dome.yml:137-137
Timestamp: 2026-07-29T08:12:37.068Z
Learning: In ArcaneSS14 prototype YAMLs, the YAML field that disables synchronization for `Content.Shared.Power.Components.BatteryComponent` is spelled exactly `netsync` (all lowercase), not `netSync`. For prototypes derived from `PowerNetworkBattery` (including `BaseAPC`, `CoreSubstation`, `BaseSMES`, `TeslaCoil`), set `netsync: false`; otherwise `Content.Server.Power.EntitySystems.BatterySystem.OnStartup` can throw `DebugAssertException` for a predicted battery connected to a power net. For `Resources/Prototypes/_Orion/Entities/Objects/Tools/energy_dome.yml`, ensure `EnergyDomeWiredTest` uses `netsync: false`.

Applied to files:

  • Resources/Prototypes/Entities/Structures/Machines/lathe.yml
🔇 Additional comments (4)
Resources/Prototypes/Entities/Structures/Machines/lathe.yml (2)

116-116: 🗄️ Data Integrity & Integration

Проверьте слияние MaterialStorage в итоговых прототипах.

BaseSiloUtilizer добавляет MaterialStorage.connectToSilo: true, но каждый изменённый prototype повторно объявляет MaterialStorage. Если наследование заменяет компонент целиком, новые машины не подключатся к silo, несмотря на добавленный parent.

Проверьте resolved-прототипы для всех десяти сущностей. В каждом должны сохраниться SiloUtilizer, DeviceNetwork, DeviceLinkSink и connectToSilo: true. Контракт определён в Resources/Prototypes/_Goobstation/Entities/Structures/Machines/material_silo.yml, Lines 43-57.

Also applies to: 211-211, 291-291, 361-361, 469-469, 539-539, 575-575, 651-651, 748-748, 847-847


116-116: 🗄️ Data Integrity & Integration

Запустите обязательную проверку YAML.

Перед слиянием выполните dotnet run --project Content.YAMLLinter/Content.YAMLLinter.csproj -c DebugOpt.

As per coding guidelines: для изменений в Resources/**/*.yml требуется валидировать YAML и resource-изменения.

Also applies to: 211-211, 291-291, 361-361, 469-469, 539-539, 575-575, 651-651, 748-748, 847-847

Source: Coding guidelines

Resources/Maps/_Goobstation/Shuttles/consul.yml (2)

3208-3208: LGTM!

Also applies to: 3732-3807, 9777-9788, 9852-9855


3208-11087: 🗄️ Data Integrity & Integration

Запустите обязательную проверку YAML.

После исправления stackType выполните dotnet run --project Content.YAMLLinter/Content.YAMLLinter.csproj -c DebugOpt.

As per coding guidelines: для изменений в Resources/**/*.yml требуется валидировать YAML и resource-изменения.

Source: Coding guidelines

@UmbiMax

UmbiMax commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

@coderabbitai Какие ещё ошибки в тестах и как их фиксить?

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ArcaneSS14/arcane"
pr="161"

sha="$(gh pr view "$pr" --repo "$repo" --json headRefOid --jq .headRefOid)"
echo "=== PR head ==="
echo "$sha"

echo "=== Checks ==="
gh pr checks "$pr" --repo "$repo" --required=false || true

echo "=== Workflow runs for current head ==="
gh api "repos/$repo/actions/runs?head_sha=$sha&per_page=100" \
  --jq '.workflow_runs[] | {id, name, status, conclusion, run_attempt, created_at, updated_at, html_url}'

run_id="$(
  gh api "repos/$repo/actions/runs?head_sha=$sha&per_page=100" \
    --jq '[.workflow_runs[] | select(.name == "Build & Test Debug" and .status == "completed")]
      | sort_by(.created_at) | last.id // empty'
)"
echo "=== Selected Build & Test Debug run ==="
echo "${run_id:-none}"

if [ -n "${run_id:-}" ]; then
  echo "=== Jobs ==="
  gh api "repos/$repo/actions/runs/$run_id/jobs?per_page=100" \
    --jq '.jobs[] | {id, name, status, conclusion, started_at, completed_at}'

  job_id="$(
    gh api "repos/$repo/actions/runs/$run_id/jobs?per_page=100" \
      --jq '.jobs[] | select(.name == "build (ubuntu-latest)") | .id' | tail -1
  )"
  echo "=== Selected build job ==="
  echo "${job_id:-none}"

  if [ -n "${job_id:-}" ]; then
    log="$(mktemp)"
    trap 'rm -f "$log"' EXIT
    gh api "repos/$repo/actions/jobs/$job_id/logs" > "$log"

    echo "=== First failure ==="
    rg -n -i -m 1 -B 30 -A 120 \
      'Error Message:|^\s*Failed [A-Za-z0-9_.]+|Unhandled exception|Exception:|Test Run Aborted|FailFast|Missing prototype|does not exist in RSI|Expected:|But was:' \
      "$log" || true

    echo "=== Failure headers and summaries ==="
    rg -n -i -C 12 \
      '^\s*Failed [A-Za-z0-9_.]+|Error Message:|Test Run Aborted|FailFast|Failed!|Passed!|Total tests|Run Content\.IntegrationTests' \
      "$log" || true
  fi
fi

echo "=== Current relevant PR diff ==="
gh api "repos/$repo/pulls/$pr/files?per_page=100" --paginate \
  --jq '.[] | select(.filename | test("(^Resources/Maps/_Goobstation/(box|kettle|leonid|loop)\\.yml$)|(^Resources/Maps/_Goobstation/Shuttles/consul\\.yml$)|(^Resources/Prototypes/.*(lathe|research)|^Content\\.IntegrationTests)")) | {filename, additions, deletions}' || true

Length of output: 89145


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ArcaneSS14/arcane"
job_id="91342184086"
log="$(mktemp)"
trap 'rm -f "$log"' EXIT
gh api "repos/$repo/actions/jobs/$job_id/logs" > "$log"

echo "=== AllTechPrintableTest: only violation messages ==="
rg -n \
  "Recipe '.+' from tech '.+' cannot be unlocked on any lathes|Recipe '.+' is dynamic on lathe.*but is not unlocked|dynamic.*not.*tech|cannot.*print" \
  "$log" || true

echo "=== AllTechPrintableTest: full block ==="
start="$(rg -n 'Failed AllTechPrintableTest' "$log" | head -1 | cut -d: -f1)"
end="$(rg -n 'GetPair: Pair 5 Test History End|Failed RepairTest' "$log" | head -1 | cut -d: -f1)"
if [ -n "${start:-}" ] && [ -n "${end:-}" ]; then
  sed -n "${start},${end}p" "$log"
fi

echo "=== RepairTest: full failure and preceding server/client errors ==="
repair="$(rg -n 'Failed RepairTest' "$log" | head -1 | cut -d: -f1)"
if [ -n "${repair:-}" ]; then
  from=$((repair - 260))
  [ "$from" -lt 1 ] && from=1
  to=$((repair + 180))
  sed -n "${from},${to}p" "$log"
fi

echo "=== Error-level logs near RepairTest ==="
rg -n -i -C 14 \
  'RepairTest|Unhandled|exception|fatal|critical|server.*(error|exception)|client.*(error|exception)|pair 6|Dirty return|state\. Dead' \
  "$log" | tail -900 || true

echo "=== Recipe / research / lathe source map ==="
for file in \
  Resources/Prototypes/Entities/Structures/Machines/lathe.yml \
  Content.IntegrationTests/Tests/ResearchTest.cs
do
  echo "----- OUTLINE $file -----"
  ast-grep outline "$file" --items all --type class,method --view expanded 2>/dev/null || true
done

rg -n -i -C 5 \
  '^\s*(staticPacks|dynamicPacks):|MedicalEquipment|UnregulatedBluespaceResearch|Consoles|GeneEngineering|NightVision|BasicArms' \
  Resources/Prototypes --glob '*.{yml,yaml}' || true

Length of output: 202184


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ArcaneSS14/arcane"
pr="161"
job_id="91342184086"
log="$(mktemp)"
trap 'rm -f "$log"' EXIT
gh api "repos/$repo/actions/jobs/$job_id/logs" > "$log"

echo "=== Unique AllTechPrintableTest violations ==="
python3 - "$log" <<'PY'
from pathlib import Path
import re, sys

text = Path(sys.argv[1]).read_text(errors="replace")
start = text.find("Failed AllTechPrintableTest")
end = text.find("GetPair: Pair 5 Test History End", start)
block = text[start:end if end != -1 else None]
block = re.sub(r'\x1b\[[0-9;]*m', '', block)

patterns = [
    r"Recipe '([^']+)' from tech '([^']+)' cannot be unlocked on any lathes\.",
    r"Recipe '([^']+)' is dynamic on (?:lathe|lathes).*?(?:but|and).*?(?:not unlocked|not.*tech).*",
    r"Recipe '([^']+)'.{0,180}(?:dynamic).{0,180}(?:not unlocked|not.*tech).*",
]
seen = set()
for line in block.splitlines():
    for pattern in patterns:
        match = re.search(pattern, line, re.I)
        if match:
            value = match.group(0)
            if value not in seen:
                seen.add(value)
                print(value)
            break
print(f"TOTAL={len(seen)}")
PY

echo "=== RepairTest source ==="
file="$(fd -HI -t f '^VendingInteractionTest\.cs$' Content.IntegrationTests | head -1)"
echo "$file"
ast-grep outline "$file" --items all --type class,method --match 'VendingInteractionTest|RepairTest' --view expanded
sed -n '130,205p' "$file"

echo "=== InteractionTest.Activate source ==="
helpers="$(fd -HI -t f '^InteractionTest\.Helpers\.cs$' Content.IntegrationTests | head -1)"
echo "$helpers"
sed -n '350,410p' "$helpers"

echo "=== Repair-related prototypes and systems ==="
rg -n -i -C 10 \
  'RepairTest|Repair.*Vending|Vending.*Repair|Repairable|Repair.*(Weld|Wire|Crowbar)|Broken.*Vending|VendingMachine' \
  Content Resources --glob '*.{cs,yml,yaml}' \
  -g '!Content.IntegrationTests/Tests/Vending/VendingInteractionTest.cs' | head -700

echo "=== PR files that could affect vending or interaction ==="
gh api "repos/$repo/pulls/$pr/files?per_page=100" --paginate \
  --jq '.[] | select(.filename | test("(?i)(vending|interaction|repair|tool|lathe|research)")) | .filename'

Length of output: 51876


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ArcaneSS14/arcane"
job_id="91342184086"
log="$(mktemp)"
trap 'rm -f "$log"' EXIT
gh api "repos/$repo/actions/jobs/$job_id/logs" > "$log"

echo "=== All AllTechPrintableTest numbered assertions ==="
rg -n -A4 -B1 \
  '^[0-9]+\) +Recipe |AllTechPrintableTest>b__1\(\).*ResearchTest\.cs:line (93|100)' \
  "$log" | sed -n '1,500p'

echo "=== ResearchTest assertions ==="
sed -n '51,112p' Content.IntegrationTests/Tests/ResearchTest.cs

echo "=== Test-only vending prototype ==="
rg -n -A90 -B5 \
  'id:\s*InteractionTestVendingMachine|id:\s*InteractionTestItem|id:\s*InteractionTestRestockBox' \
  Content.IntegrationTests Resources --glob '*.{yml,yaml,cs}' || true

Length of output: 42471


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ArcaneSS14/arcane"
job_id="91342184086"
log="$(mktemp)"
trap 'rm -f "$log"' EXIT
gh api "repos/$repo/actions/jobs/$job_id/logs" > "$log"

echo "=== Dynamic recipes without a research unlock ==="
rg -o "Recipe '[^']+' is dynamic on a lathe but cannot be unlocked by research\." "$log" \
  | sed -E "s/.*Recipe '([^']+)'.*/\1/" \
  | sort -u

echo "=== Counts by AllTechPrintableTest invariant ==="
printf 'tech unlock without dynamic lathe recipe: '
rg -c "cannot be unlocked on any lathes\." "$log" || true
printf 'dynamic lathe recipe without research unlock: '
rg -c "is dynamic on a lathe but cannot be unlocked by research\." "$log" || true

echo "=== Test target prototype location ==="
rg -n -l --glob '*.{cs,yml,yaml}' 'InteractionTestVendingMachine' . | while IFS= read -r file; do
  echo "--- $file ---"
  rg -n -A80 -B5 'InteractionTestVendingMachine' "$file"
done

Length of output: 4621


@UmbiMax, в текущем CI осталось две первичные группы ошибок.

Content.Tests проходят: 372 passed.
Ошибки карт, Stack, spawn point и JobPrototype в этом запуске больше не появляются.

1. AllTechPrintableTest: 34 несоответствия research ↔ lathe

Тест проверяет два правила:

  1. Каждый technology.recipeUnlocks должен быть в dynamicPacks или emagDynamicPacks хотя бы одного лата.
  2. Каждый dynamic recipe лата должен разблокироваться хотя бы одной technology.

A. Research разблокирует recipe, которого нет ни в одном dynamic lathe pack

Нарушений: 14.

Technology Recipe
MedicalEquipment Bloodpack, SawElectric, ClothingNeckStethoscope
UnregulatedBluespaceResearch MedicalBeamGunSyndicate
Consoles MedicalRecordsComputerCircuitboard, CrewMonitoringComputerCircuitboard
GeneEngineering MedicalScannerMachineCircuitboard
NightVision ClothingEyesNightVisionMedicalGoggles
BasicArms MagazineShotgun, MagazineShotgunEmpty, MagazineShotgunSlug
Cytology MedicalBiofabMachineBoard, DiagnoserMachineCircuitboard, VaccinatorMachineCircuitboard

Фикс: для каждого recipe добавить содержащий его latheRecipePack в dynamicPacks нужного лата.

Основные файлы:

  • Resources/Prototypes/Entities/Structures/Machines/lathe.yml
  • Resources/Prototypes/_Goobstation/Entities/Structures/Machines/lathe.yml
  • Resources/Prototypes/_Orion/Research/Nodes/*.yml
  • Resources/Prototypes/**/Recipes/Lathes/Packs/*.yml

Не переносите весь набор staticPacks в dynamicPacks без проверки баланса. Например, medical recipes должны быть dynamic на MedicalTechFab, а security ammo — на AmmoTechFab или SecurityTechFab.

B. Lathe содержит dynamic recipe без research unlock

Нарушений: 20.

AdvancedJetInjector
BorgModuleAdvancedTopical
BorgModuleArtistry
BorgModuleInflatable
BoxFolderClipboardEmpty
BoxFolderPlasticClipboardEmpty
CableDetStack1
ChemistryEmptyVialSmall
EmptyDetonator
FauxTileAstroIronsand
FauxTileAstroIronsandBorderless
FauxTileDarkAstroGrass
FauxTileDesertAstroSand
FauxTileLightAstroGrass
JetInjector
MaterialDurathread
SmartFridgeCircuitboard
TowelColorWhite
UtilityKnife
VoiceSensor

Фикс: для каждого recipe выбрать одно из двух действий:

  • Добавить recipe в recipeUnlocks подходящей technology.
  • Перенести recipe из dynamicPacks в staticPacks, если research не должен быть нужен.

Рекомендуемый принцип:

  • SmartFridgeCircuitboard, VoiceSensor, AdvancedJetInjector, borg modules и MaterialDurathread — добавить в подходящие research nodes.
  • TowelColorWhite, пустые папки, EmptyDetonator, базовый UtilityKnife, базовые кабели и часть faux tiles — вероятнее должны быть static recipes. Проверьте игровой дизайн перед переносом.
  • Не исправляйте это удалением assertion из ResearchTest. Тест обнаруживает недоступный игроку контент и контент без легального unlock.

2. Vending.VendingInteractionTest.RepairTest

Это вторая независимая ошибка.

BreakVendor() наносит vending machine 100 blunt damage:

var damage = new DamageSpecifier(damageType, FixedPoint2.New(100));

После этого Target уже отсутствует на сервере. Первый Activate() после BreakVendor() падает на проверке:

SEntMan.TryGetEntity(target!.Value, out var sTarget)
Expected: True
But was: False

То есть vending machine не остаётся в состоянии «сломана, но ремонтируема». Она удаляется. Вероятная причина — 100 damage теперь достигает destruction threshold, а не только broken threshold.

Фикс теста:

  1. В BreakVendor() применяйте damage, который переводит machine в broken state, но не достигает destruction threshold.
  2. Либо переопределите Destructible только у InteractionTestVendingMachine, чтобы 100 damage не удалял test entity.
  3. Добавьте assertion сразу после BreakVendor():
Assert.That(SEntMan.EntityExists(SEntMan.GetEntity(Target)), Is.True);

Лучше не менять production destruction thresholds ради integration test. Тест должен создавать именно repairable state.

Каскадные ошибки

Ошибки вида:

Pool manager has not been initialized

не являются отдельными дефектами. Они начинаются после смерти pair 6 в RepairTest.

После исправления research/lathe и RepairTest нужен новый запуск Build & Test Debug. Только он покажет ошибки, которые сейчас маскирует каскад.

You are interacting with an AI system.

@UmbiMax
UmbiMax merged commit 2d1c1ee into ArcaneSS14:master Aug 1, 2026
11 of 12 checks passed
This was referenced Aug 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants