hub: the contract's text read from the repository — contract.path/section and GET …/tasks/{id}/contract (§6.8, §6.15)
tsk-2w6v9kv0tv50 done feature Maity Coder Claude 004 (maity-coder-claude-004)
Contract merged in #348 (maintainer's inbox decision 2026-10-25: contracts stay in Git, readable in the hub). Implement docs/api.md §6.8 The contract's text stays in the repository:
contract.path(defaultdocs/api.md) andcontract.section(nullable) on contract cards; settable where the card may be edited; history entries (§6.15);pathvalidated as repository-relative (not_a_path).GET /projects/{slug}/tasks/{id}/contract(project:read): resolve the default branch to one commit through the probe, read the file at that commit (≤ 1 MiB, UTF-8;too_large/not_text), find the ATX heading (levels 2–4) whose first token is exactly the section number (6.2 ≠ 6.20 ≠ 6.2.1; fenced code skipped; duplicatesambiguous_in_file; nonenot_found_in_file; file missingnot_found_on_forge;sectionnull →unset), return{path, section, ref, heading, text, url, truncated, fetched_at}withtextcut at 64 KiB on a character boundary.- Probe: only the project's own repository (§6.13);
probe_not_configured/probe_unavailableas the import does. One read kept ≤ 60 s, keyed by repository + resolved commit + path + section (never served across a changed selection), coalescing concurrent calls; nothing stored. - Untrusted text per §6.14: no mention resolution, no instruction.
help.mdentry. Tests: the matching rules incl. 6.2/6.20/6.2.1, fenced-code skip, duplicates, moved file, cache key change, cap and truncation; the probe mocked as in the import tests. - No migration if
contractis stored as JSON today; if a column is needed, ask me for a number first.
Contract owner: A. After #241/#324 are through their migration turn, or before if you have the time — your call.