From 05f8a96e7ea758977fb0aad4fdb93372912bdcb5 Mon Sep 17 00:00:00 2001 From: dev Date: Wed, 30 Sep 2026 21:59:59 +0200 Subject: [PATCH] Plan of format 3 in datekeys-ts, and its first steps in the handoff The plan follows the one of the Go reference: the codec first, then the reader together with the testdata of spec-v0.10 in one commit, as the v0.9 did, since the guard of the harness wants every synced file read by a test; then the writer, the ZIP of the page and the two pages. Co-Authored-By: Claude Opus 5.5 --- HANDOFF.md | 4 +- PLAN_formato3_ts.md | 131 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 PLAN_formato3_ts.md diff --git a/HANDOFF.md b/HANDOFF.md index 3648d1a..3822ded 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -248,7 +248,7 @@ El vector de GT (`vectors/tlock_ibe.json`) ya está cubierto en `App`. El paso 9 2. Hecho: el autor aprobó la regla de invisibles («sí a las dos»), que el spec recoge en R4b y en §29.6 (`631d09c`). 3. Hecho: con su permiso se descargaron los 19 ficheros de datos, 8,25 MB, en `datekeys-go/.cache/`, fuera de git. 4. Hecho: la referencia Go implementa el formato 3, con el [plan](PLAN_formato3_go.md) y el detalle de abajo. Con la autorización del autor («sí, sube la rama y pon el tag»), la rama `v0.10` y el tag anotado `spec-v0.10` están en el remoto, en `cc35d2c`, que solo cambia textos para nombrar el tag. Después, a petición suya («sí, avanza main hasta la v0.10»), `main` avanzó hasta `cc35d2c` sin fusión y está subida. - 5. **Siguiente:** `datekeys-ts` sincroniza `testdata` y lee y escribe el formato 3, con un plan propio. + 5. **En curso:** `datekeys-ts` lee y escribe el formato 3 con su [plan](PLAN_formato3_ts.md). En la rama `v0.10` de `App`, sin subir, están hechos los pasos 0 y 1 (`fbb90b2`, las tablas y las reglas de rutas y de texto) y, del paso 5, el CRC-32 y la disposición del ZIP (`36096a9`). Sigue el paso 2, el códec. 6. La entrega 2 abre la versión siguiente del spec, y la 3 espera al documento «Servicio de sellado DateKeys v1». Para otra revisión externa, el artefacto hay que volver a publicarlo desde el diseño nuevo. **Hecho el 30-09 por la noche: el formato 3 en Go.** Rama `v0.10` de `datekeys-go`, subida con el tag `spec-v0.10` en `cc35d2c`, un commit o varios por paso del plan: @@ -264,7 +264,7 @@ El vector de GT (`vectors/tlock_ibe.json`) ya está cubierto en `App`. El paso 9 - el corpus de mutaciones pasa de 168 KB a 706 KB. 476 KB son el caso de 65 536 carpetas implícitas que pide el §64, cuyo head mide 235 KB; - los textos del CLI que el spec no fija son de la referencia: la cabecera «Ficheros escritos en CARPETA (N):», los avisos de nombres peligrosos y los mensajes del escritor. Los que fija, los veredictos y las dos etiquetas, van literales. - Para `datekeys-ts`: - - el generador de tablas aún no escribe el módulo TypeScript, que el plan preveía con una bandera: es el primer paso de su plan, con el mismo `TablesDigest`; + - hecho: el generador escribe el módulo TypeScript con la bandera `-ts` (`13910b3`, en `main` de `datekeys-go`, un commit por delante de `origin/main` y sin subir), y el digest recalculado en TypeScript coincide con `TablesDigest`; - `testdata` cambia entero de `"spec"`, y añade nueve fixtures, cuatro ficheros de vectores, `mutations.json` con 209 casos (`version changed` pone ahora `VERSION` 4, y hay casos con `verdicts`) y el diferencial con 5 110; - los textos de las reglas de rutas y de texto deben coincidir byte a byte: `paths.json` y `head_schema.json` los traen en `result` y `detail`. - Herramientas: en esta máquina los heredocs de Bash rompen apóstrofos y barras invertidas, y la herramienta Write convierte `\uXXXX` en caracteres literales. Funciona escribir los cambios como scripts de Python en el scratchpad. diff --git a/PLAN_formato3_ts.md b/PLAN_formato3_ts.md new file mode 100644 index 0000000..820d06e --- /dev/null +++ b/PLAN_formato3_ts.md @@ -0,0 +1,131 @@ +# 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` | Pruebas unitarias con los casos de las pruebas Go. `testdata` sigue en `spec-v0.9` | +| 3 | Lector y `testdata` en `spec-v0.10`: `VERSION` 3, control de versión 3, paso 17 en subpasos, sumidero y lectura hasta EOF | Un solo commit, como `0118890` en la v0.9: fixtures, mutaciones con veredictos, diferencial y vectores nuevos | +| 4 | Escritor: `encryptFiles`, en dos fases y con dos lecturas; `encrypt` solo para vectores | Ida y vuelta, secciones deterministas de los fixtures, y Go abre lo que escribe TypeScript | +| 5 | ZIP de la página y su sumidero en OPFS | Hechos el CRC-32 y la disposición del ZIP. Falta que `archive/zip` de Go lea lo que escribe la página | +| 6 | Página `/inspect`: veredictos, autor, comentario, rutas y descargas | Un fichero suelto o un ZIP en OPFS, con la descarga de cada fichero | +| 7 | Página `/create`: ficheros y carpetas, rutas editables, exclusiones, comentario, autor y mtime | Tamaño exacto antes de escribir, y una cápsula que abren Go y la propia página | +| 8 | Documentación, verificación y revisión adversarial | README, CHANGELOG, `npm run verify` y una revisión adversarial del lector, del escritor y de las páginas | + +### 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 con P más lo que añade el ZIP. +- `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.