Skip to content
Back to skills

Agent Transcript Logging

ASecurity

Use this skill whenever writing any code that creates, stores, queries, exports, or retains AI agent conversation records in the Myelin platform. This defines the transcript entry schema, retention rules, demo namespace isolation, citation tracking, escalation linkage, and the audit export contract. Transcript writes must happen before responses are rendered to the learner. Load this skill before writing any agent logging, transcript query, or chat history code.

  • 666 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 8, 2026
ai-agentsjavascriptjavarailsdatabase

Security analysis

A100/100

Scanned October 8, 2026

npx -y skills add majiayu000/claude-skill-registry --skill agent-transcript-logging --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Agent Transcript Logging?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Agent Transcript Logging
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/majiayu000-agent-transcript-logging/badge)](https://www.skillsdirectory.com/skills/majiayu000-agent-transcript-logging)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
---
name: agent-transcript-logging
description: Use this skill whenever writing any code that creates, stores, queries, exports, or retains AI agent conversation records in the Myelin platform. This defines the transcript entry schema, retention rules, demo namespace isolation, citation tracking, escalation linkage, and the audit export contract. Transcript writes must happen before responses are rendered to the learner. Load this skill before writing any agent logging, transcript query, or chat history code.
---

This skill defines the transcript and audit logging contract for the Tau AI learning coach. Every message sent by a learner and every response generated by the agent must be logged before the response is delivered. Transcripts are audit-grade records — they cannot be edited, deleted, or delayed.

---

## Core Principle: Write Before Render

The agent response must not be rendered to the learner until the transcript entry is committed to the database. A failed transcript write is a blocking error — surface it to the session layer and retry before proceeding. Do not silently swallow transcript write failures.

```javascript
async function commitAndRender(transcriptEntry, renderFn) {
  try {
    await writeTranscriptEntry(transcriptEntry);
  } catch (err) {
    // Log the failure and retry once
    console.error('Transcript write failed:', err);
    await writeTranscriptEntry(transcriptEntry); // retry
    // If second attempt fails, throw — do not render without a committed transcript
  }
  renderFn(); // only reached if write succeeded
}
```

---

## Transcript Entry Schema

Every agent interaction — every learner message and every agent response — produces one transcript entry.

```json
{
  "transcriptId":   "uuid v4 — unique per entry",
  "sessionId":      "uuid — current learning/simulation session",
  "conversationId": "uuid — groups all entries for one continuous chat session",
  "learnerId":      "string — tenant-scoped identifier, never raw email",
  "tenantId":       "string — institution identifier",
  "timestamp":      "ISO 8601 UTC",

  "role":           "LEARNER | AGENT",
  "messageContent": "string — full message text (learner input or agent output)",

  "agentMode":      "TEXT | GUIDED_MEDIA | LIVE_CONVERSATIONAL | null",

  "simulationContext": {
    "activeModuleId":  "string | null",
    "activeModuleTitle": "string | null",
    "activeStepId":    "string | null — e.g. LOTO_STEP_03, or null if not in simulation",
    "attemptCount":    "integer | null"
  },

  "mediaReferences": [
    {
      "contentId":  "string — approved content registry ID",
      "title":      "string",
      "url":        "string"
    }
  ],

  "citedSources": ["string — approved URLs referenced in this response"],

  "policyFlag":     "ASSESSMENT_BOUNDARY | SAFETY_REDIRECT | DISTRESS | OUT_OF_SCOPE | null",

  "escalationId":   "uuid | null — links to escalation_queue entry if policyFlag is DISTRESS",

  "demoMode":       "boolean — true if this entry belongs to a demo session",

  "tokenUsage": {
    "promptTokens":     "integer | null",
    "completionTokens": "integer | null"
  }
}
```

### Required fields

All fields above are required. The following may be null when genuinely not applicable:
- `agentMode` (null for LEARNER role entries)
- `simulationContext.*` (null when learner is not in simulation)
- `mediaReferences` (empty array, not null, when no references)
- `citedSources` (empty array, not null)
- `policyFlag` (null when no policy trigger)
- `escalationId` (null unless DISTRESS flag fired)
- `tokenUsage.*` (null if not available from the LLM provider)

`messageContent` must never be null or empty string. An empty response from the agent is a system error — do not log it as a valid entry.

---

## Conversation Grouping

`conversationId` groups all entries that belong to one continuous chat session between the learner and the agent. It is separate from `sessionId` (the simulation/learning session).

Rules:
- A new `conversationId` is generated when the learner opens the coach panel for the first time in a session
- A new `conversationId` is generated if the learner closes and reopens the coach panel after more than 30 minutes of inactivity
- `conversationId` is preserved across mode switches within the same chat session
- The same `conversationId` must appear on both the learner's message entry and the agent's response entry for each exchange

---

## Retention Rules

Transcript retention is 2 years from the `timestamp` of each entry, per the platform data retention policy (PRD Section 12).

Retention is enforced by the tenant's data retention automation. The platform must:
1. Tag each entry with `retentionExpiresAt = timestamp + 2 years` at write time
2. Run a nightly retention job that archives (not deletes) entries past their expiry
3. Never delete a transcript entry that is linked to an open escalation (`escalationId` is non-null and escalation status is not RESOLVED or DISMISSED)

Demo transcripts (`demoMode: true`) follow the same 2-year retention rule but are stored in the demo namespace and excluded from learner-facing transcript views and all institution reporting.

---

## Demo Namespace Isolation

Demo transcript entries must be written to `demo_agent_transcripts`, not `agent_transcripts`. The two tables must never be joined in any query.

```javascript
async function writeTranscriptEntry(entry) {
  const table = entry.demoMode ? 'demo_agent_transcripts' : 'agent_transcripts';
  await db.query(
    `INSERT INTO ${table} (...fields) VALUES (...values)`,
    buildValues(entry)
  );
}
```

Live transcript queries (learner-facing chat history, lecturer review, audit exports) must always filter `WHERE demo_mode = false` as a structural safeguard, even if the query is scoped to a live tenant.

---

## Escalation Linkage

When a `policyFlag: 'DISTRESS'` event fires, the transcript entry and the escalation queue entry must be linked:

1. Write the escalation queue entry first (see `agent-safety-guardrails` skill for escalation schema)
2. Capture the returned `escalationId`
3. Include `escalationId` in the transcript entry
4. Write the transcript entry

```javascript
async function handleDistressEvent(message, context, agentResponse) {
  // 1. Write escalation first
  const escalation = await writeEscalationEntry({
    sessionId:       context.sessionId,
    learnerId:       context.learnerId,
    tenantId:        context.tenantId,
    triggerMessage:  message,
    escalationType:  'DISTRESS',
    timestamp:       new Date().toISOString(),
    slaDeadline:     new Date(Date.now() + 15 * 60 * 1000).toISOString()
  });

  // 2. Write learner message transcript entry with escalation link
  await writeTranscriptEntry({
    ...buildLearnerEntry(message, context),
    policyFlag:   'DISTRESS',
    escalationId: escalation.escalationId
  });

  // 3. Write agent response transcript entry with escalation link
  await writeTranscriptEntry({
    ...buildAgentEntry(agentResponse, context),
    policyFlag:   'DISTRESS',
    escalationId: escalation.escalationId
  });

  // 4. Render response
  renderResponseToUI(agentResponse);
}
```

---

## Transcript Queries

### Learner-facing chat history

```javascript
async function getLearnerTranscripts(session, limit = 40) {
  await db.query(`SET LOCAL app.current_tenant_id = '${session.tenantId}'`);
  return db.query(`
    SELECT transcript_id, role, message_content, agent_mode,
           simulation_context, media_references, cited_sources, timestamp
    FROM agent_transcripts
    WHERE tenant_id = $1
      AND learner_id = $2
      AND conversation_id = $3
      AND demo_mode = false
    ORDER BY timestamp ASC
    LIMIT $4
  `, [session.tenantId, session.learnerId, session.conversationId, limit]);
}
```

### Lecturer review — all transcripts for a cohort learner

```javascript
async function getLecturerTranscriptView(lecturerSession, learnerId) {
  requireRole(lecturerSession, ['LECTURER', 'ADMIN']);
  await db.query(`SET LOCAL app.current_tenant_id = '${lecturerSession.tenantId}'`);
  return db.query(`
    SELECT t.transcript_id, t.role, t.message_content, t.agent_mode,
           t.simulation_context, t.policy_flag, t.escalation_id, t.timestamp
    FROM agent_transcripts t
    WHERE t.tenant_id = $1
      AND t.learner_id = $2
      AND t.demo_mode = false
    ORDER BY t.timestamp DESC
  `, [lecturerSession.tenantId, learnerId]);
}
```

Lecturers may only query transcripts for learners in their assigned cohorts. The cohort membership check must be applied before this query is executed.

### Audit export — full transcript pack for a session

```javascript
async function getAuditTranscriptPack(adminSession, targetSessionId) {
  requireRole(adminSession, ['ADMIN', 'SUPER_ADMIN']);
  await db.query(`SET LOCAL app.current_tenant_id = '${adminSession.tenantId}'`);
  return db.query(`
    SELECT *
    FROM agent_transcripts
    WHERE tenant_id = $1
      AND session_id = $2
      AND demo_mode = false
    ORDER BY timestamp ASC
  `, [adminSession.tenantId, targetSessionId]);
}
```

Audit export packs must include all entries for the session in chronological order, with `policyFlag`, `escalationId`, and `citedSources` fields preserved.

---

## PII Handling in Transcript Content

`messageContent` contains free-text from the learner. It may contain PII — names, contact details, personal circumstances. The following rules apply:

- Do not index `messageContent` in any full-text search system accessible to staff dashboards — it is a raw audit record, not a searchable knowledge base
- Redact `messageContent` in any analytics aggregation or reporting export that is not an explicit audit request
- If a learner submits a data subject access request, their transcripts must be included in the export
- If a learner revokes consent, transcripts are retained (they are audit records) but must be flagged with `consentRevokedAt` and excluded from any active coaching context

---

## Token Usage Tracking

`tokenUsage` is logged for cost monitoring and capacity planning. It is not exposed to learners or lecturers. It is available to Super Admin and in operational dashboards only.

If the LLM provider does not return token counts in the response, set both fields to null — do not estimate.

---

## What Not To Do

- Do not render a response before the transcript entry is committed
- Do not silently catch transcript write failures — retry once, then throw
- Do not write demo transcripts to the live `agent_transcripts` table
- Do not allow `messageContent` to be null or empty string
- Do not link an escalation entry to a transcript entry in the wrong order — escalation is written first, then transcript
- Do not expose `messageContent` in staff-facing analytics aggregates
- Do not delete transcript entries linked to open escalations
- Do not omit `demoMode` from any transcript entry — it must be an explicit boolean
- Do not query `agent_transcripts` without a `demo_mode = false` filter in live contexts

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.