Skip to content

feat: Update environment configuration and enhance logging for local …#60

Merged
monkscode merged 2 commits intodevelopfrom
chor/locator-enhancements
Apr 24, 2026
Merged

feat: Update environment configuration and enhance logging for local …#60
monkscode merged 2 commits intodevelopfrom
chor/locator-enhancements

Conversation

@monkscode
Copy link
Copy Markdown
Owner

@monkscode monkscode commented Apr 23, 2026

…development; normalize Robot Framework CSS selectors

Summary by CodeRabbit

  • New Features

    • Added LOG_FORMAT control for selectable console vs JSON logs; local runner now auto-resolves the robot tests directory for bind mounts.
  • Bug Fixes

    • Normalize CSS selectors to avoid Robot Framework treating them as comments.
    • Ensure final workflow progress reaches 100% and is reported reliably.
    • Fix environment loading order for consistent logging behavior.
  • Documentation

    • Require explicit css= prefix for CSS locators; clarify HOST_ROBOT_TESTS_DIR usage.
  • Tests

    • Added tests for the Robot Framework normalizer and tightened hint-format checks.

…development; normalize Robot Framework CSS selectors
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 23, 2026

Walkthrough

Adds a Robot Framework CSS-selector normalizer and integrates it into the workflow pipeline; introduces LOG_FORMAT support and console color detection; adjusts local dev bootstrapping (image tags, HOST_ROBOT_TESTS_DIR resolution, run.sh env exports); and ensures final 100% progress events are suppressed when workflows are unregistered.

Changes

Cohort / File(s) Summary
Environment examples
/.env.example, src/backend/.env.example
Switched image tags from PR-specific to *-local; removed default HOST_ROBOT_TESTS_DIR assignment from top-level .env.example; added LOG_FORMAT variable in backend env example.
Local bootstrap & env handling
run.sh, tools/browser_use_service.py
run.sh: prepends Docker Desktop bin to PATH, exports BROWSER_HEADLESS=false and LOG_FORMAT=console, and computes absolute HOST_ROBOT_TESTS_DIR (Windows-aware). browser_use_service: load env earlier so import-time logging reads LOG_FORMAT.
Logging
src/backend/config/logging_config.py
Console renderer now uses sys.stdout.isatty() to decide coloring when LOG_FORMAT=console.
Robot code normalization
src/backend/crew_ai/robot_code_normalizer.py, src/backend/services/workflow_service.py
New normalize_robot_code(robot_code: str) added and invoked in workflow pipeline to prefix bare CSS selectors (e.g., #id, .class) with css= and avoid Robot Framework comment misinterpretation.
Crew progress & event suppression
src/backend/crew_ai/crew.py, src/backend/crew_ai/progress_events.py
Ensure an explicit 100% progress dispatch on success; _push_if_forward now verifies queue registration under lock to avoid late/dangling dispatches.
Feedback & hints
src/backend/crew_ai/optimization/nl_feedback_engine.py, src/backend/crew_ai/tasks.py
Refactored confidence/boost logic and SQL scope filtering; hint rendering delegated to a helper with a more detailed header. plan_steps_task prompt adds guardrail to forbid echoing Robot Framework code from USER FEEDBACK blocks.
Browser library guidance
src/backend/crew_ai/library_context/browser_context.py
Documentation and template guidance now require explicit css= prefix for CSS locators and warn about bare # leading to Robot Framework comment behavior.
Tests
tests/test_crew_ai/test_robot_code_normalizer.py, tests/test_optimization/test_nl_feedback_pipeline.py
Added comprehensive tests for normalizer behavior and tightened/updated tests for hint formatting and scope mapping.

Sequence Diagram(s)

sequenceDiagram
    participant CrewAI as CrewAI
    participant Workflow as WorkflowService
    participant Normalizer as CSSNormalizer
    participant Validator as RobotValidator
    participant Executor as DockerExecutor
    participant Progress as ProgressQueue

    CrewAI->>Workflow: emit generated robot code
    Workflow->>Normalizer: submit raw code for normalization
    Normalizer-->>Workflow: return normalized robot code
    Workflow->>Validator: validate normalized code
    Validator-->>Workflow: validation result
    Workflow->>Executor: run in container
    Executor-->>Workflow: execution results
    Workflow->>Progress: push progress events (including final 100%)
    Progress-->>Workflow: acknowledge / suppressed if unregistered
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

enhancement, documentation

Poem

🐰 I hopped through code to fix a CSS snare,
I begged the locators to always say "css=" fair,
Logs now wear colors when the terminal's bright,
Paths resolve on Windows so tests mount right,
And at the finish line the progress hits one-hundred with flair. 🥕

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.19% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is truncated and incomplete, ending with 'for local …', making it impossible to determine the full scope of changes or primary intent. Provide the complete pull request title without truncation so the full message can be evaluated against the changeset content.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chor/locator-enhancements

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Apr 23, 2026

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 97.50000% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/backend/crew_ai/robot_code_normalizer.py 96.96% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@monkscode
Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 23, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/backend/config/logging_config.py (1)

69-101: ⚠️ Potential issue | 🟡 Minor

Avoid writing ANSI-colored console logs into application.log.

The same formatter instance is used for both file and console handlers. When LOG_FORMAT=console and stdout is a TTY (line 70: colors=sys.stdout.isatty()), the rotating file handler receives ANSI color escape sequences, polluting logs/application.log.

Use separate formatters: file formatter with colors=False, console formatter with colors=sys.stdout.isatty().

🛠️ Proposed fix: separate file and console formatters
-    renderer = (
-        structlog.dev.ConsoleRenderer(colors=sys.stdout.isatty())
-        if os.environ.get("LOG_FORMAT", "").lower() == "console"
-        else structlog.processors.JSONRenderer()
-    )
-    formatter = structlog.stdlib.ProcessorFormatter(
-        processor=renderer,
-        foreign_pre_chain=_SHARED_PROCESSORS,
-    )
+    use_console_format = os.environ.get("LOG_FORMAT", "").lower() == "console"
+    file_renderer = (
+        structlog.dev.ConsoleRenderer(colors=False)
+        if use_console_format
+        else structlog.processors.JSONRenderer()
+    )
+    console_renderer = (
+        structlog.dev.ConsoleRenderer(colors=sys.stdout.isatty())
+        if use_console_format
+        else structlog.processors.JSONRenderer()
+    )
+    file_formatter = structlog.stdlib.ProcessorFormatter(
+        processor=file_renderer,
+        foreign_pre_chain=_SHARED_PROCESSORS,
+    )
+    console_formatter = structlog.stdlib.ProcessorFormatter(
+        processor=console_renderer,
+        foreign_pre_chain=_SHARED_PROCESSORS,
+    )
@@
-        file_handler = logging.StreamHandler(sys.stdout)
+        file_handler = logging.StreamHandler(sys.stdout)
 
-    file_handler.setFormatter(formatter)
+    file_handler.setFormatter(
+        console_formatter
+        if isinstance(file_handler, logging.StreamHandler) and file_handler.stream is sys.stdout
+        else file_formatter
+    )
@@
         console_handler = logging.StreamHandler(sys.stdout)
-        console_handler.setFormatter(formatter)
+        console_handler.setFormatter(console_formatter)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/backend/config/logging_config.py` around lines 69 - 101, The current code
reuses a single ProcessorFormatter named formatter for both file_handler and
console_handler which causes ANSI color escape sequences to be written into
application.log when LOG_FORMAT=console; create two separate
renderers/formatters: a file_renderer =
structlog.dev.ConsoleRenderer(colors=False) (or JSONRenderer when not console)
and a console_renderer =
structlog.dev.ConsoleRenderer(colors=sys.stdout.isatty()) (or JSONRenderer as
appropriate), then build two ProcessorFormatter instances (e.g., file_formatter
and console_formatter) using the same _SHARED_PROCESSORS but different renderer
arguments, call file_handler.setFormatter(file_formatter) and
console_handler.setFormatter(console_formatter), and keep the existing logic
that skips adding a separate console_handler when file_handler is a
StreamHandler(sys.stdout).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@run.sh`:
- Around line 40-44: The current assignment to SCRIPT_DIR can run pwd (falling
back to the caller dir) when the initial cd fails; change the command grouping
so the fallback to plain pwd only happens if cd into "$(dirname
"${BASH_SOURCE[0]}")" succeeded. In other words, ensure SCRIPT_DIR is computed
by first performing cd, and only then running "pwd -W 2>/dev/null || pwd" within
that successful cd context (so HOST_ROBOT_TESTS_DIR uses the script directory,
not the caller's cwd); reference the SCRIPT_DIR assignment and
HOST_ROBOT_TESTS_DIR and the use of pwd -W in your fix.

In `@src/backend/crew_ai/crew.py`:
- Around line 419-427: The duplicate 100% progress event can be enqueued if
_push_if_forward() checks progress after the workflow was unregistered; update
_push_if_forward in src/backend/crew_ai/progress_events.py to first verify the
workflow is still registered (e.g., confirm the progress_queue or
_current_progress entry exists and matches the given workflow_id) before
comparing/setting progress, so that calls arriving after unregister_workflow()
are ignored and cannot enqueue a stale 100% event; keep the existing dedupe
logic but add this early-stale-guard to prevent re-enqueueing after
unregistration.

In `@src/backend/crew_ai/optimization/nl_feedback_engine.py`:
- Around line 459-488: The RF detection currently only matches ${...} via
_RF_VAR_RE and misses typical Robot Framework table-style lines (keyword +
multiple spaces + args) and locator patterns; update _RF_VAR_RE (used in
_format_feedback_hint) to a broader regex that also matches: (1) lines with
Robot table spacing/columns (e.g., a token followed by 2+ spaces or tabs and
further tokens like "Click    `#searchBox`"), (2) common locator prefixes (#, .,
//, css=, xpath=), and (3) assignment patterns like "\w+=\s+Set Variable" or
keywords such as "Fill Text|Click|Input Text|Set Variable". Replace the current
re.compile(r'\$\{[A-Za-z_][A-Za-z0-9_ ]*\}') with a single composite pattern
capturing these cases so lines containing RF keywords/locators/assignments are
appended to code_lines in _format_feedback_hint.

In `@src/backend/crew_ai/robot_code_normalizer.py`:
- Around line 69-73: The current loop that uses _CELL_SPLIT_RE and _BARE_CSS_RE
rewrites any bare `#`/`.` token regardless of context; limit rewriting to actual
locator cells by first determining the cell's role (keyword name, argument
index, or section type) and only apply the css= rewrite when the cell is a
locator argument for locator-taking keywords (e.g., Click, Fill Text, Get Text,
Wait For Elements State) or when in a Variables section with a variable name
containing "locator"/"selector"; update the loop that assigns parts[i] and
increments rewrote to check the keyword/arg position and section metadata before
replacing a bare match so non-locator cells (assertions, tags, messages) are
left unchanged.

---

Outside diff comments:
In `@src/backend/config/logging_config.py`:
- Around line 69-101: The current code reuses a single ProcessorFormatter named
formatter for both file_handler and console_handler which causes ANSI color
escape sequences to be written into application.log when LOG_FORMAT=console;
create two separate renderers/formatters: a file_renderer =
structlog.dev.ConsoleRenderer(colors=False) (or JSONRenderer when not console)
and a console_renderer =
structlog.dev.ConsoleRenderer(colors=sys.stdout.isatty()) (or JSONRenderer as
appropriate), then build two ProcessorFormatter instances (e.g., file_formatter
and console_formatter) using the same _SHARED_PROCESSORS but different renderer
arguments, call file_handler.setFormatter(file_formatter) and
console_handler.setFormatter(console_formatter), and keep the existing logic
that skips adding a separate console_handler when file_handler is a
StreamHandler(sys.stdout).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 4c8c570b-fa52-4301-b031-3020c7ac31fa

📥 Commits

Reviewing files that changed from the base of the PR and between b64214a and 0678950.

📒 Files selected for processing (11)
  • .env.example
  • run.sh
  • src/backend/.env.example
  • src/backend/config/logging_config.py
  • src/backend/crew_ai/crew.py
  • src/backend/crew_ai/library_context/browser_context.py
  • src/backend/crew_ai/optimization/nl_feedback_engine.py
  • src/backend/crew_ai/robot_code_normalizer.py
  • src/backend/crew_ai/tasks.py
  • src/backend/services/workflow_service.py
  • tools/browser_use_service.py

Comment thread run.sh
Comment on lines +40 to +44
# When running locally (not inside Docker Compose), HOST_ROBOT_TESTS_DIR must be
# an absolute Windows path so the Docker daemon can resolve the bind mount.
# The .env value (./robot_tests) is relative and only valid inside Docker Compose.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -W 2>/dev/null || pwd)"
export HOST_ROBOT_TESTS_DIR="${SCRIPT_DIR}/robot_tests"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Make the pwd -W fallback conditional on a successful cd.

Line 43 can fall back to pwd even if the cd fails, which would export HOST_ROBOT_TESTS_DIR from the caller’s current directory instead of the script directory.

🛠️ Proposed fix
-SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -W 2>/dev/null || pwd)"
+if ! SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && { pwd -W 2>/dev/null || pwd; })"; then
+    echo "Error: unable to resolve script directory."
+    exit 1
+fi
 export HOST_ROBOT_TESTS_DIR="${SCRIPT_DIR}/robot_tests"
🧰 Tools
🪛 Shellcheck (0.11.0)

[info] 43-43: Note that A && B || C is not if-then-else. C may run when A is true.

(SC2015)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@run.sh` around lines 40 - 44, The current assignment to SCRIPT_DIR can run
pwd (falling back to the caller dir) when the initial cd fails; change the
command grouping so the fallback to plain pwd only happens if cd into "$(dirname
"${BASH_SOURCE[0]}")" succeeded. In other words, ensure SCRIPT_DIR is computed
by first performing cd, and only then running "pwd -W 2>/dev/null || pwd" within
that successful cd context (so HOST_ROBOT_TESTS_DIR uses the script directory,
not the caller's cwd); reference the SCRIPT_DIR assignment and
HOST_ROBOT_TESTS_DIR and the use of pwd -W in your fix.

Comment thread src/backend/crew_ai/crew.py
Comment thread src/backend/crew_ai/optimization/nl_feedback_engine.py Outdated
Comment thread src/backend/crew_ai/robot_code_normalizer.py Outdated
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/backend/crew_ai/optimization/nl_feedback_engine.py (1)

460-470: Optional: hoist the static header to a module-level constant.

The warning header is a pure constant but gets reconstructed on every call. Making it a module-level string (and docstring the intent there) slightly improves readability and makes it trivially patchable from tests if you ever want a fixture-level override.

♻️ Proposed refactor
+_USER_FEEDBACK_HEADER = (
+    "\u26a0\ufe0f USER FEEDBACK (reference context from a past test — "
+    "do NOT copy any code literally, treat as guidance only):"
+)
+
@@
     def _format_feedback_hint(self, feedback_text: str) -> str:
-        # Pass feedback through verbatim with a universal warning header.
-        # The header travels with every hint to every agent (Planner, Assembler,
-        # Validator), so the "do not copy literally" guardrail applies even for
-        # agents whose task prompts lack an explicit disclaimer. Preserving the
-        # original line order retains the user's reasoning structure.
-        return (
-            "\u26a0\ufe0f USER FEEDBACK (reference context from a past test — "
-            "do NOT copy any code literally, treat as guidance only):\n"
-            f"{feedback_text.strip()}"
-        )
+        # Header travels with every hint so the "do not copy literally" guardrail
+        # applies to every agent (Planner, Assembler, Validator) regardless of
+        # their task-prompt disclaimers.
+        return f"{_USER_FEEDBACK_HEADER}\n{feedback_text.strip()}"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/backend/crew_ai/optimization/nl_feedback_engine.py` around lines 460 -
470, Hoist the static warning header string out of _format_feedback_hint into a
module-level constant (e.g., FEEDBACK_WARNING_HEADER) with a brief docstring
explaining it's the universal "do NOT copy literally" header used for all agent
hints; then update _format_feedback_hint to return
f"{FEEDBACK_WARNING_HEADER}\n{feedback_text.strip()}" so the header is
constructed once and remains easily overrideable in tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/backend/crew_ai/optimization/nl_feedback_engine.py`:
- Around line 460-470: Hoist the static warning header string out of
_format_feedback_hint into a module-level constant (e.g.,
FEEDBACK_WARNING_HEADER) with a brief docstring explaining it's the universal
"do NOT copy literally" header used for all agent hints; then update
_format_feedback_hint to return
f"{FEEDBACK_WARNING_HEADER}\n{feedback_text.strip()}" so the header is
constructed once and remains easily overrideable in tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c526a4c6-6b72-4cdf-9841-1b1a5a6e6739

📥 Commits

Reviewing files that changed from the base of the PR and between 0678950 and 86d7aab.

📒 Files selected for processing (5)
  • src/backend/crew_ai/optimization/nl_feedback_engine.py
  • src/backend/crew_ai/progress_events.py
  • src/backend/crew_ai/robot_code_normalizer.py
  • tests/test_crew_ai/test_robot_code_normalizer.py
  • tests/test_optimization/test_nl_feedback_pipeline.py

@monkscode monkscode merged commit 968e4a4 into develop Apr 24, 2026
8 checks passed
@monkscode monkscode deleted the chor/locator-enhancements branch April 24, 2026 10:44
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.

2 participants