Drop atime from the format spec and pin the file entry fields (closes #158)
check / check (push) Failing after 2m22s

atime left mf.proto earlier and nothing reads or writes it, but
docs/FORMAT.md still narrated its removal and listed it among the
determinism rules. The spec now describes the file entry by its fields
only.

A new test compares the MFFilePath message descriptor, by name and
number, with a list copied from the spec's field table. It does not read
the spec, so changing a field means changing the proto, the spec and
that list together.

Model: opus-5-5
This commit is contained in:
2026-10-06 00:26:42 +00:00
parent 99b3e0e202
commit 040aa8e113
2 changed files with 25 additions and 4 deletions
-4
View File
@@ -78,9 +78,6 @@ Each file entry contains:
| `mtime` | 302 | Timestamp (optional) | Modification time | | `mtime` | 302 | Timestamp (optional) | Modification time |
| `ctime` | 303 | Timestamp (optional) | Change time (inode metadata change) | | `ctime` | 303 | Timestamp (optional) | Change time (inode metadata change) |
Field 304 (`atime`) has been removed from the specification. Access time is
volatile and non-deterministic; it is not useful for integrity verification.
## Path Rules ## Path Rules
All `path` values must satisfy these invariants: All `path` values must satisfy these invariants:
@@ -130,7 +127,6 @@ By default, manifests are generated deterministically:
- File entries are sorted by `path` in **lexicographic byte order** - File entries are sorted by `path` in **lexicographic byte order**
- `createdAt` is omitted unless explicitly requested - `createdAt` is omitted unless explicitly requested
- `atime` is never included (field removed from schema)
This ensures that two independent runs over the same directory tree produce This ensures that two independent runs over the same directory tree produce
byte-identical `.mf` files (assuming file contents and metadata have not byte-identical `.mf` files (assuming file contents and metadata have not
+25
View File
@@ -8,6 +8,8 @@ import (
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"google.golang.org/protobuf/reflect/protoreflect"
"sneak.berlin/go/mfer/mfer"
) )
// mf.pb.go is generated from mf.proto and committed. `make generate` // mf.pb.go is generated from mf.proto and committed. `make generate`
@@ -27,3 +29,26 @@ func TestGeneratedCodeMatchesProto(t *testing.T) {
"mfer/mf.proto has changed since mfer/mf.pb.go was generated "+ "mfer/mf.proto has changed since mfer/mf.pb.go was generated "+
"from it: run `make generate` and commit the result") "from it: run `make generate` and commit the result")
} }
// A file entry has exactly the fields docs/FORMAT.md lists for MFFilePath.
func TestFileEntryFieldsMatchSpec(t *testing.T) {
t.Parallel()
want := map[string]protoreflect.FieldNumber{
"path": 1,
"size": 2,
"hashes": 3,
"mimeType": 301,
"mtime": 302,
"ctime": 303,
}
got := map[string]protoreflect.FieldNumber{}
fields := (&mfer.MFFilePath{}).ProtoReflect().Descriptor().Fields()
for i := range fields.Len() {
got[string(fields.Get(i).Name())] = fields.Get(i).Number()
}
require.Equal(t, want, got)
}