Files
jobtrackingapp/docs/redesign/phase-7/p7-07/CONTRACT.md

91 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# P7-07 — Documents metadata contract
**Status: backend contract implemented, implemented-scope CONDITIONAL GO; affected/native checks and hygiene pass. Normal push verified as8e4db12. P7-07 remains ongoing; the separate [Documents UI](UI.md) is in progress.** This is the first P7-07 boundary in the [approved handoff](../../phase-6/p6-05/PHASE7_HANDOFF.md#migration-order-and-commit-boundaries), implementing the owner projection required by [Documents architecture](../../phase-3/DOCUMENTS_UX.md).
## Bounded implementation
`DocumentsController`, `DocumentsIndexService` and its scoped Program registration add authenticated **GET `/api/documents`** over existing resources. No generic Document table, schema migration, storage relocation, browser crawl, editor replacement, PDF generation or mail-persistence change is introduced.
The response envelope contains exactly `items`, `page`, `pageSize`, `total` and `totalPages`. Each item has exactly **27 public metadata fields**:
`id`, `resourceId`, `type`, `name`, `state`, `visibility`, `jobApplicationId`, `jobTitle`, `companyName`, `jobIsDeleted`, `recordedAt`, `dateKind`, `version`, `historyCount`, `attachmentCount`, `cvVariantId`, `cvVariantName`, `cvVariantVersion`, `fileType`, `fileSize`, `purpose`, `useForAi`, `provider`, `sourceHref`, `downloadHref`, `publicHref`, `hasCurrentContent`.
| Existing kind | Honest projection and destination |
|---|---|
| `cv` | Current variant name/version, optional owned job, private/public state; existing builder and active public slug. No GET PDF action is invented |
| `cover-letter` | Job draft and recorded history, including history after current text is cleared; `hasCurrentContent` distinguishes those states. Latest history date is not a claim of current-edit time; open Application → Letter |
| `application-material` | Existing saved answer/recruiter-draft presence; unknown date stays unknown. Ordinary Notes do not become a document; open Application → Answers |
| `tailored-cv` | Existing structured draft or legacy tailored text, with recorded edit/generation/update date when available; open Application → CV, without inventing a downloadable file |
| `attachment` | Current upload metadata, purpose/type/size/date and existing explicit AI-use flag; existing job Documents and authenticated download |
| `submitted-package` | Frozen package version/capture time, captured CV identifiers/name/version and captured attachment count; existing Submission context. Current CV/upload changes cannot relabel captured metadata |
| `email-draft` | Saved subject/provider/revision and owned job context; draft does not mean delivery. Open Correspondence; P7-06 remains the persistence/send owner |
`sourceHref` opens the existing owner context. Correspondence does not promise exact-ID Resume, and Submission does not promise automatic selection of a particular package. Attachment `downloadHref` is `/api/attachments/download/{id}`: the UI must use the existing authenticated download path without duplicating an Axios `/api` base. CV download remains the existing explicit export operation; list GET cannot render it.
## Filtering, ownership and database bounds
| Parameter | Supported contract |
|---|---|
| `page` / `pageSize` | Defaults 1/25; ranges 1–10000 and 1–100 |
| `q` | At most 200 characters; trimmed metadata search with literal `%`, `_` and `!`, plus supported Nordic case folding |
| `type` | `all` or one of the seven kinds above |
| `state` | `all`, `current`, `draft`, `frozen` |
| `visibility` | `all`, `private`, `public`; specific visibility intersects CV entries only |
| `jobApplicationId` | Optional positive owned-parent filter; a foreign or missing job returns no private metadata |
| `sort` | `date-desc`, `date-asc`, `name-asc`, `name-desc`; deterministic Type/ResourceId ordinal ties |
The local authentication scheme and server current-user service choose the owner. Caller-supplied owner IDs cannot select another tenant. Every UNION branch guards its owner; parent/company joins also guard ownership. Malformed foreign associations expose no foreign context. Independent/detached CVs remain available to their owner; detach does not delete a variant. Owned soft-deleted job history remains visible with `jobIsDeleted`, rather than silently applying an active-Jobs-only policy.
The service executes **two parameterized SELECT commands**: filtered count and a database `LIMIT`/`OFFSET` page. It does not materialize the complete collection to sort in memory. A 128-item SQLite fixture proves a seven-row second page, exact count, two read commands and unchanged database bytes. Invalid parameters fail before catalog queries. Search covers names/role/company/captured CV name, never private content snippets. SQL computes existing text presence without projecting bodies, Notes, settings/profile JSON, rendered HTML, storage paths, recipient/thread/request identities or bytes to the response. No renderer, AI, file-copy or listing-write dependency is registered in the contract fixture.
SQLite ordering separates whole UTC seconds from seven fractional digits to preserve exact instants, offsets and one-tick differences. Unknown dates and uploads whose original local timezone was not recorded sort last in both date directions. Uploads retain their raw unzoned wall-clock value as `uploaded-local`; no UTC instant is fabricated. MariaDB SQL composes `TIMESTAMPDIFF(MICROSECOND, …) * 10`; live MariaDB execution and production precision remain unverified.
## Reproduction and retained failures
All times are UTC on **2026-10-14**. Exact commands/TRX/source receipts remain under `.artifacts/phase7/current/backend-career/` and `.artifacts/phase7/p7-07/`; failed evidence is retained.
| Observation | Actual result and boundary |
|---|---|
| Existing related baseline | **37 PASS**, 11:05:19.519871–11:05:23.691550, `--no-build`; 20 pre/post sources and inherited API DLL match. Newly written Documents tests were not compiled in this lane |
| Initial controller HTTP RED | **0 pass/11 fail**, 11:12:34.372367–11:12:50.335904; missing endpoint returns 404. Eleven ordinary Kestrel/SQLite tests use controlled test identities, not actual Program cookies |
| Actual Program old-API RED | **0 pass/3 fail**, 11:25:16.963090–11:25:28.045792; actual local Identity sessions/CSRF-protected source writes encounter missing `/documents` 404 |
| MariaDB policy RED | **0 pass/1 fail**, 11:27:50.588566–11:28:02.188395; composition lacks the explicit ordinal Unicode marker policy. No MariaDB connection or execution occurs |
| First full candidate | **10 pass/2 fail**, 11:30:29.637426–11:30:41.875848; marker-presence and pagination-composition checks remain unresolved |
| Focused diagnostic | **0 pass/2 fail**, 15:41:17.837210–15:41:31.709973; exact empty-LF `application-material:5`/whitespace `:7` membership and literal debug-display LIMIT fail. No earlier ten-pass rerun is inferred |
| Corrected full candidate | **12 PASS**, 15:47:19.771612–15:47:36.190838; 26 pre/post sources match. TRX SHA256 `cf5791cef3e159d144cb5a4b0628beeba78562d1975676108458005e028e2277` |
| Final strengthened fixture | **12 PASS**, 15:50:24.880378–15:50:35.341407; all BMP `char.IsWhiteSpace` characters exercise the same presence rule. All 26 pre/post sources match; zero failed/not-executed cases. TRX SHA256 `4d34e2045ced1a0b2bd3251893a5cad0e012c396fabd8b989466a84b2365b139` |
| Final related/authentication lanes | **80 PASS**, 15:52:48.930990–15:52:53.240041, `--no-build`: 37 existing source/archive contracts, 25 draft/send cases and 18 authentication posture/session cases. All 33 pre/post sources match; same final Release DLL |
| Actual Program current-API native | **3 PASS**, 15:54:17.479545–15:54:29.733344; actual local Identity cookies, disposable SQLite, independent A/B owners and existing CSRF-protected source mutations |
| Ordinary P2-D01 consumer | **1 PASS**, 15:54:29.738467–15:54:43.075754; existing frozen-document workspace case, without skip or expected-failure marker |
The old-API receipt named `pre.json` was actually captured **11:26:04.698648**, after the native command ended **11:25:28.045792**. Its raw “DURING” wording is inaccurate; `native-red-old-api/capture-qualification.json` preserves and corrects it. That receipt is an after-run observation, not pre/during proof. The separately frozen prior compile/source/binary boundary qualifies the old-API comparison. A usage pause precedes the 15:40 resumption and held-file verification; no tests or outcomes are inferred during that gap.
Modern answer markers remain ordinal and case-sensitive, including text with Unicode before them. MariaDB `CONVERT(… USING utf8mb4) COLLATE utf8mb4_bin` preserves character positions; legacy prefix matching remains case-insensitive and empty modern content retains precedence. Shared `HasContent` removes .NET whitespace. `CHAR(9)` through `CHAR(13)` preserve control characters despite EF/Windows SQL newline formatting; SQL TRIM alone was insufficient. Assertions retain the exact four expected document IDs and no private-content leakage.
Pomelo `ToQueryString()` displays literal pagination values. The corrected test separately inspects an **actual, unexecuted DbCommand**, proving distinct bound LIMIT/OFFSET parameters with expected values and owner binding. This is opaque SQL composition evidence: **11 SQLite HTTP cases + one MariaDB composition case**, not twelve provider executions or a TCP/server qualification.
Final service SHA256 is `def370bd0dbe90260e42800fded99302e56f8ac6b7693e73dd0a60cf0a3f7fd1`; strengthened test SHA256 is `f16d3d78b072faaea87c376888803ecbf8daec6da62368d89ea78ecd8bc9bac2`. `contract-final` retains genuine pre/post captures and the API DLL SHA256 `5bc23b88df160434f15b5cd09535144bdac3eeebc241d5e1837d854d936f4842`.
## Remaining acceptance and preservation
The three passing API-only native cases cover actual cookie owner isolation, exact metadata/filter/page/search contracts and observable source revision/detach/delete readback. They do not render a Documents page, exercise downloads or prove internal SQL/service-call counts. The backend fixture separately proves all seven kinds, read bounds and unchanged captured metadata/archived bytes after live CV/upload/letter changes.
Final native capsule `.artifacts/phase7/p7-07/native-final/report/results.json` SHA256 is `baa1d592e5408220ed9d6f9742af907014c275cbbcb3ad76fb7117435ace345e`. All 23 product/specification pre/post inputs and recorded binaries remain unchanged across native three cases and P2-D01. Browser TypeScript passed at 11:25 on unchanged specification SHA256 `aca359ec044332ed688bd108352c28d6f4c960c8201dd4a7853b6281d341f483`. The inherited frontend export `LFCrTZzDtX5hNBSRBj5Xx` is an unused UI launch fixture for the API-only cases; P2-D01 uses the existing workspace. No new frontend build or Jest execution is claimed.
Root actually inspected the sole P2-D01 consumer PNG. Its opacity transition and full-page sticky-element capture limit visual conclusions; it is not Documents UI evidence or a fresh rendering-fidelity qualification. API-only cases perform no axe scans. Independent source review found no critical issue; that review supplies no additional runtime, provider or MariaDB evidence.
Release contract execution, related/authentication cases, native cases, P2-D01 and browser TypeScript are recorded above. Secret/documentation/whitespace checks, commit and normal development push remain pending. After the contract push is verified, implement the separate Documents UI boundary using these honest states and existing owner actions.
The twelve additions derive backend inventory **1,053** from **1,041**, not a fresh full-suite pass count. Three new browser cases derive **304** from **301**; publication remains pending. Frontend inventory stays **739/94**, with no Jest changes. P7-12 owns fresh integrated counts; inherited full backend 1,033/frontend 573/89/sidecar 49 are not replaced by these focused runs.
All **57 PRES groups remain 56 PARTIAL / C11 DEFERRED**; C05–C12/W05–W08/M04/S05/E09 receive bounded contract evidence, not whole-group PASS. Preserve the original routes, canonical Career Profile authority, CV overrides/associations, frozen content and captured files. P5-13 stays withheld with zero qualification calls; optional followMode stays deferred. No manual UI/visual, reader/device, provider or production acceptance is claimed.
**Contract implemented-scope acceptance: CONDITIONAL GO; hygiene and normal push pending. P7-07: ongoing; Documents UI not started. Actual MariaDB execution and manual acceptance remain unverified. Production: NO-GO; main unchanged. Phase 8: no implementation.**
Final metadata-contract hygiene PASS observed 2026-10-14T16:00:00.842457+00:00: secret and working/staged/phase whitespace gates pass;18 docs/504 local links resolve,57 groups and32 original routes retained. Backend contract implemented CONDITIONAL GO; normal development push pending. Actual MariaDB execution and Documents UI remain unverified; main unchanged, production NO-GO.
## Contract push and UI continuation — 2026-10-16
Metadata contract normal push directly verified as **8e4db121ae0ec3f1637c83ddeaa3f31abd0a61be** at 2026-10-14T16:02:14.215615 UTC: clean HEAD = origin = live development; main27657f3 unchanged. Backend implemented scope remains CONDITIONAL GO, with actual MariaDB execution unverified. A fresh fetch on 2026-10-16 found no intervening commits. The separate Documents interface now proceeds; its entry and bounded requirements are recorded in the P7-07 UI report. Earlier pending-push statements are historical observations. No production deployment or Phase8 implementation.