Skip to content

feat(content-drive): bulk folder delete frontend - #37612

Draft
rjvelazco wants to merge 12 commits into
37063-content-drive-bulk-folder-delete-backendfrom
issue-37063-bulk-folder-delete-frontend-impl
Draft

rjvelazco wants to merge 12 commits into
37063-content-drive-bulk-folder-delete-backendfrom
issue-37063-bulk-folder-delete-frontend-impl

Conversation

@rjvelazco

@rjvelazco rjvelazco commented Sep 17, 2026

Copy link
Copy Markdown
Member

Frontend half of #37063 — Red phase

Stacked on 37063-content-drive-bulk-folder-delete-backend, so this PR shows only the client work. It must not merge to main ahead of the server half: Delete would be offered and every submission would fail.

Draft on purpose. This is the failing test set and nothing else — no behaviour is implemented yet.

What is here

  • The US1 test set: 17 tests across the submission service, the quick-action registry, the store's run registration and guard, the confirmation copy, and the maximum-paths courtesy check. 16 fail; 1 passes vacuously until the action exists.
  • The wire types, in dotcms-models, typed once so a divergence when the real endpoint lands is a compile error at a handful of sites rather than runtime breakage spread through the UI.
  • One fixtures module, so every spec asserts against the same source of truth for the shapes.
  • The frontend contract and data model under specs/37063-bulk-folder-delete-frontend/.

Why there are stubs that throw

A spec for a symbol that does not exist is a compile error, which the constitution's Red gate does not accept. So each production file lands first as a stub whose body throws '… is not implemented yet (T0xx)', letting the specs compile and fail on what they assert. Every failure reads as a behaviour failure, not a module-resolution one.

These are temporary by agreement. T091 in the task list checks that grep -rn "is not implemented yet" core-web/libs returns nothing before the feature is done — a stub reaching main is a feature that throws at runtime, and the tests that would catch it are the ones written against the stub.

The one thing worth reading if you read nothing else

The queue's active listing returns every non-terminal run — failed and abandoned ones included, not only working ones (backend C-011). Marking folders from it unfiltered reports a delete that already failed as still running, and the folder stays marked until the framework moves it on.

That is why isJobInProgress exists rather than a not-finished check, and why the fixtures deliberately carry a failed run and an abandoned one. The symptom — "sometimes folders stay marked forever" — reads in QA as a client defect and is not one.

Open question for the backend half

C-004 requires the four submission refusals to be distinguishable, but an empty submission and an over-maximum submission are both 400 with nothing to tell them apart, and they need different messages. contracts/bulk-delete-api.md on the base branch confirms the statuses without separating those two.

The tests assume an error code in the body. If the server picks a different shape, only dot-folder-bulk-delete.service.ts and its spec change — the refusal kinds the rest of the client switches on are stable.

Raised in full at #37063 (comment 5720585127).

Not here, deliberately

Testing

cd core-web
pnpm nx test data-access -- -t "DotFolderBulkDeleteService"
pnpm nx test portlets-content-drive -- -t "37063"

Per ADR-0013 the merge queue skips integration, Postman and Karate for a core-web/**-only change, so the unit layer is most of this PR's safety net rather than a supplement to a broader one.

Baseline before these changes: green across portlets-content-drive, content-drive-ui, ui, data-access. Note the typecheck target is already red on main for data-access and the portlet (106 and 8 errors, all in dotcms-js/utils) — unchanged by this PR, verified against HEAD.

🤖 Generated with Claude Code

This PR fixes: #37063

@rjvelazco
rjvelazco added this pull request to stack #37613 September 17, 2026 20:32
@rjvelazco rjvelazco changed the title test(content-drive): bulk folder delete frontend — Red phase for US1 (#37063) feat(content-drive): bulk folder delete frontend Sep 17, 2026
@claude

claude Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Claude finished @rjvelazco's task in 4m 34s —— View job


Review — bulk folder delete frontend (#37612)

Reviewed against origin/37063-content-drive-bulk-folder-delete-backend. The code is well past the "Red-phase stubs" the PR body describes — the feature is actually implemented, so I reviewed the real behaviour. Overall it's careful, and the i18n keys (including every -many variant) are all present. Two functional gaps stand out.

New Issues

  • 🟡 Medium: core-web/.../utils/folder-delete-ceilings.ts:31the max-paths courtesy check is never wired in. refuseOverPathCeiling and the folderBulkDelete/maxPaths config (dot-system-config.model.ts:73) exist and are unit-tested, but nothing calls the function: there is no folderDeleteCeilings store computed (contrast uploadCeilings in dot-content-drive.store.ts), and executeFolderBulkDelete in dot-content-drive-action-center.component.ts:755 submits without consulting it. Upload's equivalent is wired (dot-content-drive-shell.component.ts:1531). Net effect: an over-max selection is posted to the server and refused only there (OVER_MAX_PATHS), so the pre-submit courtesy the PR describes does not happen. If this is deferred to a later commit in the stack, worth saying so in the PR. Fix this →

  • 🟡 Medium: core-web/.../store/features/folder-delete-runs/with-folder-delete-runs.ts:186establishInFlightFolders re-marks the run's full submitted path set, which can leave failed folders marked indefinitely for other authors. On load it does acc[run.id] = run.parameters?.assetPaths ?? [] — the original submission, not the still-in-flight subset — then a full patchState replace. Consider a multi-folder run still RUNNING where one folder already failed (its FOLDER_DELETE_FINISHED announcement fired before this client connected). A second author who opens Content Drive now establishes from the active listing, re-marks that failed folder (it still exists and renders), and it never clears: for a non-submitter there is no BULK_FOLDER_DELETE_COMPLETED (scoped to the submitter) and the per-folder FINISHED won't be replayed to a new socket subscriber, while clearRun is called by nobody. That is the exact "folders stay marked forever" symptom the PR says it avoids — here it survives across the establish path. It stays marked until the run drops out of the active listing and the author reloads again.

    • Assumption: the active listing's parameters.assetPaths is the original submission (as DotFolderDeleteActiveRun documents) and the socket does not replay past FOLDER_DELETE_FINISHED events to newly-connected clients.
    • What to verify: whether the backend active listing exposes remaining vs original paths, and whether a completed-folder announcement is re-delivered on (re)subscribe. If either is false this is a non-issue.
    • Secondary, same method: the full-replace on the HTTP response can also clobber a FOLDER_DELETE_STARTED announcement that arrived for a run not present in the snapshot the server returned (narrow init-time race).

Notes (non-blocking)

  • For a folder-delete partial outcome, dot-content-drive-shell.component.ts:780 falls back to content-drive.action-center.toast.executed-partial for the summary detail (there's no partialDetailKey set on the delete path, unlike upload's content-drive.upload.toast.partial). The per-reason lines from describeFolderDeleteOutcome are correct; only the one generic count sentence leans on workflow-flavoured wording. Minor.

The M-3 rollback note in the earlier automated comment concerns the base branch's searchScope REST change, not this frontend PR — nothing to act on here.

· issue-37063-bulk-folder-delete-frontend-impl

@github-actions github-actions Bot added the Area : Frontend PR changes Angular/TypeScript frontend code label Sep 17, 2026
@claude

claude Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

test

@claude

claude Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Pull Request Unsafe to Rollback!!!

  • Category: M-3 -- REST / GraphQL / Headless API Contract Change

  • Risk Level: MEDIUM

  • Why it is unsafe: This PR adds a new field, filters.searchScope, to the JSON request body of POST /api/v1/drive/search, and in the same PR updates the Angular Content Drive frontend to send that field when a user selects the Title scope (core-web/.../dot-content-drive-search-input.component.ts, dot-content-drive.store.ts) -- this is the M-3 co-migrated caller signal called out in the rollback-unsafe reference.

    The new field is deserialized through the shared JAX-RS ObjectMapper (DotObjectMapperProvider.createDefaultMapper()), which never disables DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES (Jackson default is true, fail-on-unknown), and neither AbstractQueryFilters nor AbstractDriveRequestForm carries a JsonIgnoreProperties(ignoreUnknown = true) override. ContentDriveResource.search() binds the request body directly to a DriveRequestForm via Consumes(APPLICATION_JSON).

    Net effect: if the backend is rolled back to N-1 while a Content Drive tab built against N is still open (browser cache, or a rolling/mixed-version deploy), any search where the user has the Title scope selected sends searchScope: TITLE inside filters to the N-1 backend. N-1 AbstractQueryFilters has no searchScope() member at all, so Jackson throws UnrecognizedPropertyException on that request instead of ignoring the extra field -- the search fails for that user with an error response instead of falling back to today behavior.

  • Code that makes it unsafe:

    • dotCMS/src/main/java/com/dotcms/rest/api/v1/drive/AbstractQueryFilters.java:42-44 -- new searchScope() member, no ignoreUnknown guard on the interface.
    • dotCMS/src/main/java/com/dotcms/rest/api/v1/DotObjectMapperProvider.java (createDefaultMapper()) -- shared mapper never disables FAIL_ON_UNKNOWN_PROPERTIES.
    • dotCMS/src/main/java/com/dotcms/rest/api/v1/drive/ContentDriveResource.java (search(), Consumes(APPLICATION_JSON) binding to DriveRequestForm form) -- the endpoint that deserializes the new field through that shared mapper.
    • dotCMS/src/main/java/com/dotcms/rest/api/v1/drive/ContentDriveHelper.java:590-598 -- new consumer of requestForm.filters().searchScope().
    • Frontend co-migration in the same PR: core-web/libs/portlets/content-drive/portlet/src/lib/... search input/store changes that emit searchScope once a user picks Title.
  • Alternative (if possible): Add JsonIgnoreProperties(ignoreUnknown = true) to AbstractQueryFilters (and ideally AbstractDriveRequestForm) so an unrecognized field degrades to field not present instead of throwing -- the standard fix M-3 recommends for additive request-contract changes; it only affects unknown properties. Alternatively/additionally, document in the release notes that a rollback during a Content Drive session with Title scope active will show a transient search error until the page reloads against the rolled-back backend (self-healing, no data at risk).

No database migrations, Elasticsearch mapping changes, or structural data-model changes were found in this PR -- the scan-limit/time-budget rewrite in BrowserAPIImpl.java is pure in-memory query logic with no persisted or indexed state at risk.

rjvelazco and others added 7 commits September 18, 2026 11:30
…#37063)

Red phase for the frontend half. No behaviour is implemented: every new
production file is a stub that throws, so the specs compile and fail on what
they assert rather than on a missing symbol. The stubs are temporary by
agreement and T091 checks that none survive the feature.

Types first, because the server half is specified but not built. The wire
contract is typed once in dotcms-models so a divergence when the real endpoint
lands is a compile error at a handful of sites rather than runtime breakage
spread through the UI, and one fixtures module gives every spec the same
source of truth for the shapes.

The one that will be got wrong if it is not written down: the queue's "active"
listing returns every non-terminal run, failed and abandoned included. Marking
folders from it unfiltered reports a delete that already failed as still
running, and the folder stays marked until the framework moves it on. That is
why isJobInProgress exists rather than a not-finished check, and why the
fixtures carry a failed and an abandoned run.

Also adds the frontend contract and data model, including an open gap raised
with the backend half: C-004 requires the four submission refusals to be
distinguishable but an empty selection and an over-maximum selection are both
400 with nothing to tell them apart. The tests assume an error code in the
body; if the server picks differently only the service and its spec change.
Recorded at #37063 (comment)

16 tests failing, 1 vacuously passing until the action exists.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…7063)

Green phase for the first story. An author selects folders, confirms once,
and the submission is accepted while they keep working.

Delete joins the quick-action registry as the first folders-ONLY action, and
that distinction earned a new flag. `supportsFolders` widens an action's scope
to the whole selection because it acts on contentlets and folders alike; Delete
acts on folders and nothing else, so scoping it that way left it rendering at
count 0 over a selection of files — a disabled Delete row that applies to
nothing, which is noise and mildly alarming noise. `foldersOnly` scopes it to
the folders in the selection, so a selection holding none drops the action
entirely, matching how the contentlet-only actions already drop from a
folder-only selection.

Two identities, deliberately kept apart: the server works in site-qualified
paths, the listing marks by row key, and the run carries BOTH a folder's inode
and its identifier because the search service only backfills one of them when
the API returned none.

The confirmation says the folders and everything inside them go, permanently.
It makes no claim about workflow: "no workflow action runs on the contents" is
false where a content type declares an action for the destroy system action,
and the issue text proposed saying exactly that.

The run's total is replaced with the count the SERVER accepted once the handle
comes back, so the first screen and the last agree even when a duplicate or a
nested path is dropped.

Two test corrections, both cases of the spec being wrong rather than the code:
the back button is `action-preview-back`, and the message-service mock echoes
its key and drops its arguments, so the folder count is asserted through the
service rather than through rendered text.

249 test files green across portlets-content-drive, content-drive-ui, ui and
data-access.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…too (#37063)

US2. The grid already marked its rows — that shipped with #37166 — and this is
the other half. Without it the grid's marking is a door left open: an author
can still walk into, expand, or drop content onto a folder that is being
destroyed, and what they put there dies with it.

Three separate refusals, not one. Selecting a marked node navigates nowhere,
expanding it lists nothing, and dropping on it is declined before the node
becomes an active drop target — content dropped there would be created and
then destroyed with the folder.

The tree reads the SAME store signal the grid marks rows from rather than
deriving its own. Two sources would drift, and the drift reads as a folder
inert in one surface and usable in the other, which is worse than marking
neither: it teaches the author that the marking cannot be trusted.

`inFlightKeys` is optional and empty by default, because the AssetPicker
renders this same tree and has no such concept. It carries both a folder's
inode and its identifier for the reason the grid does — the search service
only backfills one of them when the API returned none.

Marking stops at the folders submitted and their own nodes. It does not walk
descendants: a prefix test on every node on every render, for folders that are
about to disappear, buys nothing.

Both surfaces now report busy to assistive technology. Reduced opacity and
disabled pointer events reach one sense only, and for an operation that
permanently destroys things the consequence of missing the signal is acting on
a folder about to cease to exist.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…eryone (#37063)

US3, and the correctness core of the feature. Until now the marking lived only
in the page that started the delete: reload, and a folder being destroyed looked
perfectly ordinary again — openable, uploadable, a valid drop target. A measured
delete of a 20,000-file folder took about eight minutes, which is long enough
that an author will reload, open a second tab, or hand the screen to a colleague
inside that window.

Three mechanisms, each doing one job, and none of them polling. The set is
ESTABLISHED once on load from the queue's in-flight listing; KEPT CURRENT from
the per-folder announcements the server makes as folders enter and leave a
delete; and RE-ESTABLISHED on the next load rather than inherited — a run whose
process dies never announces that it ended, so announcements alone would mark a
folder indefinitely with nothing to correct it.

THE ONE THAT WILL BE GOT WRONG IF IT IS NOT WRITTEN DOWN: the endpoint is called
"active" but answers with every run in a NON-TERMINAL state, failed and
abandoned ones included. Read unfiltered, it reports a delete that already
failed as still running, and the folder stays marked until the framework moves
the run on. The symptom reads in QA as "sometimes folders stay marked forever",
which looks like a client defect and is not one. Hence isJobInProgress, and
hence a fixture that deliberately carries a failed run and an abandoned one.

Kept out of withActionExecution deliberately. That feature owns runs this client
fired — started here, ended here. This one holds runs other authors started,
which this client can never end. Two lifecycles behind one name would make
busyRows mean two things. They merge at a single computed instead, so the
listing and the sidebar tree cannot disagree.

Paths are the key throughout, because that is the identity every server message
uses, and they resolve to row keys at the edge — against the rows currently
shown, so the work is bounded by the page rather than by how much is being
deleted across the instance.

One spec fix while writing the tests: a single mocked socket stream made a start
announcement reach the finish handler too, so the feature added a folder and
immediately dropped it — a green test proving the opposite of what it read as.

250 test files green across the four touched projects.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…vived (#37063)

US4, and the last P1. A run that ends now tells the author the server's counts
and names every folder that did not go, with a reason they can act on.

Reason -> copy mirrors failure-reasons.ts, which does the same job for upload,
and for the same stated reason: a reason with no copy is a hole the author sees.
A Record rather than a switch, so adding a reason to the contract fails the
build here until someone writes its sentence. Written once because folder copy
(#37062) and move (#37165) read the same outcome shape and the same vocabulary.

SKIPPED needed two sentences, not one. A folder an ancestor already removed and
a folder the run never reached are both "not attempted" for entirely different
reasons, and only the second is a problem. Telling an author their folder was
skipped, when what happened is that deleting its parent took it, reads as a
fault where there was none.

The server's per-folder message is never rendered anywhere. It is diagnostic,
written for a log, and a test asserts a real stack trace does not reach the DOM.

Reporting reuses the store's existing outcome queue and the shell's existing
rendering rather than adding a second path: reportFolderDeleteCompleted mirrors
reportUploadCompleted down to the hasOwnProperty guard and the refusal to invent
counts for a run that recorded no outcome. Substituting zeros would claim a
clean run over nothing; substituting the number submitted would claim every
folder was deleted.

One new field on the result, outcomeKind, picks the describer. The two reason
sets overlap only on PERMISSION_DENIED and UNCLASSIFIED, so guessing from the
values would resolve a delete's IN_USE through upload's mapping and land on the
unclassified fallback — a reason that HAS copy, rendered as though it had none.

Delete's lines come back as one group rather than upload's warn/error split:
upload splits because a refused filename and a missing permission are different
kinds of news, while every delete line is already "this folder survived, here is
why". Severity follows whether anything actually failed, so a run whose only
shortfall is skipped folders does not arrive in red.

251 test files green across the four touched projects.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… the outcome (#37063)

US5, US6 and US7, plus polish. The feature is complete against its spec on the
client side; what remains needs a server to exist.

US5 needed no code. Both its tests passed as written, because #37166 had already
moved run ownership from the dialog into the store — so a delete surviving its
dialog closing is a property the portlet already had rather than one this
feature adds. Recorded as a verification rather than dressed up as work.

US6: the sidebar tree now reloads when a delete settles, and only then. The
listing and the tree load separately, so refreshing one is not refreshing the
other, and a tree still offering a folder the listing has dropped is how an
author navigates into nothing. An upload changes contents rather than hierarchy,
so reloading the tree for one is a request that can only return the same tree.

US7 needed a new concept, and the reason is worth stating. Delete is withheld
only when the author may delete NONE of the selection; one they may only partly
delete is still submitted whole, with the refusals reported per folder. Those
two rules cannot both live in `eligibleWhen`, which narrows *which items* an
action fires on — using it here would silently shrink a destructive action the
author explicitly selected, which is the failure FR-004a exists to prevent. So
availability became its own whole-selection predicate, `availableWhen`, and the
count stays what will actually be submitted.

The gate itself is EDIT + EDIT_PERMISSIONS: what the shipped context-menu delete
checks, and what FolderAPIImpl.delete enforces. Absent permissions read as
permitted rather than refused — undefined means the search did not ask, which is
not the same as an empty array meaning the author holds none.

The end-to-end test is authored and skipped, covering the two things a unit test
cannot reach: a run surviving a reload, and both surfaces settling together. The
suite lints skipped tests as errors, rightly, so the exemption is explicit at the
line with its reason and its removal task rather than the rule being weakened.

One assertion was reduced rather than contorted. "An upload does not reload the
tree" cannot be honestly asserted in the shell spec, because the sidebar renders
inside it and owns its own effect that reloads the tree — every form of "was not
called" passes or fails on that caller's timing rather than on the behaviour.
The comment says so, and says what isolating it would cost.

252 test files green; lint clean; no stub survives the feature.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… pinned them (#37063)

The gap this client raised is closed. Submission refusals are told apart by
`errorCode` on the REST layer's existing `ErrorEntity` — the type
`ValidationException` already builds its body from — rather than by the ad-hoc
field the tests had to assume while the question was open. Status alone could
never separate the two 400s, and each needs different copy.

This is the divergence the typing was for: one method and its spec changed, and
nothing else. The refusal kinds the rest of the client switches on were stable
throughout, exactly as the contract note promised.

The fallback deliberately does NOT guess between the two 400s when a body
carries no code. Telling an author they selected nothing when they hit the
ceiling sends them looking for the wrong fix; UNCLASSIFIED says less and misleads
less.

ONE SHORTFALL, recorded rather than worked around. FR-040 asks the client to name
the folder an overlapping run collided on, and the body carries no structured
field for it — the folder appears inside `message` as server-generated English,
which is not localised and so is not rendered. The refusal therefore says
something in the selection is already being deleted without saying which. Parsing
the sentence was considered and rejected: it breaks the first time the wording
changes, and it puts server English on screen by the back door. Closing it needs
a structured field, which is now written down in the contract.

Also merges the base branch, which was one commit ahead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rjvelazco
rjvelazco force-pushed the issue-37063-bulk-folder-delete-frontend-impl branch from 5dd7998 to 638c898 Compare September 18, 2026 15:30
rjvelazco and others added 3 commits September 18, 2026 11:56
…ds (#37063)

CI caught what the unit tests could not: `signalStore` has typed overloads for at
most fifteen features, and this feature pushed the Content Drive store to
sixteen.

The failure mode is worth knowing, because it does not point at the cause. Past
the limit the call silently falls off its overload, `store` degrades to `object`,
and the compiler reports "Property 'pages' does not exist on type 'object'" on
pagination lines nobody touched. Nothing says "too many features"; the one honest
signal is a TS2769 on the `signalStore(` call itself, buried in the cascade.

Two arguments came back. `allBusyRows` moves into `withFolderDeleteRuns`, which
is better placement anyway — it merges that feature's own state with `busyRows`,
so the feature owning half the answer now owns all of it and declares the other
half as a required input. The two ceiling computeds fold into one block.

Both failure describers now take the wide `DotBatchItemResult<string>[]`. Their
whole contract is already that an unrecognised reason falls back rather than
throwing, so the narrow type bought nothing and cost a cast at the one call site
handling a shared outcome — a cast being precisely where a fallback stops being
visible to the next reader.

WHY THE TESTS MISSED IT: Vitest transpiles through esbuild without typechecking,
so a type error cannot fail a unit test here. `nx build` is the gate that catches
it, and it is now part of the local loop rather than something the pipeline finds
twenty minutes later.

Verified locally before pushing: typecheck back to its pre-existing baseline with
nothing from this feature, lint clean, `nx build dotcms-ui` green, and 251 test
files green across the four touched projects.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
)

Whitespace only, 27 lines. Folding the two `withComputed` blocks into one left
`$canAddChildren` and its JSDoc a level short, which the frontend `format-test`
step rejects.

Worth recording why this reached CI at all: the local checks in this session ran
through `npx prettier`, which does not resolve this workspace's binary — it
reported timings and changed nothing. `pnpm exec prettier` is the repo's 3.8.1
and is what `format-test` runs, so it is what the local loop uses from here.

Verified with the build's own command: `pnpm exec prettier --check` over all 36
files this PR touches now reports "All matched files use Prettier code style",
with tests and `nx build dotcms-ui` still green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ded (#37063)

The strict gate checks only the lines a PR writes, and the four spec files this
branch touched carried nine violations. None of them changed what the tests
assert; all nine were the specs being looser than the code they cover.

- the e2e file called `drive.goto()`, which does not exist — the page object's
  method is `goTo()`. Transpiled fine, would have failed at run time, and the
  file is skipped until the backend lands so nothing would have caught it.
- `dot-folder-bulk-delete.service.spec.ts` typed a request body as `unknown`
  and then indexed into it.
- `withActionExecution.spec.ts` left the submit mock's return type to be
  inferred from a bare `Subject`.
- `with-folder-delete-runs.spec.ts` cast a ten-field-short object literal to
  `DotContentDriveState`. Replaced with `buildContentDriveState`, a complete
  builder in `shared/`, sourced from `shared/constants` — `withActionExecution`
  already records that the hand-rolled fixtures in sibling specs went stale and
  stayed green, because the runner transpiles without typechecking.

The same spec's `signalStore` call also has to supply `busyRows`, which
`withFolderDeleteRuns` declares as an input and the real store gets from
`withActionExecution`. It is stood up through `signalStoreFeature` with an
explicit input rather than an inline `withComputed`: left to positional
inference in a chain that long, TypeScript widens the store to `object` and
reports it as an unreadable "no overload matches" on `signalStore` itself.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…d seeds (#37063)

The strict gate rejected `currentSite: undefined` and `path: DEFAULT_PATH`
(which is `undefined`): both fields are declared non-optional on
`DotContentDriveInit`. The store's own `initialState` does exactly this to mean
"not resolved yet", but those lines are pre-existing and the gate only checks
lines a pull request writes — copying them into a new file put the same lie on
new lines.

Seeded with `SYSTEM_HOST` and an empty path instead. That is also the better
default for a feature spec, which tests behaviour after the portlet has settled
rather than before it mounts; a spec that wants the pre-mount shape overrides
these two and owns the cast.

Found by CI, not locally: the file was still untracked when the gate ran, and
the harness diffs base...HEAD through git, so it never saw it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area : Frontend PR changes Angular/TypeScript frontend code

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Content Drive: bulk folder delete, async job endpoint and frontend wiring

1 participant