Files
jobtrackingapp/docs/redesign/phase-9/p9-00/inventory.py
T

101 lines
8.0 KiB
Python

"""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()})))