You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Browse filesBrowse the repository at this point in the historyBrowse files
FZ2000
committed
docs: correct behavioural contracts the code stopped matching
An audit compared every prose claim against the implementation. These are
the ones a scripting user would have been burned by. In every case the
code is right and the documentation was describing an older version.
EXIT CODES — eleven places across five files
sync.md, search.md x3, inspect.md x4, auth.md and config.md all said an
unhandled HTTPError escapes as a Python traceback and exits 1, "not the
2 that the rest of the CLI uses". main() has caught it since the
error-advice refactor:
except urllib.error.HTTPError as e:
specific = e.code in (401, 403, 404, 429) or 500 <= e.code < 600
_fail_with_context(_advice_for_http(e), offer_traceback=not specific)
and _fail_with_context ends in sys.exit(2), with the traceback written at
debug level so -v shows it and a default run does not. Measured, not
assumed — stubbing a 400 into `da daily`:
[error] DeviantArt refused the request (HTTP 400 Bad Request).
ACTUAL exit code: 2
The docs told anyone writing `case $? in` to branch on a code the CLI
never returns. PermissionError was documented the same way and is
likewise caught, as an OSError.
Note what was NOT changed: `config get` exiting 1 for an unset key,
`auth status` exiting 1 on warn, `sync watched` exiting 1 on partial
failure, and exit-codes.md's statement that a genuinely unrecognised
exception is left alone with its traceback. Those are all still true —
main()'s except chain does not catch, say, a ValueError.
`da whoami` was described as calling the API directly "rather than
through the retry-on-401 wrapper", so a server-side revocation supposedly
produced a traceback. cmd_whoami goes through authed_http_json and
recovers by itself. The confusion is understandable: cmd_auth_status DOES
call http_json directly a few hundred lines earlier, deliberately,
because its job is to test the token as-is.
RESUME — sync.md, four passages
"There is no per-artist 'how far did I get' marker in state.json …
Resuming is manual." There is: _record_gallery_progress writes
state["galleries"][<artist>], _gallery_progress reads it back, and a
truncated walk logs `resuming alice at offset 96 (previous walk did not
finish)`. The early all-known exit is gated on the artist being recorded
complete, precisely so it cannot strand the rest of the gallery.
`sync watched` was documented as walking every artist at "--offset 0,
always". It passes offset=None deliberately, with a comment saying why —
pinning to 0 would defeat per-artist resume across a backfill, which is
the case that needs it most. docs/reference/files-on-disk.md already
documented the marker, so sync.md contradicted both the code and a
sibling page.
`da auth status` — documented as offline
"no network, no locks, one small object on stdout" and "Nothing else is
consulted — not the config file, not the network, not DeviantArt". It
resolves an access token and calls /placebo, which means it takes the
token lock and, on an hourly polling cadence, rotates the refresh token
on almost every call. The page now says so, and documents the two states
it can return that were missing from the three-key table — `revoked` and
`unreachable`, plus the `error` key that accompanies them. The
distinction matters: `unreachable` is why a dropped wifi link does not
send someone to re-authenticate.
SMALLER, ALL VERIFIED AGAINST THE CODE
- "Eleven commands accept --json" — thirteen do, walking build_parser().
The list also omitted `daily` and `search user`.
- "in particular DA_JITTER is read by install_schedule.sh … never by
`da` itself" — config.py's env_map has DA_JITTER. It was documented
under a heading that says the opposite; now listed as the config
override it is, in the table with the other four.
- "NO_COLOR … Honoured regardless of --color" — it is consulted only in
the `auto` branch; `--color always` sets colour unconditionally, which
is what makes forcing colour into a pipe possible.
- config.md said values are "not validated or converted" and that `set`
has no exit 2. Measured: `da config set jitter 40%` prints
`[error] jitter must be a number — got '40%'` and exits 2. A
non-object config.json warns and is replaced at exit 0, rather than
raising the documented TypeError.
Verified: lychee 194 OK / 0 errors with fragments checked, doc references
and doc flag tables both clean. One of my own edits introduced a broken
`#configuration` anchor and then a pointer to a table DA_JITTER was not
actually in; lychee caught the first and the second is fixed by adding
the row.
Copy file name to clipboardExpand all lines: docs/commands/sync.md
+36-25Lines changed: 36 additions & 25 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -198,10 +198,13 @@ corrupt index, or a 401 that survives a forced token refresh. `0` — with
198
198
a `skipping:` message — when another sync already holds the lock.
199
199
200
200
One deviation from the norm: an unexpected HTTP status from the feed
201
-
endpoint (anything that is not 429 or a retried 5xx) is **not** caught.
202
-
It escapes as a Python traceback and the process exits `1`, not `2`.
203
-
`sync artist` handles the same situation differently, which is worth
204
-
knowing before you write a wrapper script around either.
201
+
endpoint (anything that is not 429 or a retried 5xx) is not handled
202
+
inside the walk. It reaches `main()`'s backstop handler, which prints a
203
+
one-line explanation and exits `2` — the same code the rest of the CLI
204
+
uses for "could not do the job". No traceback, unless you pass `-v`.
205
+
`sync artist` handles the same situation inside the walk instead,
206
+
recording a stop reason; the difference matters if you parse the summary
207
+
line, not if you branch on the exit code.
205
208
206
209
## da sync artist
207
210
@@ -255,21 +258,25 @@ Two details make the early stop safe rather than merely fast:
255
258
- Known ids are filtered out before the metadata batch, so a page that
256
259
is 23/24 duplicates costs one metadata call for one deviation.
257
260
258
-
What the early stop does **not** do is compensate for a walk that
259
-
stopped part-way down. There is no per-artist "how far did I get" marker
260
-
in `state.json`; the index is the only memory, and it says nothing about
261
-
order. So if a run is truncated by its time budget at offset 96, the
262
-
newest four pages are indexed and everything older is not — and the next
263
-
plain run reads page 0, finds it entirely known, reports `caught up` and
264
-
stops without ever requesting offset 96 again. Resuming is manual, which
265
-
is why the summary is followed by a
266
-
`resume: da sync artist <name> --offset <n>` line whenever the walk
267
-
stopped for a non-terminal reason.
268
-
269
-
The same offset is recorded in `state.json` as `last_sync.last_offset`
270
-
(only for non-terminal stops, so `gallery complete` and `caught up` do
271
-
not record one). Re-run with that `--offset`, or with `--full`, or the
272
-
rest of the gallery stays unsynced.
261
+
A walk that stops part-way down **is** resumed automatically. The
262
+
per-artist position is recorded in `state.json` under
263
+
`galleries.<artist>`, so if a run is truncated by its time budget at
264
+
offset 96, the next plain run starts there rather than at page 0:
265
+
266
+
```text
267
+
resuming alice at offset 96 (previous walk did not finish)
268
+
```
269
+
270
+
That marker is also what stops the early "everything on this page is
271
+
known" exit from stranding the rest of the gallery — the early stop is
272
+
gated on the artist being recorded as complete, so a truncated walk
273
+
keeps going instead of reporting `caught up` at page 0.
274
+
275
+
`--offset` still overrides it when you pass one explicitly, and `--full`
276
+
still ignores it and walks from the top. The summary's
277
+
`resume: da sync artist <name> --offset <n>` line is a convenience for
278
+
running the rest immediately; you do not need it for the next scheduled
279
+
run to make progress.
273
280
274
281
**`--full`** disables the early stop only. It walks every page to the
275
282
end of the gallery but still skips anything the index already knows, so
@@ -408,8 +415,12 @@ daily job; `sync feed` is.
408
415
|`--concurrency CONCURRENCY`| int | config `concurrency`, else `4`| Passed to each artist walk, clamped to 1–16. |
409
416
|`--dry-run`| flag | off | Passed to each artist walk. Nothing is written and the index is untouched, but every gallery is still paged and metadata is still fetched. |
410
417
411
-
There is no `--limit` and no `--offset`. Each artist is walked with
412
-
`--limit 24` and `--offset 0`, always.
418
+
There is no `--limit` and no `--offset`. Each artist is walked at the
419
+
gallery page cap, and — deliberately — with no offset supplied, so each
420
+
one resumes its own unfinished walk from the position recorded in
421
+
`state.json`. Passing `0` here would pin every artist to page 0 and
422
+
defeat that, which is precisely the case a backfill across many artists
423
+
needs most.
413
424
414
425
### Behaviour
415
426
@@ -448,10 +459,10 @@ for even one page, so the rest are skipped with a warning reading
448
459
449
460
Skipped artists are not failures. Re-running picks up where it stopped in
450
461
the useful sense: the artists already walked are all-known, so each costs
451
-
one API call before the walk moves on — but a *truncated* artist, one cut
452
-
off mid-gallery, is not resumed, for exactly the reason described under
453
-
[`sync artist`](#behaviour-1). Its remaining pages need
454
-
`da sync artist <name> --offset N` or `--full`.
462
+
one API call before the walk moves on, and a *truncated* artist — one cut
463
+
off mid-gallery — picks up where it left off, for the reason described
464
+
under [`sync artist`](#behaviour-1). Nothing needs doing by hand; a
465
+
backfill across many artists converges over successive scheduled runs.
455
466
456
467
**Failure handling.** Each artist runs inside a `try`. An artist that
457
468
exits or crashes is logged, counted as failed, and the run moves on to
0 commit comments