Add box observability and resource controls - #22
Conversation
📝 WalkthroughWalkthroughChangestx9 platform workflows
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
guest/tx9-logs (1)
1541-1576: 🚀 Performance & Scalability | 🔵 Trivial | ⚖️ Poor tradeoffAvoid the always-on fingerprint map on the hot path
emitted_countsis populated for every direct-read row even when the read completes successfully, so this path uses O(unique events) memory before the fallback ever runs. Sincehermes_database_events()reads both tables without a stableORDER BY, the exact dedup state is doing real work here; if large Hermes DBs are expected, consider whether this recovery tradeoff is acceptable or whether the fallback can be redesigned around a cheaper identity scheme.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@guest/tx9-logs` around lines 1541 - 1576, The direct-read path in the surrounding recovery flow always builds the O(unique events) emitted_counts fingerprint map, even when no fallback is needed. Redesign the fallback deduplication around a cheaper identity scheme or defer/limit state collection so successful direct reads avoid retaining every event, while preserving correct duplicate suppression when snapshot recovery follows a partial read.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@guest/tx9-logs`:
- Around line 1541-1576: The direct-read path in the surrounding recovery flow
always builds the O(unique events) emitted_counts fingerprint map, even when no
fallback is needed. Redesign the fallback deduplication around a cheaper
identity scheme or defer/limit state collection so successful direct reads avoid
retaining every event, while preserving correct duplicate suppression when
snapshot recovery follows a partial read.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: c2143013-2511-4ace-be01-a9e80a82978e
📒 Files selected for processing (2)
guest/tx9-logstests/regressions-logs.sh
🚧 Files skipped from review as they are similar to previous changes (1)
- tests/regressions-logs.sh
Summary
Operational boundaries
Validation
make checkgit diff --checkNote
Add box observability with
tx9 logs,tx9 resources, and structured log capture viatx9-logstx9 logs [export] <box>to query or export structured logs from agent/executor volumes via a transient container running the newguest/tx9-logshelper, with filtering, redaction, and JSON output options.tx9 resources show/set/reset <box>to inspect and live-update per-box CPU/memory limits and advisory volume budgets, with transactional rollback on failure.guest/tx9-logs, a Python3 logging daemon that wraps subprocesses, redacts sensitive tokens, rotates logs, and persists structured.jsonllogs; both agent and executor entrypoints now use it instead of shell redirect loops.tx9with no arguments now shows an ASCII overview panel with container resource usage, volume usage, and dashboard URLs instead of printing usage and exiting 1.tx9 create,tx9 import, andtx9 upgradenow accept resource flags (CPU/memory/budget) that are validated, persisted, and applied to containers at creation or recreation time.docker.ClientgainsVolumeUsageandContainerUpdateResources;WriteBoxEnvnow performs an atomic temp-file-and-rename write.tx9-logs-supervised exec, changing signal handling and restart behavior for existing boxes.Macroscope summarized b94eb01.
Greptile Summary
This PR adds box observability and resource controls. The main changes are:
tx9 logsquery and export commands.tx9 resourcesshow, set, and reset commands.Confidence Score: 5/5
This looks safe to merge.
No blocking issues found in the changed code.
What T-Rex did
Important Files Changed
Comments Outside Diff (6)
General comment
./tests/regressions-logs.shfails at its first JSONL assertion with exit code 4. The capturedexecutor.jsonlcontains stdout output records that match the filter, but the file also ends with non-matching records such as stderr/process_exit. Withjq -e 'select(...)' file, jq's exit status reflects the last output/input evaluation behavior and is 4 when the final result is false/null/no output, even if earlier records matched and were printed. This makes the regression script fail on valid structured output.jq -e 'select(.source == "executor" and .stream == "stdout" and .type == "output")' executor.jsonl >/dev/nullas an existence assertion over a stream of JSONL records. Because later non-matching records are evaluated after matching records, the command exits 4.jq -e 'any(inputs; .source == "executor" and .stream == "stdout" and .type == "output")' < executor.jsonlorjq -e 'select(...)' executor.jsonl | grep -q .with pipefail handled appropriately. Apply the same pattern to similar JSONL existence checks if present.General comment
./tests/regressions-logs.shfails almost immediately after runningguest/tx9-logs capture --source executor ...; the traced run exits atjq -e 'select(.source == "executor" and .stream == "stdout" and .type == "output")' "$executor_root/logs/executor.jsonl" >/dev/nullwithEXIT_CODE: 4. This means the PR's explicit log regression validation is not passing in the sandbox, despite syntax/static checks and changed Go package tests passing.executor.jsonlfrom the first regression fixture does not satisfy the regression script's required stdout output-record selector, causingjq -eto return no truthy result underset -e. The trace shows failure before the later redaction, rotation, query, and export checks can run.guest/tx9-logs captureoutput for the first fixture intests/regressions-logs.shand restore emission of an executor stdout record withsource: "executor",stream: "stdout", andtype: "output"for captured stdout, or update the regression only if the schema intentionally changed and all consumers were migrated.General comment
make testfails in the changed log regression path. A diagnosticbash -x ./tests/regressions-logs.shrerun shows the script exits with status 4 immediately after invokingjq -e 'select(.source == "executor" and .stream == "stdout" and .type == "output")' "$executor_root/logs/executor.jsonl". The wrapped capture command itself returned the expected status 7 and the raw stdout/stderr/log files were created with expected permissions before the failure point, so the failure is localized to the structured JSONL assertion path rather than missing Docker or shellcheck.jq -e select(...)assertion intests/regressions-logs.shlines 29-30; no product-side diagnostic is emitted by the script before cleanup removes the temp directory.executor.jsonlon assertion failure and adjust the structured log generation or assertion so at least one executor stdout output event matching.source == "executor" and .stream == "stdout" and .type == "output"is present and accepted. Then rerun./tests/regressions-logs.shandmake test.General comment
./tests/regressions-logs.shexits with status 4 during the first JSONL validation step:jq -e 'select(.source == "executor" and .stream == "stdout" and .type == "output")' executor.jsonl >/dev/null. A direct repro of the same capture path produced anexecutor.jsonlfile containing a matching stdout output event, butjq -ecan still return 4 when the final input record does not match the filter, because the script usesselect(...)over all records rather than aggregating whether any record matched. This causes the regression suite to fail even though the expected event is present.jq -e 'select(...)' fileacross multiple JSONL records. With-e, jq's exit status reflects the last output value; when later records are non-matching and produce no output, the command can exit 4 even if an earlier record matched.jq -e 'any(inputs; .source == "executor" and .stream == "stdout" and .type == "output")'with appropriate JSONL input handling, or collect matches and assertlength > 0.General comment
./tests/regressions-logs.shexits with status4at its first JSONL assertion:jq -e 'select(.source == "executor" and .stream == "stdout" and .type == "output")' "$executor_root/logs/executor.jsonl" >/dev/null. A direct repro showsguest/tx9-logs capturewrites valid matching stdout/output records, but jq still returns4because the filter is evaluated across multiple JSONL objects and the final non-matching input produces no truthy output underjq -e.jq -e select(...)as an existence check over a multi-record JSONL file. With jq 1.6, the command's exit status can be4when the last processed input yields no output, even if earlier records matched and were printed.jq -e 'any(inputs; .source == "executor" and .stream == "stdout" and .type == "output")'with appropriate handling for the first input, or usejq -e 'select(...)' file | grep -q .without relying on jq's multi-input-eexit status.General comment
./tests/regressions-logs.shexits 4 at its first JSONL assertion. The script runsjq -e 'select(.source == "executor" and .stream == "stdout" and .type == "output")' executor.jsonl >/dev/null; matching stdout events are present, but jq exits 4 because-ebases status on the last output value and the final JSONL record is a non-matchingprocess_exitevent.jq -e select(...)over a multi-record JSONL stream without aggregating the predicate, so a later non-match can make jq return failure even after prior matches succeeded.jq -e 'any(.source == "executor" and .stream == "stdout" and .type == "output")' executor.jsonlwith slurp/input handling appropriate for JSONL, orjq -e '[select(...)] | length > 0'when slurping records.Reviews (6): Last reviewed commit: "fix: validate capture restart delays" | Re-trigger Greptile