@dailephd/my-frontend-observer 0.9.1 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +490 -471
- package/LICENSE +21 -21
- package/README.md +375 -357
- package/dist/application/projectCheckService.d.ts +6 -0
- package/dist/application/projectCheckService.js +8 -1
- package/dist/application/projectCheckService.js.map +1 -1
- package/dist/application/projectWorkflowService.d.ts +7 -2
- package/dist/application/projectWorkflowService.js +10 -3
- package/dist/application/projectWorkflowService.js.map +1 -1
- package/dist/application/visualChangeAgentHandoffService.d.ts +28 -0
- package/dist/application/visualChangeAgentHandoffService.js +111 -0
- package/dist/application/visualChangeAgentHandoffService.js.map +1 -0
- package/dist/application/visualChangeProjectWorkflowService.d.ts +95 -0
- package/dist/application/visualChangeProjectWorkflowService.js +376 -0
- package/dist/application/visualChangeProjectWorkflowService.js.map +1 -0
- package/dist/application/visualChangeReviewService.d.ts +50 -0
- package/dist/application/visualChangeReviewService.js +69 -0
- package/dist/application/visualChangeReviewService.js.map +1 -0
- package/dist/application/visualChangeWorkflowPersistenceService.d.ts +26 -0
- package/dist/application/visualChangeWorkflowPersistenceService.js +15 -0
- package/dist/application/visualChangeWorkflowPersistenceService.js.map +1 -0
- package/dist/artifacts/visualChangeWorkflowArtifactReader.d.ts +9 -0
- package/dist/artifacts/visualChangeWorkflowArtifactReader.js +47 -0
- package/dist/artifacts/visualChangeWorkflowArtifactReader.js.map +1 -0
- package/dist/artifacts/visualChangeWorkflowArtifactWriter.d.ts +20 -0
- package/dist/artifacts/visualChangeWorkflowArtifactWriter.js +41 -0
- package/dist/artifacts/visualChangeWorkflowArtifactWriter.js.map +1 -0
- package/dist/cli.js +9 -7
- package/dist/cli.js.map +1 -1
- package/dist/domain/visualChangeAgentHandoff.d.ts +82 -0
- package/dist/domain/visualChangeAgentHandoff.js +80 -0
- package/dist/domain/visualChangeAgentHandoff.js.map +1 -0
- package/dist/domain/visualChangeAgentHandoffSerialization.d.ts +2 -0
- package/dist/domain/visualChangeAgentHandoffSerialization.js +11 -0
- package/dist/domain/visualChangeAgentHandoffSerialization.js.map +1 -0
- package/dist/domain/visualChangeCycle.d.ts +8 -0
- package/dist/domain/visualChangeCycle.js +7 -0
- package/dist/domain/visualChangeCycle.js.map +1 -0
- package/dist/domain/visualChangeWorkflow.d.ts +125 -0
- package/dist/domain/visualChangeWorkflow.js +109 -0
- package/dist/domain/visualChangeWorkflow.js.map +1 -0
- package/dist/domain/visualChangeWorkflowIdentity.d.ts +5 -0
- package/dist/domain/visualChangeWorkflowIdentity.js +24 -0
- package/dist/domain/visualChangeWorkflowIdentity.js.map +1 -0
- package/dist/index.d.ts +21 -1
- package/dist/index.js +12 -1
- package/dist/index.js.map +1 -1
- package/dist/projectWorkflow/projectPaths.d.ts +3 -0
- package/dist/projectWorkflow/projectPaths.js +7 -0
- package/dist/projectWorkflow/projectPaths.js.map +1 -1
- package/dist/viewer/assets/index-DglJ6f28.css +1 -0
- package/dist/viewer/assets/index-DsODREY5.js +9 -0
- package/dist/viewer/index.html +15 -15
- package/dist/viewer/sw.js +1 -1
- package/dist/viewerServer/evidence/classify.d.ts +3 -1
- package/dist/viewerServer/evidence/classify.js +10 -0
- package/dist/viewerServer/evidence/classify.js.map +1 -1
- package/dist/viewerServer/evidence/handles.js +1 -0
- package/dist/viewerServer/evidence/handles.js.map +1 -1
- package/dist/viewerServer/evidence/projection.d.ts +5 -0
- package/dist/viewerServer/evidence/projection.js +19 -0
- package/dist/viewerServer/evidence/projection.js.map +1 -1
- package/dist/viewerServer/evidence/visualChangeWorkflowView.d.ts +31 -0
- package/dist/viewerServer/evidence/visualChangeWorkflowView.js +36 -0
- package/dist/viewerServer/evidence/visualChangeWorkflowView.js.map +1 -0
- package/dist/viewerServer/httpServer.js +323 -1
- package/dist/viewerServer/httpServer.js.map +1 -1
- package/dist/viewerServer/referenceApproval.d.ts +22 -0
- package/dist/viewerServer/referenceApproval.js +42 -0
- package/dist/viewerServer/referenceApproval.js.map +1 -0
- package/dist/viewerServer/referenceVisualChangeAuthoring.d.ts +28 -0
- package/dist/viewerServer/referenceVisualChangeAuthoring.js +134 -0
- package/dist/viewerServer/referenceVisualChangeAuthoring.js.map +1 -0
- package/dist/viewerServer/runtimeVisualChangeAuthoring.d.ts +33 -0
- package/dist/viewerServer/runtimeVisualChangeAuthoring.js +81 -0
- package/dist/viewerServer/runtimeVisualChangeAuthoring.js.map +1 -0
- package/dist/viewerServer/visualChangeAuthoring.d.ts +46 -0
- package/dist/viewerServer/visualChangeAuthoring.js +63 -0
- package/dist/viewerServer/visualChangeAuthoring.js.map +1 -0
- package/dist/viewerServer/visualChangeHandoff.d.ts +23 -0
- package/dist/viewerServer/visualChangeHandoff.js +31 -0
- package/dist/viewerServer/visualChangeHandoff.js.map +1 -0
- package/dist/viewerServer/visualChangeReview.d.ts +30 -0
- package/dist/viewerServer/visualChangeReview.js +46 -0
- package/dist/viewerServer/visualChangeReview.js.map +1 -0
- package/docs/ARCHITECTURE.md +1394 -1373
- package/docs/CI_CD.md +349 -327
- package/docs/COMMANDS.md +1035 -1012
- package/docs/CONTRACTS.md +1971 -1926
- package/docs/CURRENT_STATE.md +1277 -1238
- package/docs/DEVELOPMENT.md +240 -237
- package/docs/DOCUMENTATION_PRESERVATION_POLICY.md +50 -50
- package/docs/PROJECT_DESCRIPTION.md +2248 -2224
- package/docs/PROJECT_MILESTONES.md +2681 -2558
- package/docs/PROJECT_OVERVIEW.md +200 -191
- package/docs/QUICKSTART.md +100 -96
- package/docs/RELEASE.md +37 -33
- package/docs/ROADMAP.md +1105 -1033
- package/docs/SECURITY.md +297 -275
- package/docs/WORKFLOWS.md +806 -770
- package/docs/plans/v0.10-implementation-plan.md +1509 -0
- package/docs/plans/v0.8-implementation-plan.md +655 -655
- package/docs/plans/v0.8.1-cli-usability-patch-plan.md +505 -505
- package/docs/plans/v0.9-implementation-plan.md +1529 -1529
- package/docs/plans/v0.9.1-implementation-plan.md +468 -468
- package/docs/reports/v0.10-batch1-visual-change-workflow-foundation.md +102 -0
- package/docs/reports/v0.10-batch2-project-composition-check-recording.md +103 -0
- package/docs/reports/v0.10-batch3-viewer-visual-change-workspace.md +93 -0
- package/docs/reports/v0.10-batch4-actual-frontend-entry.md +59 -0
- package/docs/reports/v0.10-batch5-reference-driven-entry.md +238 -0
- package/docs/reports/v0.10-batch6-coding-agent-handoff.md +85 -0
- package/docs/reports/v0.10-batch7-correction-review-acceptance.md +145 -0
- package/docs/reports/v0.10-batch8-integrated-acceptance.md +109 -0
- package/docs/reports/v0.10-implementation-completeness-documentation-reconciliation.md +344 -0
- package/docs/reports/v0.10-pre-release-readiness.md +120 -0
- package/docs/reports/v0.10-release-preparation.md +70 -0
- package/docs/reports/v0.10.1-project-check-baseline-context-implementation.md +86 -0
- package/docs/reports/v0.7-bounded-fidelity-context-prompt7.md +243 -243
- package/docs/reports/v0.7-implementation-completeness-documentation-reconciliation.md +497 -497
- package/docs/reports/v0.7-pre-release-readiness.md +337 -337
- package/docs/reports/v0.7-reference-binding-prompt5.md +223 -223
- package/docs/reports/v0.7-reference-compatibility-prompt4.md +234 -234
- package/docs/reports/v0.7-reference-correction-workflow-prompt8.md +222 -222
- package/docs/reports/v0.7-reference-fidelity-prompt6.md +216 -216
- package/docs/reports/v0.7-reference-foundation-prompt1.md +151 -151
- package/docs/reports/v0.7-reference-regions-prompt2.md +195 -195
- package/docs/reports/v0.7-reference-requirements-prompt3.md +217 -217
- package/docs/reports/v0.7-release-prep.md +423 -423
- package/docs/reports/v0.8-binding-fidelity-interaction-batch6.md +279 -279
- package/docs/reports/v0.8-bounded-context-correlation-batch7.md +233 -233
- package/docs/reports/v0.8-comparison-contract-inspection-batch4.md +279 -279
- package/docs/reports/v0.8-evidence-index-readers-batch2.md +247 -247
- package/docs/reports/v0.8-implementation-completeness-documentation-reconciliation.md +741 -741
- package/docs/reports/v0.8-integrated-viewer-acceptance-batch8.md +128 -128
- package/docs/reports/v0.8-observation-svg-inspection-batch3.md +223 -223
- package/docs/reports/v0.8-prerelease-readiness-cross-platform-security-code-rot.md +687 -687
- package/docs/reports/v0.8-reference-candidate-inspection-batch5.md +232 -232
- package/docs/reports/v0.8-viewer-runtime-pwa-batch1.md +278 -278
- package/docs/reports/v0.8.1-implementation-completeness-documentation-reconciliation.md +114 -114
- package/docs/reports/v0.8.1-prerelease-readiness-cross-platform-security-code-rot.md +170 -170
- package/docs/reports/v0.9-architecture-retrieval.md +14 -37
- package/docs/reports/v0.9-final-pre-release-readiness.md +209 -209
- package/docs/reports/v0.9-final-readiness-corrections.md +530 -530
- package/docs/reports/v0.9-pre-release-readiness.md +169 -169
- package/docs/reports/v0.9.1-batch1-pwa-hard-gate-isolation.md +359 -359
- package/docs/reports/v0.9.1-batch2-hard-gate-validation-integration.md +262 -262
- package/docs/reports/v0.9.1-pre-release-readiness.md +206 -206
- package/package.json +59 -59
- package/dist/viewer/assets/index-BN41MI7m.css +0 -1
- package/dist/viewer/assets/index-CkKXnlrI.js +0 -9
|
@@ -1,359 +1,359 @@
|
|
|
1
|
-
# v0.9.1 Batch 1 report: PWA hard-gate isolation
|
|
2
|
-
|
|
3
|
-
## 1. VERDICT
|
|
4
|
-
|
|
5
|
-
`PASS_V0_9_1_BATCH1_PWA_HARD_GATE_ISOLATED`
|
|
6
|
-
|
|
7
|
-
The PWA server-down hard gate is now a self-contained experiment. It passes when
|
|
8
|
-
selected alone and when the whole PWA file runs. No production file changed. No
|
|
9
|
-
product defect was found.
|
|
10
|
-
|
|
11
|
-
This report covers Batch 1 only. v0.9.1 is not released. Full repository
|
|
12
|
-
regression and cross-platform release readiness are not complete. Batch 2 owns
|
|
13
|
-
them.
|
|
14
|
-
|
|
15
|
-
## 2. Repository identity
|
|
16
|
-
|
|
17
|
-
1. Repository: `C:\Users\daile\Projects\my-frontend-observer`
|
|
18
|
-
2. Package: `@dailephd/my-frontend-observer`, version `0.9.0` (unchanged)
|
|
19
|
-
3. Branch: `feature/v0.9.1-pwa-hard-gate-isolation`, created from `master`
|
|
20
|
-
4. Starting HEAD: `985d0ce47a070fd9f84eae478f4098df00d57bd8` (merge of PR #15)
|
|
21
|
-
5. Released baseline: v0.9.0 at `eacd543f64d45d8b721e766173af87e6a878152c`
|
|
22
|
-
6. Runtime: Node `v24.20.0`, Playwright `1.62.1`, Vitest `4.1.11`, Windows 11
|
|
23
|
-
|
|
24
|
-
## 3. Planning authority
|
|
25
|
-
|
|
26
|
-
1. PR #15 (`docs: plan v0.9.1 PWA hard-gate isolation`) was merged into `master`
|
|
27
|
-
at 2026-09-21T15:11:04Z as `985d0ce`.
|
|
28
|
-
2. `docs/plans/v0.9.1-implementation-plan.md` is present on `master` and was
|
|
29
|
-
read in full. It is the frozen authority for this batch.
|
|
30
|
-
3. The planning doc changes in `ROADMAP.md`, `CURRENT_STATE.md`,
|
|
31
|
-
`DEVELOPMENT.md`, `CI_CD.md`, and `PROJECT_MILESTONES.md`, plus
|
|
32
|
-
`DOCUMENTATION_PRESERVATION_POLICY.md`, were read.
|
|
33
|
-
4. A first attempt at this prompt stopped with
|
|
34
|
-
`BLOCKED_V0_9_1_PLANNING_NOT_MERGED` because PR #15 was still open. No changes
|
|
35
|
-
were made in that attempt.
|
|
36
|
-
|
|
37
|
-
## 4. Original isolated failure reproduction
|
|
38
|
-
|
|
39
|
-
After `npm run build` (exit 0), this command ran against the unchanged test:
|
|
40
|
-
|
|
41
|
-
`npx vitest run --config vitest.browser.config.ts tests/browser/pwaHardening.test.ts -t "HARD GATE"`
|
|
42
|
-
|
|
43
|
-
1. Exit code: 1
|
|
44
|
-
2. Result: 1 failed, 6 skipped
|
|
45
|
-
3. Failure step: `await page.reload()` right after `server.close()`
|
|
46
|
-
4. Failure message: `page.reload: net::ERR_CONNECTION_REFUSED`
|
|
47
|
-
5. Shell reload failed: yes. The page was not served by a service worker.
|
|
48
|
-
6. Evidence-unavailable assertion reached: no. The test failed before it.
|
|
49
|
-
|
|
50
|
-
A diagnostic outside the repository (scratchpad only, not committed) repeated
|
|
51
|
-
the original steps and recorded service-worker and cache state at the moment the
|
|
52
|
-
original test shut the server down. The result was the same with the historical
|
|
53
|
-
fixed profile and with a fresh temporary profile:
|
|
54
|
-
|
|
55
|
-
1. Registration present: true
|
|
56
|
-
2. Worker state: `installing` = true, `active` = null
|
|
57
|
-
3. `navigator.serviceWorker.controller`: null
|
|
58
|
-
4. Cache Storage: one `workbox-precache-v2-<origin>/` cache with 5 entries, so
|
|
59
|
-
the precache was still being filled
|
|
60
|
-
5. Reload after close: `net::ERR_CONNECTION_REFUSED`
|
|
61
|
-
|
|
62
|
-
So service-worker control and a complete shell cache were both absent at the
|
|
63
|
-
shutdown boundary.
|
|
64
|
-
|
|
65
|
-
## 5. Root cause
|
|
66
|
-
|
|
67
|
-
Two faults combined.
|
|
68
|
-
|
|
69
|
-
1. The wait condition was wrong. The old test waited for
|
|
70
|
-
`(await navigator.serviceWorker.getRegistration())?.active !== undefined`.
|
|
71
|
-
`registration.active` is `null`, not `undefined`, while a worker is still
|
|
72
|
-
installing. The condition was therefore true as soon as any registration
|
|
73
|
-
existed. It never proved activation, control, or a filled precache.
|
|
74
|
-
2. The test relied on shared state. In normal file order, earlier tests in the
|
|
75
|
-
same describe had already used the same server origin and the same
|
|
76
|
-
persistent context. By the time the hard gate ran, the worker was active and
|
|
77
|
-
controlling that origin, so the weak wait did not matter.
|
|
78
|
-
|
|
79
|
-
When selected alone, the viewer server starts on a new random port (`port: 0`).
|
|
80
|
-
That is a new origin with no worker. The fixed profile's older registrations
|
|
81
|
-
belong to other origins and do not help. The page was still uncontrolled when
|
|
82
|
-
the server closed, so the reload went to the network and failed.
|
|
83
|
-
|
|
84
|
-
Classification stays `TEST_DEFECT`. `PRODUCT_CHANGE_REQUIRED = false`.
|
|
85
|
-
|
|
86
|
-
## 6. Previous shared-state topology
|
|
87
|
-
|
|
88
|
-
In the first describe block, `beforeAll` created and shared:
|
|
89
|
-
|
|
90
|
-
1. one evidence root from `mkdtemp`
|
|
91
|
-
2. one viewer server
|
|
92
|
-
3. one persistent Chromium context launched from the fixed profile
|
|
93
|
-
`.my-dev-kit-workflow/v0.8/batch-08/pwa-profile`
|
|
94
|
-
|
|
95
|
-
The hard gate was the fourth `it()` in that block. The three earlier tests
|
|
96
|
-
registered the worker, fetched the manifest, and warmed the cache on the same
|
|
97
|
-
origin. The fixed profile also kept service-worker and cache data between local
|
|
98
|
-
runs. That block's `afterAll` never closed its server. Only the hard gate's
|
|
99
|
-
mid-test `server.close()` shut it down.
|
|
100
|
-
|
|
101
|
-
## 7. New hard-gate resource ownership
|
|
102
|
-
|
|
103
|
-
The hard gate now lives in its own describe block:
|
|
104
|
-
`PWA server-down safety - self-contained hard-gate experiment`. It has no
|
|
105
|
-
`beforeAll` and uses no describe-level variables. Inside the test it creates and
|
|
106
|
-
owns:
|
|
107
|
-
|
|
108
|
-
1. an evidence root:
|
|
109
|
-
`mkdtemp(path.join(tmpdir(), 'my-frontend-observer-pwa-hard-gate-evidence-'))`
|
|
110
|
-
2. the fixture, through the existing helpers `writeManyRegionsOneTargetFixture`
|
|
111
|
-
and `persistAnnotationUnder`. These are now wrapped by a local
|
|
112
|
-
`writePwaFixture` helper that both describe blocks use. The real annotation
|
|
113
|
-
is kept so the annotation view and media routes are part of the cache
|
|
114
|
-
boundary.
|
|
115
|
-
3. a dedicated viewer server from `startViewer` on port 0
|
|
116
|
-
4. a persistent profile:
|
|
117
|
-
`mkdtemp(path.join(tmpdir(), 'my-frontend-observer-pwa-hard-gate-profile-'))`
|
|
118
|
-
5. a `BrowserContext` from
|
|
119
|
-
`chromium.launchPersistentContext(profileRoot, { headless: true })`
|
|
120
|
-
6. a fresh page
|
|
121
|
-
|
|
122
|
-
The test name is unchanged, so `-t "HARD GATE"` still selects it.
|
|
123
|
-
|
|
124
|
-
## 8. Fresh-profile design
|
|
125
|
-
|
|
126
|
-
1. The hard gate uses its own temporary profile and context. It never shares
|
|
127
|
-
one with any other test.
|
|
128
|
-
2. The first describe block, which keeps the focused registration, manifest,
|
|
129
|
-
and `/api/` cache tests, now creates its own temporary profile
|
|
130
|
-
(`my-frontend-observer-pwa-profile-*`) in `beforeAll` and deletes it in
|
|
131
|
-
`afterAll`.
|
|
132
|
-
3. That `afterAll` now also closes its viewer server, since the hard gate no
|
|
133
|
-
longer closes it.
|
|
134
|
-
4. The constant for `.my-dev-kit-workflow/v0.8/batch-08/pwa-profile` was
|
|
135
|
-
removed. No test uses that path any more. The historical directory itself was
|
|
136
|
-
left on disk untouched.
|
|
137
|
-
5. Profile and evidence removal uses
|
|
138
|
-
`rm(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })`,
|
|
139
|
-
because Chromium can briefly hold profile files after close on Windows.
|
|
140
|
-
|
|
141
|
-
## 9. Service-worker registration proof
|
|
142
|
-
|
|
143
|
-
After the first navigation, the test evaluates `navigator.serviceWorker.ready`
|
|
144
|
-
in the real page and requires:
|
|
145
|
-
|
|
146
|
-
1. `registration.active !== null` is true
|
|
147
|
-
2. `registration.scope` equals `<viewer origin>/`
|
|
148
|
-
|
|
149
|
-
`ready` only resolves when a worker is active, so there is no fixed sleep.
|
|
150
|
-
|
|
151
|
-
Observed: `serviceWorkerActive = true`.
|
|
152
|
-
|
|
153
|
-
## 10. Current-client control proof
|
|
154
|
-
|
|
155
|
-
Control is checked on its own, apart from activation:
|
|
156
|
-
|
|
157
|
-
1. The test reads `navigator.serviceWorker.controller !== null`.
|
|
158
|
-
2. If it is null, the page reloads while the server is still live.
|
|
159
|
-
3. The test then waits (bounded at 10 seconds) for
|
|
160
|
-
`navigator.serviceWorker.controller !== null`.
|
|
161
|
-
4. It requires `controller.scriptURL` to equal `<viewer origin>/sw.js`.
|
|
162
|
-
|
|
163
|
-
Observed: `controlledOnFirstLoad = true` and `clientControlled = true`. The
|
|
164
|
-
generated worker calls `self.skipWaiting()` and `clientsClaim()`, so once
|
|
165
|
-
activation finishes it takes control of the first page. The reload branch
|
|
166
|
-
stays in place for any platform where control arrives later.
|
|
167
|
-
|
|
168
|
-
## 11. App-shell cache proof
|
|
169
|
-
|
|
170
|
-
The test reads every Cache Storage entry from the page before shutdown. It
|
|
171
|
-
requires:
|
|
172
|
-
|
|
173
|
-
1. at least one cache whose name starts with `workbox-precache`
|
|
174
|
-
2. cached pathnames that include `/index.html`, `/registerSW.js`, and
|
|
175
|
-
`/manifest.webmanifest`
|
|
176
|
-
3. a cached `/index.html` (matched with `ignoreSearch`, because Workbox adds
|
|
177
|
-
`__WB_REVISION__`) whose body contains `<div id="root"></div>`
|
|
178
|
-
4. that the module entry script named inside that cached `index.html` matches
|
|
179
|
-
`/assets/*.js` and is itself in the cache
|
|
180
|
-
|
|
181
|
-
Why this is stable: the generated `sw.js` precaches `index.html`,
|
|
182
|
-
`registerSW.js`, and `manifest.webmanifest` under fixed names, and serves
|
|
183
|
-
navigations with `createHandlerBoundToURL("index.html")`. Only the Vite asset
|
|
184
|
-
names are hashed. The test never hardcodes a hash. It reads the entry script
|
|
185
|
-
name from the cached shell, so the check stays valid across rebuilds.
|
|
186
|
-
|
|
187
|
-
Observed: 7 cached entries (index, registerSW, manifest, two icons, one JS
|
|
188
|
-
bundle, one CSS bundle).
|
|
189
|
-
|
|
190
|
-
## 12. API cache-exclusion proof
|
|
191
|
-
|
|
192
|
-
From the controlled page, the hard gate first fetches `/api/index`, the
|
|
193
|
-
annotation view route, and the annotation overlay media route. It requires each
|
|
194
|
-
to return `200` with `cache-control: no-store`. It then counts cached requests
|
|
195
|
-
whose pathname starts with `/api/` across every cache.
|
|
196
|
-
|
|
197
|
-
Observed: `apiCacheEntryCount = 0`.
|
|
198
|
-
|
|
199
|
-
The separate focused test `never caches /api/ responses at the service-worker
|
|
200
|
-
cache-storage layer` was kept. The hard gate does not depend on it.
|
|
201
|
-
|
|
202
|
-
## 13. Live evidence proof
|
|
203
|
-
|
|
204
|
-
Before shutdown the test:
|
|
205
|
-
|
|
206
|
-
1. waits for the first `.evidence-list__item`
|
|
207
|
-
2. requires it to be visible
|
|
208
|
-
3. requires the `.evidence-list` text to contain the fixture candidate id
|
|
209
|
-
`many-regions-candidate` (read from the fixture helper's return value, not
|
|
210
|
-
hardcoded)
|
|
211
|
-
4. makes a direct Node-side `fetch` to `/api/index` (5 second timeout) and
|
|
212
|
-
requires HTTP 200 with a body that contains the same candidate id
|
|
213
|
-
|
|
214
|
-
## 14. Server-down proof
|
|
215
|
-
|
|
216
|
-
After `await server.close()` the test makes a direct Node-side `fetch` to the
|
|
217
|
-
same `/api/index` URL with a 5 second timeout. A Node request cannot be
|
|
218
|
-
intercepted by the page's service worker, so this tests the origin server alone.
|
|
219
|
-
The test requires the request to fail. If it gets any HTTP response, the test
|
|
220
|
-
fails with that status in the message.
|
|
221
|
-
|
|
222
|
-
The page was not navigated away. The controlled page and worker stayed in place.
|
|
223
|
-
|
|
224
|
-
## 15. Offline reload proof
|
|
225
|
-
|
|
226
|
-
Only after the checks in sections 9 to 14 pass does the test call
|
|
227
|
-
`page.reload()`. It then requires:
|
|
228
|
-
|
|
229
|
-
1. `h1, header` to render within 10 seconds, so the shell was served from the
|
|
230
|
-
precache
|
|
231
|
-
2. `navigator.serviceWorker.controller !== null` after the reload
|
|
232
|
-
|
|
233
|
-
## 16. Stale-evidence safety proof
|
|
234
|
-
|
|
235
|
-
After the offline reload the test requires:
|
|
236
|
-
|
|
237
|
-
1. `.evidence-list__error` to appear
|
|
238
|
-
2. body text to contain `Evidence index unavailable`
|
|
239
|
-
3. body text not to contain the previously visible candidate id
|
|
240
|
-
4. zero `.evidence-list__item` elements
|
|
241
|
-
|
|
242
|
-
These assertions are unchanged from the original or stricter. None were
|
|
243
|
-
weakened.
|
|
244
|
-
|
|
245
|
-
## 17. Cleanup proof
|
|
246
|
-
|
|
247
|
-
Cleanup runs in `finally` in a fixed order: close the context, close the server
|
|
248
|
-
unless it was already closed on purpose, remove the evidence root, remove the
|
|
249
|
-
profile. Each step runs even if an earlier step fails. Errors are collected.
|
|
250
|
-
|
|
251
|
-
1. If the experiment failed, cleanup errors are logged with `console.error` and
|
|
252
|
-
the original assertion error is what the test reports.
|
|
253
|
-
2. If the experiment passed but cleanup failed, the test fails with every
|
|
254
|
-
cleanup error listed.
|
|
255
|
-
3. After cleanup, the test requires that neither the evidence root nor the
|
|
256
|
-
profile directory exists.
|
|
257
|
-
|
|
258
|
-
A leak audit counted `my-frontend-observer-pwa-*` and
|
|
259
|
-
`my-frontend-observer-b8-pwa*` directories under the OS temp directory before
|
|
260
|
-
and after each run. All counts were 0. That covers the hard gate's profile and
|
|
261
|
-
evidence root and the first describe block's temporary profile and evidence
|
|
262
|
-
root.
|
|
263
|
-
|
|
264
|
-
Not executed: the failure-path cleanup (steps 1 and 2 above) was not triggered
|
|
265
|
-
on purpose. It is supported by code inspection only.
|
|
266
|
-
|
|
267
|
-
## 18. Isolated run #1
|
|
268
|
-
|
|
269
|
-
`npx vitest run --config vitest.browser.config.ts tests/browser/pwaHardening.test.ts -t "HARD GATE"`
|
|
270
|
-
|
|
271
|
-
PASS. 1 passed, 6 skipped. Exit 0. The run came right after a fresh
|
|
272
|
-
`npm run build`.
|
|
273
|
-
|
|
274
|
-
## 19. Isolated run #2
|
|
275
|
-
|
|
276
|
-
The same command was run again right away. PASS. 1 passed, 6 skipped. Exit 0.
|
|
277
|
-
|
|
278
|
-
The isolated command was run five times in total over the edits. The final two
|
|
279
|
-
runs, on the committed test content, both passed. One of them used
|
|
280
|
-
`--reporter=verbose` and printed the boundary line:
|
|
281
|
-
|
|
282
|
-
`HARD GATE pre-shutdown boundary {"serviceWorkerActive":true,"controlledOnFirstLoad":true,"clientControlled":true,"shellEntryScriptCached":true,"precacheEntryCount":7,"apiCacheEntryCount":0,"liveEvidenceVisible":true}`
|
|
283
|
-
|
|
284
|
-
That line was produced by the test itself after its assertions passed.
|
|
285
|
-
|
|
286
|
-
## 20. Full PWA file result
|
|
287
|
-
|
|
288
|
-
`npx vitest run --config vitest.browser.config.ts tests/browser/pwaHardening.test.ts`
|
|
289
|
-
|
|
290
|
-
PASS. 7 passed. Exit 0.
|
|
291
|
-
|
|
292
|
-
## 21. Typecheck/lint
|
|
293
|
-
|
|
294
|
-
1. `npm run typecheck`: PASS (exit 0).
|
|
295
|
-
2. `npm run lint`: PASS (exit 0). The first lint run failed with
|
|
296
|
-
`no-unsafe-finally` because the cleanup error was thrown inside `finally`.
|
|
297
|
-
The throw was moved after the `try/finally` block, then isolated runs 1 and 2,
|
|
298
|
-
the full file, typecheck, and lint were all run again and passed.
|
|
299
|
-
|
|
300
|
-
## 22. Production-code audit
|
|
301
|
-
|
|
302
|
-
`git diff --name-only` lists only `tests/browser/pwaHardening.test.ts` plus this
|
|
303
|
-
report. No file under `src/` or `viewer/` changed. `viewer/vite.config.ts`,
|
|
304
|
-
the service-worker caching policy, `package.json`, `package-lock.json`,
|
|
305
|
-
`.github/workflows/`, and `CHANGELOG.md` were not touched. `git diff --check`
|
|
306
|
-
passed.
|
|
307
|
-
|
|
308
|
-
## 23. Product-defect decision
|
|
309
|
-
|
|
310
|
-
1. `PRODUCT_DEFECT_DISCOVERED = false`
|
|
311
|
-
2. `PRODUCT_CHANGE_REQUIRED = false`
|
|
312
|
-
|
|
313
|
-
With every precondition proven, the released v0.9.0 viewer shell loads offline
|
|
314
|
-
from the precache. The evidence surface shows its explicit unavailable state.
|
|
315
|
-
Previously fetched evidence is not shown as current. The original failure came
|
|
316
|
-
only from the test's weak wait and its reliance on shared state.
|
|
317
|
-
|
|
318
|
-
## 24. Changed files
|
|
319
|
-
|
|
320
|
-
1. `tests/browser/pwaHardening.test.ts`
|
|
321
|
-
2. `docs/reports/v0.9.1-batch1-pwa-hard-gate-isolation.md`
|
|
322
|
-
|
|
323
|
-
Generated paths (all removed by the tests or kept outside the repository):
|
|
324
|
-
|
|
325
|
-
1. `%TEMP%\my-frontend-observer-pwa-hard-gate-evidence-*` (removed)
|
|
326
|
-
2. `%TEMP%\my-frontend-observer-pwa-hard-gate-profile-*` (removed)
|
|
327
|
-
3. `%TEMP%\my-frontend-observer-pwa-profile-*` and
|
|
328
|
-
`%TEMP%\my-frontend-observer-b8-pwa-*` (removed)
|
|
329
|
-
4. The diagnostic script and config in the Claude Code session scratchpad
|
|
330
|
-
(outside the repository, not committed)
|
|
331
|
-
5. `dist/` from `npm run build` (ignored build output)
|
|
332
|
-
|
|
333
|
-
These small temporary directories sit under the OS temp directory on `C:\`, as
|
|
334
|
-
the task's `mkdtemp(tmpdir())` design specifies. They are deleted at the end of
|
|
335
|
-
every passing run.
|
|
336
|
-
|
|
337
|
-
## 25. Batch 2 handoff
|
|
338
|
-
|
|
339
|
-
Batch 2 still owns:
|
|
340
|
-
|
|
341
|
-
1. the permanent `test:pwa-hard-gate` package script
|
|
342
|
-
2. wiring that command into the security or pre-release readiness path
|
|
343
|
-
3. moving `CURRENT_STATE.md`, `ROADMAP.md`, `DEVELOPMENT.md`, `CI_CD.md`, and
|
|
344
|
-
`CHANGELOG.md` from planned to implemented wording
|
|
345
|
-
4. the full unit, browser, security, build, docs, and pack validation
|
|
346
|
-
|
|
347
|
-
Notes for Batch 2:
|
|
348
|
-
|
|
349
|
-
1. The focused test `never caches /api/ responses` still uses the same weak
|
|
350
|
-
`?.active !== undefined` wait that the old hard gate used. It stays correct
|
|
351
|
-
in file order, because it runs after the registration test on the same
|
|
352
|
-
origin. If it is ever meant to stand alone, it should wait on
|
|
353
|
-
`navigator.serviceWorker.ready` instead. It was not changed here to keep
|
|
354
|
-
Batch 1 narrow.
|
|
355
|
-
2. The standalone display-mode test still writes a log to
|
|
356
|
-
`.my-dev-kit-workflow/v0.8/batch-08/logs`. That is a log file, not a browser
|
|
357
|
-
profile, and was not changed.
|
|
358
|
-
3. The historical directory `.my-dev-kit-workflow/v0.8/batch-08/pwa-profile`
|
|
359
|
-
still exists on disk. No test needs it now. It was not deleted.
|
|
1
|
+
# v0.9.1 Batch 1 report: PWA hard-gate isolation
|
|
2
|
+
|
|
3
|
+
## 1. VERDICT
|
|
4
|
+
|
|
5
|
+
`PASS_V0_9_1_BATCH1_PWA_HARD_GATE_ISOLATED`
|
|
6
|
+
|
|
7
|
+
The PWA server-down hard gate is now a self-contained experiment. It passes when
|
|
8
|
+
selected alone and when the whole PWA file runs. No production file changed. No
|
|
9
|
+
product defect was found.
|
|
10
|
+
|
|
11
|
+
This report covers Batch 1 only. v0.9.1 is not released. Full repository
|
|
12
|
+
regression and cross-platform release readiness are not complete. Batch 2 owns
|
|
13
|
+
them.
|
|
14
|
+
|
|
15
|
+
## 2. Repository identity
|
|
16
|
+
|
|
17
|
+
1. Repository: `C:\Users\daile\Projects\my-frontend-observer`
|
|
18
|
+
2. Package: `@dailephd/my-frontend-observer`, version `0.9.0` (unchanged)
|
|
19
|
+
3. Branch: `feature/v0.9.1-pwa-hard-gate-isolation`, created from `master`
|
|
20
|
+
4. Starting HEAD: `985d0ce47a070fd9f84eae478f4098df00d57bd8` (merge of PR #15)
|
|
21
|
+
5. Released baseline: v0.9.0 at `eacd543f64d45d8b721e766173af87e6a878152c`
|
|
22
|
+
6. Runtime: Node `v24.20.0`, Playwright `1.62.1`, Vitest `4.1.11`, Windows 11
|
|
23
|
+
|
|
24
|
+
## 3. Planning authority
|
|
25
|
+
|
|
26
|
+
1. PR #15 (`docs: plan v0.9.1 PWA hard-gate isolation`) was merged into `master`
|
|
27
|
+
at 2026-09-21T15:11:04Z as `985d0ce`.
|
|
28
|
+
2. `docs/plans/v0.9.1-implementation-plan.md` is present on `master` and was
|
|
29
|
+
read in full. It is the frozen authority for this batch.
|
|
30
|
+
3. The planning doc changes in `ROADMAP.md`, `CURRENT_STATE.md`,
|
|
31
|
+
`DEVELOPMENT.md`, `CI_CD.md`, and `PROJECT_MILESTONES.md`, plus
|
|
32
|
+
`DOCUMENTATION_PRESERVATION_POLICY.md`, were read.
|
|
33
|
+
4. A first attempt at this prompt stopped with
|
|
34
|
+
`BLOCKED_V0_9_1_PLANNING_NOT_MERGED` because PR #15 was still open. No changes
|
|
35
|
+
were made in that attempt.
|
|
36
|
+
|
|
37
|
+
## 4. Original isolated failure reproduction
|
|
38
|
+
|
|
39
|
+
After `npm run build` (exit 0), this command ran against the unchanged test:
|
|
40
|
+
|
|
41
|
+
`npx vitest run --config vitest.browser.config.ts tests/browser/pwaHardening.test.ts -t "HARD GATE"`
|
|
42
|
+
|
|
43
|
+
1. Exit code: 1
|
|
44
|
+
2. Result: 1 failed, 6 skipped
|
|
45
|
+
3. Failure step: `await page.reload()` right after `server.close()`
|
|
46
|
+
4. Failure message: `page.reload: net::ERR_CONNECTION_REFUSED`
|
|
47
|
+
5. Shell reload failed: yes. The page was not served by a service worker.
|
|
48
|
+
6. Evidence-unavailable assertion reached: no. The test failed before it.
|
|
49
|
+
|
|
50
|
+
A diagnostic outside the repository (scratchpad only, not committed) repeated
|
|
51
|
+
the original steps and recorded service-worker and cache state at the moment the
|
|
52
|
+
original test shut the server down. The result was the same with the historical
|
|
53
|
+
fixed profile and with a fresh temporary profile:
|
|
54
|
+
|
|
55
|
+
1. Registration present: true
|
|
56
|
+
2. Worker state: `installing` = true, `active` = null
|
|
57
|
+
3. `navigator.serviceWorker.controller`: null
|
|
58
|
+
4. Cache Storage: one `workbox-precache-v2-<origin>/` cache with 5 entries, so
|
|
59
|
+
the precache was still being filled
|
|
60
|
+
5. Reload after close: `net::ERR_CONNECTION_REFUSED`
|
|
61
|
+
|
|
62
|
+
So service-worker control and a complete shell cache were both absent at the
|
|
63
|
+
shutdown boundary.
|
|
64
|
+
|
|
65
|
+
## 5. Root cause
|
|
66
|
+
|
|
67
|
+
Two faults combined.
|
|
68
|
+
|
|
69
|
+
1. The wait condition was wrong. The old test waited for
|
|
70
|
+
`(await navigator.serviceWorker.getRegistration())?.active !== undefined`.
|
|
71
|
+
`registration.active` is `null`, not `undefined`, while a worker is still
|
|
72
|
+
installing. The condition was therefore true as soon as any registration
|
|
73
|
+
existed. It never proved activation, control, or a filled precache.
|
|
74
|
+
2. The test relied on shared state. In normal file order, earlier tests in the
|
|
75
|
+
same describe had already used the same server origin and the same
|
|
76
|
+
persistent context. By the time the hard gate ran, the worker was active and
|
|
77
|
+
controlling that origin, so the weak wait did not matter.
|
|
78
|
+
|
|
79
|
+
When selected alone, the viewer server starts on a new random port (`port: 0`).
|
|
80
|
+
That is a new origin with no worker. The fixed profile's older registrations
|
|
81
|
+
belong to other origins and do not help. The page was still uncontrolled when
|
|
82
|
+
the server closed, so the reload went to the network and failed.
|
|
83
|
+
|
|
84
|
+
Classification stays `TEST_DEFECT`. `PRODUCT_CHANGE_REQUIRED = false`.
|
|
85
|
+
|
|
86
|
+
## 6. Previous shared-state topology
|
|
87
|
+
|
|
88
|
+
In the first describe block, `beforeAll` created and shared:
|
|
89
|
+
|
|
90
|
+
1. one evidence root from `mkdtemp`
|
|
91
|
+
2. one viewer server
|
|
92
|
+
3. one persistent Chromium context launched from the fixed profile
|
|
93
|
+
`.my-dev-kit-workflow/v0.8/batch-08/pwa-profile`
|
|
94
|
+
|
|
95
|
+
The hard gate was the fourth `it()` in that block. The three earlier tests
|
|
96
|
+
registered the worker, fetched the manifest, and warmed the cache on the same
|
|
97
|
+
origin. The fixed profile also kept service-worker and cache data between local
|
|
98
|
+
runs. That block's `afterAll` never closed its server. Only the hard gate's
|
|
99
|
+
mid-test `server.close()` shut it down.
|
|
100
|
+
|
|
101
|
+
## 7. New hard-gate resource ownership
|
|
102
|
+
|
|
103
|
+
The hard gate now lives in its own describe block:
|
|
104
|
+
`PWA server-down safety - self-contained hard-gate experiment`. It has no
|
|
105
|
+
`beforeAll` and uses no describe-level variables. Inside the test it creates and
|
|
106
|
+
owns:
|
|
107
|
+
|
|
108
|
+
1. an evidence root:
|
|
109
|
+
`mkdtemp(path.join(tmpdir(), 'my-frontend-observer-pwa-hard-gate-evidence-'))`
|
|
110
|
+
2. the fixture, through the existing helpers `writeManyRegionsOneTargetFixture`
|
|
111
|
+
and `persistAnnotationUnder`. These are now wrapped by a local
|
|
112
|
+
`writePwaFixture` helper that both describe blocks use. The real annotation
|
|
113
|
+
is kept so the annotation view and media routes are part of the cache
|
|
114
|
+
boundary.
|
|
115
|
+
3. a dedicated viewer server from `startViewer` on port 0
|
|
116
|
+
4. a persistent profile:
|
|
117
|
+
`mkdtemp(path.join(tmpdir(), 'my-frontend-observer-pwa-hard-gate-profile-'))`
|
|
118
|
+
5. a `BrowserContext` from
|
|
119
|
+
`chromium.launchPersistentContext(profileRoot, { headless: true })`
|
|
120
|
+
6. a fresh page
|
|
121
|
+
|
|
122
|
+
The test name is unchanged, so `-t "HARD GATE"` still selects it.
|
|
123
|
+
|
|
124
|
+
## 8. Fresh-profile design
|
|
125
|
+
|
|
126
|
+
1. The hard gate uses its own temporary profile and context. It never shares
|
|
127
|
+
one with any other test.
|
|
128
|
+
2. The first describe block, which keeps the focused registration, manifest,
|
|
129
|
+
and `/api/` cache tests, now creates its own temporary profile
|
|
130
|
+
(`my-frontend-observer-pwa-profile-*`) in `beforeAll` and deletes it in
|
|
131
|
+
`afterAll`.
|
|
132
|
+
3. That `afterAll` now also closes its viewer server, since the hard gate no
|
|
133
|
+
longer closes it.
|
|
134
|
+
4. The constant for `.my-dev-kit-workflow/v0.8/batch-08/pwa-profile` was
|
|
135
|
+
removed. No test uses that path any more. The historical directory itself was
|
|
136
|
+
left on disk untouched.
|
|
137
|
+
5. Profile and evidence removal uses
|
|
138
|
+
`rm(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })`,
|
|
139
|
+
because Chromium can briefly hold profile files after close on Windows.
|
|
140
|
+
|
|
141
|
+
## 9. Service-worker registration proof
|
|
142
|
+
|
|
143
|
+
After the first navigation, the test evaluates `navigator.serviceWorker.ready`
|
|
144
|
+
in the real page and requires:
|
|
145
|
+
|
|
146
|
+
1. `registration.active !== null` is true
|
|
147
|
+
2. `registration.scope` equals `<viewer origin>/`
|
|
148
|
+
|
|
149
|
+
`ready` only resolves when a worker is active, so there is no fixed sleep.
|
|
150
|
+
|
|
151
|
+
Observed: `serviceWorkerActive = true`.
|
|
152
|
+
|
|
153
|
+
## 10. Current-client control proof
|
|
154
|
+
|
|
155
|
+
Control is checked on its own, apart from activation:
|
|
156
|
+
|
|
157
|
+
1. The test reads `navigator.serviceWorker.controller !== null`.
|
|
158
|
+
2. If it is null, the page reloads while the server is still live.
|
|
159
|
+
3. The test then waits (bounded at 10 seconds) for
|
|
160
|
+
`navigator.serviceWorker.controller !== null`.
|
|
161
|
+
4. It requires `controller.scriptURL` to equal `<viewer origin>/sw.js`.
|
|
162
|
+
|
|
163
|
+
Observed: `controlledOnFirstLoad = true` and `clientControlled = true`. The
|
|
164
|
+
generated worker calls `self.skipWaiting()` and `clientsClaim()`, so once
|
|
165
|
+
activation finishes it takes control of the first page. The reload branch
|
|
166
|
+
stays in place for any platform where control arrives later.
|
|
167
|
+
|
|
168
|
+
## 11. App-shell cache proof
|
|
169
|
+
|
|
170
|
+
The test reads every Cache Storage entry from the page before shutdown. It
|
|
171
|
+
requires:
|
|
172
|
+
|
|
173
|
+
1. at least one cache whose name starts with `workbox-precache`
|
|
174
|
+
2. cached pathnames that include `/index.html`, `/registerSW.js`, and
|
|
175
|
+
`/manifest.webmanifest`
|
|
176
|
+
3. a cached `/index.html` (matched with `ignoreSearch`, because Workbox adds
|
|
177
|
+
`__WB_REVISION__`) whose body contains `<div id="root"></div>`
|
|
178
|
+
4. that the module entry script named inside that cached `index.html` matches
|
|
179
|
+
`/assets/*.js` and is itself in the cache
|
|
180
|
+
|
|
181
|
+
Why this is stable: the generated `sw.js` precaches `index.html`,
|
|
182
|
+
`registerSW.js`, and `manifest.webmanifest` under fixed names, and serves
|
|
183
|
+
navigations with `createHandlerBoundToURL("index.html")`. Only the Vite asset
|
|
184
|
+
names are hashed. The test never hardcodes a hash. It reads the entry script
|
|
185
|
+
name from the cached shell, so the check stays valid across rebuilds.
|
|
186
|
+
|
|
187
|
+
Observed: 7 cached entries (index, registerSW, manifest, two icons, one JS
|
|
188
|
+
bundle, one CSS bundle).
|
|
189
|
+
|
|
190
|
+
## 12. API cache-exclusion proof
|
|
191
|
+
|
|
192
|
+
From the controlled page, the hard gate first fetches `/api/index`, the
|
|
193
|
+
annotation view route, and the annotation overlay media route. It requires each
|
|
194
|
+
to return `200` with `cache-control: no-store`. It then counts cached requests
|
|
195
|
+
whose pathname starts with `/api/` across every cache.
|
|
196
|
+
|
|
197
|
+
Observed: `apiCacheEntryCount = 0`.
|
|
198
|
+
|
|
199
|
+
The separate focused test `never caches /api/ responses at the service-worker
|
|
200
|
+
cache-storage layer` was kept. The hard gate does not depend on it.
|
|
201
|
+
|
|
202
|
+
## 13. Live evidence proof
|
|
203
|
+
|
|
204
|
+
Before shutdown the test:
|
|
205
|
+
|
|
206
|
+
1. waits for the first `.evidence-list__item`
|
|
207
|
+
2. requires it to be visible
|
|
208
|
+
3. requires the `.evidence-list` text to contain the fixture candidate id
|
|
209
|
+
`many-regions-candidate` (read from the fixture helper's return value, not
|
|
210
|
+
hardcoded)
|
|
211
|
+
4. makes a direct Node-side `fetch` to `/api/index` (5 second timeout) and
|
|
212
|
+
requires HTTP 200 with a body that contains the same candidate id
|
|
213
|
+
|
|
214
|
+
## 14. Server-down proof
|
|
215
|
+
|
|
216
|
+
After `await server.close()` the test makes a direct Node-side `fetch` to the
|
|
217
|
+
same `/api/index` URL with a 5 second timeout. A Node request cannot be
|
|
218
|
+
intercepted by the page's service worker, so this tests the origin server alone.
|
|
219
|
+
The test requires the request to fail. If it gets any HTTP response, the test
|
|
220
|
+
fails with that status in the message.
|
|
221
|
+
|
|
222
|
+
The page was not navigated away. The controlled page and worker stayed in place.
|
|
223
|
+
|
|
224
|
+
## 15. Offline reload proof
|
|
225
|
+
|
|
226
|
+
Only after the checks in sections 9 to 14 pass does the test call
|
|
227
|
+
`page.reload()`. It then requires:
|
|
228
|
+
|
|
229
|
+
1. `h1, header` to render within 10 seconds, so the shell was served from the
|
|
230
|
+
precache
|
|
231
|
+
2. `navigator.serviceWorker.controller !== null` after the reload
|
|
232
|
+
|
|
233
|
+
## 16. Stale-evidence safety proof
|
|
234
|
+
|
|
235
|
+
After the offline reload the test requires:
|
|
236
|
+
|
|
237
|
+
1. `.evidence-list__error` to appear
|
|
238
|
+
2. body text to contain `Evidence index unavailable`
|
|
239
|
+
3. body text not to contain the previously visible candidate id
|
|
240
|
+
4. zero `.evidence-list__item` elements
|
|
241
|
+
|
|
242
|
+
These assertions are unchanged from the original or stricter. None were
|
|
243
|
+
weakened.
|
|
244
|
+
|
|
245
|
+
## 17. Cleanup proof
|
|
246
|
+
|
|
247
|
+
Cleanup runs in `finally` in a fixed order: close the context, close the server
|
|
248
|
+
unless it was already closed on purpose, remove the evidence root, remove the
|
|
249
|
+
profile. Each step runs even if an earlier step fails. Errors are collected.
|
|
250
|
+
|
|
251
|
+
1. If the experiment failed, cleanup errors are logged with `console.error` and
|
|
252
|
+
the original assertion error is what the test reports.
|
|
253
|
+
2. If the experiment passed but cleanup failed, the test fails with every
|
|
254
|
+
cleanup error listed.
|
|
255
|
+
3. After cleanup, the test requires that neither the evidence root nor the
|
|
256
|
+
profile directory exists.
|
|
257
|
+
|
|
258
|
+
A leak audit counted `my-frontend-observer-pwa-*` and
|
|
259
|
+
`my-frontend-observer-b8-pwa*` directories under the OS temp directory before
|
|
260
|
+
and after each run. All counts were 0. That covers the hard gate's profile and
|
|
261
|
+
evidence root and the first describe block's temporary profile and evidence
|
|
262
|
+
root.
|
|
263
|
+
|
|
264
|
+
Not executed: the failure-path cleanup (steps 1 and 2 above) was not triggered
|
|
265
|
+
on purpose. It is supported by code inspection only.
|
|
266
|
+
|
|
267
|
+
## 18. Isolated run #1
|
|
268
|
+
|
|
269
|
+
`npx vitest run --config vitest.browser.config.ts tests/browser/pwaHardening.test.ts -t "HARD GATE"`
|
|
270
|
+
|
|
271
|
+
PASS. 1 passed, 6 skipped. Exit 0. The run came right after a fresh
|
|
272
|
+
`npm run build`.
|
|
273
|
+
|
|
274
|
+
## 19. Isolated run #2
|
|
275
|
+
|
|
276
|
+
The same command was run again right away. PASS. 1 passed, 6 skipped. Exit 0.
|
|
277
|
+
|
|
278
|
+
The isolated command was run five times in total over the edits. The final two
|
|
279
|
+
runs, on the committed test content, both passed. One of them used
|
|
280
|
+
`--reporter=verbose` and printed the boundary line:
|
|
281
|
+
|
|
282
|
+
`HARD GATE pre-shutdown boundary {"serviceWorkerActive":true,"controlledOnFirstLoad":true,"clientControlled":true,"shellEntryScriptCached":true,"precacheEntryCount":7,"apiCacheEntryCount":0,"liveEvidenceVisible":true}`
|
|
283
|
+
|
|
284
|
+
That line was produced by the test itself after its assertions passed.
|
|
285
|
+
|
|
286
|
+
## 20. Full PWA file result
|
|
287
|
+
|
|
288
|
+
`npx vitest run --config vitest.browser.config.ts tests/browser/pwaHardening.test.ts`
|
|
289
|
+
|
|
290
|
+
PASS. 7 passed. Exit 0.
|
|
291
|
+
|
|
292
|
+
## 21. Typecheck/lint
|
|
293
|
+
|
|
294
|
+
1. `npm run typecheck`: PASS (exit 0).
|
|
295
|
+
2. `npm run lint`: PASS (exit 0). The first lint run failed with
|
|
296
|
+
`no-unsafe-finally` because the cleanup error was thrown inside `finally`.
|
|
297
|
+
The throw was moved after the `try/finally` block, then isolated runs 1 and 2,
|
|
298
|
+
the full file, typecheck, and lint were all run again and passed.
|
|
299
|
+
|
|
300
|
+
## 22. Production-code audit
|
|
301
|
+
|
|
302
|
+
`git diff --name-only` lists only `tests/browser/pwaHardening.test.ts` plus this
|
|
303
|
+
report. No file under `src/` or `viewer/` changed. `viewer/vite.config.ts`,
|
|
304
|
+
the service-worker caching policy, `package.json`, `package-lock.json`,
|
|
305
|
+
`.github/workflows/`, and `CHANGELOG.md` were not touched. `git diff --check`
|
|
306
|
+
passed.
|
|
307
|
+
|
|
308
|
+
## 23. Product-defect decision
|
|
309
|
+
|
|
310
|
+
1. `PRODUCT_DEFECT_DISCOVERED = false`
|
|
311
|
+
2. `PRODUCT_CHANGE_REQUIRED = false`
|
|
312
|
+
|
|
313
|
+
With every precondition proven, the released v0.9.0 viewer shell loads offline
|
|
314
|
+
from the precache. The evidence surface shows its explicit unavailable state.
|
|
315
|
+
Previously fetched evidence is not shown as current. The original failure came
|
|
316
|
+
only from the test's weak wait and its reliance on shared state.
|
|
317
|
+
|
|
318
|
+
## 24. Changed files
|
|
319
|
+
|
|
320
|
+
1. `tests/browser/pwaHardening.test.ts`
|
|
321
|
+
2. `docs/reports/v0.9.1-batch1-pwa-hard-gate-isolation.md`
|
|
322
|
+
|
|
323
|
+
Generated paths (all removed by the tests or kept outside the repository):
|
|
324
|
+
|
|
325
|
+
1. `%TEMP%\my-frontend-observer-pwa-hard-gate-evidence-*` (removed)
|
|
326
|
+
2. `%TEMP%\my-frontend-observer-pwa-hard-gate-profile-*` (removed)
|
|
327
|
+
3. `%TEMP%\my-frontend-observer-pwa-profile-*` and
|
|
328
|
+
`%TEMP%\my-frontend-observer-b8-pwa-*` (removed)
|
|
329
|
+
4. The diagnostic script and config in the Claude Code session scratchpad
|
|
330
|
+
(outside the repository, not committed)
|
|
331
|
+
5. `dist/` from `npm run build` (ignored build output)
|
|
332
|
+
|
|
333
|
+
These small temporary directories sit under the OS temp directory on `C:\`, as
|
|
334
|
+
the task's `mkdtemp(tmpdir())` design specifies. They are deleted at the end of
|
|
335
|
+
every passing run.
|
|
336
|
+
|
|
337
|
+
## 25. Batch 2 handoff
|
|
338
|
+
|
|
339
|
+
Batch 2 still owns:
|
|
340
|
+
|
|
341
|
+
1. the permanent `test:pwa-hard-gate` package script
|
|
342
|
+
2. wiring that command into the security or pre-release readiness path
|
|
343
|
+
3. moving `CURRENT_STATE.md`, `ROADMAP.md`, `DEVELOPMENT.md`, `CI_CD.md`, and
|
|
344
|
+
`CHANGELOG.md` from planned to implemented wording
|
|
345
|
+
4. the full unit, browser, security, build, docs, and pack validation
|
|
346
|
+
|
|
347
|
+
Notes for Batch 2:
|
|
348
|
+
|
|
349
|
+
1. The focused test `never caches /api/ responses` still uses the same weak
|
|
350
|
+
`?.active !== undefined` wait that the old hard gate used. It stays correct
|
|
351
|
+
in file order, because it runs after the registration test on the same
|
|
352
|
+
origin. If it is ever meant to stand alone, it should wait on
|
|
353
|
+
`navigator.serviceWorker.ready` instead. It was not changed here to keep
|
|
354
|
+
Batch 1 narrow.
|
|
355
|
+
2. The standalone display-mode test still writes a log to
|
|
356
|
+
`.my-dev-kit-workflow/v0.8/batch-08/logs`. That is a log file, not a browser
|
|
357
|
+
profile, and was not changed.
|
|
358
|
+
3. The historical directory `.my-dev-kit-workflow/v0.8/batch-08/pwa-profile`
|
|
359
|
+
still exists on disk. No test needs it now. It was not deleted.
|