# Plan: formato 3 en `datekeys-ts` (spec v0.10, entrega 1) *30 de septiembre de 2026. Implementa en `datekeys-ts`, rama `v0.10`, lo que la referencia Go ya hace con el tag `spec-v0.10` (`cc35d2c`): leer y escribir cápsulas de formato 3, en la librería y en las páginas. Fuentes: el spec v0.10, [spec_v0.10/formato3_diseno.md](spec_v0.10/formato3_diseno.md), cuyas decisiones de la página ya aprobó el autor (apartados 3 y 5, decisiones 8 a 11 del apartado 9), y el código Go de `capsule/format3.go`, `open3.go`, `encrypt3.go` e `internal/pathrule`.* ## Reglas de trabajo - Cada paso va en su commit, o en varios, sobre la rama `v0.10`, y cada commit deja `npm run verify` en verde. `main` no se toca mientras el autor no decida si cierra `0.2.0` (§2.3 del handoff). - Nada se sube sin la autorización del autor. - Sin dependencias nuevas: las tablas Unicode las genera `datekeys-go` (`pathrule/gen -ts`), y el ZIP y el CRC-32 son código propio. - `testdata` solo llega de `datekeys-go`, con `scripts/sync-testdata.mjs` y el tag `spec-v0.10`. - Los textos de error siguen los de Go byte a byte, también los de las reglas de rutas y de texto, y las líneas de los veredictos en español. - `src/lib/dkc` sigue a la referencia Go. Lo que solo tiene la página va en `src/lib/inspector`: el ZIP, el CRC-32 y el sumidero en OPFS. - Los módulos nuevos se cubren al 100 %, como los de las fases 2 y 3 (`vitest.config.ts`). ## Pasos | Paso | Contenido | Resultado | |---|---|---| | 0 | Tablas: el generador de `datekeys-go` escribe `src/lib/dkc/pathrule-tables.ts` | Hecho el 30-09: `datekeys-go` `13910b3`, sin subir. El digest recalculado en TypeScript coincide con `TablesDigest` | | 1 | `pathrule.ts`: NFD, pliegue, clave de R7, de R1 a R10 con R4b, R6b, R6c y R9, y las reglas de texto de §29.6 | Hecho el 30-09: los casos de las pruebas Go con sus textos, y cobertura del 100 % | | 2 | Códec del formato 3, sin tocar la trama: `BODY`, `security` y head, y `ERR_HEAD_INVALID` | Hecho el 30-09, `d9a9cf5`: los casos de las pruebas Go, con los textos de Go sacados de la referencia | | 3 | Lector y `testdata` en `spec-v0.10`: `VERSION` 3, control de versión 3, paso 17 en subpasos, sumidero y lectura hasta EOF | Hecho el 30-09, `b176ad2`: los 209 casos del corpus con el texto exacto de Go, congelado en `testing/mutation-texts.json` | | 4 | Escritor: `encryptFiles`, en dos fases y con dos lecturas; `encrypt` solo para vectores | Hecho el 30-09, de `3daa1f7` a `2efc8bc`: los cinco fixtures de `EncryptFiles` byte a byte, los textos de Go, `/create` en formato 3 con un fichero, y Go abre seis cápsulas de formato 3 de TypeScript | | 5 | ZIP de la página y su sumidero en OPFS | Hecho el 30-09, `36096a9` y `ee82f43`: `ZipSink` con escrituras posicionadas, y `archive/zip` de Go lee las muestras, 65 535 entradas incluidas | | 6 | Página `/inspect`: veredictos, autor, comentario, rutas y descargas | Hecho el 30-09, `651178a`: un fichero suelto o un ZIP en OPFS, con la descarga de cada fichero; en Chromium, el ZIP de `format3_tree` con sus CRC-32 parcheados y el borrado del temporal | | 7 | Página `/create`: ficheros y carpetas, rutas editables, exclusiones, comentario, autor y mtime | Hecho el 30-09, `7dc88ef`: el tamaño exacto antes de escribir, y `datekeys decrypt` de Go y `/inspect` abren una cápsula de seis ficheros escrita por la página | | 8 | Documentación, verificación y revisión adversarial | Hecho el 01-10, de `7527c35` a `e6cca0b` y el CHANGELOG: tres revisiones con subagentes y sus correcciones confirmadas; `/create` rediseñado y el sitio siempre claro, a petición del autor. Pendiente: ver el handoff | ### Paso 1. Reglas de rutas y de texto - `pathrule-tables.ts` es código generado: no se edita a mano. Se regenera con `go run ./internal/pathrule/gen -data .cache -go internal/pathrule/tables.go -ts ../App/src/lib/dkc/pathrule-tables.ts` desde `datekeys-go`, y una prueba recalcula su digest. - `pathrule.ts` no usa `normalize`, `toLowerCase`, `localeCompare`, `Intl` ni las clases `\p{...}`, porque su versión de Unicode cambia con el motor. Los límites cuentan bytes de UTF-8, con el `utf8Length` de `bytes.ts`. - `index.ts` no lo reexporta: sus tablas pesan 136 KB y se cargan bajo demanda, con el lector y con el formulario de `/create` (pasos 6 y 7). ### Paso 2. Códec - Módulos por esquema, como `header.ts` y `control.ts`: `body.ts` (la trama de §29.2, `AREA_LEN`, `SECURITY_LEN` y `HEAD_LEN`, y el área rellena de ceros), `security.ts` (el mapa exterior, la firma y el sello, los veredictos X, F0, F1, S0, S1 y S2 con sus textos, y el codificador) y `head.ts` (capas 2 a 4 del head, R1 y R8 en la capa 3, y la capa 4 con `ERR_HEAD_INVALID`). - `ERR_HEAD_INVALID` en `errors.ts`, que pasa a 19 códigos, y su glosa en `format.ts`. - El objeto de extensiones `HEAD_CBOR` en `extension.ts`, con sus claves 6 y 7. - Pruebas: los casos de `format3_test.go` con sus bytes y sus textos. Los vectores de `head_schema.json` y `security.json` llegan en el paso 3. ### Paso 3. Lector y datos de prueba - `npm run testdata:sync -- --commit spec-v0.10`, `SPEC_VERSION` 0.10, y la autocomprobación del arnés con `want 0.10`. - Trama: `FORMAT_3`, y `isFormat` acepta de 1 a 3. El control de versión 3 tiene las claves del de versión 2. `accessSlots` da 16 en los formatos 2 y 3, y el relleno vale para los dos. - `open`: - Gana `OpenOptions.sink`: `begin(head)`, `create(i)` con un `WritableStream`, `commit()` y `abort()`. Tiene la semántica del `Sink` de Go: nada se presenta como válido antes de `commit`. - Sin sumidero, un formato 3 rechaza con un `TypeError` justo tras el paso 2, antes de pedir nada, como `ErrSinkRequired`. - El paso 17 va en los subpasos 17.1 a 17.8, con la precedencia de Go: - Un fallo de `age`, o una longitud distinta de P, prevalece. - Si no, manda el primer subpaso que falle. - Los códigos distintos de `ERR_INTEGRITY` solo se dan tras leer `PAYLOAD_AGE` hasta EOF. - `Opened` gana `head`, `verdicts`, `areaLen` y `unusableHeadExtensions`. - `inspect` acepta el formato 3 en los pasos 1 a 8, y su vista JSON coincide con la de Go. - Un sumidero en memoria en `opener.ts`, para los fixtures y para contenidos de hasta 64 MiB. Hasta el paso 6, la página inspecciona el formato 3 y, al abrirlo, dice que aún no entrega ficheros. - Pruebas: - `fixtures.test.ts` con los formatos 1, 2 y 3. - `open.test.ts`: cada fixture abre, con el SHA-256 de `BODY` y el de cada fichero. - `mutations.json`: 209 casos, con la clave `verdicts` y los casos `ok`, desde memoria y desde un `Blob`, con un sumidero que debe acabar abortado. - `inspect_differential.json`: 5 110 casos sobre 14 bases. - `cbor.json` con el control de versión 3; `head_schema.json`, `security.json`, `paths.json` y `path_fold.json`. - `ibe-vectors.json`, regenerado con `scripts/ibe-go-vectors.go` para los nueve fixtures nuevos. - Los textos de error de los 209 casos quedan congelados desde Go, con un script, en `testing/`, igual que `capsule-vectors.json`. En la v0.9 se compararon una sola vez, fuera del repositorio. - `check-build.mjs` añade a los secretos de fixture que no pueden publicarse el head, la sal, el comentario, los ficheros y `BODY`. ### Paso 4. Escritor - `encryptFiles(files, opts)`: - Cada fichero trae `path`, `size`, `mtime` opcional (en milisegundos, como `File.lastModified`) y una forma de leerlo dos veces: un `Blob` o `open()`. - Las opciones ganan `comment`, `author` y las extensiones del head. - Primera fase: - Rutas y textos con las reglas del lector: CR LF y un CR suelto pasan a LF en el comentario. - La mtime con la regla 16 de §62.1. - L medido con un head de sal y hashes a cero. - `|HEAD|` de 16 MiB como máximo, y L como máximo L_MAX. - La primera pasada de SHA-256, en piezas de 64 KiB. - Segunda fase: - La sal y `HEAD_CBOR`, el control de versión 3 y el `SECURITY_CBOR` vacío. - La autodecodificación de los tres CBOR (MUST). - La segunda pasada, que aborta si un fichero cambió de tamaño o de SHA-256, con el texto de Go. - El `.dkk`. - `capsuleLength3` en `lengths.ts`: el tamaño exacto antes de escribir, para la página. - `encrypt` escribe el formato 2 solo con `testVectors`, con el texto de Go. La página deja de usarlo en el paso 7. - Interoperabilidad: - `scripts/capsule-ts-samples.mjs` gana muestras del formato 3, y `scripts/capsule-go-verdicts.go` las abre con `capsule.Open` y un sumidero. - `capsule-vectors.json` se regenera. - Con sorteos fijos, las secciones deterministas coinciden con las de los fixtures `format3_*`. ### Paso 5. ZIP y sumidero de la página - Hechos y cubiertos al 100 %: - `crc32.ts`. - `zip.ts`: entradas almacenadas, nombres UTF-8 con el bit 11, sin descriptores de datos ni entradas de carpeta, el CRC-32 parcheado en la cabecera local, fechas DOS en UTC entre 1980 y 2107, el campo NTFS 0x000A, que manda, el 0x5455 cuando cabe en 32 bits con signo, y ZIP64 por tamaño, posición o número de entradas. - `zipsink.ts`, un sumidero sobre un solo fichero de OPFS: - Un único fichero de un segmento se escribe tal cual. - En otro caso va el ZIP, con las cabeceras escritas por adelantado, el CRC-32 con escrituras posicionadas y el directorio central en `commit`. - Cada fichero se ofrece como un trozo (`slice`) del de OPFS. - `tempfile.ts` gana la escritura posicionada. - Interoperabilidad: - Un script escribe ZIP con TypeScript: nombres no ASCII, mtime ausente o fuera de rango, ficheros vacíos y 65 535 entradas. - `archive/zip` de Go los lee, y el resultado queda congelado en `testing/zip-vectors.json`. - La entrada de 4 GiB solo se prueba en la disposición: el fichero entero no cabe en una prueba. ### Paso 6. Página `/inspect` - Primero los veredictos, en sus líneas de §29.7, y después el autor declarado y el comentario, con el prefijo de la regla. - Las rutas, en `` y con los invisibles escapados. - Avisos según la clave de R7: `.lnk`, `.url`, `.library-ms`, `.searchConnector-ms`, `desktop.ini`, `.git`, ejecutables y un `-` inicial. - Descargas: - Un único fichero de un segmento se descarga con su nombre, pasado por `safeFileName`. - Un único fichero dentro de una carpeta se descarga primero, y el ZIP después (decisión 8). - Con varios ficheros, el ZIP se llama `.zip`, o `.zip`, y cada fichero tiene su descarga. - El espacio de OPFS se comprueba dos veces: con el contenido cifrado antes de empezar, como en el formato 2, y con la longitud exacta del ZIP al leer el head, antes de escribir nada. - `check-build.mjs` comprueba que las tablas de `pathrule` no entran en la primera carga de ninguna página. ### Paso 7. Página `/create` - Ficheros y carpetas, con `multiple`, `webkitdirectory` y arrastrar y soltar. - Rutas editables, validadas con `pathrule` a medida que se escriben. - Exclusiones tachadas, que se pueden reactivar: `.DS_Store`, `Thumbs.db`, `desktop.ini`, `._*` y `__MACOSX/`. - Comentario, autor declarado, y una casilla de mtime marcada por defecto. - Las dos fases del escritor, con el progreso de las dos pasadas. - El tamaño exacto del `.dkc` y la comprobación de la cuota antes de escribir. - `creator.ts` sigue inspeccionando la cápsula escrita y exige el tamaño y la ronda del plan. ### Paso 8. Documentación, verificación y revisión - README: versiones, módulos, páginas, vectores, dependencias y `testdata`. CHANGELOG: bajo «sin publicar»; el número de versión lo decide el autor. - `npm run verify` y `npm run testdata:check`. - Revisión adversarial de las páginas, del lector y del escritor, con subagentes, como en la fase 3. - Actualizar el handoff.