Summary
The CSV audit export omits the origin, record_kind, and metadata columns, even though they exist in the audit schema and are returned by the JSON export. This makes CSV exports inconsistent with JSON, and means a CSV export filtered by one of these fields has no column showing the value it was filtered on.
Background
origin / record_kind / metadata were added to the audit schema in #12 and are surfaced throughout the dashboard in #16 (Origin column, record-kind chips, channel/sender columns + filters). #16 also made /api/audit/export accept origin, record_kind, channel_id, and sender_id filter params. But the CSV column list was never updated to include the new fields.
Gap originates in #12 (columns added to the schema without CSV coverage); surfaced by the export filters added in #16. Tracked separately as it predates #16 and is out of its scope.
Current behaviour
- JSON export (
GET /api/audit/export?format=json) returns the full AuditRecord[], so origin/record_kind/metadata are present.
- CSV export (
?format=csv, used by both the dashboard API and the CLI export) is driven by a hardcoded header list that lacks all three: packages/proxy/src/audit/csv.ts — CSV_HEADERS (lines 8–33).
- Consequence:
GET /api/audit/export?format=csv&origin=openclaw correctly returns only OpenClaw rows, but the CSV has no origin column to show it — filterable but not visible. Same for record_kind and the metadata.channel_id / metadata.sender_id context.
Expected behaviour
CSV export should include origin, record_kind, and metadata so it matches the JSON export and the dashboard's column set.
Proposed fix
- Add
'record_kind', 'origin', and 'metadata' to CSV_HEADERS in packages/proxy/src/audit/csv.ts. recordToRow (lines 64–74) already JSON-stringifies object-typed values, so metadata serializes with no further change (and the existing formula-injection guard in csvEscape still applies).
- Update
packages/proxy/src/audit/csv.test.ts to assert the new columns.
- Update the "CSV Format" section of
docs/audit.md (note metadata serializes as a JSON string, like tool_input / evidence_chain).
Notes / decisions for the implementer
- Column ordering: appending the three new headers at the end of
CSV_HEADERS is the lowest-churn option and keeps existing column positions stable for any downstream consumers parsing by index.
- This is purely additive to the export surface; no schema or API-contract change.
Related
Summary
The CSV audit export omits the
origin,record_kind, andmetadatacolumns, even though they exist in the audit schema and are returned by the JSON export. This makes CSV exports inconsistent with JSON, and means a CSV export filtered by one of these fields has no column showing the value it was filtered on.Background
origin/record_kind/metadatawere added to the audit schema in #12 and are surfaced throughout the dashboard in #16 (Origin column, record-kind chips, channel/sender columns + filters). #16 also made/api/audit/exportacceptorigin,record_kind,channel_id, andsender_idfilter params. But the CSV column list was never updated to include the new fields.Gap originates in #12 (columns added to the schema without CSV coverage); surfaced by the export filters added in #16. Tracked separately as it predates #16 and is out of its scope.
Current behaviour
GET /api/audit/export?format=json) returns the fullAuditRecord[], so origin/record_kind/metadata are present.?format=csv, used by both the dashboard API and the CLI export) is driven by a hardcoded header list that lacks all three:packages/proxy/src/audit/csv.ts—CSV_HEADERS(lines 8–33).GET /api/audit/export?format=csv&origin=openclawcorrectly returns only OpenClaw rows, but the CSV has noorigincolumn to show it — filterable but not visible. Same forrecord_kindand themetadata.channel_id/metadata.sender_idcontext.Expected behaviour
CSV export should include
origin,record_kind, andmetadataso it matches the JSON export and the dashboard's column set.Proposed fix
'record_kind','origin', and'metadata'toCSV_HEADERSinpackages/proxy/src/audit/csv.ts.recordToRow(lines 64–74) already JSON-stringifies object-typed values, sometadataserializes with no further change (and the existing formula-injection guard incsvEscapestill applies).packages/proxy/src/audit/csv.test.tsto assert the new columns.docs/audit.md(notemetadataserializes as a JSON string, liketool_input/evidence_chain).Notes / decisions for the implementer
CSV_HEADERSis the lowest-churn option and keeps existing column positions stable for any downstream consumers parsing by index.Related