Reactive and knowledge-base-grounded; the agent drafts and flags, it never decides what goes back to the prospect.
Step 0 — Find what's new
# List messages in the questionnaire label/folder, newest first.
gmail.messages.list(label={{questionnaire_label}}, order="newest")
# Check each thread for an existing draft reply — a thread that already has
# one has already been worked.
gmail.drafts.list(thread=message.thread_id)
There is no ledger — this is a fresh session per questionnaire. The Gmail thread itself is the record of what's already been drafted. A thread with no draft is new; work the oldest unhandled one first.
Step 1 — Parse the questionnaire into individual questions
- Pull the attachment from the thread (SIG workbook, CAIQ, or custom spreadsheet).
- Open it with
google_sheetsand read every tab — some vendor formats spread sections (encryption, access control, incident response, …) across separate sheets. - Extract each row as a discrete question, keeping its section, row reference, and exact wording. Note the vendor format so the draft goes back in the same layout.
Step 2 — Match each question to the vetted knowledge base
Work question by question against our approved answers and policy docs, carried as skills and memory until the team updates them:
| Question topic | Vetted source | Typical confidence |
|---|---|---|
| Encryption at rest / in transit | .kortix/memory/security-answers.md#encryption |
High — exact match |
| SSO / access controls | .kortix/memory/security-answers.md#access-controls |
High |
| Incident response | .kortix/memory/security-answers.md#incident-response |
High |
| Data retention & deletion | .kortix/memory/security-answers.md#data-retention |
High |
| Subprocessors / sub-processing | .kortix/memory/security-answers.md#subprocessors |
Medium — verify the list is current |
| Compliance standards (SOC 2, ISO 27001, …) | .kortix/memory/security-answers.md#standards |
High |
| Anything with no matching entry | — | Flag for a person |
A "confident match" means the question maps clearly to one vetted entry. If a question is a close paraphrase of a vetted one, use the vetted answer as written; if it combines two topics, compose from the matching entries rather than inventing new language. If the knowledge base has no entry for a topic, that is a flag, not an opening to reason from first principles.
Step 3 — Draft the response in the vendor's own format
Write each matched answer into the corresponding cell/field of the SIG, CAIQ, or custom spreadsheet — same layout, same tab, same row it came in on. Use the vetted wording; adapt only for length or formatting the cell requires, never the substance. Work on a copy of the attachment, never the original file.
Step 4 — Flag low-confidence questions
For every question from Step 2 with no confident match, mark its row (a flag column, a comment, or the vendor's own "needs follow-up" field if it has one) and add it to a running list: row reference, the question text, and why it isn't answered from the vetted set.
Step 5 — Draft the reply, never send
Attach the filled spreadsheet to a Gmail draft reply on the original thread, addressed to the sender. Save it as a draft only — never call send. The subject and body should make clear a completed draft is attached and pending internal review.
Step 6 — Post to {{security_channel}} and stop
Post one message to {{security_channel}}: a link to the draft, the vendor format, and the flagged-question list (row reference + question, one line each). If nothing was flagged, say so explicitly rather than omitting the line. The run ends here — a person from security reviews the draft, answers the flagged questions, and sends it.