Skip to content

fix(workflow): make Dynamic Workflow rows readable and close three gaps - #41

Merged
elkaix merged 10 commits into
mainfrom
fix/dynamic-workflow-row-layout
Aug 8, 2026
Merged

fix(workflow): make Dynamic Workflow rows readable and close three gaps#41
elkaix merged 10 commits into
mainfrom
fix/dynamic-workflow-row-layout

Conversation

@elkaix

@elkaix elkaix commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Related Issue

No open issue — the problems are described below.

Problem

Five defects, all found while watching real Dynamic Workflow runs.

The Dynamic Workflow card was unreadable.

  1. The member row measured the streamed detail at full length before the task, so a finished agent's summary left the task one column wide and every row read as ..
  2. Streamed deltas carried a closed line into the next delta, so one message fused into a single string that grew for as long as the agent talked and filled all three activity slots with the same prefix.
  3. prompt_template is optional, so a caller may pass a whole prompt as each item. Every row then opened with the same paragraph and the task column clipped inside it — six rows reading You are auditing the pythinker-code mono... name nothing.

Three workflow behaviors did not do what they said.

  1. Saving a workflow wrote a skill file into a root the open session had already scanned. The registry is built once at construction, so /<name> stayed a plain message until the session reloaded.
  2. A permission rule naming a model parsed and then never fired — the matcher globbed it against the profile name.
  3. Auto mode approved the DynamicWorkflow call itself, so the plan preview never rendered for the mode the start prompt offers by default.

The changelog did not match npm. 0.9.1, 0.10.0 and 0.11.0 have changelog blocks but appear on neither npm nor any git tag: a Version Packages PR bumped them and the publish step never completed.

What changed

Card layout. The row is budgeted instead of measured left to right: the task keeps a floor and a share, the detail takes what is left. The pending streamed line is tracked apart from the displayed one and capped, so activity shows the last three lines an agent wrote.

Shared task preamble. The preamble every task repeats is measured across every member, cut at the last shared word boundary, and replaced by a one-column mark. Four guards keep it honest, each covered by a test that fails when the guard is removed:

  • at least two non-empty items, or nothing is elided;
  • the cut lands on a shared space — never mid-word, and since a space is a whole code unit, never mid-surrogate. This is also what leaves the shortest row a word rather than the bare mark;
  • a head under 16 columns is left alone, because the mark costs about what it would free;
  • all-or-nothing, so the same column never means two different things.

The prefix is measured across every member rather than the visible ones, so it does not rewrite itself as rows scroll.

Workflow behavior. Session.reloadSkills re-discovers a saved workflow, and the two half-refresh methods in the TUI collapse into one that reloads before it rebuilds. Rule subjects now carry the model a call explicitly asks for, namespaced so an existing profile rule cannot start matching a same-named model. Auto mode asks once per distinct plan; a session grant or an allow rule falls through, and yolo is unchanged.

Docs and changelog. The tool and config reference no longer describes a progress cube the TUI stopped drawing, no longer claims argument patterns are unsupported, and now covers disable_workflows and workflow_size_guideline. Entries from the three never-published versions move into 0.12.0, which is what actually shipped them; the entry count and PR set are unchanged.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

Summary by CodeRabbit

  • New Features

    • Dynamic Workflow plans now appear before execution in auto mode, with one approval request per plan.
    • Permission rules can target workflow and subagent models.
    • Skills and saved workflows become available immediately without restarting.
    • VS Code displays the active permission mode and clearer workflow lane statuses.
  • Bug Fixes

    • Improved Dynamic Workflow activity, task readability, long-summary handling, and abandoned-lane reporting.
    • Improved skill-command refresh reliability after configuration changes or errors.
  • Documentation

    • Expanded configuration, workflow behavior, permission rules, and release notes documentation.

elkaix added 5 commits August 7, 2026 20:21
The member row measured the streamed detail at full length before the
task, so a finished agent's summary left the task one character wide and
every row read as ".". Budget the row instead: the task keeps a floor
and a share, the detail takes what is left.

Streamed deltas carried a closed line into the next delta, so one message
fused into a single string that grew for as long as the agent talked and
filled all three activity slots with the same prefix. Track the pending
line apart from the displayed one and cap it.
… gaps

Saving a workflow wrote a SKILL.md into a root the open session had
already scanned. The registry is built once at construction, so the file
was invisible and /<name> stayed a plain message until the session
reloaded. Session.reloadSkills re-discovers it, and the two half-refresh
methods in the TUI collapse into one that reloads before it rebuilds.

A permission rule naming a model parsed and then never fired: the Agent
matcher globbed it against the profile name. Rule subjects now carry the
model a call explicitly asks for, namespaced so an existing profile rule
cannot start matching a same-named model.

Auto mode approved the DynamicWorkflow call itself, so the plan preview
never rendered for the mode the start prompt offers by default. Auto now
asks once per distinct plan; a session grant or an allow rule falls
through, and yolo is unchanged.

Corrects the tool and config reference, which still described a progress
cube the TUI no longer draws, claimed argument patterns were unsupported,
and omitted disable_workflows and workflow_size_guideline.
… shipped them

0.9.1, 0.10.0 and 0.11.0 have changelog blocks but appear on neither npm
nor any git tag: a Version Packages PR bumped them and the publish step
never completed. Their entries describe merged work, which reached users
in 0.12.0, so the entries move there rather than being dropped with the
headings. The version list now matches npm exactly.

Entry count and PR set are unchanged.
`prompt_template` is optional, so a caller may pass a whole prompt as
each item. Every agent row then opened with the same paragraph and the
task column clipped inside it, leaving six rows that named nothing.

Measure the shared head across every member, cut it at the last shared
word boundary, and mark the elision with a single column. The elision is
all-or-nothing and skipped for a short head, so a column never means two
different things and a mark never costs more than it frees.
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 22 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7d084d8d-4fa6-44c6-b689-efcc8a89c127

📥 Commits

Reviewing files that changed from the base of the PR and between 579e093 and ee8ee83.

📒 Files selected for processing (1)
  • packages/agent-core/test/session/init.test.ts
📝 Walkthrough

Walkthrough

This PR updates Dynamic Workflow approval and model matching, adds session skill reloading, improves TUI and VS Code workflow status rendering, and records release and configuration changes.

Changes

Dynamic Workflow updates

Layer / File(s) Summary
Approval and model-rule matching
packages/agent-core/src/agent/permission/..., packages/agent-core/src/tools/..., packages/agent-core/test/..., docs/configuration/..., docs/reference/...
Auto mode requests plan approval once per plan. Agent and DynamicWorkflow rules can match explicit model subjects.
Session skill reload and command refresh
packages/agent-core/src/session/..., packages/agent-core/src/rpc/..., packages/node-sdk/src/..., apps/pythinker-code/src/tui/commands/..., apps/pythinker-code/src/tui/pythinker-tui.ts, apps/pythinker-code/test/tui/commands/...
Sessions can rediscover skills through RPC and SDK layers. Workflow saves refresh skill commands after reloading session skills.
Mission-control activity and layout rendering
apps/pythinker-code/src/tui/components/messages/..., apps/pythinker-code/src/tui/constant/..., apps/pythinker-code/test/tui/components/messages/..., .changeset/dynamic-workflow-*.md
Streaming output keeps separate recent lines. Shared task prefixes are elided, and task, detail, and elapsed-time columns use bounded widths.
VS Code permission and workflow status
apps/vscode/shared/..., apps/vscode/src/runtime/..., apps/vscode/webview-ui/src/..., apps/vscode/test/...
Status updates carry permission mode. The input area displays the mode, and workflow cards identify active, settled, and abandoned lanes.
Agent prompt refresh
packages/agent-core/src/agent/index.ts, packages/agent-core/src/session/index.ts
The agent retains its active profile and refreshes its system prompt after skill discovery without changing active tools or turn limits.
Release and configuration documentation
apps/pythinker-code/CHANGELOG.md, docs/release-notes/changelog.md, docs/configuration/..., docs/reference/..., .changeset/*
Release records document Dynamic Workflow features, fixes, configuration fields, permission behavior, and historical releases.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% 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
Title check ✅ Passed The title uses the valid fix prefix, stays within 72 characters, uses imperative wording, and accurately summarizes the changes.
Description check ✅ Passed The description includes all required sections, explains the problems and changes, and confirms tests, documentation, and changeset actions.
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.

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

@pkg-pr-new

pkg-pr-new Bot commented Aug 8, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@pythoughts/pythinker-code@ee8ee83
npx https://pkg.pr.new/@pythoughts/pythinker-code@ee8ee83

commit: ee8ee83

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🧹 Nitpick comments (3)
packages/agent-core/test/tools/agent.test.ts (1)

74-86: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move the runnable-execution guard out of the test body.

Oxlint reports conditional test bodies at Lines 74 and 86. Use a shared helper that unwraps a runnable ToolExecution, then keep this test as direct assertions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/agent-core/test/tools/agent.test.ts` around lines 74 - 86, Add a
shared test helper that unwraps a successful ToolExecution and throws for
errors, then use it when resolving executions in this test. Replace the inline
isError guards around onOpus and inherited with the helper so the test body
contains only direct assertions and avoids conditional test statements.

Source: Linters/SAST tools

docs/reference/tools.md (1)

94-94: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Split the Dynamic Workflow reference into focused paragraphs.

Line 94 combines inputs, model selection, limits, execution behavior, approval rules, and permission matching. Split these into focused paragraphs or subsections. Keep all existing content.

As per coding guidelines, “keep one idea per paragraph.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/reference/tools.md` at line 94, Restructure the DynamicWorkflow
reference around the existing content into focused paragraphs or subsections,
keeping every detail unchanged. Separate workflow inputs and item behavior,
subagent model selection, limits and execution lifecycle, tool-call
restrictions, approval behavior, and permission matching so each paragraph
covers one idea.

Source: Coding guidelines

apps/pythinker-code/test/tui/commands/dynamic-workflow.test.ts (1)

450-456: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the unnecessary conditional from the ordering assertion.

The supplied Oxlint result flags the ?? 0 expression at Line 455 under vitest(no-conditional-in-test). The earlier expectations already prove both calls occurred, so compare the two invocation orders directly.

Proposed fix
-        (host.refreshSkillCommands as ReturnType<typeof vi.fn>).mock.invocationCallOrder[0] ?? 0,
+        (host.refreshSkillCommands as ReturnType<typeof vi.fn>).mock.invocationCallOrder[0],
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/pythinker-code/test/tui/commands/dynamic-workflow.test.ts` around lines
450 - 456, Remove the unnecessary nullish-coalescing fallback from the
invocation-order assertion in the dynamic workflow test, comparing reloadSkills
and refreshSkillCommands invocation orders directly. Keep the preceding
call-count expectations unchanged.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changeset/plan-preview-in-auto-mode.md:
- Line 2: Change the `@pythoughts/pythinker-code` bump in
.changeset/plan-preview-in-auto-mode.md:2 from minor to major for the auto-mode
approval change. Keep the `@pythoughts/pythinker-code` bump in
.changeset/model-permission-rules.md:2 as minor; no change is required there.

In @.changeset/saved-workflow-invocable.md:
- Line 2: Update the changeset to include the public
`@pythoughts/pythinker-code-sdk` package with an additive minor release bump
alongside `@pythoughts/pythinker-code`, covering the new reloadSkills API exposed
by the SDK.

In `@apps/pythinker-code/src/tui/commands/config.ts`:
- Line 1201: Move the host.refreshSkillCommands(host.session) call to execute
after reloadSession() completes in the session reload flow, matching the
ordering used by /reload so it reads the refreshed skill registry.

In
`@apps/pythinker-code/src/tui/components/messages/dynamic-workflow-mission-control.ts`:
- Around line 286-294: The streaming update around combined, pending, and
setLatest must record every nonempty line from a multi-line delta in order
instead of only latestNonEmptyLine(combined). Split combined into lines, emit
each completed line through the existing activity-recording path, preserve only
the unclosed suffix in member.carry, and add a regression test covering several
lines delivered in one assistant.delta.
- Around line 1085-1087: Update the whitespace regular expression in
normalizeText to include the Unicode flag, changing its flags from global-only
to global plus Unicode while preserving the existing whitespace normalization
behavior.

In `@apps/pythinker-code/src/tui/pythinker-tui.ts`:
- Around line 434-439: Update refreshSkillCommands and its callers to track a
monotonically increasing refresh generation and source session, applying
listSkills results only when both still match the current session and
generation. Ignore stale successes and failures, and clear skillCommands and
skillCommandMap when listing skills fails for the current session instead of
retaining commands from the previous session.

In `@docs/configuration/config-files.md`:
- Around line 85-86: Update the top-level default_permission_mode description to
state that auto normally skips approval but DynamicWorkflow still asks for
approval in auto mode. Keep the existing permission-mode behavior and other
field descriptions unchanged.

In `@docs/release-notes/changelog.md`:
- Around line 37-40: Update the referenced changelog entries to wrap commands,
values, and identifiers in backticks: use `API key`, `off`, `low`, `medium`,
`high`, `xhigh`, `/update`, `anyOf`, `/login`, `/provider`, `pythinker provider
catalog add`, and `--api-key <key>`. Apply the same formatting consistently
across the additional referenced entries without changing their wording.

In `@packages/agent-core/src/session/index.ts`:
- Around line 1128-1131: Update reloadSkills() to rebuild
agent.config.systemPrompt after loadSkills() so the rendered {{ PYTHINKER_SKILLS
}} listing reflects newly saved workflows while preserving the existing
readiness and skill-loading sequence. Add a Vitest regression test that saves or
registers a new workflow, calls reloadSkills(), and verifies the active system
prompt includes it.

---

Nitpick comments:
In `@apps/pythinker-code/test/tui/commands/dynamic-workflow.test.ts`:
- Around line 450-456: Remove the unnecessary nullish-coalescing fallback from
the invocation-order assertion in the dynamic workflow test, comparing
reloadSkills and refreshSkillCommands invocation orders directly. Keep the
preceding call-count expectations unchanged.

In `@docs/reference/tools.md`:
- Line 94: Restructure the DynamicWorkflow reference around the existing content
into focused paragraphs or subsections, keeping every detail unchanged. Separate
workflow inputs and item behavior, subagent model selection, limits and
execution lifecycle, tool-call restrictions, approval behavior, and permission
matching so each paragraph covers one idea.

In `@packages/agent-core/test/tools/agent.test.ts`:
- Around line 74-86: Add a shared test helper that unwraps a successful
ToolExecution and throws for errors, then use it when resolving executions in
this test. Replace the inline isError guards around onOpus and inherited with
the helper so the test body contains only direct assertions and avoids
conditional test statements.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 35026664-833a-49bf-9b39-20ec9eec56f1

📥 Commits

Reviewing files that changed from the base of the PR and between 0fdec1e and 560c82f.

📒 Files selected for processing (36)
  • .changeset/dynamic-workflow-activity-lines.md
  • .changeset/dynamic-workflow-shared-task-preamble.md
  • .changeset/dynamic-workflow-task-column.md
  • .changeset/model-permission-rules.md
  • .changeset/plan-preview-in-auto-mode.md
  • .changeset/saved-workflow-invocable.md
  • apps/pythinker-code/CHANGELOG.md
  • apps/pythinker-code/src/tui/commands/config.ts
  • apps/pythinker-code/src/tui/commands/dispatch.ts
  • apps/pythinker-code/src/tui/commands/dynamic-workflow.ts
  • apps/pythinker-code/src/tui/commands/reload.ts
  • apps/pythinker-code/src/tui/components/messages/dynamic-workflow-mission-control.ts
  • apps/pythinker-code/src/tui/constant/rendering.ts
  • apps/pythinker-code/src/tui/pythinker-tui.ts
  • apps/pythinker-code/test/tui/commands/dynamic-workflow.test.ts
  • apps/pythinker-code/test/tui/commands/experiments.test.ts
  • apps/pythinker-code/test/tui/commands/reload.test.ts
  • apps/pythinker-code/test/tui/components/messages/dynamic-workflow-mission-control.test.ts
  • docs/configuration/config-files.md
  • docs/reference/tools.md
  • docs/release-notes/changelog.md
  • packages/agent-core/src/agent/permission/policies/dynamic-workflow-plan-ask.ts
  • packages/agent-core/src/agent/permission/policies/index.ts
  • packages/agent-core/src/rpc/core-api.ts
  • packages/agent-core/src/rpc/core-impl.ts
  • packages/agent-core/src/session/index.ts
  • packages/agent-core/src/session/rpc.ts
  • packages/agent-core/src/tools/builtin/collaboration/agent.ts
  • packages/agent-core/src/tools/builtin/collaboration/dynamic-workflow.ts
  • packages/agent-core/src/tools/support/rule-match.ts
  • packages/agent-core/test/agent/permission.test.ts
  • packages/agent-core/test/session/init.test.ts
  • packages/agent-core/test/tools/agent.test.ts
  • packages/agent-core/test/tools/builtin-current.test.ts
  • packages/node-sdk/src/rpc.ts
  • packages/node-sdk/src/session.ts

Comment thread .changeset/plan-preview-in-auto-mode.md
Comment thread .changeset/saved-workflow-invocable.md
Comment thread apps/pythinker-code/src/tui/commands/config.ts Outdated
Comment thread apps/pythinker-code/src/tui/pythinker-tui.ts
Comment thread docs/configuration/config-files.md
Comment thread docs/release-notes/changelog.md
Comment thread packages/agent-core/src/session/index.ts
elkaix added 3 commits August 7, 2026 21:23
reloadSkills refreshed the registry but not the rendered prompt, so a workflow
saved mid-session was invocable as `/<name>` and still absent from the skill
listing the model reads. Agent gains a prompt-only re-render that leaves the
active tool set and turn limit alone, and remembers the profile its prompt was
built from so a main agent on a custom profile is not re-rendered as the default.

A member row recorded only the last line of a delta, so a provider that packed
several closed lines into one chunk lost the rest. Every closed line is now its
own event; the unclosed tail is shown but is not one.

refreshSkillCommands applied whichever listing finished last. Several callers
start it without awaiting, so a slow list for the session the user just left
could land on the one they switched to. A generation guard drops stale results,
and a failure now clears commands that belong to another session.

Applying experimental flags rebuilt the command set before reloadSession, so it
read the registry the reload was about to replace.

Also: bump the SDK for the new public reloadSkills, note the auto-mode approval
exception in the config reference, split the DynamicWorkflow reference into one
idea per paragraph, and format changelog values as code.
The per-agent bar was filled by `stepCount / busiest lane`. That compares
agents to each other rather than measuring progress through anything, so
agents doing similar amounts of work all sat near full and never visibly
moved — the card read as stuck while the run was fine, and it needed a
caption underneath to explain what the bar even meant. A running lane now
shows the silver spinner the webview already ships, and the caption and the
bar are gone. The header bar stays: agents finished over agents started is
the one ratio on the card that is true and that moves when something happens.

A lane also kept spinning after the workflow returned. The tool call had a
result and one lane still read as running, so the card showed live work for
an agent that was cancelled or cut off with the turn. Once the result is in,
those lanes are marked `no result` and counted under the list instead.

Rows are a table now — status, label, current activity, counts — so the
counts line up in a column, and the activity line folds into the row rather
than taking a second line per agent.
`/yolo` and `/auto` toggle when sent without an argument, and the chat never
showed which mode was in effect. Sending `/yolo` to make sure YOLO was on
turned it off instead, and nothing on screen said so — the next tool call
asked for approval and the command looked broken rather than inverted.

The host already fetched the mode for every status announce and dropped it
before sending. It now rides along, a mode change announces itself to every
attached view, and the composer carries a red badge for YOLO and AUTO in the
same danger colour the terminal footer uses. Manual stays unlabelled.
@elkaix

elkaix commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

All 9 inline findings and the 3 nitpicks are triaged — 7 fixed, 5 dismissed with rationale on-thread. Head is now 722c011.

Fixed

  • SDK changeset bump for the new public reloadSkills
  • reloadSkills now re-renders the skill listing the model reads, via a prompt-only path that does not reset a running agent's tools or re-fire InstructionsLoaded
  • multi-line deltas record every closed line (the old code recorded the open line and dropped the closed one)
  • refreshSkillCommands generation guard against a stale listing landing on a newer session
  • experimental-flag apply now refreshes commands after reloadSession
  • auto-mode DynamicWorkflow approval exception documented in the config reference
  • DynamicWorkflow reference split one idea per paragraph; two genuinely unformatted changelog values backticked

Dismissed

  • major bump: CLAUDE.md does require maintainer confirmation for major, contrary to the comment. Asked, and the maintainer chose minor.
  • /\\s+/gu: pre-existing untouched line, warning-level, 12 identical occurrences in that file — fixing one is noise.
  • ?? 0 in the ordering assertion: verified the suggested edit breaks tsc under noUncheckedIndexedAccess (error TS2345).
  • conditional-in-test helper: the inline guard is this file's existing idiom at three pre-existing sites; a helper for two of five leaves the rule firing.

Every new test was mutation-checked — each one fails when the guard it covers is removed.

This push also carries two VS Code fixes outside the review: the workflow card no longer renders a fake per-agent progress bar or keep spinning lanes after the workflow returned, and the chat now shows the live permission mode, which is what made a bare /yolo silently toggle YOLO off.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
apps/vscode/webview-ui/src/components/index.ts (1)

27-27: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use a star re-export.

This non-root index.ts file should re-export PermissionModeBadge with export * from "./PermissionModeBadge".

As per coding guidelines, “In non-root index.ts files, prefer re-exports such as export * from './module'.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/vscode/webview-ui/src/components/index.ts` at line 27, Update the
non-root index.ts export for PermissionModeBadge to use a star re-export from
"./PermissionModeBadge" instead of a named re-export.

Source: Coding guidelines

apps/vscode/webview-ui/src/components/PermissionModeBadge.tsx (1)

1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Replace @/ imports with #/ imports.

Use the preferred alias consistently in changed TypeScript imports.

  • apps/vscode/webview-ui/src/components/PermissionModeBadge.tsx#L1-L1: replace @/components/ui/tooltip with #/components/ui/tooltip.
  • apps/vscode/webview-ui/src/components/WorkflowCard.tsx#L4-L5: replace both @/lib/... imports with #/lib/....

As per coding guidelines, “Use import ... from '#/...' in preference to the equivalent @/... alias.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/vscode/webview-ui/src/components/PermissionModeBadge.tsx` at line 1,
Replace the tooltip import alias in PermissionModeBadge.tsx at
apps/vscode/webview-ui/src/components/PermissionModeBadge.tsx#L1-L1 with the
preferred `#/` path, and update both lib imports in WorkflowCard.tsx at
apps/vscode/webview-ui/src/components/WorkflowCard.tsx#L4-L5 from `@/` to `#/`.
Preserve the imported symbols and module paths.

Source: Coding guidelines

packages/agent-core/src/agent/index.ts (1)

362-371: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extend the reloadSkills re-renders the skill listing the model reads test. It already covers the non-default profile, prompt update, and unchanged tool setter calls. Add assertions that the active tool names and maxStepsPerTurn are unchanged.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/agent-core/src/agent/index.ts` around lines 362 - 371, Extend the
existing reloadSkills re-renders the skill listing the model reads test to
capture and assert that the active tool names and maxStepsPerTurn remain
unchanged after refreshSystemPrompt; preserve its current non-default profile,
prompt update, and tool setter call assertions.

Source: Path instructions

apps/pythinker-code/test/tui/commands/experiments.test.ts (1)

91-95: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the conditional fallback from this test assertion.

?? 0 triggers vitest(no-conditional-in-test). Assert both mocks were called, then compare their first call orders directly. This also gives clearer failures when a call is missing.

Proposed fix
+    expect(host.session.reloadSession).toHaveBeenCalled();
+    expect(host.refreshSkillCommands).toHaveBeenCalled();
     expect(host.session.reloadSession.mock.invocationCallOrder[0]).toBeLessThan(
-      host.refreshSkillCommands.mock.invocationCallOrder[0] ?? 0,
+      host.refreshSkillCommands.mock.invocationCallOrder[0],
     );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/pythinker-code/test/tui/commands/experiments.test.ts` around lines 91 -
95, Update the assertion in the reload-session test to first assert that both
reloadSession and refreshSkillCommands were called, then compare their first
invocationCallOrder values directly. Remove the ?? 0 fallback from the
invocation-order comparison.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/agent-core/src/agent/index.ts`:
- Line 127: Add a Vitest case for refreshSystemPrompt using a non-default
profile, asserting that the active tools and config.maxStepsPerTurn are
preserved after refresh. Keep the existing _activeProfile name unchanged.

In `@packages/agent-core/src/session/index.ts`:
- Around line 1142-1156: Update reloadSkills() to call
main.tools.refreshBuiltinTools() after loadSkills() completes, ensuring newly
available builtin skill tools are registered. Preserve the existing active-tool
selection and system-prompt refresh behavior, and add coverage that invokes the
newly available skill without changing active tools.

---

Nitpick comments:
In `@apps/pythinker-code/test/tui/commands/experiments.test.ts`:
- Around line 91-95: Update the assertion in the reload-session test to first
assert that both reloadSession and refreshSkillCommands were called, then
compare their first invocationCallOrder values directly. Remove the ?? 0
fallback from the invocation-order comparison.

In `@apps/vscode/webview-ui/src/components/index.ts`:
- Line 27: Update the non-root index.ts export for PermissionModeBadge to use a
star re-export from "./PermissionModeBadge" instead of a named re-export.

In `@apps/vscode/webview-ui/src/components/PermissionModeBadge.tsx`:
- Line 1: Replace the tooltip import alias in PermissionModeBadge.tsx at
apps/vscode/webview-ui/src/components/PermissionModeBadge.tsx#L1-L1 with the
preferred `#/` path, and update both lib imports in WorkflowCard.tsx at
apps/vscode/webview-ui/src/components/WorkflowCard.tsx#L4-L5 from `@/` to `#/`.
Preserve the imported symbols and module paths.

In `@packages/agent-core/src/agent/index.ts`:
- Around line 362-371: Extend the existing reloadSkills re-renders the skill
listing the model reads test to capture and assert that the active tool names
and maxStepsPerTurn remain unchanged after refreshSystemPrompt; preserve its
current non-default profile, prompt update, and tool setter call assertions.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 10d0c2cc-38e3-470f-ba7f-14d14c05f9e0

📥 Commits

Reviewing files that changed from the base of the PR and between 560c82f and 722c011.

📒 Files selected for processing (24)
  • .changeset/saved-workflow-invocable.md
  • apps/pythinker-code/CHANGELOG.md
  • apps/pythinker-code/src/tui/commands/config.ts
  • apps/pythinker-code/src/tui/components/messages/dynamic-workflow-mission-control.ts
  • apps/pythinker-code/src/tui/pythinker-tui.ts
  • apps/pythinker-code/test/tui/commands/experiments.test.ts
  • apps/pythinker-code/test/tui/components/messages/dynamic-workflow-mission-control.test.ts
  • apps/vscode/shared/legacy-sdk.ts
  • apps/vscode/src/runtime/session-runtime.ts
  • apps/vscode/test/event-handlers.test.ts
  • apps/vscode/test/pythinker-runtime.test.ts
  • apps/vscode/webview-ui/src/components/PermissionModeBadge.tsx
  • apps/vscode/webview-ui/src/components/WorkflowCard.tsx
  • apps/vscode/webview-ui/src/components/index.ts
  • apps/vscode/webview-ui/src/components/inputarea/InputArea.tsx
  • apps/vscode/webview-ui/src/lib/workflow-lanes.ts
  • apps/vscode/webview-ui/src/stores/chat.store.ts
  • apps/vscode/webview-ui/src/stores/event-handlers.ts
  • docs/configuration/config-files.md
  • docs/reference/tools.md
  • docs/release-notes/changelog.md
  • packages/agent-core/src/agent/index.ts
  • packages/agent-core/src/session/index.ts
  • packages/agent-core/test/session/init.test.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • apps/pythinker-code/src/tui/commands/config.ts
  • apps/pythinker-code/test/tui/components/messages/dynamic-workflow-mission-control.test.ts
  • docs/configuration/config-files.md
  • docs/release-notes/changelog.md
  • apps/pythinker-code/CHANGELOG.md
  • apps/pythinker-code/src/tui/components/messages/dynamic-workflow-mission-control.ts

Comment thread packages/agent-core/src/agent/index.ts
Comment thread packages/agent-core/src/session/index.ts
…keep

Cover what refreshSystemPrompt is not allowed to touch: reloadSkills now
asserts the active tool names and maxStepsPerTurn are unchanged, and a new
case shows a skill saved into an empty root is invocable after the reload.

Also replace the ?? 0 index fallback in the experiments call-order assertion
with a non-null assertion, so a missing call reports the missing call rather
than a comparison against a sentinel, and use a star re-export for
PermissionModeBadge.
@elkaix

elkaix commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

All findings triaged in 579e093. The two inline threads are answered and resolved on-thread; the four nitpicks and the pre-merge warning are below.

Applied

  • apps/vscode/webview-ui/src/components/index.ts:27 — now export * from "./PermissionModeBadge". The file exports exactly one symbol, so the change is behaviorally identical and it follows the repo rule for non-root index.ts. The other 28 lines are left alone; converting them is outside this PR.
  • packages/agent-core/src/agent/index.ts (362-371 nitpick) — same edit as the inline thread above. reloadSkills re-renders the skill listing the model reads now asserts the active tool names and maxStepsPerTurn are unchanged, with a test profile carrying tools: ['Read', 'Write'] and maxTurns: 7 so neither assertion is vacuous.
  • apps/pythinker-code/test/tui/commands/experiments.test.ts:94 — the ?? 0 fallback is gone.

Not applied

  • PermissionModeBadge.tsx:1 and WorkflowCard.tsx:4-5, @/ to #/ — the #/ alias does not exist in this workspace. apps/vscode/webview-ui/tsconfig.json declares only "@/*" and "shared/*", and webview-ui/vite.config.ts resolves only @ and shared. All 76 files that use an alias here use @/; none use #/. The rule reads "prefer #/ (equivalent to @/)", and the two are equivalent only where both resolve — here the change would fail to resolve at build time.

Applied differently

  • The proposed diff for the call-order assertion does not compile. invocationCallOrder[0] is number | undefined under noUncheckedIndexedAccess, so dropping ?? 0 without a narrowing gives TS2345: Argument of type 'number | undefined' is not assignable to parameter of type 'number | bigint'. The two toHaveBeenCalled asserts the diff adds are also already present at lines 87 and 89. The fallback is now a non-null assertion instead, which typechecks and gives a better failure: vitest 4 raises TypeError: expected value must be number or bigint, received "undefined" when the mock was never called, rather than comparing against a sentinel 0. The stated lint justification does not apply either — vitest/no-conditional-in-test is not in .oxlintrc.json, and lint was green with the fallback in place.

Pre-merge warning

  • Docstring Coverage, 40% against an 80% threshold: advisory, and not one of the six required checks (build, test, lint, typecheck, nix build .#pythinker-code, Check flake.nix workspace sync). Not addressed here — this PR is a fix, and writing docstrings across the untouched files it would take is outside its scope.

Validation

  • pnpm run typecheck (root) — pass
  • apps/vscode pnpm run typecheck (both tsconfig.json and webview-ui/tsconfig.json, which the root typecheck does not cover) — pass
  • pnpm run lint (root) — pass, warnings only, none new
  • packages/agent-core full suite — 3471 passed, 209 files
  • apps/vscode suite — 338 passed, 18 files
  • pre-push hook (nix hash freshness, changed-package typecheck, changed tests) — pass

The new assertions were mutation-checked: making refreshSystemPrompt reset maxStepsPerTurn and call setActiveTools turns both reloadSkills tests red.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/agent-core/test/session/init.test.ts`:
- Around line 1262-1266: Extend the test around reloadSkills() to invoke the
registered Skill tool on main with the audit-routes skill, and assert that
activation succeeds. Keep the existing skill-listing and setActiveTools
assertions, ensuring the test exercises the registry lookup and fails if
activation is broken.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6db8c9cf-3cdd-4278-b868-c65ebe025a56

📥 Commits

Reviewing files that changed from the base of the PR and between 722c011 and 579e093.

📒 Files selected for processing (3)
  • apps/pythinker-code/test/tui/commands/experiments.test.ts
  • apps/vscode/webview-ui/src/components/index.ts
  • packages/agent-core/test/session/init.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/vscode/webview-ui/src/components/index.ts
  • apps/pythinker-code/test/tui/commands/experiments.test.ts

Comment thread packages/agent-core/test/session/init.test.ts Outdated
Asserting the Skill tool is present and the registry holds the skill does
not exercise the lookup that activation performs. Call the tool with
audit-routes and check it does not error, so a broken lookup fails the test
that claims the skill is invocable.
@elkaix
elkaix merged commit e534040 into main Aug 8, 2026
11 checks passed
@elkaix
elkaix deleted the fix/dynamic-workflow-row-layout branch August 8, 2026 09:17
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