Skip to content

[OP-20348] Frontend typings: remaining modules - #25666

Open
myabc wants to merge 7 commits into
implementation/op-20349-eslint-typing-fix-hal-index-signaturefrom
implementation/op-20348-eslint-typing-fix-remaining-modules
Open

myabc wants to merge 7 commits into
implementation/op-20349-eslint-typing-fix-hal-index-signaturefrom
implementation/op-20348-eslint-typing-fix-remaining-modules

Conversation

@myabc

@myabc myabc commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Ticket

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

What are you trying to accomplish?

Part of OP-20341, the last batch: removes ESLint typing violations from the remaining modules (BCF, boards, grids, graphs, modals, autocompleters, states and the HTTP client maps), without changing behaviour.

  • Types the HTTP client param and header maps with the value types Angular's HttpParams and HttpHeaders accept.
  • Types the BCF API resources and gallery, board list and version payloads, graph datasets, grid DOM access and widget references.
  • Types modal locals as unknown instead of any.

The scoped files go from 115 typing violations to 2; the remaining two clear once #25664 types grouped values. Application-code typing violations drop from 531 to 417 on this branch.

With all OP-20341 pull requests merged together (#25657, #25659–#25666), application-code typing violations are at zero, and tsc, ngc and the frontend unit tests pass on the combined tree.

What approach did you choose and why?

Changes are annotations and boundary casts; no runtime narrowing was added. Small equivalent rewrites: a date formatter returns String(value) where URLSearchParams stringified the same value, the states lookup falls back with ?? on an object-or-undefined value, the version payloads are object literals with the same keys, and the graph update handler drops an event argument it never read.

BcfApiRequestService#_request becomes sendRequest, and the embedded graph keeps nullable group labels separate from the display labels.

Typing modal locals as unknown means new reads of this.locals.x need a cast or a typed locals interface; the date picker modal loses eleven inline disables as a result. The BCF request payload is unknown and cast only on the GET path, as the HAL resource service already does.

Stacked on #25661, which makes the HalResource index signature unknown. It merges cleanly with the sibling pull requests.

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 (12 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/core/states/states.service.js
index 6175d62..d635dff 100644
--- frontend/src/app/core/states/states.service.js
+++ frontend/src/app/core/states/states.service.js
@@ -22,7 +22,7 @@ export class States extends StatesGroup {
         this.additional = {};
     }
     forType(stateName) {
-        let state = this[stateName] || this.additional[stateName];
+        let state = this[stateName] ?? this.additional[stateName];
         if (!state) {
             state = this.additional[stateName] = multiInput();
         }
diff --git frontend/src/app/features/admin/editable-query-props/editable-query-props.component.js
index cc6500a..1050cfa 100644
--- frontend/src/app/features/admin/editable-query-props/editable-query-props.component.js
+++ frontend/src/app/features/admin/editable-query-props/editable-query-props.component.js
@@ -29,7 +29,7 @@ let EditableQueryPropsComponent = class EditableQueryPropsComponent {
                 return JSON.parse(this.queryProps);
             }
             catch (e) {
-                console.error(`Failed to parse query props from ${this.queryProps}: ${e}`);
+                console.error(`Failed to parse query props from ${this.queryProps}: ${String(e)}`);
                 return {};
             }
         })();
diff --git frontend/src/app/features/bim/bcf/api/bcf-api-request.service.js
index 3087925..5254891 100644
--- frontend/src/app/features/bim/bcf/api/bcf-api-request.service.js
+++ frontend/src/app/features/bim/bcf/api/bcf-api-request.service.js
@@ -18,7 +18,7 @@ export class BcfApiRequestService {
             withCredentials: true,
             responseType: 'json',
         };
-        return this._request('get', path, config);
+        return this.sendRequest('get', path, config);
     }
     request(method, path, data = {}, headers = {}) {
         if (method === 'get') {
@@ -30,9 +30,9 @@ export class BcfApiRequestService {
             withCredentials: true,
             responseType: 'json',
         };
-        return this._request(method, path, config);
+        return this.sendRequest(method, path, config);
     }
-    _request(method, path, config) {
+    sendRequest(method, path, config) {
         return this
             .http
             .request(method, path, config)
diff --git frontend/src/app/features/boards/board/add-list-modal/add-list-modal.component.js
index 6639a16..2108d89 100644
--- frontend/src/app/features/boards/board/add-list-modal/add-list-modal.component.js
+++ frontend/src/app/features/boards/board/add-list-modal/add-list-modal.component.js
@@ -87,14 +87,14 @@ let AddListModalComponent = class AddListModalComponent extends OpModalComponent
         });
     }
     getVersionPayload(name) {
-        const payload = {};
-        payload.name = name;
-        payload._links = {
-            definingProject: {
-                href: this.apiV3Service.projects.id(this.currentProject.id).path,
+        return {
+            name,
+            _links: {
+                definingProject: {
+                    href: this.apiV3Service.projects.id(this.currentProject.id).path,
+                },
             },
         };
-        return payload;
     }
     warnIfNoOptions(values) {
         let hasMember = false;
diff --git frontend/src/app/features/boards/board/board-partitioned-page/board-list-container.component.js
index 7034928..afb347f 100644
--- frontend/src/app/features/boards/board/board-partitioned-page/board-list-container.component.js
+++ frontend/src/app/features/boards/board/board-partitioned-page/board-list-container.component.js
@@ -99,6 +99,7 @@ let BoardListContainerComponent = class BoardListContainerComponent extends Unti
         }
         const active = this.getActionFiltersFromWidget(board);
         this.opModalService.show(AddListModalComponent, this.injector, { board, active });
+        return undefined;
     }
     changeVisibilityOfList(board, boardWidget, visible) {
         if (!visible) {
diff --git frontend/src/app/features/user-preferences/state/user-preferences.service.js
index 7107beb..3ba594c 100644
--- frontend/src/app/features/user-preferences/state/user-preferences.service.js
+++ frontend/src/app/features/user-preferences/state/user-preferences.service.js
@@ -17,7 +17,7 @@ let UserPreferencesService = class UserPreferencesService {
         this.store.setLoading(true);
         this.preferenceAPI(user)
             .get()
-            .subscribe((prefs) => this.store.update(prefs), (error) => this.toastService.addError(error))
+            .subscribe((prefs) => { this.store.update(prefs); }, (error) => this.toastService.addError(error))
             .add(() => this.store.setLoading(false));
     }
     update(user, delta) {
diff --git frontend/src/app/shared/components/autocompleter/version-autocompleter/version-autocompleter.component.js
index 2b9bcde..5ab343a 100644
--- frontend/src/app/shared/components/autocompleter/version-autocompleter/version-autocompleter.component.js
+++ frontend/src/app/shared/components/autocompleter/version-autocompleter/version-autocompleter.component.js
@@ -55,14 +55,14 @@ let VersionAutocompleterComponent = class VersionAutocompleterComponent extends
         });
     }
     getVersionPayload(name) {
-        const payload = {};
-        payload.name = name;
-        payload._links = {
-            definingProject: {
-                href: this.apiV3Service.projects.id(this.currentProject.id).path,
+        return {
+            name,
+            _links: {
+                definingProject: {
+                    href: this.apiV3Service.projects.id(this.currentProject.id).path,
+                },
             },
         };
-        return payload;
     }
 };
 __decorate([
diff --git frontend/src/app/shared/components/date/op-date-time.component.js
index 0cb0bcc..aa6eaf0 100644
--- frontend/src/app/shared/components/date/op-date-time.component.js
+++ frontend/src/app/shared/components/date/op-date-time.component.js
@@ -13,7 +13,7 @@ let OpDateTimeComponent = class OpDateTimeComponent {
 };
 __decorate([
     Input(),
-    __metadata("design:type", Object)
+    __metadata("design:type", String)
 ], OpDateTimeComponent.prototype, "dateTimeValue", void 0);
 OpDateTimeComponent = __decorate([
     Component({
diff --git frontend/src/app/shared/components/datepicker/wp-date-picker-modal/wp-date-picker.modal.js
index a60cda0..9b6f972 100644
--- frontend/src/app/shared/components/datepicker/wp-date-picker-modal/wp-date-picker.modal.js
+++ frontend/src/app/shared/components/datepicker/wp-date-picker-modal/wp-date-picker.modal.js
@@ -30,17 +30,18 @@ let OpWpDatePickerModalComponent = class OpWpDatePickerModalComponent extends Op
         this.closeMe();
     }
     updateFrameSrc() {
-        const url = new URL(this.pathHelper.workPackageDatepickerDialogContentPath(this.locals.resource.id), window.location.origin);
+        const resource = this.locals.resource;
+        const url = new URL(this.pathHelper.workPackageDatepickerDialogContentPath(resource.id), window.location.origin);
         url.searchParams.set('field', this.locals.name);
-        url.searchParams.set('work_package[initial][start_date]', this.nullAsEmptyStringFormatter(this.locals.resource.startDate));
-        url.searchParams.set('work_package[initial][due_date]', this.nullAsEmptyStringFormatter(this.locals.resource.dueDate));
-        url.searchParams.set('work_package[initial][duration]', this.nullAsEmptyStringFormatter(this.locals.resource.duration));
-        url.searchParams.set('work_package[initial][ignore_non_working_days]', this.nullAsEmptyStringFormatter(this.locals.resource.includeNonWorkingDays));
-        url.searchParams.set('work_package[start_date]', this.nullAsEmptyStringFormatter(this.locals.resource.startDate));
-        url.searchParams.set('work_package[due_date]', this.nullAsEmptyStringFormatter(this.locals.resource.dueDate));
-        url.searchParams.set('work_package[duration]', this.nullAsEmptyStringFormatter(this.locals.resource.duration));
-        url.searchParams.set('work_package[ignore_non_working_days]', this.nullAsEmptyStringFormatter(this.locals.resource.includeNonWorkingDays));
-        if (this.locals.resource?.id === 'new' && this.locals.resource.startDate) {
+        url.searchParams.set('work_package[initial][start_date]', this.nullAsEmptyStringFormatter(resource.startDate));
+        url.searchParams.set('work_package[initial][due_date]', this.nullAsEmptyStringFormatter(resource.dueDate));
+        url.searchParams.set('work_package[initial][duration]', this.nullAsEmptyStringFormatter(resource.duration));
+        url.searchParams.set('work_package[initial][ignore_non_working_days]', this.nullAsEmptyStringFormatter(resource.includeNonWorkingDays));
+        url.searchParams.set('work_package[start_date]', this.nullAsEmptyStringFormatter(resource.startDate));
+        url.searchParams.set('work_package[due_date]', this.nullAsEmptyStringFormatter(resource.dueDate));
+        url.searchParams.set('work_package[duration]', this.nullAsEmptyStringFormatter(resource.duration));
+        url.searchParams.set('work_package[ignore_non_working_days]', this.nullAsEmptyStringFormatter(resource.includeNonWorkingDays));
+        if (resource.id === 'new' && resource.startDate) {
             url.searchParams.set('work_package[start_date_touched]', 'true');
         }
         this.turboFrameSrc = url.toString();
@@ -49,7 +50,7 @@ let OpWpDatePickerModalComponent = class OpWpDatePickerModalComponent extends Op
         if (value === undefined || value === null) {
             return '';
         }
-        return value;
+        return String(value);
     }
 };
 OpWpDatePickerModalComponent = __decorate([
diff --git frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph.component.html
index 2d2917c..07d812b 100644
--- frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph.component.html
+++ frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph.component.html
@@ -5,7 +5,7 @@
   <widget-wp-graph-menu
       slot="menu"
       [resource]="resource"
-      (onConfigured)="updateGraph($event)" />
+      (onConfigured)="updateGraph()" />
 </widget-header>
 
 <op-wp-embedded-graph class='op-widget-box--body -no-overflow'
diff --git frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph.component.js
index 9c20e20..44c506a 100644
--- frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph.component.js
+++ frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph.component.js
@@ -17,7 +17,7 @@ let WidgetWpGraphComponent = class WidgetWpGraphComponent extends AbstractWidget
     set chartType(type) {
         this.resource.options.chartType = type;
     }
-    updateGraph(config) {
+    updateGraph() {
         this.graphConfiguration.persistAndReload()
             .then(() => {
             this.repaint();
diff --git frontend/src/app/shared/components/work-package-graphs/embedded/wp-embedded-graph.component.js
index 35e2f12..bdab69d 100644
--- frontend/src/app/shared/components/work-package-graphs/embedded/wp-embedded-graph.component.js
+++ frontend/src/app/shared/components/work-package-graphs/embedded/wp-embedded-graph.component.js
@@ -34,21 +34,21 @@ let WorkPackageEmbeddedGraphComponent = class WorkPackageEmbeddedGraphComponent
         }
     }
     updateChartData() {
-        let uniqLabels = Array.from(new Set(this.datasets.reduce((array, dataset) => {
-            const groups = (dataset.groups || []).map((group) => group.value);
+        const uniqLabels = Array.from(new Set(this.datasets.reduce((array, dataset) => {
+            const groups = (dataset.groups ?? []).map((group) => group.value);
             return array.concat(groups);
         }, [])));
         const labelCountMaps = this.datasets.map((dataset) => {
-            const countMap = (dataset.groups || []).reduce((hash, group) => ({
+            const countMap = (dataset.groups ?? []).reduce((hash, group) => ({
                 ...hash,
                 [group.value]: group.count,
             }), {});
             return {
                 label: dataset.label,
-                data: uniqLabels.map((label) => countMap[label] || 0),
+                data: uniqLabels.map((label) => countMap[String(label)] || 0),
             };
         });
-        uniqLabels = uniqLabels.map((label) => {
+        const displayLabels = uniqLabels.map((label) => {
             if (label === null) {
                 return this.i18n.t('js.placeholders.default');
             }
@@ -56,7 +56,7 @@ let WorkPackageEmbeddedGraphComponent = class WorkPackageEmbeddedGraphComponent
         });
         this.setHeight();
         this.chartLabels.length = 0;
-        this.chartLabels.push(...uniqLabels);
+        this.chartLabels.push(...displayLabels);
         this.chartData.length = 0;
         this.chartData.push(...labelCountMaps);
     }

@myabc
myabc added this pull request to stack #25658 September 29, 2026 08:16
@myabc
myabc marked this pull request as ready for review September 29, 2026 08:16
Copilot AI balanced review requested due to automatic review settings September 29, 2026 08:16

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

🔵 Needs a closer look

Its broad set of unchecked boundary casts across dynamic APIs and UI modules warrants final human validation.

Review effort: Balanced
Findings: None

What changed in this PR

This PR completes the remaining frontend typing cleanup across shared services, UI components, boards, grids, graphs, BCF, and HTTP infrastructure without intended runtime changes.

Changes:

  • Replaces broad any usage with concrete, generic, or unknown types.
  • Types HTTP maps, graph datasets, BCF resources, modal locals, and grid references.
  • Removes obsolete lint suppressions and unused callback parameters.
File Description
drop-modal-teleportation.service.ts Types teleported templates.
wp-embedded-graph.component.ts Types graph datasets and aggregation.
wp-graph-configuration.ts Types graph query properties.
wp-graph-configuration.service.ts Removes obsolete lint suppression.
wp-graph-configuration.modal.ts Types injected modal components.
trigger-actions-entry.component.ts Types HTTP errors.
remote-field-updater.component.ts Types response entries.
principal-types.ts Types custom field values as unknown.
option-list.component.ts Types control callbacks.
wp-button-macro.modal.ts Types modal locals and text.
wiki-include-page-macro.modal.ts Types modal page data.
code-block-macro.modal.ts Infers translation text type.
child-pages-macro.modal.ts Types modal page settings.
modal.types.ts Changes arbitrary modal locals to unknown.
wp-graph.component.ts Removes unused output argument.
wp-graph.component.html Updates graph callback invocation.
custom-text.component.ts Types changed widget resources.
grid.component.ts Types widget constructors and references.
drag-and-drop.service.ts Types dragged DOM lookup.
area.service.ts Types nonstandard scrolling API.
editable-toolbar-title.component.ts Types changed title values.
wp-date-picker.modal.ts Types date-picker resources and formatting.
op-date-time.component.ts Types date and time values.
version-autocompleter.component.ts Uses an inferred version payload.
create-autocompleter.component.ts Types arbitrary models as unknown.
user-preferences.service.ts Types HTTP errors.
http.interfaces.ts Defines accepted HTTP map value types.
highlighting-tab.component.ts Types modal board data.
board.service.ts Types translated board attributes.
board-partitioned-page.component.ts Types parsed board filters.
board-list-container.component.ts Types list creation results and errors.
board-list.component.ts Types column request parameters.
add-list-modal.component.ts Types modal data and version payloads.
bcf-export-button.component.ts Types export events.
bcf-wp-attribute-group.component.ts Types gallery images and actions.
bcf-viewpoint-collection.paths.ts Types BCF creation payloads.
bcf-topic.resource.ts Types date deserializers.
bcf-api.service.ts Types dynamic BCF path traversal.
bcf-api-request.service.ts Types request payloads and responses.
editable-query-props.component.ts Safely stringifies parsing errors.
states.service.ts Types dynamic state lookup.
capabilities.service.ts Types capability request errors.
schema-cache.service.ts Tightens bulk-loading return type.
model-auth.service.ts Types authorization links and teardown streams.
apiv3-query-paths.ts Types query request parameters.
apiv3-queries-paths.ts Types collection query parameters.

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

@myabc
myabc force-pushed the implementation/op-20348-eslint-typing-fix-remaining-modules branch from 0b25b60 to 59cef96 Compare September 29, 2026 09:47
@myabc myabc added eslint 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

Copy link
Copy Markdown

Warning

Flaky specs

  • rspec ./modules/overviews/spec/features/managing_dashboard_page_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 #25666, linked for reference only):

- `rspec ./modules/overviews/spec/features/managing_dashboard_page_spec.rb[1:1:1]`

Treat this as a standalone task, unrelated to PR #25666. Create a new branch from origin/dev and open a new pull request targeting dev — do not stack it on PR #25666 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.

@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
Types HTTPClientParamMap with the value shapes HttpParams accepts and
HTTPClientHeaders with the shapes HttpHeaders accepts, instead of
Record<string, any>. Both maps feed Angular's HttpClient unchanged, so
the narrower types only document what already worked.

The BCF request service now takes its request payload as unknown, as
the HAL resource service does, and casts it to a param map only on the
GET branch. The query path helpers that forward loosely typed query
params cast them at that boundary.

https://community.openproject.org/wp/OP-20348
Types the BCF API path parser walk, the TypedJSON deserializer inputs
and the viewpoint gallery images and actions with the types the
libraries declare, instead of any. The parser still returns the
resource it reaches and the deserializers still receive the raw date
strings, so runtime behaviour is unchanged.

https://community.openproject.org/wp/OP-20348
Declares the OpModalLocalsMap index signature as unknown instead of
any, so every modal states the type of each local it reads. The
modals that read locals now cast them at that boundary to the type
their callers already pass.

The date picker modal reads its resource through a small interface
instead of eleven lint suppressions. Its formatter now stringifies the
non-working-days flag itself; URLSearchParams did the same before, so
the URL is unchanged.

The macro and add-list modals infer their text maps instead of
declaring them as any.

https://community.openproject.org/wp/OP-20348
Types the board list query params as an HTTP param map, the add-list
handler's return value, the parsed board filter URL param and the
board type label translation, instead of letting them fall back to
any.

The version payloads in the add-list modal and the version
autocompleter are built as object literals instead of being assembled
on an any-typed object, so their shape is inferred. The payload keys
and values are unchanged.

https://community.openproject.org/wp/OP-20348
Types the graph dataset query props, the graph query params props and
the embedded graph's label and count maps instead of using any. The
graph configuration drops an interface that merged with its own class
of the same name; the class already declares every member.

The graph widget's configured handler drops the event argument it
never read, and the graph modal's prepend token takes an unknown
component type like its work package table counterpart.

https://community.openproject.org/wp/OP-20348
Types the placeholder's non-standard scrollIntoViewIfNeeded method,
the dragged area element lookup, the widget component registrations
and the custom text widget's changed resource instead of reaching
them through any. The lookups keep asserting that the elements exist,
as the any casts did.

https://community.openproject.org/wp/OP-20348
Types the model auth links, the dynamic state lookup, the date-time
component inputs, the option list form callbacks, the principal
custom fields, the remote field response, the create autocompleter
model and the teleport template ref instead of using any.

Error callbacks that hand HTTP errors to the toast service now declare
them as HttpErrorResponse, which the service already accepts. The
state lookup falls back with ?? instead of ||; the looked-up members
are state objects or undefined, so the result is unchanged.

https://community.openproject.org/wp/OP-20348
@myabc
myabc force-pushed the implementation/op-20348-eslint-typing-fix-remaining-modules branch from 59cef96 to 6689447 Compare September 29, 2026 18:29
@github-actions

Copy link
Copy Markdown

Warning

Flaky specs

  • rspec ./modules/bim/spec/features/bim_filter_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 #25666, linked for reference only):

- `rspec ./modules/bim/spec/features/bim_filter_spec.rb[1:1:1]`

Treat this as a standalone task, unrelated to PR #25666. Create a new branch from origin/dev and open a new pull request targeting dev — do not stack it on PR #25666 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.

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