From cd3eb19f009f8dc0531be3cf3c2e4a0a4b9c5ccc Mon Sep 17 00:00:00 2001 From: Oliver Slapinski Date: Sat, 22 Aug 2026 01:22:29 -0400 Subject: [PATCH 1/2] prompts: add Sashiko review profile Signed-off-by: Oliver Slapinski --- designs/DESIGN_SASHIKO_REVIEW_PROFILE.md | 79 ++++++++++++++++ src/prompt_bundle.rs | 27 ++++++ src/worker/prompts.rs | 90 +++++++++++++++++++ third_party/prompts/sashiko/README.md | 22 +++++ third_party/prompts/sashiko/callstack.md | 22 +++++ .../prompts/sashiko/false-positive-guide.md | 30 +++++++ .../prompts/sashiko/inline-template.md | 16 ++++ .../prompts/sashiko/patterns/ai-boundaries.md | 10 +++ .../sashiko/patterns/async-concurrency.md | 11 +++ .../sashiko/patterns/git-subprocess.md | 10 +++ .../sashiko/patterns/persistence-retries.md | 10 +++ .../sashiko/patterns/webhook-security.md | 10 +++ .../prompts/sashiko/project-context.md | 21 +++++ third_party/prompts/sashiko/review-core.md | 18 ++++ third_party/prompts/sashiko/severity.md | 35 ++++++++ .../prompts/sashiko/technical-patterns.md | 66 ++++++++++++++ 16 files changed, 477 insertions(+) create mode 100644 designs/DESIGN_SASHIKO_REVIEW_PROFILE.md create mode 100644 third_party/prompts/sashiko/README.md create mode 100644 third_party/prompts/sashiko/callstack.md create mode 100644 third_party/prompts/sashiko/false-positive-guide.md create mode 100644 third_party/prompts/sashiko/inline-template.md create mode 100644 third_party/prompts/sashiko/patterns/ai-boundaries.md create mode 100644 third_party/prompts/sashiko/patterns/async-concurrency.md create mode 100644 third_party/prompts/sashiko/patterns/git-subprocess.md create mode 100644 third_party/prompts/sashiko/patterns/persistence-retries.md create mode 100644 third_party/prompts/sashiko/patterns/webhook-security.md create mode 100644 third_party/prompts/sashiko/project-context.md create mode 100644 third_party/prompts/sashiko/review-core.md create mode 100644 third_party/prompts/sashiko/severity.md create mode 100644 third_party/prompts/sashiko/technical-patterns.md diff --git a/designs/DESIGN_SASHIKO_REVIEW_PROFILE.md b/designs/DESIGN_SASHIKO_REVIEW_PROFILE.md new file mode 100644 index 000000000..9fdb07350 --- /dev/null +++ b/designs/DESIGN_SASHIKO_REVIEW_PROFILE.md @@ -0,0 +1,79 @@ +# Design: Sashiko Review Profile + +## Goal + +Add a bundled prompt profile for reviewing Sashiko's Rust code. The existing +review binary can select it directly, and daemon prompt-directory selection can +use the same profile when that separate plumbing is available. The profile +should improve review of Sashiko-specific failure modes without changing the +Linux kernel profile or introducing model-backed tests. + +## Current Constraint + +`PromptRegistry` loads optional subsystem and stage guidance from the selected +directory, but its shared identity and stage instructions still contain Linux +kernel defaults. `review-core.md` is currently a profile-validation marker; it +is not added to the worker's shared context. + +Changing every stage is outside this follow-up and overlaps the broader stage +configuration proposed by PR #188. A Sashiko profile nevertheless needs one +piece of guidance that is loaded for every review so kernel-specific examples +are not mistaken for project requirements. + +## Proposed Change + +Teach `PromptRegistry::build_context()` to load an optional +`project-context.md` before conditional subsystem guidance. + +- Profiles without this file produce the same shared context as before. +- The Sashiko profile uses it to identify the project, establish Rust and + service-review priorities, and mark inapplicable kernel examples as such. +- Existing stage-specific filenames remain unchanged. +- No stage configuration, remote prompt loading, template substitution, or + custom tools are introduced. + +Add `third_party/prompts/sashiko/` with: + +- `review-core.md` as the validated entry point and review protocol; +- `project-context.md` as always-loaded Sashiko guidance; +- focused guidance for async execution, Git/worktree safety, webhook and + secret boundaries, persistence/retries, and AI-provider boundaries; +- stage files for call-stack analysis, false-positive filtering, severity, and + final inline formatting. + +The profile intentionally omits `subsystem/subsystem.md`. Its small set of +guides is therefore loaded deterministically without a model-driven +preselection call. + +## Compatibility + +The kernel, systemd, and iproute profiles do not contain +`project-context.md`, so their generated shared context remains byte-for-byte +unchanged. CLI arguments, review stages, AI providers, tools, output protocol, +forge ingestion, databases, Git baselines, and worktree behavior are not +modified. + +The new profile is bundled locally by the existing build script. It performs +no network access and does not enable itself automatically. + +## Validation + +Deterministic tests will prove: + +- an absent optional project context leaves shared context unchanged; +- a present project context is loaded and identified in clean prompt logs; +- all required Sashiko files are embedded in the prompt bundle; +- the embedded profile can be materialized and loaded by `PromptRegistry`; +- the loaded context contains the Sashiko identity and critical security, + async, Git, persistence, and AI-boundary guidance; +- the profile does not require a subsystem-selection model call. + +No test invokes an AI provider or external service. + +## Non-goals + +This change does not make all hardcoded stages project-neutral, deploy a +Sashiko instance, change GitHub output formatting, support arbitrary +multi-repository review, or replace PR #188. Extracting the remaining +kernel-specific stage wording is a separate incremental refactor with its own +backward-compatibility tests. diff --git a/src/prompt_bundle.rs b/src/prompt_bundle.rs index 01d5351c9..289453544 100644 --- a/src/prompt_bundle.rs +++ b/src/prompt_bundle.rs @@ -84,6 +84,33 @@ mod tests { ); } + #[test] + fn test_prompt_bundle_contains_complete_sashiko_profile() { + let required = [ + "sashiko/review-core.md", + "sashiko/project-context.md", + "sashiko/technical-patterns.md", + "sashiko/callstack.md", + "sashiko/false-positive-guide.md", + "sashiko/severity.md", + "sashiko/inline-template.md", + "sashiko/patterns/async-concurrency.md", + "sashiko/patterns/git-subprocess.md", + "sashiko/patterns/webhook-security.md", + "sashiko/patterns/persistence-retries.md", + "sashiko/patterns/ai-boundaries.md", + ]; + + for required_path in required { + assert!( + PROMPT_BUNDLE_FILES + .iter() + .any(|(path, _)| *path == required_path), + "missing bundled Sashiko prompt: {required_path}" + ); + } + } + #[test] fn test_prompt_bundle_root_uses_xdg_data_home() { let temp = tempfile::tempdir().unwrap(); diff --git a/src/worker/prompts.rs b/src/worker/prompts.rs index ac76291a2..0f32ce47c 100644 --- a/src/worker/prompts.rs +++ b/src/worker/prompts.rs @@ -172,6 +172,12 @@ impl PromptRegistry { clean.push_str("\n"); clean.push_str("The following documents contain the official technical patterns, architectural rules, and subsystem-specific guidelines that you MUST adhere to during your review. Use these as the absolute source of truth for identifying anti-patterns and violations.\n\n"); + // Project-specific identity and review boundaries must be present even + // when subsystem guidance is selected dynamically. Existing profiles + // do not provide this optional file, preserving their current context. + self.append_file(&mut content, &mut clean_files, "project-context.md") + .await?; + // Subsystem Guidelines let subsystem_dir = self.base_dir.join("subsystem"); @@ -1152,6 +1158,90 @@ mod tests { use crate::ai::AiRole; use crate::worker::stage::create_stage; + fn materialize_embedded_profile(root: &Path, profile: &str) -> PathBuf { + let profile_prefix = format!("{profile}/"); + let profile_path = root.join(profile); + + for &(relative, content) in crate::prompt_bundle::PROMPT_BUNDLE_FILES { + let Some(profile_relative) = relative.strip_prefix(&profile_prefix) else { + continue; + }; + let destination = profile_path.join(profile_relative); + if let Some(parent) = destination.parent() { + std::fs::create_dir_all(parent).unwrap(); + } + std::fs::write(destination, content).unwrap(); + } + + profile_path + } + + #[tokio::test] + async fn test_build_context_preserves_profiles_without_project_context() { + let temp_dir = tempfile::tempdir().unwrap(); + let prompts = PromptRegistry::new(temp_dir.path().to_path_buf()); + + let (content, clean) = prompts.build_context(None).await.unwrap(); + + assert!(!content.contains("# project-context.md")); + assert!(!clean.contains("@project-context.md")); + } + + #[tokio::test] + async fn test_build_context_loads_optional_project_context() { + let temp_dir = tempfile::tempdir().unwrap(); + std::fs::write( + temp_dir.path().join("project-context.md"), + "Sashiko project identity marker\n", + ) + .unwrap(); + let prompts = PromptRegistry::new(temp_dir.path().to_path_buf()); + + let (content, clean) = prompts.build_context(None).await.unwrap(); + + assert!(content.contains("# project-context.md")); + assert!(content.contains("Sashiko project identity marker")); + assert!(clean.contains("@project-context.md")); + } + + #[tokio::test] + async fn test_bundled_sashiko_profile_loads_project_guidance() { + let temp_dir = tempfile::tempdir().unwrap(); + let profile_path = materialize_embedded_profile(temp_dir.path(), "sashiko"); + let prompts = PromptRegistry::new(profile_path.clone()); + + assert!(profile_path.join("review-core.md").is_file()); + assert!(!profile_path.join("subsystem/subsystem.md").exists()); + + let (context, clean) = prompts.build_context(None).await.unwrap(); + + assert!(context.contains("The project under review is Sashiko")); + assert!(context.contains("Async and Concurrency Boundaries")); + assert!(context.contains("Webhook and Repository Security Boundaries")); + assert!(context.contains("Persistence, Retry, and Recovery Boundaries")); + assert!(context.contains("AI Provider and Cost Boundaries")); + assert!(clean.contains("@project-context.md")); + assert!(clean.contains("@async-concurrency.md")); + } + + #[tokio::test] + async fn test_bundled_sashiko_profile_loads_stage_guides() { + let temp_dir = tempfile::tempdir().unwrap(); + let profile_path = materialize_embedded_profile(temp_dir.path(), "sashiko"); + let prompts = PromptRegistry::new(profile_path); + + let (execution, _) = prompts.get_stage_prompt(3).await.unwrap(); + assert!(execution.contains("Sashiko Call-Path Analysis")); + assert!(execution.contains("Sashiko Technical Review Patterns")); + + let (verification, _) = prompts.get_stage_prompt(10).await.unwrap(); + assert!(verification.contains("Sashiko False-Positive Guide")); + assert!(verification.contains("Sashiko Severity Levels")); + + let (report, _) = prompts.get_stage_prompt(11).await.unwrap(); + assert!(report.contains("Sashiko Inline Review Format")); + } + #[test] fn test_append_stage_dismissed_concerns_preserves_category_type() { let mut items = Vec::new(); diff --git a/third_party/prompts/sashiko/README.md b/third_party/prompts/sashiko/README.md new file mode 100644 index 000000000..5924dc70d --- /dev/null +++ b/third_party/prompts/sashiko/README.md @@ -0,0 +1,22 @@ +# Sashiko Review Profile + +This profile adds project-specific guidance for reviewing Sashiko's Rust +daemon, forge integrations, Git operations, persistence, and AI-provider +boundaries. + +The existing review binary can select it explicitly with: + +```text +review --prompts third_party/prompts/sashiko [other arguments] +``` + +Daemon configuration for selecting this directory is separate from the +profile and is not introduced here. + +The profile is local and deterministic. Loading it does not contact an AI +provider or any external service; model calls occur only when a review runs. + +The current review engine retains several kernel-oriented stage descriptions. +`project-context.md` identifies Sashiko as the target and limits those examples +to cases that actually apply to this Rust service. Making every stage +project-neutral is intentionally left to a separate refactor. diff --git a/third_party/prompts/sashiko/callstack.md b/third_party/prompts/sashiko/callstack.md new file mode 100644 index 000000000..6b64bf3fe --- /dev/null +++ b/third_party/prompts/sashiko/callstack.md @@ -0,0 +1,22 @@ +# Sashiko Call-Path Analysis + +Trace the complete path affected by the patch rather than stopping at the +modified function. + +For inbound work, follow parsing and authentication through queue submission, +persistence, patch extraction, baseline/worktree creation, review execution, +and result publication. For outbound work, trace the returned error and state +changes back to the operator-visible API or log. + +At each async boundary record: + +- who owns the task, channel, child process, temporary directory, and database + transition; +- which values cross the boundary and whether they remain tied to the same + repository, patchset, base, head, and review attempt; +- what happens on cancellation, receiver closure, timeout, retry, and partial + success; +- whether cleanup is awaited and whether a later retry can safely repeat it. + +Read concrete callers and callees before dismissing a concern. A comment or +expected deployment topology is not proof that a path is unreachable. diff --git a/third_party/prompts/sashiko/false-positive-guide.md b/third_party/prompts/sashiko/false-positive-guide.md new file mode 100644 index 000000000..3da711208 --- /dev/null +++ b/third_party/prompts/sashiko/false-positive-guide.md @@ -0,0 +1,30 @@ +# Sashiko False-Positive Guide + +Report only regressions introduced by the patch and supported by a concrete +triggering path. + +Before reporting: + +1. inspect the full Result propagation path and any caller-side validation; +2. inspect the lock or ownership boundary rather than inferring a race from + two functions that cannot run concurrently; +3. distinguish durable authoritative state from a recoverable derived file; +4. verify whether a Tokio child uses kill-on-drop and whether timeout handling + explicitly waits for termination; +5. check whether command data is passed as a separate argument rather than + interpolated into a shell command; +6. distinguish optional configuration defaults from explicit invalid values; +7. check whether the suspicious behavior exists on the base revision; +8. verify later patches in the same series before reporting an intermediate + inconsistency. + +Do not dismiss a finding merely because a deployment normally uses localhost, +a webhook normally comes from GitHub, a channel normally remains open, or an +AI provider normally responds. Conversely, do not report a missing defense +when an earlier authenticated layer or exact caller contract proves the input +cannot reach the code. + +Test-only localhost fixtures, fake providers, and temporary repositories are +not production bypasses unless the patch makes them reachable in production. +The explicit unsafe-submit option is not a production recommendation, but its +continued existence alone is not a new regression. diff --git a/third_party/prompts/sashiko/inline-template.md b/third_party/prompts/sashiko/inline-template.md new file mode 100644 index 000000000..f454de790 --- /dev/null +++ b/third_party/prompts/sashiko/inline-template.md @@ -0,0 +1,16 @@ +# Sashiko Inline Review Format + +Produce plain text suitable for a code-review comment. Do not use Markdown code +fences. Start with the reviewed commit, its Author line, and subject. Quote only +the minimal relevant diff using email-style `>` prefixes. + +Place each comment immediately after the quoted code that introduces the +problem. Put `[Severity: Critical]`, `[Severity: High]`, +`[Severity: Medium]`, or `[Severity: Low]` on the line before the comment. + +Name the exact file, function or symbol, triggering condition, and consequence. +Do not invent line numbers. Ask a concise technical question where natural, +and avoid accusations, generic checklists, or findings already disproved by +the false-positive pass. + +End the report with a blank line. diff --git a/third_party/prompts/sashiko/patterns/ai-boundaries.md b/third_party/prompts/sashiko/patterns/ai-boundaries.md new file mode 100644 index 000000000..265a40cbf --- /dev/null +++ b/third_party/prompts/sashiko/patterns/ai-boundaries.md @@ -0,0 +1,10 @@ +# AI Provider and Cost Boundaries + +Provider responses, tool calls, and usage metadata are untrusted external data. +Validate schemas and bounds, redact secrets from errors, and preserve provider +capability differences. Token and output budgets must include the intended +cached/uncached quantities without underflow or double counting. + +Rate-limit and transient retries must honor cancellation and deadlines. Tests +must use deterministic fakes and must not require credentials, live models, +network access, or paid quota. diff --git a/third_party/prompts/sashiko/patterns/async-concurrency.md b/third_party/prompts/sashiko/patterns/async-concurrency.md new file mode 100644 index 000000000..67bb3a6b8 --- /dev/null +++ b/third_party/prompts/sashiko/patterns/async-concurrency.md @@ -0,0 +1,11 @@ +# Async and Concurrency Boundaries + +Check Tokio task ownership, channel capacity and closure, shared-state locking, +deadline propagation, cancellation, and shutdown ordering. A task that owns a +resource must either be awaited or have an explicit cancellation and cleanup +path. Never hold a synchronous or async mutex across unrelated slow work unless +the protected invariant requires it. + +For races, name both operations, their owners, the shared state, and an actual +interleaving. Verify whether a repository, remote, worktree, patchset, quota, or +database lock already serializes that interleaving before reporting it. diff --git a/third_party/prompts/sashiko/patterns/git-subprocess.md b/third_party/prompts/sashiko/patterns/git-subprocess.md new file mode 100644 index 000000000..cedb9179e --- /dev/null +++ b/third_party/prompts/sashiko/patterns/git-subprocess.md @@ -0,0 +1,10 @@ +# Git, Filesystem, and Subprocess Boundaries + +Check every subprocess status and stderr path. Ensure stdin is closed when the +child expects end-of-file, timeout paths kill and reap the child, and output +cannot grow without a bound appropriate to the command. + +Git operations run against shared repository state. Preserve protocol +restrictions, safe argument separation, worktree locks, exact commit identity, +and cleanup of only Sashiko-owned paths. A temporary directory dropping does +not by itself clean Git's worktree metadata. diff --git a/third_party/prompts/sashiko/patterns/persistence-retries.md b/third_party/prompts/sashiko/patterns/persistence-retries.md new file mode 100644 index 000000000..6f9a019eb --- /dev/null +++ b/third_party/prompts/sashiko/patterns/persistence-retries.md @@ -0,0 +1,10 @@ +# Persistence, Retry, and Recovery Boundaries + +Trace each logical operation across database writes, queue sends, Git changes, +files, and remote publication. Identify which state is authoritative and prove +that partial failure is atomic, compensated, or safely recoverable. + +Retries and duplicate webhooks must not create duplicate patchsets, publish a +result twice, delete state from a newer attempt, or repeat a non-idempotent +tool action. Check the identity key and snapshot revalidation used by every +retry. diff --git a/third_party/prompts/sashiko/patterns/webhook-security.md b/third_party/prompts/sashiko/patterns/webhook-security.md new file mode 100644 index 000000000..bbb6f55b6 --- /dev/null +++ b/third_party/prompts/sashiko/patterns/webhook-security.md @@ -0,0 +1,10 @@ +# Webhook and Repository Security Boundaries + +Treat headers, JSON fields, repository URLs, commit ranges, PR metadata, and +forge error text as untrusted. Preserve authentication before side effects, +constant-time secret verification, event validation, canonical SHA checks, and +repository URL restrictions. + +Check reverse-proxy behavior explicitly: a loopback peer is not trusted when a +configured secret should authenticate the original request. Never use unsafe +submission flags to solve production deployment or testing problems. diff --git a/third_party/prompts/sashiko/project-context.md b/third_party/prompts/sashiko/project-context.md new file mode 100644 index 000000000..76c3a182c --- /dev/null +++ b/third_party/prompts/sashiko/project-context.md @@ -0,0 +1,21 @@ +# Sashiko Project Context + +The project under review is Sashiko, not the Linux kernel. Sashiko is a Rust +daemon and CLI for automated code review. Treat kernel-specific APIs and +hardware examples in generic stage text as analogies only; do not report their +absence as a defect in Sashiko. + +Prioritize behavior that can corrupt a patch, review the wrong revision, lose +or duplicate persistent state, expose a secret, accept an unauthenticated or +unsafe request, leak a subprocess or worktree, exceed an operator's token +budget, or silently change existing Linux-kernel review behavior. + +Review against Rust 1.90 and the repository's existing public and operational +contracts. Prefer deterministic tests with local temporary repositories, +localhost servers, and fake AI boundaries. Never require a paid model, external +forge, kernel checkout, database service, or network connection to validate a +finding. + +Configuration compatibility is part of the public contract. Optional settings +must preserve their historical defaults, and an explicit invalid value must +fail clearly rather than silently selecting a different behavior. diff --git a/third_party/prompts/sashiko/review-core.md b/third_party/prompts/sashiko/review-core.md new file mode 100644 index 000000000..0c2a24078 --- /dev/null +++ b/third_party/prompts/sashiko/review-core.md @@ -0,0 +1,18 @@ +# Sashiko Patch Review Protocol + +Review the proposed change as a regression analysis of Sashiko, an async Rust +service that ingests untrusted patch and forge data, manages Git repositories +and worktrees, invokes subprocesses, persists review state, and coordinates AI +providers. + +For every finding: + +1. identify the changed function and the concrete triggering path; +2. inspect relevant callers, callees, configuration defaults, and cleanup; +3. distinguish an introduced regression from pre-existing behavior; +4. prove the consequence with code rather than a hypothetical concern; +5. check tests for the boundary that actually failed; +6. report the exact file and symbol without inventing a line number. + +Preserve established Linux-kernel behavior unless the patch explicitly and +safely changes it. Do not require external model calls for ordinary tests. diff --git a/third_party/prompts/sashiko/severity.md b/third_party/prompts/sashiko/severity.md new file mode 100644 index 000000000..9522d7150 --- /dev/null +++ b/third_party/prompts/sashiko/severity.md @@ -0,0 +1,35 @@ +# Sashiko Severity Levels + +Assign severity from the demonstrated consequence, triggering path, and +reachability. State that reasoning before the label. + +## Critical + +- unauthenticated remote code execution or credential disclosure; +- reviewing attacker-selected local/internal resources through a new reachable + path; +- irreversible corruption or deletion of repositories, patches, or durable + review state across users. + +## High + +- authenticated or commonly reachable data corruption; +- reviewing or publishing results for the wrong base, head, repository, or PR; +- a daemon-wide deadlock, persistent outage, or unbounded paid-model usage; +- a reliable webhook-authentication bypass or broadly exposed secret. + +## Medium + +- a recoverable failed review, leaked task/process/worktree, duplicate work, or + bounded resource exhaustion; +- a compatibility regression affecting a supported configuration or provider; +- incorrect retry, quota, or state reporting with operational impact. + +## Low + +- a real but minor usability, diagnostic, documentation, or cold-path + inefficiency issue with limited operational effect. + +Speculation is capped at medium, but uncertainty is not evidence. If the +trigger cannot be established after reading the relevant path, do not report +the concern as a finding. diff --git a/third_party/prompts/sashiko/technical-patterns.md b/third_party/prompts/sashiko/technical-patterns.md new file mode 100644 index 000000000..6a1764172 --- /dev/null +++ b/third_party/prompts/sashiko/technical-patterns.md @@ -0,0 +1,66 @@ +# Sashiko Technical Review Patterns + +## Async ownership and cancellation + +- Trace every spawned task, channel sender, receiver, and subprocess to its + owner. Prove how it terminates during success, error, timeout, and shutdown. +- Do not treat dropping a future as sufficient subprocess cleanup. Check that + the child is killed when required and subsequently reaped. +- Identify synchronous filesystem, process, compression, or database work in + async paths that can stall unrelated reviews. +- For bounded channels, inspect backpressure and closure. A failed send must not + leave a database row claiming that work is queued when no consumer received + it. + +## Git and worktree state + +- Treat repository URLs, commit IDs, ranges, patches, paths, refs, and remote + output as untrusted input. +- Verify Git arguments remain separate process arguments and that protocol + restrictions are preserved on network-facing fetches. +- Shared repository metadata operations require the existing synchronization. + Check concurrent remote changes, worktree creation/removal, and pruning. +- Cleanup may remove only Sashiko-owned paths. Check path derivation, ownership + markers, temporary-directory lifetimes, and partial-failure behavior. +- A review must use the intended base and head. Check range direction, SHA + validation, patch order, baseline selection, and final worktree contents. + +## Webhook and secret boundaries + +- When a webhook secret is configured, signature verification must apply even + when a reverse proxy makes the peer address look local. +- Preserve event-type checks, constant-time signature/token comparison, SHA + validation, positive PR numbers, and repository URL checks. +- Host blocklists are best-effort SSRF defenses, not proof that DNS resolution + is safe. Do not weaken primary authentication based on the blocklist. +- Never log credentials embedded in URLs, headers, provider errors, child + arguments, settings, or Git remotes. Follow values through error formatting. + +## Persistence and retries + +- Multi-step state transitions must be atomic or explicitly recoverable. Look + for a durable state update followed by a channel send, remote call, or file + write that can fail independently. +- Retryable work must be idempotent. Check duplicate patch ingestion, repeated + webhook delivery, outbox insertion, review attempts, and derived artifacts. +- Preserve the authoritative state before deleting or publishing derived data. +- Propagate errors with enough context to recover, without leaking secrets or + turning recoverable failures into panics. + +## AI-provider and review boundaries + +- Preserve provider selection, request/response schemas, tool permissions, + timeout behavior, quota accounting, and token-budget enforcement. +- Cached tokens are a breakdown of prompt tokens, not additional usage. Check + arithmetic for underflow, double counting, and inconsistent provider fields. +- Classify rate-limit, transient, and fatal errors consistently. Retrying must + respect cancellation and must not replay an unsafe side effect. +- Tests should stop at a deterministic fake provider or subprocess boundary. + A unit or CI test must not consume credentials, quota, or paid API calls. + +## Backwards compatibility + +- Check Serde defaults, denied unknown fields, environment overrides, CLI + defaults, persisted schemas, and old Settings.toml files. +- A project-specific change must not silently alter NNTP, GitLab, Patchwork, + local review, kernel prompts, baselines, worktree semantics, or AI providers. From d045226e9965ddb8a231f0524df4cfac8444d773 Mon Sep 17 00:00:00 2001 From: Oliver Slapinski Date: Thu, 10 Sep 2026 17:53:51 -0400 Subject: [PATCH 2/2] tests: avoid prompt bundle merge conflict Keep the Sashiko completeness test at a merge-friendly anchor. Signed-off-by: Oliver Slapinski --- src/prompt_bundle.rs | 48 ++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/prompt_bundle.rs b/src/prompt_bundle.rs index 010e7a5c9..264dae3a2 100644 --- a/src/prompt_bundle.rs +++ b/src/prompt_bundle.rs @@ -84,6 +84,30 @@ mod tests { ); } + #[test] + fn test_prompt_bundle_root_uses_xdg_data_home() { + let temp = tempfile::tempdir().unwrap(); + let old_xdg = std::env::var_os("XDG_DATA_HOME"); + unsafe { + std::env::set_var("XDG_DATA_HOME", temp.path()); + } + + assert_eq!( + prompt_bundle_root().unwrap(), + temp.path() + .join("sashiko/prompts") + .join(PROMPT_BUNDLE_REVISION) + ); + + unsafe { + if let Some(value) = old_xdg { + std::env::set_var("XDG_DATA_HOME", value); + } else { + std::env::remove_var("XDG_DATA_HOME"); + } + } + } + #[test] fn test_prompt_bundle_contains_complete_sashiko_profile() { let required = [ @@ -117,28 +141,4 @@ mod tests { "the trusted profile bundle must not advertise a candidate-relative prompt path" ); } - - #[test] - fn test_prompt_bundle_root_uses_xdg_data_home() { - let temp = tempfile::tempdir().unwrap(); - let old_xdg = std::env::var_os("XDG_DATA_HOME"); - unsafe { - std::env::set_var("XDG_DATA_HOME", temp.path()); - } - - assert_eq!( - prompt_bundle_root().unwrap(), - temp.path() - .join("sashiko/prompts") - .join(PROMPT_BUNDLE_REVISION) - ); - - unsafe { - if let Some(value) = old_xdg { - std::env::set_var("XDG_DATA_HOME", value); - } else { - std::env::remove_var("XDG_DATA_HOME"); - } - } - } }