CLI: exhaustive agent-grade test of the whole calternal CLI surface (machine output, exit codes, idempotency, failure modes) #350

Closed
opened 2026-09-28 15:11:38 +00:00 by kayg · 21 comments
Owner

Owner (2026-09-28): 'please extensively test the CLI surface as it's something that will mostly be used by agents!' The CLI (crates/calternal-cli, binary calternal) exposes: login, logout, whoami, sync (add/remove/status/pause/resume/conflicts), ls, cat, put, get, mv, rm, trash, restore, versions, search, share, today, note (new/open/append), log. Agents (the per-user agent container, Codex/Claude, MCP later) will drive it non-interactively, so agent-grade means:
1. Contract for every command (write it into crates/calternal-cli/README.md and --help):

  • --json on EVERY command, with a documented, stable schema (version field), and no human text mixed into stdout when --json is set; diagnostics go to stderr only.
  • Documented exit codes (0 ok; distinct codes for usage error, not logged in, not found, conflict, permission/scope denied, quota exceeded, rate limited, network/transient, server error), identical across commands.
  • Non-interactive by default when stdin is not a TTY: never prompt; fail fast with a clear error. --yes for destructive confirmation where needed.
  • Idempotency: put of identical content = no-op success; mv/rm of an already-moved/removed path = defined result; retries safe; an --if-match <etag> / --if-none-match option for conditional writes.
  • Retries with backoff and jitter on transient errors (429/502/503/504, connection reset) for EVERY command, bounded, honouring Retry-After (fixes #348's login case generically); --timeout, --retries.
  • Paths: Unicode NFC/NFD, spaces, emoji, very long names, leading dashes (--), .. rejected client-side AND server-side; stdin/stdout streaming (put -, cat binary-safe, large files with bounded memory).
  • --server/env config (CALTERNAL_SERVER, token from env or keyring for headless agents; document the precedence); no secrets in logs or error messages.
    2. Test suite (crates/calternal-cli/tests/ against a real local server, not mocks): every command × happy path × each failure mode above; golden JSON schemas; a fault-injection proxy (drop, delay, 429/502/503, truncated body, TLS error) proving retries and exit codes; concurrency (two CLIs writing the same file → conflict, not corruption); sync edge cases (rename cycles, case-only renames on macOS/Linux, deletes during upload, 10k small files, a 2 GB file resumable across a kill -9); offline behaviour.
    3. Agent dogfood: script a realistic agent session (search → cat → edit → put with if-match → log → verify) and run it 100 times with random faults; zero corrupted files, zero unexplained exit codes.
    4. Cross-user (#331): the CLI can never touch another user's paths; scoped app passwords (#328) once merged.
    Report: a table of commands × (json, exit codes, retries, idempotent, tested), every bug fixed or filed. CLI bugs found by the paperless import (#342) go here too.
Owner (2026-09-28): 'please extensively test the CLI surface as it's something that will mostly be used by agents!' The CLI (`crates/calternal-cli`, binary `calternal`) exposes: login, logout, whoami, sync (add/remove/status/pause/resume/conflicts), ls, cat, put, get, mv, rm, trash, restore, versions, search, share, today, note (new/open/append), log. Agents (the per-user agent container, Codex/Claude, MCP later) will drive it non-interactively, so **agent-grade** means: **1. Contract for every command** (write it into `crates/calternal-cli/README.md` and `--help`): - `--json` on EVERY command, with a documented, stable schema (version field), and no human text mixed into stdout when --json is set; diagnostics go to stderr only. - **Documented exit codes** (0 ok; distinct codes for usage error, not logged in, not found, conflict, permission/scope denied, quota exceeded, rate limited, network/transient, server error), identical across commands. - **Non-interactive by default** when stdin is not a TTY: never prompt; fail fast with a clear error. `--yes` for destructive confirmation where needed. - **Idempotency:** `put` of identical content = no-op success; `mv`/`rm` of an already-moved/removed path = defined result; retries safe; an `--if-match <etag>` / `--if-none-match` option for conditional writes. - **Retries with backoff and jitter** on transient errors (429/502/503/504, connection reset) for EVERY command, bounded, honouring Retry-After (fixes #348's login case generically); `--timeout`, `--retries`. - Paths: Unicode NFC/NFD, spaces, emoji, very long names, leading dashes (`--`), `..` rejected client-side AND server-side; stdin/stdout streaming (`put -`, `cat` binary-safe, large files with bounded memory). - `--server`/env config (`CALTERNAL_SERVER`, token from env or keyring for headless agents; document the precedence); no secrets in logs or error messages. **2. Test suite** (`crates/calternal-cli/tests/` against a real local server, not mocks): every command × happy path × each failure mode above; golden JSON schemas; a **fault-injection proxy** (drop, delay, 429/502/503, truncated body, TLS error) proving retries and exit codes; concurrency (two CLIs writing the same file → conflict, not corruption); sync edge cases (rename cycles, case-only renames on macOS/Linux, deletes during upload, 10k small files, a 2 GB file resumable across a kill -9); offline behaviour. **3. Agent dogfood:** script a realistic agent session (search → cat → edit → put with if-match → log → verify) and run it 100 times with random faults; zero corrupted files, zero unexplained exit codes. **4. Cross-user** (#331): the CLI can never touch another user's paths; scoped app passwords (#328) once merged. Report: a table of commands × (json, exit codes, retries, idempotent, tested), every bug fixed or filed. CLI bugs found by the paperless import (#342) go here too.
Author
Owner

Starting #350 on branch job/cli-agent, based on dev at eb4ff20a9862a627f6d0aea1ca617ca9a9491bce.

Initial evidence: the issue's #348 reproduction is confirmed by crates/calternal-cli/src/login.rs: device-token polling returns an error for any non-202 status. I am tracing command output, error mapping, shared HTTP paths, and existing local-server campaigns before adding failing tests.

Starting #350 on branch `job/cli-agent`, based on `dev` at `eb4ff20a9862a627f6d0aea1ca617ca9a9491bce`. Initial evidence: the issue's #348 reproduction is confirmed by `crates/calternal-cli/src/login.rs`: device-token polling returns an error for any non-202 status. I am tracing command output, error mapping, shared HTTP paths, and existing local-server campaigns before adding failing tests.
Author
Owner

Owner correction (2026-09-28): human, pretty output is the default for every command; --json is an explicit flag on every command that switches to machine output. Do NOT auto-switch to JSON when stdout is not a TTY. (Plain text off-TTY is fine: no colours/escape codes when stdout is not a TTY, but the same human layout.) Everything else in the contract stays: stable JSON schemas behind --json, documented exit codes, diagnostics on stderr, non-interactive when stdin is not a TTY.

Owner correction (2026-09-28): **human, pretty output is the default for every command; `--json` is an explicit flag on every command** that switches to machine output. Do NOT auto-switch to JSON when stdout is not a TTY. (Plain text off-TTY is fine: no colours/escape codes when stdout is not a TTY, but the same human layout.) Everything else in the contract stays: stable JSON schemas behind --json, documented exit codes, diagnostics on stderr, non-interactive when stdin is not a TTY.
Author
Owner

Finding: issue #350's newest comment requires explicit and human-readable defaults. Evidence in : non-JSON success currently uses , so it is still JSON; JSON success/error responses have no schema version. is absent. The existing real-server CLI probe in covers cursed AGENTS.md
CLAUDE.md
CONTEXT.md
CONTRIBUTING.md
Cargo.lock
Cargo.toml
Containerfile
LICENSE
NOTICE
README.md
apps
assets
bench
bun.lock
contracts
crates
deploy
docs
fuzz
package.json
packages
rust-toolchain.toml
scripts
target
tests names and a 40 MiB transfer, but not output versioning or a command contract matrix. I am adding those contract checks and documentation, then I will extend the real-server probe.

Finding: issue #350's newest comment requires explicit and human-readable defaults. Evidence in : non-JSON success currently uses , so it is still JSON; JSON success/error responses have no schema version. is absent. The existing real-server CLI probe in covers cursed AGENTS.md CLAUDE.md CONTEXT.md CONTRIBUTING.md Cargo.lock Cargo.toml Containerfile LICENSE NOTICE README.md apps assets bench bun.lock contracts crates deploy docs fuzz package.json packages rust-toolchain.toml scripts target tests names and a 40 MiB transfer, but not output versioning or a command contract matrix. I am adding those contract checks and documentation, then I will extend the real-server probe.
Author
Owner

Correction: the previous progress comment was mangled by shell command substitution. Please ignore it.

Finding: issue #350's newest comment requires explicit --json and human-readable defaults. Evidence in crates/calternal-cli/src/main.rs: non-JSON success currently uses serde_json::to_string_pretty, so it is still JSON; JSON success and error responses have no schema version. crates/calternal-cli/README.md is absent. The existing real-server CLI probe in tests/adversarial/attack2.py covers cursed ls names and a 40 MiB transfer, but not output versioning or a command contract matrix. I am adding those contract checks and documentation, then I will extend the real-server probe.

Correction: the previous progress comment was mangled by shell command substitution. Please ignore it. Finding: issue #350's newest comment requires explicit `--json` and human-readable defaults. Evidence in `crates/calternal-cli/src/main.rs`: non-JSON success currently uses `serde_json::to_string_pretty`, so it is still JSON; JSON success and error responses have no schema version. `crates/calternal-cli/README.md` is absent. The existing real-server CLI probe in `tests/adversarial/attack2.py` covers cursed `ls` names and a 40 MiB transfer, but not output versioning or a command contract matrix. I am adding those contract checks and documentation, then I will extend the real-server probe.
Author
Owner

Finding: the CLI read CALTERNAL_TOKEN directly and passed it to the requested server, bypassing calternal-sync::load_token's CALTERNAL_TOKEN_SERVER binding. The new subprocess test first failed with network exit code 9 against a different loopback server; after routing all CLI commands through Api::open/load_token, it fails fast with exit code 3 and does not print the credential. The real-server adversarial fixture must also set the matching server binding.

Finding: the CLI read `CALTERNAL_TOKEN` directly and passed it to the requested server, bypassing `calternal-sync::load_token`'s `CALTERNAL_TOKEN_SERVER` binding. The new subprocess test first failed with network exit code 9 against a different loopback server; after routing all CLI commands through `Api::open`/`load_token`, it fails fast with exit code 3 and does not print the credential. The real-server adversarial fixture must also set the matching server binding.
Author
Owner

Finding: ls, cat, versions, share, and note new --folder sent .. paths to the server without local validation. The new path-boundary test first reached the closed loopback port for ls and returned a network error. Those commands now use the shared calternal-path::RelPath validator before network I/O; the regression test passes for all five paths.

Finding: `ls`, `cat`, `versions`, `share`, and `note new --folder` sent `..` paths to the server without local validation. The new path-boundary test first reached the closed loopback port for `ls` and returned a network error. Those commands now use the shared `calternal-path::RelPath` validator before network I/O; the regression test passes for all five paths.
Author
Owner

Finding: issue #350 requires --timeout, bounded --retries, backoff with jitter, and Retry-After handling across CLI HTTP commands. I added a shared retry helper in calternal-sync, wired the CLI and login flow to it, and documented defaults of 120 seconds and 3 retries (maximum 5). The CLI retries 429/502/503/504 responses; connection failures retry only for safe methods because replaying an ambiguous mutation can duplicate a committed action. calternal-sync::RemoteClient::new() retains its existing behavior; the CLI uses the new policy constructor. Evidence: cargo test -p calternal-sync passed (54 library tests, 2 daemon tests); cargo test -p calternal-cli passed (17 unit tests, 10 integration tests), including Retry-After delay and dropped-connection coverage; clippy passed for both touched crates with warnings denied; cargo fmt --all -- --check passed. Commit: 0c38b723.

Finding: issue #350 requires `--timeout`, bounded `--retries`, backoff with jitter, and `Retry-After` handling across CLI HTTP commands. I added a shared retry helper in `calternal-sync`, wired the CLI and login flow to it, and documented defaults of 120 seconds and 3 retries (maximum 5). The CLI retries 429/502/503/504 responses; connection failures retry only for safe methods because replaying an ambiguous mutation can duplicate a committed action. `calternal-sync::RemoteClient::new()` retains its existing behavior; the CLI uses the new policy constructor. Evidence: `cargo test -p calternal-sync` passed (54 library tests, 2 daemon tests); `cargo test -p calternal-cli` passed (17 unit tests, 10 integration tests), including Retry-After delay and dropped-connection coverage; clippy passed for both touched crates with warnings denied; `cargo fmt --all -- --check` passed. Commit: `0c38b723`.
Author
Owner

Final-gate finding: the first full workspace cargo clippy --all-targets -- -D warnings attempt failed in calternal-server because apps/web/build was absent. Frontend is embedded from that directory (crates/calternal-server/src/main.rs:63-65), and RustEmbed consequently emitted no get method; the diagnostics were at security.rs, main.rs and wire.rs. This is a missing generated build prerequisite, not a CLI source error. I am building the web bundle before rerunning final gates.

Final-gate finding: the first full workspace `cargo clippy --all-targets -- -D warnings` attempt failed in `calternal-server` because `apps/web/build` was absent. `Frontend` is embedded from that directory (`crates/calternal-server/src/main.rs:63-65`), and RustEmbed consequently emitted no `get` method; the diagnostics were at `security.rs`, `main.rs` and `wire.rs`. This is a missing generated build prerequisite, not a CLI source error. I am building the web bundle before rerunning final gates.
Author
Owner

Final-gate test finding: the first workspace cargo test attempt stopped during compilation before tests ran. Its only errors were rustc failing to write dependency files in temporary directories under another worktree (/home/kayg/Developer/calternal-wt/search-d/target/tmp/sccache.../deps.d: No such file or directory). This environment has RUSTC_WRAPPER=/home/kayg/.nix-profile/bin/sccache; the CLI worktree TMPDIR itself resolved correctly to cli-agent/target/tmp. No test assertion failed. I am recording this as a shared-host build failure and continuing the remaining required checks.

Final-gate test finding: the first workspace `cargo test` attempt stopped during compilation before tests ran. Its only errors were rustc failing to write dependency files in temporary directories under another worktree (`/home/kayg/Developer/calternal-wt/search-d/target/tmp/sccache.../deps.d`: No such file or directory). This environment has `RUSTC_WRAPPER=/home/kayg/.nix-profile/bin/sccache`; the CLI worktree TMPDIR itself resolved correctly to `cli-agent/target/tmp`. No test assertion failed. I am recording this as a shared-host build failure and continuing the remaining required checks.
Author
Owner

Findings from real use (Claude, 2026-09-28, CLI on o2 against calternal.cloud), add to this job's scope:

  1. calternal ls prints raw JSON without --json (owner rule: human pretty output by default; JSON only with --json). Check every command for the same.
  2. calternal ls <folder> silently stops at the first page: the response has total: 614 and a next_cursor, but the CLI shows 100 entries and no hint that more exist. An agent will believe the listing is complete. Default: follow next_cursor until done (streaming), with --limit N to cap it; when capped, pretty output prints 'showing N of TOTAL' and JSON keeps next_cursor and total.
  3. #371: put exits 1 after a successful upload when the server stores a canonically equivalent Unicode form (NFC vs NFD); verify by the returned item_id/hash, not by re-stat of the input path. Fix it here (same CLI), and add a regression test.
  4. put cannot set the modified date (paperless created dates were lost). Add --mtime <RFC3339> if the API supports it; otherwise file the API gap.
Findings from real use (Claude, 2026-09-28, CLI on o2 against calternal.cloud), add to this job's scope: 1. `calternal ls` prints raw JSON **without** `--json` (owner rule: human pretty output by default; JSON only with `--json`). Check every command for the same. 2. `calternal ls <folder>` silently stops at the first page: the response has `total: 614` and a `next_cursor`, but the CLI shows 100 entries and no hint that more exist. An agent will believe the listing is complete. Default: follow `next_cursor` until done (streaming), with `--limit N` to cap it; when capped, pretty output prints 'showing N of TOTAL' and JSON keeps `next_cursor` and `total`. 3. #371: `put` exits 1 after a successful upload when the server stores a canonically equivalent Unicode form (NFC vs NFD); verify by the returned item_id/hash, not by re-stat of the input path. Fix it here (same CLI), and add a regression test. 4. `put` cannot set the modified date (paperless created dates were lost). Add `--mtime <RFC3339>` if the API supports it; otherwise file the API gap.
Author
Owner

Continuing #350 on job/cli-agent. Current HEAD: 40271a559b287a0da76430068c47ac68b20dec6a; current merge base with dev: cf919dd3bfb13e8bb09a923b61f359c861b20bd9. The worktree was clean at start.

I re-read the issue and all 10 comments, including the four real-use findings. Initial trace confirms ls fetches only one Files page, put verifies by re-statting the submitted path after upload, and the existing Files upload API already accepts source mtime metadata. I am adding targeted regression coverage for the reported output, pagination, Unicode normalization, and RFC3339 mtime behavior before changing implementation.

Continuing #350 on `job/cli-agent`. Current HEAD: `40271a559b287a0da76430068c47ac68b20dec6a`; current merge base with `dev`: `cf919dd3bfb13e8bb09a923b61f359c861b20bd9`. The worktree was clean at start. I re-read the issue and all 10 comments, including the four real-use findings. Initial trace confirms `ls` fetches only one Files page, `put` verifies by re-statting the submitted path after upload, and the existing Files upload API already accepts source mtime metadata. I am adding targeted regression coverage for the reported output, pagination, Unicode normalization, and RFC3339 mtime behavior before changing implementation.
Author
Owner

Finding 1 and 2 evidence: before the fix, Api::ls sent one GET with only path; it did not request or follow next_cursor. The new subprocess regression first observed one request where two were required, and ls --limit 1 returned usage exit 2 because the option did not exist. The branch already had the shared human renderer, but the real-server ls check was the only route-specific human-output coverage; the new local-server test now asserts that default ls writes readable text. The focused listing tests pass after the change. I am also extending tests/adversarial/attack2.py to cover pagination against the real server.

Finding 1 and 2 evidence: before the fix, `Api::ls` sent one GET with only `path`; it did not request or follow `next_cursor`. The new subprocess regression first observed one request where two were required, and `ls --limit 1` returned usage exit 2 because the option did not exist. The branch already had the shared human renderer, but the real-server `ls` check was the only route-specific human-output coverage; the new local-server test now asserts that default `ls` writes readable text. The focused listing tests pass after the change. I am also extending `tests/adversarial/attack2.py` to cover pagination against the real server.
Author
Owner

Upload findings with regression evidence:

  • #371: the local Files endpoint returned an installed NFD path in the successful TUS response. The current CLI then queried the original NFC input path, received HTTP 404, and exited 4 after the upload had completed. The regression asserts verification uses the server-returned path and returns its item ID and hash.
  • Mtime: put --mtime 2020-01-02T03:04:05+02:30 currently fails argument parsing with exit 2. The Files upload API already parses and applies source-mtime-ns; I am wiring the CLI option to that existing contract and checking its encoded upload metadata.
Upload findings with regression evidence: - #371: the local Files endpoint returned an installed NFD path in the successful TUS response. The current CLI then queried the original NFC input path, received HTTP 404, and exited 4 after the upload had completed. The regression asserts verification uses the server-returned path and returns its item ID and hash. - Mtime: `put --mtime 2020-01-02T03:04:05+02:30` currently fails argument parsing with exit 2. The Files upload API already parses and applies `source-mtime-ns`; I am wiring the CLI option to that existing contract and checking its encoded upload metadata.
Author
Owner

The upload regressions are fixed in commit d85ee338.

  • Successful TUS responses provide the server-installed path. The CLI validates that path, resolves the installed Files item there, verifies its hash, and returns its item ID. This covers NFC input stored as NFD and zero-length uploads.
  • put --mtime <RFC3339> parses the offset timestamp to signed nanoseconds and sends the existing Files API source-mtime-ns metadata. An explicit mtime uploads identical bytes so it can update the modified date.

The small calternal-sync API addition returns the installed path from existing TUS response headers; existing upload methods keep their prior return values and behavior. Regression evidence: cargo test -p calternal-sync passed (55 library tests, 2 daemon tests); cargo test -p calternal-cli passed (18 unit tests, 15 integration tests). The real-server probe now checks 101-entry pagination, capped output, and initial and mtime-only uploads. Final gates and the single adversarial round remain.

The upload regressions are fixed in commit `d85ee338`. - Successful TUS responses provide the server-installed path. The CLI validates that path, resolves the installed Files item there, verifies its hash, and returns its item ID. This covers NFC input stored as NFD and zero-length uploads. - `put --mtime <RFC3339>` parses the offset timestamp to signed nanoseconds and sends the existing Files API `source-mtime-ns` metadata. An explicit mtime uploads identical bytes so it can update the modified date. The small `calternal-sync` API addition returns the installed path from existing TUS response headers; existing upload methods keep their prior return values and behavior. Regression evidence: `cargo test -p calternal-sync` passed (55 library tests, 2 daemon tests); `cargo test -p calternal-cli` passed (18 unit tests, 15 integration tests). The real-server probe now checks 101-entry pagination, capped output, and initial and mtime-only uploads. Final gates and the single adversarial round remain.
Author
Owner

The adversarial CLI section did not reach product checks in the motion-spring round. tests/adversarial/attack2.py invokes the binary at the hard-coded path <repo>/target/debug/calternal; this job is required to use CARGO_TARGET_DIR=/mnt/hdd/targets/jobs/motion-spring, so the subprocess raised FileNotFoundError before the first CLI command. This is a harness path mismatch, not a CLI behavior result. The selected run continued into the remaining round-two sections.

The adversarial CLI section did not reach product checks in the motion-spring round. `tests/adversarial/attack2.py` invokes the binary at the hard-coded path `<repo>/target/debug/calternal`; this job is required to use `CARGO_TARGET_DIR=/mnt/hdd/targets/jobs/motion-spring`, so the subprocess raised `FileNotFoundError` before the first CLI command. This is a harness path mismatch, not a CLI behavior result. The selected run continued into the remaining round-two sections.
Author
Owner

Real local adversarial round on job/cli-agent after the dev merge:

ADVERSARIAL_ROUND2_ONLY=1 ROUND2_SECTIONS=feed,cli ADVERSARIAL_SKIP_WEB_BUILD=1 tests/adversarial/run.sh

Result: exit 1, 3 findings, 0 SLOW findings. The feed section reported no finding. The three CLI reports are not confirmed product defects:

  • cli human ls: the fixture uploads names with newline, tab, and ESC, but ignores each upload response. calternal_fs::normalize_new_name rejects control characters. The CLI listed 2 accepted entries, so this fixture did not exercise escaping. The listing renderer passes names through human_scalar, which escapes newline and control characters. I left the existing fixture expectation unchanged.
  • cli retry after 503 and cli retry after connection reset: the probe documentation identifies bearer A as the owner, cli_env exports bearer A, and the fault proxy's second request reads /api/v1/auth/me using token A. Both requests reached the proxy (the probe did not report an unexpected request count), and the returned username was owner; the assertion expects agent. I left the existing assertion unchanged.

These two fixture mismatches are recorded for owner review under the rule against changing existing test expectations without an approved behavior change. The single adversarial round is complete; I did not rerun it.

Real local adversarial round on `job/cli-agent` after the `dev` merge: `ADVERSARIAL_ROUND2_ONLY=1 ROUND2_SECTIONS=feed,cli ADVERSARIAL_SKIP_WEB_BUILD=1 tests/adversarial/run.sh` Result: exit 1, 3 findings, 0 SLOW findings. The `feed` section reported no finding. The three CLI reports are not confirmed product defects: - `cli human ls`: the fixture uploads names with newline, tab, and ESC, but ignores each upload response. `calternal_fs::normalize_new_name` rejects control characters. The CLI listed 2 accepted entries, so this fixture did not exercise escaping. The listing renderer passes names through `human_scalar`, which escapes newline and control characters. I left the existing fixture expectation unchanged. - `cli retry after 503` and `cli retry after connection reset`: the probe documentation identifies bearer A as the owner, `cli_env` exports bearer A, and the fault proxy's second request reads `/api/v1/auth/me` using token A. Both requests reached the proxy (the probe did not report an unexpected request count), and the returned username was `owner`; the assertion expects `agent`. I left the existing assertion unchanged. These two fixture mismatches are recorded for owner review under the rule against changing existing test expectations without an approved behavior change. The single adversarial round is complete; I did not rerun it.
Author
Owner

kayg/calternal#350 final report

Branch: job/cli-agent
Head: 7d64a5a78e2b81b4a2eab1b30e83d32c1b55aff0
Push: git push origin job/cli-agent completed successfully. The branch includes the required dev merge (8724942a).

Built

  • ls now defaults to readable human output and follows Files cursors in 100-entry pages. --limit N caps output, reports showing N of TOTAL in human mode, and preserves next_cursor and total in JSON.
  • put --mtime RFC3339 sends source mtime in the existing TUS metadata. A mtime-only change still uploads. Verification follows the server-reported installed path and returns the installed item identity and hash, so Unicode normalization does not cause a false failure.
  • Added renderer coverage for hostile names returned by a server. human_scalar escapes control characters in human ls output.

Files changed from dev:

  • Cargo.lock
  • crates/calternal-cli/Cargo.toml
  • crates/calternal-cli/README.md
  • crates/calternal-cli/src/login.rs
  • crates/calternal-cli/src/main.rs
  • crates/calternal-cli/src/remote_commands.rs
  • crates/calternal-cli/tests/output_contract.rs
  • crates/calternal-sync/src/lib.rs
  • crates/calternal-sync/src/remote.rs
  • tests/adversarial/attack2.py

Commits: 40657b50 (paged listings), d85ee338 (upload identity and mtime), 8724942a (merge dev), 7d64a5a7 (escaped-name renderer test).

Decisions

  • The Files page size is 100, matching the API default. A capped JSON listing keeps the server cursor and total. A later-page error leaves valid partial JSON and returns an error.
  • --mtime accepts RFC3339 and encodes signed nanoseconds. Supplying it forces an upload even when the local and remote content hashes match.
  • Upload verification uses the server-returned installed path because the server can normalize Unicode names.

Gates

These workspace gates ran after the dev merge and before the final test-only commit. The final commit adds no production code; its focused test and formatting check also passed.

cargo fmt --all -- --check: exit 0; no output.

cargo clippy --all-targets -- -D warnings: exit 0. Verbatim output:

   Compiling calternal-server v0.0.1 (/home/kayg/Developer/calternal-wt/cli-agent/crates/calternal-server)
    Checking calternal-plugin-video v0.0.1 (/home/kayg/Developer/calternal-wt/cli-agent/crates/plugins/video)
    Checking calternal-collab v0.0.1 (/home/kayg/Developer/calternal-wt/cli-agent/crates/calternal-collab)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 19.23s

cargo test: exit 0. Verbatim test-result lines from all 74 unit, integration, and doc-test suites:

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
test result: ok. 53 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 55.03s
test result: ok. 18 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.10s
test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.68s
test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.65s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.00s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 8.06s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 117.25s
test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.87s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.48s
test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.31s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 11.47s
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 2.16s
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 30.86s
test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.19s
test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 43.48s
test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.04s
test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 6.39s
test result: ok. 9 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.85s
test result: ok. 17 passed; 0 failed; 4 ignored; 0 measured; 0 filtered out; finished in 0.39s
test result: ok. 37 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 11.43s
test result: ok. 40 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 8.36s
test result: ok. 489 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.42s
test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 4.60s
test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.09s
test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.80s
test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s
test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
test result: ok. 21 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 11.39s
test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.65s
test result: ok. 29 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 6.24s
test result: ok. 48 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.71s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.37s
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.51s
test result: ok. 122 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 152.45s
test result: ok. 103 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 67.82s
test result: ok. 17 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.56s
test result: ok. 42 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 5.72s
test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.08s
test result: ok. 30 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 1.91s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.86s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.10s
test result: ok. 17 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 11.12s
test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.04s
test result: ok. 1 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 5.36s
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 68 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 8.29s
test result: ok. 55 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.56s
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 4.60s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

After the test-only commit, cargo test -p calternal-cli human_listing_escapes_controls_in_server_names passed: test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s. The final cargo fmt --all -- --check and git diff --check both exited 0.

Adversarial round

One local round ran with ROUND2_SECTIONS=feed,cli. It exited 1 with 3 findings and 0 SLOW findings. The feed section reported no findings. Evidence for the three CLI reports is in the preceding issue comment: the hostile-name fixture ignores rejected uploads, and both retry assertions expect agent while the probe sends bearer A (the owner) and confirms two proxy requests. Existing expectations remain unchanged per the owner rule. A focused renderer test now verifies escaping on the listing code path. The adversarial round was not repeated.

Known gaps

The broader CLI coverage in the issue remains incomplete: a successful login/logout session flow, sync service installation, repeated randomized agent-session faults, and a 2 GiB resumable transfer still need coverage. The issue remains open.

# kayg/calternal#350 final report Branch: `job/cli-agent` Head: `7d64a5a78e2b81b4a2eab1b30e83d32c1b55aff0` Push: `git push origin job/cli-agent` completed successfully. The branch includes the required `dev` merge (`8724942a`). ## Built - `ls` now defaults to readable human output and follows Files cursors in 100-entry pages. `--limit N` caps output, reports `showing N of TOTAL` in human mode, and preserves `next_cursor` and `total` in JSON. - `put --mtime RFC3339` sends source mtime in the existing TUS metadata. A mtime-only change still uploads. Verification follows the server-reported installed path and returns the installed item identity and hash, so Unicode normalization does not cause a false failure. - Added renderer coverage for hostile names returned by a server. `human_scalar` escapes control characters in human `ls` output. Files changed from `dev`: - `Cargo.lock` - `crates/calternal-cli/Cargo.toml` - `crates/calternal-cli/README.md` - `crates/calternal-cli/src/login.rs` - `crates/calternal-cli/src/main.rs` - `crates/calternal-cli/src/remote_commands.rs` - `crates/calternal-cli/tests/output_contract.rs` - `crates/calternal-sync/src/lib.rs` - `crates/calternal-sync/src/remote.rs` - `tests/adversarial/attack2.py` Commits: `40657b50` (paged listings), `d85ee338` (upload identity and mtime), `8724942a` (merge `dev`), `7d64a5a7` (escaped-name renderer test). ## Decisions - The Files page size is 100, matching the API default. A capped JSON listing keeps the server cursor and total. A later-page error leaves valid partial JSON and returns an error. - `--mtime` accepts RFC3339 and encodes signed nanoseconds. Supplying it forces an upload even when the local and remote content hashes match. - Upload verification uses the server-returned installed path because the server can normalize Unicode names. ## Gates These workspace gates ran after the `dev` merge and before the final test-only commit. The final commit adds no production code; its focused test and formatting check also passed. `cargo fmt --all -- --check`: exit 0; no output. `cargo clippy --all-targets -- -D warnings`: exit 0. Verbatim output: ```text Compiling calternal-server v0.0.1 (/home/kayg/Developer/calternal-wt/cli-agent/crates/calternal-server) Checking calternal-plugin-video v0.0.1 (/home/kayg/Developer/calternal-wt/cli-agent/crates/plugins/video) Checking calternal-collab v0.0.1 (/home/kayg/Developer/calternal-wt/cli-agent/crates/calternal-collab) Finished `dev` profile [unoptimized + debuginfo] target(s) in 19.23s ``` `cargo test`: exit 0. Verbatim test-result lines from all 74 unit, integration, and doc-test suites: ```text test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s test result: ok. 53 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 55.03s test result: ok. 18 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.10s test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.68s test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.65s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.00s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 8.06s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 117.25s test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.87s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.48s test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.31s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 11.47s test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 2.16s test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 30.86s test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.19s test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 43.48s test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.04s test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 6.39s test result: ok. 9 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.85s test result: ok. 17 passed; 0 failed; 4 ignored; 0 measured; 0 filtered out; finished in 0.39s test result: ok. 37 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 11.43s test result: ok. 40 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 8.36s test result: ok. 489 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.42s test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 4.60s test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.09s test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.80s test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s test result: ok. 21 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 11.39s test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.65s test result: ok. 29 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 6.24s test result: ok. 48 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.71s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.37s test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.51s test result: ok. 122 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 152.45s test result: ok. 103 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 67.82s test result: ok. 17 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.56s test result: ok. 42 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 5.72s test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.08s test result: ok. 30 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 1.91s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.86s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.10s test result: ok. 17 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 11.12s test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.04s test result: ok. 1 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 5.36s test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test result: ok. 68 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 8.29s test result: ok. 55 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.56s test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 4.60s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` After the test-only commit, `cargo test -p calternal-cli human_listing_escapes_controls_in_server_names` passed: `test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s`. The final `cargo fmt --all -- --check` and `git diff --check` both exited 0. ## Adversarial round One local round ran with `ROUND2_SECTIONS=feed,cli`. It exited 1 with 3 findings and 0 SLOW findings. The `feed` section reported no findings. Evidence for the three CLI reports is in the preceding issue comment: the hostile-name fixture ignores rejected uploads, and both retry assertions expect `agent` while the probe sends bearer A (the owner) and confirms two proxy requests. Existing expectations remain unchanged per the owner rule. A focused renderer test now verifies escaping on the listing code path. The adversarial round was not repeated. ## Known gaps The broader CLI coverage in the issue remains incomplete: a successful login/logout session flow, sync service installation, repeated randomized agent-session faults, and a 2 GiB resumable transfer still need coverage. The issue remains open.
Author
Owner

Continuing #350 on job/cli-agent from the existing work. Current HEAD is 7d64a5a78e2b81b4a2eab1b30e83d32c1b55aff0; the current merge base with dev is fba83527f2cccf2334934bb1fd0932be7c0e209b. The worktree is clean. I am verifying the four real-use reports against the committed implementation and regression tests before the final gates.

Continuing #350 on `job/cli-agent` from the existing work. Current HEAD is `7d64a5a78e2b81b4a2eab1b30e83d32c1b55aff0`; the current merge base with `dev` is `fba83527f2cccf2334934bb1fd0932be7c0e209b`. The worktree is clean. I am verifying the four real-use reports against the committed implementation and regression tests before the final gates.
Author
Owner

Post-merge adversarial triage (selected feed,cli,search_hidden pass is still running):

  • feed reported SLOW conditional replace fixture: 14.9s status 201. This is load-only under the job rule.
  • cli human ls reported unescaped terminal controls, but the fixture does not check responses from its five setup uploads (tests/adversarial/attack2.py, cli section). The server listing returned only two accepted names; the existing human_listing_escapes_controls_in_server_names regression exercises the renderer directly. This is the same fixture mismatch recorded earlier, not a confirmed product defect.

No confirmed non-SLOW CLI defect has appeared so far. I am letting the selected pass finish once.

Post-merge adversarial triage (selected `feed,cli,search_hidden` pass is still running): - `feed` reported `SLOW conditional replace fixture: 14.9s status 201`. This is load-only under the job rule. - `cli human ls` reported unescaped terminal controls, but the fixture does not check responses from its five setup uploads (`tests/adversarial/attack2.py`, `cli` section). The server listing returned only two accepted names; the existing `human_listing_escapes_controls_in_server_names` regression exercises the renderer directly. This is the same fixture mismatch recorded earlier, not a confirmed product defect. No confirmed non-SLOW CLI defect has appeared so far. I am letting the selected pass finish once.
Author
Owner

Final report

Finished #350 on job/cli-agent. Merged dev into the job branch, then pushed it. No merge into dev was made.

Head: 3c4caab7bda4e5ff98aa87953a0614fb2d31d91d

Built

  • ls uses human output by default; --json keeps the versioned JSON envelope.
  • ls follows directory cursors across pages. --limit provides a capped listing and preserves its JSON cursor.
  • put --mtime accepts RFC3339 and sends the source mtime in nanoseconds.
  • Upload verification uses the path returned by the server, so NFC/NFD normalization does not create a false mismatch.
  • Added CLI output contract tests for defaults, pagination, Unicode paths, mtime, and retry behavior.

Files in the branch diff from dev: Cargo.lock; crates/calternal-cli/Cargo.toml; crates/calternal-cli/README.md; crates/calternal-cli/src/login.rs; crates/calternal-cli/src/main.rs; crates/calternal-cli/src/remote_commands.rs; crates/calternal-cli/tests/output_contract.rs; crates/calternal-sync/src/lib.rs; crates/calternal-sync/src/remote.rs; tests/adversarial/attack2.py.

Gates

cargo fmt --check produced no output and exited 0.

cargo clippy --all-targets -- -D warnings exited 0. Final output:

Finished `dev` profile [unoptimized + debuginfo] target(s) in 24m 05s

cargo test exited 101 at the existing 10,000-block collaboration timing test. CLI tests passed:

test result: ok. 19 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.07s
test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.12s
test session::public_edit_limit_tests::ten_thousand_markdown_blocks_open_and_sync_within_two_seconds ... FAILED
10,000-block collaboration phases: parse=2.071490926s, Yrs=622.136118ms, block-index=8.739956ms, first-sync=98.55126ms (616204 bytes), snapshot=27.095715ms (616199 bytes), total=2.828013975s
test result: FAILED. 14 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.33s
error: test failed, to rerun pass `-p calternal-collab --lib`

bun run check exited 0:

svelte-check found 0 errors and 0 warnings

bun run test exited 0:

 Test Files  114 passed (114)
      Tests  751 passed (751)
   Start at  01:43:06
   Duration  181.18s (transform 65%, environment 13%, import 11%, tests 8%, setup 2%)

Adversarial: the selected round kept the server alive. The CLI fixture reported three mismatches: its human-listing setup ignores upload statuses, and both retry checks expected username agent while receiving a valid Owner API response. The direct renderer regression passed. Slow uploads were 14.9s and 6.3s under host load. The Mail API probe passed anonymous access, hostile inputs, malformed and oversized payloads, hostile IDs/cursors, and 24 parallel reads. No confirmed hostile-input defect was found.

Known gaps

The README keeps these issue #350 cases open: successful end-to-end login/logout, sync service installation, 100 randomized agent sessions, and a 2 GB resumable sync transfer.

Decisions not specified in the design

  • Use 100 entries as the default page size; let --limit cap output without discarding the next cursor.
  • Parse put --mtime as RFC3339 and convert it to the existing signed nanosecond field. An explicit mtime forces the upload even when bytes match.
  • Verify uploads against the server-installed path returned by the upload API, rather than assuming local Unicode normalization.
# Final report Finished #350 on `job/cli-agent`. Merged `dev` into the job branch, then pushed it. No merge into `dev` was made. Head: `3c4caab7bda4e5ff98aa87953a0614fb2d31d91d` ## Built - `ls` uses human output by default; `--json` keeps the versioned JSON envelope. - `ls` follows directory cursors across pages. `--limit` provides a capped listing and preserves its JSON cursor. - `put --mtime` accepts RFC3339 and sends the source mtime in nanoseconds. - Upload verification uses the path returned by the server, so NFC/NFD normalization does not create a false mismatch. - Added CLI output contract tests for defaults, pagination, Unicode paths, mtime, and retry behavior. Files in the branch diff from `dev`: `Cargo.lock`; `crates/calternal-cli/Cargo.toml`; `crates/calternal-cli/README.md`; `crates/calternal-cli/src/login.rs`; `crates/calternal-cli/src/main.rs`; `crates/calternal-cli/src/remote_commands.rs`; `crates/calternal-cli/tests/output_contract.rs`; `crates/calternal-sync/src/lib.rs`; `crates/calternal-sync/src/remote.rs`; `tests/adversarial/attack2.py`. ## Gates `cargo fmt --check` produced no output and exited 0. `cargo clippy --all-targets -- -D warnings` exited 0. Final output: Finished `dev` profile [unoptimized + debuginfo] target(s) in 24m 05s `cargo test` exited 101 at the existing 10,000-block collaboration timing test. CLI tests passed: test result: ok. 19 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.07s test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.12s test session::public_edit_limit_tests::ten_thousand_markdown_blocks_open_and_sync_within_two_seconds ... FAILED 10,000-block collaboration phases: parse=2.071490926s, Yrs=622.136118ms, block-index=8.739956ms, first-sync=98.55126ms (616204 bytes), snapshot=27.095715ms (616199 bytes), total=2.828013975s test result: FAILED. 14 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.33s error: test failed, to rerun pass `-p calternal-collab --lib` `bun run check` exited 0: svelte-check found 0 errors and 0 warnings `bun run test` exited 0: Test Files 114 passed (114) Tests 751 passed (751) Start at 01:43:06 Duration 181.18s (transform 65%, environment 13%, import 11%, tests 8%, setup 2%) Adversarial: the selected round kept the server alive. The CLI fixture reported three mismatches: its human-listing setup ignores upload statuses, and both retry checks expected username `agent` while receiving a valid `Owner` API response. The direct renderer regression passed. Slow uploads were 14.9s and 6.3s under host load. The Mail API probe passed anonymous access, hostile inputs, malformed and oversized payloads, hostile IDs/cursors, and 24 parallel reads. No confirmed hostile-input defect was found. ## Known gaps The README keeps these issue #350 cases open: successful end-to-end login/logout, sync service installation, 100 randomized agent sessions, and a 2 GB resumable sync transfer. ## Decisions not specified in the design - Use 100 entries as the default page size; let `--limit` cap output without discarding the next cursor. - Parse `put --mtime` as RFC3339 and convert it to the existing signed nanosecond field. An explicit mtime forces the upload even when bytes match. - Verify uploads against the server-installed path returned by the upload API, rather than assuming local Unicode normalization.
Author
Owner

Merged into dev by Claude after review; deployed to calternal.cloud. Closing.

Merged into dev by Claude after review; deployed to calternal.cloud. Closing.
kayg closed this issue 2026-09-29 00:53:10 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#350
No description provided.