diff --git a/.agents/skills/add-settings-e2e-test/SKILL.md b/.agents/skills/add-settings-e2e-test/SKILL.md new file mode 100644 index 00000000000..90558df9571 --- /dev/null +++ b/.agents/skills/add-settings-e2e-test/SKILL.md @@ -0,0 +1,179 @@ +--- +name: add-settings-e2e-test +description: Add or update durable Playwright coverage for Sim settings using the correct literal contract or owning spec, existing personas, guarded orchestration, semantic assertions, cleanup, and secret-safe diagnostics. Use when asked to add a settings browser test, cover a settings regression, or update settings E2E acceptance behavior. +argument-hint: +--- + +# Add Settings E2E Test + +Add the smallest durable browser proof for the requested observable behavior. +Do not begin by copying a nearby test: first identify which acceptance dataset +or spec owns the behavior. + +## Read first + +1. `apps/sim/e2e/MAINTENANCE.md` — change type to contract/spec ownership. +2. `apps/sim/e2e/README.md` — orchestrator, project boundaries, focused commands, + diagnostics, acceptance and security boundaries, and CI policy. + +Both files are canonical. This skill is the authoring procedure, not a second +copy of their inventories or commands. If this procedure ever conflicts with a +canonical document, the canonical document wins. + +## Procedure + +### 1. Define the observable contract + +Write down: + +- the user persona and resource plane (account, organization, or workspace); +- the action or direct URL; +- the exact observable result: path, semantic readiness, visible/hidden item, + access outcome, enabled/disabled control, warning, or persisted state; +- whether product behavior is intentionally changing or an implementation + change must preserve the existing contract. + +Intended behavior changes update product code and literal acceptance +expectations together. Behavior-preserving fixes add or strengthen proof without +rewriting unrelated expectations. + +### 2. Choose the owner before writing code + +- **Canonical section, route, copy, readiness, or persona visibility:** update + `apps/sim/e2e/settings/navigation/contracts.ts`; its specs generate cases from + the literal dataset, including unauthenticated redirect cases. +- **Browser Back, app Back, direct-entry fallback, or return destination:** edit + `apps/sim/e2e/settings/navigation/history.spec.ts` directly. +- **Direct access, plan/role/entitlement gate, or mutation availability:** update + `apps/sim/e2e/settings/authorization/contracts.ts`; its access and mutation + specs generate cases from that dataset. + - If an existing navigation case already owns the sidebar proof, add or reuse + an `existingNavigationProofs` entry and set + `sidebar.existingProofId`. Do not execute the same sidebar assertion again + in the authorization case. +- **Unsaved-change behavior:** edit + `apps/sim/e2e/settings/authorization/unsaved-changes.spec.ts` directly. +- **Secrets or API key lifecycle:** edit the owning credential spec directly. + Change `apps/sim/e2e/settings/credentials/contracts.ts` only when reused + authorization proof IDs or boundaries change. +- **People, access control, SSO, data retention, or MCP lifecycle:** edit the + owning workflow spec. Change `apps/sim/e2e/settings/workflows/contracts.ts` + when stable lifecycle or cross-contract proof references change. +- **Persona or seeded relationship:** update + `apps/sim/e2e/settings/personas.ts` and the relevant + scenario/factory/integrity proof. + +The categories are non-exclusive. Prefer extending an existing literal row or +owning workflow over adding a duplicate standalone test. + +### 3. Use the owning fixture wrapper + +- Browser-touching settings specs must not import runtime `test` or `expect` + from `@playwright/test`. Type-only imports such as `Page` or `Response` are + allowed. +- Navigation, authorization, persona-contract, and persona-isolation browser + specs use `apps/sim/e2e/fixtures/persona-test.ts`. +- Credential specs use the local `credential-test.ts`; workflow specs use the + local `workflow-test.ts`; authenticated or unauthenticated smoke and + browser-touching harness-level specs use + `apps/sim/e2e/fixtures/browser-test.ts`. +- Pure dataset-only contract-integrity specs and non-browser foundation policy + specs may use the Playwright test runner directly. If they begin creating a + browser, context, or page, move them to the owning wrapper first. +- Persona-based tests must create contexts through `contextForPersona` or an + owning helper that calls it. The inherited `page` and `context` fixtures on + `persona-test` are not persona-authenticated or network-guarded. +- `browser-test` guards its default context. Every manual + `browser.newContext()` must install `installBrowserNetworkGuard()` immediately, + then close the context and assert the guard even when the test fails; follow + the aggregate-cleanup pattern in authenticated smoke coverage. Do not use + `browser.newPage()`: it creates an implicit unguarded context; create and guard + an explicit context, then call `context.newPage()`. +- Reuse the owning directory's helpers. Credential tests use their local + `newPersonaPage` helper so the page is registered for failure sanitization. + Wrapper selection alone does not install every context, cleanup, attachment, + redaction, or artifact safeguard; bypassing the guarded fixture/helper path is + a test-safety bug. + +### 4. Reuse the existing world + +- Reuse an existing persona and seeded resource when it expresses the required + role, plan, entitlement, and ownership boundary. +- Add a persona or scenario edge only when no existing driver can prove the + contract without changing its meaning. +- Use run-namespaced factories and unique resources. Do not introduce shared + mutable fixture state. +- Keep sensitive fixture values browser-resident through the existing helpers. + The Playwright process must not receive database credentials, admin keys, + persona passwords, or plaintext values that the owning spec keeps in-page. + +### 5. Assert semantics, not implementation details + +- Use accessible roles, labels, names, and visible text. +- Assert exact paths and user-facing contract copy when those are the behavior + under test. +- Wait on semantic readiness, relevant same-origin responses, or explicit + authorization state—not arbitrary sleeps or CSS classes. +- For access controls, prove direct URL behavior as well as sidebar visibility. +- Do not import production navigation or authorization implementations to + generate expected values. + +### 6. Make mutations reversible + +- Register cleanup before the first mutation. +- Create uniquely named resources and restore the exact captured baseline. +- Use the production UI and same-origin APIs for behavior under test; use a + trusted database probe only where the existing suite defines one. +- Keep cleanup LIFO and safe after partial failure. Never weaken fixture + invariants merely to make a test pass. + +### 7. Respect external and diagnostic boundaries + +- Use the existing strict loopback Stripe and MCP fakes, the mail mock-success + path with provider credentials absent, and the other reviewed test + boundaries. An unexpected external request must fail rather than silently + egress. +- Credentials, tokens, certificates, verification values, storage state, and + sensitive response bodies may be used only through existing reviewed paths; + never log, attach, or retain them in diagnostics. +- The SSO workflow's public-certificate browser input is one reviewed example; + preserve its input clearing and diagnostic suppression. +- Preserve the owning spec's trace, screenshot, video, and attachment policy. + New suppression requires a security reason and corresponding safety coverage. + +### 8. Keep unit and integrity proof aligned + +- Update affected production unit tests for navigation, route gates, + authorization, billing, or business rules. +- Update the owning `contract-integrity.spec.ts` when adding IDs, references, or + a new contract axis. +- Preserve stable proof IDs; do not rerun a browser case merely because another + contract can reference its existing proof. + +### 9. Verify through the orchestrator + +- Run the affected unit and contract-integrity tests. +- Run the focused canonical project and path documented in + `apps/sim/e2e/README.md`. +- Invoke only `bun run test:e2e`; never invoke raw `playwright test`. +- Use `--reuse-build` and a single explicit project with `--no-deps` for local + iteration when the README permits it. +- Follow the retry policy in the canonical README and Playwright config. It is + currently zero; do not enable retries or change worker policy in a feature + test. Do not use `test.only`, unexplained skips, arbitrary browser sleeps, or + test-local environment bypasses. +- After focused proof passes, rely on the required complete CI suite. Run a + repeated stability gate only when the change's scope or acceptance plan calls + for one. + +## Completion checklist + +- [ ] Every observable facet has one clear owner, and all applicable owners were + updated. +- [ ] Allowed and denied outcomes exist where the feature is gated. +- [ ] Locators and readiness assertions are semantic and accessible. +- [ ] Mutations register cleanup first and restore the exact baseline. +- [ ] No secret or external-egress boundary was weakened. +- [ ] Related unit and contract-integrity tests are aligned and pass. +- [ ] Focused execution used the guarded orchestrator and complied with the + canonical retry policy. diff --git a/.agents/skills/reviewed-development/IMPLEMENTATION.md b/.agents/skills/reviewed-development/IMPLEMENTATION.md new file mode 100644 index 00000000000..81237943c11 --- /dev/null +++ b/.agents/skills/reviewed-development/IMPLEMENTATION.md @@ -0,0 +1,139 @@ +# Implementation Mode + +Implement the requested change completely, validate it in proportion to risk, +and subject the current result to repeated independent review before delivery. + +## 1. Confirm the implementation contract + +Read the accepted plan, current user request, relevant repository instructions, +and current branch state. Establish: + +- Objective, scope, non-goals, and definition of done. +- Files and systems expected to change. +- Tests, rollout, documentation, and manual acceptance required. +- Whether commit, push, branch, or PR operations were explicitly authorized. +- Existing user changes that must be preserved. + +If the plan has become stale, verify the current repository and adapt it +carefully. Escalate only decisions that materially change product behavior, +risk, or destructive outcomes. + +## 2. Re-ground in current code + +Inspect the implementation paths and nearest established patterns before +editing. Do not rely solely on summaries or the plan's file list. Check for +changes made since planning, hidden coupling, generated artifacts, migration +rules, and repository-specific validation commands. + +Create a concise execution checklist for substantial work. Implement in +coherent phases, keeping only one phase actively changing the repository at a +time. + +## 3. Implement the smallest complete solution + +- Follow existing architecture, naming, contracts, and ownership boundaries. +- Address root causes instead of suppressing failures or adding broad + workarounds. +- Keep security, authorization, data integrity, secret handling, and cleanup + fail-closed. +- Preserve backward compatibility and safe rollout ordering where versions can + overlap. +- Avoid unrelated cleanup, speculative abstraction, duplicate helpers, and + infrastructure without a demonstrated need. +- Update tests and documentation alongside the behavior they protect. + +Do not weaken invariants or tests merely to obtain a passing result. + +## 4. Verify continuously + +After each meaningful phase, run the cheapest relevant proof. Before review, +run the complete validation justified by the change, such as: + +- Focused unit and integration tests. +- Type checking, linting, formatting, generated-file, and boundary checks. +- Migration, deployment, or compatibility verification. +- Browser or end-to-end tests through the project's supported orchestrator. +- Manual checks that cannot be automated. + +Read failures as evidence. Fix introduced problems, distinguish unrelated +baseline failures explicitly, and do not report a check as passed unless it +actually ran successfully. + +## 5. Self-review the current diff + +Before commissioning reviewers: + +- Compare the implementation with every plan requirement. +- Inspect the full diff, including generated and staged files. +- Check edge cases, negative paths, concurrency, partial failure, and cleanup. +- Confirm tests prove observable behavior rather than implementation details. +- Remove accidental duplication, debugging output, dead code, and unnecessary + complexity. +- Verify no credentials, local artifacts, or unrelated user changes entered the + diff. + +## 6. Commission independent reviews + +Generate two complete, neutral prompts from the current task, plan, repository, +implementation, diff, and verification evidence according to the +independent-review protocol. Launch two fresh reviewers concurrently when +possible. + +Do not ask reviewers to validate the parent's preferred approach. Ask for their +genuine assessment of correctness, security, pattern fit, test quality, +maintainability, redundancy, overengineering, operational safety, and complete +coverage of the user's objective. + +Reviewers must inspect the repository directly and remain read-only. + +## 7. Triage, fix, and re-verify + +Independently verify every finding: + +- Fix valid issues with the smallest clean solution that matches repository + patterns. +- Push back on false positives or harmful suggestions with concrete evidence. +- Resolve contradictions by examining code and requirements, not by choosing + the more confident reviewer. +- Re-run all checks affected by substantive changes. +- Resume each reviewer separately with the current implementation and the + response to its findings. Continue until it accepts the resolution or the + protocol's unresolved-disagreement stop condition applies. + +Do not tell the other reviewer or reviewers in later cycles what an earlier +agent found. + +## 8. Re-review the current implementation + +Generate fresh prompts describing only the current objective, code, diff, and +evidence. Begin another independent review cycle with an entirely new pair. +Never reuse reviewers from an earlier cycle. + +If this review causes substantive edits, reviewers disagree materially, or +meaningful uncertainty remains, run one additional fresh pair after +re-verification, subject to the protocol's three-cycle cap. The final accepted +review must apply to the implementation being delivered. + +## 9. Delivery gate + +Do not declare completion until: + +- The implementation satisfies the current plan and definition of done. +- Relevant automated checks pass, with manual-only checks identified. +- No Critical, High, or Medium concern remains unresolved without an explicit, + evidence-backed decision. +- The final substantive revision has been independently reviewed. +- The diff is scoped, clean, and free of secrets or temporary artifacts. + +Commit, push, or create a PR only if the user's request authorized those +mutations. Follow repository-specific git and PR procedures rather than +inventing a generic release flow. + +Report concisely: + +- What changed and why. +- Verification performed and outcomes. +- Material issues found and corrected. +- Evidence-backed pushbacks. +- Remaining manual actions or known limitations. +- Commit, branch, or PR details when delivery was authorized. diff --git a/.agents/skills/reviewed-development/INDEPENDENT_REVIEW.md b/.agents/skills/reviewed-development/INDEPENDENT_REVIEW.md new file mode 100644 index 00000000000..632df885244 --- /dev/null +++ b/.agents/skills/reviewed-development/INDEPENDENT_REVIEW.md @@ -0,0 +1,133 @@ +# Independent Review Protocol + +This protocol defines how the working model commissions independent reviews. It +is not a reviewer prompt template. + +Every reviewer in this protocol is a separate subagent launched by the working +model, not a perspective role-played in the parent conversation. In Cursor, use +the `Subagent` tool. A fresh reviewer means a new subagent invocation and agent +ID; a follow-up within the same review cycle resumes that subagent's existing +conversation. + +## Generate prompts from the current task + +Generate each reviewer prompt after inspecting the current repository and +artifact. The prompt must be self-contained because a fresh reviewer has no +access to the parent conversation. + +Include the context that materially affects the review: + +- A concise overview of the relevant codebase and architecture. +- The user's actual objective, constraints, non-goals, and definition of done. +- The current plan, diff, branch, or exact files to inspect. +- Established local patterns and sources of truth the work should follow. +- Important data, security, migration, deployment, or compatibility boundaries. +- Verification already performed, with exact commands and outcomes when useful. +- The areas changed and the behavior they are intended to provide. + +Generate the prompt for the task at hand. Do not copy a fixed generic prompt or +fill a mechanical template with shallow substitutions. + +## Keep every review independent + +- Begin each independent review cycle with two fresh reviewers, preferably from + different model families with strong reasoning capability. Honor models + requested by the user. +- During that cycle, resume each reviewer separately to resolve its findings. + Never reuse either conversation in a later independent review cycle. +- Do not tell a reviewer whether it belongs to the first, second, final, or any + other review pass. +- The initial prompt in a cycle must not include findings, verdicts, responses, + or changes from earlier cycles. +- Do not tell reviewers what conclusion to reach or imply that defects must + exist. Acceptance is a valid result. +- Use the same generated prompt for both reviewers by default. Differ only when + a model requires different technical context or tooling instructions, while + keeping both reviews broad and neutral. +- Ask reviewers to inspect repository evidence directly rather than trusting the + implementation summary. +- Keep reviewers read-only unless the user explicitly requested a separate + implementation attempt. + +## Ask for a genuine broad review + +Ask each reviewer to judge the artifact as a whole and report anything it +genuinely dislikes. The review should consider, where relevant: + +- Correctness, requirement coverage, and edge cases. +- Security, privacy, data integrity, authorization, and secret handling. +- Concurrency, failure recovery, cleanup, and partial-success behavior. +- Compatibility with existing architecture, conventions, and nearby patterns. +- Missing, brittle, redundant, or misleading tests. +- Unnecessary complexity, duplicated logic, speculative abstraction, and + overengineering. +- Performance, operational, migration, rollout, and CI consequences. +- Documentation and manual steps required for safe ownership after merge. + +This is a review surface, not a quota. Explicitly tell reviewers that acceptance, +many findings, or anything between are valid outcomes. They must not optimize +for finding count or manufacture criticism. + +## Severity and response format + +Require an explicit verdict and order concrete findings by severity: + +- **Critical**: credible security compromise, data loss, destructive rollout, + or another issue that makes proceeding unsafe. +- **High**: likely correctness failure, unmet core requirement, serious + architectural mismatch, or major operational risk. +- **Medium**: material robustness, maintainability, test, performance, or + pattern-consistency issue that should be resolved. +- **Small**: non-blocking polish or a narrowly scoped improvement. + +Each finding must include: + +- Concrete repository evidence, preferably a file and line or exact behavior. +- Why it matters and when it can fail. +- The smallest appropriate correction or decision needed. + +If no substantive defect exists, the reviewer should say that plainly rather +than manufacture criticism. + +## Triage without deference + +The working model owns the result. For every finding: + +1. Verify it against the repository and the user's objective. +2. Fix it when it is correct and within scope. +3. Push back when it is incorrect, conflicts with requirements, or would add + unjustified complexity; retain the evidence for the final summary. +4. Re-run affected verification after substantive changes. +5. Resume that reviewer with the current artifact and an evidence-based account + of what changed or why the finding was rejected. + +Handle each reviewer independently; do not use one reviewer's opinion to steer +the other. Continue the exchange until the reviewer accepts the resolution or +has no substantive concern. Ask it to re-inspect the current artifact rather +than merely approve the parent's explanation. + +Agreement does not mean obeying the reviewer. A valid endpoint may be a fix or +an evidence-backed pushback that the reviewer accepts. Follow-ups within a +review cycle are not capped: continue the separate exchange with each reviewer +until the concern is resolved. The three-cycle cap limits newly created reviewer +pairs, not follow-up messages in an active cycle. + +Do not forward this cycle's triage history to reviewers in later cycles. Their +initial prompts describe only the current artifact and current evidence. + +## Review completion + +Run two independent review cycles by default. Each cycle begins with exactly two +newly created reviewers, and each reviewer reaches resolution through the +follow-up process above. Track cycle count only in the working session; never +expose it in reviewer prompts. + +Run one additional cycle only when the later cycle causes substantive changes, +reviewers disagree on a material issue, or meaningful uncertainty remains. +Never exceed three cycles or six fresh reviewers for one planning or +implementation task. + +After the third cycle, stop even if a reviewer still disagrees. Report the +unresolved concern, evidence, attempted resolution, and available decision to +the user rather than launching more reviewers. The final reviewed artifact must +be the artifact being presented or shipped, not an earlier revision. diff --git a/.agents/skills/reviewed-development/PLANNING.md b/.agents/skills/reviewed-development/PLANNING.md new file mode 100644 index 00000000000..22f6d1fc443 --- /dev/null +++ b/.agents/skills/reviewed-development/PLANNING.md @@ -0,0 +1,118 @@ +# Planning Mode + +Create a plan that is grounded in the current repository, independently +challenged, and ready for another agent or engineer to implement. End with the +reviewed plan; do not implement product changes. + +## 1. Establish the real objective + +Derive or confirm: + +- The user-visible or operational outcome. +- Scope, non-goals, constraints, and compatibility requirements. +- Definition of done, including executable proof and any manual acceptance. +- Delivery boundaries such as migrations, rollout order, CI, documentation, + deployment configuration, or branch strategy. +- Decisions that genuinely belong to the user. + +Do not mechanically accept assumptions in the request. Verify claims where the +repository can answer them, and ask only for choices that materially alter the +result. + +## 2. Explore before designing + +Inspect enough of the repository to understand: + +- Current architecture and data flow. +- Canonical sources of truth and nearby implementations. +- Existing tests, fixtures, validation, observability, and deployment patterns. +- Historical compatibility surfaces that cannot be changed atomically. +- Work already present on the branch and unrelated changes that must remain + untouched. + +Use parallel exploration when areas are independent. The parent model remains +responsible for synthesis; do not delegate the entire planning task to one +subagent. + +## 3. Draft an implementation-ready plan + +Write the plan for the actual task rather than forcing a universal template. +Include the following when relevant: + +- Goal and definition of done. +- Current behavior and evidence. +- Technical direction and why it fits existing patterns. +- Concrete files, modules, interfaces, and data flows to change. +- Sequenced implementation phases and dependencies. +- Authorization, security, privacy, secret, and trust boundaries. +- Schema, migration, compatibility, rollout, and rollback considerations. +- Test strategy at unit, integration, browser, CI, and manual levels. +- Diagnostics and cleanup behavior. +- Documentation and ownership updates. +- Explicit non-goals, assumptions, risks, and unresolved user decisions. + +Prefer the smallest complete design. Avoid speculative frameworks, duplicated +sources of truth, compatibility layers without a consumer, and cleanup unrelated +to the objective. + +## 4. Commission independent reviews + +Generate self-contained prompts from the current repository and plan according +to the independent-review protocol. Launch two fresh reviewers concurrently +when possible. Do not assign one a desired conclusion or narrow each reviewer +to a concern chosen by the parent; both should be free to assess the whole plan, and should be asked for their genuine opinion, whether this is that everything is perfect, we 're almost there, or everything is terrible and the direction should be completely changed. + +Ask reviewers to verify the proposed design against real code and identify +mistakes, missing requirements, pattern mismatches, unsafe assumptions, +overengineering, redundancy, weak proof, and anything else they genuinely +dislike. + +## 5. Triage and refine + +For each finding: + +- Confirm the evidence yourself. +- Improve the plan when the concern is valid. +- Push back with concrete technical reasoning when it is not. +- Ask the user only when the finding exposes a real product or risk decision + that the repository cannot resolve. +- Resume each reviewer separately with the revised plan and the response to its + findings. Continue until it accepts the resolution or the protocol's + unresolved-disagreement stop condition applies. + +Preserve useful dissent in the working notes, but do not bias later reviewers +with earlier opinions or describe the plan as already corrected. + +## 6. Re-review the current plan + +After refinement, generate new neutral context from the current repository and +current plan. Begin another independent review cycle with an entirely new pair. +Never reuse reviewers from an earlier cycle. + +If this review leads to substantive plan changes, material disagreement, or +unresolved uncertainty, repeat once with another fresh pair, subject to the +protocol's three-cycle cap. Each reviewer sees only the present task and +artifact, never the review sequence. + +## 7. Finalize without implementing + +Before presenting or saving the plan, ensure: + +- Every core requirement maps to a concrete change and proof. +- File and architecture references match the current repository. +- Migration and rollout ordering is safe. +- Tests cover allowed, denied, failure, and cleanup behavior where applicable. +- No Critical, High, or Medium concern remains unresolved without an explicit, + evidence-backed decision. +- The final substantive revision has been independently reviewed. + +Save the plan only where the user or project convention requires. Report: + +- The finalized plan location or plan itself. +- Important decisions and tradeoffs. +- Material reviewer-driven changes. +- Evidence-backed pushbacks. +- User-owned or manual steps that remain. + +Stop there. Implementation requires a separate request using this skill's +implementation mode. diff --git a/.agents/skills/reviewed-development/SKILL.md b/.agents/skills/reviewed-development/SKILL.md new file mode 100644 index 00000000000..f2bbae3775e --- /dev/null +++ b/.agents/skills/reviewed-development/SKILL.md @@ -0,0 +1,33 @@ +--- +name: reviewed-development +description: Plan or implement substantial engineering work with repeated independent reviews by fresh agents. Use when the user requests evidence-based planning or careful end-to-end implementation with unbiased multi-model review before delivery. +argument-hint: " [task, plan path, or delivery instructions]" +disable-model-invocation: true +--- + +# Reviewed Development + +Run a substantial planning or implementation task with repository-grounded, +independent review. + +## Select the mode + +Infer the mode from the user's request or the first argument: + +- **Plan**: design and refine an implementation plan without changing product + code. Read `.agents/skills/reviewed-development/PLANNING.md`. +- **Implement**: execute an approved plan or concrete task, verify it, and + prepare the authorized delivery. Read + `.agents/skills/reviewed-development/IMPLEMENTATION.md`. + +If the request genuinely does not reveal whether the user wants a plan or code, +ask before proceeding. Never let planning silently become implementation. + +For either mode, first read +`.agents/skills/reviewed-development/INDEPENDENT_REVIEW.md`. It defines how the +working model must dynamically generate neutral prompts for fresh reviewers. It +is a protocol, not a fixed reviewer prompt. + +The parent model remains responsible for exploration, decisions, triage, +verification, and the final result. Reviewers advise; they do not replace the +working model's judgment. diff --git a/.claude/commands/add-settings-e2e-test.md b/.claude/commands/add-settings-e2e-test.md new file mode 100644 index 00000000000..6dcd41ed96c --- /dev/null +++ b/.claude/commands/add-settings-e2e-test.md @@ -0,0 +1,178 @@ +--- +description: Add or update durable Playwright coverage for Sim settings using the correct literal contract or owning spec, existing personas, guarded orchestration, semantic assertions, cleanup, and secret-safe diagnostics. Use when asked to add a settings browser test, cover a settings regression, or update settings E2E acceptance behavior. +argument-hint: +--- + +# Add Settings E2E Test + +Add the smallest durable browser proof for the requested observable behavior. +Do not begin by copying a nearby test: first identify which acceptance dataset +or spec owns the behavior. + +## Read first + +1. `apps/sim/e2e/MAINTENANCE.md` — change type to contract/spec ownership. +2. `apps/sim/e2e/README.md` — orchestrator, project boundaries, focused commands, + diagnostics, acceptance and security boundaries, and CI policy. + +Both files are canonical. This skill is the authoring procedure, not a second +copy of their inventories or commands. If this procedure ever conflicts with a +canonical document, the canonical document wins. + +## Procedure + +### 1. Define the observable contract + +Write down: + +- the user persona and resource plane (account, organization, or workspace); +- the action or direct URL; +- the exact observable result: path, semantic readiness, visible/hidden item, + access outcome, enabled/disabled control, warning, or persisted state; +- whether product behavior is intentionally changing or an implementation + change must preserve the existing contract. + +Intended behavior changes update product code and literal acceptance +expectations together. Behavior-preserving fixes add or strengthen proof without +rewriting unrelated expectations. + +### 2. Choose the owner before writing code + +- **Canonical section, route, copy, readiness, or persona visibility:** update + `apps/sim/e2e/settings/navigation/contracts.ts`; its specs generate cases from + the literal dataset, including unauthenticated redirect cases. +- **Browser Back, app Back, direct-entry fallback, or return destination:** edit + `apps/sim/e2e/settings/navigation/history.spec.ts` directly. +- **Direct access, plan/role/entitlement gate, or mutation availability:** update + `apps/sim/e2e/settings/authorization/contracts.ts`; its access and mutation + specs generate cases from that dataset. + - If an existing navigation case already owns the sidebar proof, add or reuse + an `existingNavigationProofs` entry and set + `sidebar.existingProofId`. Do not execute the same sidebar assertion again + in the authorization case. +- **Unsaved-change behavior:** edit + `apps/sim/e2e/settings/authorization/unsaved-changes.spec.ts` directly. +- **Secrets or API key lifecycle:** edit the owning credential spec directly. + Change `apps/sim/e2e/settings/credentials/contracts.ts` only when reused + authorization proof IDs or boundaries change. +- **People, access control, SSO, data retention, or MCP lifecycle:** edit the + owning workflow spec. Change `apps/sim/e2e/settings/workflows/contracts.ts` + when stable lifecycle or cross-contract proof references change. +- **Persona or seeded relationship:** update + `apps/sim/e2e/settings/personas.ts` and the relevant + scenario/factory/integrity proof. + +The categories are non-exclusive. Prefer extending an existing literal row or +owning workflow over adding a duplicate standalone test. + +### 3. Use the owning fixture wrapper + +- Browser-touching settings specs must not import runtime `test` or `expect` + from `@playwright/test`. Type-only imports such as `Page` or `Response` are + allowed. +- Navigation, authorization, persona-contract, and persona-isolation browser + specs use `apps/sim/e2e/fixtures/persona-test.ts`. +- Credential specs use the local `credential-test.ts`; workflow specs use the + local `workflow-test.ts`; authenticated or unauthenticated smoke and + browser-touching harness-level specs use + `apps/sim/e2e/fixtures/browser-test.ts`. +- Pure dataset-only contract-integrity specs and non-browser foundation policy + specs may use the Playwright test runner directly. If they begin creating a + browser, context, or page, move them to the owning wrapper first. +- Persona-based tests must create contexts through `contextForPersona` or an + owning helper that calls it. The inherited `page` and `context` fixtures on + `persona-test` are not persona-authenticated or network-guarded. +- `browser-test` guards its default context. Every manual + `browser.newContext()` must install `installBrowserNetworkGuard()` immediately, + then close the context and assert the guard even when the test fails; follow + the aggregate-cleanup pattern in authenticated smoke coverage. Do not use + `browser.newPage()`: it creates an implicit unguarded context; create and guard + an explicit context, then call `context.newPage()`. +- Reuse the owning directory's helpers. Credential tests use their local + `newPersonaPage` helper so the page is registered for failure sanitization. + Wrapper selection alone does not install every context, cleanup, attachment, + redaction, or artifact safeguard; bypassing the guarded fixture/helper path is + a test-safety bug. + +### 4. Reuse the existing world + +- Reuse an existing persona and seeded resource when it expresses the required + role, plan, entitlement, and ownership boundary. +- Add a persona or scenario edge only when no existing driver can prove the + contract without changing its meaning. +- Use run-namespaced factories and unique resources. Do not introduce shared + mutable fixture state. +- Keep sensitive fixture values browser-resident through the existing helpers. + The Playwright process must not receive database credentials, admin keys, + persona passwords, or plaintext values that the owning spec keeps in-page. + +### 5. Assert semantics, not implementation details + +- Use accessible roles, labels, names, and visible text. +- Assert exact paths and user-facing contract copy when those are the behavior + under test. +- Wait on semantic readiness, relevant same-origin responses, or explicit + authorization state—not arbitrary sleeps or CSS classes. +- For access controls, prove direct URL behavior as well as sidebar visibility. +- Do not import production navigation or authorization implementations to + generate expected values. + +### 6. Make mutations reversible + +- Register cleanup before the first mutation. +- Create uniquely named resources and restore the exact captured baseline. +- Use the production UI and same-origin APIs for behavior under test; use a + trusted database probe only where the existing suite defines one. +- Keep cleanup LIFO and safe after partial failure. Never weaken fixture + invariants merely to make a test pass. + +### 7. Respect external and diagnostic boundaries + +- Use the existing strict loopback Stripe and MCP fakes, the mail mock-success + path with provider credentials absent, and the other reviewed test + boundaries. An unexpected external request must fail rather than silently + egress. +- Credentials, tokens, certificates, verification values, storage state, and + sensitive response bodies may be used only through existing reviewed paths; + never log, attach, or retain them in diagnostics. +- The SSO workflow's public-certificate browser input is one reviewed example; + preserve its input clearing and diagnostic suppression. +- Preserve the owning spec's trace, screenshot, video, and attachment policy. + New suppression requires a security reason and corresponding safety coverage. + +### 8. Keep unit and integrity proof aligned + +- Update affected production unit tests for navigation, route gates, + authorization, billing, or business rules. +- Update the owning `contract-integrity.spec.ts` when adding IDs, references, or + a new contract axis. +- Preserve stable proof IDs; do not rerun a browser case merely because another + contract can reference its existing proof. + +### 9. Verify through the orchestrator + +- Run the affected unit and contract-integrity tests. +- Run the focused canonical project and path documented in + `apps/sim/e2e/README.md`. +- Invoke only `bun run test:e2e`; never invoke raw `playwright test`. +- Use `--reuse-build` and a single explicit project with `--no-deps` for local + iteration when the README permits it. +- Follow the retry policy in the canonical README and Playwright config. It is + currently zero; do not enable retries or change worker policy in a feature + test. Do not use `test.only`, unexplained skips, arbitrary browser sleeps, or + test-local environment bypasses. +- After focused proof passes, rely on the required complete CI suite. Run a + repeated stability gate only when the change's scope or acceptance plan calls + for one. + +## Completion checklist + +- [ ] Every observable facet has one clear owner, and all applicable owners were + updated. +- [ ] Allowed and denied outcomes exist where the feature is gated. +- [ ] Locators and readiness assertions are semantic and accessible. +- [ ] Mutations register cleanup first and restore the exact baseline. +- [ ] No secret or external-egress boundary was weakened. +- [ ] Related unit and contract-integrity tests are aligned and pass. +- [ ] Focused execution used the guarded orchestrator and complied with the + canonical retry policy. diff --git a/.claude/commands/reviewed-development.md b/.claude/commands/reviewed-development.md new file mode 100644 index 00000000000..78c6d2ec0f4 --- /dev/null +++ b/.claude/commands/reviewed-development.md @@ -0,0 +1,31 @@ +--- +description: Plan or implement substantial engineering work with repeated independent reviews by fresh agents. Use when the user requests evidence-based planning or careful end-to-end implementation with unbiased multi-model review before delivery. +argument-hint: " [task, plan path, or delivery instructions]" +--- + +# Reviewed Development + +Run a substantial planning or implementation task with repository-grounded, +independent review. + +## Select the mode + +Infer the mode from the user's request or the first argument: + +- **Plan**: design and refine an implementation plan without changing product + code. Read `.agents/skills/reviewed-development/PLANNING.md`. +- **Implement**: execute an approved plan or concrete task, verify it, and + prepare the authorized delivery. Read + `.agents/skills/reviewed-development/IMPLEMENTATION.md`. + +If the request genuinely does not reveal whether the user wants a plan or code, +ask before proceeding. Never let planning silently become implementation. + +For either mode, first read +`.agents/skills/reviewed-development/INDEPENDENT_REVIEW.md`. It defines how the +working model must dynamically generate neutral prompts for fresh reviewers. It +is a protocol, not a fixed reviewer prompt. + +The parent model remains responsible for exploration, decisions, triage, +verification, and the final result. Reviewers advise; they do not replace the +working model's judgment. diff --git a/.claude/rules/sim-settings-e2e.md b/.claude/rules/sim-settings-e2e.md new file mode 100644 index 00000000000..55925020834 --- /dev/null +++ b/.claude/rules/sim-settings-e2e.md @@ -0,0 +1,17 @@ +--- +paths: + - "apps/sim/**" + - "packages/platform-authz/src/workspace.ts" + - "packages/platform-authz/src/predicates.ts" +--- + +# Settings E2E Maintenance + +When a change can affect observable settings routes, copy, visibility, +authorization, entitlements, mutations, or persisted workflows, read +`apps/sim/e2e/MAINTENANCE.md` before finishing and assess every applicable +literal Playwright contract. + +Keep acceptance expectations independent of production navigation and +authorization implementations. Behavior-preserving refactors need focused +verification, not artificial contract edits. diff --git a/.claude/rules/sim-settings-pages.md b/.claude/rules/sim-settings-pages.md index 154b45a2599..a160ed2d19c 100644 --- a/.claude/rules/sim-settings-pages.md +++ b/.claude/rules/sim-settings-pages.md @@ -1,20 +1,29 @@ --- paths: + - "apps/sim/app/account/settings/**" + - "apps/sim/app/organization/*/settings/**" - "apps/sim/app/workspace/*/settings/**" + - "apps/sim/components/settings/**" - "apps/sim/ee/**/components/**" --- # Settings Pages -The Next.js `settings/[section]/layout.tsx` owns all settings page chrome via -`SettingsHeaderShell` — a fixed header bar (a left back chip + right-aligned -action chips), a scroll region, and a centered `max-w-[48rem]` content column led -by a **title + description from navigation metadata**. The chrome stays mounted -across section navigation (it never re-renders or re-lays-out). Each section -renders through the **`SettingsPanel`** registrar -(`@/app/workspace/[workspaceId]/settings/components/settings-panel`), which feeds -the shell its header data and renders only the section body. Sections supply -**data**, never chrome. +All three settings planes share `SettingsHeaderShell` and the canonical +`SettingsPanel` implementation in `@/components/settings/settings-panel`, but +their chrome is mounted at different boundaries: + +- Account and organization `settings/layout.tsx` mount + `StandaloneSettingsShell`, which owns their sidebar, header shell, section + provider, and unload guard. +- Workspace `settings/[section]/layout.tsx` mounts the persistent header shell; + the outer workspace `settings/layout.tsx` owns its unload guard. + +The active shell owns the fixed header bar (a left back chip + right-aligned +action chips), scroll region, and centered `max-w-[48rem]` content column led by +a **title + description from the shared settings registry**. Each section +renders only its body through `SettingsPanel`; sections supply **data**, never +chrome. Do NOT hand-roll any of these in a settings page — they are owned by the layout shell (fed through `SettingsPanel`): @@ -29,6 +38,8 @@ shell (fed through `SettingsPanel`): ## Canonical page shape ```tsx +// Established section-component import; this compatibility barrel re-exports +// the canonical @/components/settings/settings-panel implementation. import { SettingsPanel } from '@/app/workspace/[workspaceId]/settings/components/settings-panel' return ( @@ -80,21 +91,29 @@ return ( - `scrollContainerRef?: React.Ref` — forwards a ref to the scroll region (e.g. programmatic scroll-to-bottom). -## Title + description live in navigation metadata +## Title + description live in the shared settings registry -`apps/sim/app/workspace/[workspaceId]/settings/navigation.ts` is the single source -of truth. Every `NavigationItem` carries a one-line `description`; `SettingsPanel` -resolves both via `getSettingsSectionMeta(section)` and the -`SettingsSectionProvider` the settings shell wraps around the active section. +`apps/sim/components/settings/navigation.ts` is the single source of truth. +Each `SettingsSectionRegistryEntry` owns a `unified` projection with the default +description and gating metadata plus optional account, organization, and +workspace plane projections. Account and organization shells provide `plane` +and `section`, so the shared `SettingsPanel` resolves metadata with +`getSettingsSectionMeta(plane, section)`. The workspace adapter at +`app/workspace/[workspaceId]/settings/navigation.ts` resolves the mandatory +unified projection with its one-argument `getSettingsSectionMeta(section)` and +the workspace renderer passes that metadata to `SettingsSectionProvider`. Adding a new settings page: -1. Add the `SettingsSection` id + a `NavigationItem` (with `label` **and** - `description`) in `navigation.ts`. Keep descriptions verb-first, one line, - ~40–55 chars, in the product voice (see `.claude/rules/constitution.md`). -2. Render the component inside the shell's `effectiveSection` switch in - `settings/[section]/settings.tsx`. -3. Build the component body inside `` — no shell, no title block. +Follow the procedure in `.claude/skills/add-settings-page/SKILL.md`. The +non-negotiable architecture invariants are: + +- Every registry entry has a mandatory unified projection, workspace renderer, + and server-side direct-route outcome. +- Every optional account, organization, or workspace plane projection has its + corresponding renderer and access/feature gate. +- The component body uses `SettingsPanel`; associated unit tests and every + applicable literal browser contract change in the same PR. ## Text-scale tokens (no literal pixel sizes) @@ -187,8 +206,9 @@ changes" modal: (from `@/app/workspace/[workspaceId]/components/credential-detail`). The in-view header **Discard** chip (via `SaveDiscardActions onDiscard`) is a *reset to original* — distinct from the back-confirm's discard, which leaves. -- **`useSettingsBeforeUnload`** is mounted **once** in the settings shell - (`settings/[section]/settings.tsx`) — never add a per-page `beforeunload`. +- **`useSettingsBeforeUnload`** is mounted once per active shell boundary: + workspace `settings/layout.tsx` and `StandaloneSettingsShell` for account or + organization. Never add a per-page `beforeunload`. - **Dirty *computation* stays local** (shapes differ: field-compare vs normalize+stringify) — only how dirty is *consumed* is shared. Derive it (a `const`/`useMemo`), never store it in `useState`. @@ -218,9 +238,9 @@ exception — it lives outside `[section]` and keeps its own `CredentialDetailLa A settings page is design-system-clean when: - [ ] Its main return is a `` (or `<>……` with modal siblings) — no hand-rolled shell/header/scroll/column. -- [ ] It renders **no** hand-rolled `

`/description title block — the title comes from nav metadata. +- [ ] It renders **no** hand-rolled `

`/description title block — the title comes from the shared settings registry. - [ ] Header chips are in `actions`; a standalone search is in the `search` prop. -- [ ] Its `NavigationItem` has an accurate, consistent-length `description`. +- [ ] Its `SettingsSectionRegistryEntry` has an accurate, consistent-length `unified.description` and only the plane projections it supports. - [ ] Detail sub-views and entitlement/loading gates keep their own chrome (intentional). - [ ] If it has editable state: Save/Discard go through `SaveDiscardActions`, dirty is wired via `useSettingsUnsavedGuard` (called before any early-return gate), and there is **no** hand-rolled Save button / `beforeunload` / "Unsaved changes" modal. - [ ] No business logic, handlers, or conditional rendering changed by the migration. diff --git a/.claude/skills/add-settings-page/SKILL.md b/.claude/skills/add-settings-page/SKILL.md index 69173332ad4..408ce9c8389 100644 --- a/.claude/skills/add-settings-page/SKILL.md +++ b/.claude/skills/add-settings-page/SKILL.md @@ -5,31 +5,62 @@ description: Add a new Sim settings page, or audit existing settings pages for d # Settings Page (add / audit) -Sim settings pages all render through the shared **`SettingsPanel`** primitive, -which owns the page chrome and renders a nav-driven title + description. The full -convention lives in `.claude/rules/sim-settings-pages.md` — read it first; this -skill is the procedure. +Sim settings pages render their bodies through the shared **`SettingsPanel`** +registrar, which publishes header metadata and actions to the active +`SettingsHeaderShell`. The shell owns and renders the page chrome, including the +registry-driven title and description. The full convention lives in +`.claude/rules/sim-settings-pages.md` — read it first; this skill is the +procedure. Key paths: -- Layout primitive: `apps/sim/app/workspace/[workspaceId]/settings/components/settings-panel/settings-panel.tsx` -- Nav metadata (titles + descriptions): `apps/sim/app/workspace/[workspaceId]/settings/navigation.ts` -- Section switch + provider: `apps/sim/app/workspace/[workspaceId]/settings/[section]/settings.tsx` +- Panel registrar implementation: `apps/sim/components/settings/settings-panel.tsx` +- Established section-component import: `@/app/workspace/[workspaceId]/settings/components/settings-panel` (compatibility barrel) +- Section types + registry: `apps/sim/components/settings/navigation.ts` +- Account renderer: `apps/sim/components/settings/account-settings-renderer.tsx` +- Account route gate: `apps/sim/app/account/settings/[section]/page.tsx` +- Organization renderer: `apps/sim/components/settings/organization-settings-renderer.tsx` +- Organization route gate: `apps/sim/app/organization/[organizationId]/settings/[section]/page.tsx` +- Workspace metadata adapter: `apps/sim/app/workspace/[workspaceId]/settings/navigation.ts` +- Workspace renderer + provider: `apps/sim/app/workspace/[workspaceId]/settings/[section]/settings.tsx` +- Workspace route gate: `apps/sim/app/workspace/[workspaceId]/settings/[section]/page.tsx` - Pages: `apps/sim/app/workspace/[workspaceId]/settings/components//.tsx` and EE pages under `apps/sim/ee//components/` +- Browser acceptance maintenance: `apps/sim/e2e/MAINTENANCE.md` ## Mode A — Add a new settings page -1. **Navigation.** In `navigation.ts`: add the id to the `SettingsSection` union, - then a `NavigationItem` with `label` AND a one-line `description` (verb-first, - ~40–55 chars, product voice per `.claude/rules/constitution.md`). Place it in - the right `section` group and set any gating flags (`requiresHosted`, - `requiresEnterprise`, etc.). -2. **Wire the switch.** Add the component to the `effectiveSection` render switch - in `settings/[section]/settings.tsx` (lazy `dynamic(...)` like its siblings). -3. **Build the body inside `SettingsPanel`.** Never hand-roll the shell, header +1. **Registry.** In `apps/sim/components/settings/navigation.ts`, add the id to + `UnifiedSettingsSection` and add a `SettingsSectionRegistryEntry` to + `SETTINGS_SECTION_REGISTRY`. Its mandatory `unified` projection owns the + default description and workspace gating flags (`requiresHosted`, + `requiresEnterprise`, etc.). For each plane-specific projection the page + needs, also add the id to `AccountSettingsSection`, + `OrganizationSettingsSection`, or `WorkspaceSettingsSection` and add that + optional projection. Use plane-specific copy only when its scope genuinely + differs. Keep descriptions verb-first, one line, ~40–55 chars, in the product + voice (see `.claude/rules/constitution.md`). +2. **Always wire the unified workspace surface.** Every registry entry + participates in unified workspace navigation before its gates are applied, + whether or not it has `planes.workspace`. Render the `unified.id` in the + workspace `settings/[section]/settings.tsx` switch so an allowed route cannot + resolve to a blank page. +3. **Wire optional standalone planes.** If the entry declares `planes.account` + or `planes.organization`, add the component to + `account-settings-renderer.tsx` or `organization-settings-renderer.tsx`. + Never declare a standalone projection whose renderer cannot render it. +4. **Preserve every route gate.** In the workspace + `settings/[section]/page.tsx`, classify the unified section in + `WORKSPACE_SECTION_MAP` or `ORGANIZATION_SECTION_MAP` when it belongs to + either access plane; otherwise add an explicit direct gate when needed or + verify that host-context membership is the intended boundary. Enforce the + section's permission, deployment, plan, and entitlement outcome. For + standalone account or organization projections, also update their + `[section]/page.tsx` gate and the shared organization access/feature helpers + when applicable. Sidebar gating never replaces server authorization. +5. **Build the body inside `SettingsPanel`.** Never hand-roll the shell, header bar, scroll region, content column, or title block. Put header buttons in `actions`, a standalone search in `search={{ value, onChange, placeholder }}`, and the page content as `children`. Modals go beside the panel inside a `<>`. -4. **If the page has editable state**, wire the shared save/discard stack — put +6. **If the page has editable state**, wire the shared save/discard stack — put `SaveDiscardActions` (dirty-gated Discard+Save chips) in `actions`, and call `useSettingsUnsavedGuard({ isDirty })` **before any early-return gate**. Detail sub-views additionally route the back chip through @@ -37,7 +68,16 @@ Key paths: hand-roll a Save button, a `beforeunload`, or an "Unsaved changes" modal — they're centralized. See the "Save / Discard + unsaved-changes guard" section in `.claude/rules/sim-settings-pages.md`. -5. **Verify:** `cd apps/sim && bunx tsc --noEmit`; `bunx biome check --write `. +7. **Update tests and browser contracts.** Update the shared navigation, + workspace route/navigation, and access unit tests affected by the new + projection or gate. Then follow `apps/sim/e2e/MAINTENANCE.md`: intended + observable changes require paired literal Playwright contracts, while + behavior-preserving refactors require focused verification without + expectation churn. +8. **Verify:** From `apps/sim`, run `bunx tsc --noEmit` and + `bunx biome check --write ` on changed source files. Then run the + affected unit tests and the focused orchestrated E2E project from + `e2e/README.md`. ## Mode B — Audit existing settings pages @@ -54,8 +94,10 @@ For each page component, confirm the checklist in `.claude/rules/sim-settings-pa `.claude/rules/sim-settings-pages.md` for the token map and the row title/subtitle pairing convention): `git grep -n "text-\[1[0-8]px\]" -- 'apps/sim/**/settings/' 'apps/sim/ee/'` -4. Confirm each page imports `SettingsPanel` and that its `NavigationItem` has an - accurate `description` of consistent length with its peers. +4. Confirm each page imports `SettingsPanel` and that its + `SettingsSectionRegistryEntry` has an accurate `unified.description` of + consistent length with its peers, plus only the plane projections it + supports. - Editable pages: confirm Save/Discard go through `SaveDiscardActions` and dirty is wired via `useSettingsUnsavedGuard` (called before early-return gates) — flag any hand-rolled Save button, `beforeunload`, or unsaved modal. diff --git a/.cursor/commands/add-settings-e2e-test.md b/.cursor/commands/add-settings-e2e-test.md new file mode 100644 index 00000000000..57f717e644c --- /dev/null +++ b/.cursor/commands/add-settings-e2e-test.md @@ -0,0 +1,173 @@ +# Add Settings E2E Test + +Add the smallest durable browser proof for the requested observable behavior. +Do not begin by copying a nearby test: first identify which acceptance dataset +or spec owns the behavior. + +## Read first + +1. `apps/sim/e2e/MAINTENANCE.md` — change type to contract/spec ownership. +2. `apps/sim/e2e/README.md` — orchestrator, project boundaries, focused commands, + diagnostics, acceptance and security boundaries, and CI policy. + +Both files are canonical. This skill is the authoring procedure, not a second +copy of their inventories or commands. If this procedure ever conflicts with a +canonical document, the canonical document wins. + +## Procedure + +### 1. Define the observable contract + +Write down: + +- the user persona and resource plane (account, organization, or workspace); +- the action or direct URL; +- the exact observable result: path, semantic readiness, visible/hidden item, + access outcome, enabled/disabled control, warning, or persisted state; +- whether product behavior is intentionally changing or an implementation + change must preserve the existing contract. + +Intended behavior changes update product code and literal acceptance +expectations together. Behavior-preserving fixes add or strengthen proof without +rewriting unrelated expectations. + +### 2. Choose the owner before writing code + +- **Canonical section, route, copy, readiness, or persona visibility:** update + `apps/sim/e2e/settings/navigation/contracts.ts`; its specs generate cases from + the literal dataset, including unauthenticated redirect cases. +- **Browser Back, app Back, direct-entry fallback, or return destination:** edit + `apps/sim/e2e/settings/navigation/history.spec.ts` directly. +- **Direct access, plan/role/entitlement gate, or mutation availability:** update + `apps/sim/e2e/settings/authorization/contracts.ts`; its access and mutation + specs generate cases from that dataset. + - If an existing navigation case already owns the sidebar proof, add or reuse + an `existingNavigationProofs` entry and set + `sidebar.existingProofId`. Do not execute the same sidebar assertion again + in the authorization case. +- **Unsaved-change behavior:** edit + `apps/sim/e2e/settings/authorization/unsaved-changes.spec.ts` directly. +- **Secrets or API key lifecycle:** edit the owning credential spec directly. + Change `apps/sim/e2e/settings/credentials/contracts.ts` only when reused + authorization proof IDs or boundaries change. +- **People, access control, SSO, data retention, or MCP lifecycle:** edit the + owning workflow spec. Change `apps/sim/e2e/settings/workflows/contracts.ts` + when stable lifecycle or cross-contract proof references change. +- **Persona or seeded relationship:** update + `apps/sim/e2e/settings/personas.ts` and the relevant + scenario/factory/integrity proof. + +The categories are non-exclusive. Prefer extending an existing literal row or +owning workflow over adding a duplicate standalone test. + +### 3. Use the owning fixture wrapper + +- Browser-touching settings specs must not import runtime `test` or `expect` + from `@playwright/test`. Type-only imports such as `Page` or `Response` are + allowed. +- Navigation, authorization, persona-contract, and persona-isolation browser + specs use `apps/sim/e2e/fixtures/persona-test.ts`. +- Credential specs use the local `credential-test.ts`; workflow specs use the + local `workflow-test.ts`; authenticated or unauthenticated smoke and + browser-touching harness-level specs use + `apps/sim/e2e/fixtures/browser-test.ts`. +- Pure dataset-only contract-integrity specs and non-browser foundation policy + specs may use the Playwright test runner directly. If they begin creating a + browser, context, or page, move them to the owning wrapper first. +- Persona-based tests must create contexts through `contextForPersona` or an + owning helper that calls it. The inherited `page` and `context` fixtures on + `persona-test` are not persona-authenticated or network-guarded. +- `browser-test` guards its default context. Every manual + `browser.newContext()` must install `installBrowserNetworkGuard()` immediately, + then close the context and assert the guard even when the test fails; follow + the aggregate-cleanup pattern in authenticated smoke coverage. Do not use + `browser.newPage()`: it creates an implicit unguarded context; create and guard + an explicit context, then call `context.newPage()`. +- Reuse the owning directory's helpers. Credential tests use their local + `newPersonaPage` helper so the page is registered for failure sanitization. + Wrapper selection alone does not install every context, cleanup, attachment, + redaction, or artifact safeguard; bypassing the guarded fixture/helper path is + a test-safety bug. + +### 4. Reuse the existing world + +- Reuse an existing persona and seeded resource when it expresses the required + role, plan, entitlement, and ownership boundary. +- Add a persona or scenario edge only when no existing driver can prove the + contract without changing its meaning. +- Use run-namespaced factories and unique resources. Do not introduce shared + mutable fixture state. +- Keep sensitive fixture values browser-resident through the existing helpers. + The Playwright process must not receive database credentials, admin keys, + persona passwords, or plaintext values that the owning spec keeps in-page. + +### 5. Assert semantics, not implementation details + +- Use accessible roles, labels, names, and visible text. +- Assert exact paths and user-facing contract copy when those are the behavior + under test. +- Wait on semantic readiness, relevant same-origin responses, or explicit + authorization state—not arbitrary sleeps or CSS classes. +- For access controls, prove direct URL behavior as well as sidebar visibility. +- Do not import production navigation or authorization implementations to + generate expected values. + +### 6. Make mutations reversible + +- Register cleanup before the first mutation. +- Create uniquely named resources and restore the exact captured baseline. +- Use the production UI and same-origin APIs for behavior under test; use a + trusted database probe only where the existing suite defines one. +- Keep cleanup LIFO and safe after partial failure. Never weaken fixture + invariants merely to make a test pass. + +### 7. Respect external and diagnostic boundaries + +- Use the existing strict loopback Stripe and MCP fakes, the mail mock-success + path with provider credentials absent, and the other reviewed test + boundaries. An unexpected external request must fail rather than silently + egress. +- Credentials, tokens, certificates, verification values, storage state, and + sensitive response bodies may be used only through existing reviewed paths; + never log, attach, or retain them in diagnostics. +- The SSO workflow's public-certificate browser input is one reviewed example; + preserve its input clearing and diagnostic suppression. +- Preserve the owning spec's trace, screenshot, video, and attachment policy. + New suppression requires a security reason and corresponding safety coverage. + +### 8. Keep unit and integrity proof aligned + +- Update affected production unit tests for navigation, route gates, + authorization, billing, or business rules. +- Update the owning `contract-integrity.spec.ts` when adding IDs, references, or + a new contract axis. +- Preserve stable proof IDs; do not rerun a browser case merely because another + contract can reference its existing proof. + +### 9. Verify through the orchestrator + +- Run the affected unit and contract-integrity tests. +- Run the focused canonical project and path documented in + `apps/sim/e2e/README.md`. +- Invoke only `bun run test:e2e`; never invoke raw `playwright test`. +- Use `--reuse-build` and a single explicit project with `--no-deps` for local + iteration when the README permits it. +- Follow the retry policy in the canonical README and Playwright config. It is + currently zero; do not enable retries or change worker policy in a feature + test. Do not use `test.only`, unexplained skips, arbitrary browser sleeps, or + test-local environment bypasses. +- After focused proof passes, rely on the required complete CI suite. Run a + repeated stability gate only when the change's scope or acceptance plan calls + for one. + +## Completion checklist + +- [ ] Every observable facet has one clear owner, and all applicable owners were + updated. +- [ ] Allowed and denied outcomes exist where the feature is gated. +- [ ] Locators and readiness assertions are semantic and accessible. +- [ ] Mutations register cleanup first and restore the exact baseline. +- [ ] No secret or external-egress boundary was weakened. +- [ ] Related unit and contract-integrity tests are aligned and pass. +- [ ] Focused execution used the guarded orchestrator and complied with the + canonical retry policy. diff --git a/.cursor/commands/reviewed-development.md b/.cursor/commands/reviewed-development.md new file mode 100644 index 00000000000..eb6ba86101c --- /dev/null +++ b/.cursor/commands/reviewed-development.md @@ -0,0 +1,26 @@ +# Reviewed Development + +Run a substantial planning or implementation task with repository-grounded, +independent review. + +## Select the mode + +Infer the mode from the user's request or the first argument: + +- **Plan**: design and refine an implementation plan without changing product + code. Read `.agents/skills/reviewed-development/PLANNING.md`. +- **Implement**: execute an approved plan or concrete task, verify it, and + prepare the authorized delivery. Read + `.agents/skills/reviewed-development/IMPLEMENTATION.md`. + +If the request genuinely does not reveal whether the user wants a plan or code, +ask before proceeding. Never let planning silently become implementation. + +For either mode, first read +`.agents/skills/reviewed-development/INDEPENDENT_REVIEW.md`. It defines how the +working model must dynamically generate neutral prompts for fresh reviewers. It +is a protocol, not a fixed reviewer prompt. + +The parent model remains responsible for exploration, decisions, triage, +verification, and the final result. Reviewers advise; they do not replace the +working model's judgment. diff --git a/.cursor/rules/sim-settings-e2e.mdc b/.cursor/rules/sim-settings-e2e.mdc new file mode 100644 index 00000000000..7f0a26df953 --- /dev/null +++ b/.cursor/rules/sim-settings-e2e.mdc @@ -0,0 +1,15 @@ +--- +description: Keep settings browser acceptance contracts aligned with observable behavior +globs: ["apps/sim/**", "packages/platform-authz/src/workspace.ts", "packages/platform-authz/src/predicates.ts"] +--- + +# Settings E2E Maintenance + +When a change can affect observable settings routes, copy, visibility, +authorization, entitlements, mutations, or persisted workflows, read +`apps/sim/e2e/MAINTENANCE.md` before finishing and assess every applicable +literal Playwright contract. + +Keep acceptance expectations independent of production navigation and +authorization implementations. Behavior-preserving refactors need focused +verification, not artificial contract edits. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a18541e5751..ffa88c783e7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,11 +20,6 @@ on: branches: [main, staging, dev] pull_request: branches: [main, staging, dev] - # Docs content and markdown don't affect the app build or images; push - # runs stay unfiltered because they feed the deploy pipeline. - paths-ignore: - - 'apps/docs/content/**' - - '**/*.md' concurrency: group: ci-${{ github.ref }} diff --git a/.github/workflows/migrations.yml b/.github/workflows/migrations.yml index ab177ed0c5d..71c1fbc00a7 100644 --- a/.github/workflows/migrations.yml +++ b/.github/workflows/migrations.yml @@ -26,6 +26,7 @@ jobs: name: Apply Database Migrations runs-on: ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-latest' }} timeout-minutes: 45 + environment: ${{ inputs.environment }} steps: - name: Checkout code @@ -62,6 +63,10 @@ jobs: DATABASE_URL: ${{ inputs.environment == 'production' && secrets.DATABASE_URL || inputs.environment == 'staging' && secrets.STAGING_DATABASE_URL || inputs.environment == 'dev' && secrets.DEV_DATABASE_URL || '' }} MIGRATION_DATABASE_URL: ${{ inputs.environment == 'production' && secrets.MIGRATION_DATABASE_URL || inputs.environment == 'staging' && secrets.STAGING_MIGRATION_DATABASE_URL || '' }} ENVIRONMENT: ${{ inputs.environment }} + # One-shot migration 0266 inputs. Configure them independently in + # each protected GitHub environment before its first rollout. + SSO_PROVIDER_WRITES_QUIESCED: ${{ secrets.SSO_PROVIDER_WRITES_QUIESCED }} + SSO_AUDIT_APPROVED_PROVIDER_IDS: ${{ secrets.SSO_AUDIT_APPROVED_PROVIDER_IDS }} run: | if [ -z "$DATABASE_URL" ]; then echo "ERROR: no database URL secret resolved for environment '${ENVIRONMENT}'" >&2 diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml index 8e7ff3e9eab..a7e48d31597 100644 --- a/.github/workflows/test-build.yml +++ b/.github/workflows/test-build.yml @@ -266,3 +266,113 @@ jobs: # check build only — measured 105s cold vs 22s warm locally. NEXT_TURBOPACK_BUILD_CACHE: '1' run: bunx turbo run build --filter=sim + + settings-e2e: + name: Settings E2E + # The production Next build peaks above the free GitHub runner's memory + # ceiling. Keep the break-glass provider predicate aligned with Build App. + runs-on: ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-8vcpu-ubuntu-2404' || 'linux-x64-8-core' }} + timeout-minutes: 60 + + services: + postgres: + image: pgvector/pgvector:pg17 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: postgres + ports: + - 5432:5432 + options: >- + --health-cmd "pg_isready -U postgres" + --health-interval 5s + --health-timeout 5s + --health-retries 10 + + steps: + - name: Checkout code + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Setup Bun + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 + with: + bun-version: 1.3.13 + + - name: Setup Node + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + with: + node-version: 22 + + - name: Mount Bun cache + uses: ./.github/actions/cache-mount + with: + provider: ${{ vars.CI_PROVIDER }} + key: ${{ github.repository }}-bun-cache-${{ github.event_name }}${{ github.event.pull_request.head.repo.fork && '-fork' || '' }} + path: ~/.bun/install/cache + + - name: Mount node_modules + uses: ./.github/actions/cache-mount + with: + provider: ${{ vars.CI_PROVIDER }} + key: ${{ github.repository }}-node-modules-${{ github.event_name }}${{ github.event.pull_request.head.repo.fork && '-fork' || '' }} + path: ./node_modules + + - name: Mount Playwright browsers + uses: ./.github/actions/cache-mount + with: + provider: ${{ vars.CI_PROVIDER }} + key: ${{ github.repository }}-playwright-browsers-${{ github.event_name }}${{ github.event.pull_request.head.repo.fork && '-fork' || '' }} + path: ~/.cache/ms-playwright + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Rehearse SSO migration hardening + working-directory: packages/db + env: + E2E_PG_ADMIN_URL: 'postgresql://postgres:postgres@127.0.0.1:5432/postgres' + run: bun run db:rehearse-sso-migration + + - name: Configure E2E hostnames + run: echo "127.0.0.1 e2e.sim.ai mcp.e2e.sim.ai" | sudo tee -a /etc/hosts + + - name: Install Chromium + working-directory: apps/sim + run: bun run test:e2e:install-browsers -- --with-deps + + - name: Run complete settings E2E suite + timeout-minutes: 40 + working-directory: apps/sim + env: + CI: 'true' + E2E_PG_ADMIN_URL: 'postgresql://postgres:postgres@127.0.0.1:5432/postgres' + run: bun run test:e2e + + - name: Check E2E diagnostics eligibility + id: e2e_diagnostics + if: failure() + shell: bash + run: | + shopt -s nullglob + markers=(apps/sim/e2e/.runs/*/markers/leak-scan-complete.json) + if (( ${#markers[@]} > 0 )); then + echo "safe=true" >> "$GITHUB_OUTPUT" + else + echo "safe=false" >> "$GITHUB_OUTPUT" + fi + + - name: Upload E2E diagnostics + if: failure() && steps.e2e_diagnostics.outputs.safe == 'true' + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: settings-e2e-${{ github.run_id }} + path: | + apps/sim/playwright-report/ + apps/sim/test-results/ + apps/sim/e2e/.runs/ + !apps/sim/e2e/.runs/**/auth/** + !apps/sim/e2e/.runs/**/private/** + !apps/sim/e2e/.runs/**/homes/** + if-no-files-found: ignore + include-hidden-files: true + retention-days: 7 diff --git a/.gitignore b/.gitignore index a8a0d8e2fde..109495320d0 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,11 @@ package-lock.json # testing /coverage /apps/**/coverage +/apps/**/playwright-report/ +/apps/**/test-results/ +/apps/**/e2e/.runs/ +/apps/**/e2e/.auth/ +/apps/**/e2e/.cache/ # next.js /.next/ @@ -31,6 +36,7 @@ package-lock.json **/dist/ **/standalone/ sim-standalone.tar.gz +/.artifacts/ # redis dump.rdb diff --git a/apps/docs/app/api/chat/route.ts b/apps/docs/app/api/chat/route.ts index 2150e044980..658787f8918 100644 --- a/apps/docs/app/api/chat/route.ts +++ b/apps/docs/app/api/chat/route.ts @@ -1,9 +1,9 @@ import { openai } from '@ai-sdk/openai' import { convertToModelMessages, stepCountIs, streamText, tool, type UIMessage } from 'ai' import { sql } from 'drizzle-orm' -import { z } from 'zod' import { db, docsEmbeddings } from '@/lib/db' import { generateSearchEmbedding } from '@/lib/embeddings' +import { searchDocsInputSchema } from '@/lib/search-tool-schema' export const runtime = 'nodejs' export const maxDuration = 30 @@ -332,9 +332,7 @@ export async function POST(req: Request) { searchDocs: tool({ description: 'Search the Sim documentation for relevant content. Use this before answering any question about Sim.', - inputSchema: z.object({ - query: z.string().describe('A focused natural-language search query.'), - }), + inputSchema: searchDocsInputSchema, execute: async ({ query }) => searchDocs(query, locale), }), }, diff --git a/apps/docs/content/docs/en/platform/enterprise/sso.mdx b/apps/docs/content/docs/en/platform/enterprise/sso.mdx index ee3d53be3ec..7c483f30523 100644 --- a/apps/docs/content/docs/en/platform/enterprise/sso.mdx +++ b/apps/docs/content/docs/en/platform/enterprise/sso.mdx @@ -252,7 +252,7 @@ SSO provisioning creates internal organization members. External workspace membe }, { question: "A user already has an account with the same email — what happens when they sign in with SSO?", - answer: "Sim links the SSO identity to the existing account automatically, as long as your identity provider reports the email as verified (email_verified) or the provider is trusted. Most OIDC providers (Okta, Google Workspace, Auth0) assert email_verified, so linking just works. If sign-in fails with 'account not linked' — common with SAML providers that omit the claim — add the provider's ID to SSO_TRUSTED_PROVIDER_IDS on self-hosted and restart." + answer: "Before verified-domain enforcement is activated, Sim links the SSO identity when your identity provider reports a verified email or the provider is operator-trusted. After enforcement is activated, the provider's verified organization domain is the account-linking trust boundary." }, { question: "Who can configure SSO on Sim Cloud?", @@ -281,11 +281,16 @@ Self-hosted deployments use environment variables instead of the billing/plan ch SSO_ENABLED=true NEXT_PUBLIC_SSO_ENABLED=true +# Enable only after the one-shot pre-migration audit, schema migration, +# separately approved domain-ownership backfill, and live TXT check below. +SSO_DOMAIN_VERIFICATION_ENABLED=false + # Required if you want users auto-added to your organization on first SSO sign-in ORGANIZATIONS_ENABLED=true NEXT_PUBLIC_ORGANIZATIONS_ENABLED=true -# Optional: comma-separated SSO provider IDs to trust for automatic account linking +# Optional while domain verification is disabled: comma-separated SSO provider IDs +# to trust for automatic account linking # (links an SSO sign-in to an existing account with the same email). Needed when your # IdP does not assert email_verified — typically SAML providers, or OIDC providers that # omit the claim. Set it to the Provider ID you registered, then restart. @@ -295,14 +300,61 @@ SSO_TRUSTED_PROVIDER_IDS=custom-oidc,partner-saml ``` - When someone signs in with SSO and an account with the same email already exists - (for example, they previously signed up with email/password), Sim links the SSO - identity to that account automatically as long as your IdP reports the email as - verified, or the provider is trusted. If you hit an `account not linked` error, - either confirm your IdP sends `email_verified`, or add the provider's ID to - `SSO_TRUSTED_PROVIDER_IDS` and restart. + While `SSO_DOMAIN_VERIFICATION_ENABLED` is false, existing linking behavior uses + the IdP's `email_verified` claim or `SSO_TRUSTED_PROVIDER_IDS`. Once the flag is + true, Sim requires the SSO provider's organization domain to be verified and no + longer trusts an IdP-controlled email-verification claim by itself. + + + + Migration 0266 runs a one-shot legacy-provider audit before changing the + schema. If the legacy provider table exists, disable SSO/provider mutations + and keep them disabled through migration, then set + `SSO_PROVIDER_WRITES_QUIESCED=true` even when the table is empty. Provide + `SSO_AUDIT_APPROVED_PROVIDER_IDS` only for providers whose existing account + links and active sessions operators chose to retain. Omit a provider until + its links are migrated or removed and its sessions are revoked. + Remediate user-scoped, public-suffix, unknown-suffix, duplicate, or + overlapping rows before retrying. The quiescence flag is an acknowledgement, + not a technical lock against an older running application. + + Account/session approval does not prove domain ownership. After migration, + transactionally backfill `domain_verified` only for domains supported by + independently reviewed ownership evidence, read the values back, and keep + unknown rows false. Complete a live TXT verification before enabling + `SSO_DOMAIN_VERIFICATION_ENABLED`. Internal pseudo-domains are not eligible + for verified-domain enforcement. +For the first 0266 upgrade with Docker Compose, set the one-shot migration +inputs in the Compose environment while writes are actually disabled: + +```bash +export SSO_PROVIDER_WRITES_QUIESCED=true +export SSO_AUDIT_APPROVED_PROVIDER_IDS=reviewed-provider-ids +docker compose -f docker-compose.prod.yml up +``` + +For Helm, use inline migration values or a dedicated Secret: + +```yaml +migrations: + ssoPreflight: + providerWritesQuiesced: "true" + auditApprovedProviderIds: "reviewed-provider-ids" + # Or set existingSecret to a Secret containing + # SSO_PROVIDER_WRITES_QUIESCED and SSO_AUDIT_APPROVED_PROVIDER_IDS. +``` + +Secret-backed keys are optional at Kubernetes pod admission so removing retired +one-shot keys cannot block deployments after 0266. A pending 0266 migration +still fails closed unless `SSO_PROVIDER_WRITES_QUIESCED` resolves to `true`; +`SSO_AUDIT_APPROVED_PROVIDER_IDS` may be absent when no linked providers require +an explicit approval. + +Remove the one-shot values after 0266 is journaled. Do not leave provider writes +disabled or interpret the flag itself as quiescence. + You can register providers through the **Settings UI** (same as cloud) or by running the registration script directly against your database. ### Script-based registration @@ -318,6 +370,7 @@ SSO_PROVIDER_ID=okta \ SSO_ISSUER=https://dev-1234567.okta.com/oauth2/default \ SSO_DOMAIN=company.com \ SSO_USER_EMAIL=admin@company.com \ +SSO_ORGANIZATION_ID=your-organization-id \ SSO_OIDC_CLIENT_ID=your-client-id \ SSO_OIDC_CLIENT_SECRET=your-client-secret \ bun run packages/db/scripts/register-sso-provider.ts @@ -332,6 +385,7 @@ SSO_PROVIDER_ID=adfs \ SSO_ISSUER=https://your-instance.com \ SSO_DOMAIN=company.com \ SSO_USER_EMAIL=admin@company.com \ +SSO_ORGANIZATION_ID=your-organization-id \ SSO_SAML_ENTRY_POINT=https://adfs.company.com/adfs/ls \ SSO_SAML_CERT="-----BEGIN CERTIFICATE----- ... @@ -345,5 +399,7 @@ To remove a provider: ```bash SSO_USER_EMAIL=admin@company.com \ +SSO_ORGANIZATION_ID=your-organization-id \ +SSO_PROVIDER_ID=adfs \ bun run packages/db/scripts/deregister-sso-provider.ts ``` diff --git a/apps/docs/lib/search-tool-schema.test.ts b/apps/docs/lib/search-tool-schema.test.ts new file mode 100644 index 00000000000..a1c086bbc6e --- /dev/null +++ b/apps/docs/lib/search-tool-schema.test.ts @@ -0,0 +1,24 @@ +import { describe, expect, it } from 'vitest' +import { validateSearchDocsInput } from './search-tool-schema' + +describe('search documentation tool schema', () => { + it('accepts exactly one string query', () => { + expect(validateSearchDocsInput({ query: 'SSO setup' })).toEqual({ + success: true, + value: { query: 'SSO setup' }, + }) + }) + + it.each([ + ['null', null], + ['array', []], + ['missing query', {}], + ['non-string query', { query: 7 }], + ['extra properties', { query: 'SSO setup', unexpected: true }], + ])('rejects malformed input: %s', (_label, value) => { + expect(validateSearchDocsInput(value)).toMatchObject({ + success: false, + error: expect.any(TypeError), + }) + }) +}) diff --git a/apps/docs/lib/search-tool-schema.ts b/apps/docs/lib/search-tool-schema.ts new file mode 100644 index 00000000000..7a6034f5e17 --- /dev/null +++ b/apps/docs/lib/search-tool-schema.ts @@ -0,0 +1,41 @@ +import { jsonSchema } from 'ai' + +interface SearchDocsInput { + query: string +} + +export function validateSearchDocsInput(value: unknown) { + if ( + typeof value !== 'object' || + value === null || + Array.isArray(value) || + Object.keys(value).length !== 1 || + !('query' in value) || + typeof value.query !== 'string' + ) { + return { + success: false as const, + error: new TypeError('Search documentation input must contain only a string query'), + } + } + + return { + success: true as const, + value: { query: value.query }, + } +} + +export const searchDocsInputSchema = jsonSchema( + { + type: 'object', + properties: { + query: { + type: 'string', + description: 'A focused natural-language search query.', + }, + }, + required: ['query'], + additionalProperties: false, + }, + { validate: validateSearchDocsInput } +) diff --git a/apps/docs/package.json b/apps/docs/package.json index 9fde4625a2a..598ba3fdcab 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -8,6 +8,7 @@ "build": "fumadocs-mdx && NODE_OPTIONS='--max-old-space-size=8192' next build", "start": "next start", "postinstall": "fumadocs-mdx", + "test": "vitest run", "type-check": "tsc --noEmit", "lint": "biome check --write --unsafe .", "lint:check": "biome check .", @@ -53,6 +54,7 @@ "@typescript/native-preview": "7.0.0-dev.20260707.2", "postcss": "^8.5.3", "tailwindcss": "^4.0.12", - "typescript": "^7.0.2" + "typescript": "^7.0.2", + "vitest": "^4.1.0" } } diff --git a/apps/realtime/src/env.ts b/apps/realtime/src/env.ts index 5afdf3a20f3..2f75c4a595f 100644 --- a/apps/realtime/src/env.ts +++ b/apps/realtime/src/env.ts @@ -14,6 +14,7 @@ const EnvSchema = z.object({ INTERNAL_API_SECRET: z.string().min(32), NEXT_PUBLIC_APP_URL: z.string().url(), ALLOWED_ORIGINS: z.string().optional(), + REALTIME_HOST: z.string().min(1).default('0.0.0.0'), PORT: z.coerce.number().int().positive().default(3002), SIM_DB_ROLE: z.enum(['web', 'trigger', 'realtime']).optional(), DISABLE_AUTH: z diff --git a/apps/realtime/src/index.ts b/apps/realtime/src/index.ts index e43184c1f02..0c84867156b 100644 --- a/apps/realtime/src/index.ts +++ b/apps/realtime/src/index.ts @@ -31,6 +31,7 @@ async function createRoomManager(io: SocketIOServer): Promise { async function main() { const httpServer = createServer() const PORT = env.PORT + const HOST = env.REALTIME_HOST logger.info('Starting Socket.IO server...', { port: PORT, @@ -96,9 +97,9 @@ async function main() { await assertSchemaCompatibility() - httpServer.listen(PORT, '0.0.0.0', () => { - logger.info(`Socket.IO server running on port ${PORT}`) - logger.info(`Health check available at: http://localhost:${PORT}/health`) + httpServer.listen(PORT, HOST, () => { + logger.info(`Socket.IO server running on ${HOST}:${PORT}`) + logger.info(`Health check available at: http://${HOST}:${PORT}/health`) }) const shutdown = async () => { diff --git a/apps/realtime/src/routes/http.ts b/apps/realtime/src/routes/http.ts index 0f8ed73cc52..0bfef282fcf 100644 --- a/apps/realtime/src/routes/http.ts +++ b/apps/realtime/src/routes/http.ts @@ -69,6 +69,7 @@ export function createHttpHandler(roomManager: IRoomManager, logger: Logger) { status: 'ok', timestamp: new Date().toISOString(), connections, + ...(process.env.E2E_RUN_ID ? { runId: process.env.E2E_RUN_ID } : {}), }) ) } catch (error) { diff --git a/apps/sim/AGENTS.md b/apps/sim/AGENTS.md index 6c52c2df02d..4f5a6214ab4 100644 --- a/apps/sim/AGENTS.md +++ b/apps/sim/AGENTS.md @@ -221,6 +221,7 @@ export function useEntityList(workspaceId?: string) { - Use `vi.hoisted()` + `vi.mock()` + static imports; do not use `vi.resetModules()` + `vi.doMock()` + dynamic imports except for true module-scope singletons. - Do not use `vi.importActual()`. - Prefer mocks and factories from `@sim/testing`. +- Before finishing a change that can affect observable settings routes, copy, visibility, authorization, entitlements, mutations, or persisted workflows, follow [`e2e/MAINTENANCE.md`](e2e/MAINTENANCE.md) and assess every applicable literal Playwright contract. Behavior-preserving refactors need focused verification, not artificial expectation edits. ## Utils Rules diff --git a/apps/sim/app/(landing)/layout.tsx b/apps/sim/app/(landing)/layout.tsx index 9fef6524507..f9c31e12780 100644 --- a/apps/sim/app/(landing)/layout.tsx +++ b/apps/sim/app/(landing)/layout.tsx @@ -11,6 +11,7 @@ import { XPageViewTracker } from '@/app/(landing)/x-page-view-tracker' const HUBSPOT_SCRIPT_SRC = 'https://js-na2.hs-scripts.com/246720681.js' as const const X_PIXEL_ID = 'q5xbl' as const +const isMarketingAnalyticsEnabled = isHosted && !process.env.E2E_PROFILE /** X (Twitter) conversion tracking base code — loads uwt.js and fires the initial PageView. */ const X_PIXEL_BASE_CODE = `!function(e,t,n,s,u,a){e.twq||(s=e.twq=function(){s.exe?s.exe.apply(s,arguments):s.queue.push(arguments); @@ -43,7 +44,7 @@ export default function LandingLayout({ children }: { children: ReactNode }) { {children} {/* HubSpot + X pixel tracking — hosted only */} - {isHosted && ( + {isMarketingAnalyticsEnabled && ( <>