Repository navigation
Conversation
Rust readers now refuse unknown incompatible flags, and Rust append-open refuses compact files over 4 GiB and files missing a hash table, as Go already does. Adds paired Go/Rust tests and records the header audit with the remaining behavior decisions in SOW-0156.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the
|
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 430 |
| Duplication | 37 |
AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.
TIP This summary will be updated as you push new changes.
There was a problem hiding this comment.
Pull Request Overview
✅ No blocking issues noticed
One acceptance criterion appears only partially covered: the header inventory validation lacks a dedicated test for unknown incompatible flags. The Rust test file is also relatively complex and has clone findings, so adding focused coverage would reduce regression risk.
Status tracking is inconsistent between the root and canonical indexes and should be reconciled, though this does not indicate a runtime defect. No high-confidence blocking issues were found.
Test suggestions
- Rust reader rejects an unknown incompatible flag.
- Rust append-open rejects an unknown incompatible flag.
- Rust append-open rejects a file missing the DATA hash table.
- Rust append-open rejects a file missing the FIELD hash table.
- Rust append-open rejects compact files larger than 4 GiB without resizing them.
- Go reader and append-open paths reject unknown incompatible flags.
- The header inventory path rejects unknown incompatible flags.
- Each rejected open leaves the file byte-for-byte unchanged.
Prompt proposal for missing tests
Consider implementing these tests if applicable:
1. The header inventory path rejects unknown incompatible flags.
Low confidence findings
- Reconcile the root and canonical status indexes so they present the same current-work state.
TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback
There was a problem hiding this comment.
All reported issues were addressed across 10 files
Architecture diagram
sequenceDiagram
participant Client as SDK Client
participant Reader as Rust Reader (file.rs)
participant Writer as Rust Append-Writer (file_mut.rs)
participant Header as JournalHeader Validator
participant Inv as header_inventory.rs
participant Go as Go SDK (reference)
participant Sys as systemd (reference)
Note over Client,Sys: Rust header open gates at Go parity
Client->>Reader: open(path)
Reader->>Header: validate header
Header->>Header: check incompatible_flags & !SUPPORTED_INCOMPATIBLE_FLAGS
alt Unknown incompatible flags present (A1)
Header-->>Reader: UnsupportedJournalFile
Reader-->>Client: error, file unchanged
else Flags OK
Reader->>Reader: continue normal open
end
Client->>Writer: open_for_append(path)
Writer->>Header: validate header
Header->>Header: check has KeyedHash flag + all flags supported
alt Missing KeyedHash or unsupported flags (A1)
Header-->>Writer: UnsupportedJournalFile
Writer-->>Client: error, file unchanged
else Flags OK
Header->>Header: check data_hash_table_offset, field_hash_table_offset, tail_object_offset
alt Any hash table missing (A5)
Header-->>Writer: InvalidObjectLocation
Writer-->>Client: error, file unchanged
else All tables present
Header->>Header: check Compact flag + file_size limits
alt Compact file exceeds 4 GiB (A4)
Header-->>Writer: ObjectExceedsFileBounds
Writer-->>Client: error, file unchanged
else Size OK
Writer->>Writer: proceed with append operations
Writer-->>Client: success
end
end
end
Note over Inv,Header: Shared flag constant
Header->>Inv: SUPPORTED_INCOMPATIBLE_FLAGS
Inv->>Inv: validate_writer_compatibility uses same constant
Note over Go,Sys: Reference implementations
Go-->>Sys: Go already follows systemd behavior
Sys-->>Go: for A1, A4, A5
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed across 4 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
# Conflicts: # SOW-status.md
# Conflicts: # .agents/sow/SOW-status.md
There was a problem hiding this comment.
2 issues found across 2 files (changes from recent commits).
Confidence score: 4/5
SOW-0156-20261010-fixed-header-consistency-audit.mdhas conflicting lifecycle records: its status is in progress, while the plan, execution log, and outcome still describe D1–D6 as pending or paused. Align those records.SOW-status.mdstill lists SOW-0156 as paused with D1–D6 pending. Update the index to match the SOW.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name=".agents/sow/SOW-status.md">
<violation number="1" location=".agents/sow/SOW-status.md:35">
P3: The root `SOW-status.md` still reports SOW-0156 as paused with D1-D6 pending. Update that entry in the same change so the two status indexes match.</violation>
</file>
<file name=".agents/sow/current/SOW-0156-20261010-fixed-header-consistency-audit.md">
<violation number="1" location=".agents/sow/current/SOW-0156-20261010-fixed-header-consistency-audit.md:5">
P2: `Status: in-progress` now conflicts with the SOW's remaining lifecycle records: `Plan` says D1-D6 are open, the execution log says it is paused on them, and `Outcome` still says the user is deciding them. Update those records so the status, plan, and outcome describe the same state.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
Apply systemd v260.1's writable-open header rules in both SDKs: append-open refuses header sizes other than 272, unknown compatible flags, a missing tail-entry-boot-ID flag, archived files, invalid tail metadata and regular files above 4 GiB as unsupported, so Log sets them aside and continues the sequence. Root retention finalizes retired files through an archive-only open. Writers refuse realtime outside 1..2^55-1 and monotonic at or above 2^55 before mutation (Go ErrTimestampOutOfRange, Rust TimestampOutOfRange), and Log no longer inherits out-of-range tails as clock floors. Readers refuse unknown header states and short sealed headers, accept single-bucket hash tables in Rust, and ignore the truncated 32-bit tail entry array hint above 4 GiB, so files earlier releases grew past 4 GiB stay readable. Validated against fixtures written by the v0.10.0 writer.
There was a problem hiding this comment.
All reported issues were addressed across 36 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
Append-open now refuses sealed files in both SDKs, because the writer cannot restore sealing state and would append an unsealed tail; Log sets such a file aside and starts a new one. Continuing sealing with the key is tracked in SOW-0171. Root-retention logs archive an active file they cannot extend under its archive name instead of failing to start, since root inventory rejects set-aside files. Go archive-only opens no longer require a boot ID.
There was a problem hiding this comment.
All reported issues were addressed across 15 files (changes from recent commits).
Requires human review: Auto-approval blocked because this review re-detected 1 unresolved issue already reported by Cubic.
View guided diff | Turn on auto-fix | Re-trigger cubic
The header's tail entry array hint and compact offsets are 32-bit, so a file must never reach 4 GiB. Before an entry mutates the file, both writers bound its growth from payload sizes (DATA, FIELD and a minimum entry array per item, the ENTRY, a minimum global array and due TAGs) and refuse it with Go ErrJournalFileFull / Rust JournalError::JournalFileFull when it could pass 2^32 - 8 MiB. Nothing is written and the writer stays usable. Entry arrays stop doubling near the ceiling, and arena growth and creation refuse sizes above 2^32 - 1 for both layouts. The directory writers rotate once on this error and retry the entry; an entry that does not fit an empty file is returned, and the Rust Log restores its clocks as Go does. Completes SOW-0156 and tracks the Rust relative-directory finding as SOW-0172.
There was a problem hiding this comment.
All reported issues were addressed across 22 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
…e create Rust append and archive-only opens now refuse an unknown state and a short sealed header, as readers and Go already did. Both writers validate the new file's layout (and Go its sealing options) before opening, so a refused create leaves an existing target untouched. Go header errors name the refused value. Adds paired tests, fixtures from earlier release tags, and corrects the writer docs and spec for the 4 GiB boundary, invalid files and the append ceiling.
There was a problem hiding this comment.
All reported issues were addressed across 18 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
Log stopped at startup on an unknown state or short sealed header, where journald sets the file aside. Both languages now do the same and keep the sequence; root retention reports the error. Go's chain scan reads such headers for sequence identity, matching Rust. Move the writable header refusals into one helper and note the zero-realtime exception in the spec.
Stacked on #17. Merge order: #13, #14, #15, #16, #17, then this. SOW-0156 (
.agents/sow/done/SOW-0156-20261010-fixed-header-consistency-audit.md) is completed.Decisions for review
ErrTimestampOutOfRange/ErrJournalFileFull, RustJournalError::TimestampOutOfRange/JournalError::JournalFileFull(both Rust variants are unreleased and listed indocs/Rust-API.md).2^32 - 8 MiB, not 4 GiB: the arena grows in 8 MiB steps, so the last object must end there for the file to stay at or below2^32 - 1.Logaccepts a relative directory, then fails on the first write; Go works. Options and a recommendation are in the SOW.What changes
Logsets such an active file aside; root-retentionLogs archive it.1..2^55-1and monotonic at or above2^55.Logrotates once and retries.Validation
go test ./...andcargo test --workspacepass, on this branch and on the top of the stack (SOW-0153: Go stability guide links to the install guide; Rust writer-state APIs named #22).Log.Logwith no limits, fed 1 MiB entries.2^32 - 8 MiB.Summary by cubic
Applies systemd v260.1's writable-open rules and timestamp bounds to both SDKs, bringing Rust header gates to Go parity, and records the SOW-0156 decisions that unblocked the work.
Logsets such an active file aside and continues the sequence; root-retention logs archive it under its archive name instead of failing to start. Continuing sealed files with the sealing key is tracked in SOW-0171.1..2^55-1and monotonic at or above2^55before mutation via newErrTimestampOutOfRange/TimestampOutOfRangeerrors;Logno longer inherits out-of-range tails as clock floors.ErrJournalFileFull/JournalError::JournalFileFullerrors, rotating once and retrying in directory writers, and returning the entry when it does not fit an empty file.Lognow sets such active files aside at startup and keeps the sequence instead of stopping, with Go's chain scan reading those headers for sequence identity. Rust accepts single-bucket hash tables, and both ignore the truncated 32-bit tail array hint above 4 GiB, keeping files from earlier releases readable.Written for commit 4ff1aca. Summary will update on new commits.