Docs: marketplace, and clean up new-code warnings/lints

CLAUDE.md gets a Marketplace subsection under Key Conventions (the sync
script is app-embedded and re-uploaded on every sync, never baked into
container/ — pre-flight F9) and the Settings export/import section now
covers marketplace account tokens traveling in ExportedSecrets and the
import preview's warning on global hook and plugin installs.
HOW-TO-USE.md gets a Marketplace section (placed after Shared Claude
Authentication) with its Table of Contents entry (pre-flight N13). The
spec doc's stale keychain service name, gh-login flags and
upload_bytes_to_container signature are amended to match the shipped
code (pre-flight N10).

Also fixes the new marketplace code's remaining build/clippy warnings:
BTreeMap/Sha256/Digest imports in tree.rs gated behind #[cfg(test)]
(their only uses are on MemTree, already test-only), the unused
`pub use marketplace::*` glob re-export dropped from models/mod.rs,
gh_login::strip_ansi marked #[cfg(test)] (production streams through
AnsiStripper instead), and four clippy lints in marketplace test code
(double_ended_iterator_last, cloned_ref_to_slice_refs x2,
single_match). Flushes the unresolved getMarketplaceSyncReport promise
in MarketplaceSection.test.tsx's "opens the Marketplace filtered to
this project" test to remove its act() warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-27 09:54:24 -07:00
co-authored by Claude Opus 5.5
parent 9588687934
commit 2c1d6d8713
10 changed files with 99 additions and 25 deletions
+4 -4
View File
@@ -641,12 +641,12 @@ mod tests {
// And the target must never see a connection carrying the token —
// ideally no connection at all, since the client never follows.
match rx.recv_timeout(StdDuration::from_millis(500)) {
Ok(request) => assert!(
// no connection at all is also the expected outcome
if let Ok(request) = rx.recv_timeout(StdDuration::from_millis(500)) {
assert!(
!request.contains(FAKE) && !request.to_ascii_lowercase().contains("private-token"),
"the redirect target must never receive the token: {request}"
),
Err(_) => {} // no connection at all — the expected outcome
);
}
}
}
+3 -2
View File
@@ -67,8 +67,9 @@ pub fn valid_host(host: &str) -> bool {
/// Remove terminal control sequences and carriage returns from one complete
/// piece of text. An unterminated sequence at the end is dropped. The login
/// itself uses a streaming [`AnsiStripper`], which carries a sequence split
/// across chunks instead.
pub fn strip_ansi(s: &str) -> String {
/// across chunks instead; this one-shot form exists for tests only.
#[cfg(test)]
fn strip_ansi(s: &str) -> String {
AnsiStripper::default().push(s.as_bytes())
}
+2 -2
View File
@@ -91,7 +91,7 @@ pub fn classify_fetch_error(chain: &str) -> FetchError {
let cause = chain
.lines()
.filter_map(|l| l.trim_start().strip_prefix("└─"))
.last()
.next_back()
.unwrap_or(chain);
return FetchError::Network(first_line(cause));
}
@@ -600,7 +600,7 @@ mod tests {
want.sort();
assert_eq!(pins(&repo), want);
set_pins(&repo, &[b.clone()]).unwrap();
set_pins(&repo, std::slice::from_ref(&b)).unwrap();
assert_eq!(pins(&repo), vec![format!("{}{}", PIN_PREFIX, b)]);
}
+1 -1
View File
@@ -241,7 +241,7 @@ mod tests {
skipped: vec![script_skip.clone()],
..Default::default()
};
let merged = with_payload_skips(report, &[payload_skip.clone()]);
let merged = with_payload_skips(report, std::slice::from_ref(&payload_skip));
assert_eq!(merged.skipped, vec![payload_skip, script_skip]);
}
+2
View File
@@ -4,8 +4,10 @@
//! against [`MemTree`] with no git involved, and runs in production against
//! [`GitTree`], which reads git objects straight out of the bare cache.
#[cfg(test)]
use std::collections::BTreeMap;
#[cfg(test)]
use sha2::{Digest, Sha256};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-1
View File
@@ -11,7 +11,6 @@ pub mod update_info;
pub use app_settings::*;
pub use container_config::*;
pub use gateway_settings::*;
pub use marketplace::*;
pub use migration::*;
pub use note::*;
pub use project::*;
@@ -77,8 +77,9 @@ describe("MarketplaceSection", () => {
);
});
it("opens the Marketplace filtered to this project", () => {
it("opens the Marketplace filtered to this project", async () => {
render(<MarketplaceSection project={project} />);
await screen.findByText(/a file you created has the same name/);
fireEvent.click(screen.getByRole("button", { name: "Open in Marketplace" }));
expect(useAppState.getState().activeTabKey).toBe(MARKETPLACE_TAB_KEY);
expect(useAppState.getState().marketplaceFilterProjectId).toBe("p1");