ED03 - Formato de diario: artefactos JSON, firma temporal, V7GUID, sidecars

Problema

Un empresario quiere llevar su diario en Git - pero ¿cómo se ve exactamente una entrada a nivel técnico? La práctica actual no conoce una respuesta vinculante:

Idea central

Una entrada de diario de GitCover es un artefacto JSON con:

  1. JSON-Schema-First - el esquema existe antes de la entrada; la entrada se valida contra el esquema (Pre-Commit-Hook)
  2. V7GUID (Class Identifier) - clasifica el documento/la Action según la Registry .gitcover (qué/qué tipo); sirve para la organización de archivado y las consultas a la DB (p. ej., EF Core en Vertical App)
  3. uuidV7 (Object ID) - ya es por sí sola la identidad única (DocID) de la entrada, RFC 9562 §5.7, generada a partir de una marca de tiempo predefinida (o now()) vía helper de GitCover (UuidV7Gen), el resto se rellena con aleatoriedad
  4. Composite Key V7GUID:uuidV7 - la clave compuesta del sidecar GCPN, sirve para la organización de archivado y las consultas; el tiempo de registro está contenido en la uuidV7 (marca de tiempo de 48 bits), sin campo datetime/date separado
  5. Obligación de sidecar - cada comprobante recibe un sidecar .v7g.md con su propia uuidV7 (acto de clasificación) y la uuidV7 del documento
  6. Trazabilidad retrógrada/progresiva - desde el asiento contable → comprobante → entrada de diario → uuidV7 y de vuelta, todo resoluble

Compliance by Design: el formato no se añade a posteriori - es estructural. El esquema, el V7GUID (Class), el uuidV7 (Object) y el sidecar son campos obligatorios sin los cuales una entrada no se incorpora al repo.

Sin datetime/date redundante: el tiempo de registro está anclado en la propia uuidV7 (marca de tiempo de 48 bits, RFC 9562 §5.7). Una representación String separada en esquemas JSON o datos JSON es redundante y no se mantiene en los hechos. La representación String para la transferencia DTO/HTMX es responsabilidad del Harness, no de la capa de hechos.

El esquema 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

El esquema existe antes de la entrada - se almacena en .gitcover/schemas/diary-entry-1.0.schema.json y está versionado. Una entrada sin referencia de esquema ($schema) es rechazada por el Pre-Commit-Hook.

Esquema diary-entry-1.0.schema.json (simplificado)

{
  "$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" } }
  }
}

Importante - sin campo datetime/date: el tiempo de registro está contenido en la uuidV7 (marca de tiempo de 48 bits, RFC 9562 §5.7). Una representación String separada es redundante y no se mantiene en los hechos. La uuidV7 por sí sola es la DocID única; el Composite Key V7GUID:uuidV7 sirve para la organización de archivado y las consultas a la DB.

V7GUID - Estructura y marca de tiempo de 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
Rango de bits Ancho Contenido Fijado por RFC
0–47 48 bits Milisegundos Unix desde la época (1970-01-01T00:00:00Z) Sí (RFC 9562 §5.7)
48–51 4 bits Version = 0111 (UUIDv7)
52–63 12 bits rand_a (aleatorio) No
64–65 2 bits Variant = 10
66–127 62 bits rand_b (aleatorio) No

Principio central: el componente de marca de tiempo de 48 bits (bits 0–47) es autoritativo para el tiempo de registro. Está anclado en la propia uuidV7 y no puede modificarse a posteriori sin invalidar la GUID. Un campo datetime separado no existe - el tiempo está contenido en la uuidV7. La representación String para DTO/HTMX es responsabilidad del Harness.

Extensión de GitCover: jerarquía e IDs de proceso en los bits 66–127

La especificación V7GUID de GitCover (work/OSS/TOP/.gitcover/specs/v7guid/) amplía el componente aleatorio de 62 bits (bits 66–127) con una jerarquía de 6 niveles e IDs de proceso - esta es la extensión protegida por patente (DPMA Az. 10 2025 003 091.6):

Rango de bits Ancho Contenido
66–89 6×4 bits Jerarquía de 6 niveles (L1–L6, 4 bits cada uno)
90–97 8 bits ProcessTypeId (tipo de proceso)
98–105 8 bits GatewayId (punto de control)
106–113 8 bits Status (máscara de bits del ciclo de vida)
114–121 8 bits Kind (tipo de actividad)
122–127 6 bits VariantId (clase de variante)

Nota: esta extensión no es imprescindible para la serie del diario - cobra relevancia en ED02 (Tenant/esfera/rol) y en artículos posteriores (Harness). Aquí basta la base RFC 9562 con marca de tiempo de 48 bits.

Rutinas helper: crear V7GUID a partir de claves externas

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

La herramienta OSS de GitCover UuidV7Gen (work/OSS/TOP/tools/UuidV7Gen/) proporciona dos operaciones centrales:

gen - generar 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 - extraer la marca de tiempo de la 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

Clave externa → uuidV7

Las rutinas helper permiten crear una clave uuidV7 válida a partir de una clave externa (p. ej., una marca de tiempo en el nombre de archivo como 260815_0900_Lohnabrechnung.pdf) - con marca de tiempo predefinida, no now():

  1. Analizar la clave externa - 260815_09002026-08-15T09:00:00Z
  2. Calcular la marca de tiempo de 48 bits - milisegundos Unix desde la época
  3. Generar la uuidV7 - Guid.CreateVersion7() con marca de tiempo explícita, el resto con aleatoriedad
  4. Composite Key - V7GUID (Class de la Registry) : uuidV7 (Object con marca de tiempo)
  5. Documentación GoBD - registro inmediato anclado de forma trazable (sin campo datetime separado)

Referencia GoBD: el registro inmediato (GoBD Rz. 146) queda documentado criptográficamente mediante la marca de tiempo de 48 bits en la uuidV7 - no solo afirmado. La marca de tiempo es parte de la identidad, no modificable a posteriori.

Estructura del sidecar (.v7g.md)

Cada comprobante recibe un sidecar con dos referencias 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

Ejemplo 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 (campo 1) - Class Identifier del sidecar (clasificación según la Registry .gitcover: qué/qué tipo)
  • uuidV7 (campo 2) - Object ID del propio sidecar, generada con marca de tiempo predefinida (¿cuándo se clasificó?), marca de tiempo de 48 bits anclada en la GUID
  • v7g_taxonomy[].v7guid - Object ID del documento clasificado (¿qué documento se clasifica?)

Así no solo el documento queda identificado de forma única, sino también el acto de clasificación en sí - incluido el momento (en la uuidV7), quién clasificó y en qué rol. Sin campo datetime separado - el tiempo está contenido en los valores uuidV7.

Trazabilidad retrógrada y progresiva

%%{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
Dirección Inicio Resolución mediante Objetivo
Retrógrada Asiento contable (SKR04) source_sha256 → comprobante → uuidV7 Entrada de diario con marca de tiempo de 48 bits
Progresiva Comprobante (SHA-256) uuidV7 → entrada de diario → source_sha256 Asiento contable → Grundbuch → balance de apertura

Referencia GoBD: la trazabilidad retrógrada corresponde a GoBD Rz. 146 (trazabilidad). La trazabilidad progresiva corresponde a GoBD Rz. 147 (verificabilidad). Ambas quedan garantizadas by Design mediante uuidV7 + SHA-256

  • no mediante referencias cruzadas manuales.

Registro de tiempo en el diario

Cada entrada de diario contiene una tabla de registro de tiempo:

Inicio Fin Horas Actividad Tenant Esfera Fuente
0900 1200 3,0 Datos maestros de cuenta de nómina ORG-1 ideell E1
1200 1400 2,0 I+D: especificación V7GUID ORG-1 ideell E1

Referencia FZul: el registro de tiempo es la base de los justificantes de horas FZul (ED28). La separación I+D vs. administración se realiza mediante el campo role (F&E-Leiter vs. GF) y las tags (["fzul", "forschung"] vs. ["verwaltung"]).

Qué acaba en el repositorio Git - y qué no

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

Importante: solo los artefactos estructurados (JSON, sidecars, comprobantes, esquemas, diccionarios) se incorporan al repositorio Git. Los documentos privados se incorporan nunca al repo, salvo que se hayan introducido y clasificado explícitamente como artefacto. Es un requisito de protección de datos y de cumplimiento (RGPD, GoBD).

Apalancamiento de riesgo

Hoy (barato) Mañana (a prueba de auditoría) Riesgo mitigado
Artefacto JSON con V7GUID (Class) + uuidV7 (Object) Tiempo de registro anclado criptográficamente en uuidV7 Cuestionamiento del tiempo de registro
Validación de esquema (Pre-Commit) Solo entran al repo entradas válidas Violación de esquema, campos obligatorios incompletos
Sidecar con Composite Key V7GUID:uuidV7 Acto de clasificación trazable Cuestionamiento de la clasificación
source_sha256 por entrada Trazabilidad retrógrada Cuestionamiento de la asignación del comprobante
Helper UuidV7Gen para claves externas Registro inmediato documentado (marca de tiempo en uuidV7) Infracción GoBD "no inmediato"

Requisitos del Harness (vista previa)

Se puede deducir de ED03:

ID Requisito Prioridad
FA-1.1 Entradas de diario como artefactos JSON (Schema-First) MUST
FA-1.2 Composite Key V7GUID (Class) : uuidV7 (Object) - sin campo datetime/date separado; tiempo en la uuidV7 (RFC 9562 §5.7) MUST
FA-1.3 uuidV7 generada a partir de una marca de tiempo predefinida (no now()) vía helper MUST
FA-2.1 SHA-256 como ID de comprobante MUST
FA-2.2 Obligación de sidecar .v7g.md MUST
FA-2.3 Sidecar con Composite Key V7GUID:uuidV7 (Class + Object) MUST
FA-2.12 Trazabilidad retrógrada (asiento → comprobante → uuidV7) MUST
FA-2.13 Trazabilidad progresiva (comprobante → asiento → Grundbuch) MUST
TA-1.5 Rutinas helper para la creación de V7GUID a partir de claves externas (UuidV7Gen) MUST
TA-1.6 Requisitos temporales de GoBD documentados (marca de tiempo de 48 bits en la uuidV7) MUST
TA-2.1 Pre-Commit: validación de esquema JSON MUST

La lista completa de requisitos en Harness-Anforderungen.md.

Fuentes

Topología de fuentes y enlaces de referencia CDN

Rol Lugar Propósito
Primary / SSoT git.gitcover.org/GCC Almacenamiento canónico (firmado con GPG, versionado)
Public OSS Mirror / CDN codeberg.org/gitcover-commons Réplica de solo lectura; descubrimiento FLOSS
Community Hub github.com/gitcover-commons Issues & Discussions; referencia del código fuente en Codeberg

Nota: esta asignación de fuentes, mirror y community hub refleja el estado actual y puede cambiar. Por favor, compruebe la fuente canónica correspondiente en gitcover.org para conocer el estado actual.