Skip to content

Add workingDir folder selection to Ripple API topic creation - #121

Merged
mumez merged 7 commits into
developfrom
feature/ripple-api-workingdir
Sep 10, 2026
Merged

mumez merged 7 commits into
developfrom
feature/ripple-api-workingdir

Conversation

@mumez

@mumez mumez commented Sep 10, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add a /workingDirs/list endpoint to AbTopicManagerRipple and extend /topics/create with optional workingDir/isNewFolder parameters.

Changes

  • AbTopicManagerRipple: new /workingDirs/list request handler listing immediate subfolders of the agentic-browser root directory (excluding reserved topic-template/screenshots), each with name, createdAt, modifiedAt.
  • AbTopicManagerRipple>>handleCreateTopic:: accepts optional workingDir (relative folder name) and isNewFolder (default false) body keys, validated against path traversal, reserved names, and root-boundary escape; reuses an existing folder or creates+seeds a new one from the topic template; raises RpError code 10009 (folder-name collision when isNewFolder: true) or 10010 (invalid/reserved name).
  • New private helpers: resolveWorkingDirPath:isNewFolder:, validateWorkingDirName:, isReservedWorkingDirName:, workingDirJson:, newTopicFrom:workingDirPath:, workingDirAlreadyExistsError:, invalidWorkingDirNameError:, and workingDirsRootDirectory/workingDirsRootDirectory: accessors.
  • AbTopicManagerRippleTest: new tests covering working-dir listing (empty, populated, reserved-folder exclusion) and topic creation with new/existing/colliding working directories and invalid names, plus a withTempWorkingDirsRoot: test helper.
  • Adds docs/scripting-features/feature-ripple-api-topic-working-dir.scripting.md, the orchestration script used to generate this change.

Commits Included

5ea23f4 Add workingDir folder selection to Ripple API topic creation

Files Changed

docs/scripting-features/feature-ripple-api-topic-working-dir.scripting.md
src/AgenticBrowser-WebUI-Tests/AbTopicManagerRippleTest.class.st
src/AgenticBrowser-WebUI/AbTopicManagerRipple.class.st

Add /workingDirs/list to enumerate agentic-browser subfolders (excluding
reserved topic-template/screenshots) with timestamps, and extend
/topics/create with optional workingDir + isNewFolder body keys so a
caller can reuse an existing folder or require a brand-new one, with
path-traversal and reserved-name validation. Enables the web-ui to let
users pick or create a Topic's working directory (kanban issue
1789021772221).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

| target |
self validateWorkingDirName: aDirName.
target := AbSettings defaultAgenticBrowserRootDirectory / aDirName.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Required - create path ignores the injected root, so the create tests pollute the real filesystem.

handleListWorkingDirs: lists from self workingDirsRootDirectory (the overridable ivar), but resolveWorkingDirPath: builds the target against AbSettings defaultAgenticBrowserRootDirectory directly. The accessor comment says the ivar is overridable for test isolation - yet the create path bypasses it entirely, so:

  1. The list and create endpoints can resolve to different roots.
  2. withTempWorkingDirsRoot: has no effect on the create-path tests. testCreateTopicWithNewWorkingDirCreatesAndSeedsFromTemplate, the reuse test, and the collision test therefore write real folders (seeded from the template, or holding a marker file) directly under the live defaultAgenticBrowserRootDirectory and never delete them on teardown - genuine test pollution of the repo/image filesystem.

Suggested: resolve against self workingDirsRootDirectory here (consistent with the list handler) and wrap the three create-path tests in withTempWorkingDirsRoot: so the helper actually isolates and cleans up.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in 1ff5dac: resolveWorkingDirPath:isNewFolder: now resolves against self workingDirsRootDirectory (same accessor handleListWorkingDirs: uses) instead of AbSettings defaultAgenticBrowserRootDirectory directly, so both endpoints agree on the root and tests can override it. The three affected create-path tests are now wrapped in withTempWorkingDirsRoot: (which nests the temp root under the real agentic-browser root so AbTopicTemplateDirectory>>copyTo:'s own guard still allows seeding) and clean up via deleteAll in the block's ensure:.

{ #category : 'private' }
AbTopicManagerRipple >> validateWorkingDirName: aDirName [

((aDirName includesSubstring: '/')

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Required - empty/whitespace-only and "." workingDir names are not rejected and collapse to the agentic-browser root.

validateWorkingDirName: rejects /, backslash, .., absolute paths, and reserved names - but not an empty string or a single dot. With workingDir: \"\" (or "."), root / \"\" / root / \".\" resolves to the root directory itself, which trivially passes the beginsWith: re-check since it equals the root. A topic then receives the entire agentic-browser root as its working directory (containing topic-template, screenshots, and every other topic folder) - the opposite of the per-topic isolation this feature exists to provide.

Add a reject for empty/whitespace-only and "."/".." names (e.g. aDirName trimmed isEmpty), plus a regression test for the empty-string case.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in 1ff5dac: validateWorkingDirName: now rejects an empty/whitespace-only name and a bare "." (via aDirName trimmed isEmpty and aDirName trimmed = '.'), in addition to the existing checks. Added testCreateTopicSignalsErrorForEmptyWorkingDirName and testCreateTopicSignalsErrorForDotWorkingDirName regression tests.

]

{ #category : 'private' }
AbTopicManagerRipple >> isReservedWorkingDirName: aString [

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Optional - reserved-name set duplicates knowledge owned elsewhere.

#( topic-template screenshots ) is hardcoded here. These names are derived by AbTopicTemplateDirectory and AbScreenshotAttachment (their root folders under the agentic-browser dir). If a third reserved folder is later added there, this list silently goes stale and future topics could target it. Consider a single source of truth (e.g. those classes exposing their reserved folder basename) and consume it here and in the list handler.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Addressed in 1ff5dac: added AbTopicTemplateDirectory class >> reservedFolderName and AbScreenshotAttachment class >> reservedFolderName, each returning their own literal, and initialize/screenshotDirectory now derive their path from that method instead of an inline string literal. AbTopicManagerRipple>>isReservedWorkingDirName: now checks against { AbTopicTemplateDirectory reservedFolderName. AbScreenshotAttachment reservedFolderName } instead of a hardcoded array, so a rename in either owning class can't silently drift out of sync.

]

{ #category : 'private' }
AbTopicManagerRipple >> newTopicFrom: body workingDirPath: workingDirPath [

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Nit - newTopicFrom: reads like a pure constructor but has side effects.

It registers the topic (addTopic:) and, for non-nil workingDirPath, triggers working-directory creation/seeding. createTopicFrom:workingDirPath: would reveal the side effects better; alternatively split construction from the addTopic:/ensureExists step.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Renamed to createTopicFrom:workingDirPath: in 1ff5dac to name the side effects (addTopic:, directory creation/seeding) honestly.

isNewFolder: (body at: 'isNewFolder' ifAbsent: [ false ]) ].
[ topic := self newTopicFrom: body workingDirPath: workingDirPath ]
on: Error
do: [ :e | self createTopicFailedError: e messageText ].

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

FYI - validation errors take a different path than creation errors.

resolveWorkingDirPath: (raising the 10009/10010 RpError) runs outside the on: Error handler that wraps newTopicFrom: (which rewraps failures as 10006). That is deliberate so the specific codes propagate - good - but request-validation and creation-failure now flow through two error paths, and correctness depends on resolveWorkingDirPath: staying outside the handler. Consider a comment noting that, so a future move inside the block does not silently collapse 10009/10010 into 10006.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Good catch - added a comment at the call site in 1ff5dac explaining that resolveWorkingDirPath:isNewFolder: must stay outside the on: Error handler so its 10009/10010 codes aren't rewrapped as 10006, so a future edit doesn't move it in without noticing.

@mumez mumez left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Reviewing this change from first principles:

  • 2 Required — (1) the create path ignores the injected workingDirsRootDirectory, so the three create-path tests write real folders into the live defaultAgenticBrowserRootDirectory and never clean up (list and create also disagree about the root); (2) empty/"." workingDir names aren't rejected and collapse to the whole agentic-browser root.
  • 1 Optional — reserved-name list hardcodes names owned by AbTopicTemplateDirectory/AbScreenshotAttachment.
  • 2 nits/FYI — newTopicFrom: name vs. its side effects; validation vs. creation check errors flow through two paths.

Verification story: the PR reports all SUnit tests pass, but I could not independently run the suite in this environment, and the two Required issues concern tests that pass while polluting the real filesystem (they never assert on teardown state, only on exists). So a green suite here is not strong evidence of isolation. I'd like to see the create-path tests routed through withTempWorkingDirsRoot: with a teardown assertion, and an empty-string/"." regression test.

Verdict: request changes on the two Required items (bright-side: the correctness design — workingDirPath: set before ensureExists seeding, default-path behavior preserved when workingDir is omitted, and the collision flag semantics — is sound and well-tested otherwise).

…erved names

- Route /topics/create's workingDir resolution through the same overridable
  workingDirsRootDirectory used by /workingDirs/list (was hardcoded to
  AbSettings defaultAgenticBrowserRootDirectory), so tests can isolate
  filesystem effects and both endpoints agree on the root.
- Reject empty/whitespace-only and "." workingDir names, which previously
  collapsed to the agentic-browser root itself and would have handed a
  topic the entire root (containing every other topic's folder) as its
  working directory.
- Derive the reserved folder name list from AbTopicTemplateDirectory and
  AbScreenshotAttachment (each now exposes reservedFolderName) instead of
  a hardcoded literal array, so a future rename can't silently drift.
- Rename newTopicFrom:workingDirPath: to createTopicFrom:workingDirPath:
  to name its side effects (addTopic:, directory creation) honestly.
- Wrap the three create-path tests that exercise workingDir in
  withTempWorkingDirsRoot:, and add regression tests for empty/"." names.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@mumez

mumez commented Sep 10, 2026

Copy link
Copy Markdown
Owner Author

Pushed 1ff5dac addressing both Required items from the review plus the optional and nit suggestions:

  • Create-path workingDir resolution now goes through the same overridable workingDirsRootDirectory accessor the list endpoint uses, and the three affected tests are wrapped in withTempWorkingDirsRoot: so they no longer write into the live agentic-browser root.
  • Empty/whitespace-only and "." names are now rejected (previously they collapsed to the root itself), with new regression tests.
  • Reserved folder names are now sourced from AbTopicTemplateDirectory reservedFolderName / AbScreenshotAttachment reservedFolderName instead of a hardcoded literal array.
  • newTopicFrom:workingDirPath: renamed to createTopicFrom:workingDirPath:.
  • Added a comment documenting why validation must stay outside the on: Error handler.

All 76 AgenticBrowser-WebUI-Tests and 485 AgenticBrowser-Tests pass after the fix.

@mumez

mumez commented Sep 10, 2026

Copy link
Copy Markdown
Owner Author

String >> trimmed comment says that its obsolete. trimBoth is better.

trimmed is deprecated in Pharo 14 in favor of trimBoth (pharo-project/pharo#18307).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@mumez

mumez commented Sep 10, 2026

Copy link
Copy Markdown
Owner Author

Good catch, fixed in b591d04: replaced both trimmed calls with trimBoth (trimmed is deprecated in Pharo 14, see pharo-project/pharo#18307). All 76 AgenticBrowser-WebUI-Tests still pass.

@mumez

mumez commented Sep 10, 2026 •

Copy link
Copy Markdown
Owner Author

In AbTopicManagerRipple >> validateWorkingDirName: aDirName,

#('..' '/' '\') anySatisfy: [ :each | aDirName includesSubstring: each]

would be better for avoiding deep nesting

…sfy:

Reduces nesting by replacing the chained includesSubstring: or: checks
for '..', '/', '\' with a single anySatisfy: over the literal array.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@mumez

mumez commented Sep 10, 2026

Copy link
Copy Markdown
Owner Author

Fixed in 1008d33: flattened the three includesSubstring: checks into #('..' '/' '\\') anySatisfy: [ :each | aDirName includesSubstring: each ] as suggested, reducing the nesting depth from 6 to 4. All 76 AgenticBrowser-WebUI-Tests still pass.

Adds the new /workingDirs/list request endpoint, documents the optional
workingDir/isNewFolder body keys on /topics/create, and adds error codes
10009/10010 to the reference.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@mumez

mumez commented Sep 10, 2026

Copy link
Copy Markdown
Owner Author

Pushed e901508: updated docs/web-ui-api.md with the new request /workingDirs/list endpoint, the workingDir/isNewFolder fields on /topics/create, and error codes 10009/10010.

Refactor `workingDir` to `workingDirectory`, `isNewFolder` to
`checkExistingDirectory`, and `/workingDirs/list` to
`/workingDirectories/list` for consistency and clarity.
@mumez
mumez merged commit 5b2e8b1 into develop Sep 10, 2026
3 checks passed
mumez added a commit that referenced this pull request Sep 10, 2026
* Replace trimmed with trimBoth for string normalization

* Persist AbTopicManager on image shutdown

Register AbTopicManager for system shutdown to ensure topics are saved
automatically when the image closes, even if the browser window is not
explicitly closed. Added safety checks to prevent creating an empty
manager instance during shutdown.

* Fix layout constraints for hideThinkCheckBox in AbMessagesPresenter

* Add AbTopicManager defaultLoaded: helper

Introduce a helper method to set the default topic manager and announce
the change, ensuring UI components correctly refresh. Update existing
persistence and test logic to use this method.

* Fix nil check in AbTopicManager defaultLoaded:

* Lower default log level to 2 in AbLocalLogger

* Prevent serialization errors from blocking image shutdown

* Automate stall recovery for agent topics (#117)

* Add openCurrent method to AgenticBrowser class

* Reduce default topic stall threshold to 120 seconds

* Automate stall recovery for agent topics

- Add `stallMonitoringIntervalSeconds` to `AbSettings`
- Implement background stall monitoring in `AbTopicManager`
- Add `resumePrompt` to `AbTopicGoal` to nudge stalled topics
- Decouple topic monitoring and teardown from UI lifecycle

* Update goal stall threshold to 120 seconds

* Fix AbTopicManager startup and default loading

Ensure stalled topic monitoring and package watching are correctly
initialized or cleaned up during system startup and when swapping the
default manager.

* Confirm save on window close in AbBrowserPresenter

* Add step timeout handling with retry support (#118)

* Add timeout handling to orchestration steps

Introduce `onTimeout:` to `AbBaseOrchestrationStep` to allow custom
handling of timeouts instead of raising an exception. Update step
implementations to use `handleTimeout:` for consistent error reporting.

* Refactor orchestration timeout handling

Replace manual exception passing with `signalTimeout` to encapsulate
timeout creation and error handling logic.

* Add timeout handling to AbBaseOrchestration forkRunThen:

* Refactor orchestration steps to support retryable timeouts

Introduce a template method pattern in `AbBaseOrchestrationStep` where
`run` handles `AbOrchestrationStepTimeout` exceptions by invoking an
optional `onTimeout:` block. Add `retry` capability and convenience
methods for configuring wait timeouts across orchestration steps.

* Add AbMockOrchestrationForGroupCompletesAfterRetry test mock

* Improve orchestration step retry and timeout handling

- Reset `lastError` and `stepResult` during retry to ensure clean state.
- Ensure `AbOrchestrationGroupParallelStep` unsubscribes from item
  announcers on completion or timeout using `ensure:`.
- Add `resetForRetry` hooks to orchestration steps to properly clear
  transient state.

* Add runOnTimeout: to AbBaseOrchestration

* Document timeout handling in scripting DSL

Add documentation for `runOnTimeout:`, `forkRunThen:onTimeout:`, and
per-step `onTimeout:` handlers to prevent debugger invocation on
orchestration timeouts.

* Update SKILL.md to enforce forkRunThen:onTimeout:

Require the use of `forkRunThen:onTimeout:` for all orchestration
scripts
to ensure background processes handle timeouts gracefully. Add guidance
on monitoring and recovery for stalled steps.

* Add resetForRerun to orchestration mock classes

Ensure all items in an orchestration group implement resetForRerun by
adding the method to mock classes and removing the conditional check in
AbOrchestrationGroupStep.

* Isolate step settings in orchestrations

Ensure `waitTimeoutSeconds:` only affects the individual step by using a
private copy of `AbSettings` instead of mutating the shared
orchestration settings.

* Add goalChanged event to Web UI API

Introduce AbTopicGoalChanged announcement to propagate goal updates to
connected clients. Update AbTopicGoal and AbTopic to trigger this
announcement when descriptions are modified, and ensure the Web UI
broadcasts the event.

* Update ab-scripting-feature-dev workflow

Add support for positional arguments to specify output directory and
generate-only mode, and update documentation to reflect these changes.

* Update documentation for ab-scripting-feature-dev skill

* Update UI terminology for human-in-the-loop approvals

* Add workingDir folder selection to Ripple API topic creation

Add /workingDirs/list to enumerate agentic-browser subfolders (excluding
reserved topic-template/screenshots) with timestamps, and extend
/topics/create with optional workingDir + isNewFolder body keys so a
caller can reuse an existing folder or require a brand-new one, with
path-traversal and reserved-name validation. Enables the web-ui to let
users pick or create a Topic's working directory (kanban issue
1789021772221).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Revert "Add workingDir folder selection to Ripple API topic creation"

This reverts commit 2480335.

* Add workingDir folder selection to Ripple API topic creation (#121)

* Add workingDir folder selection to Ripple API topic creation

Add /workingDirs/list to enumerate agentic-browser subfolders (excluding
reserved topic-template/screenshots) with timestamps, and extend
/topics/create with optional workingDir + isNewFolder body keys so a
caller can reuse an existing folder or require a brand-new one, with
path-traversal and reserved-name validation. Enables the web-ui to let
users pick or create a Topic's working directory (kanban issue
1789021772221).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Address review: fix root-mismatch, reject empty/dot names, dedupe reserved names

- Route /topics/create's workingDir resolution through the same overridable
  workingDirsRootDirectory used by /workingDirs/list (was hardcoded to
  AbSettings defaultAgenticBrowserRootDirectory), so tests can isolate
  filesystem effects and both endpoints agree on the root.
- Reject empty/whitespace-only and "." workingDir names, which previously
  collapsed to the agentic-browser root itself and would have handed a
  topic the entire root (containing every other topic's folder) as its
  working directory.
- Derive the reserved folder name list from AbTopicTemplateDirectory and
  AbScreenshotAttachment (each now exposes reservedFolderName) instead of
  a hardcoded literal array, so a future rename can't silently drift.
- Rename newTopicFrom:workingDirPath: to createTopicFrom:workingDirPath:
  to name its side effects (addTopic:, directory creation) honestly.
- Wrap the three create-path tests that exercise workingDir in
  withTempWorkingDirsRoot:, and add regression tests for empty/"." names.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Use trimBoth instead of deprecated String>>trimmed

trimmed is deprecated in Pharo 14 in favor of trimBoth (pharo-project/pharo#18307).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Flatten path-separator checks in validateWorkingDirName: with anySatisfy:

Reduces nesting by replacing the chained includesSubstring: or: checks
for '..', '/', '\' with a single anySatisfy: over the literal array.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Document /workingDirs/list and /topics/create workingDir/isNewFolder

Adds the new /workingDirs/list request endpoint, documents the optional
workingDir/isNewFolder body keys on /topics/create, and adds error codes
10009/10010 to the reference.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Rename working directory API fields

Refactor `workingDir` to `workingDirectory`, `isNewFolder` to
`checkExistingDirectory`, and `/workingDirs/list` to
`/workingDirectories/list` for consistency and clarity.

* Recategorize error signaling methods in AbTopicManagerRipple

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
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