Skip to content

feat: differentiate system/agent/human blocker origins visually (#487)#530

Merged
frankbria merged 1 commit into
mainfrom
feature/issue-487-differentiate-blocker-origins
Apr 3, 2026
Merged

feat: differentiate system/agent/human blocker origins visually (#487)#530
frankbria merged 1 commit into
mainfrom
feature/issue-487-differentiate-blocker-origins

Conversation

@frankbria

@frankbria frankbria commented Apr 3, 2026

Copy link
Copy Markdown
Owner

Summary

Implements #487: UX: Differentiate system vs agent vs human-created blockers visually

  • Added BlockerOrigin enum (system | agent | human) to the Python Blocker model
  • Added created_by column to the blockers DB schema with migration (existing rows default to 'human')
  • API exposes created_by in BlockerResponse; the create endpoint always uses 'human' (internal callers set agent/system origin directly)
  • Agent call sites in react_agent.py, agent.py, adapters/, and prd_discovery.py pass the correct origin
  • BlockerCard shows an origin badge (icon + color) and origin-specific guidance text beneath the metadata row

Acceptance Criteria

  • Origin badge visible on every blocker
  • Badge uses distinct color/icon per origin type (Settings01 blue for system, AI amber for agent, UserCircle02 purple for human)
  • Blocker detail text adapts to origin type
  • Recovery instructions match the origin
  • API returns created_by field in blocker response

Test Plan

  • 8 new Python unit tests (TDD — all green before implementation): tests/core/test_blocker_origin.py
  • 6 new React tests for origin badge rendering: web-ui/__tests__/components/blockers/BlockerCard.test.tsx
  • All existing tests pass (no regressions)
  • Linting clean (ruff check)
  • Web-UI build passes (npm run build)
  • Code review addressed (removed created_by from external API to prevent origin spoofing)

Implementation Notes

  • API-created blockers are always 'human'; system/agent origins are set exclusively by internal code paths
  • DB migration uses COALESCE in queries so existing workspaces without the column return 'human' gracefully
  • The pre-existing shows success state test failure in BlockerCard.test.tsx is unrelated to this PR (present on main before this branch)

Closes #487

Summary by CodeRabbit

New Features

  • Blockers now display origin badges showing their source: Manual (user-created), Agent (agent-requested), or System (automatically detected).
  • Origin-specific guidance text provides context for why each blocker was created.
  • Blocker origin information is tracked and persisted across all operations.

Tests

  • Comprehensive test coverage for blocker origin tracking, validation, and backward compatibility.

Add created_by field to blockers to visually distinguish origin types
in the BlockerResolutionView UI.

Changes:
- core/blockers.py: add BlockerOrigin enum (system/agent/human), created_by
  field on Blocker dataclass, update create/get/list DB queries
- core/workspace.py: add created_by column to blockers schema + migration for
  existing databases (defaults to 'human')
- ui/routers/blockers_v2.py: expose created_by in BlockerResponse; API-created
  blockers always use 'human' origin (prevents spoofing via API)
- core/react_agent.py: stall blockers use created_by='system', agent blockers
  use created_by='agent'
- core/agent.py, adapters/verification_wrapper.py, adapters/builtin.py,
  prd_discovery.py: all agent-initiated blockers use created_by='agent'
- web-ui/src/types/index.ts: add BlockerOrigin type, created_by field on Blocker
- web-ui/src/components/blockers/BlockerCard.tsx: origin badge (icon + color per
  origin) and guidance text beneath the metadata row
- tests/core/test_blocker_origin.py: 8 new tests (TDD, all green)
- web-ui/__tests__/…/BlockerCard.test.tsx: 6 new origin badge tests

Closes #487
@coderabbitai

coderabbitai Bot commented Apr 3, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

Pull request was closed or merged during review

Walkthrough

This PR adds a blocker origin tracking system by introducing a created_by field to the Blocker model with three possible values (system, agent, human), updating the database schema with runtime migration logic, modifying all blocker creation calls across the codebase to specify the origin, and extending UI components to visually distinguish blockers by their creator.

Changes

Cohort / File(s) Summary
Core Blocker Model & Database
codeframe/core/blockers.py, codeframe/core/workspace.py
Added BlockerOrigin enum and created_by field to Blocker dataclass with default HUMAN. Extended create() with created_by parameter, persisted to new database column. Updated database reads with COALESCE for backward compatibility and schema migration logic to add column to existing tables.
Blocker Creation Across Backend Modules
codeframe/core/agent.py, codeframe/core/adapters/builtin.py, codeframe/core/adapters/verification_wrapper.py, codeframe/core/react_agent.py, codeframe/core/prd_discovery.py
Updated all blocker creation calls to include created_by parameter. Agent modules set "agent", system modules set "system", with dynamic logic in react_agent.py to distinguish stall detection ("system") from other agent blockers ("agent")
API & Type Definitions
codeframe/ui/routers/blockers_v2.py, web-ui/src/types/index.ts
Added created_by field to BlockerResponse Pydantic model and Blocker TypeScript interface. Updated response mapping to extract and return created_by.value from core Blocker objects.
UI Components
web-ui/src/components/blockers/BlockerCard.tsx, web-ui/__mocks__/@hugeicons/react.js
Enhanced BlockerCard with origin badge display, origin-specific icons and styling via ORIGIN_CONFIG mapping, and contextual guidance text per origin type. Added icon mocks for UI tests.
Test Coverage
web-ui/__tests__/components/blockers/BlockerCard.test.tsx, tests/core/test_blocker_origin.py
Added comprehensive tests validating enum mapping, blocker creation with various origins, default behavior, persistence retrieval, listing, and backward compatibility for legacy rows without created_by column. UI tests verify badge rendering and guidance text per origin.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 A blocker's tale now told with care,
System, agent, human—each declares its flair,
With badges bright and guidance true,
We hop along, distinguishing who's who! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 61.54% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title concisely and accurately describes the main feature: adding visual differentiation for blocker origins (system/agent/human) as specified in issue #487.
Linked Issues check ✅ Passed All requirements from #487 are implemented: BlockerOrigin enum added, created_by field persisted across database/API, origin badge with distinct icons/colors rendered in BlockerCard, and origin-specific guidance text displayed.
Out of Scope Changes check ✅ Passed All changes are directly aligned with #487. The added icon mocks (Settings01Icon, UserCircle02Icon) support the origin badge UI feature; all code changes target created_by handling or visual presentation thereof.

✏️ 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 feature/issue-487-differentiate-blocker-origins

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

@claude

claude Bot commented Apr 3, 2026

Copy link
Copy Markdown

Review: feat: differentiate system/agent/human blocker origins (#487)

Overall this is solid work — clean enum design, good backward-compat migration strategy, and proper TDD with tests written before implementation. The security decision to lock API-created blockers to human origin is exactly right.

A few things worth addressing before merge:

Bug / Semantic Mismatch — system guidance text

In BlockerCard.tsx, the system origin guidance reads: "Agent was inactive for too long. Answer to continue or retry execution."

But system origin is used in two different situations:

  1. Stall detection (react_agent.py:_create_text_blocker when reason == "stall_detected") — guidance fits.
  2. PRD discovery pause (prd_discovery.py) — a user paused a Socratic discovery session. Showing "Agent was inactive for too long" here is incorrect and confusing.

Either the guidance needs to be generic enough to cover both cases (e.g. "System-initiated pause. Review the context and answer to continue."), or prd_discovery.py should use a different origin value.

Fragile Magic String in react_agent.py

origin = "system" if reason == "stall_detected" else "agent"

This couples the origin logic to a raw string with no constant defined. If the string ever changes at the call site, this silently degrades to "agent" with no error. Consider extracting a constant (STALL_REASON = "stall_detected") or checking via the StallDetectedError/StallAction types instead.

Migration Test Doesn't Exercise the ALTER TABLE Path

TestExistingBlockersMigration.test_blockers_without_created_by_default_to_human inserts a row without created_by and relies on the COALESCE in the SELECT query — but the tmp_path workspace fixture creates a fresh DB with the new schema, so created_by already exists. The actual ALTER TABLE blockers ADD COLUMN ... migration branch is never exercised.

The test validates the COALESCE fallback (good!), but the migration path itself has no coverage. A test that creates a DB, drops the created_by column, then calls _init_database again would close this gap.

Minor: Weak Typing on BlockerResponse.created_by

BlockerOrigin is already defined in blockers.py. Using it in BlockerResponse gives free Pydantic validation and OpenAPI enum documentation:

from codeframe.core.blockers import BlockerOrigin
created_by: BlockerOrigin = BlockerOrigin.HUMAN

Nit: blockers.create() Parameter Type

blockers.create() accepts created_by: str but Blocker.created_by is typed as BlockerOrigin. Aligning the parameter type would make the public API clearer.


Summary: The two items worth fixing before merge are the semantic mismatch on system guidance text and the magic string for stall detection origin. The migration test gap is also worth closing. Typing improvements are optional but recommended.

@frankbria frankbria merged commit 787add1 into main Apr 3, 2026
9 of 10 checks passed
frankbria added a commit that referenced this pull request Apr 3, 2026
## Summary
Follow-up fixes after #530 was merged without reading claude-review output:

- System guidance text generalized to cover stall-timeout + PRD discovery pause
- `_REASON_STALL_DETECTED` constant extracted (eliminates 5 magic string usages)
- Migration ALTER TABLE test added to cover the actual schema migration path
- `BlockerResponse.created_by` typed as `BlockerOrigin` enum for Pydantic validation + OpenAPI docs
- `blockers.create()` parameter typed as `Union[BlockerOrigin, str]`

## Validation
- All CI checks green including claude-review and CodeRabbit
- 9 Python tests, 20 React tests passing, no regressions
- SQLite serialization confirmed: INSERT uses `origin.value` (stores plain string, not enum repr)
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.

UX: Differentiate system vs agent vs human-created blockers visually

1 participant