Skip to content

feat: import standards and map controls to requirements - #5

Merged
quality-runtime[bot] merged 1 commit into
mainfrom
feat/standards-and-mappings
Sep 19, 2026
Merged

quality-runtime[bot] merged 1 commit into
mainfrom
feat/standards-and-mappings

Conversation

@quality-runtime

Copy link
Copy Markdown
Contributor

Adds the first half of requirement → control → evidence to the API: organizations can import a standard, read its requirements in the order the standard states them, and map controls to the requirements they are meant to address. Evidence, attestation and file storage follow in a separate PR.

What changes

  • Import a standard whole. POST /standards takes the name, edition and 1–2,000 requirements in one transaction; position comes from array order. A repeated name and edition is 409 (targeted ON CONFLICT). Imports get a 1 MiB body limit on that exact route; everything else keeps 64 KiB.

  • Read requirements as the document states them. GET /standards/{id}/requirements pages by (position, id) ascending — a second cursor ordering beside newest-first — and filters by ?mapped=true|false and ?reference= (exact match after trimming, for clause IDs cited in commits).

  • Requirements stand on their own. GET /requirements/{id} and GET /requirements/{id}/controls read the mapping from the requirement's end.

  • Map controls as a set. PUT /controls/{id}/requirements replaces the whole set and records one audit event on the control. Its ETag hashes the set's contents and is the same on every page of GET /controls/{id}/requirements, so a client can write back only what it read:

    GET /api/v1/organizations/{org}/controls/{id}/requirements   → ETag: "3f9c…"
    PUT /api/v1/organizations/{org}/controls/{id}/requirements
    If-Match: "3f9c…"
    { "requirementIds": ["req_…", "req_…"] }                    → 200, or 412 if the set moved
  • Unknown query parameters and import fields are refused, not dropped: a misspelt ?maped=false would otherwise return the unfiltered list.

Decisions worth checking

  • ADR 0008 — one row per edition, a copy per organization, nullable text for copyrighted standards, and position allowed to tie.
  • ADR 0009 — import rather than create-one-at-a-time; how collections name their ordering.
  • ADR 0010 — whole-set PUT, FOR UPDATE on the control plus FOR KEY SHARE on the named requirements, and why a retired control can still be remapped.
  • ADR 0011 — requirements get their own path; the filter is mapped, not covered.

Reads that check a parent and then list its children, or return a page with the set's ETag, run in repeatable read so both answers describe one moment.

Verification

On the committed tree, checked out clean:

  • bun run check — format, lint, types
  • bun run test — 442 passing, including the real-PostgreSQL race suite (TEST_DATABASE_URL set): a conditional remapping against a competing lock, and a requirement's standard deleted mid-replacement answering 400 rather than a foreign-key 500
  • uvx --from 'reuse[charset-normalizer]' reuse lint

Known follow-ups

  • Top-level cursors (controls, history, standards) are not bound to the organization, so one can be replayed in another organization the caller belongs to. Pre-existing for controls and history; to be fixed across all three together.
  • Links to ADR 0012 and 0013 stay broken until the evidence PR adds them.

Standards arrive whole in one import and their requirements are read in the order the standard states them, filterable by whether any control is mapped and by the reference people cite. A control's requirements are replaced as a set, guarded by an ETag computed from the set's contents, and the mapping is readable from both ends. Collection queries and the import refuse unknown fields rather than silently answering a different question.

Evidence, attestation and file storage follow separately.

Signed-off-by: quality-runtime[bot] <330432719+quality-runtime[bot]@users.noreply.github.com>
@quality-runtime
quality-runtime Bot merged commit 726b7cd into main Sep 19, 2026
6 checks passed
@quality-runtime
quality-runtime Bot deleted the feat/standards-and-mappings branch September 19, 2026 17:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants