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 :
- Notes en texte libre en Markdown, Word ou papier - ni lisibles par machine, ni validées par schéma, ni traçables
- Lignes Excel avec date, activité, heures - non versionnées, non ancrées cryptographiquement, non à l'épreuve des révisions
- Exports de logiciel de paie en PDF - non exploitables par machine (§ 147 Abs. 6 AO), non traçables de manière rétrograde/progressive
- Aucune identité unique - chaque entrée a un nom de fichier, mais le nom de fichier peut changer ; l'identité de l'entrée n'est pas stable
- Aucun ancrage temporel - la date figure comme chaîne de caractères dans le document, mais qui a réellement effectué la saisie, et quand ? L'heure de saisie n'est pas liée cryptographiquement à l'identité
Message clé
Une entrée de journal GitCover est un artefact JSON avec :
- 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)
- 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) - 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 - 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 lauuidV7(horodatage 48 bits), aucun champdatetime/dateséparé - Obligation de sidecar - chaque pièce justificative reçoit un sidecar
.v7g.mdavec sa propreuuidV7(acte de classification) et lauuidV7du document - Traçabilité rétrograde/progressive - résoluble de l'écriture comptable →
pièce justificative → entrée de journal →
uuidV7et 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/dateredondant : l'heure de saisie est ancrée dans lauuidV7elle-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)
(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 lauuidV7(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. LauuidV7seule constitue la DocID unique ; le Composite KeyV7GUID:uuidV7sert à l'organisation du classement et aux requêtes DB.
V7GUID - structure et horodatage 48 bits
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
uuidV7elle-même et ne peut pas être modifiée a posteriori sans invalider le GUID. Un champdatetimeséparé n'existe pas - le temps est contenu dans lauuidV7. 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
(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() :
- Parser la clé étrangère -
260815_0900→2026-08-15T09:00:00Z - Calculer l'horodatage 48 bits - millisecondes Unix depuis l'Epoch
- Générer la
uuidV7-Guid.CreateVersion7()avec horodatage explicite, le reste étant aléatoire - Composite Key -
V7GUID(Class issue de la Registry) :uuidV7(Object avec marque temporelle) - 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 :
(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 GUIDv7g_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 champdatetimeséparé - le temps est contenu dans les valeursuuidV7.
Traçabilité rétrograde et progressive
(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-Leitervs.GF) et lestags([\"fzul\", \"forschung\"]vs.[\"verwaltung\"]).
Ce qui va dans le dépôt Git - et ce qui n'y va pas
(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
- RFC 9562 §5.7 (UUIDv7) - horodatage Unix-ms 48 bits
- Spécification V7GUID -
work/OSS/TOP/.gitcover/specs/v7guid/(normatif) - DPMA Az. 10 2025 003 091.6 - brevet V7GUID (revendication principale 2, revendications 5–8)
- Outil OSS GitCover
UuidV7Gen-work/OSS/TOP/tools/UuidV7Gen/(Helpergen/decode) - GoBD (circulaire BMF, Rz. 146 - traçabilité, Rz. 147 - vérifiabilité)
- AO (§ 147 Abs. 2 - "maschinell auswertbar", § 147 Abs. 6 - accès aux données)
AFJD/agents/(anonymisé) - concept SSoT avec artefacts JSON, sidecars
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.