fix(ci): three staging regressions — skill chain-load, duplicate Jobs tab, onboarding E2E (#2637)

Scheduled batched CI on staging was red across three unrelated paths.
All three are fixed in-place; the existing tests become the regression
coverage.

1. `tests/support/test_rig.rs`: rebuild the skill registry against the
   test's `with_skills_dir()` tempdir and actually run `discover_all()`.
   `AppBuilder::init_database()` reloads `config` from DB/TOML/env at the
   top of `build_all()`, which clobbered `config.skills.local_dir` back
   to the default (`~/.ironclaw/skills/`). Any registry `build_all()`
   constructed therefore pointed at the user's real skills dir, not the
   tempdir the test had laid down — so `loaded_skill_names()` came back
   empty and the v1 chain-load assertion panicked. Write the tempdir
   paths back onto `components.config.skills.*` so `AgentDeps::skills_config`
   agrees with the registry. `skill_chain_load_lifecycle::v1_chain_load_pulls_in_required_companions`
   now passes.

2. `crates/ironclaw_gateway/static/index.html`: drop the duplicate
   right-side `status-logs-btn` Jobs button added in #2353. The main
   tab-bar already has `<button data-tab="jobs">Jobs</button>`, and the
   duplicate had no `data-v1-only`/`data-v2-only` marker, so both
   rendered simultaneously. That broke `test_connection.py` (Playwright
   strict-mode rejected `.tab-bar button[data-tab="jobs"]` resolving to
   two elements) and also left both buttons visually `active` when the
   Jobs tab was open.

3. `tests/e2e/scenarios/test_extensions.py`: align
   `test_onboarding_failed_sse_shows_error_toast_and_reloads_extensions`
   with every other auth-card test in the file — resolve the real
   thread id via `_active_thread_id(page)` before calling
   `_show_auth_card`. `showAuthCard` short-circuits on
   `isCurrentThread(data.thread_id)`, and the synthetic `"thread-fail"`
   id fails that check once `currentThreadId` is populated after
   `go_to_extensions(page)`. The auth card was never rendered, so the
   follow-up `wait_for` for `.auth-card` hit its 5s timeout.

Verified: `cargo test --features libsql --test skill_chain_load_lifecycle`
and `--test skill_setup_marker_lifecycle` pass; `cargo clippy --tests
--features libsql` is clean.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Illia Polosukhin
2026-04-18 18:37:58 +09:00
committed by GitHub
parent 82a0b7598a
commit 5058a1cf0c
3 changed files with 22 additions and 10 deletions

View File

@@ -163,8 +163,6 @@
<button data-tab="routines" data-v1-only data-tab-role="routines" data-i18n="tab.routines">Routines</button>
<button data-tab="settings" data-i18n="tab.settings">Settings</button>
<div class="spacer"></div>
<button class="status-logs-btn" data-tab="jobs" data-i18n="tab.jobs" title="Jobs">Jobs</button>
<button class="status-logs-btn" data-tab="logs" data-i18n="tab.logs" title="Logs">Logs</button>
<a class="status-logs-btn docs-link-btn" href="https://docs.ironclaw.com/" target="_blank" rel="noopener noreferrer" data-i18n="tab.docs" title="Docs">Docs</a>

View File

@@ -1282,7 +1282,11 @@ async def test_onboarding_failed_sse_shows_error_toast_and_reloads_extensions(pa
await go_to_extensions(page)
count_before = len(reload_count)
await _show_auth_card(page, extension_name="gmail", auth_url="https://example.com/oauth", request_id="req-fail", thread_id="thread-fail")
# Use the real active thread id so showAuthCard's isCurrentThread gate
# lets the card render. A synthetic "thread-fail" id is rejected once
# the page has initialized currentThreadId.
thread_id = await _active_thread_id(page)
await _show_auth_card(page, extension_name="gmail", auth_url="https://example.com/oauth", request_id="req-fail", thread_id=thread_id)
assert await page.locator(SEL["auth_card"] + '[data-extension-name="gmail"]').count() == 1
# Inject a counter to confirm refreshCurrentSettingsTab is called

View File

@@ -1161,14 +1161,24 @@ impl TestRigBuilder {
.register_routine_tools(Arc::clone(db_arc), engine);
}
// Skills tools: use the config-resolved skills dirs so that a
// custom `with_skills_dir()` path propagates all the way to
// the registry (instead of always pointing at an empty temp dir).
// Skills tools: rebuild the registry against the test's tempdir.
//
// `AppBuilder::init_database()` re-resolves `config` from
// DB/TOML/env, which clobbers `config.skills.local_dir` back
// to the default (`~/.ironclaw/skills/`). Any registry
// `build_all()` already constructed therefore points at the
// user's real skills dir, not the tempdir the test laid
// down. Rebuild here from the in-scope `skills_dir` /
// `installed_skills_dir`, actually run discovery, and write
// the paths back onto `components.config` so downstream
// consumers (AgentDeps::skills_config) see the same dirs.
if enable_skills {
let registry = Arc::new(std::sync::RwLock::new(
ironclaw_skills::SkillRegistry::new(components.config.skills.local_dir.clone())
.with_installed_dir(components.config.skills.installed_dir.clone()),
));
components.config.skills.local_dir = skills_dir.clone();
components.config.skills.installed_dir = installed_skills_dir.clone();
let mut registry = ironclaw_skills::SkillRegistry::new(skills_dir.clone())
.with_installed_dir(installed_skills_dir.clone());
let _loaded = registry.discover_all().await;
let registry = Arc::new(std::sync::RwLock::new(registry));
let catalog = ironclaw_skills::catalog::shared_catalog();
components
.tools