Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions .agents/sow/SOW-status.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,11 @@ order; the user may reorder it.
differs (completed 2026-11-06, options A and 2B).
22. SOW-0175 - Split the journalctl rewrites by responsibility (tracked by user
decision 2026-11-07; after SOW-0097).
23. SOW-0176 - Single ownership follow-ups: open decisions 8 and 9, I7 and
divergences found in SOW-0097 (needs user decisions).
23. SOW-0177 - Report the validated sealed range from verification (tracked
2026-11-08 from SOW-0176; needs a public API decision).

## Current

None.

## Pending

- SOW-0066 - V1 Release: v1.0.0 API freeze and release; not scheduled (user
Expand Down Expand Up @@ -77,14 +75,15 @@ None.
by user decision.
- SOW-0171 - Continue Sealing When Reopening A Sealed File: tracked
2026-11-03 (SOW-0156 decision N1); not scheduled.
- SOW-0177 - Report The Validated Sealed Range From Verification: tracked from
Comment thread
ktsaou marked this conversation as resolved.
Resolved
SOW-0176 item I7; needs a public API decision.
- SOW-0175 - Split The journalctl Rewrites By Responsibility: tracked
2026-11-07 (SOW-0097 decision 7); after SOW-0097.
- SOW-0176 - Single Ownership Follow-ups: tracked 2026-11-08 from SOW-0097;
needs user decisions (legacy export contract, Rust list_boots Result) plus
I7 and six Go/Rust or SDK/systemd divergences.

## Recently Closed Or Completed

- SOW-0176 - Single Ownership Follow-ups: completed 2026-11-08; follow-up
SOW-0177.
- SOW-0174 - Rust Registry Unwatch Leaves Files When The Directory Key
Differs: completed 2026-11-06. Unwatch forgets exactly the files no
remaining watch covers, overlapping watches work in any order, failures
Expand Down
418 changes: 418 additions & 0 deletions .agents/sow/done/SOW-0176-20261108-single-ownership-followups.md

Large diffs are not rendered by default.

129 changes: 0 additions & 129 deletions .agents/sow/pending/SOW-0176-20261108-single-ownership-followups.md

This file was deleted.

159 changes: 159 additions & 0 deletions .agents/sow/pending/SOW-0177-20261108-verify-sealed-range-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# SOW-0177 - Report The Validated Sealed Range From Verification

## Status

Status: open

`completed` is the successful terminal status. `done` is a directory name, not a status value. Do not use `Status: done` or `Status: complete`.

Sub-state: tracked from SOW-0176 (item I7) on 2026-11-08; needs a public API decision.

## Requirements

### Purpose

Make `journalctl --verify --verify-key` print the same validated-range summary
as systemd v262 for sealed files.

### User Request

Follow-up of the SOW-0176 verify-key parity decision (I7 = A).

### Assistant Understanding

Facts:

- After `PASS`, v262 prints `=> Validated from %s to %s, final %s entries not
sealed.`, `=> No sealing yet, %s of entries not sealed.` or `=> No sealing
yet, no entries in file.` (systemd/systemd @ 8cc40e0c5e9234bf45084751ac53b1fbfe70b492
`src/journal/journalctl-misc.c:70-85`).
- `VerifyFileWithKey` and `verify_file_with_key` return only success or failure.
Both already track the realtime of the last verified TAG.

Inferences:

- Exposing the range needs a new public result type in both SDKs, plus
`format_timespan` output in the CLIs.

Unknowns:

- The public API shape, decided at activation.

### Acceptance Criteria

- Both CLIs print the three v262 summary forms, compared against the pinned
OpenSSL-enabled v262 oracle.
- The range comes from one public SDK function per language.

## Analysis

Sources checked: SOW-0176 keydiff results and the files cited above.

## Pre-Implementation Gate

Status: blocked

Problem / root-cause model:

- The verifier does not return the range v262 reports.

Evidence reviewed:

- See `## Requirements`.

Affected contracts and surfaces:

- Go and Rust verification APIs, both journalctl rewrites.

Existing patterns to reuse:

- `verifySealed` / `verify_sealed` tag tracking.

Risk and blast radius:

- Additive API; stderr output only.

Sensitive data handling plan:

- Synthetic fixtures only.

Implementation plan:

1. Decide the API, add it in both languages, print the summary, compare with the oracle.

Validation plan:

- `tests/interoperability/run_verify_matrix.py` and a stderr comparison.

Artifact impact plan:

- AGENTS.md: none expected.
- Runtime project skills: none expected.
- Specs: journalctl parity matrix.
- End-user/operator docs: API pages.
- End-user/operator skills: none.
- SOW lifecycle: pending until activated.

Open-source reference evidence:

- Cited above.

Open decisions:

- Public API shape.

## Implications And Decisions

None yet.

## Plan

See the implementation plan above.

## Implementation And Review Plan

Implementation:

- The project manager implements the approved API in both languages, with one
chunk for the SDK result and one for the CLI output.

Reviewers:

- Whole-SOW self-review after local validation; tests are the gate. External
reviewers are not used.

Repository boundary block for every external-agent prompt:

```text
CRITICAL REPOSITORY BOUNDARY:
- Do not make changes outside this repository for any reason.
- Repository path: current repository root.
- You may inspect external references read-only when the task requires it.
- Write, edit, delete, move, reset, checkout, install, generate, cache, or format nothing outside this repository.
- The only write exception outside the repository is /tmp.
- Prefer .local/ inside this repository for scratch work, generated temporary files, cloned references, logs, and working notes.
```

## Execution Log
Comment thread
ktsaou marked this conversation as resolved.
Resolved

None yet.

## Validation

Pending.

## Outcome

Pending.

## Lessons Extracted

Pending.

## Followup

None yet.

## Regression Log

None.
2 changes: 1 addition & 1 deletion .agents/sow/specs/journalctl-v260-parity-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ Every official v262 output mode is `file-backed-required`:

| Option | Args | Classification | Required portable behavior |
| --- | --- | --- | --- |
| `--verify-key=` | key | file-backed-required | Verify sealed journal files when combined with `--verify` or implied verification. Invalid seed/key text must fail with official-style parse errors. |
| `--verify-key=` | key | file-backed-required | Verify sealed journal files when combined with `--verify` or implied verification. Key text follows v262 `journal_auth_load_key` through the SDK parser, is judged per file, and a rejected key stops the run with v262's message. |
| `--interval=` | duration | recognized-no-op | Parse for `--setup-keys`; no portable behavior exists without `--setup-keys`, so it is accepted as a no-op elsewhere. |
| `--force` | none | recognized-no-op | Parse for `--setup-keys`; no portable behavior exists without `--setup-keys`, so it is accepted as a no-op elsewhere. |
| `--setup-keys` | none | recognized-unsupported | Official v262 checks `/var/log/journal`, reads host machine and boot IDs, and writes `/var/log/journal/<machine-id>/fss`; portable file-backed commands must reject it instead of mutating host journal state. Evidence: `systemd/systemd @ 8cc40e0c5e9234bf45084751ac53b1fbfe70b492`, `src/journal/journalctl-authenticate.c:80`, `src/journal/journalctl-authenticate.c:89`, `src/journal/journalctl-authenticate.c:93`, `src/journal/journalctl-authenticate.c:97`, `src/journal/journalctl-authenticate.c:139`. |
Expand Down
23 changes: 15 additions & 8 deletions .agents/sow/specs/product-scope.md
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,9 @@ Root retention counts file lengths including preallocation, expires by tail
saved time, and evicts whole files in tail/path order. Default retention policies
and file format are unchanged; Go now applies cleanup after a successful
successor-creation retry following a safe creation failure.
Ordinary `Log` retention in both languages counts disposed `*.journal~` files and
deletes them before archived history under file-count and byte limits, as
journald does; an age limit deletes any file older than the cutoff.
`MaintainRootRetention` finalizes verified retired active
files and only seals live actives for idle expiry or required size-policy
allocation changes, leaving successors lazy even after eager startup. Startup,
Expand Down Expand Up @@ -641,6 +644,10 @@ Current shared high-level directory writer API slice:
leaves the file in place. Default chain naming treats only online files as
active, so an unknown-state chain file stays behind as history while the new
file still continues its sequence.
- Rust and Go non-root directory retention counts disposed `*.journal~` files
of the same source, both `<source>@...` forms, and deletes them oldest first
and before archives, as journald's vacuum does. Max-age retention deletes any
expired file whatever its place in that order.
- Rust and Go writers refuse entry timestamps with realtime outside
`1..2^55-1` or monotonic at or above 2^55 before mutation (Go
`ErrTimestampOutOfRange`, Rust `JournalError::TimestampOutOfRange`); the
Expand Down Expand Up @@ -1079,11 +1086,12 @@ Current Go reader feature slice:
- boot listing (`Reader.ListBoots`, `DirectoryReader.ListBoots`,
`SdJournalListBoots`) reports every boot from the `_BOOT_ID` DATA entry
arrays, merged across files in directory order and ordered by last entry,
first entry and ID. When no usable canonical index exists it scans entry
headers instead, so header IDs can name boots then; a directory scans only
when no file has such an index or one cannot be read. Only canonical
lowercase `_BOOT_ID` values name indexed boots. It does not change matches
or position. Both journalctl rewrites use it;
first entry and ID. A journal with no `_BOOT_ID` DATA lists no boots, as
v262 journalctl does; `HeaderFallback` (Rust `header_fallback`) scans entry
headers then, and for a directory only when no file has `_BOOT_ID` DATA.
Noncanonical `_BOOT_ID` DATA counts as present but names no boot. Read
failures return an error. It does not change matches or position. Both
journalctl rewrites use the default;
- file-backed journalctl output modes cover the stock v262 family:
`short`, `short-full`, `short-iso`, `short-iso-precise`,
`short-precise`, `short-monotonic`, `short-delta`, `short-unix`,
Expand Down Expand Up @@ -1229,9 +1237,8 @@ Current Rust reader feature slice:
contract as the Go `EntryEncoder`; both journalctl rewrites render through
it. `export_entry_bytes` and `json_entry` keep the older lossless contract;
- boot listing (`FileReader::list_boots`, `DirectoryReader::list_boots`,
`SdJournalListBoots`) follows the Go boot-listing contract;
`DirectoryReader::list_boots` and `SdJournal::list_boots` cannot return an
error and fall back to one boot per file header when files cannot be read;
`SdJournalListBoots`) follows the Go boot-listing contract, including
returning an error when files cannot be read;
- file-backed journalctl output modes cover the stock v262 family:
`short`, `short-full`, `short-iso`, `short-iso-precise`,
`short-precise`, `short-monotonic`, `short-delta`, `short-unix`,
Expand Down
Loading
Loading