## Request

**Symptom**
In multi-tenant SaaS deployments, root tenant administrators need to activate many users across secondary tenants by issuing password reset emails. Currently, every click of the “Send password reset email” button forces a step-up TOTP confirmation, making bulk user activation extremely slow (around 40 minutes for 20 users).

**Desired Behaviour**
When a root tenant administrator is operating on a secondary tenant through a **management session**:

1. The “Send password reset email” operation must **not** require step-up confirmation (no TOTP prompt). The admin should be able to trigger the password reset email without additional authentication.
2. The front-end must not show the confirmation modal for this operation while in a management session.

When an administrator is operating on their **own tenant** (including the root tenant’s own admin interface):

3. The “Send password reset email” operation must **still** require step-up confirmation, exactly as before.

All other sensitive admin operations (set password, enable encryption, toggle escrow, etc.) must continue to require confirmation regardless of session type, just as they do today.

**Constraints & Safety**
- The exemption must be scoped only to the “Send password reset email” action. Do not relax confirmation requirements for any other operation.
- The exemption must be applied at the backend authorization level (`check_confirmation`) so that the server does not incorrectly accept unconfirmed requests for non-management sessions, even if the front-end fails to enforce.
- A management session is identifiable by a session property (currently `management_session`). Use that property to decide whether to skip confirmation for this action. If the property is absent or false, confirmation must be enforced.
- For clarity, remove the “send_password_reset_email” operation from the global `require_confirmation` list (since it now decides confirmation dynamically based on session type) and enforce the rule inside the operation handler itself. The front-end confirmation logic should mirror this dynamic decision.

Exempt management sessions from confirmation on password reset link


**Request:**
When a whistleblower or a receiver fetches the details of a tip via the REST API, the returned JSON payload should make it unambiguous which role the calling actor holds. Add two mutually-exclusive boolean fields to every tip response: `im_whistleblower` and `im_receiver`. For a whistleblower’s request, set `im_whistleblower` to `true` and `im_receiver` to `false`; for a receiver’s request, set the opposite. Both fields must be present in every response so clients can reliably branch on role without inspecting other context. This applies to all tip-retrieval responses across the relevant endpoints.

updated tip REST to easily discern if WB or receiver has the data


We need to enforce that every tenant's subdomain is globally unique—including situations where a hostname's first label collides with an existing subdomain—across tenant creation (admin and signup), tenant updates, and hostname changes.

## Problem
Right now, it is possible to:
- create a tenant whose subdomain equals an existing tenant’s subdomain (or the leading part of another tenant’s hostname)
- change a hostname to one whose first label matches another tenant’s subdomain
- for secondary tenants, use a hostname that ends with the root tenant’s hostname
- bypass these checks during public signup

This leads to routing conflicts and security issues.

## Desired Behavior / Acceptance Criteria

1. **Tenant creation (admin + signup)**
   - If the requested subdomain is already in use by *any* other tenant—whether stored as `subdomain` or as the first label of a tenant’s `hostname`—the request must be rejected.
   - Rejection should produce a clear error (e.g., `ForbiddenOperation`).

2. **Tenant update**
   - When an existing tenant’s subdomain is changed, the same uniqueness check applies, but the tenant itself is excluded from the conflict check (i.e., it may keep its own subdomain).

3. **Hostname assignment (admin set-hostname operation)**
   - The hostname must not be an exact duplicate of another tenant’s hostname.
   - The hostname’s first label must not match any other tenant’s subdomain.
   - The hostname must not end with a forbidden origin: the existing forbidden endings (`onion`, `localhost`) must still be blocked.
   - **For secondary tenants only** (tenant id != 1): the hostname must not end with the root tenant’s hostname (i.e., it cannot be a subdomain of the root tenant). This check is in addition to the existing forbidden endings.
   - Violations should raise `InputValidationError` with the existing message “Hostname contains a forbidden origin or is already reserved.”

4. **Signup flow**
   - The public signup endpoint must also reject a signup when the requested subdomain collides with an existing tenant’s subdomain or hostname label.

## Notes / Non-goals
- The checks should be performed **before** any configuration is written to the database.
- The existing behavior that rejects an update when the resulting subdomain + root domain equals the root tenant’s hostname must be preserved and co-exist with the new rules.
- The implementation should avoid duplicating the uniqueness logic across the creation, update, and signup paths.

Enforce tenant subdomain uniqueness across signup and admin paths


Token acquisition for whistleblower submissions and authentication currently requires several steps and enforces a minimum waiting period before a token can be used. The goal is to remove the forced minimum delay and to streamline the client-side flow so that callers get a ready‑to‑use token in one step.

**What needs to change**

1. **Backend: eliminate the minimum use delay**
   - Once a token’s proof‑of‑work answer is accepted (the token is “solved”), it should be immediately usable.
   - The backend must no longer reject a `Token.use` call with “Too early to use this token” based on a separate `min_ttl`.
   - Only a single expiration timeout should exist – a token that is solved is valid until it expires after its total lifetime.

2. **Backend: serialization of tokens**
   - The token payload sent to the client (via `/api/token` creation and subsequent responses) must contain a single `ttl` field (integer, seconds) instead of the current `min_ttl` and `max_ttl` fields.
   - The field `ttl` represents the total lifetime of the token (e.g., 1800).

3. **Client: combine token creation, proof‑of‑work, and answer submission**
   - The client code that needs a token (the `Authentication` service when performing login, and the `Submission` service when creating a submission) should be able to retrieve a fully‑solved token with a single call, without orchestrating multiple requests.
   - The proof‑of‑work computation and submission must happen inside the token resource, not as a separate service (`glbcToken`) that the caller has to manage.
   - As a consequence, the client must no longer artificially delay the returned token; a solved token is available immediately after the proof‑of‑work is submitted and accepted.

**Acceptance criteria (observable behaviour)**

- A solved token can be used right away. Making a request that calls `TokenList.use` on a solved token succeeds regardless of how long ago the token was created; no “too early” error is raised.
- The JSON representation of a token includes `ttl` and does **not** include `min_ttl` or `max_ttl`.
- The `Authentication` login workflow and the `Submission` creation workflow both receive a solved token without imposing a client‑side minimum waiting period.
- The overall token workflow remains secure: unsolved tokens still cannot be used, and tokens that are past their lifetime (`ttl`) are rejected.


**Title:** Replace deprecated CSP `report-uri` with `report-to` + `Reporting-Endpoints`

**Description:**
The application currently uses the deprecated `report-uri` directive in its Content-Security-Policy headers to send violation reports to `/api/report`. Modern browsers are starting to drop support for `report-uri` in favor of the `report-to` directive combined with a `Reporting-Endpoints` response header. This means our CSP violation reporting will stop working, and we also fail best‑practice security header audits (e.g., Observatory, securityheaders.com) that expect the newer mechanism.

We need to update every place in the codebase where a CSP policy is assembled so that:

- The `report-uri /api/report;` entry is replaced with `report-to csp-endpoint`
- A new response header `Reporting-Endpoints` is set to `csp-endpoint="/api/report"` (the endpoint URL path must remain exactly `/api/report`)
- The change must apply consistently across **all** CSP policies that the server returns, including:
  - The entry point (`/index.html`)
  - The crypto worker (`/workers/crypto.worker.js`)
  - The file viewer (`/viewer*`)
  - Any other fallback policy that currently includes a `report-uri` directive
- The `Reporting-Endpoints` header should be present on responses that include a `report-to` CSP directive; do not send it when CSP reporting is not configured

There is already an `/api/report` handler that accepts `POST` requests; no change is needed on that endpoint.

**Acceptance criteria:**
- No CSP header uses `report-uri` anymore.
- Every CSP header that previously used `report-uri` now uses `report-to csp-endpoint`.
- Responses carrying a `report-to` directive include the corresponding `Reporting-Endpoints: csp-endpoint="/api/report"` header.
- Existing behaviour (e.g., allowed sources, nonce handling, sandboxing) must remain unchanged.

Update CSP policy using report-to and Reporting-Endpoints


The receiver login endpoint does not accept the receiver UUID as the username, even though the client is now sending it. It currently attempts to look up a User by an email-like identifier rather than using the Receiver model. Additionally, the authentication responses are inconsistent: the receiver login omits the `status` and `password_change_needed` fields that the frontend relies on, and the session generation returns an ID instead of the session object, preventing other attributes from being used in the response.

Make the following changes to the authentication handler:
- Accept the receiver UUID as the username for `/authentication` POST and validate the associated user's role (`receiver`), disabled state, and password.
- Return the receiver UUID as `user_id` in the login response rather than a User identifier.
- Ensure the login response always includes `status` and `password_change_needed` for all roles.
- Make the `generate_session` method return the session object so that `session.user_id` and `session.user_status` can be used directly in the response.
- Update the GET `/authentication` handler to include the new fields similarly.
- Align the `login_admin` helper to return the user’s actual username consistently.


The password‑reset flow stores temporary files named after the reset token on the ramdisk. The token comes from user input (in the password‑reset request) and from a stored session property (during password change). No input validation restricts the token to a safe filename, and no guard prevents path‑traversal when the token is joined with the ramdisk directory for reading or deletion.

**Required changes**

1. **Enforce token format** – Accept only tokens that are exactly 64 lowercase hexadecimal characters (`[a-f0-9]`). Reject any other value early (before filesystem operations). Apply this format check everywhere the token is accepted from external input (the password‑reset request body).

2. **Add path‑traversal checks** – Before any file read or deletion that uses the token as part of a path under the ramdisk directory, call the existing `directory_traversal_check` helper (from `globaleaks.utils.fs`) to verify that the constructed absolute path stays within the ramdisk root. Do this both when reading the token file during password‑reset validation and when securely removing the token file during password change.

The combination of strict input validation and the path‑safety guard ensures that crafted token values cannot be used to escape the intended ramdisk directory.

Add path validation and input format checks for password reset tokens


We need to populate and expose the Tor2Web indicator on submissions.

The `InternalTip` model already has a `tor2web` boolean field, but nothing sets it. We want to record whether a whistleblower submission was made through a Tor2Web gateway and include this in the tip data that receivers see.

Requirements:

- During anonymous submission (the final submission PUT endpoint), determine the Tor2Web status from the request headers using the existing `get_tor2web_header` helper in the authentication module.
- Pass that boolean through the submission creation chain (`db_create_submission` and its `@transact` wrapper `create_submission`) and assign it to `submission.tor2web` when the `InternalTip` record is created.
- In the receiver-facing tip serialization (the function that builds the dictionary for receiver tip responses), add `'tor2web': internaltip.tor2web` to the output.
- Update all direct callers of `create_submission` in the test suite to supply the new argument (choose a sensible default value for test submissions, e.g. `True` for the generic helper and `False` for the GPG delivery test).


As a developer extending GlobaLeaks, I need the `Step` model to support deletion while preserving the ordered sequence invariant. Steps are children of a `Context` and are numbered sequentially (1, 2, 3, …). Currently there is no dedicated deletion logic for `Step` – when a step is removed, the remaining steps keep their original numbers, leaving gaps that can cause problems when new steps are inserted later.

**Expected behaviour:**
When a `Step` is deleted, all steps in the same `Context` that have a higher number should have their `number` decremented by one. After deletion the surviving steps must form a contiguous, gap-free sequence starting from 1, just as they would after creation. Deleting a step that is the only one in its context (or the last one) is a no-op beyond removing the object.

**Additional issue – base `Model.delete` method:**
The existing `delete` method in the `Model` base class is unusable in its current form: it appears to expect an `obj_id` argument that is never defined (the method signature is `def delete(self, store):` but the body references `obj_id`). This broken method makes it impossible to delete any model instance through the base API, which should instead simply remove the current instance from the given store. Fixing this base method is a requirement to make `Step.delete` work correctly.

**Impact:**
Without this fix, administrative operations that remove a step from a context (e.g., reordering a whistleblower submission flow) leave holes in the numbering. Later attempts to add a new step can produce duplicate or unexpected numbers, and the step order displayed to users becomes ambiguous. The corrected base `Model.delete` should restore basic instance deletion across all model classes that extend it.

Add delete() function to step, caring the update of step numbers.


Simplify tenant initialization by removing the explicit `mode` parameter from the wizard (`db_wizard`/`wizard`). Currently callers always pass a hardcoded mode value, but the mode should instead be inherited from the root tenant’s configuration when initializing a secondary tenant. When the wizard runs for the root tenant itself, it should skip the mode‑related setup entirely: after creating the initial context, refresh the memory variables and return early. This eliminates the redundant argument from all call sites (signup activation, the wizard endpoint handler, and any test setup).


**Title:** `PUT /submission/{id}` with unknown ID incorrectly creates a new submission instead of returning 404

**Background**
The whistleblower submission API (`SubmissionInstance`) supports GET, PUT, and DELETE for individual submissions. When the resource ID does not exist, both GET and DELETE properly respond with a `SubmissionIdNotFound` (404). However, sending a PUT to an unknown submission ID currently causes the backend to quietly create a new draft submission in the database, which is wrong—it should reject the request with the same 404 error.

**Desired behaviour**
1. Fix the PUT handler so that when the submission ID is not found in the database, it immediately raises `SubmissionIdNotFound` instead of constructing and inserting a new `InternalTip`. The error should be logged (matching the pattern used by GET and DELETE).
2. Ensure the PUT handler still works correctly for finalizing an existing submission (i.e., a whistleblower can submit a draft and then finalize it via a subsequent PUT with `finalize: true`).
3. (Minor) In the `get_submission` helper, rename the parameter `submission_d` to `submission_id` for clarity and consistency with the rest of the module.

**Relevant code areas**
- The PUT endpoint is handled by `SubmissionInstance.put` (class in `submission.py`).
- The update logic lives in the `update_submission` transaction function.
- The `get_submission` read-only transaction function is used for the GET path.

**Acceptance criteria**
- A PUT to `/submission/<nonexistent_id>` returns a `SubmissionIdNotFound` error (HTTP 404) and does **not** create any database row.
- Finalizing an existing submission via PUT continues to work.
- The `get_submission` parameters are renamed without changing behaviour.
- Existing tests for submission creation, deletion, and retrieval still pass, and test coverage for the PUT path is improved by adding at least a test that finalizes an existing submission through the handler.

improved unit tests related to submission handler


## Issue: Admin PGP expiration and system alert emails are not being sent

The `PGPCheckSchedule` job is meant to notify the node administrator when any user's PGP key has expired or will expire within the next 15 days. Currently these admin emails never arrive, and expired keys are not cleaned up.

**Expected behavior**

- The scheduler should select all users that have a non-empty `pgp_key_public` and a non-null `pgp_key_expiration` whose expiration falls **before** `now + 15 days`.
- For each such user the scheduler should include the user in the admin alert payload.
- If the key has already expired (expiration < now), the user’s `pgp_key_public`, `pgp_key_fingerprint`, and `pgp_key_expiration` should be reset to the model’s empty defaults (empty string `''` and `datetime_null()`), so the user will be prompted to upload a new key.
- The alert mail must actually be persisted to the database; currently `Templating.db_prepare_mail` builds a `Mail` object but never adds it to the store, so the mail is silently discarded and never reaches the admin.

**What’s broken**

1. **Inverted expiration window** – The current logic checks `expiration < now - 15 days` instead of `expiration < now + 15 days`. This excludes keys that are about to expire and may also skip some already-expired keys.
2. **Wrong clear values** – When a key is expired, the fields are set to `None` instead of the empty-string/datetime-null defaults used throughout the `User` model.
3. **Mail not stored** – `Templating.db_prepare_mail` does not call `store.add(mail)`, so the crafted email never enters the outbound queue.

**Acceptance criteria**

- An admin receives a notification email listing every user whose PGP key is expired or will expire within 15 days.
- After the notification is triggered for an expired key, that user’s PGP key fields are cleared to their default empty values.
- The `Mail` object produced by the templating layer is added to the store so that delivery can proceed.
- The existing (skipped) unit test for `PGPCheckSchedule` is updated to validate the new behavior (mail count and expiration-state assertions).

Fix generation of admin emails for pgp expiration and system alerts


The file-serving logic used by handlers (e.g., for export downloads) does not always finish the HTTP response. When a file cannot be opened (for instance because the file is missing), the server logs the error but never sends a final response to the client. Even when the file is opened and streamed successfully, the connection is left hanging after the file has been fully written. Fix the request completion so that a response is always sent, regardless of whether the file could be opened or after the file has been transmitted.


We need to add rate limiting to the support request endpoint, similar to how other sensitive endpoints are already protected. The `/api/support` endpoint is used by whistleblowers to request assistance and it sends administrator notification emails. Currently, there is no throttling applied to this endpoint, which means an attacker could flood administrators with support notification emails by repeatedly calling the endpoint.

This is especially problematic because a caller only needs a report token to hit `/api/support` — they do not need a full session — and the current rate-limit decorator appears to check report submission limits only when a session is present, or applies login-specific limits for the `/api/auth/login` path. The support endpoint falls through those branches without any protection.

We need:
- Rate limiting for the support endpoint that applies regardless of whether the caller has a session or just a token. An unauthenticated token-only caller must not be able to bypass the limits.
- The throttling should follow the same multi-granularity pattern used by the report submission endpoint: per-tenant-per-IP, per-IP, per-tenant, and per-system, all on a per-hour basis.
- New configurable thresholds should be added to the system configuration so that instance administrators can tune the limits, using the same naming and structure as the existing `threshold_reports_per_hour_*` settings. For example, the new settings might be named `threshold_support_per_hour_per_tenant`, etc., and they should be exposed through the appropriate config filter lists.
- The implementation should protect against support request flooding by raising a ForbiddenOperation error when any of the limits are exceeded.

The impact: Without this, a malicious user who has obtained a valid report token can enqueue unbounded administrative email, potentially harassing administrators or degrading the notification system. Adding rate limiting will bring the support endpoint in line with other user-facing endpoints that already have similar protection.


In the GlobaLeaks platform, a receiver (recipient) can opt out of email notifications for a specific tip via the user interface.
However, this per‑tip opt‑out is currently not honored: the receiver still receives notification emails even after disabling them for that tip.

The issue has two observable symptoms:
1. The setting for a receiver‑tip pair (`enable_notifications`) is not always represented as a proper boolean throughout the system, which can cause truthiness checks to behave unexpectedly.
2. The background job that generates and spools notification emails never checks whether a receiver has opted out for the particular tip before constructing and sending a mail.

Make the receiver‑tip notification preference work end‑to‑end:

- The field that stores the per‑tip notification opt‑out must be a boolean (not an integer surrogate) so that it always carries the semantics of a yes/no flag.
- When that field is serialized for API responses (e.g., the receiver’s tip detail endpoint), the serialized value must be an unambiguous boolean type.
- The notification‑email scheduling logic must respect the opt‑out: before creating an email for a tip event, it must inspect the receiver’s notification preference for that tip and skip email generation entirely when notifications are disabled.

The expected behavior after the fix is simple: if a receiver has disabled notifications for a given tip, they will receive no further emails about that tip’s activity, regardless of any global notification settings.

Changed model, handled email spooling, added bool cast.


We need to make GLTypes more Pythonic and decoupled. Today, defining a recurring data type requires extending `GLTypes`, calling `__init__`, and manually calling `define()` for each field. This is verbose and tightly coupled to a complex base class that does many things (tracking, serialization, aquire). The REST answer type `errorMessage` even does inline assignment in its constructor. We want to start moving toward a declarative, validation-focused system where each type is just a dictionary-like specification and a simple function can validate incoming JSON against it.

**Acceptance criteria** (all observable from outside):

1. **`errorMessage` is a plain function, not a class.**
   Any code that previously constructed `errorMessage(httpErrorCode=..., errorDict=...)` must still receive a dict-like response with keys `http_error_code`, `error_code`, and `error_message`. The function returns a dictionary. Note: the `error_message` value must come from `errorDict`'s `'string'` key (not `'message'`), reflecting the current client expectation.

2. **New declarative type base.**
   There is a base type (call it `GLTypes`, but as a dict subclass) that carries a class-level `specification` dict. A type like `fileDict` does not need any `__init__` or `define` calls; it simply sets `specification = {"filename": unicode, "size": int, ...}` and the validation layer knows how to interpret it.

3. **Validation function.**
   There is a function that accepts a JSON string and a type class. It parses the JSON and validates that the resulting object conforms to the type’s specification. The following must be validated:
   - Each key in the JSON must be present in the specification; extra keys cause an error (e.g., `GLTypeError`).
   - Each value’s type must match the specification. For Python built-in types (`int`, `unicode`, `bool`), the check is a simple `type(value) is expected`. For nested `GLTypes` subclasses, the validation recurses. For lists, the specification is a single-element list (e.g., `[fileDict]`), and every item in the JSON array must be validated accordingly (recursing into nested types if the item is a dict).
   - Special marker types (e.g., `dateType`, `timeType`, `tipID`, `receiverID`, etc.) should be considered valid by default (their concrete validation is a no-op or deferred).

4. **Define the main recurring types with the new system.**
   The following types must be defined using the new declarative base, with correct field names and types (matching their current definitions in `utils/recurringtypes.py` and the form expected by clients):
   - `fileDict`
   - `folderDict` (contains a list of `fileDict`)
   - `receiverDescriptionDict`
   - `nodePropertiesDict`
   - `adminStatisticsDict`
   - `publicStatisticsDict`
   - `formFieldsDict`
   - `moduleDataDict`
   - `contextDescriptionDict`
   - `commentDescriptionDict`
   - `tipSubIndex`
   - `tipIndexDict`
   - `tipDetailsDict`
   - `localizationDict` (empty spec, for future use)

5. **No regression in existing REST endpoints.**
   The old `GLTypes` classes in `answers.py` (like `nodeMainSettings`, `newSubmission`, etc.) must remain unchanged and continue to use the legacy infrastructure from `utils/recurringtypes.py` for now. Only `errorMessage` is converted to the new approach.

The goal is to have a simple, self-describing type system that can later be used to auto-generate documentation and to validate input messages without the old, coupled `define`/`aquire` machinery.

Refactor GLTypes to be something more pythonic and less coupled.


**Title:** RecursionError / denial-of-service when recipient opens a report with deeply nested answers updated through identity or additional-questionnaire endpoints

**Description:**
The main whistleblower submission endpoint validates that submitted answers conform to the questionnaire schema’s nesting depth. However, two other whistleblower endpoints—the identity‑update endpoint and the additional‑questionnaire “fill” endpoint—do not perform this validation. A modified client can therefore persist arbitrarily deeply nested answers (e.g., an object nested 3000+ levels deep) via these endpoints without being rejected.

When an assigned recipient later opens, exports, or redacts the report, the server recurses into the persisted answers and hits the Python recursion limit, resulting in a `RecursionError` that crashes the request. This is a denial‑of‑service vector that can be exploited against any report where a whistleblower updates their identity or submits answers to an additional questionnaire.

**Expected behavior:**
Every server‑side path that persists whistleblower‑supplied answers (initial submission, identity‑information update, additional‑questionnaire fill) must validate the incoming answers against the authoritative questionnaire schema’s maximum allowed nesting depth before saving them. Deeply nested answers that exceed the schema’s fieldgroup hierarchy should be rejected with a validation error, exactly as the main submission endpoint already does. The validation must use the authoritative schema retrieved from the database so that fieldgroup children (including templates) are correctly accounted for.

Validate answers nesting depth on all whistleblower persistence paths


**Title:** Uniform login flow for admin/receiver with improved timing-attack resistance

**Summary**
1. The login endpoints for admin, receiver, and whistleblower roles return different result shapes and use separate code paths, making the authentication handler fragile and harder to audit.
2. A random login delay meant to protect against side-channel timing attacks is currently applied **after** credential validation, which leaks information: an attacker can distinguish rejected from accepted credentials by measuring whether the delay occurs.
3. Receiver accounts created via the admin API cannot be used for login through a unified `/authentication` endpoint because the receiver’s internal user record does not have a predictable, exposed username.

**Steps to reproduce (observable behavior)**
- Send a valid admin login request; observe that the response arrives after a noticeable random delay.
- Send an invalid admin login request; observe that the response (e.g., 401) arrives immediately, with **no** delay.
- The same timing disparity exists for receiver and whistleblower login.
- Inspect a newly created receiver: the receiver’s `id` (visible in admin responses) cannot be used directly as the login username, because the underlying `User.username` is an unrelated UUID.

**Expected behavior**
- Every login attempt, regardless of role or credential validity, should be subject to the same random delay **before** credential checks, so that response timing reveals nothing about the correctness of the credentials.
- Admin and receiver login requests should follow the same code path and return an identical response structure (user_id, state, password_change_needed), eliminating code duplication and reducing the risk of divergent security checks.
- Whistleblower login should also return a consistent tuple so the authentication handler can treat all roles uniformly.
- A freshly created receiver should be able to authenticate using the receiver ID that the administration API exposes. The corresponding `User` record’s `username` must equal that receiver ID.

**Impact**
- Currently, an attacker can perform timing analysis across repeated login attempts to enumerate valid credentials. Unifying the flow and applying the delay early closes this side channel.
- Separate login implementations for admin and receiver have already led to inconsistencies; a unified implementation prevents future drift and simplifies maintenance.
- Making receiver login work via a known identifier (their receiver ID) is a prerequisite for any common login interface that accepts a role parameter.

**Implementation constraints (non‑negotiable)**
- The random delay must fire **before** any credential‑related database lookup or comparison for **every** role (admin, receiver, whistleblower).
- All login back‑end functions must return a 3‑tuple `(user_id, status, password_change_needed)` on success and `(None, None, None)` on failure. No login function should return a scalar or `False`.
- Receiver creation must ensure that `receiver.id == user.id == user.username` so that the receiver’s public ID can be used in the unified login lookup.
- Existing external API contracts (e.g., the field names in the JSON response of the authentication endpoint) must not change.

Uniform Admin/Receiver login; improve anti-side channel protection.


Add a new unauthenticated API endpoint `GET /public` that returns all public resources (node, contexts, receivers) in a single response. The response must be a JSON object with the keys `"node"`, `"contexts"`, and `"receivers"`, each using the exact same format as the existing `GET /node`, `GET /contexts`, and `GET /receivers` endpoints respectively. The endpoint must respect the `language` query parameter (same as those individual endpoints). Also define a response descriptor `PublicResourcesDesc` in the request-schema module so the response can be validated. All existing public endpoints must continue to work unchanged.

Implement /public including all the public resrources: Node, Contexts, Receivers


We want the list of supported languages returned by the public node endpoint to always reflect the application’s built‑in set rather than a value that might be stale or inconsistent in the database.

Acceptance criteria
- When an unauthenticated client requests the node configuration, the `languages_supported` field in the response must equal the `LANGUAGES_SUPPORTED` constant defined in the `globaleaks` package (see `globaleaks/__init__.py`). It must not be read from a database column or model field on `Node`.
- The `node` database table must not contain a `languages_supported` column. The SQL schema that defines that table should be cleaned up accordingly.
- The `Node` Storm model must not have a `languages_supported` property.
- The database migration script that upgrades from schema version 6 to version 7 must stop copying `languages_supported` from the old `node` row into the new one (the column is removed in version 7).
- Any test-only mock objects that pretend to supply a `languages_supported` value from storage must be updated so they no longer imply the field comes from the database. (The field can still be present as a no-op or comment, but it must not look like a persisted attribute that the serializer queries from a model.)

Edge‑cases / safety notes
- The change only applies to the *public* / unauthenticated node endpoint. Make sure the admin node serialization (if it also exposes `languages_supported`) uses the same constant, or document why it differs.
- The removal of the database column is a schema change; existing installations going through the upgrade path must not lose data they still need (the supported languages are not per‑node data, so dropping is safe).
- Do not inadvertently change the structure of `LANGUAGES_SUPPORTED` – it is a list of `{ "code": ..., "name": ... }` objects that the client expects.

spotted a bug: supported_languages has not to be a DB variable


The whistleblower session-expired exception class is named inconsistently with the standard abbreviation "WB" (which should be uppercase, as in other context). The current class name `WbSessionExpired` uses a lowercase 'b', while the comparable admin and receiver classes use full words. This creates a naming inconsistency.

Rename the exception class to `WBSessionExpired` (all-caps "WB") and update the single spot in `authenticated()` handler where it is raised to use the new name. The reason string and error code should remain unchanged; only the class name needs to be aligned.

improved unit tests of authentication handlers


## Task: Clean up and fix `Aabb` API in `godot-core`

### Motivation
The public `Aabb` API has a few misleading names, an outdated doc note, and a bug in `intersects_segment` when the segment direction is parallel to an axis. This issue unifies the needed changes.

### Acceptance criteria

1. **Rename `from_corners` to `from_position_end`**
   - The method `from_corners(position, end)` actually takes the minimum corner and the opposite maximum corner, *not* two arbitrary corners. Rename it to `from_position_end` to make the contract explicit.
   - Keep the old name as a deprecated forwarding shim so existing user code continues to compile (with a warning).

2. **Rename `get_endpoint` / `endpoints` to `get_corner` / `corners`**
   - Rename `endpoints()` to `corners()` and `get_endpoint(idx)` to `get_corner(idx)`, aligning with the term “corner” that the codebase already uses in comments.
   - Add a `#[doc(alias = "get_endpoint")]` on the renamed method so users who search for the Godot name can find it.
   - The vertex order **must** remain identical.

3. **Remove outdated doc reference to `InnerAabb`**
   - The struct-level comment currently says: “Currently most methods are only available through `InnerAabb`”. This is no longer true; remove that sentence.

4. **Fix `intersects_segment` and provide a version that returns the hit point**
   - The existing `intersects_segment(from, to) -> bool` has a bug: when a component of the direction vector (`to - from`) is zero, a division by zero occurs, causing NaN propagation and wrong results.
   - **Fix** this by handling zero-direction components: for each axis where the direction is approximately zero, verify that the segment origin’s coordinate along that axis lies inside the AABB slab. If it lies outside, the segment does **not** intersect.
   - Additionally, provide a new method `intersect_segment(from, to) -> Option<Vector3>` that returns the **first** intersection point (or `None`). This matches the Godot method which returns a `Vector3` or a null.
   - The existing `intersects_segment -> bool` should remain available and delegate to the same inner logic.
   - The inner logic (private helper) must compute an `Option<real>` t-value (distance along the segment to the entry point) and must use `FloatExt::is_zero_approx()` for zero checks, respecting floating-point imprecision.
   - All intersection methods must assert that `self.size` is non-negative (existing panics are fine).

5. **Make trivial query methods `const fn`**
   - `has_surface`, `has_volume`, `longest_axis_size`, and `shortest_axis_size` only read `self` fields. Mark them `const fn` so they can be evaluated at compile time.

6. **Fix `merge` to use correct associated min/max functions**
   - In `Aabb::merge`, the calls to coordinate-wise min/max currently use method syntax (`self.position.coord_min(b.position)`, `self.end().coord_max(b.end())`). The correct signatures are `Vector3::coord_min(a, b)` and `Vector3::coord_max(a, b)`. Correct these call sites, and also replace any internal use of the old `from_corners` with the renamed `from_position_end`.


**Summary:** The macros for registering exported methods and virtual methods both generate unsafe callback bodies for `ptrcall` and `varcall` with essentially identical logic. This duplication is error-prone (e.g. the “TODO reuse code” comments) and makes modifications costly. Extract the duplicated unsafe function bodies into shared helper macros so they stay in sync.

**Context & motivation:**
- The project uses two macros that produce GDNative method tables: `gdext_wrap_method_inner` (exported methods) and `gdext_virtual_method_inner` (virtual methods overrides).
- Both macros generate an `unsafe extern "C" fn call(…)` for **ptrcall** and another for **varcall**. The two code blocks are nearly identical – they:
  - cast the `GDExtensionClassInstancePtr` to the user storage via `as_storage::<C>` (or, for virtual methods, directly cast the pointer),
  - iterate over arguments (`from_sys` / `From<Variant>`) using a local index variable,
  - call the Rust method on the instance, and
  - write the return value to `ret`.
- The only difference is that one uses `sys::GodotFfi` and raw pointers, the other uses `Variant`. Everything else is structurally the same.
- Any fix or improvement (like refcounting updates, error handling) would need to be applied in multiple places and could easily be missed. The code already contains `// TODO reuse code, see ((1))` comments acknowledging the problem.

**Desired outcome:**
- Introduce two internal (non-public) `macro_rules!` macros that each generate the complete ptrcall/varcall unsafe callback body given the class type and method signature. One macro should produce the ptrcall variant (using `sys::GodotFfi`), the other the varcall variant (using `Variant`).
- Rewrite `gdext_wrap_method_inner` and `gdext_virtual_method_inner` so these callbacks are produced by invoking the new shared macros. The macros should receive:
  - the four pointer identifiers (`instance_ptr`, `args`, `ret`, and for varcall `err`),
  - the user class type,
  - the method name with its signature (including `self` and all parameters).
- Ensure that the expanded callbacks are **semantically identical** to the current ones. There must be no change in how arguments are extracted, how the instance is accessed, or how the return value is written.
- The `println!` debug prints (`"ptrcall: ..."` / `"varcall: ..."`) should remain, included by the new macros.
- The existing public API of the parent macros (`gdext_wrap_method_inner`, `gdext_virtual_method_inner`) must not change; only the implementation changes.
- The old manual `instance as *mut $type_name` cast used in virtual methods should move into the shared ptrcall macro, using the same storage access pattern as the wrap methods (`as_storage`), because both are supposed to be able to retrieve the instance from storage. (The existing virtual method macro did a direct pointer cast, but switching to `as_storage` is part of the deduplication and should be verified not to break anything.)

**Non-goals / out of scope:**
- No changes to public APIs, method registration behavior, or the existing test suite are required.
- No need to touch the old commented-out `Method` trait in `builder/method.rs` unless it interferes with readability; that file may be cleaned trivially if desired but is not part of the core requirement.

**Verification criteria:**
- The existing tests for method binding and virtual methods pass (implicitly, the code remains equivalent).
- A grep for the previously duplicated argument extraction patterns (e.g. `let $pname = <$pty as sys::GodotFfi>::from_sys(*args.offset(idx))`) shows they appear only in the new shared macros, not repeated across multiple top-level macros.

Extract duplicate ptrcall/varcall macro code


**Description**
Casts from a `Gd<T>` to another `Gd<U>` (and the corresponding `FromVariant` / `try_to` path) currently always succeed, even when the underlying Godot object is not of type `U`. This is caused by an upstream Godot bug that makes the engine-level cast trivially succeed.

We need a runtime guard that checks the object's actual class before accepting the cast, as a temporary workaround until the Godot bug is fixed.

**Expected behavior**
- `Gd<Object>::try_cast::<Concrete>()` (and `owned_cast`) must return `Err` / `None` when the object does not have the expected Godot class.
- Converting a `Variant` containing an object via `try_to::<Gd<Concrete>>()` must fail if the object does not match the target class.
- The fix should not leak memory or drop refcounts incorrectly; the `Object` upcast used for the class check must not double-free or decrement the reference count.
- Mark the workaround with a `TODO(#234)` referencing the upstream Godot issue so it can be removed later.

**Where**
The logic lives in the `Gd<T>` methods for casting (e.g., `owned_cast`) and in the `FromVariant` implementation for `Gd<T>`.

Fix cast always succeeding due to Godot bug.


**Feature request: Unified initialization lifecycle with MainLoop stage**

The `ExtensionLibrary` trait currently provides `on_level_init(InitLevel)` and `on_level_deinit(InitLevel)` to hook into the Godot initialization/deinitialization sequence for levels `Core`, `Servers`, `Scene`, and `Editor`. Starting from Godot 4.5, it additionally exposes separate methods `on_main_loop_startup()`, `on_main_loop_shutdown()`, and `on_main_loop_frame()` for main-loop lifecycle events.

This split is awkward because there is no unified way to treat the main-loop startup/shutdown as just another initialization stage. Users who want to perform work at every stage (including when the engine is fully initialized and just before shutdown) must implement two distinct sets of callbacks and manually coordinate state, while a single-pair approach would be simpler and more consistent.

**Desired behavior**

- Introduce a single pair of methods, conceptually `on_stage_init` and `on_stage_deinit`, that receive an extended stage type which includes all existing `InitLevel` values plus a new `MainLoop` variant (available only when the Godot API is >= 4.5).
- The stage enum should be a superset of `InitLevel`: each `Core`/`Servers`/`Scene`/`Editor` variant maps to the corresponding initialization level, and `MainLoop` represents the fully-initialized state after all levels are loaded.
- The internal GDExtension lifecycle callbacks (layer initialization/deinitialization and the new 4.5 main-loop startup/shutdown functions) should call the unified `on_stage_init`/`on_stage_deinit` methods with the appropriate stage.
- The per-frame callback (`on_main_loop_frame`) should remain a separate method; its semantics do not change.
- For backward compatibility, the existing `on_level_init`, `on_level_deinit`, `on_main_loop_startup`, and `on_main_loop_shutdown` methods should be deprecated. Their default implementations should forward to the new unified methods, so that existing code that overrides only the old methods continues to work without modifications. (Conversely, the new methods’ defaults should respect any overrides of the old per-level methods for the shared levels.)
- The new stage type and the unified methods should be publicly re-exported in the prelude just like `InitLevel` is today.

**Impact / motivation**

- Users can write a single `match` on the stage in `on_stage_init`/`on_stage_deinit` to handle all initialization stages, including the main loop, eliminating the need for separate methods.
- The API remains backwards-compatible; existing extension code compiles and runs unchanged.
- This design naturally accommodates future initialization stages without adding new top-level methods.

ExtensionLibrary: on_main_loop_* -> on_stage_[de]init + on_main_loop_frame


Title: `GString`/`StringName`/`NodePath` ignore format specifiers (padding, alignment, precision)

When formatting a `GString` with standard format specifiers like `{s:>6}` (right-align, width 6) or `{s:.3}` (precision 3), the output is the full string, ignoring width, alignment, and precision. This deviates from how Rust’s `str` and `String` behave.

Expected behavior: these specifiers should be respected, producing padded/truncated strings as usual. The same applies to `StringName` and `NodePath` since they implement `Display`.

Performance note: if no formatting options are given (default `Display`), the implementation should avoid allocating an intermediate `String`.

String formatting: support padding, alignment and precision


We need to be able to construct a `ClassId` from a class name that is only known at runtime (for example, classes defined in GDScript or other scripting languages). Currently the public API requires a static Rust type, which only works for Rust-defined classes. Expose a safe public constructor that accepts a string (preferably both an owned string and a borrowed string slice for ergonomics) and returns a `ClassId` that is correctly registered in the global cache—so that multiple calls with the same name return equal `ClassId` instances.

Additionally, the sentinel value for “no class” (used when building `PropertyInfo` for non‑object types like `i32`, `Vector3`, etc.) is currently available but hidden from public documentation. Make this constructor visible and document when and why it should be used.

The behaviour should remain consistent with the existing caching infrastructure: equality and hashing of dynamic `ClassId` values, the empty‑string “none” variant, and Unicode‑aware validation (panicking only on non‑ASCII names with Godot versions older than 4.4) must all continue to work as they do today.

`ClassId`: add `new_dynamic()` + `none()` constructors


We want to simplify the internal API of the Godot codegen so that utility functions no longer accept `&Option<Vec<T>>` when they only need to iterate. Instead they should take a plain `&[T]`. The `Option` wrapper is an artifact of JSON deserialization but doesn't belong in the gen-layer signatures.

Acceptance criteria:
- The Rust bindings generated for Godot classes, built-in types, constants, enums, and utility functions must be **byte-for-byte identical** to the output before this change.
- All existing unit tests in the codegen crate must pass.
- Functions that currently take `&Option<Vec<…>>` (e.g. `make_methods`, `make_enums`, `make_constants`, `make_builtin_methods`, `make_function_definition`, `make_params`, `function_uses_pointers`) should take `&[T]` directly.
- Call sites that have an `Option<Vec<T>>` (like those from `Class` or `ClassMethod` fields) should use a small helper that converts `&Option<Vec<T>>` to `&[T]` (empty slice when `None`). The helper should live in the same `util` module.
- The empty-case early-return logic that is currently duplicated inside those functions (returning `TokenStream::new()` or a `false` bool) is no longer needed and should be removed.
- The `function_uses_pointers` signature must also be adjusted to take `&[MethodArg]` and `Option<&MethodReturn>` (not `&Option<&MethodReturn>`), and its body should be simplified accordingly.
- No public interface of the generated crates (gdext bindings) may change.

Codegen refactor: &[T] instead of &Option<Vec<T>> parameters


We need to support calling methods on `Object` values dynamically, using a method name, and converting the returned `Variant` back into a typed Rust value. Currently `Object::call` exists in the API but it is not usable because:

1. `StringName` is not listed among the types the code generator considers “known”. As a result, APIs that take a `StringName` argument (including `call`) are not being generated.
2. `Variant` itself does not implement the conversion traits (`ToVariant`, `FromVariant`, `VariantMetadata`), so it cannot be used as a parameter type or return type in the generated binding layer.
3. There is no straightforward `From<&str>` conversion for `StringName`, making it cumbersome to create method-name arguments from a Rust string literal.

The goal is to make the following pattern work (pseudocode):

```rust
let obj: Gd<Object> = ...;
let result: Variant = obj.call(StringName::from("get_instance_id"));
let id = InstanceId::try_from_variant(&result).unwrap();
```

To achieve this we need to:

- Add `StringName` and `Variant` to the shared known-types list used by the code generator.
- Implement `ToVariant`, `FromVariant`, and `VariantMetadata` for `Variant` and any other missing built-in trait impls that the binding layer requires for `Variant` to be marshalled as a method return type (e.g., `GodotFfi`).
- Add a `From<&str>` implementation for `StringName` as a convenience, reusing the existing path through `GodotString`.

The fix should follow the patterns already established for other built-in types, and should not introduce unnecessary unsafe code beyond what is already required by the FFI layer.


Implementation request:

The `#[godot(…)]` attribute on a struct marked `#[derive(GodotClass)]` should accept a new `no_init` flag that signals the class has no default initializer. The previous spelling `no_default` must be removed entirely, and any reference to `no_default` in attribute position must now produce a clear compile-time error (it should be treated as an unrecognized key, not silently accepted).

Further, the procedural macro must reject any attribute key it does not recognize. If a user provides something like `#[godot(base=RefCounted, no_init, bogus_key)]` or misspells a key, the macro must emit an error indicating the unrecognized key(s) rather than silently ignoring them.

The `no_init` flag must be used without a value (bare key). Supplying a value to it (such as `no_init = true`) must be a compile-time error.

Acceptance criteria:
- Using `no_init` in the `#[godot(…)]` attribute causes the derive macro to treat the class as having no init method, consistent with the previous behavior of `no_default`.
- Using `no_default` in the attribute results in a compile error about an unknown attribute key.
- Any unknown key in the attribute results in a compile error (not just a warning or silent ignore). The error message should surface the unrecognized key(s).
- Providing any value to `no_init` results in a compile error.


**Ticket: Prevent double-panic / abort when `Gd::free()` fails during unwind**

**Problem**
Users sometimes call `Gd::free()` inside a `Drop` implementation (or other destructor-like context). If the thread is already in the middle of a panic unwind and `free()` hits one of its error checks (ref-counted type, already destroyed, active `bind` / `bind_mut`, etc.), the second panic immediately aborts the process. This makes it impossible for Godot extensions to survive a `Drop` that happens to free an object in an illegal state while another panic is unwinding.

**Desired behavior**
When the current thread is already panicking (i.e., a panic unwind is in progress), `Gd::free()` must not panic. Instead it should:
- Log an error message via the engine’s `godot_error!` macro (or equivalent dedicated error reporting channel).
- Return early without destroying the object.
- **Keep the existing panic behavior** when the thread is *not* unwinding – the current assertions and panics must remain untouched for the normal case.

Additionally, any internal integrity check that would itself panic during unwind (for example a dynamic type sanity check) should be skipped entirely while unwinding, to avoid re-entrant panic paths.

**Constraints / edge cases**
- The fix should not change the observable contract of `Gd::free()` in non-unwinding scenarios; existing callers must still see panics on the same invalid states.
- The early return during unwind intentionally leaks the object’s C++/engine-side resources, which is acceptable because the process is already heading toward termination/unwinding – safety is prioritized over clean-up.
- Bifrost note: a FIXME comment in the existing code already describes this exact requirement and suggests using `std::thread::panicking()`.

**Acceptance criteria**
- Calling `Gd::free()` on a destroyed/ref-counted/bound object during a panic unwind does not cause a double panic/abort.
- An appropriate engine error is emitted in that case, and the object is left untouched.
- Normal (non-unwinding) misuse of `free()` still panics as before.

Gd::free() now checks whether it is already panic-unwinding


Cache the raw pointer obtained from `global_get_singleton` for engine singletons, so that repeated calls like `Engine::singleton()` avoid a fresh FFI lookup and `StringName` construction every time. The pointer is stable as long as the singleton's init level remains loaded, so a per-type cache is safe *if* it is invalidated whenever that level is unloaded or the engine performs a full deinit/reinit (otherwise the cached pointer becomes dangling/stale).

Safety requirements:
- After the singleton's init level is unloaded, the cached pointer must not be used; the cache must fall back to a fresh (null) fetch or panic, consistent with the existing deinit-dangling guard.
- A full deinit/reinit cycle (e.g. hot-reload) creates new singleton instances at the same level numbers; the cache must recognize this and re-fetch, never serving a pointer from a previous cycle.
- The cache must still respect the existing invariant that calling `singleton()` before the class's init level is loaded returns null / causes the expected panic (not caching a null pointer as valid).
- The caching must not introduce synchronization problems; the fast path (hit) should be cheap and correct without blocking.

The optimization should apply to all generated `Singleton` implementations for built-in engine classes that today go through `singleton_unchecked_type`.


**Add a shared `EngineEnum` trait and generic `Variant` conversion for engine enums**

Engine enums are currently generated with an ad-hoc `ord() -> i64` method on each enum's impl block. We need a common trait, similar to `EngineClass`, that all generated enum types automatically implement. This trait should:

- Provide `ord() -> i32` (not `i64`; the ordinal is stored as `i32`).
- Provide `try_from_ord(ord: i32) -> Option<Self>`, returning `None` for ordinals that don't correspond to any enumerator.
- Be implemented automatically by the code generator for every engine enum.

Remove the current per-enum `ord()` method; it should be supplied by the trait instead.

Additionally, implement `ToVariant` and `FromVariant` for any `T: EngineEnum` via the ordinal as `i32` (reusing the existing impls for `i32`).

Export the trait in the prelude so `ord()` and `try_from_ord` are discoverable, like other engine auto-traits.


**Feature Request: Suppress Godot console errors during `try_load` / `try_save`**

The `try_load` and `try_save` functions in `godot-rust` return `Result` to signal failures gracefully. However, the underlying Godot engine calls still log error messages to the console when something goes wrong (e.g., missing file, invalid path). This makes the log noisy and misleading, because the error is expected and handled by the caller.

**Desired behavior**
- While `try_load` or `try_save` is executing, Godot’s error printing should be temporarily disabled so that no console error appears for expected failures.
- Once the function returns (successfully or with an `Err`), the original error-printing setting must be restored exactly, even if a panic occurs.
- If suppression scopes are ever nested (e.g., a public suppressor used inside another suppressor, or a `try_load` happening during an already-suppressed block), the flag should only be toggled once when the outermost scope begins, and only restored to the pre-suppression value when the outermost scope exits. Inner guards should be transparent.
- The suppression building-block should be exposed as a reusable, panic-safe RAII guard (e.g., a `suppress_godot_errors()` function) so that other error-handling code in the library and in user applications can benefit from the same behavior.
- Thread-safety: concurrent calls from different threads must not corrupt the counting logic; the implementation should use atomic primitives to track suppression depth and the original state. (The actual `set_print_error_messages` call is main-thread-only, but the bookkeeping must remain consistent across threads.)
- Any existing local workarounds that merely set error printing to `true` on drop should be replaced by this robust mechanism so that previously-disabled error printing is not inadvertently switched on.

Suppress Godot errors in fallible functions


```
Title: Gd::try_cast drops the original object on failure, making manual free impossible

When I use Gd::try_cast<Derived>() and the downcast fails, the function consumes self and returns None, dropping the original Godot object. This is documented: "If T's dynamic type is not Derived or one of its subclasses, None is returned and the reference is dropped."

The problem is that for objects that are not reference‑counted (e.g., created with new_alloc()), I cannot call .free() after a failed try_cast because the Gd is already gone. The underlying owned_cast already returns Result<RawGd<U>, RawGd<T>>, keeping the original object on failure. It would be much more useful if try_cast could hand the original Gd back to the caller on failure (for example, by returning Result<Gd<Derived>, Gd<T>>). That way I can still free the object, log something, or try a different cast.

This is blocking correct manual memory management and forces workarounds. The current TODO comment in the source even mentions "consider Result<Gd<Derived>, Self> so that user can still use original object (e.g. to free if manual)". Implementing this would make the API safer and more flexible.
```

Gd::try_cast() now returns Err(self) instead of None on failure


Aabb: add get_endpoint and fix support for zero direction components

- Expose a method `get_endpoint(idx: usize) -> Vector3` that returns the 0‑based vertex of the axis‑aligned bounding box, matching Godot’s vertex order (indices 0‑7). This should panic for indices ≥8.
- Provide a corresponding `endpoints() -> [Vector3; 8]` that yields all eight vertices in that same order.
- `support` currently uses `dir.signum()`; `signum(0.0)` returns `1.0`, causing an incorrect support vertex when any direction component is zero. The corrected behavior must return the vertex furthest in the given direction, treating zero components so that the non‑contribution axes stay at the minimum (position) rather than adding the full size.


# Task: Switch integration tests to auto-discovery via `#[itest]`

We already have the `#[itest]` attribute that can mark integration-test functions for auto-registration and the necessary macro-based infrastructure to collect them (e.g., `plugin_foreach!`). However, our test runner still relies on manual per-module `run()` functions that explicitly call every test and return a combined success boolean. These manual aggregators are redundant and require boilerplate that duplicates the test list.

## Acceptance criteria

1. **Tests are discovered through `#[itest]`**
   Every integration test function that carries `#[itest]` is automatically found and executed by the runner when the test suite is invoked. It should not be necessary to explicitly call test functions in a per-module `run()`.

2. **Manual `run()` aggregators are removed**
   All `pub fn run() -> bool` (and `pub(crate) fn run() -> bool`) functions that currently aggregate calls to test functions inside test modules are deleted. This includes the commented-out or empty ones. The only remaining public entry points for running the integration test suite are the ones that use the auto-registration infrastructure.

3. **The central runner uses auto-registration**
   The main test-runner function (currently `run_tests()` in `lib.rs`) must collect the set of test cases via the `__GODOT_ITEST` plugin registry rather than delegating to module-level `run()` calls. The runner should still report test counts, pass/fail, and any skipped tests.

4. **No compilation warnings or errors after removal**
   After removing the manual `run()` functions, the project must compile cleanly without warnings (especially unused import warnings). Any imports that were only brought in for the removed `run()` functions should be cleaned up.

## Out of scope

- Changing the `#[itest]` attribute mechanism itself or the registration macros.
- Modifying individual test function bodies.
- Altering the reported test output format beyond what naturally follows from using the existing auto-registration.

## Why

This completes the transition to attribute-based test declaration and eliminates a source of duplication between the list of `#[itest]` functions and the manual aggregation, making it harder to accidentally leave a test undiscovered.

Remove explicit calls to tests, now handled by #[itest] auto-registration


We need a feature request describing the problem before the patch. The user-facing issue: Godot APIs with raw pointer parameters (like `const char *` or `void *`) fail to compile in Rust bindings because the generated code requires those pointer types to implement `ToVariant`, `FromVariant`, and `VariantMetadata` — traits that are semantically unsound for arbitrary pointers and should not be needed. The fix should make pointer arguments use the pointer-call path exclusively, avoiding the variant-call path that demands those traits. The request should be observable from a user's perspective: calling a function with a pointer argument just works, without manual trait impls. I’ll also note the double-pointer formatting quirk from the Godot API.

Here's the request:```
Title: Support Raw Pointer Arguments in Generated Bindings Without Variant Conversion Traits

Currently, any Godot API function that takes or returns a raw pointer (e.g., `void*`, `const char*`, `int*`) causes the generated Rust bindings to fail to compile unless the user manually provides impls of `ToVariant`, `FromVariant`, and `VariantMetadata` for those pointer types. This is infeasible and undesirable—raw pointers should never be converted to/from a `Variant` and the library should not require that.

When such a function is encountered:
- The binding generator should produce a callable Rust method for the function.
- The runtime invocation should use the direct pointer‑call (ptrcall) path exclusively, never the variant‑call (varcall) path, because varcall requires `ToVariant`/`FromVariant`/`VariantMetadata`.
- The generated code must not impose any `ToVariant`, `FromVariant`, or `VariantMetadata` bounds on the pointer type parameter.

For functions that do *not* involve raw pointers the current behavior of preferring varcall when those bounds are satisfied should be preserved—only the pointer case needs decoupling.

A secondary but related issue: the Godot extension API JSON sometimes represents double pointers with a space between the type and the stars (e.g., `int **`). The codegen must handle this so that double‑pointer types are correctly recognized as raw pointers and subjected to the same ptrcall‑only rule.

Acceptance criteria:
- A Godot function with any number of raw‑pointer parameters can be invoked from Rust without the user implementing any conversion traits for the pointer types.
- Functions containing only non‑pointer types continue to use varcall when those types implement the necessary variant traits.
- The ptrcall path remains available as a fallback for any type that implements `GodotFuncMarshal` but not the variant traits, not just pointers.
```

Remove ToVariant, FromVariant, and VariantMetadata impls for pointers


As a Godot game using gdext, I want to organize my Rust code by splitting the `#[godot_api]` inherent implementation of a `GodotClass` across multiple `impl` blocks, so that large classes can be kept readable and so that proc‑macros can generate parts of the API without disrupting hand‑written registration.

**Acceptance Criteria**

1. **Multiple inherent `impl` blocks allowed**
   - A user can write several `#[godot_api] impl MyClass` blocks in the same module.
   - Exactly one block acts as the *primary* block; it does **not** have any additional attribute arguments.
   - Every *additional* block must be annotated with the attribute argument `secondary`, i.e. `#[godot_api(secondary)] impl MyClass`.
   - If no primary block is present (e.g., all blocks are marked `secondary`), the result is a compile error with a clear message.

2. **Registration completeness**
   - All Godot‑visible items from every block are registered with the engine at startup, so that calling them from GDScript works identically to having everything in one block. This includes:
     - Functions annotated with `#[func]`
     - Signals registered via `#[signal]`
     - Constants exposed with the built‑in constant mechanism
     - RPCs
   - The ordering of registration across blocks does not matter (i.e., the user sees no observable difference).

3. **No duplicate class‑plugin or trait implementations**
   - The underlying Godot class plugin (the thing that registers the class with the engine) is emitted exactly once, from the primary block only.
   - The `ImplementsGodotApi` trait is implemented for the class exactly once, by the primary block.
   - Secondary blocks must not duplicate these, otherwise the code would fail to compile due to duplicate items.

4. **Trait impls are unaffected**
   - `#[godot_api] impl IObject for MyClass` blocks (i.e., trait implementations) continue to work exactly as before.
   - If any attribute arguments (like `secondary`) are supplied on a trait `impl`, the macro should produce a clear error message that explains trait implementations do not accept parameters.

5. **Thread‑safety and early registration**
   - The generated code must correctly push registration functions (for methods, constants, etc.) into a static storage that is consumed before `main` starts, without introducing data races or re‑entrancy issues, even when multiple secondary blocks exist.
   - This must compile and run correctly on all supported targets (including WASM, Linux, Android, etc.), respecting platform‑specific pre‑main initialization requirements.

**Non‑goal**
- This feature does not need to support splitting virtual method overrides across multiple blocks; those remain in trait impls as currently designed.

[macros] allow multiple inherent impl blocks.


When both `#[export]` and `#[var]` are applied to a field in a `#[derive(GodotClass)]` struct, `#[var]` should still be able to override the hint, hint string, and usage flags — even when the field also carries `#[export]`. Currently, `#[export]` unconditionally replaces the `#[var]` hint with its own derived hint, making `#[var]` overrides ineffective.

In particular:
- An explicit hint (e.g. `hint = GLOBAL_FILE`) supplied in `#[var]` should appear in the final property registration, not be silently overwritten by the export’s default hint.
- An explicit `hint_string` from `#[var]` should be used. If not given, the export’s default hint string is acceptable.
- These semantics must hold for any variant of `#[export]` (enum, resource, range, etc.) combined with any `#[var]` hint.
- The fix should not break the existing behavior when only `#[export]` is present, or when only `#[var]` provides a hint.


When an `impl` item carries both a `#[func]` (or `#[signal]`, `#[constant]`) attribute and any other attribute (e.g., `#[cfg(...)]`, `#[rustfmt::skip]`, or a procedural macro), the Godot attribute is silently ignored and the item is not registered. Users expect that other attributes are simply passed through; only the Godot-specific one should control registration. Ensure that valid Godot attributes are recognized regardless of the presence or position of other attributes on the same declaration, while still rejecting multiple competing Godot attributes (`#[func]`, `#[signal]`, `#[constant]`) on the same item.

Fix #[func]-like attrs being ignored with macros under them


**Title:** Support constructing `ClassName` from dynamic runtime strings (e.g., introspection)

**Context**
The `ClassName` type represents a Godot class name and uses a global cache keyed by `TypeId` for types known at compile time. There is currently no way to obtain a `ClassName` from a purely dynamic string – something needed when using Godot’s scripting or introspection APIs that return class names as `String`. The cache also has separate storage and lookup for static (type‑based) and dynamically‑registered class names, so creating a `ClassName` from a runtime string that happens to match an existing type’s name would produce a different instance, breaking equality and hash consistency.

**Desired behavior**

1. Provide an API (e.g. `ClassName::new_dynamic(name: String) -> Self`) that creates a `ClassName` from any runtime string.
2. The resulting `ClassName` must be globally cached: constructing the same string again yields a `ClassName` equal to the first (same `global_index`, equal under `==` and `Hash`, same `to_string()`, etc.).
3. If the runtime string matches a class name already registered for a Rust type (via the `GodotClass` trait or the code‑gen macro), `new_dynamic` must return the same existing `ClassName` instance. Conversely, if `new_dynamic` is called first and later the same name is registered for a type, that type’s `class_name()` must return the same instance.
4. The empty string must behave as `ClassName::none()` and not create duplicate entries.
5. Unicode class names must be supported on Godot ≥ 4.4 (the existing static paths already handle this; the dynamic path must follow the same rules and validation).
6. The implementation must remain thread‑safe (the global cache is already behind a lock). Ordering between `ClassName` values is still stable within a run but not across runs; lexicographic ordering is not required.

**Constraints / edge cases**
- Do not introduce duplicate entries for the same string, even when a name comes from different sources (dynamic string, code‑gen, or manual `GodotClass` impl).
- The existing static‑creation paths (proc‑macro generated code and `ClassName::alloc_next_*`) should be adapted to use the same unified cache so that the above identity guarantees hold in both directions.
- The sentinel index 0 (empty name, `ClassName::none()`) must remain the first entry.
- The per‑type caching of `ClassName` (e.g., the `OnceLock` in the code‑gen output) should keep working correctly.

`ClassName` construction from dynamic values


**Title:** Provide deprecation warnings for renamed `IObject` virtual methods (e.g., `get_property` → `on_get`)

**Describe the problem**
After a recent renaming of several virtual methods from the `IObject` trait (originally introduced in PR #1527), the old names stopped compiling entirely. For example:

- `get_property` was renamed to `on_get`
- `set_property` was renamed to `on_set`
- `validate_property` was renamed to `on_validate_property`
- `get_property_list` was renamed to `on_get_property_list`
- `property_get_revert` was renamed to `on_property_get_revert`

Users who override these methods in their `#[godot_api] impl IRefCounted / IObject` blocks now get a hard compile error, with no deprecation period or guidance.

**Desired behavior**
The old method names should still compile and keep working, but they should emit a **deprecation warning** pointing directly to the user’s function definition. The warning message should tell the user which new name to use. This gives downstream projects a chance to migrate without breaking builds.

The implementation should follow the existing deprecation patterns used elsewhere in the crate (for example, the approach found in `godot-core/src/deprecated.rs` and the way macro-expanded code triggers those warnings).

**Additional context**
- The deprecation should be **source-location precise** – the warning span must refer to the user’s method site, not to some generated wrapper.
- The old behavior must not change semantically; the deprecated methods should still be recognized and wired up correctly by the engine.
- The deprecation support should be easy to remove in a future major version when the old names are finally dropped.

Support deprecation for old `IObject` names


**Title:** Move `NodeExt` / `PackedSceneExt` convenience methods to inherent impls

**Context**
The `godot` crate exposes a handful of convenience methods (`get_node_as`, `try_get_node_as`, `instantiate_as`, `try_instantiate_as`) through extension traits `NodeExt` and `PackedSceneExt`. Users must import those traits (or rely on the prelude’s `use … as _`) to access the methods. This hurts discoverability: the methods don’t appear in the generated documentation for `Node` or `PackedScene` itself, and many users reasonably expect to find them there. Internally the boundary between “generated” and “manual” methods is already blurred by things like `Object::try_call()`, so moving these to inherent impls is a natural improvement.

**Request**
1. Remove the public traits `godot::engine::NodeExt` and `godot::engine::PackedSceneExt` entirely.
2. Add the same methods (`get_node_as`, `try_get_node_as`, `instantiate_as`, `try_instantiate_as`) directly to the corresponding structs `Node` and `PackedScene` via inherent `impl` blocks.
   - Keep the existing signatures and semantics (including panic messages).
   - Simplify the trait bounds where possible: for example, use `T: Inherits<Node>` instead of `T: GodotClass + Inherits<Node>` since `Inherits` already implies `GodotClass`.
3. Calling these methods on a `Gd<SomeNode>` (where `SomeNode: Inherits<Node>`) must still work without any trait import; the existing `Deref` to `Node` should handle that.
4. Clean up the prelude (`godot/src/prelude.rs`) so it no longer imports the removed traits.
5. Update any internal callers (within the workspace) that explicitly used `NodeExt` or `PackedSceneExt` so they use the new inherent methods instead.

**Acceptance**
- The convenience methods are usable on `Node` and `PackedScene` without any trait import.
- The generated documentation for `Node` and `PackedScene` lists `get_node_as`, `try_get_node_as`, `instantiate_as`, `try_instantiate_as`.
- No public `NodeExt` or `PackedSceneExt` traits remain.
- Existing behavior and error messages are unchanged.

Remove NodeExt + PackedSceneExt, use inherent impl instead


**Summary:**
The `Debug` implementation for `Variant` currently converts objects to `Gd<T>` handles when formatting, which increments the reference count of reference-counted objects. This “observer effect” makes debugging invasive: examining a variant can change the program’s reference counts and potentially alter object lifetime behavior. The fix should make `Variant`’s debug formatting inspect object metadata without affecting reference counts.

**Acceptance criteria / desired behavior:**

1. **No reference-count side effect**
   Printing a `Variant` that holds a valid `RefCounted`-based object using `{:?}` must not change the object’s reference count. The debug output should still include the instance ID, class name, and reference count (showing the count *before* the debug call).

2. **Freed object handling**
   When a `Variant` holds a freed (dead) object, the debug output must indicate that the object is freed. The implementation must not panic, and it must not perform operations that would be invalid on a dead object.

3. **Informational parity**
   For a valid object the debug output should present the same essential fields that a `Gd<T>` debug currently shows (instance ID, class, and reference count). The exact debug *type name* (e.g., whether the output uses `Gd` or another prefix) may change – this is acceptable as long as the information is accurate and consistent.

4. **No external behavioral change for non-object variants**
   Variants of types other than `Object` should continue to use the existing formatting path.

**Context / constraints:**
- The fix belongs in the `Debug` trait implementation for `Variant`; it must avoid creating a temporary `Gd<T>` handle for objects.
- Techniques that can be used include calling Godot object methods (e.g., `get_class`, `get_reference_count`) directly on the `Variant` without converting to a `Gd`, or using other non-owning inspection mechanisms provided by the engine binding.
- The solution must work across supported Godot 4.x versions (the codebase currently gates some APIs with `cfg(since_api = "4.4")`). For older versions where `Variant::object_id_unchecked()` is not available, a backward-compatible path should be provided, even if it involves a temporary `Gd` with adjusted reference count arithmetic.
- Error messages that embed a `Variant`’s debug representation (e.g., in conversion errors) will naturally adopt the new debug format; ensure these messages remain clear.

Debug impl for Variant now holds objects, to not modify ref-count


**Title**: Remove redundant `variant_type` from `#[export]` attribute by inferring from field type

Currently, every exported property requires a `variant_type` key in the `#[export(...)]` attribute, like
```rust
#[export(getter = "...", setter = "...", variant_type = "::godot::sys::VariantType::Int")]
```
This is unnecessarily verbose and error-prone, because the Rust field type already carries the information that determines the correct `VariantType`. The trait `VariantMetadata` (which provides `fn variant_type() -> VariantType`) already exists and is implemented for all built-in Godot types; we should use it to infer the variant type automatically.

**Desired behavior**:
- The `variant_type` key is removed from the `export` attribute grammar. Users only supply `getter` and `setter`.
- The derive macro uses the field’s Rust type to call `<FieldType>::variant_type()` and passes that `VariantType` directly to `PropertyInfo::new`.
- Exported fields of type `Option<Gd<T>>` (or other `Option<T>` variants where `T: VariantMetadata`) must also work. Since `Option` itself is not currently a `VariantMetadata` implementor, this may require a **blanket impl** of `VariantMetadata` for `Option<T>` that delegates to `T`. The semantic expectation is that a nullable/optional property still reports the same base variant type (e.g., `Object`), matching how the engine handles unset values.

**Non‑obvious edge cases / safety**:
- The `KvParser` must be adjusted so that a missing (now‑removed) `variant_type` key does not cause a compile error or panic via `handle_lit_required`.
- The type path extracted from the field’s AST must be usable in a `quote!` expression as `<#field_type>::variant_type()`. That should already be the case because `VariantMetadata` is a trait bound for these types.
- Since we are generating a call to `::godot::builtin::meta::VariantMetadata::variant_type()`, we may need to emit a `use` statement in the generated code block to bring the trait into scope, avoiding a potential `method not found` error.

This task removes boilerplate, reduces the chance of mismatched type strings, and respects the existing trait‑based design.

Exports: Infer VariantType from PropertyType


```markdown
**Title:** Add fluent `with` and `without` methods to `EngineBitfield` for flag manipulation

**Background:**
Currently, bitfield enums that implement the `EngineBitfield` trait require users to manually compose flag values using `ord()`, `from_ord()` and raw bitwise operations (e.g., `MyFlags::from_ord(self.ord() | other.ord())` or `MyFlags::from_ord(self.ord() & !other.ord())`). This is verbose, easy to get wrong, and doesn’t feel like idiomatic Rust.

We want to provide a more ergonomic API that follows the patterns found in popular Rust bitflag libraries, while staying consistent with the existing trait design.

**Request:**
Add two default methods on the `EngineBitfield` trait:

- `fn with(self, flags: Self) -> Self` – return a new value that has all flags from `self` *and* all flags from `flags` (set union).
- `fn without(self, flags: Self) -> Self` – return a new value that has all flags from `self` *except* any that are also present in `flags` (set difference).

Both methods must:
- Be pure (return a new `Self`, do not mutate `self`).
- Work correctly when `self` is `0` (no flags) or when `flags` is `0`.
- Be chainable, e.g., `a.with(b).without(c)`.
- Use the existing `ord()` -> integer and `from_ord(integer)` -> `Self` contract that all `EngineBitfield` implementations already provide, without adding any new required trait methods.
- Include clear doc comments explaining their semantics.

**Optional / follow-up:**
The codegen that generates `BitOr`/`BitOrAssign` impls for bitfield enums (`godot-codegen/src/generator/enums.rs`) contains a comment about available operations. It would be nice to update that comment to indicate that the new `with`/`without` methods are the preferred way to combine and remove flags, rather than encouraging raw bitwise operators. However the core work is the trait methods themselves.

**Impact:**
Additive only – no breaking changes, no new required methods. All existing bitfield enums that implement `EngineBitfield` will gain `with` and `without` automatically through the default implementations.
```

Add 'with' and 'without' methods to EngineBitfield.


**Title:** Virtual method parameters of type `Gd<T>` should be `Option<Gd<T>>`

**Background**
Godot’s GDExtension API does not provide nullability metadata for virtual method parameters. Currently, the bindings generate virtual trait methods where every object parameter is a concrete `Gd<T>`. This is incorrect: the engine may pass `null` objects, which are not representable as non‑optional `Gd<T>` and lead to undefined behaviour or panic at the binding layer.

**Request**
Adjust the code generation for virtual methods so that any parameter whose type is an engine class (i.e., a Godot object) appears as `Option<Gd<T>>` rather than `Gd<T>`. This applies only to _virtual_ methods (the overridable ones in the generated `*Virtual` traits). Regular (non‑virtual) methods should keep their current signatures, where automatic conversion via `AsObjectArg` is safe and convenient.

**Acceptance criteria**
- Virtual methods defined by Godot classes expose object parameters as `Option<Gd<T>>`.
- Regular methods remain unchanged.
- The change must be reflected in the integration tests under `itest/rust/src/object_tests/virtual_methods_test.rs`: all virtual trait implementations that previously received `Gd<SomeClass>` must accept `Option<Gd<SomeClass>>`.
- While updating those tests, fix the obvious typo where a parameter is named `_namee` (should be `_name`).

Virtual functions now take Option<Gd<T>> instead of Gd<T>


**Title:** Native structures (CareTInfo, Glyph, ObjectId, etc.) are missing `Clone`, `PartialEq`, and `Debug` implementations

### Problem
The native structures exposed in `godot::engine::native` (e.g., `CaretInfo`, `Glyph`, `AudioFrame`, `ObjectId`) cannot be cloned, compared with `==`, or printed with `{:?}`. Attempting any of these operations results in a compilation error because the traits are not implemented.

### Expected behavior
All native structures generated by the binding should derive `Clone`, `PartialEq`, and `Debug`. This would allow users to:

- Clone a structure when they need a copy (e.g., to pass around a value without consuming the original).
- Compare structures for equality (two identical structs should be equal, and different ones not).
- Inspect the contents of a struct during debugging or logging via the `Debug` format.

Since the struct definitions come from a code generator (not hand‑written), the derivations should be added to the generation logic so that every native struct automatically implements these common traits.

derive Clone, PartialEq, Debug for native structs


```
Title: StringName missing Variant conversion support and #[itest] rejects function attributes

The `StringName` type does not implement `ToVariant` / `FromVariant`, which
means it cannot be passed to `Variant::evaluate` or other conversion APIs.
Godot's own variant system supports `StringName` natively, so this is a gap.
Fix: add the necessary `impl_variant_traits!` invocation in the variant impls
module, similar to the existing ones for `GodotString` and other builtins.
Ensure the FFI adapter functions (`string_name_to_variant` / `string_name_from_variant`)
are used and that the correct `GDNATIVE_VARIANT_TYPE_STRING_NAME` is provided.

Separately, the `#[itest]` procedural macro currently rejects any function
with attributes (including common ones like `#[rustfmt::skip]` or
`#[allow(...)]`). This prevents legitimate formatting or lint annotations
on integration-test functions. The macro should be relaxed to allow
arbitrary attributes while continuing to reject generic parameters,
parameters, return types, and where clauses. The validation logic should
check only those structural properties, not the presence of attributes.

Acceptance criteria:
- `StringName` values can be converted to and from `Variant`, and can be used
  as operands in `Variant::evaluate` comparisons (e.g., equality with
  `GodotString` or another `StringName`).
- A test function annotated with `#[itest]` can also carry other attributes
  (e.g., `#[rustfmt::skip]`) without triggering a compile error.
```


Subject: GFile performance and correctness issues with std::io traits

When using `GFile` as a `std::io::Read`, `Write`, `BufRead`, or `Seek`, there are several problems that make it impractical for all but the smallest files.

**Observed behavior:**

1. **Extreme slowness for `Read` and `Write`.**
   `read()` appears to call `get_8` once per byte instead of using a bulk read like Godot's `get_buffer`. Similarly, `write()` calls `store_8` per byte instead of `store_buffer`. Writing or reading a file of even a few tens of kilobytes is unbearably slow.

2. **`flush()` is a no-op.**
   The `Write::flush()` implementation does nothing. I expect it to forward to the underlying `FileAccess::flush()` so buffered data actually reaches disk.

3. **`BufRead` panics / undefined behavior.**
   When wrapping a `GFile` in `BufReader` and reading lines or filling a large buffer, I get a panic: index out of bounds. This suggests that the internal `buffer` field used by `fill_buf` is not guaranteed to be large enough, or the bookkeeping for `consume` is wrong. `fill_buf` must return a slice backed by valid, owned storage for the entire returned length.

4. **Stale file length after writes.**
   After writing to a file, `SeekFrom::End` or `length()` may return the old length instead of the updated one. This can cause reads to miss newly written data or seek incorrectly. The file length should always reflect the current size of the file visible through this `GFile`.

**Expected behavior:**

- `Read::read` and `Write::write` should use Godot’s bulk buffer APIs (`get_buffer` / `store_buffer`) to transfer as many bytes as possible in a single FFI call.
- `Write::flush` must flush the underlying `FileAccess`.
- `BufRead::fill_buf` must never panic and must return a valid slice that remains valid until `consume` is called. The internal buffer must be properly sized and populated.
- The file length used for `Read`, `SeekFrom::End`, and length queries must stay consistent: writing must invalidate any stale cached length, and reading/seek should use the correct, up-to-date value without degrading performance (i.e., avoid calling `get_length()` repeatedly if it’s expensive, but never lie about the length after a write).

**Impact:**
Without these fixes, `GFile` is unusable as a drop-in `std::io` file type for real-world workloads. The combination of per-byte FFI and missing flush/incorrect length breaks both performance and correctness guarantees of the standard library traits.


The `TruthIncompatibleType` bug checker does not flag type mismatches when using ProtoTruth’s assertion entry points (`ProtoTruth.assertThat`) or when proto-specific fluent modifier methods (such as `ignoringFields`) are present in the assertion chain. This leaves users of the proto extension without the same compile-time protection they receive when using the standard `Truth.assertThat` entry points.

**Observed problem:**
An assertion like `ProtoTruth.assertThat(protoMessageA).ignoringFields(1).isNotEqualTo(protoMessageB)` or `ProtoTruth.assertThat(iterableOfA).containsExactly(protoMessageB)` compiles without any diagnostic even when the argument’s type is clearly incompatible with the subject’s type. Standard Truth entry points (`Truth.assertThat`) correctly produce a `CollectionIncompatibleType` warning for analogous mismatches.

**Expected behavior:**
The checker should treat `com.google.common.truth.extensions.proto.ProtoTruth.assertThat` as a recognized assertion start, in the same way it already handles `Truth.assertThat` and `Truth8.assertThat`. While traversing the fluent chain to find the start, it should treat proto-specific methods that do not change the target type—such as those defined on `ProtoFluentAssertion` and `ProtoSubject` descendants—as transparent pass-through calls, exactly as it already does for other fluent subject methods. With this change, incompatible arguments to `isEqualTo`, `isNotEqualTo`, `containsExactly`, `comparingElementsUsing(...).doesNotContain(...)`, and similar operations should be flagged.

**Configuration:**
ProtoTruth support should be enabled by default. It should be possible to disable this support with an existing-style `-XepOpt` flag (e.g., `TruthIncompatibleType:ProtoTruth=false`) in case users want to opt out temporarily.

**Impact:**
Without this, ProtoTruth users lose the type safety that the bug checker is intended to provide, allowing incompatible proto type comparisons to go undetected at compile time. The gap is especially noticeable in environments that standardize on the proto extension for testing protobuf messages.

Make TruthIncompatibleType handle ProtoTruth.


**Title:** Add a checker for `Objects.equals` / `Objects.equal` on two primitive arguments

**Background**
`java.util.Objects.equals(Object, Object)` and similar utilities (e.g. Guava’s `com.google.common.base.Objects.equal`) accept generic `Object` arguments. When callers pass two primitive values, autoboxing occurs unnecessarily. A plain `==` (or `!=` in negated contexts) is simpler, avoids boxing, and respects the same semantics for primitives (including widening and comparison between, say, `int` and `long`).

**Desired behaviour**
A new `BugChecker` (e.g. `ObjectEqualsForPrimitives`, severity `WARNING`) that matches invocations of static `equals` methods whose two arguments are both primitive types, including mixed primitive types (e.g. `int` and `double`). For each match, suggest a replacement:

- `Objects.equals(a, b)` → `(a == b)`
- Negated form `!Objects.equals(a, b)` → `a != b` (or `!(a == b)` – the exact fix style can be decided during implementation, but negation must be handled sensibly)

**Edge cases / non‑goals**
- **Do not flag** when at least one argument is a reference type (e.g. `Integer`, `Boolean`, boxed number), even if the other argument is a primitive.
- **Do not flag** calls with a single primitive argument.
- The matcher should target well‑known static `equals` methods (e.g. `java.util.Objects.equals`, `com.google.common.base.Objects.equal`) rather than any method named `equals`.
- The fix must parenthesize complex source expressions to preserve correctness (the checker’s suggested fix should handle this).

**Registration**
Add the checker to `BuiltInCheckerSuppliers` so that it is available in Error Prone’s standard checks (likely as a disabled-by-default `WARNING`, consistent with similar “avoid unnecessary boxing” checks).

Add ObjectEqualsForPrimitives checker to detect using Objects.equals on primitive types.


**Feature Request: ImmutableSetForContains should also suggest conversion when list elements are unique and used only for iteration**

Currently, the `ImmutableSetForContains` check recommends replacing a `private static final ImmutableList` field with an `ImmutableSet` only when the field is used exclusively via `contains`, `containsAll`, and `isEmpty`. This suggestion should be extended to fields whose elements are provably unique and that are used only in a set of iteration-safe ways.

**Acceptance Criteria:**

1. **When unique elements are used in allowed iteration constructs**
   For a `private static final ImmutableList` initialized with `ImmutableList.of(...)`, if all arguments are compile-time constants (e.g., string literals, integer literals) and are distinct, or if all arguments are enum constants and are distinct, and the field is used *only* through the following operations, the checker should suggest converting the field to `ImmutableSet`:
   - `contains`, `containsAll`, `isEmpty` (existing allowed operations)
   - `size()`
   - `stream()`
   - `iterator()`
   - `forEach()`
   - Enhanced for-loops (`for (Element e : field)`)

2. **No suggestion when uniqueness cannot be guaranteed**
   The checker must **not** fire if:
   - The initializer is not a simple `ImmutableList.of(...)` call (e.g., uses a builder or a method result).
   - The arguments contain duplicates (e.g., `ImmutableList.of("a", "a")`).
   - Any argument is not a compile-time constant or not an enum constant.
   - The field is used with any other method (e.g., `get(index)`, `subList`, passing to a parameter expecting `List`, etc.).

3. **Conversion mechanics**
   When suggestion fires, the fix should change the field type from `ImmutableList` to `ImmutableSet`, replace `ImmutableList.of(...)` with `ImmutableSet.of(...)`, and add the necessary import if missing (exactly as the existing suggestion does for `contains`-only uses).

4. **Safety invariants**
   The extended behavior must not alter the existing suggestion logic for fields used *only* with `contains`/`containsAll`/`isEmpty`, regardless of element uniqueness. The new iteration allowance is purely an additional permitted set of operations when elements are unique.

Also suggest conversion of ImmutableList to ImmutableSet when the elements contained are unique and are used in loop constructs.