fix(job): render Config column in job list from the Queue API's config field - #662
Conversation
…g field The human-mode table for `kbagent job list` built the Config ID cell from `configId` (with a `config_id` fallback) -- but the Queue API job resource names the field `config`, and JobService returns the API row verbatim, so the column was always blank. Read `config` first with a tolerant `configId` fallback, matching the job-detail renderer. The unit-test fixtures in test_output.py and test_services.py hand-wrote the same invented `configId` key the renderer read, so they passed while real output was empty. Fixtures now use `config` (the real API shape, same as JOB_DETAIL_RESPONSE in test_cli.py) and the jobs-table test asserts the config values actually render. Python-side counterpart of the frontend fix in #658.
padak
left a comment
There was a problem hiding this comment.
Review of #662 — fix(job): render Config column in job list from the Queue API's config field
Generated by
kbagent-pr-reviewersubagent. Verdict and findings below
are advisory; the human author retains every veto. CI-coverable issues
(lint, format, tests) are confirmed viamake check, not duplicated here.
Summary
This is a minimal, well-scoped bug fix: format_jobs_table in output.py read a nonexistent configId/config_id key when rendering the human-mode job list table, so the Config column always rendered blank — the Queue API job resource actually names the field config (verified against JOB_DETAIL_RESPONSE in tests/test_cli.py and against the job-detail renderer, which already read config first). The fix flips the read order and updates five test fixtures across two files to match the real API shape, adding an explicit assertion that the rendered values now appear. Verdict: APPROVE — no blocking or non-blocking findings; only a couple of optional nits.
Verdict
- Verdict: APPROVE
- Blocking findings: 0
- Non-blocking findings: 0
- Nits: 1
Blocking findings
(none)
Non-blocking findings
(none)
Nits
[NIT-1]src/keboola_agent_cli/output.py:412— the fallback chainjob.get("config", job.get("configId", ""))keepsconfigIdas a defensive fallback even though the audit in the PR description found no live source that populates it on a Queue API job row. Harmless to keep (it costs nothing and guards against a future API variant), but if the intent is strictly "read what the API actually sends," it could be simplified tojob.get("config", "")to match the job-detail renderer's equivalent line 470 exactly (which also keeps theconfigIdfallback, so this is consistent as-is — flagging only for symmetry consideration, not a defect).
Verification log
gh pr view 662 --json title,body,files,additions,deletions,baseRefName,headRefName,labels,state→ OPEN,fix(job):prefix matches change type (bug fix), 3 files changed (+14/-10):src/keboola_agent_cli/output.py,tests/test_output.py,tests/test_services.py✓git rev-parse --abbrev-ref HEAD→fix/job-list-config-column, matches<branch>input ✓- Read
CONTRIBUTING.md"Checklist: Adding a New CLI Command", "Plugin synchronization map", "Releasing a new version" — this PR adds no new command, no new flag, no version-gated behavior, so none of the silent-drift surfaces (AGENT_CONTEXT,CLAUDE.mdcommand list,keboola-expert.md,commands-reference.md,gotchas.md,OPERATION_REGISTRY) apply. Confirmed no command/permission/route files touched in the diff ✓ gh pr diff 662(113 lines) → confirmed change is exactly: one line inoutput.py(read-order fix) + fixture key renames (configId→config) + one new assertion intest_output.py::test_jobs_table_with_jobs✓- Layer-violation greps (typer/click/httpx/requests in wrong layer) on the diff → all empty; change is entirely within
output.py(commands layer output formatting), no layer boundary crossed ✓ grep -n "_fetch_project_jobs" -A 20 src/keboola_agent_cli/services/job_service.py→ confirms the service returns the Queue API job dict verbatim (onlyclient.list_jobs()result, no key remapping), supporting the PR's root-cause claim ✓grep -n "JOB_DETAIL_RESPONSE" -A 15 tests/test_cli.py→ confirms real fixture uses"config": "123"(notconfigId), matching the PR's claimed Queue API shape ✓grep -rn "configId" src/→ the only other Queue-API-adjacent hit isoutput.py:470(job-detail renderer), which already readconfigfirst pre-PR; remaining hits are unrelated APIs (Data Science appsconfigId, flow taskconfigId,result_models.pyAliasChoiceswhich is already tolerant of all three key spellings) — audit claim in PR description holds ✓- Confirmed
--jsonmode is unaffected:format_jobs_tableis only called from the human-mode branch (commands/job.py,if not formatter.json_mode:guard around line 401); the JSON payload passes the API dict through unchanged regardless of this fix ✓ make check→6009 passed, 12 skipped, 169 deselectedin 168.35s, exit code 0 ✓uv run pytest tests/test_output.py::TestFormatJobsTable::test_jobs_table_with_jobs -v→ PASSED individually, confirming the new"101" in output/"201" in outputassertions actually exercise the real Rich-rendered table (not a mock) ✓- Convention greps (magic numbers, raw
error_codestrings, bareexcept:,print(), token leakage, newtuple[...]returns) on the diff → all empty; no signature changes, no new returns in this diff ✓ - Could not reproduce against a live Keboola project (no registered project/config-dir available in this sandboxed review session) — relied on the unit-level reproduction above (real
format_jobs_table+ real RichConsolecapture against realistic fixtures), which is sufficient for a pure rendering fix with no API-shape ambiguity left unverified.
Open questions for the author
(none)
* chore(release): 0.90.0 Bumps pyproject.toml to 0.90.0 and adds the changelog entry covering every PR merged since v0.89.0 (#658, #662, #661, #663, #665, #666, #664, #668, #667, #623), resolves the vNEXT placeholders those PRs left behind, and adds the curated What's new reel for the release. * docs(web-server): keep the What's-new anchor stable across releases The '### What's-new popup *(since vNEXT)*' heading put the version gate in the heading itself, so resolving the placeholder to 0.90.0 changed the generated slug to 'whats-new-popup-since-0900' and broke the in-page link at line 138 -- and would have broken it again on every future release. Moved the '(since 0.90.0)' tag to the first body line: the anchor is now the stable 'whats-new-popup', the gate stays visible, and check_version_gates.py still sees it (it scans the whole file, not just headings).
Summary
The human-mode table for
kbagent job listrendered an always-empty Config column.format_jobs_tablebuilt the cell asjob.get("configId", job.get("config_id", "")), but the Queue API job resource names that fieldconfig-- andJobService._fetch_project_jobsreturns the API row verbatim (it only addsproject_alias), so neither key ever exists. The repo's own realistic fixture confirms the shape:JOB_DETAIL_RESPONSEintests/test_cli.pyuses"config": "123", and the job-detail renderer already readsconfigfirst.This is the Python-side counterpart of the frontend fix in #658 (same invented
configIdkey on theJobtype; that PR was deliberately frontend-only).Changes
output.py: the jobs-table Config cell now readsconfigfirst, with a tolerantconfigIdfallback -- identical precedence to the job-detail renderer.tests/test_output.py,tests/test_services.py: job fixtures hand-wrote the same inventedconfigIdkey the renderer read, so they passed while real output was blank. They now useconfig, matching the real API shape, andtest_jobs_table_with_jobsasserts the config values actually appear in the rendered table (this assertion fails against the old renderer).Audited the rest of
src/forconfigIdreads off Queue API job rows: the only other one is the job-detail renderer, which was already correct. RemainingconfigIdusages belong to other APIs (Data Science apps, flow task configs, and the grouped-jobs endpoint whose group key genuinely isconfigId).Testing
make checkpasses (6009 passed, 12 skipped).No version bump / changelog entry per the release-PR process; no version-gated docs affected (human-mode rendering fix only).