전체 문서
이 페이지 목차
지속 파일 schema 참조
Norma의 durable primitives는 relational/graph DB가 아니라 JSONL, Markdown과 JSON 파일이다. 이 페이지는 production writer/reader가 실제 사용하는 on-disk shape를 열거한다. 파일 permission은 대부분 0644, directory는 0755이며 encryption, fsync, cross-process lock, signature는 없다.
Transcript JSONL
Transcript JSONL · 관계
이 대상을 사용하는 기능·모듈·계약 · 2개
- 계약 사용 · compaction, transcript, memory · transcript JSONL과 memory Markdown/index 파일 계약을 소비 · transcript-record · memory-schema
- 계약 사용 · noa와 noaadapter · Noa state v1과 tier archive 파일 계약을 소비 · noa-state-schema · noa-archive
main path는 <dir>/<sessionID>.jsonl, subagent는 <dir>/<sessionID>/subagents/agent-<agentID>.jsonl이다. 한 줄은 transcript.Record의 다음 10개 JSON field다.
| JSON field | type | presence/meaning |
|---|---|---|
uuid |
string | writer가 생성한 128-bit random hex(rare random failure 시 timestamp hex) |
parent_uuid |
string | 이전 성공 append record; 첫 record에는 생략 |
session_id |
string | writer identity; host 제공 값 검증 없음 |
agent_id |
string | sidechain agent id, main에서는 생략 |
is_sidechain |
bool | sidechain일 때 true, otherwise 생략 |
timestamp |
RFC3339Nano string | UTC write time |
type |
string | message 또는 boundary |
message |
llm.Message pointer |
message record에서 role+content; boundary면 보통 생략 |
usage |
llm.Usage pointer |
zero가 아닌 assistant-turn usage만 |
boundary |
llm.BoundaryMeta pointer |
boundary record만 |
Nested variants
| nested object | fields/variants |
|---|---|
message |
role; content[] |
content[] common |
type: text/thinking/tool_use/tool_result/compact_boundary/skill; variant fields는 아래와 같음 |
| text | text |
| thinking | thinking, optional signature |
| tool use | id, name, raw JSON input |
| tool result | tool_use_id, nested content[], is_error |
| compact boundary | raw input이 BoundaryMeta object 또는 legacy bare pre-token integer |
usage |
input_tokens, output_tokens, cache_read_tokens, cache_write_tokens |
boundary |
optional trigger, required-value pre_tokens, optional messages_summarized, active_skills[] |
writer는 append error의 첫 값을 Writer.Err()에 남기고 in-memory loop를 계속한다. loader는 missing file을 empty로 취급하지만 한 줄이라도 malformed JSON이면 전체 load가 error이고 corrupt tail repair는 없다. Messages는 boundary record와 nil message를 건너뛰고 usage를 더한다. Session.Resume은 trailing unanswered assistant tool-use messages를 버리고 새 record를 last UUID 뒤에 잇는다. record 근거 nested message 근거 boundary 근거
Memory Markdown
각 memory는 <memory-dir>/<slug(name)>.md, index는 MEMORY.md다.
---
name: <unescaped single-line value>
description: <unescaped single-line value>
type: user | feedback | project | reference # empty면 line 생략
---
<Markdown body>
| 항목 | writer/reader semantics |
|---|---|
| slug | name lowercase → ASCII regex [^a-z0-9]+를 -로 치환 → 앞뒤 - 제거; Unicode-only/충돌 name은 empty 또는 같은 path가 될 수 있음 |
| type | 네 상수는 tool prompt convention이며 parser가 unknown value를 거부하지 않음 |
| frontmatter | YAML library를 쓰지 않고 line을 첫 :에서 나눔; quoting/escaping/nested YAML 없음 |
| malformed/no frontmatter | 전체 파일을 content로, name은 filename stem fallback |
| scan | MEMORY.md와 subdir를 제외한 .md; parse/read error file은 조용히 skip; modtime newest first |
| index | Save 뒤 전체 scan으로 rebuild; memory file write와 index write는 transaction이 아님 |
| relevance | ASCII [a-z0-9_]+ token substring score; name/type 3, description 2, body 1, recency tie-break |
Noa state.json v1
path는 <ArchiveBaseDir>/<SessionID>/state.json; writer는 같은 directory temp file을 0644로 닫고 rename한다.
| object | JSON fields |
|---|---|
| root | version(반드시 1), sessionId, nextBlockId, blocks[], messageRefs, optional tokenSnapshot, nudge, stats, optional derivedFrom |
blocks[] |
blockId, tier, optional topic, summary, optional directMessageIds[], effectiveMessageIds[], directBlockIds[], archivePath, archiveRel, compressedTokens, startRef, endRef, createdAt, active, compressCallId |
messageRefs |
byRaw raw-id→ref map, byRef ref→raw-id map |
nudge |
lastPerMessageNudgeTokens, lastNudgeShownTokens, optional lastShownByTier string-tier→token map |
stats |
tokensCompressed, compressionCount |
missing file은 initial state다. malformed JSON, I/O error, 또는 version != 1은 Load error와 initial state를 반환한다. noaadapter.newSession은 warning 후 empty state로 계속하며 자동 rebuild하지 않는다. 명시적 RebuildFromHistory만 transcript의 recorded Compress calls를 replay하고 기존 archive를 재사용해 state를 저장한다. inherited state search는 최대 8 ancestor를 보고 corrupt/empty ancestor를 건너뛴다. state 근거 rebuild 근거
Noa archive Markdown
path는 <root>/tier<N>/<blockID>_<startRef>-<endRef>.md다. 파일 단위 temp+rename이며 archive와 state.json은 하나의 transaction이 아니다.
| section | format |
|---|---|
| frontmatter required | block, tier, range, original_tokens |
| frontmatter optional | session, created, topic; tier 1은 message_count, upper tier는 consumed_blocks: [...]와 loose_messages |
| heading | archive id/tier/ref span |
| active summary | blockquote로 표시 |
| raw entries | text/thinking 원문, tool-use JSON과 tool-result를 collision-safe Markdown fence로 truncate 없이 기록 |
| absorbed block | child summary, relative archive link, absolute path |
archive reader schema/parser는 없다. Materialize는 state를 이용해 messages를 prune할 뿐 archive 파일을 다시 읽지 않으며, missing/corrupt archive를 자동 감지하지 않는다. replay archiver는 같은 expected path가 존재하면 내용 검증 없이 채택한다. 따라서 archive 무결성·future migration·dangling state pointer는 host audit 대상이다. archive format 근거 file writer 근거 materialize 근거
Writer와 consumer 연결
| artifact | writer | primary consumers |
|---|---|---|
| transcript main | agentcore.Session.Prompt recorder |
Session.Resume, offline audit, Noa RebuildFromHistory input |
| transcript sidechain | subagent.run recorder |
offline child accounting; parent totals에 자동 합산 안 됨 |
| memory file/index | RecordMemory/Store.Save |
RecallMemory, auto-inject, system index |
state.json |
Noa adapter after state changes/rebuild | Session.View, Materialize, inheritance |
| archive Markdown | ApplyCompression through file archiver |
human/Read-tool recovery path; state stores pointer |