Lesson 187: AI Red-Team Finding Bind to Mock Audit Deficiency Tickets (2026)

Direct answer: Lesson 180 stores LLM critiques in red_team_finding. Lesson 187 requires every promoted blocker/major finding to create or link a mock_audit_deficiency_ticket row with an allow-listed failure_mode_tag—partner intake replays in mid-2026 reject orphan PDF annexes that cite AI issues without ticket IDs.

Lesson hero for AI red-team finding bind to mock audit deficiency tickets

Why this matters now (mid-2026 intake bind discipline)

Partners stopped accepting narrative-only AI annexes:

  • PDF lists “model found tuple drift” with no deficiency_ticket_id — fails replay next to Lesson 183 CSV exports.
  • Teams promote red-team text into FAQ rows but never open a ticket — Lesson 173 trend board shows zero recurrence while annex claims repeat issues.
  • failure_mode_tag free text from model output breaks Lesson 172 scoring allow-list — ingest jobs reject the whole sprint feed.

The fix is mechanical: finding_id → ticket_id → tag with a signed bind manifest in the evidence bundle.

Lesson objectives

You will implement:

  • red_team_finding_deficiency_bind — many-to-one link with audit columns
  • failure_mode_tag resolver from section_ref + severity (allow-list only)
  • forbid_promoted_finding_without_ticket application guard
  • red_team_bind_manifest.json export per publish_tuple_hash
  • Publish-gate extension red_team_unbound_promoted_finding
  • Optional auto-ticket stub for promoted blockers after human sign-off

Prerequisites

  • Lesson 180red_team_finding, human_sign_off_promotion, human-only lane
  • Lesson 172mock_audit_deficiency_ticket, failure_mode_tag allow-list
  • Lesson 183 — CSV export expects ticket IDs and tags
  • Lesson 173 — trend board aggregates by failure_mode_tag
  • Lesson 171 — active publish_tuple_hash on bind rows

failure_mode_tag allow-list (reuse Lesson 172)

Store in governance/failure_mode_tag_registry.json:

{
  "tags": [
    "tuple_hash_mismatch",
    "footer_schema_semver_drift",
    "epsilon_policy_breach",
    "sla_forecast_red",
    "faq_unbound_redline",
    "partner_reply_hash_drift",
    "ai_red_team_unmapped"
  ]
}

Rule: model output never writes tags directly. A bind resolver maps (section_ref, severity) → tag. Unknown pairs map to ai_red_team_unmapped and block publish until a human picks a registered tag.

red_team_finding_deficiency_bind

CREATE TABLE red_team_finding_deficiency_bind (
  bind_id                 UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  finding_id              UUID NOT NULL REFERENCES red_team_finding(finding_id),
  deficiency_ticket_id    UUID NOT NULL REFERENCES mock_audit_deficiency_ticket(deficiency_ticket_id),
  failure_mode_tag        TEXT NOT NULL,
  publish_tuple_hash      TEXT NOT NULL,
  bound_by_email          TEXT NOT NULL,
  bound_at_utc            TIMESTAMPTZ NOT NULL DEFAULT now(),
  bind_manifest_sha256    TEXT,
  UNIQUE (finding_id),
  CHECK (failure_mode_tag <> '')
);

CREATE OR REPLACE FUNCTION enforce_failure_mode_tag_allowlist()
RETURNS TRIGGER AS $$
BEGIN
  IF NOT EXISTS (
    SELECT 1 FROM failure_mode_tag_registry r
    WHERE r.tag = NEW.failure_mode_tag
  ) THEN
    RAISE EXCEPTION 'failure_mode_tag not in allow-list: %', NEW.failure_mode_tag;
  END IF;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Bind workflow (after human promotion)

Step Owner Action
1 Governance owner Confirm promotion_status = 'promoted' on finding
2 Scribe Create or select mock_audit_deficiency_ticket for same cert_window_id
3 Engineering Run resolver → propose failure_mode_tag; human confirms if ai_red_team_unmapped
4 Governance owner Insert red_team_finding_deficiency_bind
5 Engineering Regenerate red_team_bind_manifest.json; attach to packet
def bind_finding_to_ticket(
    finding_id: str,
    ticket_id: str,
    tag: str,
    tuple_hash: str,
    actor_email: str,
) -> None:
    assert tag in load_allowlist()
    assert finding_promoted(finding_id)
    insert_bind(finding_id, ticket_id, tag, tuple_hash, actor_email)
    manifest = build_bind_manifest(tuple_hash)
    write_evidence(manifest)

Forbid orphan promoted findings

-- Publish gate predicate (Lesson 171 extension)
-- block_reason = 'red_team_unbound_promoted_finding'
WHEN EXISTS (
  SELECT 1 FROM red_team_finding f
  JOIN governance_packet_red_team_run r USING (red_team_run_id)
  LEFT JOIN red_team_finding_deficiency_bind b USING (finding_id)
  WHERE r.publish_tuple_hash = :active_tuple
    AND f.promotion_status = 'promoted'
    AND f.severity IN ('blocker', 'major')
    AND b.bind_id IS NULL
);

Clears only when every promoted blocker/major has a bind row.

red_team_bind_manifest.json

{
  "schema": "red_team_bind_manifest_v1",
  "publish_tuple_hash": "c4e8…",
  "cert_window_id": "q1_2027_meta",
  "binds": [
    {
      "finding_id": "8f2a…",
      "deficiency_ticket_id": "91bc…",
      "failure_mode_tag": "tuple_hash_mismatch",
      "section_ref": "annex.partner_sla_table",
      "bound_at_utc": "2026-11-03T18:22:00Z"
    }
  ],
  "manifest_sha256": "…"
}

No PDF-only AI narrative in partner packets without matching binds[] entry.

Pairing with Lesson 183 CSV export

Export column source_red_team_finding_id optional on deficiency_export_v1:

deficiency_ticket_id,failure_mode_tag,source_red_team_finding_id,publish_tuple_hash
91bc…,tuple_hash_mismatch,8f2a…,c4e8…

Lesson 173 board then shows AI-sourced deficiencies beside tabletop-scored rows—same tag vocabulary.

Common mistakes

  • Pasting model text into annex PDF without bind row — intake replay failure.
  • Letting LLM choose failure_mode_tag strings — allow-list CHECK rejects ingest.
  • Binding before promoted — ticket exists but gate still red on pending_human.
  • Duplicate ticket per finding — UNIQUE (finding_id) prevents double bind; split findings instead.
  • Orphan tickets with no finding_id — allowed for tabletop-only issues; do not require reverse bind.

Troubleshooting

Symptom Likely cause Fix
Gate red_team_unbound_promoted_finding Missing bind on major Insert bind or demote finding
CSV export rejects row Free-text tag Pick registry tag
Partner asks for PDF proof Manifest not attached Export red_team_bind_manifest.json
Duplicate bind error Re-bind same finding Update ticket link via new finding row
Trend board spike on ai_red_team_unmapped Resolver gaps Add mapping row to registry + resolver table

Verification checklist

  • [ ] Promoted blocker without bind blocks publish
  • [ ] Unknown tag fails INSERT on bind table
  • [ ] Manifest SHA-256 in CI matches regenerated file
  • [ ] Lesson 183 export includes source_red_team_finding_id when bound
  • [ ] Rejected findings do not require binds

Mini exercise (30 minutes)

Promote one major finding from a Lesson 180 dry run. Create deficiency ticket with tag faq_unbound_redline. Insert bind. Export manifest. Deliberately omit bind on second promoted finding—confirm publish gate fires.

Continuity

  • Lesson 180 — red-team run and human promotion lane.
  • Lesson 172 — ticket schema and tabletop scoring.
  • Lesson 183 — machine-readable export for sprint feed.
  • Lesson 173 — recurrence uses shared failure_mode_tag.
  • Lesson 186 — panel roster quorum separate from AI bind path.
  • Next: Lesson 188 — sprint hardening closure receipt vs Lesson 173 variance gate.

FAQ

Do minor/info findings need binds?
No—only promoted blocker/major per publish gate.

Can one ticket bind multiple findings?
Yes—multiple findings, one ticket: use separate bind rows sharing deficiency_ticket_id (drop UNIQUE on ticket side only if product requires; default one finding per ticket).

What about PDF annexes already shipped?
Issue correction packet with manifest + new binds; do not edit historical PDF hashes silently.

Does this replace human tabletop scoring?
No. Tabletop creates tickets; red-team adds or links tickets for AI-discovered gaps.


Mid-2026 partners trust AI assistance when every critique has a ticket ID and a tag they can replay. Bind findings, export the manifest, and retire orphan PDFs.