Skip to content

[OP-20345] Frontend typings: fields framework - #25662

Open
myabc wants to merge 23 commits into
implementation/op-20349-eslint-typing-fix-hal-index-signaturefrom
implementation/op-20345-eslint-typing-fix-fields
Open

myabc wants to merge 23 commits into
implementation/op-20349-eslint-typing-fix-hal-index-signaturefrom
implementation/op-20345-eslint-typing-fix-fields

Conversation

@myabc

@myabc myabc commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Ticket

https://community.openproject.org/wp/OP-20345

What are you trying to accomplish?

Part of OP-20341: removes ESLint typing violations from the display and edit fields framework, without changing behaviour.

  • Types changeset request payloads with a ChangesetPayload interface.
  • Types Field#resource as HalResource; field values are unknown and each field type casts to the shape it already assumed.
  • Types the field registries, their constructor signatures and the edit field handlers.
  • Types display field rendering and its context options.

shared/components/fields goes from 107 typing violations to 0; application-code typing violations drop from 531 to 418.

What approach did you choose and why?

Values stay unknown at the framework level and are cast where each concrete field consumes them, so no runtime narrowing was added. Two expressions changed form with identical output: a ?.id as string became ?.id ?? null before a formatter that maps both to '', and || '' became ?? '' after a formatter that returns string|null.

Typing exposed two declarations that did not match their use: the display field constructor signature now matches how DisplayFieldService instantiates fields, and the select autocompleter registry stores component classes rather than strings. WorkPackageChangeset#applyChanges adopts the new payload type.

Templates were checked with ngc in addition to tsc.

Stacked on #25661, which makes the HalResource index signature unknown.

AI involvement

Directed – I specified the requirements and AI implemented most of it; I validated via testing rather than a full line-by-line review.

Merge checklist

  • Added/updated tests
  • Added/updated documentation in Lookbook (patterns, previews, etc)
  • Tested major browsers (Chrome, Firefox, Edge, ...)
Browser JavaScript diff (7 files, type-only changes omitted)

Each changed browser file (TypeScript and templates, excluding specs, test helpers and declaration files) is transpiled on its own with frontend/tsconfig.json, before and after; this is the emitted JavaScript that differs.

diff --git frontend/src/app/shared/components/fields/changeset/resource-changeset.js
index 10a9033..c298f9c 100644
--- frontend/src/app/shared/components/fields/changeset/resource-changeset.js
+++ frontend/src/app/shared/components/fields/changeset/resource-changeset.js
@@ -205,13 +205,14 @@ export class ResourceChangeset {
         }
         const isArrayType = (fieldSchema.type || '').startsWith('[]');
         let isArray = false;
-        if (val.forEach || val.elements) {
+        const collection = val;
+        if (collection.forEach || collection.elements) {
             isArray = true;
         }
         if (isArray && isArrayType) {
             const links = [];
             if (val) {
-                const elements = (val.forEach && val) || val.elements;
+                const elements = (collection.forEach ? val : collection.elements);
                 elements.forEach((link) => {
                     if (link.href) {
                         links.push({ href: link.href });
diff --git frontend/src/app/shared/components/fields/display/display-field.module.js
index 271ebcf..a0e5203 100644
--- frontend/src/app/shared/components/fields/display/display-field.module.js
+++ frontend/src/app/shared/components/fields/display/display-field.module.js
@@ -61,7 +61,7 @@ export class DisplayField extends Field {
         }
         return this.valueString;
     }
-    render(element, displayText, options = {}) {
+    render(element, displayText, _options = {}) {
         element.textContent = displayText;
     }
     renderEmpty(element) {
diff --git frontend/src/app/shared/components/fields/edit/field-types/date-edit-field/date-edit-field.component.js
index 9561ddd..613d5ce 100644
--- frontend/src/app/shared/components/fields/edit/field-types/date-edit-field/date-edit-field.component.js
+++ frontend/src/app/shared/components/fields/edit/field-types/date-edit-field/date-edit-field.component.js
@@ -14,7 +14,7 @@ let DateEditFieldComponent = class DateEditFieldComponent extends EditFieldCompo
         this.autofocus = !this.handler.inEditMode;
     }
     get value() {
-        return this.formatter(this.resource[this.name]) || '';
+        return this.formatter(this.resource[this.name]) ?? '';
     }
     set value(value) {
         this.resource[this.name] = this.parseValue(value);
diff --git frontend/src/app/shared/components/fields/edit/field-types/formattable-edit-field/formattable-edit-field.component.js
index 25efde1..f90c0a7 100644
--- frontend/src/app/shared/components/fields/edit/field-types/formattable-edit-field/formattable-edit-field.component.js
+++ frontend/src/app/shared/components/fields/edit/field-types/formattable-edit-field/formattable-edit-field.component.js
@@ -75,8 +75,9 @@ let FormattableEditFieldComponent = class FormattableEditFieldComponent extends
         }
     }
     get rawValue() {
-        if (this.value?.raw) {
-            return this.value.raw;
+        const value = this.value;
+        if (value?.raw) {
+            return value.raw;
         }
         return '';
     }
diff --git frontend/src/app/shared/components/fields/edit/field-types/progress-popover-edit-field.component.js
index c0d4aeb..a738ac5 100644
--- frontend/src/app/shared/components/fields/edit/field-types/progress-popover-edit-field.component.js
+++ frontend/src/app/shared/components/fields/edit/field-types/progress-popover-edit-field.component.js
@@ -68,11 +68,11 @@ let ProgressPopoverEditFieldComponent = class ProgressPopoverEditFieldComponent
         url.searchParams.set('work_package[initial][estimated_hours]', this.formatter(this.resource.estimatedTime));
         url.searchParams.set('work_package[initial][remaining_hours]', this.formatter(this.resource.remainingTime));
         url.searchParams.set('work_package[initial][done_ratio]', this.nullAsEmptyStringFormatter(this.resource.percentageDone));
-        url.searchParams.set('work_package[initial][status_id]', this.nullAsEmptyStringFormatter(this.resource.status?.id));
+        url.searchParams.set('work_package[initial][status_id]', this.nullAsEmptyStringFormatter(this.resource.status?.id ?? null));
         url.searchParams.set('work_package[estimated_hours]', this.formatter(this.resource.estimatedTime));
         url.searchParams.set('work_package[remaining_hours]', this.formatter(this.resource.remainingTime));
         url.searchParams.set('work_package[done_ratio]', this.nullAsEmptyStringFormatter(this.resource.percentageDone));
-        url.searchParams.set('work_package[status_id]', this.nullAsEmptyStringFormatter(this.resource.status?.id));
+        url.searchParams.set('work_package[status_id]', this.nullAsEmptyStringFormatter(this.resource.status?.id ?? null));
         if (this.resource?.id === 'new') {
             url.searchParams.set('work_package[status_id_touched]', 'true');
         }
diff --git frontend/src/app/shared/components/fields/edit/field-types/select-edit-field/select-edit-field.component.js
index a94dda0..eb882cd 100644
--- frontend/src/app/shared/components/fields/edit/field-types/select-edit-field/select-edit-field.component.js
+++ frontend/src/app/shared/components/fields/edit/field-types/select-edit-field/select-edit-field.component.js
@@ -28,7 +28,8 @@ let SelectEditFieldComponent = class SelectEditFieldComponent extends EditFieldC
         this.hiddenOverflowContainer = '.__hidden_overflow_container';
     }
     get selectedOption() {
-        const href = this.value ? this.value.href : null;
+        const value = this.value;
+        const href = value ? value.href : null;
         return this.availableOptions.find((o) => o.href === href);
     }
     set selectedOption(val) {
diff --git frontend/src/app/shared/components/fields/field.service.js
index 938ab2b..081ca87 100644
--- frontend/src/app/shared/components/fields/field.service.js
+++ frontend/src/app/shared/components/fields/field.service.js
@@ -19,14 +19,16 @@ export class AbstractFieldService {
         return this.getClassFor(fieldName, type);
     }
     addFieldType(fieldClass, fieldType, attributes) {
-        fieldClass.fieldType = fieldType;
-        this.register(fieldClass, attributes);
+        const registered = fieldClass;
+        registered.fieldType = fieldType;
+        this.register(registered, attributes);
         return this;
     }
     addSpecificFieldType(resourceType, fieldClass, fieldType, attributes) {
-        fieldClass.fieldType = `${resourceType}-${fieldType}`;
+        const registered = fieldClass;
+        registered.fieldType = `${resourceType}-${fieldType}`;
         attributes = attributes.map((attribute) => `${resourceType}-${attribute}`);
-        this.register(fieldClass, attributes);
+        this.register(registered, attributes);
         return this;
     }
     extendFieldType(fieldType, attributes) {

@github-actions

Copy link
Copy Markdown

Warning

Flaky specs

  • rspec ./modules/bim/spec/features/card_view/select_card_spec.rb[1:1:3]
  • rspec ./modules/bim/spec/features/card_view/select_card_spec.rb[1:2:1]
🤖 Ask Copilot to investigate

Copy the prompt below into a new comment on this PR to delegate the investigation to GitHub Copilot. It will look into the flakiness and open a separate pull request with you as reviewer.

@copilot The following spec(s) are flaky in CI (first seen on PR #25662, linked for reference only):

- `rspec ./modules/bim/spec/features/card_view/select_card_spec.rb[1:1:3]`
- `rspec ./modules/bim/spec/features/card_view/select_card_spec.rb[1:2:1]`

Treat this as a standalone task, unrelated to PR #25662. Create a new branch from origin/dev and open a new pull request targeting dev — do not stack it on PR #25662 or reuse that branch.

Follow the playbook in docs/development/testing/handling-flaky-tests/README.md to find the root cause and fix the underlying race — do not skip, delete, or weaken the spec to make it pass; disabling is a last resort per the playbook, and only with a bug ticket. Verify the fix by running the spec(s) repeatedly (e.g. `script/bulk_run_rspec --run-count 10`).

If you cannot reproduce the flake or are not confident in a fix after reasonable investigation, do not fabricate a change or skip the spec to force CI green. Instead, leave the pull request in draft and document what you tried, the suspected cause, and any leads in its description, then assign @myabc to take over.

Once the fix is verified, title the PR after the spec(s) it fixes, and use the PR description to explain the root cause, how the change resolves it, and the before/after results. Label the PR `flaky-spec`, assign @myabc, and request a review from @myabc.
On every commit, set @myabc as the sole co-author with a `Co-authored-by:` trailer (use their GitHub no-reply email so it links to their account), so it is traceable who dispatched the fix.

Turns off no-explicit-any and the no-unsafe-* rules for specs and the
test helpers that are kept out of the production bundle. Test doubles
and expectation helpers routinely work with loosely typed values, and
the typing effort under OP-20341 targets application code only.

The two no-unsafe exemptions that lived in the vitest spec block move
into the new block, which covers the helper files as well.

Removes the inline disables in specs and test setup that the exemption
makes redundant.

https://community.openproject.org/wp/OP-20342
Removes the api.v3 Result, Collection and Duration interfaces, the
Factory global and the op.QueryParams namespace. Nothing references
them, and most declared their members as any.

https://community.openproject.org/wp/OP-20342
Declares the tablesorter call and its language defaults, and the
metadata plugin slot the reporting page clears, instead of any. The
reporting controller no longer needs its inline disables for them.

https://community.openproject.org/wp/OP-20342
Adds a HookSignatures map for the hooks the core registers and calls,
so register and call check callback parameters and return typed
results instead of any. Arbitrary hook names still fall back to an
open signature, because plugins register and call hooks by name.
The fallback excludes the known names, so a mismatched callback or
argument for a known hook fails to compile; the spec pins that down.

The bulk context menu actions are now declared as WorkPackageAction,
which the typed hook result exposed as narrower than its use.

https://community.openproject.org/wp/OP-20342
Types the dynamic component output maps with ng-dynamic-component's
EventHandler, which is what ndcDynamicOutputs accepts, and the checksum
callback as a plain thunk. The unconstrained Function type accepted
any callable and let calls through unchecked.

https://community.openproject.org/wp/OP-20342
Deletes WorkPackageResource#updateLinkedResources. It has no callers
and calls an undeclared wpCacheService, so it only compiled because
of the resource's `any` index signature.

https://community.openproject.org/wp/OP-20344
Makes HalLink#$fetch and #$callable generic and gives CallableHalLink
its call signature and a HalLink-typed $link, so link functions no
longer rely on the global `Function.$link?:any` augmentation, which is
removed.

HalResource#$links is now a map of CallableHalLink and #$embedded a map
of unknown values. Resource-specific link interfaces declare their
action links as CallableHalLink<T> instead of `Promise<any>` methods,
and are intersected with the base maps on `$links`/`$embedded` so the
resource interfaces they are merged into keep the `any` index
signature until it is flipped separately.

WorkPackageResourceEmbedded drops its `HalResource|any` unions and the
link interface no longer inherits the embedded attributes. The unused
QueryFilterResource embedded declaration is removed.

Callers that relied on the untyped maps get boundary casts.

https://community.openproject.org/wp/OP-20344
Types HalResource#$source as HalSource and accepts an unknown source in
the constructor and $initialize, which already unwrap a HalResource
passed in place of a raw source. The initializer callback takes a
HalResource, $copy and $plain drop their `any`, and lazy() becomes
generic over the value it defines.

initializeHalProperties now reads the source through typed views of
`_links` and `_embedded` and passes the target maps to setupProperty
directly instead of looking them up by a computed `$`-name, removing
the builder's `any` casts. Runtime behaviour is unchanged.

Callers and specs that relied on the untyped source get boundary
casts.

https://community.openproject.org/wp/OP-20344
Types ResourceChangeset#schema and WorkPackageChangeset#schema as
ISchemaProxy, which is what both getters return. FormResource#schema
stays the embedded form schema and gets its own FormSchemaResource
type, a SchemaResource whose attributes are field schemas, and its
commit link becomes a CallableHalLink.

IFieldSchema is now derived from IOPFieldSchema instead of duplicating
it with `any`-typed allowedValues and options. Fields that load
allowed values through the link cast it at the boundary.

SchemaProxy dispatches its proxied methods by name through
proxyMethod, which keeps them bound to the handler and still honours
subclass overrides, instead of wrapping unbound method references in a
Function proxy. The payload helper checks own schema keys with
Object.hasOwn.

https://community.openproject.org/wp/OP-20344
Declares attributes and link callables that callers already read
through the `any` index signature: FormResource#payload, #commit and
#configureForm, QueryResource#star, #unstar, #icalUrl and #updatedAt,
TimeEntryResource#hours, WorkPackageCollectionResource#customFields
and #createWorkPackage, and WorkPackageResource#configureForm and
#bcfViewpoints. WorkPackageResource#description becomes Formattable and
RelationResource#type a string.

Class and interface declaration merges are replaced by member
declarations on the class (with `implements` for the exported shape),
which emit no code under useDefineForClassFields: false. Resource
state getters cast through unknown instead of `any`.

Callers whose values are now typed get boundary casts.

https://community.openproject.org/wp/OP-20344
Types the request data, created sources and registered classes in
HalResourceService, the HTTP client options and interceptor, and
ErrorResource's errors and details. HalResourceNotificationService
takes ErrorResource where it reads error attributes and treats raw
responses as unknown.

HalError#resource is nullable, so the two callers passing it to
showError assert it at the boundary, as the service already assumed.

HTTPClientParamMap keeps its `any` values: query and BCF callers pass
untyped objects as request params.

https://community.openproject.org/wp/OP-20344
Types the remaining `any` members of the query filter, filter schema,
schema dependency and grid resources, lets attachable resources read
the now typed addAttachment link directly, and casts the two schema
and parent reads that go through the resource index signature.

https://community.openproject.org/wp/OP-20344
Removes type assertions, eslint-disable directives and imports that
the typed HAL links, sources, schemas and resource attributes made
unnecessary, and marks the two now typed $load() calls whose promises
were already discarded with `void`.

https://community.openproject.org/wp/OP-20344
Declares the base link map as holding a callable link, an array of
links, or nothing, since the builder resolves array-valued HAL links
to arrays and resources only carry the links the API sent. The self
link is always present, as the builder creates one when missing.

Call sites that read a link they have already checked, or that the
resource always carries, cast it at the point of use.

https://community.openproject.org/wp/OP-20344
Casts the resource attributes that display and edit fields read by
name at the point of use, keeping today's values. These reads compiled
only because HalResource declared an any index signature, which the
next commit removes.

https://community.openproject.org/wp/OP-20349
Casts work package attributes and raw links that views and services
read without a declared type, keeping today's behaviour, ahead of the
HalResource index signature becoming unknown.

https://community.openproject.org/wp/OP-20349
Casts undeclared resource attributes read in grids, context menus,
modals, search, BCF, the team planner and the costs and GitLab plugin
modules, keeping today's behaviour, ahead of the HalResource index
signature becoming unknown.

https://community.openproject.org/wp/OP-20349
Casts the resource attribute a spec reads by name, since specs are
typechecked even though they are exempt from the typing lint rules.

https://community.openproject.org/wp/OP-20349
Declares the HalResource index signature as unknown instead of any.
The any signature let every undeclared attribute read skip type
checking, which a code comment had tracked as a source of bugs since
2019 (#31462). Reads of undeclared attributes now need a declaration
or a cast, as the preceding commits add.

The payload helper picks the current schema with ?? instead of ||; the
value is a schema resource or nullish, so the result is unchanged.

https://community.openproject.org/wp/OP-20349
Declares a ChangesetPayload interface for the plain HAL payload that
ResourceChangeset builds and posts to the form and update links, and
types minimalPayload, applyChanges and buildPayloadFromChanges with
it. The WorkPackageChangeset override follows the new signature.

Types getLinkedValue input as unknown and casts at the collection
boundary, keeping the existing forEach/elements detection. Replaces
redundant unknown unions and the proxy identifier any cast.

https://community.openproject.org/wp/OP-20345
Types Field.resource as HalResource instead of any, and the value
accessors and parseValue of Field and EditFieldComponent as unknown,
now that HalResource attributes are unknown.

The edit field types cast attribute reads at the boundary to the
shapes they already assumed (formattables, HAL resource arrays, date
and duration strings), so behaviour is unchanged. The hours formatter
accepts unknown because its template passes the untyped value.
Removes eslint-disable directives and type assertions that the typed
resource made unnecessary, and types the select autocompleter
registry with component classes.

https://community.openproject.org/wp/OP-20345
Types field class registration with a FieldClass constructor type and
casts once to the registry type where the static fieldType is set.
Declares IDisplayFieldType with the (name, context) constructor that
DisplayFieldService actually calls; the previous signature was
masked by the any[] fallback.

Declares DisplayFieldContextOptions for the options display fields
read, types the edit field handler submit callbacks, and derives
ResourceChangesetClass from the ResourceChangeset constructor as the
hook service already does.

https://community.openproject.org/wp/OP-20345
Types the render options of display fields as the schema option
record, the values passed to the resources display field helpers as
unknown[], and casts hierarchy item labels at the template literal
boundary.

Removes eslint-disable directives that the typed display context and
value made unused.

https://community.openproject.org/wp/OP-20345
@myabc
myabc force-pushed the implementation/op-20349-eslint-typing-fix-hal-index-signature branch from ac71ea0 to 2705f0a Compare September 29, 2026 09:47
@myabc
myabc force-pushed the implementation/op-20345-eslint-typing-fix-fields branch from 02f30cc to 17815ec Compare September 29, 2026 09:47
@myabc myabc added the eslint label Sep 29, 2026
@myabc myabc added javascript Pull requests that update Javascript code needs review labels Sep 29, 2026
@myabc myabc added this to the 18.0.x milestone Sep 29, 2026
@github-actions github-actions Bot added the ai: Directed 🪄 A human specified the requirements and AI implemented most of it; They validated via testing. label Sep 29, 2026
@myabc
myabc requested a lite review from Copilot September 29, 2026 10:20
@myabc
myabc marked this pull request as ready for review September 29, 2026 10:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟢 Approval recommended

No unresolved review issues were identified, and all approval assessments are positive.

Review effort: Lite
Findings: None

What changed in this PR

Updates the frontend fields framework with stricter TypeScript typings while preserving behavior.

Changes:

  • Adds typed field, resource, registry, handler, and changeset contracts.
  • Replaces unsafe any usage with field-specific types and casts.
  • Aligns display-field and autocompleter registrations with actual usage.
File Description
frontend/​src/​app/​shared/​components/​fields/​field.service.ts Types field constructors and registrations.
frontend/​src/​app/​shared/​components/​fields/​field.base.ts Types resources and field values.
frontend/​src/​app/​shared/​components/​fields/​edit/​services/​hal-resource-editing.service.ts Types changeset constructors.
frontend/​src/​app/​shared/​components/​fields/​edit/​field-types/​versions-edit-field.component.ts Removes obsolete option cast.
frontend/​src/​app/​shared/​components/​fields/​edit/​field-types/​user-edit-field.component.ts Removes obsolete lint suppression.
frontend/​src/​app/​shared/​components/​fields/​edit/​field-types/​select-edit-field/​select-edit-field.component.ts Types select values and targets.
frontend/​src/​app/​shared/​components/​fields/​edit/​field-types/​select-edit-field/​select-autocompleter-register.service.ts Types registered component classes.
frontend/​src/​app/​shared/​components/​fields/​edit/​field-types/​project-edit-field.component.ts Uses typed resource identity.
frontend/​src/​app/​shared/​components/​fields/​edit/​field-types/​progress-popover-edit-field.component.ts Types progress resource values.
frontend/​src/​app/​shared/​components/​fields/​edit/​field-types/​plain-formattable-edit-field.component.ts Types formattable values.
frontend/​src/​app/​shared/​components/​fields/​edit/​field-types/​multi-select-edit-field.component.ts Types options and selected values.
frontend/​src/​app/​shared/​components/​fields/​edit/​field-types/​hours-duration-edit-field.component.ts Types formatter input.
frontend/​src/​app/​shared/​components/​fields/​edit/​field-types/​formattable-edit-field/​formattable-edit-field.component.ts Types editor context and values.
frontend/​src/​app/​shared/​components/​fields/​edit/​field-types/​days-duration-edit-field.component.ts Types duration input.
frontend/​src/​app/​shared/​components/​fields/​edit/​field-types/​date-picker-edit-field.component.ts Removes obsolete suppressions.
frontend/​src/​app/​shared/​components/​fields/​edit/​field-types/​date-edit-field/​date-edit-field.component.ts Types formatted date values.
frontend/​src/​app/​shared/​components/​fields/​edit/​editing-portal/​edit-field-handler.ts Types submission handler results.
frontend/​src/​app/​shared/​components/​fields/​edit/​edit-field.component.ts Types field values and parsing.
frontend/​src/​app/​shared/​components/​fields/​display/​field-types/​work-display-field.module.ts Removes obsolete lint suppression.
frontend/​src/​app/​shared/​components/​fields/​display/​field-types/​resources-display-field.module.ts Types rendered resource values.
frontend/​src/​app/​shared/​components/​fields/​display/​field-types/​render-hierarchy-item.ts Types hierarchy display values.
frontend/​src/​app/​shared/​components/​fields/​display/​field-types/​formattable-display-field.module.ts Types rendering options.
frontend/​src/​app/​shared/​components/​fields/​display/​field-types/​date-display-field.module.ts Removes obsolete lint suppression.
frontend/​src/​app/​shared/​components/​fields/​display/​display-field.service.ts Types display contexts and constructors.
frontend/​src/​app/​shared/​components/​fields/​display/​display-field.module.ts Types render options.
frontend/​src/​app/​shared/​components/​fields/​changeset/​resource-changeset.ts Adds typed payload handling.
frontend/​src/​app/​shared/​components/​fields/​changeset/​changeset.ts Clarifies unknown value returns.
frontend/​src/​app/​features/​work-packages/​components/​wp-edit/​work-package-changeset.ts Applies the shared payload type.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@github-actions

Copy link
Copy Markdown

Warning

Flaky specs

  • rspec ./spec/features/notifications/navigation_spec.rb[1:1:1]
🤖 Ask Copilot to investigate

Copy the prompt below into a new comment on this PR to delegate the investigation to GitHub Copilot. It will look into the flakiness and open a separate pull request with you as reviewer.

@copilot The following spec(s) are flaky in CI (first seen on PR #25662, linked for reference only):

- `rspec ./spec/features/notifications/navigation_spec.rb[1:1:1]`

Treat this as a standalone task, unrelated to PR #25662. Create a new branch from origin/dev and open a new pull request targeting dev — do not stack it on PR #25662 or reuse that branch.

Follow the playbook in docs/development/testing/handling-flaky-tests/README.md to find the root cause and fix the underlying race — do not skip, delete, or weaken the spec to make it pass; disabling is a last resort per the playbook, and only with a bug ticket. Verify the fix by running the spec(s) repeatedly (e.g. `script/bulk_run_rspec --run-count 10`).

If you cannot reproduce the flake or are not confident in a fix after reasonable investigation, do not fabricate a change or skip the spec to force CI green. Instead, leave the pull request in draft and document what you tried, the suspected cause, and any leads in its description, then assign @myabc to take over.

Once the fix is verified, title the PR after the spec(s) it fixes, and use the PR description to explain the root cause, how the change resolves it, and the before/after results. Label the PR `flaky-spec`, assign @myabc, and request a review from @myabc.
On every commit, set @myabc as the sole co-author with a `Co-authored-by:` trailer (use their GitHub no-reply email so it links to their account), so it is traceable who dispatched the fix.

@myabc
myabc force-pushed the implementation/op-20349-eslint-typing-fix-hal-index-signature branch from 2705f0a to cdd9389 Compare September 29, 2026 18:29

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai: Directed 🪄 A human specified the requirements and AI implemented most of it; They validated via testing. eslint javascript Pull requests that update Javascript code needs review

Development

Successfully merging this pull request may close these issues.

2 participants