13 KiB
P7-07 — Documents metadata contract
Status: backend contract implemented, implemented-scope CONDITIONAL GO; final affected/native checks pass. Hygiene, commit and normal push remain pending. P7-07 remains ongoing; Documents UI waits for the contract push. This is the first P7-07 boundary in the approved handoff, implementing the owner projection required by Documents architecture.
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.