test(qa): qualify bounded revision timeline and notification volume

This commit is contained in:
cesnimda
2026-10-23 02:21:21 +02:00
parent 82be698dcf
commit 4b7727e22b
19 changed files with 9048 additions and 30 deletions
+5 -3
View File
@@ -23,11 +23,13 @@ $env:PLAYWRIGHT_STATIC_EXPORT='true'
$env:PLAYWRIGHT_PREBUILT_EXPORT='true'
$env:CV_FIDELITY_OUTPUT='E:\Storage\Projects\Active\Job tracker\.artifacts\phase9\fidelity'
$env:CV_SETTINGS_FIDELITY_OUTPUT='E:\Storage\Projects\Active\Job tracker\.artifacts\phase9\settings-fidelity'
$env:PLAYWRIGHT_JSON_OUTPUT_NAME='E:\Storage\Projects\Active\Job tracker\.artifacts\phase9\ordinary-accepted.json'
rtk proxy node node_modules/@playwright/test/cli.js test --retries=0 --reporter=list,json --output=../.artifacts/phase9/ordinary-accepted-results
$env:PLAYWRIGHT_JSON_OUTPUT_NAME='E:\Storage\Projects\Active\Job tracker\.artifacts\phase9\ordinary-shard-1.json'
rtk proxy node node_modules/@playwright/test/cli.js test --shard=1/4 --retries=0 --reporter=list,json --output=../.artifacts/phase9/ordinary-shard-1-results
```
Run separate browser configurations sequentially, each with its own JSON/output name: `playwright.cv-ai.config.ts`, `playwright.auth-recovery.config.ts`, `playwright.rate-limits.config.ts`, `playwright.ingress.config.ts`, `playwright.pdf.config.ts`, and `../docs/redesign/phase-5/p5-15/p5-15-01/diagnostics/playwright.config.ts`. Use the same CLI `--config=... --retries=0 --reporter=list,json`. The Phase9 lane uses `../docs/redesign/phase-9/p9-05/diagnostics/playwright.config.ts` (18 cases). Native PDF admission uses inert processes; it is distinct from native rendering fidelity.
Repeat the ordinary command serially with `--shard=2/4`, `3/4` and `4/4`, separate `ordinary-shard-N.json` and output names, and an unchanged source/export. Playwright keeps connected files together because fullyParallel is false. Native `test --list --reporter=json` records the complete discovery set without starting servers. Final reconciliation requires the exact discovered362 IDs to appear once across the four successful shards. Interrupted attempts are retained but excluded. This preserves completed result checkpoints rather than repeatedly losing a nearly hour-long run; assertions/retries/skips are unchanged.
Run separate browser configurations sequentially, each with its own JSON/output name: `playwright.cv-ai.config.ts`, `playwright.auth-recovery.config.ts`, `playwright.rate-limits.config.ts`, `playwright.ingress.config.ts`, `playwright.pdf.config.ts`, and `../docs/redesign/phase-5/p5-15/p5-15-01/diagnostics/playwright.config.ts`. Use the same CLI `--config=... --retries=0 --reporter=list,json`. The Phase9 lane uses `../docs/redesign/phase-9/p9-05/diagnostics/playwright.config.ts` (18 cases); the separate API-only volume lane uses `../docs/redesign/phase-9/p9-05/volume/playwright.config.ts` (3 cases). Native PDF admission uses inert processes; it is distinct from native rendering fidelity.
The configs start isolated synthetic local API/storage and a static frontend. Do not point these workflows at production. Successful native reads/writes, scoped intercepted failure responses and withheld model operations must remain distinguishable.
+40 -10
View File
@@ -21,7 +21,7 @@ def browser_cases(suites):
rows.extend(browser_cases(suite.get('suites', [])))
for spec in suite.get('specs', []):
for test in spec.get('tests', []):
rows.append(dict(file=spec.get('file'), title=spec['title'], status=test['status'],
rows.append(dict(id=spec.get('id'), project=test.get('projectName'), file=spec.get('file'), title=spec['title'], status=test['status'],
results=[dict(status=r['status'], retry=r.get('retry', 0), durationMs=r.get('duration', 0),
errors=r.get('errors', []), attachments=r.get('attachments', [])) for r in test.get('results', [])]))
return rows
@@ -42,20 +42,45 @@ assert browser_cases([{'specs': [{'title': 'self-check', 'tests': [{'status': 'e
if __name__ == '__main__':
report = dict(observedUtc=datetime.now(timezone.utc).isoformat(), lanes={}, rawFiles={})
manifest_path = HERE / 'p9-00/source-manifest.json'
manifest = json.loads(manifest_path.read_text(encoding='utf-8'))
changed = [name for name, expected in manifest.items() if not (ROOT / name).is_file() or digest(ROOT / name) != expected]
assert not changed, changed
report['sourceIdentity'] = dict(inputs=len(manifest), differences=changed, manifestSha256=digest(manifest_path),
qualification='Selected product/test, diagnostic, Release assembly and static-export bytes equal the frozen manifest. Documentation is outside this selected-source identity.')
report['documentOracle'] = dict(path='scripts/verify-cv-fidelity.py', sha256=digest(ROOT / 'scripts/verify-cv-fidelity.py'), qualification='Original unchanged oracle; native rendering and admission counts are separate.')
sources = dict(ordinary='ordinary-accepted', ai='ai', auth='auth', rate='rate', ingress='ingress', pdf='pdf', preferences='preferences', phase9='phase9-frozen')
report['supplementalSources'] = {str(path.relative_to(ROOT)): digest(path) for path in sorted((HERE / 'p9-05/volume').glob('*.ts'))}
discovered = json.loads((RAW / 'discovered.json').read_text(encoding='utf-8-sig'))
discovered_cases = browser_cases(discovered['suites'])
discovered_ids = {(case['id'], case['project']) for case in discovered_cases}
assert not discovered.get('errors') and len(discovered_cases) == len(discovered_ids) == 362
report['discovery'] = dict(cases=362, errors=discovered.get('errors', []), source='.artifacts/phase9/discovered.json',
qualification='List-only inventory; no tests executed. Its not-run statuses are not regression skip results.')
write_json(HERE / 'p9-00/discovered-cases.json', [{key: case[key] for key in ['id', 'project', 'file', 'title']} for case in discovered_cases])
sources = dict(ordinary=[f'ordinary-shard-{number}' for number in range(1, 5)], ai='ai', auth='auth', rate='rate', ingress='ingress', pdf='pdf', preferences='preferences', phase9='phase9-frozen', volume='volume-qualified')
for lane, source in sources.items():
file = RAW / f'{source}.json'
if not file.exists():
files = [RAW / f'{name}.json' for name in (source if isinstance(source, list) else [source])]
if not all(file.exists() for file in files):
continue
data = json.loads(file.read_text(encoding='utf-8-sig'))
cases = browser_cases(data['suites'])
report['lanes'][lane] = dict(stats=data['stats'], cases=len(cases),
datasets = [json.loads(file.read_text(encoding='utf-8-sig')) for file in files]
cases = [case for data in datasets for case in browser_cases(data['suites'])]
stats = dict(startTime=min(data['stats']['startTime'] for data in datasets),
**{key: sum(data['stats'][key] for data in datasets) for key in ['duration', 'expected', 'skipped', 'unexpected', 'flaky']})
if lane == 'ordinary':
assert len({(case['id'], case['project']) for case in cases}) == len(cases), 'Duplicate cases across native shards'
assert {(case['id'], case['project']) for case in cases} == discovered_ids, 'Shard union differs from full native discovery'
report['lanes'][lane] = dict(stats=stats, cases=len(cases),
retries=sum(max(len(c['results']) - 1, 0) for c in cases),
statuses={status: sum(c['status'] == status for c in cases) for status in ['expected', 'unexpected', 'flaky', 'skipped']},
fileFailures=data.get('errors', []))
folder = HERE / ('p9-01' if lane == 'ordinary' else 'p9-05' if lane == 'phase9' else 'p9-06')
fileFailures=[error for data in datasets for error in data.get('errors', [])],
sourceReports=[dict(file=str(file.relative_to(ROOT)), stats=data['stats']) for file, data in zip(files, datasets)])
if lane == 'ordinary':
report['lanes'][lane]['membershipMatchesDiscovery'] = True
folder = HERE / ('p9-01' if lane == 'ordinary' else 'p9-05' if lane in ['phase9', 'volume'] else 'p9-06')
folder.mkdir(parents=True, exist_ok=True)
if lane == 'volume':
observations = [dict(name=attachment['name'], observation=attached_json(attachment)) for case in cases for result in case['results'] for attachment in result['attachments'] if attachment['name'].startswith('phase9-volume-')]
write_json(HERE / 'p9-05/volume-results.json', observations)
if lane == 'phase9':
matrix, axe, screenshots = [], [], []
for case in cases:
@@ -84,6 +109,11 @@ if __name__ == '__main__':
for attachment in case['results'][0]['attachments']:
if attachment['name'].startswith('route-census-') and attachment['contentType'] == 'application/json':
write_json(HERE / 'p9-01' / (attachment['name'] + '.json'), attached_json(attachment))
elif attachment['name'] == 'actual-browser-zoom-observations':
observation = attached_json(attachment)
first = observation['records'][0]
assert first['language'] in ['en', 'nb'] and first['mode'] in ['light', 'dark']
write_json(HERE / 'p9-03' / f"zoom-{first['language']}-{first['mode']}.json", observation)
for case in cases:
for result in case['results']:
for attachment in result['attachments']:
@@ -105,7 +135,7 @@ if __name__ == '__main__':
if sidecar.exists():
report['lanes']['sidecar'] = ET.parse(sidecar).find('.//testsuite').attrib
report['failedOrSupersededAttempts'] = {}
for lane in ['phase9', 'phase9-successor', 'phase9-final', 'phase9-qualified', 'phase9-accepted', 'ordinary', 'ordinary-final', 'landing-red', 'landing-green', 'landing-qualified']:
for lane in ['phase9', 'phase9-successor', 'phase9-final', 'phase9-qualified', 'phase9-accepted', 'ordinary', 'ordinary-final', 'ordinary-accepted', 'ordinary-complete', 'volume', 'landing-red', 'landing-green', 'landing-qualified']:
file = RAW / f'{lane}.json'
if file.exists():
data = json.loads(file.read_text(encoding='utf-8-sig'))
+1 -1
View File
@@ -14,7 +14,7 @@ The exact group-level entry disposition is **56 PARTIAL / PRES-C11 DEFERRED**. O
| Frontend assertions | Report records 917 passed /104 suites, 0 failed/pending | Original wrapper was interrupted; its report is retained as `frontend-entry-reported.json`. A clean completed successor process is required for final acceptance. |
| Types / lint / build | PASS; static frontend export uses `http://localhost:5302/api` | Rebuilt again after the bounded locale repair; no deployment. |
| Sidecar | Initial 55 pass /1 fail; full corrected fixture successor 56/56 | Entry failure and red/green proof retained; see P9-T01. No model loading/inference acceptance. |
| Ordinary browser | Interrupted after 36 completed cases | Not a complete accepted run. Full uninterrupted successor required. |
| Ordinary browser | Interrupted after 36 completed cases | Not a complete accepted run. Complete successor evidence required; final protocol uses four native serial shards with exact full-discovery membership, zero skips/retries and four successful exits. |
| Provider availability | Docker Linux engine pipe missing; no `mariadbd`/`mysqld` runtime found | MariaDB runtime NOT TESTED. Do not substitute generated SQL or SQLite results. |
Commands are the existing pinned `.dotnet/dotnet.exe test ... --configuration Release --no-restore`, Jest `--runInBand --json`, `npm run typecheck`, `npm run lint`, `npm run build`, offline pytest with `SKIP_MODEL_LOAD=1`, and native Playwright with static/prebuilt export, isolated synthetic data root, `--retries=0`. Raw logs and result files remain under `.artifacts/phase9/`. The final portable [validation record](../validation.json) supersedes provisional run counts without erasing failed attempts.
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -2,12 +2,14 @@
The33-route matrix generates396 actual native screenshots:320/768/1440px × English/Bokmål × light/dark. [Route rows](route-results.json) retain applied document language/theme, root width, headings and native read status. [Screenshot index](screenshots.json) identifies full-page originals with SHA256; [contact-sheet index](evidence/index.json) identifies33 overview sheets. Each sheet contains12 first900px crops; it does not show every full-page state.
A separate read-only reviewer actually inspected all33 original route sheets (396 thumbnail crops). Full-size followups inspected Admin768NB/light, CV320NB/dark and job-detail320EN. The supposed job-detail clipping was a thumbnail artefact; deliberate horizontal tab/progression scrollers remain. No gross blank/overlap/unusable first-viewport layout was otherwise confirmed in those sheets. This is agent image inspection, not human acceptance or exhaustive glyph/contrast/readability review.
A separate read-only AI reviewer actually inspected all33 original route sheets (396 thumbnail crops). Full-size followups inspected Admin768NB/light, CV320NB/dark and job-detail320EN. The supposed job-detail clipping was a thumbnail artefact; deliberate horizontal tab/progression scrollers remain. No gross blank/overlap/unusable first-viewport layout was otherwise confirmed in those sheets. This is agent image inspection, not human acceptance or exhaustive glyph/contrast/readability review.
The reviewer confirmed overlapping public-page h2 text in full EN/light and NB/dark320px originals. P9-V01 now sets lineHeight1.4 on the existing four owners. An unchanged native text-rectangle assertion fails on the original and the insufficient1.2 attempt, then passes both language cases over16 width/theme vectors at320/390/768/1440. Independent inspection of final320EN/light and NB/dark full-page screenshot crops confirms the heading separation.
The initial Reminders and Profile matrix captures were loading-only and cannot qualify their body layouts. The matrix now waits for Reminders' settled region and the seeded Profile username. The reviewer inspected the eight corrected320px EN/NB light/dark originals: actual empty-state/forms were visible, with no clipping/overlap identified. Additional final-width inspection must be recorded when actually performed; do not infer it from screenshot generation alone.
Final frozen review actually re-inspected all33 regenerated sheets (396 first900px thumbnail crops), with no new gross overlap/clipping/blank render identified. Nine final full-page originals were additionally inspected: `/`, `/reminders` and `/profile`, each at320EN/light,768NB/light and1440NB/dark. Those root headings are separated and the two private routes show settled content at all three inspected widths. This supplements the earlier inspections; it does not certify every full-page original or all text readability.
Existing ordinary browser fixtures cover populated/empty/loading/error/long-translation states, dialog recovery, dense Jobs/Board, long Career/CV, saved-settings variants and the Dashboard editor. Their exact scope is limited to the executable scenarios; this396-route cross-product is not a cross-product of every state. Intentional grid/document scrollers preserve content; fixed CV paper geometry was not changed to fit the screen.
Real software keyboards, physical touch/drag, actual zoom/text enlargement, alternate browsers, screen readers and professional Bokmål review remain in the [human checklist](../p9-03/MANUAL_CHECKLIST.md). Automated focus/axe/overflow assertions and emulated touch are recorded separately.
+2
View File
@@ -6,6 +6,8 @@ The inherited 64 incomplete axe occurrences are deduplicated in [triage](inherit
The settled diagnostic suite explicitly inspects zero badges, keyboard-opened drawer focus, Escape return, closed drawer/descendant visibility and 24 Tab presses, plus Dashboard editor controls. Exact final scans and incompletes are in [settled-axe.json](settled-axe.json) when accepted; failed transition observations remain retained in raw traces. No NVDA, VoiceOver, human reader, physical device, forced-colour or actual browser-zoom acceptance is claimed. [Human checklist](MANUAL_CHECKLIST.md) gives exact steps, owners and closure records.
Final `phase9-frozen` evidence is16 scans with0violations and120 incomplete-node occurrences:100`bgOverlap` (contextual colour inspection still required),16`shortTextContent` (the verified invisible zero badge; not applicable in that state) and4`focusable-modal-open` (background root during the active modal, a state/tool limitation; actual reader/modal behaviour remains M09-01/02/05). Counts are repeated occurrences across states, not120 unique defects. The earlier failed NB observations sampled15 visible descendants after root hiding but before Slide exit; the successor polls the complete unchanged settled predicate and then verifies24Tab presses, with no retries/skips or product drawer change.
The dictionary [comparison](localisation.json) found 2,871 English and 2,871 Bokmål keys, no missing/extra keys and no placeholder mismatch. The 49 identical values are candidates reviewed as technical/proper/established terms, not automatic untranslated-copy defects. Dictionary parity alone does not certify grammar, dates, pluralisation or every rendered state.
P9-L01 fixes the AdminUsers vendor pagination controls by passing the installed MUI nbNO locale; English remains the default. Two parameterized Jest regressions and a visible native desktop pagination assertion cover the actual shared owner. The rendered route scan separately lists candidate exact-English strings; persisted `Created` event types in job/audit metadata remain a Low linguistic residual, not a missing dictionary key. Authored text and provider labels must not be indiscriminately translated. Natural professional Bokmål still needs M09-08 human review.
+5 -1
View File
@@ -6,10 +6,12 @@ The final separate Phase9 diagnostic lane completed **18/18**, zero failures/ski
Fresh real preview and native exported PDF bytes from the retained corpus passed **16/16 fixtures /36 pages** under the unchanged oracle: exact page count/membership, physical dimensions ±0.5mm, marker positions ±2mm, font sizes ±0.5pt and original RGB tolerance. Nine defaults, long EN/NB, 8mm Letter, 28mm roomy, grid/bubbles, long sidebar and unbreakable boundary fixtures are included. No renderer geometry or tolerance changed.
[Portable oracle results](evidence/fidelity-results.json) retain all measurements. All36 page thumbnails were actually inspected in [sheet1](evidence/pdf-sheet-1.png), [sheet2](evidence/pdf-sheet-2.png) and [sheet3](evidence/pdf-sheet-3.png). Inspection found no visible clipping/overlap/blank output in those thumbnails. Several last pages contain little content; that is recorded layout behavior, not missing text according to the marker oracle. Thumbnail inspection is not print/readability, photo/crop, every glyph or intended Linux-font acceptance. Full-size spot checks and the final whole-suite successor are recorded separately when completed.
[Portable oracle results](evidence/fidelity-results.json) retain all measurements. The final frozen ordinary run's native corpus producer passed; the unchanged oracle again passed16/16 over its fresh36 pages. All36 regenerated page thumbnails were actually inspected in [sheet1](evidence/pdf-sheet-1.png), [sheet2](evidence/pdf-sheet-2.png) and [sheet3](evidence/pdf-sheet-3.png), plus the full-size preview/PDF `unbreakable-and-boundary/comparison-4.png`. Inspection found no visible clipping/overlap/blank output in those views. Several last pages contain little content; that is recorded layout behavior, not missing text according to the marker oracle. Thumbnail/spot inspection is not print/readability, photo/crop, every glyph or intended Linux-font acceptance. Whole-suite completion remains a separate gate.
Existing browser fixtures also cover hidden preview mounting, image-layout readiness, dirty edits, stale CV revisions, preview transport recovery, canonical versus variant overrides, frozen submitted packages/archived binaries and missing optional sections. Their full-suite outcomes belong to final regression. Native PDF admission uses inert child processes and is not added to fidelity counts.
The separate Settings corpus passed the same unchanged oracle over **8 fixtures /24 pages**: EN/NB × light/dark, before and after actual document-control changes. All24 page thumbnails were inspected in [Settings sheet1](evidence/settings-pdf-sheet-1.png) and [sheet2](evidence/settings-pdf-sheet-2.png); a full-size light-EN before preview/PDF comparison was also inspected. No visible clipping/overlap/blank output was found in those views. [Measured results](evidence/settings-fidelity-results.json) retain the before/after page dimensions, marker/font/colour agreement. These are separate24 pages, not extra fixtures in the16/36 baseline corpus, and do not close human/Linux/print requirements.
## Sustained local workload
[Raw finite measurements](performance.json): **65.303s**, two paced readers, **240 successful reads**, 250 owned applications, eight Career roles with48 distinct long bullets, and20 native navigations across Dashboard/Table/Board/long CV. The same saved canonical profile and250-job membership remained intact afterward. CV readiness waits for settled pagination and verifies the final bullet's page membership and multiple rendered pages before recording timing.
@@ -19,3 +21,5 @@ API latency across these reads: **p50 23ms /p95 76ms /max115ms**. Native ready t
CDP CPU/task/heap/DOM metrics are retained for every navigation. They are observations, not leak diagnosis or attributed backend resource profiling. Two paced readers for a minute do not establish capacity/SLA, sustained drag frames, physical mobile performance, long-term memory stability, MariaDB/Linux behavior or real model inference throughput. No production load was generated.
Retained ordinary fixtures extend edge-case coverage to no-data,1,000-job Board membership/filtering, long timelines/profiles, revision conflicts, stale sessions, HTTP/network failures, missing/foreign references and recoverable drafts. Remaining worker durability, creation idempotency and high RD21 stale writes are explicit risks. No synthetic data was loaded into production.
A separately isolated [volume supplement](volume/README.md) adds3 passing native API cases:100 CV revisions plus additive restore101,100 long manual timeline messages with exact membership and foreign-owner denial, and100 cancellation notices with bounded listing/read/dismissal counts. Its source hashes and results are separate from the825-input ordinary-run snapshot. This establishes finite persisted membership/count/revision behavior, not large-history browser capacity.
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

@@ -1,5 +1,7 @@
[
{
"id": "ddd864604af8a6a0198f-360c04fa6be46bbcba6d",
"project": "chromium",
"file": "accessibility.spec.ts",
"title": "P9 settled badge drawer and editor triage en light",
"status": "expected",
@@ -21,6 +23,8 @@
]
},
{
"id": "ddd864604af8a6a0198f-d5c781053552377067ae",
"project": "chromium",
"file": "accessibility.spec.ts",
"title": "P9 settled badge drawer and editor triage en dark",
"status": "expected",
@@ -42,6 +46,8 @@
]
},
{
"id": "ddd864604af8a6a0198f-2e3bdd1a8283956a0e35",
"project": "chromium",
"file": "accessibility.spec.ts",
"title": "P9 settled badge drawer and editor triage nb light",
"status": "expected",
@@ -63,6 +69,8 @@
]
},
{
"id": "ddd864604af8a6a0198f-864eae11bccec12247a6",
"project": "chromium",
"file": "accessibility.spec.ts",
"title": "P9 settled badge drawer and editor triage nb dark",
"status": "expected",
@@ -84,6 +92,8 @@
]
},
{
"id": "1aed2e881af2e0d7a227-8579037087f487b9f8d8",
"project": "chromium",
"file": "data.spec.ts",
"title": "P9 characterize retained RD21 stale writes separately from CV revision safety",
"status": "expected",
@@ -105,6 +115,8 @@
]
},
{
"id": "7066e7f2e0ac7d296410-f9c3cfbba3d3cb3cd4f7",
"project": "chromium",
"file": "matrix.spec.ts",
"title": "P9 all 33 routes 320 en light",
"status": "expected",
@@ -291,6 +303,8 @@
]
},
{
"id": "7066e7f2e0ac7d296410-3a13c24159dbd785ebda",
"project": "chromium",
"file": "matrix.spec.ts",
"title": "P9 all 33 routes 320 en dark",
"status": "expected",
@@ -477,6 +491,8 @@
]
},
{
"id": "7066e7f2e0ac7d296410-6d49111c17a1dff08c8e",
"project": "chromium",
"file": "matrix.spec.ts",
"title": "P9 all 33 routes 320 nb light",
"status": "expected",
@@ -663,6 +679,8 @@
]
},
{
"id": "7066e7f2e0ac7d296410-5fd0b9872d4828bde53b",
"project": "chromium",
"file": "matrix.spec.ts",
"title": "P9 all 33 routes 320 nb dark",
"status": "expected",
@@ -849,6 +867,8 @@
]
},
{
"id": "7066e7f2e0ac7d296410-0baa4f01c2d84e97be11",
"project": "chromium",
"file": "matrix.spec.ts",
"title": "P9 all 33 routes 768 en light",
"status": "expected",
@@ -1035,6 +1055,8 @@
]
},
{
"id": "7066e7f2e0ac7d296410-f2247ee0ab8d5b198b4f",
"project": "chromium",
"file": "matrix.spec.ts",
"title": "P9 all 33 routes 768 en dark",
"status": "expected",
@@ -1221,6 +1243,8 @@
]
},
{
"id": "7066e7f2e0ac7d296410-f1032e36a96fbb1dcefa",
"project": "chromium",
"file": "matrix.spec.ts",
"title": "P9 all 33 routes 768 nb light",
"status": "expected",
@@ -1407,6 +1431,8 @@
]
},
{
"id": "7066e7f2e0ac7d296410-06956c48e2db0078d906",
"project": "chromium",
"file": "matrix.spec.ts",
"title": "P9 all 33 routes 768 nb dark",
"status": "expected",
@@ -1593,6 +1619,8 @@
]
},
{
"id": "7066e7f2e0ac7d296410-d810e2662b4adde1defd",
"project": "chromium",
"file": "matrix.spec.ts",
"title": "P9 all 33 routes 1440 en light",
"status": "expected",
@@ -1779,6 +1807,8 @@
]
},
{
"id": "7066e7f2e0ac7d296410-e5e4f4ee9e4f4fb18acd",
"project": "chromium",
"file": "matrix.spec.ts",
"title": "P9 all 33 routes 1440 en dark",
"status": "expected",
@@ -1965,6 +1995,8 @@
]
},
{
"id": "7066e7f2e0ac7d296410-d71823335925949fbcf8",
"project": "chromium",
"file": "matrix.spec.ts",
"title": "P9 all 33 routes 1440 nb light",
"status": "expected",
@@ -2151,6 +2183,8 @@
]
},
{
"id": "7066e7f2e0ac7d296410-cdfb026d92e493311855",
"project": "chromium",
"file": "matrix.spec.ts",
"title": "P9 all 33 routes 1440 nb dark",
"status": "expected",
@@ -2337,6 +2371,8 @@
]
},
{
"id": "dfb76b6411378a2f08ea-e69f55f4f639e0fe64e1",
"project": "chromium",
"file": "sustained.spec.ts",
"title": "P9 bounded sustained reads with 250 jobs and long CV while using native workspaces",
"status": "expected",
@@ -0,0 +1,71 @@
[
{
"id": "30685a6c0e3dbeec97fc-41d7ff2d700986d6de49",
"project": "chromium",
"file": "volume.spec.ts",
"title": "P9 volume: 100 CV revisions retain exact history, stale rejection and additive restore",
"status": "expected",
"results": [
{
"status": "passed",
"retry": 0,
"durationMs": 6435,
"errors": [],
"attachments": [
{
"name": "phase9-volume-cv",
"contentType": "application/json",
"bytes": 183,
"sha256": "06ada1842930140b730f802849b79a75f3cab67cbc7b42f514224940b687fb53"
}
]
}
]
},
{
"id": "30685a6c0e3dbeec97fc-38eecc55ba78ad0862e8",
"project": "chromium",
"file": "volume.spec.ts",
"title": "P9 volume: 100 manual timeline messages retain exact membership and foreign-owner denial",
"status": "expected",
"results": [
{
"status": "passed",
"retry": 0,
"durationMs": 5520,
"errors": [],
"attachments": [
{
"name": "phase9-volume-timeline",
"contentType": "application/json",
"bytes": 173,
"sha256": "c6cbc352b555858d0b7b7133e442381b417be48fe54c204b2f106afe6b007f85"
}
]
}
]
},
{
"id": "30685a6c0e3dbeec97fc-b9a8d42713cfaef64e77",
"project": "chromium",
"file": "volume.spec.ts",
"title": "P9 volume: 100 native cancellation notices preserve bounded lists, unread counts and ownership",
"status": "expected",
"results": [
{
"status": "passed",
"retry": 0,
"durationMs": 51407,
"errors": [],
"attachments": [
{
"name": "phase9-volume-notifications",
"contentType": "application/json",
"bytes": 253,
"sha256": "66cebc8fb3824f74e7452f25482357f27695237b9f0f7ec63f8d090986aa05ba"
}
]
}
]
}
]
@@ -0,0 +1,33 @@
[
{
"name": "phase9-volume-cv",
"observation": {
"savedRevisions": 100,
"finalVersions": 101,
"staleStatus": 409,
"canonicalUnchanged": true,
"limits": "Native isolated SQLite API; no100-row history UI or MariaDB/long-term capacity claim."
}
},
{
"name": "phase9-volume-timeline",
"observation": {
"messages": 100,
"exactMembership": true,
"foreignRead": 404,
"limits": "Native persisted manual correspondence; no external mail delivery or100-message browser rendering claim."
}
},
{
"name": "phase9-volume-notifications",
"observation": {
"notices": 100,
"defaultList": 25,
"maxList": 100,
"repeatedReadCount": 99,
"afterUnreadDismiss": 98,
"foreignMutation": 404,
"limits": "Existing guarded inert operation seed + native cancel/read/dismiss. No enabled worker/provider/model or100-item bell UI claim."
}
}
]
@@ -0,0 +1,15 @@
# Bounded volume supplement
**3/3 passed**, zero failures/skips/flaky/retries, `volume-qualified`, exit0 at20:46:29UTC on22October. Full TypeScript checking also passed. The initial runtime pass used an incorrect type-only import; that import was corrected and the complete three-case successor passed on the final source. Neither run changes product code.
| Scenario | Actual native result | Limit |
|---|---|---|
| 100 CV revisions | Exact descending1–100 membership; stale99 save rejected409 without appending history; restoring revision2 appends101 with its original headline; canonical Career readback unchanged | Isolated SQLite API. No100-row history UI, MariaDB, long-term growth or real model claim |
| 100 long manual correspondence entries | All100 persisted IDs returned exactly; long content retained; second owner's job read404 | Manual timeline records, no external email delivery or100-message browser usability acceptance |
| 100 notifications | Existing guarded one-insert inert-operation helper + actual native cancellation generates100 distinct notices; default25/max100 list, limit101 rejected400; repeated Read counts99 once, unread Dismiss counts98, foreign mutation404 | No enabled-worker/provider/inference proof or100-item bell UI acceptance |
The separate config reuses the existing API fixture on **localhost5304**, with a unique `jobtracker-e2e-*` direct child of system temporary storage and reuse disabled. It does not share the ordinary browser database or start another frontend. The existing helper verifies actual temp path, schema and exact owner before permitting one inert operation insert; all notification creation/read/dismissal goes through native product APIs. No production data or new seeding framework is used.
[Portable observations](../volume-results.json), [cases](../volume-cases.json) and [validation](../../validation.json) retain outcomes. The two source hashes are separately recorded as `supplementalSources`; the original825-input browser/product manifest remains unchanged. This supplement is additional to the18-case diagnostics and the362-case ordinary suite, never a replacement for either.
From `job-tracker-ui`, use the existing pinned DOTNET environment and run `rtk proxy node node_modules/@playwright/test/cli.js test --config=../docs/redesign/phase-9/p9-05/volume/playwright.config.ts --retries=0 --reporter=list,json`, with its own `PLAYWRIGHT_JSON_OUTPUT_NAME`.
@@ -0,0 +1,12 @@
import { defineConfig } from '../../../../../job-tracker-ui/node_modules/@playwright/test';
import base from '../../../../../job-tracker-ui/playwright.config';
import os from 'node:os';
import path from 'node:path';
const server = Array.isArray(base.webServer) ? base.webServer[0] : base.webServer!;
const dataRoot = path.join(os.tmpdir(), `jobtracker-e2e-p9-volume-${process.pid}-${Date.now().toString(36)}`);
export default defineConfig({ ...base, testDir: __dirname, testMatch: '*.spec.ts', retries: 0,
metadata: { isolatedDataRoot: dataRoot },
webServer: { ...server, cwd: path.resolve(__dirname, '../../../../../job-tracker-ui'),
command: server.command.replaceAll('5302', '5304'), url: 'http://localhost:5304/health',
reuseExistingServer: false, env: { ...server.env, Data__Root: dataRoot } } });
@@ -0,0 +1,99 @@
import { test, expect, type APIRequestContext, type PlaywrightWorkerArgs } from '../../../../../job-tracker-ui/node_modules/@playwright/test';
import { spawnSync } from 'node:child_process';
import path from 'node:path';
const base = 'http://localhost:5304/api';
const password = 'E2ePassword123!';
async function login(api: APIRequestContext, email: string) {
expect((await api.post(`${base}/auth/login`, { data: { email, password } })).status()).toBe(200);
const me = await (await api.get(`${base}/auth/me`)).json();
return { 'X-CSRF-TOKEN': (await api.storageState()).cookies.find(cookie => cookie.name === 'XSRF-TOKEN')!.value, 'X-Session-Context': me.sessionContext as string };
}
async function owner(playwright: PlaywrightWorkerArgs['playwright']) {
const admin = await playwright.request.newContext();
const api = await playwright.request.newContext();
try {
const headers = await login(admin, 'e2e@example.test');
const email = `p9-volume-${Date.now()}@example.test`;
const response = await admin.post(`${base}/users`, { headers, data: { email, password, roles: [] } });
expect(response.status()).toBe(200);
return { api, email, id: (await response.json()).id as string, headers: await login(api, email) };
} finally { await admin.dispose(); }
}
test('P9 volume: 100 CV revisions retain exact history, stale rejection and additive restore', async ({ playwright }, info) => {
test.setTimeout(180000);
const account = await owner(playwright);
try {
const { api, headers } = account;
expect((await api.put(`${base}/career/profile`, { headers, data: { profile: { version: '1', contact: { fullName: 'Volume fixture' }, summary: ['Canonical unchanged'] } } })).status()).toBe(200);
const source = await (await api.get(`${base}/career/profile`)).json();
const created = await api.post(`${base}/cv/variants`, { headers, data: { name: 'Volume CV', settings: { themeId: 'minimal' } } });
expect(created.status()).toBe(200); let cv = await created.json(); const id = cv.id;
for (let version = 2; version <= 100; version++) {
const response = await api.put(`${base}/cv/variants/${id}`, { headers, data: { expectedVersion: cv.version, name: cv.name, settings: { ...cv.settings, headline: `Revision ${version}` }, source: 'manual' } });
expect(response.status()).toBe(200); cv = await response.json(); expect(cv.version).toBe(version);
}
const history = await (await api.get(`${base}/cv/variants/${id}/versions`)).json();
expect(history.map((row: { version: number }) => row.version)).toEqual(Array.from({ length: 100 }, (_, index) => 100 - index));
expect((await api.put(`${base}/cv/variants/${id}`, { headers, data: { expectedVersion: 99, settings: cv.settings } })).status()).toBe(409);
expect(await (await api.get(`${base}/cv/variants/${id}/versions`)).json()).toEqual(history);
const restored = await api.post(`${base}/cv/variants/${id}/versions/2/restore`, { headers, data: { expectedVersion: 100 } });
expect(restored.status()).toBe(200); expect(await restored.json()).toMatchObject({ version: 101, settings: { headline: 'Revision 2' } });
expect((await (await api.get(`${base}/cv/variants/${id}/versions`)).json()).length).toBe(101);
expect(await (await api.get(`${base}/career/profile`)).json()).toEqual(source);
await info.attach('phase9-volume-cv', { body: JSON.stringify({ savedRevisions: 100, finalVersions: 101, staleStatus: 409, canonicalUnchanged: true, limits: 'Native isolated SQLite API; no100-row history UI or MariaDB/long-term capacity claim.' }), contentType: 'application/json' });
} finally { await account.api.dispose(); }
});
test('P9 volume: 100 manual timeline messages retain exact membership and foreign-owner denial', async ({ playwright }, info) => {
test.setTimeout(180000);
const account = await owner(playwright); const other = await owner(playwright);
try {
const { api, headers } = account;
const company = await api.post(`${base}/companies`, { headers, data: { name: 'Synthetic volume timeline' } });
expect(company.status()).toBe(201);
const job = await api.post(`${base}/jobapplications`, { headers, data: { companyId: (await company.json()).id, jobTitle: 'Volume timeline fixture', status: 'Saved' } });
expect(job.status()).toBe(201); const jobId = (await job.json()).id;
const ids: number[] = [];
for (let index = 0; index < 100; index++) {
const response = await api.post(`${base}/correspondence`, { headers, data: { jobApplicationId: jobId, from: 'Synthetic manual note', content: `Timeline evidence ${index} ${'Documented progress. '.repeat(20)}`, channel: 'manual', direction: 'incoming' } });
expect(response.status()).toBe(201); ids.push((await response.json()).id);
}
const response = await api.get(`${base}/correspondence/${jobId}`); expect(response.status()).toBe(200);
const rows = await response.json(); expect(rows.map((row: { id: number }) => row.id).sort((a: number, b: number) => a - b)).toEqual([...ids].sort((a, b) => a - b));
expect(rows.every((row: { content: string }) => row.content.startsWith('Timeline evidence '))).toBe(true);
expect((await other.api.get(`${base}/correspondence/${jobId}`)).status()).toBe(404);
await info.attach('phase9-volume-timeline', { body: JSON.stringify({ messages: rows.length, exactMembership: true, foreignRead: 404, limits: 'Native persisted manual correspondence; no external mail delivery or100-message browser rendering claim.' }), contentType: 'application/json' });
} finally { await account.api.dispose(); await other.api.dispose(); }
});
test('P9 volume: 100 native cancellation notices preserve bounded lists, unread counts and ownership', async ({ playwright }, info) => {
test.setTimeout(300000);
const account = await owner(playwright); const other = await owner(playwright);
try {
const dataRoot = info.config.metadata.isolatedDataRoot;
expect(typeof dataRoot).toBe('string'); expect(process.env.PLAYWRIGHT_REUSE_SERVERS).not.toBe('true');
for (let index = 0; index < 100; index++) {
// Existing helper admits one inert insert only after temp-path/schema/owner verification.
const seeded = spawnSync(process.env.PLAYWRIGHT_PYTHON_PATH || 'python', [path.resolve('e2e/helpers/seed-operation.py')], { input: JSON.stringify({ dataRoot, ownerId: account.id, ownerEmail: account.email }), encoding: 'utf8', timeout: 15000 });
expect(seeded.status, seeded.stderr).toBe(0);
const id = JSON.parse(seeded.stdout).operationId;
expect((await account.api.post(`${base}/operations/${id}/cancel`, { headers: account.headers })).status()).toBe(200);
}
const list = await account.api.get(`${base}/notifications?limit=100`); expect(list.status()).toBe(200);
const rows = await list.json(); expect(rows.length).toBe(100); expect(new Set(rows.map((row: { id: string }) => row.id)).size).toBe(100);
expect((await (await account.api.get(`${base}/notifications`)).json()).length).toBe(25);
expect(await (await account.api.get(`${base}/notifications/unread-count`)).json()).toEqual({ count: 100 });
expect((await account.api.get(`${base}/notifications?limit=101`)).status()).toBe(400);
expect(await (await other.api.get(`${base}/notifications`)).json()).toEqual([]);
expect((await other.api.post(`${base}/notifications/${rows[0].id}/read`, { headers: other.headers })).status()).toBe(404);
expect((await account.api.post(`${base}/notifications/${rows[0].id}/read`, { headers: account.headers })).status()).toBe(204);
expect((await account.api.post(`${base}/notifications/${rows[0].id}/read`, { headers: account.headers })).status()).toBe(204);
expect(await (await account.api.get(`${base}/notifications/unread-count`)).json()).toEqual({ count: 99 });
expect((await account.api.delete(`${base}/notifications/${rows[1].id}`, { headers: account.headers })).status()).toBe(204);
expect(await (await account.api.get(`${base}/notifications/unread-count`)).json()).toEqual({ count: 98 });
expect((await (await account.api.get(`${base}/notifications?limit=100`)).json()).length).toBe(99);
await info.attach('phase9-volume-notifications', { body: JSON.stringify({ notices: 100, defaultList: 25, maxList: 100, repeatedReadCount: 99, afterUnreadDismiss: 98, foreignMutation: 404, limits: 'Existing guarded inert operation seed + native cancel/read/dismiss. No enabled worker/provider/model or100-item bell UI claim.' }), contentType: 'application/json' });
} finally { await account.api.dispose(); await other.api.dispose(); }
});
+2
View File
@@ -14,3 +14,5 @@ Status is evidence-specific. Historic acceptance records remain unchanged. A pas
Failed diagnostic fixtures are also retained: initial route matrix mistook “CV unavailable” alert text for a heading; correct page heading is “Public CV”. Initial sustained fixture compared submitted JSON directly with the canonical normalizer and used derived date fields; corrected fixture uses canonical `start`/`end`, distinct bullets and a verified saved readback before checking preservation. The initial drawer assertion inspected immediately after ARIA removal rather than waiting for the closing transition; settled CSS/keyboard proof is required. These are QA harness mistakes, not demonstrated product defects, and failed attempts are never counted as passes.
The ordinary run interrupted after 36 completed cases is not an accepted complete run. Native-command warnings were initially promoted to terminating PowerShell errors; successor wrappers preserve actual native exit codes. No assertion was suppressed, no configured retry added and no skip introduced.
A later frozen ordinary attempt stopped after96 completed cases with no final report/exit record and no live runner when inspected on23October; its cause is not established. Another restart was deliberately stopped while changing to native serial sharding. Neither attempt is counted as complete or as proof of a product failure. The final qualification uses four native Playwright shards, preserving connected files, isolated startup and every original assertion; all362 unique case IDs and all four successful process exits are required, with no skips or retries. Original logs remain hashed. This changes result checkpointing, not the acceptance assertions or membership.
+10 -5
View File
@@ -28,20 +28,25 @@ def main():
assert set(remaining) == {row['id'] for row in groups}
cases = read('p9-01/ordinary-cases.json')
diagnostics = read('p9-05/phase9-cases.json')
assert len(cases) == 362 and len(diagnostics) == 18
assert all(row['status'] == 'expected' and len(row['results']) == 1 and row['results'][0]['status'] == 'passed' for row in cases + diagnostics)
volume = read('p9-05/volume-cases.json')
assert len(cases) == 362 and len(diagnostics) == 18 and len(volume) == 3
assert len({(row['id'], row['project']) for row in cases}) == 362
assert all(row['status'] == 'expected' and len(row['results']) == 1 and row['results'][0]['status'] == 'passed' for row in cases + diagnostics + volume)
validation = read('validation.json')['lanes']
for lane in ['ordinary', 'phase9', 'ai', 'auth', 'rate', 'ingress', 'pdf', 'preferences']:
assert validation['ordinary']['membershipMatchesDiscovery']
for lane in ['ordinary', 'phase9', 'volume', 'ai', 'auth', 'rate', 'ingress', 'pdf', 'preferences']:
record = validation[lane]
assert not record['fileFailures'] and not record['retries']
assert record['stats']['unexpected'] == record['stats']['flaky'] == record['stats']['skipped'] == 0
assert record['stats']['expected'] == record['cases']
assert sum(validation[lane]['cases'] for lane in ['ai', 'auth', 'rate', 'ingress', 'pdf', 'preferences']) == 23
assert validation['backend']['passed'] == '1084' and validation['backend']['failed'] == '0'
assert validation['backend']['notExecuted'] == '0'
assert validation['frontend']['success'] and validation['frontend']['numPassedTests'] == 919
assert validation['frontend']['numFailedTests'] == validation['frontend']['numPendingTests'] == 0
assert validation['sidecar']['tests'] == '56' and validation['sidecar']['failures'] == validation['sidecar']['errors'] == '0'
for lane in ['ordinary-accepted', 'phase9-frozen', 'ai', 'auth', 'rate', 'ingress', 'pdf', 'preferences', 'backend', 'frontend', 'sidecar', 'build']:
assert validation['sidecar']['skipped'] == '0'
for lane in [*[f'ordinary-shard-{number}' for number in range(1, 5)], 'phase9-frozen', 'volume-qualified', 'ai', 'auth', 'rate', 'ingress', 'pdf', 'preferences', 'backend', 'frontend', 'sidecar', 'build']:
assert validation[lane + 'Process']['exitCode'] == 0
rows = read('p9-02/route-results.json')
assert len(rows) == 396 and {row['pattern'] for row in rows} == set(inventory['routes'])
@@ -64,7 +69,7 @@ def main():
delta += ' P9-L01 installed vendor locale and2new Jest cases.'
if identifier == 'PRES-S02':
delta += ' Phase8 account-layout order/size/visibility/reset/conflict behaviour preserved.'
lines.append(f"| {identifier}: {cell(group['owners'])} | {group['entryStatus']} → {group['entryStatus']} | {len(matches)} | {cell(delta)} {cell(residual)} |")
lines.append(f"| {cell(group['capability'])}; owners: {cell(group['owners'])} | {group['entryStatus']} → {group['entryStatus']} | {len(matches)} | {cell(delta)} {cell(residual)} |")
portable.append(dict(id=identifier, capability=group['capability'], owners=group['owners'], entry=group['entryStatus'], phase9=group['entryStatus'], evidence=matches, remaining=residual, delta=delta))
assert sum(row['phase9'] == 'PARTIAL' for row in portable) == 56
assert sum(row['phase9'] == 'DEFERRED' for row in portable) == 1
File diff suppressed because it is too large Load Diff