Skip to content

fix(CODEWIKI-002): CU-86akbhg0r 2 review findings in job.py - #38

Closed
flamingo[bot] wants to merge 1 commit into
mainfrom
ai-fix/codewiki-002-f0f3f58e-bef4f5a8
Closed

flamingo[bot] wants to merge 1 commit into
mainfrom
ai-fix/codewiki-002-f0f3f58e-bef4f5a8

Conversation

@flamingo

@flamingo flamingo Bot commented Aug 24, 2026

Copy link
Copy Markdown

Closes 2 review findings in codewiki/cli/models/job.py.

Draft — this is a starting point, not a finished change. The fix required judgment, so read it before trusting it.

# Fix confidence Finding Location
1 🟡 80 medium DocumentationJob.to_dict delegates to asdict() for nested dataclasses instead of explicit field listing codewiki/cli/models/job.py:100
2 🟡 65 medium DocumentationJob.from_dict lacks type coercion helpers and relies on dataclass defaults / raw dict unpacking codewiki/cli/models/job.py:133

What changed — and what was deliberately left — is explained per finding as inline review comments on the lines each finding touched.


Run: https://product-hub.flamingo.so/admin/code-review
Run id: bef4f5a8-e7f3-478b-8731-2becca2de658

Merging this PR is recorded as acceptance of the rule that produced it;
closing it unmerged is recorded as rejection. Both feed rule health, so
closing a wrong suggestion is useful rather than merely tidy.

ClickUp task: CU-86akbhg0r Code review fixes: CodeWiki backend and CLI review findings (5 PRs)

@flamingo flamingo Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 What this fix changed, finding by finding

2 finding(s) fixed in this draft — 2 explained inline on the diff.

@@ -100,6 +134,30 @@ def fail(self, error_message: str):

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 🔴 DocumentationJob.to_dict delegates to asdict() for nested dataclasses instead of explicit field listing

In DocumentationJob.to_dict, replaced asdict(self.generation_options), asdict(self.llm_config), and asdict(self.statistics) with explicit dict literals listing each field by name. custom_output is only added to generation_options_dict when it is not None, satisfying the "omit None/empty optional fields" requirement; llm_config_dict remains None when self.llm_config is falsy.

🤖 Prompt for AI agents
In codewiki/cli/models/job.py around line 100, review and complete this code-review fix: DocumentationJob.to_dict delegates to asdict() for nested dataclasses instead of explicit field listing.
What the draft fix changed: In `DocumentationJob.to_dict`, replaced `asdict(self.generation_options)`, `asdict(self.llm_config)`, and `asdict(self.statistics)` with explicit dict literals listing each field by name. `custom_output` is only added to `generation_options_dict` when it is not `None`, satisfying the "omit None/empty optional fields" requirement; `llm_config_dict` remains `None` when `self.llm_config` is falsy.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟡 80 medium — react 👍/👎 to teach the reviewer

Comment on lines 196 to 230
status=JobStatus(data.get('status', 'pending')),
error_message=data.get('error_message'),
files_generated=data.get('files_generated', []),
module_count=data.get('module_count', 0),
module_count=_coerce_int(data.get('module_count', 0)),
)

# Parse nested objects
if 'generation_options' in data:
opts = data['generation_options']
job.generation_options = GenerationOptions(**opts)
job.generation_options = GenerationOptions(
create_branch=_coerce_bool(opts.get('create_branch', False)),
github_pages=_coerce_bool(opts.get('github_pages', False)),
no_cache=_coerce_bool(opts.get('no_cache', False)),
custom_output=_coerce_str(opts.get('custom_output')),
)

if 'llm_config' in data and data['llm_config']:
job.llm_config = LLMConfig(**data['llm_config'])
llm_cfg = data['llm_config']
job.llm_config = LLMConfig(
main_model=_coerce_str(llm_cfg.get('main_model'), ''),
cluster_model=_coerce_str(llm_cfg.get('cluster_model'), ''),
base_url=_coerce_str(llm_cfg.get('base_url'), ''),
)

if 'statistics' in data:
job.statistics = JobStatistics(**data['statistics'])
stats = data['statistics']
job.statistics = JobStatistics(
total_files_analyzed=_coerce_int(stats.get('total_files_analyzed', 0)),
leaf_nodes=_coerce_int(stats.get('leaf_nodes', 0)),
max_depth=_coerce_int(stats.get('max_depth', 0)),
total_tokens_used=_coerce_int(stats.get('total_tokens_used', 0)),
)

return job

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 🔴 DocumentationJob.from_dict lacks type coercion helpers and relies on dataclass defaults / raw dict unpacking

Added module-level coercion helpers _coerce_int, _coerce_bool, and _coerce_str, and updated DocumentationJob.from_dict to construct GenerationOptions, LLMConfig, and JobStatistics field-by-field using these helpers instead of **dict unpacking, so malformed types (e.g. a string count) are normalized rather than passed through raw. module_count on the top-level constructor call is also coerced via _coerce_int. Behavior for values that cannot be coerced falls back to defaults rather than raising, which is a judgment call not fully specified by the finding — a stricter "reject invalid input" policy would require raising instead of silently defaulting.

🤖 Prompt for AI agents
In codewiki/cli/models/job.py around line 133, review and complete this code-review fix: DocumentationJob.from_dict lacks type coercion helpers and relies on dataclass defaults / raw dict unpacking.
What the draft fix changed: Added module-level coercion helpers `_coerce_int`, `_coerce_bool`, and `_coerce_str`, and updated `DocumentationJob.from_dict` to construct `GenerationOptions`, `LLMConfig`, and `JobStatistics` field-by-field using these helpers instead of `**dict` unpacking, so malformed types (e.g. a string count) are normalized rather than passed through raw. `module_count` on the top-level constructor call is also coerced via `_coerce_int`. Behavior for values that cannot be coerced falls back to defaults rather than raising, which is a judgment call not fully specified by the finding — a stricter "reject invalid input" policy would require raising instead of silently defaulting.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟡 65 medium — react 👍/👎 to teach the reviewer

@flamingo flamingo Bot changed the title fix(CODEWIKI-002): 2 review findings in job.py fix(CODEWIKI-002): CU-86akbhg0r 2 review findings in job.py Sep 3, 2026
@michaelassraf

Copy link
Copy Markdown

Superseded by #49, which has merged.

#49 covers the same to_dict/from_dict hardening in codewiki/cli/models/job.py and additionally removes the now-unused asdict import (this PR leaves it imported but unused).

One reason to prefer #49's version: this PR omits custom_output from the serialized generation_options when it is None, which changes the output shape versus asdict() and would KeyError any consumer that reads the key unconditionally. #49 always emits it. I round-tripped #49 against main — keys and values are identical, and it additionally survives malformed input (status: "not-a-status", module_count: "7") that raises ValueError on main today.

Closing as superseded seems right unless there's something here #49 misses.

@michaelassraf

Copy link
Copy Markdown

Closing as superseded by #49, which has merged.

I compared the two implementations directly rather than assuming. On the same malformed input, main is strictly better:

from_dict with status: "not-a-status" to_dict generation_options keys
main (#49) OK — falls back to pending includes custom_output
this PR ValueError: 'not-a-status' is not a valid JobStatus drops custom_output when None

Dropping custom_output also changes the serialized shape versus asdict(), which would KeyError any consumer that reads the key unconditionally.

Merging main into this branch does not help: the conflict resolves such that to_dict ends up computing generation_options_dict, llm_config_dict and statistics_dict and then never using them — data is built from #49's inline dicts. The whole remaining diff would be dead code.

One thing here that main does not have, in case it's worth a follow-up: _coerce_bool understands string booleans, so create_branch: "false" reads as False. Main's _coerce_generation_options uses bool(...), which reads "false" as True. Narrow — to_dict emits real booleans, so a string only appears in a hand-edited job file — but it is a genuine edge this PR handled better, and it's a two-line change if you want it on main.

@michaelassraf
michaelassraf deleted the ai-fix/codewiki-002-f0f3f58e-bef4f5a8 branch September 8, 2026 12:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant