Repository navigation
feat(cmdb-import): read the archive sheet and let module and usage be archived - #1285
Conversation
… archived Adds the OpenSpec change cmdb-import-archive-reconciliation (REQ-CMDB-015..017, REQ-CMDB-012 and REQ-CMDB-014 modified). The register fragment opts module (0.3.9) and usage (1.5.7) into OpenRegister's archive state, the import profile names the sheet "Gearchiveerde Applicaties" and the accepted missingRecords modes, and the workbook reader returns the APPIDs of that sheet, read under the same bounds as the CMDB sheets for its key column only. The archived-applications fixture covers it. First of three slices of #1281; nothing reads the archive sheet yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…export With missingRecords archive, the default, a completed import reconciles the municipality's usages and the modules its import created: an APPID on the archive sheet is archived, one on no sheet is soft-deleted, and without the archive sheet missing applications are archived only, with a report warning. A returning application is unarchived or restored before it is updated, never duplicated. Object state is read per lens (working, then _archived=true) and from the trash, because OpenRegister's list search hydrates no archive marker and drops soft-deleted rows. The endpoint accepts missingRecords keep or archive and translates ARCHIVE_UNAVAILABLE; OpenAPI and Postman describe the new values and outcomes. Second of three slices of #1281, on top of the archive-sheet slice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…uick filter The CMDB import section lets the admin archive or delete applications missing from the export, or keep them, and shows the archived, unarchived, deleted and restored outcomes. The Applications and Applications in use pages get an Archived quick filter. Vitest and the Playwright spec cover it; the feature docs and the changelog describe it. Third of three slices of #1281, on top of the reconciliation slice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Quality Report — ConductionNL/stackiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| check-l10n-js | ✅ | ||||
| composer | ✅ | ✅ 133/133 | |||
| npm | ✅ | ✅ 807/807 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| lockfile sync | ✅ | ||||
| PHPUnit | ✅ 1233/1233 · 20 skipped | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development. |
||||
| Hydra gates | ✅ |
Quality workflow — 2026-10-12 10:38 UTC
Download the full PDF report from the workflow artifacts.
…rt-import spec The fragment test was renamed to testTheMergedModuleIsVersion039WithTheExternalIds when the module schema moved to 0.3.9; the live spec still named the 038 test. The archived change keeps its historical name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…' into feat/woo587-cmdb-import-archive-reconciliation-2-reconcile
trash() set the cached trash to empty before it read the listing, so a listing that threw failed its row and left every later row of the run matching against an empty trash: a soft-deleted module that returned was created a second time. The cache is now set only once the listing was read; the next row reads it again. The missing-archive-services path keeps caching the empty trash, since a retry cannot succeed there. Mutation-checked: testAFailedTrashReadIsNotCachedForTheNextRow fails on the previous trash() (outcomes and read count) and passes on this one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…export-import change openspec/changes/cmdb-export-import was archived to openspec/changes/archive/2026-10-05-cmdb-export-import, so the tags this change added on addRunWarnings() and the module write path named a file that no longer exists. The older tags in lib/ share the stale path and are left for a separate sweep. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ile' into feat/woo587-cmdb-import-archive-reconciliation-3-ui
…d to split the export With missingRecords archive as the default, two texts were now wrong: - The section's notes said applications missing from a newer export "are left as they are"; they are archived or deleted unless the admin keeps them. - The TOO_MANY_ROWS, FILE_TOO_LARGE and WORKBOOK_TOO_LARGE hints, in the section and in the feature docs, advised splitting the export and importing the parts one after the other. An archiving import of one part treats the applications of the other parts as missing, so each part archived or deleted the others. The hints now say to import every part with "Keep them as they are". Dutch follows in nl.js and nl.json. Checked with jest (cmdbImport.spec.js, CmdbImport.spec.js: 86 passed), test:l10n and check:l10n-js; eslint could not run locally (Node lacks module.findPackageJSON). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A split export imported with Keep them as they are archives and deletes nothing, so the FILE_TOO_LARGE and TOO_MANY_ROWS rows now say that applications which left the export are then not archived or deleted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Verdict: APPROVE (Thorough) — self-review posted as COMMENT (GitHub blocks self-APPROVE)
No blockers: the archive sheet is read under the CMDB sheets' bounds and the readBounded() refactor returns the same archive array in all three branches. The concerns are about silent gaps (uncached formulas, untested bounds) that matter once #1286 acts on these APPIDs.
Update 2026-10-12: All three concerns were fixed on the branch after posting; the four minors stay open as follow-ups.
🟡 Concerns (3)
- Uncached archive-sheet formulas vanish without a warning —
lib/Service/Cmdb/CmdbWorkbookReader.php:366. 🟡 Concern — the archive sheet drops a formula without a cached value silently, and reportspresent: truewith fewer APPIDs The archive sheet is formula-derived: the new fixture's APPID cell is<f>..</f><v>1234</v>(build-fixtures.py,archive_appid). ✅ Addressed in16337d91. - Archive sheet bounds now apply to imports that keep —
lib/Service/Cmdb/CmdbWorkbookReader.php:187. 🟡 Concern — an export that imports today can be refused because of a sheet the admin chose not to act onread()takes no mode, so from this slice on every import, includingmissingRecords=keep, loads the archive sheet and enforces on it the part-size bound (here), the row span, the referenced-string budget andTOO_MANY_ROWS. ✅ Addressed incdbec3d3 (#1287). - 'Same bounds as the CMDB sheets' is tested for one of five bounds —
tests/Unit/Service/Cmdb/CmdbWorkbookReaderTest.php:1005. 🟡 Concern — the totality promise "the archive sheet is read under the same bounds as the CMDB sheets, APPID column only" has one test REQ-CMDB-015, design D3 and the PR body promise that. ✅ Addressed in11d9a23c.
🟢 Minor (4)
- tasks.md ticks tasks whose code is in #1286/#1287 —
openspec/changes/cmdb-import-archive-reconciliation/tasks.md:37. 🟡 Concern — tasks 3 to 7 are marked implemented and tested in a PR that contains none of that codetasks.mdticks[x] Implementand[x] Testfor Task 3 (matching revives,CmdbExportImportService.php,CmdbImportReport.php), Task 4 (reconciliation), Task 5 (controller option), Task 6 (settings section) and Task 7 (fixture, docs; only its Test is open). - Referenced-string budget shrinks for workbooks without the archive sheet —
lib/Service/Cmdb/CmdbWorkbookReader.php:313. 🟢 Minor —count($profile->readSheetNames())always counts the archive sheet, also when the workbook has noneassertReferencedStringBytesdividesmaxReferencedStringBytesbysheetCount(CmdbWorkbookBounds.php:207). - appIds are raw cell values, not match keys —
lib/Service/Cmdb/CmdbWorkbookReader.php:366. 🟢 Minor —archive.appIdsholds raw typed cell values; say so, or normalise Read-only probe: cells7.0,' 8 ','app-9'come back as7(int),' 8 'and'app-9'. - Profile accepts 'archive' while the endpoint still refuses it —
lib/Settings/cmdb-import/topdesk-profile.json:48. 🟢 Minor — heads-up: until #1286 merges, the profile and the endpoint disagree The profile now lists["keep", "archive"]andmissingRecordsModes()reads it, but nothing inlib/callsmissingRecordsModes()on this head (only the unit test does):CmdbExportImportService::supportsMissingRecords()is still$mode === 'keep'andCmdbImportControllerstill answersdetails.accepted: ['keep'].
✅ Fixed before posting (1)
- Renamed test is still named by the live spec — fixed in
388181a.
✅ Verified clean
readBounded()refactor: the returnedarchivearray matches the pre-refactor version (softwarecatalog141ea249) for no sheet, a sheet without the key column and a sheet with it — checked by reading and a read-only probe.- Schema bumps module 0.3.9 / usage 1.5.7 follow the fragment-version convention and are pinned by
TopdeskCmdbFragmentTestandPublicationFieldRulesTest. x-openregister-archivematches OpenRegister'sSchema::ARCHIVE_ANNOTATION.
Scope decision: slice 1 of 3 of the closed #1281, stacked on development; reviewed against its own base only.
Gates: CI quality / Hydra Gates and the required PHP/Vue quality checks green on {h}; quality / PHPUnit was still running at submission.
ADRs verified: ADR-005, ADR-031, ADR-032, ADR-037 — all resolve on hydra main.
Jira context: WOO-587 — CMDB-import: gearchiveerde en verdwenen applicaties bij herimport (status: Refinement)
Quality Report — ConductionNL/stackiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| check-l10n-js | ✅ | ||||
| composer | ✅ | ✅ 133/133 | |||
| npm | ✅ | ✅ 807/807 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| lockfile sync | ✅ | ||||
| PHPUnit | ✅ 1233/1233 · 20 skipped | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development. |
||||
| Hydra gates | ✅ |
Quality workflow — 2026-10-12 12:53 UTC
Download the full PDF report from the workflow artifacts.
…ng progress In the reconciling phase the service reports processed = rows + index and total = rows + the municipality's applications, so the page showed "410 of 810" as if it were applications missing from the export, and the bar fell from 100% to about 50% when the phase began. The scope also holds applications that are on the sheets and need no change. The progress view now shows no count for that phase, says it checks the applications of the municipality against the export, and keeps the bar full. The l10n key is replaced in place; the docs describe the new progress line. Review #1287 f3 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After Cancel the section said the import "stops before the next row" and "the rows processed before it stopped are kept". During the comparison it stops before the next application, and the applications archived or deleted until then stay too; the cancelled banner told the admin nothing of that while the report listed them. Both strings now name applications and the l10n keys are replaced in place; the docs say the same. The spec mounts the section for real so the banner and the status line are read from the page. Review #1287 f4 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The archive sheet is formula-derived. A row whose APPID formula carries no cached value read as an empty row and was skipped without trace, so the sheet reported present: true with one APPID fewer. The reconciliation would then see that application on no sheet and soft-delete it instead of archiving it, with nothing in the report saying why. REQ-CMDB-002 already treats such a cell on the CMDB sheets as empty plus a row warning. readRows() now hands back the numbers of rows that read as empty only because a formula in them is uncached, and the reader returns them as archive.uncachedRows. The spec delta (REQ-CMDB-015, new scenario), design D3/D5 and task 2 state the rule: such a sheet is incomplete, so the import warns naming the sheet and rows and archives every missing application, deleting none. The service side follows in the stacked PR. Review #1285 f1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
REQ-CMDB-015 and design D3 promise that the archive sheet is read under the same bounds as the CMDB sheets, for its key column only, but only the row limit had a test. The part size, the row span, the referenced-string budget, the key-only read filter and the null cases of archiveSheetName() could regress unnoticed; swapping readSheetNames() for sheetNames() in read() left the reader tests green, which would blank an oversized archive sheet instead of refusing it. Adds one test per bound. RecordingXlsxReader now also keeps the read filter of each load, so the key-only claim is asserted on what the data pass is allowed to read, not on the returned array. Each test was checked by mutating the reader (archive sheet left out of that bound) and goes red. Review #1285 f5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…' into feat/woo587-cmdb-import-archive-reconciliation-2-reconcile
…the finished sentence The sentence the aria-live region announces (and the report heading shows) only had the row counts, so an import that read 0 rows and archived one application and deleted one was announced as "0 rows read: 0 created, 0 updated, 0 unchanged" and a screen reader user never heard that applications were deleted (WCAG 4.1.3). The sentence now ends with "Applications: ..." listing the non-zero counts, after a cancel too, and nothing when no application was touched. The docs mention it. Review #1287 f5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…le APPIDs The reader now lists the archive-sheet rows whose APPID formula has no cached value (archive.uncachedRows). The service treats such a sheet like an absent one for the choice between archive and delete: an application on no readable sheet may be the one that row holds, so it is archived, never soft-deleted, and the report carries a warning naming the sheet and the rows (cut after ten). Applications the sheet does list are archived as before; keep is unaffected. Without this a workbook saved without cached values turned every archived application into a deletion, unannounced. The warning is translated (en, nl) and the OpenAPI description of missingRecords names the extra case. Review #1285 f1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e the facet id set The Applications page narrows its list with `_ids` (the objects a GEMMA facet or the search matched) and merges the active quick filter's map next to it, so the tab's `_archived=true` was ANDed with an id set that FacetService builds from the working set. OpenRegister's default lens leaves archived objects out of that set, so with a facet selected the Archived tab asked for live ids that are archived and listed nothing. The Archived tab's filter now also carries an empty-string `_ids`, which wins over the page's `_ids` in the merge and is left out of the request. An empty array would not do: the filter resolver drops it before the merge. The tab lists the whole archive and ignores the GEMMA selection and the search term; the docs say so. The vitest runs the package's own filter resolver and query builder, and pins that the active tab is merged last. Read in @conduction/nextcloud-vue 2.65.0 (the installed version). Review #1287 f6 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The reconciliation listed every usage of the municipality, so a stale module deleted by hand next to the one a later import created got its own scope entry next to the working one. With the key on the archive sheet the trashed pair was restored and archived: a long-deleted copy of the application appeared in the archive. Every other match path (matchModule, upsertUsage, findInAnyState) prefers working over archived over trash. reconciliationScope() now keeps one entry per key through onePerKey(): the best module state wins, the best usage state breaks a tie, the first listed the rest. The other entries are left as they are. REQ-CMDB-015, the design D5 text and task 4 name the duplicate case. Review #1286 f4 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed-row rule testable The section's tests set `missingRecords` on the component and stubbed the radio away, so `v-model`, `value`, `name` and `:disabled` on the two radios were never exercised, no test clicked "Keep them as they are", and the rule that a deleted application has no link to its module had no test at all. The jest spec now mounts the section with radio and table stubs that render what the page does, clicks Keep and reads the posted form, checks the default, the shared name and the disabled state, and asserts the report table links an archived or failed row and not a deleted one, and shows no row number for a reconciliation row. Each test goes red when the wiring or the rule is mutated. The e2e adds a Keep import that must archive and delete nothing and a check that the archived application is absent from the Applications page before the Archived tab; the spec scenario for the choice now points at it. Review #1287 f8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… sheets the import reads The WORKBOOK_TOO_LARGE hint said the import "reads only" the two CMDB sheets and told the admin to delete the sheets it does not read; the section's file help said other sheets are ignored. The import also reads the APPID column of "Gearchiveerde Applicaties", and an admin who followed the hint would delete that sheet, so the next import archives and never deletes. Both strings now name the archive sheet and its APPID column (replacing the l10n keys in place), and the spec pins the new hint. Review #1287 f9 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
REQ-CMDB-015 promises that only this municipality's usages and what its import created are reconciled, that each application has its own error boundary, that a cancel before the scope is loaded reads nothing, and that the match never duplicates (working before archived before deleted). Most of these branches had no test that a mutation could turn red; the first one, the consumer re-check on a trash listing that is not filtered by consumer, guarded the only thing between another municipality's module and the trash. Adds tests for: a trashed usage of another consumer; modules with no external key, a bare prefix or another origin; a delete that OpenRegister refuses and a restore that throws, each with a following application still reconciled; more than one page of usages and one chunk of modules; archived beating deleted and working beating archived in the match; and an archived match failing its row under keep when the archive services are missing (design D7). The cancel test now also asserts the trash was not read and the usages were not paged. Each test was checked by mutating the service and goes red. Review #1286 f3 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ry import The docs said the import "ignores all other sheets" and that a sheet it does not read may exceed the part size limit. The archive sheet is read (its APPID column only) in every import, also with Keep, so it counts against the row span and part-size limits like a CMDB sheet and can refuse a keep import. The file section, the row limit, the part-size limit and the WORKBOOK_TOO_LARGE row now say so, and that deleting the sheet from a copy makes the import archive and never delete. Review #1285 f2 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The warning has a one-row form and an "in N rows (…)" form that names the first ten rows; the docs quoted only the first. Review pre-push q4 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Quality Report — ConductionNL/stackiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| check-l10n-js | ✅ | ||||
| composer | ✅ | ✅ 133/133 | |||
| npm | ✅ | ✅ 807/807 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| lockfile sync | ✅ | ||||
| PHPUnit | ✅ 1239/1239 · 20 skipped | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development. |
||||
| Hydra gates | ✅ |
Quality workflow — 2026-10-12 16:18 UTC
Download the full PDF report from the workflow artifacts.
… document the reader's bounds Closes the review findings: - "design D8 describes UI code not in this PR": D8 now says the UI code lands in #1287. - tasks.md ticked Implement/Test for Tasks 3-7 whose code is not in this PR: unticked, with a status note that they land in #1286 and #1287. - CmdbWorkbookReader sheet count always includes the archive sheet: documented in the read() docblock (limit / 3 is deliberate). - appIds are raw cell values: documented as raw, untrimmed and un-normalised; no consumer exists in this PR, so no code change. Class sweep: docs/features/cmdb-import.md described the referenced-string bound as "once per CMDB sheet"; it now names the archive sheet too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…' into feat/woo587-cmdb-import-archive-reconciliation-2-reconcile
The help under "Applications missing from the export" was a paragraph of seven lines. It is now one sentence; the full rules, including when applications are only archived, moved to the section's information panel. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…able; usages before modules Closes the review findings: - "Unreadable CMDB-sheet APPID still soft-deletes": a CMDB-sheet row whose APPID is a formula without a cached value is skipped as empty, so its application looked absent. The run now notes such rows per sheet and, with any, archives instead of soft-deleting and warns naming the CMDB sheet and the rows (reusing the archive-sheet warning). New test testAnUncachedCmdbRowArchivesInsteadOfDeleting. - "Extras written after the module; no failing-extra test": itemsOf() now orders every usage (own and extra) before every module, as the docblock promises. New test testAFailingExtraUsageFailsTheApplicationBeforeItsModule. - archiveServices() swallowed the container exception: it is now logged (exception class only) and chained as previous. - trash() read for missingRecords=keep: kept on purpose (matching is mode independent, a hand-deleted application must be restored, not duplicated); the reason is now in its docblock. Class sweep: the rule is updated in the service docblocks, openapi.json (missingRecords description), and the change's spec.md (requirement and new scenario), design.md (D3 text, D5 table, risks) and proposal.md. docs/features/cmdb-import.md does not describe archive reconciliation yet, so it holds no copy of the rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Closes the merge artefact from the base fix, which unticked Tasks 3-7 and said their code lands in #1286/#1287: - Tasks 3, 4 and 5 (matching, reconciliation, controller option) are implemented and tested here: Implement and Test ticked again. - Tasks 6 and 7 stay unticked: the UI is #1287, and the docs and the e2e run of task 7 are not done. - The status line now says what is true for this PR. - Task 4's acceptance criterion also names the CMDB-sheet unreadable APPID case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ile' into feat/woo587-cmdb-import-archive-reconciliation-3-ui
…describe what a failed application leaves Closes the pre-push findings: - "Fully-empty CMDB row with an uncached APPID stays invisible": the reader dropped a CMDB row whose only content is an APPID formula without a cached value before the service could see it, so its application could still be soft-deleted. read() now returns `uncachedKeyRows` (source sheet => dropped row numbers, collected only when the key column is the uncached one), and the service merges them with the rows that carry `uncached` into the same unreadable-key bookkeeping, so the run archives instead of deleting and warns. New tests: testAnEmptyCmdbRowWithAnUncachedKeyIsListed (reader) and testAFullyEmptyCmdbRowWithAnUncachedKeyArchivesInsteadOfDeleting (service). - "Failure-branch docblock overstates": the itemsOf() and reconcileApplication() docblocks and design.md D5 and Risks now state the order (own usage, extra usages, module, extra modules) and what a failure leaves: a failing extra usage leaves the own usage changed, that extra visible and the module unchanged, and the row only says failed. Class sweep: other callers of readRows() are the archive sheet (unchanged, no key filter) and readSourceRows(); the read() docblock names the new key. The spec and proposal speak of "a row of a CMDB sheet" in general and need no change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… loudly on a bad lens read Closes "e2e waits for networkidle, which never settles on Nextcloud (gate-58)": the archive test now waits for a working application row to be visible before asserting that the archived application is absent, so the negative assertion cannot pass on a list that has not rendered. No other networkidle wait remains in the touched spec (git grep over tests/e2e shows only comments explaining why it is avoided). Closes thread 4248187506 (e2e cleanup comment): the header and the helper doc no longer claim that an interrupted run leaves nothing behind or that _includeDeleted adds the trash (a soft-deleted row cannot be listed, design D9); objectsInLens now fails the test on a non-OK response instead of returning an empty list that made cleanup skip silently. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… ignored Closes "Archived tab keeps facet sidebar and search active but ignores them": FacetedCatalogIndexView tracks the active quick-filter tab and shows a note under the toolbar while a tab whose filter carries the empty _ids override is active; en and nl strings and the generated l10n js are added. A mounted view test asserts the note appears on the Archived tab, is gone on another tab, and goes red with the note removed. Closes "Archived-tab facet override is tested against a local merge, not the view": the facet-override test now calls the library's own useSelfFetchList and reads the fixedFilters getter it hands the list composable, instead of restating the spread; only the list composable and store are replaced. It goes red when the _ids override is removed from the manifest. The source grep test that pinned the merge order is dropped as redundant. Class sweep: the Services page uses the same view but has no quick filter with the override, so it shows no note. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e and unreadable CMDB APPIDs Makes tasks.md true for this PR after the merge of the reconcile base: the status line names #1286 for tasks 1-5 and this PR for task 6 (ticked, with the new Archived-tab note as an acceptance line); task 7 has its implementation ticked and its Playwright run still open. docs/features/cmdb-import.md already described archive and soft delete (the old "left as they are" sentence only remains for the Keep choice); it now also says that an APPID without a cached value on a CMDB sheet archives instead of deleting, and that the Archived tab shows a note about the ignored facets and search. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ile' into feat/woo587-cmdb-import-archive-reconciliation-3-ui
… tab The view started with no known tab and only learned of a change from the tab's click, but it remounts CnIndexPage whenever the facet or search key changes, which puts the page back on its default tab; the note then showed over the default list. The view now starts from the tab the library starts on (first quick filter marked default, else the first) and returns to it whenever the remount key changes. Tests: the note follows the default tab at mount, and disappears when a search change remounts the list while the Archived tab is active; the remount test goes red without the reset. The e2e wait for a listed application is scoped to the Applications list container, so a row of another table cannot satisfy it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…reconciliation-3-ui' into the review fixes # Conflicts: # l10n/en.js # l10n/en.json # l10n/nl.js # l10n/nl.json
Quality Report — ConductionNL/stackiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| check-l10n-js | ✅ | ||||
| composer | ✅ | ✅ 133/133 | |||
| npm | ✅ | ✅ 807/807 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| lockfile sync | ✅ | ||||
| PHPUnit | ✅ 1239/1239 · 20 skipped | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development. |
||||
| Hydra gates | ✅ |
Quality workflow — 2026-10-13 17:29 UTC
Download the full PDF report from the workflow artifacts.
WilcoLouwerse
left a comment
There was a problem hiding this comment.
Verdict: APPROVE (Standard) — self-review posted as COMMENT (GitHub blocks self-APPROVE)
Nothing left to address: the one new minor and the four open minors from round 1 were fixed on the branch before posting. All 7 threads are resolved. What still stands between this PR and a merge: 1 approval, and the CI run on
ae31ac3, which started with this push.
| Round | Found | Status |
|---|---|---|
1 (388181a) |
🟡 uncached archive formulas vanished · 🟡 archive bounds applied to keep · 🟡 'same bounds' tested for 1 of 5 · 🟢 tasks.md ticks · 🟢 string budget · 🟢 raw appIds · 🟢 profile vs endpoint | 3 🟡 resolved in 16337d9/11d9a23; 3 🟢 resolved in ae31ac3; 1 🟢 needed no change (#1286) |
2 (ae31ac3) — this review |
🟢 design D8 described UI code from #1287 | fixed before posting in ae31ac3 |
| Finding | Was | Now | Where |
|---|---|---|---|
| 🟢 tasks.md ticks tasks whose code is in #1286/#1287 | 🟢 r1 | resolved in ae31ac3 |
thread |
| 🟢 Referenced-string budget shrinks for workbooks without the archive sheet | 🟢 r1 | resolved in ae31ac3 (documented; docs copy corrected) |
thread |
| 🟢 appIds are raw cell values, not match keys | 🟢 r1 | resolved in ae31ac3 (documented) |
thread |
| 🟢 Profile accepts 'archive' while the endpoint still refuses it | 🟢 r1 | no change needed: #1286 makes the endpoint accept it | thread |
| 🟢 Design D8 describes UI code not in this PR | new | fixed before posting in ae31ac3 |
openspec/changes/cmdb-import-archive-reconciliation/design.md:118 |
Details of this round
Scope decision: delta 388181a…70d6f93 (4 commits, 7 files, 281 changed lines), plus the fix commit ae31ac3 written in this run.
✅ Fixed before posting (1)
- Design D8 describes UI code not in this PR —
ae31ac3: D8 now says the quick filters, the_idsoverride and thesrc/icons.jsentry land in #1287.
Verified at 70d6f93: 16337d9's $unseen branch is on the live path and covered by testAnUncachedArchiveKeyIsListedNotDropped. The archive-only rule reads the same in proposal, design, tasks and spec.
Gates: CI quality / Hydra Gates ✅ on 70d6f93. CI on ae31ac3 was still running when this verdict was posted. ae31ac3 changes docblock and Markdown text only. phpcs on CmdbWorkbookReader.php is clean; PHPUnit was not run for it.
Jira context: WOO-587, CMDB import: archived and vanished applications on re-import.
…chive-reconciliation-3-ui feat(cmdb-import): offer the missing-records choice and an Archived quick filter
…chive-reconciliation-2-reconcile feat(cmdb-import): archive or soft-delete applications that left the export
Quality Report — ConductionNL/stackiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ❌ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| check-l10n-js | ✅ | ||||
| composer | ✅ | ✅ 133/133 | |||
| npm | ✅ | ✅ 807/807 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| lockfile sync | ✅ | ||||
| PHPUnit | ✅ 1274/1274 · 20 skipped | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development. |
||||
| Hydra gates | ❌ |
Quality workflow — 2026-10-15 08:31 UTC
Download the full PDF report from the workflow artifacts.
…on in their own method reconcileApplication() reached the phpmd cyclomatic complexity threshold of 10 once the archive-sheet branch landed. The choice between archiving and soft-deleting, with its archive reason, moves to reconcileTarget(); the writes and their error boundary stay where they were. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed-list requirement The indexPageKey watcher and onQuickFilterChange() keep the Archived-tab note in step with the active tab; gate 16 needs their @SPEC tag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…db-import-archive-reconciliation-1-reader Brings in #1290 (sidebar search sent as _search) and #1292 (contact person names). FacetedCatalogIndexView: development's remount key (listRemount) and sidebar search are kept; this branch's Archived-tab note, the tab tracking and the watcher that resets it on a remount are kept on top. Since #1290 the list sends the search term as _search, outside the filter, so the Archived tab's empty `_ids` no longer drops it. The note, the manifest _note, the docs, the task and the tests now say the tab ignores the GEMMA filters only; the note's string changed key in en/nl. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Quality Report — ConductionNL/stackiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| check-l10n-js | ✅ | ||||
| composer | ✅ | ✅ 133/133 | |||
| npm | ✅ | ✅ 807/807 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| lockfile sync | ✅ | ||||
| PHPUnit | ✅ 1281/1281 · 20 skipped | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development. |
||||
| Hydra gates | ✅ |
Quality workflow — 2026-10-15 09:09 UTC
Download the full PDF report from the workflow artifacts.
…' into feat/woo588-cmdb-import-mapping-view-1-endpoint Brings in #1285-#1287 (archive or soft-delete applications that left the export) and, through it, development with #1290 and #1292. Both sides are kept, with these choices: - topdesk-profile.json stays at version 1.0.0, the first version for the administrator, with #1285's description (the archive sheet and the missingRecords modes). - The sheet texts keep this branch's list-based functions and name the archive sheet there: sheetsReadText() and the "delete the sheets the import does not read" hint. The old "{first}"/"{second}" keys are replaced in en/nl, not kept next to the new ones; l10n/*.js regenerated. - The shipped-fallback warning moves into addRunWarnings(), where #1285 put the run's other warnings, ahead of them as before. - The section keeps the one-sentence option help and puts the missing-applications explanation in the info panel. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What
First of three stacked PRs that replace #1281 (archive or soft-delete applications that left a newer CMDB export). This slice reads the archive sheet; nothing acts on it yet.
cmdb-import-archive-reconciliation: ADDED REQ-CMDB-015..017, MODIFIED REQ-CMDB-012 and REQ-CMDB-014. The design records why object state is read per lens and from the trash (D9).module(0.3.9) andusage(1.5.7) opt in to archiving throughx-openregister-archive. Run Force Update after installing.missingRecordsmodes (keep,archive), readable before the packs are validated.topdesk-archived-applications.xlsx, a later export in which APPID 1234 moved to the archive sheet and APPID 2 is on no sheet.Stack
developmentJira: WOO-587 (sub-task of WOO-586). Plan: WOO-587 plan. Review and merge are tracked in WOO-590.
Review fixes (2026-10-12)
388181a316337d91uncachedRows), not dropped; the spec delta, design D3/D5 and task 2 say what follows (archive, never delete — #1286).11d9a23carchiveSheetName()null cases), each mutation-checked.aa3b6648,70d6f934Tests
🤖 Generated with Claude Code