docs(qa): establish Phase 9 inventory and qualification plan

This commit is contained in:
cesnimda
2026-10-22 21:40:18 +02:00
parent 1c24e406f1
commit 8cfc684c64
6 changed files with 3606 additions and 0 deletions
+22
View File
@@ -0,0 +1,22 @@
# P9-00 — Entry baseline and coverage inventory
Development baseline and direct remote: `1c24e406f194f0f42277d837b3008a7a28dca26a` on `feature/phase8-dashboard-customisation`, clean before work. New branch is `feature/phase9-full-qa`. Main remains the older release branch and is not the redesign base. Feature pushes are authorised; `.gitea/workflows/ci-deploy.yml` restricts deployment to a main push.
The reproducible [source inventory](INVENTORY.md) and [full JSON](inventory.json) enumerate **33 concrete routes**, **57 preservation groups**, actual page/shared component owners, **43 API controller/partial owners**, migration/data owners, the Admin/Premium entitlement roles and existing test files: backend 117, frontend 104, ordinary browser 58, sidecar 2. These are file counts and associations, not exhaustive behavioral coverage. Public/anonymous, roleless authenticated and administrator actors remain distinct; arbitrary stored roles are not inferred to confer grants.
The exact group-level entry disposition is **56 PARTIAL / PRES-C11 DEFERRED**. Original contract/test/gap columns are retained in JSON and link back to the authoritative matrix. The current Documents route and context-preserving correspondence alias supersede older route descriptions; historic records are not edited.
## Fresh entry observations
| Lane | Actual entry evidence | Qualification |
|---|---|---|
| Backend | 1,084 passed; 0 failed/skipped, Release, pinned SDK 9.0.317 | `.artifacts/phase9/backend/phase9.trx`; SQLite/test-host evidence, not MariaDB runtime. |
| 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. |
| 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.
Inventory assertions require exact 33/57 membership and one deferred group. Product verification and final acceptance are separate checkpoints; P9-00 does not clear inherited manual/provider/output/release gates.
+117
View File
@@ -0,0 +1,117 @@
# P9-00 source inventory
Generated by `inventory.py`; associations below are existing candidate coverage, not executed Phase 9 passes.
## Route → workflow → feature → test evidence → risk → acceptance
| Route | Actual element owners | Preservation contracts / existing test evidence | Known risk | Phase 9 |
|---|---|---|---|---|
| `/` | `Navigate`, `LandingPage` | PRES-E01, PRES-J04; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/dashboard` | `DashboardView` | PRES-S01, PRES-S02; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/analytics` | `AnalyticsPage` | Shared shell / nearest parent contracts; direct route evidence required; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/discover` | `JobDiscoveryPage` | PRES-J01, PRES-J02; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/jobs` | `JobTable` | PRES-J05, PRES-J06; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/jobs/:id` | `ApplicationWorkspacePage` | PRES-W01, PRES-W02, PRES-W03; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/reminders` | `RemindersView` | PRES-W11; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/operations` | `OperationsPage` | PRES-W12; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/kanban` | `LegacyBoardRedirect` | PRES-J07; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/companies` | `CompaniesTable` | PRES-J08; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/offers` | `OfferComparisonPage` | PRES-J09; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/correspondence` | `CorrespondenceInboxPage` | PRES-M03, PRES-M04, PRES-M05; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/correspondence/review` | `LegacyCorrespondenceReviewRedirect` | PRES-M05; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/profile` | `ProfilePage` | PRES-E04, PRES-E05, PRES-E06; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/applications/:id` | `LegacyApplicationRedirect` | PRES-W01; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/career` | `CareerWorkspacePage` | PRES-C01, PRES-C02; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/career/builder` | `CvBuilderPage` | PRES-C05; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/career/builder/:id` | `CvBuilderEditor` | PRES-C06; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/documents` | `DocumentsPage` | Shared shell / nearest parent contracts; direct route evidence required; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/admin/audit` | `AdminAuditPage` | PRES-A02; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/admin/users` | `AdminUsersPage` | PRES-A01; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/admin/system` | `AdminSystemPage` | PRES-A03; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/trash` | `JobTable` | PRES-J06; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/settings` | `SettingsView` | PRES-J10, PRES-W11, PRES-S03, PRES-S04; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/settings/connected-accounts` | `ConnectedAccountsPage` | PRES-M06; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/login` | `LoginPage` | PRES-E02, PRES-E03, PRES-E04; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/register` | `LoginPage` | PRES-E02; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/forgot-password` | `ForgotPasswordPage` | PRES-E02; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/reset-password` | `ResetPasswordPage` | PRES-E02; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/verify-email` | `VerifyEmailPage` | PRES-E02; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/confirm-email-change` | `VerifyEmailPage` | PRES-E06; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/microsoft-legacy-relink` | `MicrosoftLegacyRelinkPage` | PRES-E03; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
| `/cv/:slug` | `PublicCvPage` | PRES-C12; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |
## Complete preservation ledger
Exact source/owner/test/gap columns for every group are retained in [inventory.json](inventory.json). The original matrix is authoritative for sub-capabilities.
| ID | Capability | Entry status | Phase 9 |
|---|---|---|---|
| PRES-E01 | **PRES-E01** — Public product/plans; honest unavailable billing | PARTIAL | NOT TESTED |
| PRES-E02 | **PRES-E02** — Local login/register/remember, challenge, reset and verification | PARTIAL | NOT TESTED |
| PRES-E03 | **PRES-E03** — Google/Microsoft login, link, legacy relink and popup results | PARTIAL | NOT TESTED |
| PRES-E04 | **PRES-E04** — 2FA challenge/recovery/setup/disable; trusted-device revocation | PARTIAL | NOT TESTED |
| PRES-E05 | **PRES-E05** — Session list/current device, revoke one/others, logout | PARTIAL | NOT TESTED |
| PRES-E06 | **PRES-E06** — Avatar, account names/headline, email change and password | PARTIAL | NOT TESTED |
| PRES-E07 | **PRES-E07** — Desktop collapse/mobile drawer, active nav and command search/actions | PARTIAL | NOT TESTED |
| PRES-E08 | **PRES-E08** — Theme/system, EN/NB hydration, toast/confirm/prompt and errors | PARTIAL | NOT TESTED |
| PRES-E09 | **PRES-E09** — Account-switch private-state disposal and stale request rejection | PARTIAL | NOT TESTED |
| PRES-J01 | **PRES-J01** — NAV discovery/filter/sort/source/deadline/details | PARTIAL | NOT TESTED |
| PRES-J02 | **PRES-J02** — Saved searches, run/delete/alerts, result changes/dismissal | PARTIAL | NOT TESTED |
| PRES-J03 | **PRES-J03** — Reviewed manual/URL/paste job create, company/tags/dates, duplicates | PARTIAL | NOT TESTED |
| PRES-J04 | **PRES-J04** — Extension/bookmarklet/share capture; post-create attachment/CV | PARTIAL | NOT TESTED |
| PRES-J05 | **PRES-J05** — Job filters/sort/page/columns, saved views and row/mobile actions | PARTIAL | NOT TESTED |
| PRES-J06 | **PRES-J06** — Bulk status/delete, soft delete/trash/restore and job edit | PARTIAL | NOT TESTED |
| PRES-J07 | **PRES-J07** — Kanban grouped statuses, pointer/keyboard move and precise menu | PARTIAL | NOT TESTED |
| PRES-J08 | **PRES-J08** — Company/contact edits, pipeline dates and linked-job history | PARTIAL | NOT TESTED |
| PRES-J09 | **PRES-J09** — Recorded offers/compensation/deadlines/notes and source links | PARTIAL | NOT TESTED |
| PRES-J10 | **PRES-J10** — CSV/JSON export, JSON import and CSV-to-JSON conversion | PARTIAL | NOT TESTED |
| PRES-W01 | **PRES-W01** — Full workspace, sections, legacy links and optional reusable overlay | PARTIAL | NOT TESTED |
| PRES-W02 | **PRES-W02** — Detail/status/progression, recruiter/dates/description/timeline | PARTIAL | NOT TESTED |
| PRES-W03 | **PRES-W03** — Analysis, match/gaps, strategy and reviewed status/workflow suggestions | PARTIAL | NOT TESTED |
| PRES-W04 | **PRES-W04** — Checklist create/edit/complete/delete/order and next action | PARTIAL | NOT TESTED |
| PRES-W05 | **PRES-W05** — Attach/select/create/tailor CV and source context | PARTIAL | NOT TESTED |
| PRES-W06 | **PRES-W06** — Cover-letter manual/AI review, versions/restore and dirty protection | PARTIAL | NOT TESTED |
| PRES-W07 | **PRES-W07** — Application drafts and immutable submitted package/archived binaries | PARTIAL | NOT TESTED |
| PRES-W08 | **PRES-W08** — Attachments upload/category/name/AI consent/preview/download/delete | PARTIAL | NOT TESTED |
| PRES-W09 | **PRES-W09** — Interview questions/answers/ready/manual removal/AI review | PARTIAL | NOT TESTED |
| PRES-W10 | **PRES-W10** — Debrief/follow-up draft/date/readiness | PARTIAL | NOT TESTED |
| PRES-W11 | **PRES-W11** — Reminder rules/actions/notifications, ICS and Google/Outlook events | PARTIAL | NOT TESTED |
| PRES-W12 | **PRES-W12** — Async progress/failure/retry/cancel/notifications | PARTIAL | NOT TESTED |
| PRES-C01 | **PRES-C01** — Canonical structured career data and long-tail/custom sections | PARTIAL | NOT TESTED |
| PRES-C02 | **PRES-C02** — Profile overview/completeness/import guidance/raw copy/history/restore | PARTIAL | NOT TESTED |
| PRES-C03 | **PRES-C03** — Upload/extract/rebuild/reprocess/diff/select/accept/discard/retry/cancel | PARTIAL | NOT TESTED |
| PRES-C04 | **PRES-C04** — Evidence bank source/category/tags/add/list/delete | PARTIAL | NOT TESTED |
| PRES-C05 | **PRES-C05** — CV library create/copy/delete/templates/share/publication/PDF | PARTIAL | NOT TESTED |
| PRES-C06 | **PRES-C06** — Studio tabs, preview debounce/pages/fit/save state/mobile panes | PARTIAL | NOT TESTED |
| PRES-C07 | **PRES-C07** — Visibility/order/custom sections and item/document overrides | PARTIAL | NOT TESTED |
| PRES-C08 | **PRES-C08** — Capability-aware templates/typography/color/header/skills/sidebar/photo | PARTIAL | NOT TESTED |
| PRES-C09 | **PRES-C09** — A4/Letter/density/margins/dates/document locale/section presentation | PARTIAL | NOT TESTED |
| PRES-C10 | **PRES-C10** — Undo/redo, save queue, dirty guard, history restore/preview lifecycle | PARTIAL | NOT TESTED |
| PRES-C11 | **PRES-C11** — Section and whole-document AI improve/translate/instruction/grammar review | DEFERRED | NOT TESTED |
| PRES-C12 | **PRES-C12** — Private PDF and published anonymous CV/PDF, long searchable pages | PARTIAL | NOT TESTED |
| PRES-M01 | **PRES-M01** — Job-scoped manual correspondence/search/delete/paste import | PARTIAL | NOT TESTED |
| PRES-M02 | **PRES-M02** — Gmail candidate/thread import/refresh/sync/unlink/relink/move | PARTIAL | NOT TESTED |
| PRES-M03 | **PRES-M03** — Global inbox filters/page/provider/link/direction/detail/source links | PARTIAL | NOT TESTED |
| PRES-M04 | **PRES-M04** — Compose/reply/provider/recipient/job; draft conflicts; confirm send/uncertainty | PARTIAL | NOT TESTED |
| PRES-M05 | **PRES-M05** — Review proposed jobs/link/reject/keep/notes/sync/create | PARTIAL | NOT TESTED |
| PRES-M06 | **PRES-M06** — Gmail/Graph/IMAP connect/status/disconnect separate from sign-in | PARTIAL | NOT TESTED |
| PRES-S01 | **PRES-S01** — Personal metrics/activity/funnel/company/skills/stage/salary/next actions | PARTIAL | NOT TESTED |
| PRES-S02 | **PRES-S02** — Owner-local five visibility preferences/onboarding dismiss/notifications | PARTIAL | NOT TESTED |
| PRES-S03 | **PRES-S03** — Rules/reminder/table/theme/language preferences and account links | PARTIAL | NOT TESTED |
| PRES-S04 | **PRES-S04** — AI privacy/usage/limits/Free-Pro/server entitlement/conditional billing | PARTIAL | NOT TESTED |
| PRES-S05 | **PRES-S05** — Account ZIP/encrypted backup/lifecycle/deletion confirmations | PARTIAL | NOT TESTED |
| PRES-A01 | **PRES-A01** — Admin user/create/roles/reset/delete/last-admin/self guards | PARTIAL | NOT TESTED |
| PRES-A02 | **PRES-A02** — Audit list/detail/supported undo/job restore | PARTIAL | NOT TESTED |
| PRES-A03 | **PRES-A03** — System/build/DB/AI/mail/SMTP/test/probe/benchmark/maintenance | PARTIAL | NOT TESTED |
## API and implementation census
43 controller/partial owners; 254 HTTP method attributes. Full base/suffix/authorization attributes are in JSON; this is source inventory, not runtime endpoint acceptance.
Existing test files: backend 117, frontend 104, browser 58, sidecar 2. File presence does not establish coverage of every branch.
Actors: anonymous/public, authenticated ordinary account, administrator, and plan entitlement variants. Actual role literals and authorization attributes are recorded in JSON. Test auth handlers, real local cookies and external identity providers require separate qualification.
Database workflows: canonical Career Profile and evidence/import, CV variants/revisions/overrides, frozen application packages, job lifecycle/soft delete, correspondence/draft versions, account preferences/session revocation, administrative restore. Migration and data-owner file lists are in JSON.
Shared UI: complete page/component lists in JSON; preserve current MUI/theme/i18n/router/confirm/prompt/resource owners. AI sidecar contracts are offline only unless actual model inference is explicitly exercised; P5-13 remains withheld.
File diff suppressed because it is too large Load Diff
+100
View File
@@ -0,0 +1,100 @@
"""Reproducible source census; test-file associations are candidates, not passes."""
import json
import re
from pathlib import Path
import shutil
ROOT = Path(__file__).resolve().parents[4]
HERE = Path(__file__).resolve().parent
UI = ROOT / 'job-tracker-ui'
def relative(path):
return path.relative_to(ROOT).as_posix()
def collect():
app = (UI / 'src/App.tsx').read_text(encoding='utf-8')
routes = {}
for match in re.finditer(r'<Route\s+path="([^"]+)"', app):
route = match.group(1)
element_match = re.search(r'element=\{\s*<([A-Za-z][A-Za-z0-9]*)', app[match.end():match.end() + 1200])
element = element_match.group(1) if element_match else 'Inspect App.tsx route body'
if route == '*':
continue
routes.setdefault(route, []).append(element)
for route, element in re.findall(r'\{ path: "([^"]+)", element: <([A-Za-z][A-Za-z0-9]*)', app):
if '*' not in route:
routes.setdefault(route, []).append(element)
# Public and authenticated '/' are one concrete pattern.
assert len(routes) == 33, (len(routes), routes)
source_matrix = (ROOT / 'docs/redesign/phase-2/PRESERVATION_MATRIX.md').read_text(encoding='utf-8')
accepted = (ROOT / 'docs/redesign/phase-8/PRESERVATION.md').read_text(encoding='utf-8')
groups = []
for line in source_matrix.splitlines():
if not line.startswith('| **PRES-'):
continue
cells = [cell.strip() for cell in line.strip('|').split('|')]
identifier = re.search(r'PRES-[A-Z]\d{2}', cells[0]).group()
previous = next(row for row in accepted.splitlines() if identifier in row and row.startswith('|'))
groups.append(dict(id=identifier, capability=cells[0], entry=cells[1], owners=cells[2],
existingEvidence=cells[3], historicGap=cells[4],
entryStatus='DEFERRED' if '| DEFERRED |' in previous else 'PARTIAL',
phase9Status='NOT TESTED'))
assert len(groups) == 57 and len({group['id'] for group in groups}) == 57
assert sum(group['entryStatus'] == 'DEFERRED' for group in groups) == 1
endpoints = []
for path in sorted((ROOT / 'JobTrackerApi/Controllers').glob('*.cs')):
text = path.read_text(encoding='utf-8-sig')
bases = re.findall(r'\[Route\("([^"]+)"\)\]', text)
methods = re.findall(r'\[Http(Get|Post|Put|Patch|Delete)(?:\("([^"]*)"\))?\]', text)
if bases or methods:
endpoints.append(dict(file=relative(path), baseRoutes=bases, actions=[dict(method=m.upper(), suffix=s) for m, s in methods],
authorizationAttributes=re.findall(r'\[Authorize[^\]]*\]', text)))
tests = dict(backend=[relative(p) for p in sorted((ROOT / 'JobTrackerApi.Tests').rglob('*Tests.cs'))],
frontend=[relative(p) for p in sorted((UI / 'src').rglob('*')) if re.search(r'\.(test|spec)\.tsx?$', p.name)],
browser=[relative(p) for p in sorted((UI / 'e2e').glob('*.spec.ts'))],
sidecar=[relative(p) for p in sorted((ROOT / 'tools/summarizer/tests').glob('test_*.py'))])
assert all(tests.values())
role_source = (ROOT / 'JobTrackerApi/Controllers/UsersController.cs').read_text(encoding='utf-8')
role_literals = sorted(set(re.findall(r'"(Admin|User|Pro|Premium|Administrator)"', role_source)))
plan_source = (ROOT / 'JobTrackerApi/Models/AccountPlans.cs').read_text(encoding='utf-8')
entitlement_roles = sorted(set(re.findall(r'Contains\("([^"]+)"', plan_source)))
return dict(baseline='1c24e406f194f0f42277d837b3008a7a28dca26a', routes=routes,
preservation=groups, apiOwners=endpoints, rolesFromUsersController=role_literals,
entitlementRoles=entitlement_roles,
rolesCaveat='Admin controls user administration; Admin/Premium grant pro entitlements; ordinary accounts may have no role. UsersController supports custom role strings, which do not imply new grants. Public/anonymous is distinct.',
frontendPages=[relative(p) for p in sorted((UI / 'src/views').glob('*.tsx'))],
sharedComponents=[relative(p) for p in sorted((UI / 'src/components').rglob('*.tsx'))], tests=tests,
databaseWorkflows=[relative(p) for p in sorted((ROOT / 'JobTrackerApi/Data').rglob('*.cs'))],
migrations=[relative(p) for p in sorted((ROOT / 'JobTrackerApi/Migrations').glob('*.cs'))],
cvDocumentOwners=[relative(p) for p in sorted((ROOT / 'JobTrackerApi').rglob('*.cs')) if any(word in p.name for word in ['Pdf', 'CvRender', 'Document', 'CareerProfile'])])
if __name__ == '__main__':
inventory = collect()
HERE.mkdir(parents=True, exist_ok=True)
(HERE / 'inventory.json').write_text(json.dumps(inventory, indent=2, ensure_ascii=False) + '\n', encoding='utf-8')
request = Path('C:/Users/Connor/.codex/attachments/08a603fb-3692-4d57-8f62-69475e72a7e0/Pasted text.txt')
if request.exists() and not (HERE.parent / 'REQUEST.md').exists():
shutil.copyfile(request, HERE.parent / 'REQUEST.md')
lines = ['# P9-00 source inventory', '', 'Generated by `inventory.py`; associations below are existing candidate coverage, not executed Phase 9 passes.', '',
'## Route → workflow → feature → test evidence → risk → acceptance', '',
'| Route | Actual element owners | Preservation contracts / existing test evidence | Known risk | Phase 9 |',
'|---|---|---|---|---|']
for route, owners in inventory['routes'].items():
matched = [group for group in inventory['preservation'] if route in re.findall(r'/[A-Za-z0-9_:/-]*', group['entry'])]
group_text = ', '.join(group['id'] for group in matched) or 'Shared shell / nearest parent contracts; direct route evidence required'
lines.append(f"| `{route}` | {', '.join('`' + owner.replace('|', '&#124;') + '`' for owner in owners)} | {group_text}; [original test mapping](../../phase-2/PRESERVATION_MATRIX.md) | Human/device/provider qualification retained | NOT TESTED |")
lines += ['', '## Complete preservation ledger', '',
'Exact source/owner/test/gap columns for every group are retained in [inventory.json](inventory.json). The original matrix is authoritative for sub-capabilities.', '',
'| ID | Capability | Entry status | Phase 9 |', '|---|---|---|---|']
lines += [f"| {g['id']} | {g['capability']} | {g['entryStatus']} | NOT TESTED |" for g in inventory['preservation']]
lines += ['', '## API and implementation census', '',
f"{len(inventory['apiOwners'])} controller/partial owners; {sum(len(a['actions']) for a in inventory['apiOwners'])} HTTP method attributes. Full base/suffix/authorization attributes are in JSON; this is source inventory, not runtime endpoint acceptance.", '',
f"Existing test files: {', '.join(f'{lane} {len(files)}' for lane, files in inventory['tests'].items())}. File presence does not establish coverage of every branch.", '',
'Actors: anonymous/public, authenticated ordinary account, administrator, and plan entitlement variants. Actual role literals and authorization attributes are recorded in JSON. Test auth handlers, real local cookies and external identity providers require separate qualification.', '',
'Database workflows: canonical Career Profile and evidence/import, CV variants/revisions/overrides, frozen application packages, job lifecycle/soft delete, correspondence/draft versions, account preferences/session revocation, administrative restore. Migration and data-owner file lists are in JSON.', '',
'Shared UI: complete page/component lists in JSON; preserve current MUI/theme/i18n/router/confirm/prompt/resource owners. AI sidecar contracts are offline only unless actual model inference is explicitly exercised; P5-13 remains withheld.']
(HERE / 'INVENTORY.md').write_text('\n'.join(lines) + '\n', encoding='utf-8')
print(json.dumps(dict(routes=len(inventory['routes']), groups=len(inventory['preservation']), apiOwners=len(inventory['apiOwners']), tests={k: len(v) for k, v in inventory['tests'].items()})))