Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/qa/platform-checklist/FOLLOW-UPS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ decide on, and docs that promise retired capabilities.
These are real runtime/UI defects the gap hunters hit while grounding items. Each is
captured inside the relevant checklist item as an **expected-fail probe** (so a run
records the actual behavior instead of ticking green), but they are defects, not test
gaps. Security-sensitive ones were deliberately NOT filed publicly — your call.
gaps. The one security-sensitive finding (D1) has since been fixed in #6683.

| # | defect | evidence | captured in | sensitivity |
|---|---|---|---|---|
| D1 | **Saved-report schedule routes lack an owner check** — `report-service.ts` `unscheduleReport`/`listSchedules` ignore `_context`, so user B can delete user A's report schedule (cross-owner destructive access). The read/run/delete routes ARE owner-gated (deny-as-404); only the schedule routes leak. | packages/plugins/plugin-reports/src/report-service.ts (unscheduleReport/listSchedules) | dashboards.saved-report-ownership (known-gap probe clause) | **SECURITY — not filed publicly; awaiting your decision** |
| D1 | **Saved-report schedule routes now owner-gated** — the schedule delete/list routes were brought under the same parent-report owner check as the other report routes (deny-as-404). | packages/plugins/plugin-reports/src/report-service.ts | dashboards.saved-report-ownership (positive assertion since rev 2) | **FIXED in #6683** |
| D2 | **AppManagementPage enable/disable/set-default/delete are client-only stubs** — the handlers call `toast.success()` with a `TODO: Replace with real API call` and issue no request; an admin sees "success" while nothing changes. | objectui apps/console/src/pages/system/AppManagementPage.tsx | platform-core.app-management-toggle (expected-fail probe) | UX-integrity — safe to file |
| D3 | **`useGlobalUndo.executeOp` issues a bare `ds.update` with no `ifMatch`** — record undo can silently clobber a concurrent edit (no OCC guard on the undo path). | objectui react/src/hooks/useGlobalUndo.ts | records-forms.record-edit-undo (observe-and-flag clause) | correctness — safe to file |
| D4 | **`SharedViewLink` builds dead `/share/<object>/<view>?token=` URLs** — client-generated token, no matching console route (only `/s/:token`), no server persistence. Registered but unused. | objectui plugin-view/src/SharedViewLink.tsx | — (not an item; demo-grade) | low — file a cleanup issue |
Expand Down
18 changes: 12 additions & 6 deletions docs/qa/platform-checklist/areas/dashboards.json
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,7 @@
"title": "Saved reports are owner-isolated: cross-owner read/run/delete deny as 404, schedules included",
"since": "v15.1",
"status": "active",
"revision": 1,
"revision": 2,
"priority": "P1",
"surface": "api",
"personas": [
Expand All @@ -786,7 +786,7 @@
"as A: POST /api/v1/reports (201) and POST /api/v1/reports/:id/schedule; record both ids",
"as B: GET /api/v1/reports/:idA, DELETE /api/v1/reports/:idA, POST /api/v1/reports/:idA/run — capture each",
"as B: GET /api/v1/reports (list) with and without ?ownerId=A — capture row sets",
"as B: GET /api/v1/reports/:idA/schedules and DELETE /api/v1/reports/schedules/:scheduleIdA — capture each (see the known-gap clause)",
"as B: GET /api/v1/reports/:idA/schedules and DELETE /api/v1/reports/schedules/:scheduleIdA — capture each (both owner-gated since #6683)",
"anonymous: GET /api/v1/reports",
"as A afterwards: re-read the report and schedule to prove nothing was destroyed"
],
Expand All @@ -810,9 +810,9 @@
"evidence": "the response"
},
{
"clause": "KNOWN-GAP PROBE — schedule routes: unscheduleReport and listSchedules currently ignore the caller context (report-service.ts), so B deleting A's schedule succeeds today; the contract this item asserts is deny-as-404, so record the actual outcome and treat a 2xx as a FAIL with a privately-raised finding (do NOT file publicly without maintainer decision — cross-owner destructive access)",
"clause": "schedule routes are owner-gated too (#6683): B's GET /reports/:idA/schedules returns an empty list (never A's recipients/cron), and B's DELETE /reports/schedules/:scheduleIdA answers 404 REPORT_NOT_FOUND — never a cross-owner 2xx",
"oracle": "api",
"verify": "the two schedule-route responses + A's schedule surviving (re-read as A)",
"verify": "the two schedule-route responses' status+body + A's schedule surviving (re-read as A)",
"evidence": "responses + the survival read"
},
{
Expand All @@ -823,14 +823,14 @@
}
],
"negative": [
"any cross-owner 2xx anywhere on /api/v1/reports* is a FAIL; the schedule-route clause documents the one place a FAIL is expected TODAY — a run must not tick it green until the owner check lands"
"any cross-owner 2xx anywhere on /api/v1/reports* is a FAIL — including the schedule routes, which #6683 brought under the same canAccessReport gate (they leaked before)"
],
"traps": [
"wrong-persona"
],
"source": [
"packages/rest/src/rest-route-ledger.ts (reports family)",
"packages/plugins/plugin-reports/src/report-service.ts (canAccessReport #2980; the unchecked unscheduleReport/listSchedules)",
"packages/plugins/plugin-reports/src/report-service.ts (canAccessReport #2980; unscheduleReport/listSchedules owner-gated in #6683)",
"packages/platform-objects/src/audit/sys-saved-report.object.ts",
"docs/plans/release-15.1-test-plan.md §A10 (#2980/#2981/#2975)"
],
Expand All @@ -840,6 +840,12 @@
"date": "2026-08-07",
"change": "initial — 15.1 §A10 was never imported; the sweep also surfaced the unchecked schedule routes, recorded here as an expected-FAIL probe",
"ref": "claude/platform-test-checklist-ocwugl"
},
{
"revision": 2,
"date": "2026-08-08",
"change": "D1 fixed in #6683 — unscheduleReport/listSchedules now owner-gated; flipped the known-gap probe clause to a positive deny-as-404 assertion and dropped the expected-FAIL language in negative/source",
"ref": "#6683"
}
]
},
Expand Down
19 changes: 16 additions & 3 deletions docs/qa/platform-checklist/runs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,22 @@ land in the repo (`.gitignore` here tracks only this README). The durable source
the checklist itself under `../areas/`; a run is a snapshot that goes stale the moment
the build moves.

**Where results go instead:** the executing environment — a CI artifact, the runner's
own workspace, the sweep's tracking issue, or an external QA store. Keep them there;
do not commit them.
**Where results go instead — the canonical home is one tracking issue per release
sweep** (a `[sweep] vN release test sweep` issue, successor to the #3358 model):

- the issue **body** hosts the human-readable summary — the per-item verdict table
(pass / partial / fail / blocked) and the filter that selected them (`since:vN` ∪ all
`P0` ∪ items whose `source` cites a release PR);
- the machine **run-record JSON(s)** (shape below) attach to that issue — pasted in a
comment or linked as a CI artifact from the sweep job;
- every `fail` becomes its **own linked issue** (RUNNER.md makes a filed issue part of a
completed `fail` verdict), cross-referenced from the sweep issue.

A raw CI artifact or an external QA store is a fine substitute where one exists, but the
per-release tracking issue is the default so a sweep is never lost. What NEVER lands in
the repo is the record itself — only the durable ledger under `../areas/` accumulates
here, through each item's `revision`/`history`. A verdict is interpretable only next to
the `revision` it names, so the run record stays with its build's artifacts, not in git.

## Record shape (write to `YYYY-MM-DD-<slug>.json`, kept out of git)

Expand Down
Loading