Synced from monorepo
Synced from monorepo Changes: - Workspace server: surface preview-proxy metrics through the hub metric pump - Shell: reclaim a session’s retained state in one entry - Shell: reclaim a session’s resident state in one entry - Pager: withhold key event types from Alacritty builds that double keys - Tools: cancel a session’s subagents when it closes - Pager: keep the whole plan in scrollback and separate reasoning from output in minimal mode - Pager: probe terminal version over DA2 and include it with feedback - SuperGrok Plus: identity, CLI, and analytics tier surfaces - Shell: inherit the session process scope into subagents - Pager: build @-file-search matcher lazily on first use - Tools: fix description and output contradictions in tool definitions - Workspace: degrade @-file-search instead of aborting on thread exhaustion - Tools: reap a session’s LSP servers when it closes - Tools: fix contradictions and defects in tool descriptions, schemas, and harness pools - MCP: reap stdio MCP children on session close - Shell: reuse spawn-time skill discovery for session telemetry - Tools: stop leaking shell-wrapper positional params into sourced scripts (fixes activate_conda under persistent/static shell) - Shell: self-heal corrupt session-search SQLite cache - Workspace: cap workspace-server tokio workers on many-core hosts - Shell: reap a session’s child processes when it closes - Crash handler: capture SIGABRT so panic-aborts leave crash reports - CLI chat proxy: team-scoped Grok Code managed-config admin routes - MCP: add CLI enable/disable for MCP servers - Shell: cap tokio worker threads for startup thread demand - Workspace: harden git_commit and add git_sync_base operation - Circuit breaker: add feature-gated gRPC retry policy Source-Revision: 2a818575225183d8ca915f5632a09b8067b5156a
This commit is contained in:
parent
02d9359435
commit
5da6962e4a
192 changed files with 10337 additions and 3421 deletions
|
|
@ -1,9 +1,10 @@
|
|||
//! E2E: the coding-data privacy upsell banner — shown on the welcome screen
|
||||
//! for an opted-out OAuth user under the `privacy_notice_rollout` flag,
|
||||
//! persisting into the agent view, and acked (never re-shown) via both
|
||||
//! buttons: `[Customize in settings]` opens the settings chooser and stamps
|
||||
//! `[privacy].privacy_banner_acked`; `[Accept]` opts the user in through the
|
||||
//! shell's `PUT /privacy/coding-data-retention` round trip before acking.
|
||||
//! buttons: `[Opt out]` dismisses on the spot, stamping
|
||||
//! `[privacy].privacy_banner_acked` without waiting on the server; `[Opt in]`
|
||||
//! opts the user in through the shell's `PUT /privacy/coding-data-retention`
|
||||
//! round trip and acks only once that succeeds.
|
||||
//!
|
||||
//! Drives the real pager binary through a PTY against the shared mock
|
||||
//! inference server (isolated `$HOME`), with a seeded opted-out OAuth entry
|
||||
|
|
@ -27,20 +28,20 @@ use xai_grok_pager_pty_harness::{
|
|||
const ROWS: u16 = 50;
|
||||
const COLS: u16 = 120;
|
||||
const BANNER_TITLE: &str = "Help improve Grok";
|
||||
const CUSTOMIZE: &str = "[Customize in settings]";
|
||||
const ACCEPT: &str = "[Accept]";
|
||||
const OPT_OUT: &str = "[Opt out]";
|
||||
const OPT_IN: &str = "[Opt in]";
|
||||
const ACK: &str = "BANNERACK";
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
#[ignore] // opt-in: spawns the real pager binary in a PTY (CI runs with --ignored)
|
||||
async fn privacy_banner_welcome_customize_ack_persists() {
|
||||
run_customize().await.expect("privacy banner customize e2e");
|
||||
async fn privacy_banner_welcome_opt_out_ack_persists() {
|
||||
run_opt_out().await.expect("privacy banner opt-out e2e");
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
#[ignore] // opt-in: spawns the real pager binary in a PTY (CI runs with --ignored)
|
||||
async fn privacy_banner_persists_into_agent_view_and_accept_opts_in() {
|
||||
run_accept().await.expect("privacy banner accept e2e");
|
||||
async fn privacy_banner_persists_into_agent_view_and_opt_in_shares() {
|
||||
run_opt_in().await.expect("privacy banner opt-in e2e");
|
||||
}
|
||||
|
||||
/// Rollout flag forced on (env override beats remote settings) and the
|
||||
|
|
@ -53,7 +54,7 @@ fn banner_env_ops() -> [EnvOp<'static>; 2] {
|
|||
]
|
||||
}
|
||||
|
||||
async fn run_customize() -> Result<()> {
|
||||
async fn run_opt_out() -> Result<()> {
|
||||
let content = ContentController::start()
|
||||
.await
|
||||
.context("start mock server")?;
|
||||
|
|
@ -66,29 +67,27 @@ async fn run_customize() -> Result<()> {
|
|||
let mut pager = spawn_pager(&binary, &content, project.path()).context("spawn pager")?;
|
||||
wait_for_banner(&mut pager)?;
|
||||
assert!(
|
||||
pager.contains_text(ACCEPT),
|
||||
"welcome banner is missing {ACCEPT}:\n{}",
|
||||
pager.contains_text(OPT_IN),
|
||||
"welcome banner is missing {OPT_IN}:\n{}",
|
||||
pager.screen_contents()
|
||||
);
|
||||
|
||||
click_text(&mut pager, CUSTOMIZE).context("click Customize")?;
|
||||
click_text(&mut pager, OPT_OUT).context("click Opt out")?;
|
||||
|
||||
// Dismissal is local and immediate — it must not wait on the server, and
|
||||
// must not detour into settings.
|
||||
pager
|
||||
.wait_for_text("Coding data sharing", Duration::from_secs(20))
|
||||
.context("settings chooser opened on Coding data sharing")?;
|
||||
.wait_for_text_absent(BANNER_TITLE, Duration::from_secs(10))
|
||||
.context("banner dismissed by [Opt out]")?;
|
||||
assert!(
|
||||
pager.contains_text("Opt in") && pager.contains_text("Opt out"),
|
||||
"chooser is missing the Opt in / Opt out choices:\n{}",
|
||||
!pager.contains_text("Coding data,"),
|
||||
"[Opt out] must answer the question, not open settings:\n{}",
|
||||
pager.screen_contents()
|
||||
);
|
||||
|
||||
// Customize acks immediately; the config write is async — poll for it.
|
||||
// The config write is async — poll for it.
|
||||
wait_for_ack_on_disk(&mut pager, content.home(), Duration::from_secs(10))?;
|
||||
|
||||
// Close the chooser, then the settings list, then quit gracefully.
|
||||
pager.inject_keys(keys::ESC).context("close chooser")?;
|
||||
pager.update(Duration::from_millis(300));
|
||||
pager.inject_keys(keys::ESC).context("close settings")?;
|
||||
pager.update(Duration::from_millis(300));
|
||||
quit_via_double_ctrl_c(&mut pager)?;
|
||||
drop(pager);
|
||||
|
||||
|
|
@ -110,7 +109,7 @@ async fn run_customize() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn run_accept() -> Result<()> {
|
||||
async fn run_opt_in() -> Result<()> {
|
||||
let content = ContentController::start()
|
||||
.await
|
||||
.context("start mock server")?;
|
||||
|
|
@ -136,12 +135,12 @@ async fn run_accept() -> Result<()> {
|
|||
pager.screen_contents()
|
||||
);
|
||||
|
||||
click_text(&mut pager, ACCEPT).context("click Accept")?;
|
||||
click_text(&mut pager, OPT_IN).context("click Opt in")?;
|
||||
|
||||
// Ack only lands after the shell's PUT round trip confirms 2xx.
|
||||
pager
|
||||
.wait_for_text_absent(BANNER_TITLE, Duration::from_secs(20))
|
||||
.context("banner disappeared after Accept")?;
|
||||
.context("banner disappeared after [Opt in]")?;
|
||||
wait_for_ack_on_disk(&mut pager, content.home(), Duration::from_secs(10))?;
|
||||
|
||||
let put_bodies: Vec<_> = content
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//! E2E: the settings modal's locked `Coding data sharing` row, driven off
|
||||
//! E2E: the settings modal's locked coding-data row, driven off
|
||||
//! the seeded auth entry through the full pipeline (auth.json → shell
|
||||
//! `GrokAuth` → auth meta → `AppView::coding_data_sharing_lock()` →
|
||||
//! `PagerLocalSnapshot` → render):
|
||||
|
|
@ -33,11 +33,16 @@ use xai_grok_pager_pty_harness::{
|
|||
const ROWS: u16 = 50;
|
||||
const COLS: u16 = 120;
|
||||
const BANNER_TITLE: &str = "Help improve Grok";
|
||||
const ROW_LABEL: &str = "Coding data sharing";
|
||||
/// Head of the row's label (`Coding data, retention, and training`). The
|
||||
/// modal truncates long labels, so match the stable prefix.
|
||||
const ROW_LABEL: &str = "Coding data";
|
||||
const CHEVRON: &str = "\u{203A}"; // ›
|
||||
const ZDR_REASON: &str = "Your team has Zero Data Retention.";
|
||||
const TEAM_REASON: &str = "Managed by your team admin.";
|
||||
const DESCRIPTION_PREFIX: &str = "Controls whether";
|
||||
/// Head of the row's description in `settings/defs.rs`. Kept short so it
|
||||
/// can't span one of the modal's word wraps — `contains_text` joins rows
|
||||
/// with `\n`, so a match on wrapped copy would silently never fire.
|
||||
const DESCRIPTION_PREFIX: &str = "Opt-in to provide SpaceXAI";
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
#[ignore] // opt-in: spawns the real pager binary in a PTY (CI runs with --ignored)
|
||||
|
|
@ -222,7 +227,7 @@ fn open_settings_and_grab_row_line(pager: &mut PtyHarness) -> Result<String> {
|
|||
pager.inject_keys(keys::ENTER).context("commit filter")?;
|
||||
pager
|
||||
.wait_for_text(ROW_LABEL, Duration::from_secs(20))
|
||||
.context("Coding data sharing row visible")?;
|
||||
.context("coding-data row visible")?;
|
||||
pager.update(Duration::from_millis(500));
|
||||
let screen = pager.screen_contents();
|
||||
screen
|
||||
|
|
@ -235,7 +240,7 @@ fn open_settings_and_grab_row_line(pager: &mut PtyHarness) -> Result<String> {
|
|||
/// Expand the focused row with `→` (Browse-mode `KeyCode::Right` inserts the
|
||||
/// focused key into `expanded_keys`) and wait for `reason` to render.
|
||||
/// Callers reach here from [`open_settings_and_grab_row_line`], which leaves
|
||||
/// the Coding data sharing row focused.
|
||||
/// the coding-data row focused.
|
||||
fn expand_focused_row(pager: &mut PtyHarness, reason: &str) -> Result<()> {
|
||||
pager.inject_keys(keys::RIGHT).context("expand row")?;
|
||||
pager.update(Duration::from_millis(300));
|
||||
|
|
|
|||
Loading…
Reference in a new issue