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

Problème

Un entrepreneur veut 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 selon la Registry .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) - est déjà, à elle seule, l'identité unique (DocID) de l'entrée, RFC 9562 §5.7, générée à partir d'une marque temporelle prédéfinie (ou de now()) via le Helper GitCover (UuidV7Gen), le reste étant rempli de manière aléatoire
  4. Composite Key V7GUID:uuidV7 - le Composite Key du sidecar GCPN, sert à l'organisation du classement et aux requêtes ; l'heure de saisie est contenue dans la uuidV7 (horodatage 48 bits), aucun 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 la 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

Compliance by Design : le format n'est pas ajouté après coup - il est structurel. Le schéma, le V7GUID (Class), la uuidV7 (Object) et le sidecar sont des champs obligatoires, sans lesquels une entrée n'est pas admise dans le dépôt.

Pas de datetime/date redondant : l'heure de saisie est ancrée dans la uuidV7 elle-même (horodatage 48 bits, RFC 9562 §5.7). Une représentation sous forme de chaîne séparée dans les schémas JSON ou les données JSON est redondante et n'est pas consignée dans les faits. La représentation sous forme de 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 stocké 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", "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" } }
  }
}

Important - pas de champ datetime/date : l'heure de saisie est contenue dans la uuidV7 (horodatage 48 bits, RFC 9562 §5.7). Une représentation sous forme de chaîne séparée est redondante et n'est pas consignée dans les faits. La uuidV7 seule constitue la DocID unique ; le Composite Key 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 le RFC
0–47 48 bits Millisecondes Unix depuis l'Epoch (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 horodatage 48 bits (bits 0–47) fait autorité pour l'heure de saisie. Elle est ancrée dans la 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 la uuidV7. La représentation sous forme de chaîne pour DTO/HTMX est de la responsabilité du Harness.

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

La spécification V7GUID de GitCover (work/OSS/TOP/.gitcover/specs/v7guid/) étend la composante aléatoire de 62 bits (bits 66–127) avec 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 déclinaison)

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 utilitaires : création d'un V7GUID à partir de clés étrangères

%%{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ération d'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 - extraction de l'horodatage depuis 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é étrangère → uuidV7

Les routines utilitaires permettent de créer, à partir d'une clé étrangère (p. ex. un horodatage dans le nom de fichier comme 260815_0900_Lohnabrechnung.pdf), une clé uuidV7 valide - avec une marque temporelle prédéfinie, et non now() :

  1. Parser la clé étrangère - 260815_09002026-08-15T09:00:00Z
  2. Calculer l'horodatage 48 bits - millisecondes Unix depuis l'Epoch
  3. Générer la uuidV7 - Guid.CreateVersion7() avec horodatage explicite, le reste étant aléatoire
  4. Composite Key - V7GUID (Class issue de la Registry) : uuidV7 (Object avec marque temporelle)
  5. Documentation GoBD - saisie en temps utile ancrée de manière traçable (pas de champ datetime séparé)

Lien avec la GoBD : la saisie en temps utile (GoBD Rz. 146) est documentée cryptographiquement par l'horodatage 48 bits dans la 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>",
  "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 (champ 1) - Class Identifier du sidecar (classification issue de la Registry .gitcover : quoi/quel type)
  • uuidV7 (champ 2) - Object ID du sidecar lui-même, générée avec une marque 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é ?)

Ainsi, non seulement le document est identifié de manière unique, mais aussi l' acte de classification lui-même - y compris le moment (dans la 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 → journal général → bilan d'ouverture

Lien avec la GoBD : la traçabilité rétrograde correspond à la GoBD Rz. 146 (traçabilité). La traçabilité progressive correspond à la GoBD Rz. 147 (vérifiabilité). Toutes deux sont by Design garanties 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 maîtresses du compte de paie ORG-1 ideell E1
1200 1400 2,0 R&D : spécification V7GUID ORG-1 ideell E1

Lien avec la 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 va dans le dépôt Git - et ce qui n'y va 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 admis dans le dépôt Git. Les documents privés ne sont jamais admis dans le dépôt, sauf s'ils ont été expressément introduits comme artefacts et classifiés comme tels. Il s'agit d'une exigence de protection des données et de conformité (RGPD, GoBD).

Levier de risque

Aujourd'hui (à faible coût) Demain (à l'épreuve des révisions) Risque atténué
Artefact JSON avec V7GUID (Class) + uuidV7 (Object) Heure de saisie ancrée cryptographiquement dans la uuidV7 Contestation de l'heure de saisie
Validation par schéma (Pre-Commit) Seules les entrées valides entrent dans le dépôt Violation du schéma, champs obligatoires incomplets
Sidecar avec Composite Key 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 étrangères Saisie en temps utile documentée (marque temporelle dans la uuidV7) Violation de la GoBD "pas en temps utile"

Exigences Harness (aperçu)

Déductibles de ED03 :

ID Exigence Priorité
FA-1.1 Entrées de journal comme artefacts JSON (Schema-First) MUST
FA-1.2 Composite Key V7GUID (Class) : uuidV7 (Object) - aucun champ datetime/date séparé ; le temps est dans la uuidV7 (RFC 9562 §5.7) MUST
FA-1.3 uuidV7 générée à partir d'une marque temporelle prédéfinie (pas now()) via le 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 Composite Key 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 → journal général) MUST
TA-1.5 Routines utilitaires pour la création de V7GUID à partir de clés étrangères (UuidV7Gen) MUST
TA-1.6 Exigences temporelles GoBD documentées (horodatage 48 bits dans la 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
Primaire / SSoT git.gitcover.org/GCC Stockage canonique (signé GPG, versionné)
Miroir OSS public / CDN codeberg.org/gitcover-commons Miroir en lecture seule ; découverte FLOSS
Hub communautaire github.com/gitcover-commons Issues & Discussions ; référence du code source sur Codeberg

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