Skip to content

feat(fields): --time-format sets the default time-field format (config phase D4b) - #109

Merged
helly25 merged 1 commit into
mainfrom
feat/time-format
Jun 23, 2026
Merged

feat(fields): --time-format sets the default time-field format (config phase D4b)#109
helly25 merged 1 commit into
mainfrom
feat/time-format

Conversation

@helly25

@helly25 helly25 commented Jun 23, 2026

Copy link
Copy Markdown
Owner

Config phase D4b: --time-format + #71 resolved

Adds --time-format=NAME, the companion to --timezone: it sets the default format for a time field rendered without an explicit {:qualifier}. Respected in every style - it is a global, so it never reaches the find-style filter (per the "find stays loose" call).

What

  • RenderContext/EvalContext carry time_format; the four time-field renderers ({atime}/{mtime}/{ctime}/{btime}) use it when the field has no qualifier. An explicit {mtime:iso} qualifier always wins.
  • run.cc resolves the last --time-format (last wins; any value is accepted verbatim - an unknown name renders literally, like printf, so nothing to reject) and threads it to the implicit-print template and the three -exec/-execdir/-capture field contexts.

Resolves #71 (default time format)

Per discussion, the modern default stays space (2026-06-22 14:30:00 +0100): human-first (matches GNU ls --time-style=long-iso and git log --date=iso), ISO-ordered so it sorts, and parseable back by ParseTimeString. --time-format makes this a soft choice - rfc3339 is one flag (--time-format=rfc3339) or one .xffrc line away, and machine consumers use --format=jsonl. TODO.md updated to record the resolution.

Test

  • fields_test: a bare {mtime} renders with time_format=epoch, falls back to space when unset, and an explicit {mtime:%Y} qualifier wins over the default.
  • run_test: end to end, --time-format=epoch --template={mtime} renders all-digit Unix seconds (asserted via ElementsAre(Not(HasSubstr("-"))) - one matcher folds the count + content).

bazel test //... green in both --config=clang and --config=clang --config=asan. clang-format + em-dash clean.

…g phase D4b)

Add --time-format=NAME, the companion to --timezone: it sets the default format
for a time field rendered without an explicit {:qualifier}. Respected in every
style (it is a global, never gated by the find-style filter).

- RenderContext/EvalContext carry time_format; the four time-field renderers use
  it when the {field} has no qualifier, and an explicit {mtime:iso} still wins.
- run.cc resolves the last --time-format (last wins; any value accepted verbatim -
  an unknown name renders literally, like printf) and threads it to the
  implicit-print template and the -exec/-execdir/-capture field contexts.

Also resolves the #71 open decision: the modern default time format stays `space`
(human-first, matches ls long-iso / git iso, sortable); --time-format makes that a
soft choice (rfc3339 is one flag, or one .xffrc line, away). TODO.md updated.

Tests: fields_test (a bare {mtime} honors time_format=epoch, falls back to "space"
with none, and an explicit {mtime:%Y} qualifier wins), run_test (end to end,
--time-format=epoch --template={mtime} renders all-digit Unix seconds via
ElementsAre). Green in both configs; clang-format + em-dash clean.
@helly25
helly25 enabled auto-merge (squash) June 23, 2026 23:44
@helly25
helly25 merged commit 75a7ee2 into main Jun 23, 2026
5 checks passed
@helly25
helly25 deleted the feat/time-format branch June 23, 2026 23:48
helly25 added a commit that referenced this pull request Jul 6, 2026
…280)

* docs(TODO): give #109, smart-case, and #84 their own TODO.md entries

These three were tracked only in the session task list / memory with no
TODO.md home. Add roadmap-tail bullets so TODO.md is complete:
- Hash-verification workflow (#109) [DISCUSS] - -eval matcher + read-into-var
  + --summary tallies; the hashing primitives already shipped (#105).
- Smart-case matching (--smart-case) [DISCUSS] - rg/fd convention; sequence
  before the flavor feature-map help row.
- Sharded-file support (#84) TBD - needs a design pass.

Fold in a correction: the old 'Per-file content hashes' bullet was stale -
the {hash} field + -hash action shipped in #105 (fields.cc, registry.cc);
its only still-open aspect (verify/match) is now the #109 entry.

* docs(TODO): promote the --sort/-j CLI bashtest to its own bullet

It was a sub-clause buried in the (shipped) #43 parallel-traversal bullet, so
it had no findable standalone location. Give it its own roadmap-tail bullet and
point the #43 record at it, so every tracked item maps to its own line.
helly25 added a commit that referenced this pull request Aug 9, 2026
Add the `-verify EXPECTED` predicate: TRUE when the file's computed digest
equals EXPECTED, a {field} template rendered per entry - the manifest-
verification companion of -hash. So:

  xff . -type f -verify {def.SUMS}      # list files matching their manifest
  xff . -type f ! -verify {def.SUMS}    # list drift / corruption

`-verify=ALGO[/ENCODING]` shares the -hash spec grammar (sha256/hex default;
sha1/sha512/md5, hex/base64); hex comparison folds case (sha256sum lowercases,
SRI-style tools upper-case) while base64 stays case-sensitive. Style::kXff, so
the find style rejects it; a bad spec is a pre-walk usage error.

Wiring mirrors -cmp (arity-1 field-template target) + -hash (kHash binding);
the parser's kHash =SPEC branch now consumes the descriptor's arity operands
so `-verify=sha256 EXPECTED` parses. ValidateHashArgs covers -verify too.

Tests: xff/cli/verify_test.sh (match/mismatch, {def.X} source, hex case-fold,
ALGO/ENCODING selectors, `! -verify` drift, bad-spec + find-style errors).
Regenerated XFF.md; CHANGELOG + TODO updated. --summary tallies deferred to a
follow-up slice.
helly25 added a commit that referenced this pull request Aug 9, 2026
* feat(#109): --summary=hash dedup grouping mode

Add `--summary=hash` as a first-class summary mode: groups matches by file
digest, so identical files collapse into one bucket and the count column reads
as a dedup histogram. Reuses the {hash} field renderer (default sha256/hex),
so it cannot drift from the field value - the same pattern as
--summary=mime/user/group. Equivalent to the already-working --summary={hash}
template, now named and documented.

Tests: summary_test.sh asserts two identical files share one digest bucket
(count 2) via --format=jsonl. Regenerated XFF.md; CHANGELOG + TODO updated.

* chore: drop issue-number references from CHANGELOG

CHANGELOG is user-facing release notes; internal issue numbers (#NNN) are
noise there. Remove them from the 0.2.0 entries.
helly25 added a commit that referenced this pull request Aug 9, 2026
helly25 added a commit that referenced this pull request Aug 9, 2026
* docs(#109): park the single-pass verify tally as a deferred refinement

The -hasheq matcher and --summary=hash dedup grouping cover the practical
hash-verification workflow. A one-pass verified/failed tally needs the per-entry
verdict threaded thread-safely through the reduction feed (parallel walk / tsan);
parked with that note until a concrete need appears.

* docs(#109): mark hash-verification workflow done
helly25 added a commit that referenced this pull request Aug 9, 2026
* docs(#109): park the single-pass verify tally as a deferred refinement

The -hasheq matcher and --summary=hash dedup grouping cover the practical
hash-verification workflow. A one-pass verified/failed tally needs the per-entry
verdict threaded thread-safely through the reduction feed (parallel walk / tsan);
parked with that note until a concrete need appears.

* docs(#109): mark hash-verification workflow done

* feat(#169): MemorySanitizer via an instrumented libc++, Linux CI cell

MSan catches reads of uninitialized memory that asan does not. The one real
prerequisite is an instrumented C++ standard library: MSan false-positives on
anything it did not instrument, and while every other dep builds from source
under bazel (so -fsanitize=memory reaches it), libc++ ships prebuilt inside the
hermetic LLVM.

- tools/build_msan_libcxx.sh builds libc++/libc++abi/libunwind from the LLVM
  source release matching bazelmod/llvm.MODULE.bazel's llvm_version, with
  -DLLVM_USE_SANITIZER=MemoryWithOrigins and the hermetic clang as compiler,
  into .msan-libcxx/ (gitignored). It stamps the prefix with version+script hash
  so a cached tree is reused, and skips cleanly on non-Linux.
- .bazelrc --config=msan mirrors asan/tsan and swaps the runtime in via
  -nostdinc++ -isystem .../include/c++/v1 and -nostdlib++ -L... -Wl,-rpath,...
- CI gains one ubuntu `msan` cell (never macOS: MSan has no macOS support) with
  the instrumented libc++ cached on the LLVM version + script hash. It starts
  continue-on-error while first findings are triaged - the same introduction
  path the clang-tidy cell took - then becomes a hard gate.

Also folds the ratified archive control surface into the docs so nothing is left
open before that work starts: --archive[=none|roots|all] + -z/-z+/-z- with find
-> none and the xff family -> roots, dual container identity, --archive-depth=1,
sniff-gating, and the pre-implementation "not yet implemented" guard. Rewrites
the stale smart-case entry (it shipped as --case=smart/-s, not a boolean), and
resolves the remaining design.md TBDs (PCRE2 spelling is --regextype=PCRE2; the
VFS seam is built, remote backends stay post-v1).

* fix(#169): runtimes build needs llvm-lit off / present

The first msan CI run failed at cmake configure: the runtimes CMakeLists
add_subdirectory()s llvm/utils/llvm-lit for its check targets, which the
partial source extraction did not include. Turn the test machinery off
(-DLLVM_INCLUDE_TESTS=OFF; we only want the libraries) and extract llvm/utils
as well so the path exists either way.

* fix(#169): extract the whole LLVM source release for the runtimes build

Second configure/compile failure from the hand-picked extraction: libcxx's
from_chars_floating_point.h includes shared/fp_bits.h, which lives under libc/,
just as the earlier failure needed llvm/utils/llvm-lit. Picking directories is a
losing game - each attempt only surfaces the next missing one - so extract the
whole source release. It costs seconds, the tree is discarded afterwards, and
what CI caches is the INSTALLED runtimes, not the sources.

* fix(#169): wire msan into the done gate

The done job asserts that EVERY declared workflow job is listed in its needs -
a guard against adding a job that silently is not gated - and adding msan
without wiring it tripped exactly that check.

msan belongs in needs like the rest: the gate step only inspects the needs KEY
SET, never the results, and a continue-on-error job cannot fail the run. So
listing it restores the completeness invariant while a red msan still cannot
block a merge, which is the whole point of introducing it report-only.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant