# Workflow and rules

Daily development workflow, development rules, and long-term maintenance of versions, dependencies and docs

Source: https://socketsvc.vercel.app/client/development/contributing
## Daily workflow

<Steps>
<Step>

Confirm the working directory and branch first, and understand the requirement, the affected modules and an observable completion condition. When fixing a bug, find the reproduction conditions and cause before changing code.

</Step>
<Step>

Work on a feature branch, e.g. `feat/client-device-ui`, `fix/voice-reconnect`. Keep each change focused on one requirement, and preserve other uncommitted work.

</Step>
<Step>

Look for existing implementations and tests first, and decide whether the change belongs in shared code, version adaptation, server or shared; avoid copying the same fix into every version.

</Step>
<Step>

Run the tests relevant to the change. If it touches the shared client, versions, Mixins or resources, run the full client matrix before committing; if it touches server/shared, also run the server build. See [common builds and tests](/client/development/setup#common-builds-and-tests) for commands.

</Step>
<Step>

Check the diff, the generated artifacts and the test evidence; accept behaviour that can only be confirmed in-game separately, and record anything left unfinished.

</Step>
<Step>

Update the related docs, tidy up commits/PR, and merge or release only after the checks and review pass (see [commit and review](/client/development/ci-release#commit-and-review)).

</Step>
</Steps>

## Development rules

### Code and state management

- Java follows the existing naming style with four-space indentation; files are UTF-8. Do not mix unrelated whole-file reformatting into feature fixes.
- Prefer changing the layer that actually owns the behaviour and reuse existing flows; new abstractions, dependencies or background threads need a concrete purpose.
- Minecraft UI/game state operations go back to the client game thread; Paper state operations respect the existing main-thread/cache boundaries. Never block an audio worker or WebSocket callback waiting for the game thread.
- Audio and network queues stay bounded; slow receivers, malformed packets and failing devices must not cause unbounded buildup, unbounded retries or resource leaks.
- Connections, devices and native codecs need an explicit lifecycle. After going offline/switching servers, old callbacks, token responses or workers must not modify the new session.
- Keep the existing constraints for mute, deafen, first-time setup and stopping speech; audio changes must verify local and remote speaking, stopping, resuming and device switching together.

### Network protocol and config compatibility

- [common/core](https://github.com/HippoDev03/SocketSVC/tree/main/common/core/src/main/java) is the protocol definition shared by both sides; after changing it, verify both client and server, not just that one side compiles.
- New capabilities need explicit negotiation and a fallback for older versions. Never send un-negotiated new control messages to an old server.
- Keep one-time tokens, player binding, and packet size/sequence/rate checks; logs and commits must not contain real tokens or full URLs that include a token.
- When changing the wire format, update the [voice protocol](/server/voice-protocol) docs in the same change, covering mixed old/new sides, invalid data, reconnects and rollback behaviour.
- New config fields need a safe default, old-file reading and invalid-value handling; keep the existing corrupt-file backup and atomic-write behaviour so player settings are not destroyed.
- Do not change the voice protocol or config format just because Minecraft is being upgraded; feature versions, game versions and the wire protocol are separate maintenance items.

### Mixins and rendering

- Compare against the target version's actual methods and bytecode; renaming without checking the call path can make icons disappear or shift.
- Nametags keep vanilla's visibility conditions, scoreboard offset and special-name offsets, and must never expose player names that were hidden.
- Changing a hook requires updating the corresponding bytecode contract test and running `mixinTest`; never lower `required`/`defaultRequire` or skip tests to hide an incompatibility.
- In-game visuals, occlusion, fonts, GUI scale and interaction flows still need in-game acceptance; successful compilation and Mixin loading do not mean the visuals are correct.

### Icons and translations

- Voice status icon source art lives in `images/`; replace the files with the same names in `client/common/src/main/resources/assets/socketsvc/textures/gui/` too. Updating only the source art does not change the game artifact.
- The status icons are currently 128×128 PNGs with transparency; their on-screen size is controlled by the UI and font settings, not the source image size.
- The HUD and settings buttons currently render at 24×24 GUI units, and the nametag font has height 12 and ascent 9; enlarging the source images does not enlarge these display sizes.
- When changing sizes, paths or what an icon is used for, check `ModernButton`, `VoiceHud`, `font/voice_icons.json` and the existing resource tests; inspect the images inside the final JAR.
- User-visible client text is maintained through `VoiceTranslations` and `assets/socketsvc/lang/`; every bundled catalog must keep the same keys and format arguments as `en_us.json`. Player names and device names are not translated.
- A new `Screen` that overrides `render` must be added to the `void render(` rule's `files` list in `gradle/compat/modern.json`. That rename is file-scoped, unlike the global ones around it, so a screen left out of the list compiles on the default (legacy) Minecraft version and fails only on 26.x with "method does not override or implement a method from a supertype". Build one modern version before assuming a UI change is done.
- When adding a language, drop a new `<locale>.json` next to the others — `TranslationRegressionTest` discovers it from the directory and checks its keys, its `%s` arguments and its English fallback with no test change needed. Then check small windows, long text and keyboard operation.

For what each status icon means, see [settings](/client/settings).

## Future changes and long-term maintenance

### Sources of truth

Avoid keeping version information separately in docs, scripts and workflows. The settings below are the primary sources for the corresponding facts; version tables and example commands in the docs are readable summaries and should be checked whenever the settings change.

| Information | Primary source | Also check when it changes |
| --- | --- | --- |
| Supported versions, loaders, dependencies and feature versions of the three sides | `gradle/socketsvc.properties` | Validation in `settings.gradle.kts`, the matrix script, the Modrinth script, [development setup](/client/development/setup) and [versions and loaders](/client/development/versions) |
| Gradle version | `gradle/wrapper/gradle-wrapper.properties` | Wrapper files, Loom/Shadow compatibility, CI |
| Gradle daemon Java | `gradle/gradle-daemon-jvm.properties` | The `updateDaemonJvm` setup in the root `build.gradle.kts`, IDE, CI JDK installation |
| Client Java per version | `<MC>.java` in the version table | Mixin level, bytecode, test JVM, native libraries |
| Paper dependency | `server/paper/build.gradle.kts` | API declaration in `plugin.yml`, server tests, install instructions |
| Wire protocol and negotiation | `common/core/src/main/java`, both sides' connection code | [Voice protocol](/server/voice-protocol), mixed-version and malicious-input tests |
| Client config defaults/compatibility | `SocketSvcConfig` | UI, translations, old-config tests |
| Server default config | `server/paper/src/main/resources/config.yml` and `server/common/src/main/resources/socketsvc-server.yml` (must be identical, checked by `ServerModConfigTest`); reading logic in `VoiceServerSettings` | reload/restart boundaries, admin commands, docs |
| Modrinth publishing config | `scripts/modrinth.json` | Loader tags, dependencies, `modrinth_release.py` |
| Validation and publishing | `scripts/build_matrix.py`, `scripts/publish_release.py` | Required JDKs, local environment, report/artifact paths, release notes |

The version catalog does not currently manage the Gradle Wrapper or all shared libraries (see `gradle/client-libraries.gradle`, `gradle/server-libraries.gradle`), so changing it alone does not complete a toolchain upgrade. If the docs disagree with the code, find out the actual behaviour and fix the discrepancy; do not assume the docs or old comments are correct.

### Handling common upgrades

| Change type | Implementation and verification | Rollback considerations |
| --- | --- | --- |
| New Minecraft release or patch | Confirm the actual API/dependencies first and add the version row explicitly; compile, Mixins, resources, full matrix and in-game testing (see [adding a release version](/client/development/versions#adding-a-release-version)) | A single target version can be withdrawn while keeping other versions' artifacts |
| Snapshot/pre-release/RC | Experiment on a separate development branch; do not add it to the supported list just because it compiles | Not published as officially supported without passing acceptance |
| Java/Gradle/Loom upgrade | Distinguish daemon Java from each version's runtime Java; update the Wrapper, daemon task config and CI together; verify the oldest and newest target versions | Keep old bytecode requirements; a tool upgrade must not accidentally raise the Java floor for all players |
| Fabric API/Loader/Mod Menu upgrade | Each affected version uses dependencies that actually exist and are compatible; make sure the optional Mod Menu does not become a required runtime dependency | Keep verified dependency combinations; adapt separately when there are binary differences |
| Paper/server upgrade | Confirm the API, actual runtime Java and plugin declaration; run server tests and test together with the client | The client version table does not guarantee server rollback or Paper compatibility |
| Opus/RNNoise/other native updates | Check the JNI Java API, resources for all six platforms and JAR packaging; run the native regression and test on every OS/architecture claimed | Packaged files existing does not mean they load; passing on one platform does not justify claiming all |
| WebSocket/protocol feature extensions | Enable only after capability negotiation; spell out new client + old server, old client + new server, new + new, and reconnect scenarios | Docs must describe the fallback when not negotiated; incompatible changes must not ship silently |
| Config/stored data migration | Define old-file reading, new defaults, invalid-value handling, backups and any needed schema detection; add old-file fixture tests | State whether old code can read it back; if not, provide backup restore steps |
| New language, icon size or font | Sync source art/packaged resources/references/tests; verify small screens, long text, transparency and nametags | Keep the path contract, or update every consumer in the same change |
| New platform, loader or feature module | Locate existing reusable boundaries first, then split platform implementations, dependencies and test matrix only for actual differences | Do not build a speculative plugin framework up front; label each artifact's support scope clearly |
| CI runner/action/release process update | Confirm permissions, toolchains, native runtime environment, artifact selection and failure reports; verify on a branch first | Keep automated builds and automated publishing separate; failed or unverified matrix entries must not count as a successful release |

Split upgrade work into units whose failures can be pinned down: keep the existing target versions buildable first, then introduce the new tool or API, and only then add the support claim. If upstream constraints make the steps inseparable, the PR must explain why they are bundled and what is affected.

### Evolving features and support scope

General feature fixes go into the shared client first; as platform differences grow, consider extracting concrete boundaries (e.g. screens, keybinds, network registration) into small interfaces. Keep transformation rules for pure name differences, and use Java implementations when behaviour or call timing differs; do not hide conditional logic in large text replacements.

If a source set, module or another loader is added in future, also confirm whether it needs version adaptation, whether it ends up in the release JAR, which tests run, and whether CI actually includes it; do not assume the generation flow, which currently only handles main/test, will cover new directories automatically.

<Callout type="info" title="Evaluation rules, not commitments">
When adding groups, recording, permissions or other voice features, first define the user flow, state owner, protocol requirements, resource limits and completion conditions, then decide whether to extend the architecture. These are rules for evaluating future work; they do not mean the features are implemented or promised.
</Callout>

### Version retirement and release records

The version table currently only has `supported`, with no status fields such as `experimental` or `deprecated`. To retire a version, first describe the scope and the replacement version in the release notes; only after support has formally ended remove the version, update `default`, the docs and validation rules, and confirm the matrix still has valid entries (see [withdrawing a version or loader](/client/development/versions#withdrawing-a-version-or-loader)).

If a machine-readable version lifecycle is needed in future, update the version table schema, Gradle validation, Python parsing, CI and tests together; adding a field alone does not mean the process supports it.

Every release records at least: commit/tag, client/server feature versions, Minecraft range, toolchain, verified OS/devices and mixed-version scenarios, known limitations, config/protocol compatibility and rollback method. Release notes can be compiled from PRs; nothing currently generates these records or publishes automatically.

### Documentation done criteria

Any change to installation, commands, config, dependencies, artifact paths, support scope or verification should update the corresponding docs and examples in the same PR. Before finishing, confirm:

- Newly documented versions/tasks/files exist in the actual configuration, and documented commands work in the shell described.
- The automation the docs describe actually exists; unfinished designs, tests and in-game acceptance are clearly marked.
- Old data, mixed-version deployment and rollback are explained, without asking users to blindly delete config or caches.
- No second copy of a CI list that can be generated from the version table; intentionally kept version summaries are in sync.
- Outdated entry points have become historical notes or point to the latest docs, so newcomers do not follow wrong paths.