Repository navigation
claude-ops/plugins: sync digest carries per-step timings #4045
Description
Summary
The digest sync-run.sh prints has no timing information. The only way to see where a run spends its time is to read the run journal's file mtimes after the fact. On a clean single-marketplace run on 2026-09-08 (77 installs, Claude Code 2.1.263, claude-ops 0.45.2) those mtimes gave:
| Step | Seconds |
|---|---|
pre-refresh fleet-state.sh read |
0.08 |
claude plugin marketplace update |
1.7 |
in-repo claude plugin update (1 record) |
1.2 |
cache-content-check.sh (77 installs) |
1.9 |
| whole script | 5.3 |
A run that reports its own step costs lets the next optimization be chosen from a measurement instead of a guess, and lets a slow marketplace refresh or a slow cache check be named in the report.
Fix
Stamp source, verified 2026-09-11 against the GNU bash manual (Edition 5.3), bash 5.0 NEWS, the GNU coreutils manual, and Apple's date(1) man page:
- Use
EPOCHREALTIMEwhen it is set: bash 5.0 added it, and the manual documents it as "the number of seconds since the Unix Epoch as a floating-point value with micro-second granularity". Resolution recorded asmicroseconds. - Else run
date +%s.%Nand accept the value only when it matches^[0-9]+\.[0-9]+$.%Nis a GNU extension that current Appledate(1)also documents, but an olderdatecan print a literalN, so the output is validated before it is trusted. Resolution recorded asnanoseconds. - Else fall back to
date +%s, the only form everydatedocuments, with resolution recorded asseconds. macOS system bash is 3.2.57, which has noEPOCHREALTIME, so this path is real on that platform and is acceptable: the timings are a diagnostic measurement with no gate. - Normalize the radix before arithmetic: the manual does not specify whether
EPOCHREALTIMEfollowsLC_NUMERIC, so read it withLC_ALL=Cwhere the shell honours it and rewrite any,to., so the digest field is always a JSON number.
Digest shape:
- Add a
timingsobject to each marketplace block:pre_refresh_read,marketplace_update,in_repo_update,user_sweep,install_enable,cache_content_check,post_read, andtotal, each seconds with three decimals; plusresolutionnaming which tier produced the stamps. Add a top-leveltotalfor the whole invocation. - Let the rendered report (claude-ops/plugins: sync-run.sh renders the Step 6 report itself; the model appends only reload guidance #4043) print one
Timing:row when a step exceeds a documented threshold; until then the field is data only.
Acceptance criteria
- The digest carries
timingswith the eight keys above andresolutionon every marketplace block, each timing a non-negative number of seconds. - IF a step did not run, because
--auditpredicted it or the policy stopped before Step 4, THEN its timing isnull, never0. - IF
date +%s.%Nprints anything other than digits, one dot, digits, THEN the run falls back todate +%sand recordsresolution: seconds. sync-run.test.shasserts the keys are present, every non-null value is non-negative,totalis at least the sum of the non-null step values, and a stubbeddatethat prints a literalNyieldsresolution: seconds.- The
--only-installre-entry reports timings for the steps it ran andnullfor the ones the first invocation ran.
Verification
plugins/claude-ops/skills/plugins/scripts/sync-run.test.sh and scripts/affected-tests.sh --run on the changed files; one live sync whose timings.total agrees with the journal mtimes to within a second.
Related
- claude-ops:plugins sync — an already-current fleet costs ~7s of script work but a full 52KB doc load, and the run cannot reproduce its own report after a compaction #3728 measured the same steps by hand with
date +%s.%Nand asked for the journal; this makes the measurement part of the run. - claude-ops/plugins: sync-run.sh renders the Step 6 report itself; the model appends only reload guidance #4043 (script-rendered report) is where a
Timing:row would land; blocked by claude-ops/plugins: cache-content-check.test.sh process-budget probe measures -1 in the Claude Code on the web container #4046 (native dependency edge set).
Activity
feat(claude-ops): stamp every sync step and carry per-step timings in…