ED03 - Format du journal : artefacts JSON, signature temporelle, V7GUID, sidecars

Problème

Un entrepreneur souhaite tenir son journal dans Git - mais à quoi ressemble techniquement une entrée, exactement ? La pratique actuelle ne connaît aucune réponse contraignante :

Message clé

Une entrée de journal GitCover est un artefact JSON avec :

  1. JSON-Schema-First - le schéma existe avant l'entrée ; l'entrée est validée par rapport au schéma (Pre-Commit-Hook)
  2. V7GUID (Class Identifier) - classifie le document/l'action à partir du registre .gitcover (quoi/quel type) ; sert à l'organisation du classement et aux requêtes DB (p. ex. EF Core dans Vertical App)
  3. uuidV7 (Object ID) - constitue déjà à elle seule l'identité unique (DocID) de l'entrée, RFC 9562 §5.7, générée à partir d'une estampille temporelle prédéfinie (ou de now()) via le helper GitCover (UuidV7Gen), le reste étant complété de manière aléatoire
  4. Clé composite V7GUID:uuidV7 - la clé composite du sidecar GCPN, sert à l'organisation du classement et aux requêtes ; l'heure de saisie est contenue dans l'uuidV7 (horodatage 48 bits), pas de champ datetime/date séparé
  5. Obligation de sidecar - chaque pièce justificative reçoit un sidecar .v7g.md avec sa propre uuidV7 (acte de classification) et l'uuidV7 du document
  6. Traçabilité rétrograde/progressive - résoluble de l'écriture comptable → pièce justificative → entrée de journal → uuidV7 et en sens inverse

Conformité by Design : le format n'est pas ajouté après coup - il est structurel. Schéma, V7GUID (Class), uuidV7 (Object) et sidecar sont des champs obligatoires sans lesquels une entrée n'est pas intégrée au dépôt.

Pas de datetime/date redondant : l'heure de saisie est ancrée dans l'uuidV7 elle-même (horodatage 48 bits, RFC 9562 §5.7). Une représentation sous forme de chaîne de caractères distincte dans les schémas JSON ou les données JSON est redondante et n'est pas maintenue dans les faits. La représentation en chaîne pour le transfert DTO/HTMX relève de la responsabilité du Harness, pas de la couche des faits.

Le schéma JSON (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

Le schéma existe avant l'entrée - il est déposé dans .gitcover/schemas/diary-entry-1.0.schema.json et est versionné. Une entrée sans référence de schéma ($schema) est rejetée par le Pre-Commit-Hook.

Schéma diary-entry-1.0.schema.json (simplifié)

{
  "$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", "docUid",
    "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)"
    },
    "docUid": {
      "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" } }
  }
}

Important - pas de champ datetime/date : l'heure de saisie est contenue dans l'uuidV7 (horodatage 48 bits, RFC 9562 §5.7). Une représentation sous forme de chaîne distincte est redondante et n'est pas maintenue dans les faits. L'uuidV7 seule est la DocID unique ; la clé composite V7GUID:uuidV7 sert à l'organisation du classement et aux requêtes DB.

V7GUID - Structure et horodatage 48 bits

%%{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
Plage de bits Largeur Contenu Fixé par la RFC
0–47 48 bits Millisecondes Unix depuis l'époque (1970-01-01T00:00:00Z) Oui (RFC 9562 §5.7)
48–51 4 bits Version = 0111 (UUIDv7) Oui
52–63 12 bits rand_a (aléatoire) Non
64–65 2 bits Variant = 10 Oui
66–127 62 bits rand_b (aléatoire) Non

Principe central : la composante d'horodatage 48 bits (bits 0–47) fait autorité pour l'heure de saisie. Elle est ancrée dans l'uuidV7 elle-même et ne peut pas être modifiée a posteriori sans invalider le GUID. Un champ datetime séparé n'existe pas - le temps est contenu dans l'uuidV7. La représentation en chaîne pour DTO/HTMX relève de la responsabilité du Harness.

Extension GitCover : hiérarchie et IDs de processus dans les bits 66–127

La spécification GitCover V7GUID (work/OSS/TOP/.gitcover/specs/v7guid/) étend la composante aléatoire de 62 bits (bits 66–127) par une hiérarchie à 6 niveaux et des IDs de processus - il s'agit de l'extension protégée par brevet (DPMA Az. 10 2025 003 091.6) :

Plage de bits Largeur Contenu
66–89 6×4 bits Hiérarchie à 6 niveaux (L1–L6, 4 bits chacun)
90–97 8 bits ProcessTypeId (type de processus)
98–105 8 bits GatewayId (point de contrôle)
106–113 8 bits Status (masque de bits du cycle de vie)
114–121 8 bits Kind (nature de l'activité)
122–127 6 bits VariantId (classe de variante)

Remarque : cette extension n'est pas strictement nécessaire pour la série consacrée au journal - elle devient pertinente dans ED02 (Tenant/Sphère/Rôle) et dans des articles ultérieurs (Harness). Ici, la base RFC 9562 avec horodatage 48 bits suffit.

Routines helper : création d'un V7GUID à partir de clés externes

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

L'outil OSS GitCover UuidV7Gen (work/OSS/TOP/tools/UuidV7Gen/) fournit deux opérations centrales :

gen - générer une 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 - extraire l'horodatage d'une 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

Clé externe → uuidV7

Les routines helper permettent de créer une clé uuidV7 valide à partir d'une clé externe (p. ex. une estampille temporelle dans le nom de fichier comme 260815_0900_Lohnabrechnung.pdf) - avec une estampille temporelle prédéfinie, pas now() :

  1. Analyser la clé externe - 260815_0900 → 2026-08-15T09:00:00Z
  2. Calculer l'horodatage 48 bits - millisecondes Unix depuis l'époque
  3. Générer l'uuidV7 - Guid.CreateVersion7() avec horodatage explicite, reste complété de manière aléatoire
  4. Clé composite - V7GUID (Class issue du registre) : uuidV7 (Object avec estampille temporelle)
  5. Documentation GoBD - saisie en temps utile ancrée de manière traçable (pas de champ datetime séparé)

Lien avec GoBD : la saisie en temps utile (GoBD Rz. 146) est documentée cryptographiquement par l'horodatage 48 bits dans l'uuidV7 - et pas seulement affirmée. L'horodatage fait partie de l'identité, il n'est pas modifiable a posteriori.

Structure du sidecar (.v7g.md)

Chaque pièce justificative reçoit un sidecar avec deux références 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

Exemple de sidecar

{
  "$schema": "https://gitcover.org/schemas/v7g-sidecar-1.0.schema.json",
  "V7GUID": "<V7GUID-Class-aus-Registry>",
  "docUid": "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
  }
}

Clé composite V7GUID:uuidV7 :

  • V7GUID (champ 1) - Class Identifier du sidecar (classification issue du registre .gitcover : quoi/quel type)
  • uuidV7 (champ 2) - Object ID du sidecar lui-même, générée avec une estampille temporelle prédéfinie (quand la classification a-t-elle eu lieu ?), horodatage 48 bits ancré dans le GUID
  • v7g_taxonomy[].v7guid - Object ID du document classifié (quel document est classifié ?)
  • sha256 - identité du document original classifié — la preuve de l'originalité, toujours et partout (même si le document se déplace : indépendante du chemin et du nom, calculée à partir du contenu)

Ainsi, non seulement le document est identifié de manière univoque (via son SHA-256), mais aussi l'acte de classification lui-même - y compris le moment (dans l'uuidV7), qui a classifié et dans quel rôle. Pas de champ datetime séparé - le temps est contenu dans les valeurs uuidV7.

Traçabilité rétrograde et 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
Direction Point de départ Résolution via Cible
Rétrograde Écriture comptable (SKR04) source_sha256 → pièce justificative → uuidV7 Entrée de journal avec horodatage 48 bits
Progressive Pièce justificative (SHA-256) uuidV7 → entrée de journal → source_sha256 Écriture comptable → Grundbuch → bilan d'ouverture

Lien avec GoBD : la traçabilité rétrograde correspond à GoBD Rz. 146 (traçabilité). La traçabilité progressive correspond à GoBD Rz. 147 (vérifiabilité). Toutes deux sont garanties by Design par uuidV7 + SHA-256 - et non par des renvois croisés manuels.

Saisie du temps dans le journal

Chaque entrée de journal contient un tableau de saisie du temps :

Début Fin Heures Activité Tenant Sphère Source
0900 1200 3,0 Données de base du compte de paie ORG-1 ideell E1
1200 1400 2,0 R&D : spécification V7GUID ORG-1 ideell E1

Lien avec FZul : la saisie du temps est la base des justificatifs d'heures FZul (ED28). La séparation R&D vs. administration s'effectue via le champ role (F&E-Leiter vs. GF) et les tags (["fzul", "forschung"] vs. ["verwaltung"]).

Ce qui est intégré au dépôt Git - et ce qui ne l'est pas

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

Important : seuls les artefacts structurés (JSON, sidecars, pièces justificatives, schémas, dictionnaires) sont intégrés au dépôt Git. Les documents privés ne sont jamais intégrés au dépôt, sauf s'ils ont été expressément introduits et classifiés en tant qu'artefact. Il s'agit d'une exigence de protection des données et de conformité (RGPD, GoBD).

Levier de risque

Aujourd'hui (peu coûteux) Demain (à l'épreuve des audits) Risque atténué
Artefact JSON avec V7GUID (Class) + uuidV7 (Object) Heure de saisie ancrée cryptographiquement dans uuidV7 Contestation de l'heure de saisie
Validation par schéma (Pre-Commit) Seules les entrées valides sont intégrées au dépôt Violation du schéma, champs obligatoires incomplets
Sidecar avec clé composite V7GUID:uuidV7 Acte de classification traçable Contestation de la classification
source_sha256 par entrée Traçabilité rétrograde Contestation du rattachement de la pièce justificative
Helper UuidV7Gen pour les clés externes Saisie en temps utile documentée (estampille temporelle dans uuidV7) Violation GoBD « pas en temps utile »

Exigences Harness (aperçu)

Déductible de ED03 :

ID Exigence Priorité
FA-1.1 Entrées de journal en tant qu'artefacts JSON (Schema-First) MUST
FA-1.2 Clé composite V7GUID (Class) : uuidV7 (Object) - pas de champ datetime/date séparé ; temps dans l'uuidV7 (RFC 9562 §5.7) MUST
FA-1.3 uuidV7 générée à partir d'une estampille temporelle prédéfinie (pas now()) via un helper MUST
FA-2.1 SHA-256 comme ID de pièce justificative MUST
FA-2.2 Obligation de sidecar .v7g.md MUST
FA-2.3 Sidecar avec clé composite V7GUID:uuidV7 (Class + Object) MUST
FA-2.12 Traçabilité rétrograde (écriture → pièce justificative → uuidV7) MUST
FA-2.13 Traçabilité progressive (pièce justificative → écriture → Grundbuch) MUST
TA-1.5 Routines helper pour la création de V7GUID à partir de clés externes (UuidV7Gen) MUST
TA-1.6 Exigences temporelles GoBD documentées (horodatage 48 bits dans l'uuidV7) MUST
TA-2.1 Pre-Commit : validation du schéma JSON MUST

La liste complète des exigences dans Harness-Anforderungen.md.

Sources

Topologie des sources et liens de référence CDN

Rôle Emplacement Objectif
Primary / SSoT git.gitcover.org/GCC Emplacement canonique (signé GPG, versionné)
Public OSS Mirror / CDN codeberg.org/gitcover-commons Miroir en lecture seule ; découverte FLOSS
Community Hub github.com/gitcover-commons Issues & Discussions ; référence du code source sur Codeberg

Remarque : cette répartition entre sources, miroir et Community Hub reflète l'état actuel et peut changer. Veuillez vérifier la source canonique respective sur gitcover.org pour connaître l'état actuel.