Files
guide/docs/spec.md
Travis Vasceannie f502286450 Update documentation, refactor string handling, and enhance logging
- Updated the development server documentation to reflect the new port configuration.
- Refactored string handling by replacing the centralized registry with dedicated selectors for better modularity and type safety.
- Enhanced logging throughout the application by integrating loguru for structured logging and improved context handling.
- Removed outdated files and streamlined the codebase for better maintainability.
- Added new HTML parsing utilities using BeautifulSoup to improve DOM traversal and element extraction.
- Updated various components to utilize the new string selectors, ensuring consistency across the codebase.
2025-12-07 14:16:27 +00:00

3.9 KiB
Raw Permalink Blame History

Spec Validation (2025-12-07)

Findings below are grounded in current source with inline excerpts.

Functional Gaps

  • Docling config is cosmetic: extract_ui_elements keeps _docling_url/_api_key params but the docstring says they are “Unused (maintained for signature compatibility)”, and the body only runs _extract_elements_from_html(...) on captured HTML (no HTTP call).
# src/guide/app/browser/diagnostics.py
async def extract_ui_elements(..., _docling_url: str | None = None, _api_key: str | None = None):
    """... docling_url: Unused (maintained for signature compatibility)"""
    html = await capture_html(page)
    ui_elements = _extract_elements_from_html(html)
  • OTP callbacks are processlocal: the store is an inmemory dict plus a modulelevel singleton. Requests and callbacks on different workers wont see the same _requests.
# src/guide/app/auth/otp_callback.py
class OtpCallbackStore:
    _requests: dict[str, OtpRequest]
...
_store: OtpCallbackStore | None = None
  • GraphQL client rejects partial successes: any errors entry triggers GraphQLOperationError, even if data is present.
# src/guide/app/raindrop/graphql.py
if validated.has_errors:
    raise errors.GraphQLOperationError(
        validated.first_error_message or "GraphQL operation failed",
        details={"errors": error_details},
    )

Configuration & Portability

  • Demonstration board bindings are hardcoded to a single environment.
# src/guide/app/raindrop/operations/demonstration.py
DEMONSTRATION_BOARD_ID = 596
DEMONSTRATION_INSTANCE_ID = 107
  • Redis URL currently unused: even with RAINDROP_DEMO_REDIS_URL=redis://192.168.50.210:6379/4, AppSettings has no Redis-related fields; only the documented keys (raindrop URLs, browser hosts, docling_, session_, n8n_*) are parsed, so the value is ignored at runtime.
# src/guide/app/core/config.py
model_config = SettingsConfigDict(env_prefix="RAINDROP_DEMO_", ...)
# defined fields: raindrop_base_url, raindrop_graphql_url, browser_hosts, docling_*, session_*, n8n_* (no redis)

Security Notes

  • JWT expiry is parsed without signature verification and feeds offline session validation; a forged token with a farfuture exp could keep a cached session “valid” until TTL expires.
# src/guide/app/auth/session_manager.py
token_expires_at = self.parse_jwt_expiry(token.value)
...
if session.token_expires_at:
    token_remaining = session.token_expires_at - now

Performance Considerations

  • Accordion collapsing issues one Playwright hop per button (buttons.nth(index) inside a loop), which scales poorly on pages with many accordions.
# src/guide/app/browser/elements/layout.py
for index in range(count):
    button: PageLocator = buttons.nth(index)
    if await icon.count() > 0:
        await button.click(timeout=max_wait)
  • Form discovery walks every [data-cy^="board-item-"] node and marshals its metadata in one evaluate call; large boards will produce heavy payloads.
// src/guide/app/browser/elements/form_discovery.py
const fields = container.querySelectorAll('[data-cy^=\"board-item-\"]');
return Array.from(fields)
    .filter(field => field.offsetParent !== null)

DX / DI Footnote

  • Action DI requires constructor params to exist in the DI context unless theyre varargs or have defaults; decorated __init__ without functools.wraps can break injection.
# src/guide/app/actions/base.py
for param_name, param in sig.parameters.items():
    if param_name in self._di_context:
        kwargs[param_name] = self._di_context[param_name]
    else:
        is_var_param = param.kind in (Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD)
        has_default = cast(object, param.default) != Parameter.empty
        if not is_var_param and not has_default:
            raise errors.ActionExecutionError(...)