ED03 - Định dạng nhật ký: JSON-Artifacts, chữ ký thời gian, V7GUID, Sidecars

Vấn đề

Một doanh nhân muốn quản lý nhật ký của mình trong Git - nhưng về mặt kỹ thuật, một mục ghi trông chính xác như thế nào? Thực tiễn hiện nay chưa có câu trả lời ràng buộc nào:

Nội dung cốt lõi

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

  1. JSON-Schema-First - schema tồn tại trước mục ghi; mục ghi được xác thực đối chiếu với schema (Pre-Commit-Hook)
  2. V7GUID (Class Identifier) - phân loại tài liệu/hành động dựa trên .gitcover Registry (cái gì/loại nào); phục vụ tổ chức lưu trữDB-Query (ví dụ: EF Core trong Vertical App)
  3. uuidV7 (Object ID) - bản thân nó đã là danh tính duy nhất (DocID) của mục ghi, RFC 9562 §5.7, được tạo từ một mốc thời gian định trước (hoặc now()) thông qua GitCover Helper (UuidV7Gen), phần còn lại được điền bằng giá trị 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 (dấu thời gian 48 bit), không có trường datetime/date riêng biệt
  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. Khả năng truy vết hồi tố/xuôi - từ bút toán → chứng từ → mục nhật ký → uuidV7 và phân giải được theo chiều ngược lại

Compliance by Design: Định dạng này không phải là thứ bổ sung sau - nó mang tính cấu trúc. Schema, V7GUID (Class), uuidV7 (Object) và Sidecar là các trường bắt buộc; thiếu chúng, mục ghi sẽ không được đưa vào Repo.

Không có datetime/date dư thừa: Thời điểm ghi nhận được neo ngay trong uuidV7 (dấu thời gian 48 bit, RFC 9562 §5.7). Một biểu diễn chuỗi riêng biệt trong JSON-Schema hoặc dữ liệu JSON là dư thừa và không được duy trì trong dữ liệu thực. Biểu diễn chuỗi phục vụ truyền DTO/HTMX là trách nhiệm của Harness, không thuộc tầng dữ liệu thực.

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 ghi - nó được lưu tại .gitcover/schemas/diary-entry-1.0.schema.json và được quản lý phiên bản. Mục ghi không có tham chiếu schema ($schema) sẽ bị Pre-Commit-Hook từ chối.

Schema diary-entry-1.0.schema.json (đơn giản hóa)

{
  "$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 (dấu thời gian 48 bit, RFC 9562 §5.7). Một biểu diễn chuỗi riêng biệt là dư thừa và không được duy trì trong dữ liệu thực. Một mình uuidV7 đã là DocID duy nhất; Composite Key V7GUID:uuidV7 phục vụ tổ chức lưu trữ và DB-Query.

V7GUID - Cấu trúc và dấu thời gian 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
Dải bit Độ rộng Nội dung RFC cố định
0–47 48 bit Số mili giây Unix kể 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 tắc cốt lõi: Thành phần dấu thời gian 48 bit (các bit 0–47) là thành phần có thẩm quyền đối với thời điểm ghi nhận. Nó được neo ngay trong uuidV7 và không thể bị thay đổi sau này nếu không làm GUID trở nên vô hiệu. Không tồn tại trường datetime riêng biệt - thời gian nằm trong uuidV7. Biểu diễn chuỗi cho DTO/HTMX là trách nhiệm của Harness.

Phần mở rộng GitCover: Hệ thống phân cấp và Process-ID trong các bit 66–127

Đặc tả V7GUID của GitCover (work/OSS/TOP/.gitcover/specs/v7guid/) mở rộng thành phần ngẫu nhiên 62 bit (các bit 66–127) bằng một hệ thống phân cấp 6 cấpcác Process-ID - đây chính là phần mở rộng được bảo hộ bằng bằng sáng chế (DPMA Az. 10 2025 003 091.6):

Dải bit Độ rộng Nội dung
66–89 6×4 bit Hệ thống phân cấp 6 cấp (L1–L6, mỗi cấp 4 bit)
90–97 8 bit ProcessTypeId (loại tiến 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 ý: Phần mở rộng này không bắt buộc đối với loạt bài về nhật ký - nó sẽ trở nên có liên quan trong ED02 (Tenant/Sphäre/Rolle) và các bài viết sau này (Harness). Ở đây, nền tảng RFC 9562 với dấu thời gian 48 bit là đủ.

Các Helper-Routine: Tạo V7GUID từ khóa ngoại

%%{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

Công cụ OSS GitCover UuidV7Gen (work/OSS/TOP/tools/UuidV7Gen/) cung cấp hai thao tác cốt lõi:

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 xuất dấu thời gian 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

Khóa ngoại → uuidV7

Các Helper-Routine cho phép tạo một khóa uuidV7 hợp lệ từ một khóa ngoại (ví dụ: một mốc thời gian trong tên tệp như 260815_0900_Lohnabrechnung.pdf) - với mốc thời gian định trước, chứ không phải now():

  1. Phân tích khóa ngoại - 260815_09002026-08-15T09:00:00Z
  2. Tính toán dấu thời gian 48 bit - số mili giây Unix kể từ Epoch
  3. Tạo uuidV7 - Guid.CreateVersion7() với dấu thời gian tường minh, phần còn lại bằng giá trị ngẫu nhiên
  4. Composite Key - V7GUID (Class từ Registry) : uuidV7 (Object mang mốc thời gian)
  5. Tài liệu hóa GoBD - việc ghi nhận kịp thời được neo một cách có thể kiểm chứng (không có trường datetime riêng biệt)

Liên hệ GoBD: Việc ghi nhận kịp thời (GoBD Rz. 146) được tài liệu hóa bằng mật mã thông qua dấu thời gian 48 bit trong uuidV7 - chứ không chỉ là lời khẳng định. Dấu thời gian là một phần của danh tính, không thể bị thay đổi sau này.

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ừ .gitcover Registry: cái gì/loại nào)
  • uuidV7 (trường 2) - Object ID của chính Sidecar, được tạo với mốc thời gian định trước (đã phân loại vào lúc nào?), dấu thời gian 48 bit được 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ận diện duy nhất mà cả hành động phân loại tự thân - bao gồm cả thời điểm (trong uuidV7), ai đã phân loại và với vai trò nào. Không có trường datetime riêng - thời gian nằm trong các giá trị uuidV7.

Khả năng truy vết hồi tố và xuôi

%%{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
Chiều Điểm xuất phát Phân giải qua Đích đến
Hồi tố Bút toán (SKR04) source_sha256 → chứng từ → uuidV7 Mục nhật ký với dấu thời gian 48 bit
Xuôi Chứng từ (SHA-256) uuidV7 → mục nhật ký → source_sha256 Bút toán → Grundbuch → bảng cân đối mở đầu

Liên hệ GoBD: Khả năng truy vết hồi tố tương ứng với GoBD Rz. 146 (tính truy vết). Khả năng truy vết xuôi tương ứng với GoBD Rz. 147 (khả năng kiểm toán). Cả hai đều được bảo đảm ngay từ thiết kế (by Design) nhờ uuidV7 + SHA-256 - chứ không phải nhờ các 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 Số giờ Hoạt động Tenant Sphäre Nguồn
0900 1200 3,0 Dữ liệu gốc tài khoản lương ORG-1 ideell E1
1200 1400 2,0 F&E: Đặc tả V7GUID ORG-1 ideell E1

Liên hệ FZul: Việc ghi nhận thời gian là cơ sở cho các chứng nhận giờ FZul (ED18). Việc tách biệt giữa F&E và công việc hành chính được thực hiện qua trường role (F&E-Leiter so với GF) và các tags (["fzul", "forschung"] so với ["verwaltung"]).

Những gì được đưa vào Git-Repo - và những 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ỉ những artifact có cấu trúc (JSON, Sidecar, chứng từ, schema, từ điển) mới được đưa vào Git-Repo. Tài liệu riêng tư không bao giờ được đưa vào Repo, trừ khi chúng đã được đưa vào một cách tường minh dưới dạng artifact và được phân loại. Đây là yêu cầu về bảo vệ dữ liệu và tuân thủ (DSGVO, GoBD).

Đòn bẩy rủi ro

Hôm nay (chi phí thấp) Ngày mai (an toàn kiểm toán) Rủi ro được giảm thiểu
JSON-Artifact với V7GUID (Class) + uuidV7 (Object) Thời điểm ghi nhận được neo bằng mật mã trong uuidV7 Phủ nhận thời điểm ghi nhận
Xác thực schema (Pre-Commit) Chỉ các mục hợp lệ mới được đưa vào Repo Vi phạm schema, trường bắt buộc không đầy đủ
Sidecar với Composite Key V7GUID:uuidV7 Hành động phân loại có thể kiểm chứng Phủ nhận việc phân loại
source_sha256 cho mỗi mục ghi Khả năng truy vết hồi tố Phủ nhận việc gán chứng từ
Helper UuidV7Gen cho khóa ngoại Ghi nhận kịp thời được tài liệu hóa (mốc thời gian trong uuidV7) Vi phạm GoBD "không kịp thời"

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

Có thể suy ra từ ED03:

ID Yêu cầu Mức ưu tiên
FA-1.1 Các mục nhật ký dưới dạng JSON-Artifact (Schema-First) MUST
FA-1.2 Composite Key V7GUID (Class) : uuidV7 (Object) - không có trường datetime/date riêng; thời gian nằm trong uuidV7 (RFC 9562 §5.7) MUST
FA-1.3 uuidV7 được tạo từ mốc thời gian định trước (không phải 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 Khả năng truy vết hồi tố (bút toán → chứng từ → uuidV7) MUST
FA-2.13 Khả năng truy vết xuôi (chứng từ → bút toán → Grundbuch) MUST
TA-1.5 Helper-Routine tạo V7GUID từ khóa ngoại (UuidV7Gen) MUST
TA-1.6 Yêu cầu thời gian của GoBD được tài liệu hóa (dấu thời gian 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 đủ nằm trong Harness-Anforderungen.md.

Nguồn

Topology nguồn và các liên kết tham chiếu CDN

Vai trò Địa điểm Mục đích
Primary / SSoT git.gitcover.org/GCC Kho lưu trữ chính tắ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 ý: Cách phân bổ này giữa nguồn, Mirror và Community-Hub phản ánh hiện trạng hiện tại và có thể thay đổi. Vui lòng kiểm tra nguồn chính tắc tương ứng trên gitcover.org để biết trạng thái mới nhất.