ED03 - Định dạng Nhật ký: JSON Artefact, Chữ ký thời gian, V7GUID, Sidecar

Vấn đề

Một doanh nhân muốn giữ nhật ký trong Git - nhưng chính xác một mục trông như thế nào về kỹ thuật? Thực hành hiện nay không có câu trả lời bắt buộc:

Tuyên bố cốt lõi

Một mục nhật ký GitCover là một JSON artefact với:

  1. JSON-Schema-First - schema tồn tại trước mục; mục được xác thực chống schema (Pre-Commit hook)
  2. V7GUID (Class Identifier) - phân loại tài liệu/hành động dựa trên registry .gitcover (cái gì/loại nào); phục vụ tổ chức lưu trữtruy vấn DB (ví dụ EF Core trong Vertical App)
  3. uuidV7 (Object ID) - đã một mình là danh tính duy nhất (DocID) của mục, RFC 9562 §5.7, tạo từ timestamp định sẵn (không phải now()) qua GitCover helper (UuidV7Gen), phần còn lại điền ngẫu nhiên
  4. Composite Key V7GUID:uuidV7 - GCPN Sidecar Composite Key, phục vụ tổ chức lưu trữ và truy vấn; thời điểm ghi nhận nằm trong uuidV7 (timestamp 48-bit), không có trường datetime/date riêng
  5. Nghĩa vụ sidecar - mỗi chứng từ nhận một sidecar .v7g.md với uuidV7 riêng (hành động phân loại) và uuidV7 của tài liệu
  6. Truy vết retrograde/progressive - giải quyết được từ booking → chứng từ → mục nhật ký → uuidV7 và ngược lại

Compliance by Design: Định dạng không phải sau sự cố - nó mang tính cấu trúc. Schema, V7GUID (Class), uuidV7 (Object) và sidecar là trường bắt buộc, không có chúng một mục không được nhận vào repo.

Không datetime/date dư thừa: Thời điểm ghi nhận được neo trong chính uuidV7 (timestamp 48-bit, RFC 9562 §5.7). Một biểu diễn chuỗi riêng trong JSON schema hoặc dữ liệu JSON là dư thừa và không được giữ trong facts. Biểu diễn chuỗi cho DTO/HTMX transfer là trách nhiệm của harness, không phải lớp facts.

JSON Schema (Schema-First)

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#FBFAF7','primaryTextColor':'#0F1B33','primaryBorderColor':'#6B7280','lineColor':'#6B7280'}}}%% flowchart LR S["JSON-Schema
(vor Eintrag)"] S --> V["Schema-Validierung
(Pre-Commit-Hook)"] E["Tagebucheintrag
(JSON)"] E --> V V -->|gültig| C["Commit akzeptiert
+ uuidV7 generiert"] V -->|ungültig| R["Commit abgelehnt
Schema-Verstoß"] style S fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style V fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style E fill:#0F1B33,stroke:#0F1B33,color:#FBFAF7 style C fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style R fill:#FDBA74,stroke:#C2410C,color:#0F1B33

Schema tồn tại trước mục - nó được lưu trong .gitcover/schemas/diary-entry-1.0.schema.json và có phiên bản. Một mục không có tham chiếu schema ($schema) bị Pre-Commit hook từ chối.

Schema diary-entry-1.0.schema.json (rút gọn)

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://gitcover.org/schemas/diary-entry-1.0.schema.json",
  "title": "GitCover Diary Entry v1.0",
  "type": "object",
  "required": [
    "$schema", "V7GUID", "uuidV7",
    "author", "role", "tenant", "sphere", "source"
  ],
  "properties": {
    "$schema": {
      "type": "string",
      "format": "uri",
      "const": "https://gitcover.org/schemas/diary-entry-1.0.schema.json"
    },
    "V7GUID": {
      "type": "string",
      "description": "Class Identifier - klassifiziert das Dokument/die Action aus .gitcover Registry (was/welcher Typ)"
    },
    "uuidV7": {
      "type": "string",
      "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
      "description": "Object ID - RFC 9562 §5.7 UUIDv7, generiert aus vorgegebener Zeitmarke (nicht now()), 48-Bit-Zeitstempel in der GUID verankert"
    },
    "author": { "type": "string", "description": "Akteur-Platzhalter, z. B. E1" },
    "role": {
      "type": "string",
      "enum": ["GF", "Buchhalter", "Lohnverantwortlicher", "F&E-Leiter", "Administrator"]
    },
    "tenant": { "type": "string", "description": "Tenant-Platzhalter, z. B. ORG-1" },
    "sphere": {
      "type": "string",
      "enum": ["ideell", "vermögensverwaltend", "zweckbetrieblich", "wirtschaftlich", "n/a"]
    },
    "source": {
      "type": "string",
      "enum": ["E1", "StB", "Notar", "Bank", "DRV", "FA", "BA", "VBG", "KK", "auto"]
    },
    "source_sha256": {
      "type": "string",
      "pattern": "^[0-9a-f]{64}$",
      "description": "SHA-256 des referenzierten Belegs (eindeutige Beleg-ID)"
    },
    "tags": { "type": "array", "items": { "type": "string" } }
  }
}

Quan trọng - không có trường datetime/date: Thời điểm ghi nhận nằm trong uuidV7 (timestamp 48-bit, RFC 9562 §5.7). Một biểu diễn chuỗi riêng là dư thừa và không được giữ trong facts. uuidV7 một mình là DocID duy nhất; Composite Key V7GUID:uuidV7 phục vụ tổ chức lưu trữ và truy vấn DB.

V7GUID - Cấu trúc và timestamp 48-bit

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#FBFAF7','primaryTextColor':'#0F1B33','primaryBorderColor':'#6B7280','lineColor':'#6B7280'}}}%% flowchart LR subgraph V7["V7GUID (128 Bit, RFC 9562 §5.7)"] direction LR T["timestamp_ms
Bits 0–47
48 Bit Unix-ms"] V["version
Bits 48–51
0111 (UUIDv7)"] R["rand_a
Bits 52–63
12 Bit"] VA["variant
Bits 64–65
10"] RB["rand_b
Bits 66–127
62 Bit"] end T --> DT["48-Bit-Zeitstempel
in uuidV7 verankert
(kein separates datetime-Feld)"] V7 --> ID["uuidV7-Feld
Object ID
(vorgegebene Zeitmarke)"] style T fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style V fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style R fill:#E5E7EB,stroke:#6B7280,color:#0F1B33 style VA fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style RB fill:#E5E7EB,stroke:#6B7280,color:#0F1B33 style DT fill:#0F1B33,stroke:#0F1B33,color:#FBFAF7 style ID fill:#0F1B33,stroke:#0F1B33,color:#FBFAF7
Khoảng bit Độ rộng Nội dung RFC-cố định
0-47 48 bit Unix mili-giây từ Epoch (1970-01-01T00:00:00Z) Có (RFC 9562 §5.7)
48-51 4 bit Version = 0111 (UUIDv7)
52-63 12 bit rand_a (ngẫu nhiên) Không
64-65 2 bit Variant = 10
66-127 62 bit rand_b (ngẫu nhiên) Không

Nguyên lý cốt lõi: Thành phần timestamp 48-bit (bit 0-47) là có thẩm quyền cho thời điểm ghi nhận. Nó được neo trong chính uuidV7 và không thể thay đổi sau đó mà không làm GUID vô hiệu. Trường datetime riêng không tồn tại - thời gian nằm trong uuidV7. Biểu diễn chuỗi cho DTO/HTMX là trách nhiệm của harness.

Mở rộng GitCover: Hierarchy và Process ID trong bit 66-127

GitCover V7GUID specification (work/OSS/TOP/.gitcover/specs/v7guid/) mở rộng thành phần ngẫu nhiên 62-bit (bit 66-127) với hierarchy 6 cấpprocess ID - đây là mở rộng được bảo hộ bằng sáng chế (DPMA Az. 10 2025 003 091.6):

Khoảng bit Độ rộng Nội dung
66-89 6×4 bit Hierarchy 6 cấp (L1-L6, mỗi cấp 4 bit)
90-97 8 bit ProcessTypeId (loại quy trình)
98-105 8 bit GatewayId (điểm kiểm soát)
106-113 8 bit Status (bitmask vòng đời)
114-121 8 bit Kind (loại hoạt động)
122-127 6 bit VariantId (lớp biến thể)

Lưu ý: Mở rộng này không bắt buộc cho loạt bài nhật ký - nó trở nên liên quan trong ED02 (Tenant/Sphere/Role) và các bài sau (Harness). Ở đây cơ sở RFC 9562 với timestamp 48-bit là đủ.

Helper Routine: Tạo V7GUID từ Foreign Key

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#FBFAF7','primaryTextColor':'#0F1B33','primaryBorderColor':'#6B7280','lineColor':'#6B7280'}}}%% flowchart LR F["Fremd-Key
(Zeitmarke in Daten
oder Dateiname)"] F --> H["Helper-Routine
UuidV7Gen"] H --> TS["48-Bit-Zeitstempel
extrahiert/konvertiert"] TS --> G["uuidV7 generiert
(Guid.CreateVersion7)"] G --> A["JSON-Artefakt
V7GUID + uuidV7"] A --> D["GoBD-Dokumentation
zeitnahe Erfassung
nachvollziehbar"] style F fill:#FDBA74,stroke:#C2410C,color:#0F1B33 style H fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style TS fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style G fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style A fill:#0F1B33,stroke:#0F1B33,color:#FBFAF7 style D fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33

GitCover OSS tool UuidV7Gen (work/OSS/TOP/tools/UuidV7Gen/) cung cấp hai thao tác trung tâm:

gen - tạo uuidV7 (guid)

# Erzeugt eine neue uuidV7 mit vorgegebener Zeitmarke (nicht now())
# Zeitmarke z. B. aus Dateiname "260815_0900" → ISO 2026-08-15T09:00:00Z
uuidv7gen gen "2026-08-15T09:00:00Z" "Tagebucheintrag-260815"

# Ausgabe: Label;UUID;ISO-Zeitstempel
# Tagebucheintrag-260815;019f2c6f-0900-7000-8000-000000000000;2026-08-15T09:00:00.000Z

decode - trích timestamp từ uuidV7

# Extrahiert den 48-Bit-Zeitstempel aus einer uuidV7
uuidv7gen decode 019f2c6f-0900-7000-8000-000000000000

# Ausgabe: ISO-8601-Zeitstempel
# 2026-08-15T09:00:00.000Z

Foreign Key → uuidV7

Helper routine cho phép tạo một khóa uuidV7 hợp lệ từ một foreign key (ví dụ timestamp trong tên tệp như 260815_0900_Lohnabrechnung.pdf) - với timestamp định sẵn, không phải now():

  1. Parse foreign key - 260815_09002026-08-15T09:00:00Z
  2. Tính timestamp 48-bit - Unix mili-giây từ Epoch
  3. Sinh uuidV7 - Guid.CreateVersion7() với timestamp tường minh, phần còn lại ngẫu nhiên
  4. Composite Key - V7GUID (Class từ registry) : uuidV7 (Object với timestamp)
  5. Tài liệu GoBD - ghi nhận kịp thời được neo truy vết (không có trường datetime riêng)

Tham chiếu GoBD: Ghi nhận kịp thời (GoBD Rz. 146) được tài liệu hóa mật mã bởi timestamp 48-bit trong uuidV7 - không chỉ tuyên bố. Timestamp là một phần của danh tính, không thể thay đổi sau.

Cấu trúc Sidecar (.v7g.md)

Mỗi chứng từ nhận một sidecar với hai tham chiếu uuidV7:

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#FBFAF7','primaryTextColor':'#0F1B33','primaryBorderColor':'#6B7280','lineColor':'#6B7280'}}}%% flowchart TD D["Dokument
(PDF, XML, EML)"] D --> S["Sidecar .v7g.md"] S --> U1["uuidV7 (eigen)
Identität des Sidecars
+ 48-Bit-Erfassungszeit"] S --> U2["uuidV7 (fremd)
Identität des Dokuments
in v7g_taxonomy"] S --> SH["sha256
Eindeutige Beleg-ID"] S --> T["v7g_taxonomy
Klassifizierung
(Tenant, Sphäre, Kategorie)"] S --> O["obsolescence
status: active/superseded/obsolete"] style D fill:#0F1B33,stroke:#0F1B33,color:#FBFAF7 style S fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style U1 fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style U2 fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style SH fill:#FDBA74,stroke:#C2410C,color:#0F1B33 style T fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style O fill:#E5E7EB,stroke:#6B7280,color:#0F1B33

Ví dụ Sidecar

{
  "$schema": "https://gitcover.org/schemas/v7g-sidecar-1.0.schema.json",
  "V7GUID": "<V7GUID-Class-aus-Registry>",
  "uuidV7": "019f2c6f-0900-7001-8000-000000000001",
  "sha256": "ab94670c0dd8499c27ef2feddd1d9c5925977d55a52150a6fca0f6567d2e5e79",
  "title": "260815_Lohnabrechnung.pdf",
  "original_filename": "260815_Lohnabrechnung.pdf",
  "locations": [
    {
      "unc_path": "./ORG-1/sources/lohn/260815_Lohnabrechnung.pdf",
      "from": "260815",
      "to": null,
      "note": "Primärspeicherort"
    }
  ],
  "v7g_taxonomy": [
    {
      "v7guid": "019f2c6f-0900-7000-8000-000000000000",
      "taxonomy": "ORG-1/Lohn",
      "valid_from": "260815",
      "valid_to": null,
      "note": "Tenant: ORG-1, Category: Lohn, Sphäre: ideell"
    }
  ],
  "gcpn": {
    "prima_nota_ref": null,
    "journal_entry_ref": null
  },
  "obsolescence": {
    "status": "active",
    "superseded_by": null,
    "superseded_at": null
  }
}

Composite Key V7GUID:uuidV7:

  • V7GUID (trường 1) - Class Identifier của sidecar (phân loại từ registry .gitcover: cái gì/loại nào)
  • uuidV7 (trường 2) - Object ID của chính sidecar, tạo với timestamp định sẵn (khi nào được phân loại?), timestamp 48-bit neo trong GUID
  • v7g_taxonomy[].v7guid - Object ID của tài liệu được phân loại (tài liệu nào đang được phân loại?)

Như vậy không chỉ tài liệu được định danh duy nhất, mà cả hành động phân loại - bao gồm thời điểm (trong uuidV7), ai phân loại và trong role nào. Không có trường datetime riêng - thời gian nằm trong các giá trị uuidV7.

Truy vết Retrograde và Progressive

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#FBFAF7','primaryTextColor':'#0F1B33','primaryBorderColor':'#6B7280','lineColor':'#6B7280'}}}%% flowchart LR subgraph RET["Retrograd (vom Buchungssatz rückwärts)"] direction LR B["Buchungssatz
(SKR04)"] B --> BE["Beleg
(SHA-256)"] BE --> DE["Tagebucheintrag
(uuidV7)"] DE --> V7["uuidV7
(48-Bit-Zeitstempel)"] end subgraph PRO["Progressiv (vom Beleg vorwärts)"] direction LR BE2["Beleg
(SHA-256)"] BE2 --> BS["Buchungssatz
(SKR04)"] BS --> GB["Grundbuch
(JSON)"] GB --> EB["Eröffnungsbilanz"] end style B fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style BE fill:#FDBA74,stroke:#C2410C,color:#0F1B33 style DE fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style V7 fill:#0F1B33,stroke:#0F1B33,color:#FBFAF7 style BE2 fill:#FDBA74,stroke:#C2410C,color:#0F1B33 style BS fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style GB fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style EB fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33
Hướng Bắt đầu Giải quyết qua Đích
Retrograde Booking (SKR04) source_sha256 → chứng từ → uuidV7 Mục nhật ký với timestamp 48-bit
Progressive Chứng từ (SHA-256) uuidV7 → mục nhật ký → source_sha256 Booking → sổ cái → bảng cân đối đầu kỳ

Tham chiếu GoBD: Truy vết retrograde tương ứng GoBD Rz. 146 (khả năng truy vết). Truy vết progressive tương ứng GoBD Rz. 147 (khả năng kiểm toán). Cả hai được đảm bảo by Design qua uuidV7 + SHA-256 - không qua tham chiếu chéo thủ công.

Ghi nhận thời gian trong nhật ký

Mỗi mục nhật ký chứa một bảng ghi nhận thời gian:

Bắt đầu Kết thúc Giờ Hoạt động Tenant Sphere Nguồn
0900 1200 3,0 Dữ liệu master tài khoản lương ORG-1 ideell E1
1200 1400 2,0 R&D: spec V7GUID ORG-1 ideell E1

Tham chiếu FZul: Ghi nhận thời gian là cơ sở cho ghi giờ FZul (ED18). Việc tách R&D vs. hành chính được thực hiện qua trường role (F&E-Leiter vs. GF) và tags (["fzul", "forschung"] vs. ["verwaltung"]).

Cái gì vào Git Repo - và cái gì không

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#FBFAF7','primaryTextColor':'#0F1B33','primaryBorderColor':'#6B7280','lineColor':'#6B7280'}}}%% flowchart TD IN["Ins Git-Repo"] IN --> J["JSON-Artefakte
(Tagebucheinträge, Grundbuch)"] IN --> S["Sidecars .v7g.md"] IN --> B["Belege
(PDF, XML, EML)"] IN --> SC["Schemata"] IN --> D["Dictionaries"] OUT["Nicht im Git-Repo"] OUT --> P["Private Dokumente
(sofern nicht ausdrücklich eingeführt)"] OUT --> T["Temporäre Dateien"] OUT --> C["Credentials/Secrets"] style IN fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style J fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style S fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style B fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style SC fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style D fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style OUT fill:#FDBA74,stroke:#C2410C,color:#0F1B33 style P fill:#FDBA74,stroke:#C2410C,color:#0F1B33 style T fill:#FDBA74,stroke:#C2410C,color:#0F1B33 style C fill:#FDBA74,stroke:#C2410C,color:#0F1B33

Quan trọng: Chỉ các artefact có cấu trúc (JSON, sidecar, chứng từ, schema, dictionary) mới được nhận vào Git repo. Tài liệu cá nhân không bao giờ được nhận vào repo trừ khi được giới thiệu và phân loại rõ ràng như artefact. Đây là yêu cầu bảo vệ dữ liệu và tuân thủ (GDPR, GoBD).

Đòn bẩy rủi ro

Hôm nay (rẻ) Ngày mai (kiểm toán được) Rủi ro giảm
JSON artefact với V7GUID (Class) + uuidV7 (Object) Thời điểm ghi nhận neo mật mã trong uuidV7 Tranh chấp thời điểm ghi nhận
Xác thực schema (Pre-Commit) Chỉ mục hợp lệ vào repo Vi phạm schema, trường bắt buộc thiếu
Sidecar với Composite Key V7GUID:uuidV7 Hành động phân loại truy vết được Tranh chấp phân loại
source_sha256 mỗi mục Truy vết retrograde Tranh chấp phân công chứng từ
Helper UuidV7Gen cho foreign key Ghi nhận kịp thời được tài liệu hóa (timestamp trong uuidV7) Vi phạm GoBD "không kịp thời"

Yêu cầu Harness (Xem trước)

Dẫn xuất từ ED03:

ID Yêu cầu Ưu tiên
FA-1.1 Mục nhật ký là JSON artefact (Schema-First) MUST
FA-1.2 Composite Key V7GUID (Class) : uuidV7 (Object) - không trường datetime/date riêng; thời gian trong uuidV7 (RFC 9562 §5.7) MUST
FA-1.3 uuidV7 tạo từ timestamp định sẵn (không now()) qua helper MUST
FA-2.1 SHA-256 làm ID chứng từ MUST
FA-2.2 Nghĩa vụ sidecar .v7g.md MUST
FA-2.3 Sidecar với Composite Key V7GUID:uuidV7 (Class + Object) MUST
FA-2.12 Truy vết retrograde (booking → chứng từ → uuidV7) MUST
FA-2.13 Truy vết progressive (chứng từ → booking → sổ cái) MUST
TA-1.5 Helper routine cho tạo V7GUID từ foreign key (UuidV7Gen) MUST
TA-1.6 Yêu cầu thời gian GoBD được tài liệu hóa (timestamp 48-bit trong uuidV7) MUST
TA-2.1 Pre-Commit: xác thực JSON schema MUST

Danh sách yêu cầu đầy đủ trong Harness-Anforderungen.md.

Nguồn

Topo nguồn và liên kết tham chiếu CDN

Role Địa điểm Mục đích
Primary / SSoT git.gitcover.org/GCC Lưu trữ chính thức (ký GPG, có phiên bản)
Public OSS Mirror / CDN codeberg.org/gitcover-commons Mirror chỉ đọc; khám phá FLOSS
Community Hub github.com/gitcover-commons Issues & Discussions; tham chiếu mã nguồn trên Codeberg

Lưu ý: Phân công nguồn, mirror và community hub này phản ánh trạng thái hiện tại và có thể thay đổi. Vui lòng kiểm tra nguồn chính thức tương ứng trên gitcover.org cho trạng thái hiện tại.