Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .agents/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Use the narrowest validation that still covers the change:
| Tests-only changes | `pnpm lint-fix`, targeted `pnpm test --run <test_file.ts>`, `pnpm check` |
| Type-level/API type changes | Targeted `pnpm test-types <filename>`, plus `pnpm check` when source types changed |
| JSDoc text/category/link changes | `pnpm lint` |
| JSDoc example changes | `pnpm lint`; package-local `pnpm docgen`; root `pnpm doctest` for runnable examples |
| JSDoc example changes | `pnpm lint`; package-local `pnpm docgen`; root `pnpm doctest --run <files>` |
| Docs-only changes | `pnpm lint-fix`; no tests required unless examples or code changed |

Never run the whole test suite. A bare `pnpm test` or `pnpm doctest` runs every package in watch mode and will not
Expand Down Expand Up @@ -134,8 +134,10 @@ Read `.patterns/testing.md` before writing or changing tests.
- AI documentation changes may include explanatory comments when useful.
- For public JSDoc categories and example best practices, read `.patterns/jsdoc.md`.
- Mark runnable TypeScript examples with `````ts import.meta.vitest``. Use the additional `suite` metadata when an example registers Vitest tests or suites, and invoke those registration APIs directly.
- Prefer direct trailing value assertions such as `operation() // => Option.some(1)`. Keep bindings only for reuse or meaningful multi-step setup, separate later assertion blocks with a blank line, use dense expected arrays such as `[1, 2]`, and keep a call on one line when the complete line is at most 120 characters.
- Assert semantic values rather than console formatting. Preserve `import.meta.vitest` on type-level examples without adding tautological runtime assertions.
- Keep marked examples self-contained, deterministic, bounded, and free of external-service dependencies. Await asynchronous work.
- Run `pnpm doctest` from the repository root to execute marked examples. Package-local `pnpm docgen` remains required to typecheck documentation examples.
- Run `pnpm doctest --run <source files>` from the repository root to execute changed examples. Package-local `pnpm docgen` remains required to typecheck documentation examples.
- When JSDoc examples are localized to a single package, run `pnpm docgen` from that package directory instead of the repository root.

## Generated Files
Expand Down
17 changes: 10 additions & 7 deletions .agents/skills/jsdocs/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,18 +145,21 @@ A good example:
- uses stable, deterministic, bounded behavior and does not require network
access, external services, timing assumptions, randomness, or machine-specific
state;
- demonstrates the meaningful result, with a concise expected-output comment
- demonstrates the meaningful result, with a concise expected-value comment
when useful; and
- uses explanatory prose only when the code cannot communicate an important
choice or caveat on its own.

### Executable examples

- Mark runnable TypeScript fences with `import.meta.vitest`. Running `pnpm doctest` from the repository root executes every marked example.
- Mark runnable TypeScript fences with `import.meta.vitest`. Run changed examples from the repository root with `pnpm doctest --run <source files>`.
- Write each marked example as a complete isolated module. Import public APIs, define every runtime value, await asynchronous work, and keep execution deterministic and bounded.
- Prefer `operation() // => expected` over introducing a result binding used only by the assertion. Retain bindings for reuse, mutation, identity checks, or meaningful multi-step setup, and insert a blank line before a separate assertion block.
- Keep direct assertions on one line up to 120 characters. Use dense expected arrays such as `[1, 2]` and semantic Effect values such as `Option.some(1)` rather than console formatting.
- Preserve `import.meta.vitest` for type-level examples, but do not add tautological runtime assertions to them.
- Use `suite` metadata when the example registers Vitest tests or suites: `````ts import.meta.vitest suite``. Call the registration API directly so the nested tests and assertions are collected and executed.
- Keep documentation-only snippets as plain `````ts`` fences.
- Treat `pnpm doctest` and package-local `pnpm docgen` as complementary checks: doctest executes marked examples, while docgen typechecks documentation examples.
- Treat targeted doctest runs and package-local `pnpm docgen` as complementary checks: doctest executes marked examples, while docgen typechecks documentation examples.

When reviewing existing examples:

Expand All @@ -170,9 +173,9 @@ When reviewing existing examples:
scaffolding than the insight justifies. Also remove it when a good replacement
would require guessing at a use case.

Prefer readable output over assertions; public documentation should not look
like a test suite. Type-level examples may demonstrate inference or assignability
without runtime output. For lazy APIs such as `Effect`, execute enough of the
Prefer concise trailing `// =>` assertions that keep the meaningful result visible;
public documentation should not look like a test suite. Type-level examples may demonstrate inference or assignability
without runtime assertions. For lazy APIs such as `Effect`, execute enough of the
program to demonstrate the behavior unless the example's value is specifically
type-level or construction-oriented.

Expand Down Expand Up @@ -252,7 +255,7 @@ When refining an existing public API module, always do a dedicated `**Gotchas**`

Run the narrowest validation that matches the change:

- For runnable JSDoc example changes, run `pnpm doctest` from the repository root.
- For runnable JSDoc example changes, run `pnpm doctest --run <source files>` from the repository root.
- For JSDoc or example changes in a package with generated docs, run `pnpm docgen` from that package directory.
- Run `pnpm lint` because the linter includes the custom rule that checks public API JSDoc.
- Do not run broad validation for prose-only skill edits.
2 changes: 1 addition & 1 deletion .changeset/doctest-console-output.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
"@effect/doctest": patch
---

Add convention-based console output assertions using standalone `// >` comments in runnable documentation examples.
Add convention-based `// =>` assertions that compare documentation example values using Effect equality.
5 changes: 5 additions & 0 deletions .changeset/fix-subscriptionref-getandupdateeffect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"effect": patch
---

Fix `SubscriptionRef.getAndUpdateEffect` to execute the effectful update.
7 changes: 5 additions & 2 deletions .patterns/jsdoc.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,12 @@ Keep these distinctions:
## Example Best Practices

- Mark runnable TypeScript examples with `````ts import.meta.vitest`` so `pnpm doctest` executes them.
- Use inline or standalone `// >` comments to assert the complete console output of a runnable example. Move inline markers to the next line when the source line would exceed 120 characters. Each marker represents one expected output line; an angle-bracketed label such as `// > <system time zone>` matches exactly one unavoidable environment-dependent line, and examples without markers remain execution-only.
- Use a trailing `// =>` comment to assert an expression or single initialized `const` identifier against a TypeScript expression on the same line. Values use Effect's `Equal.equals` semantics, and examples without markers remain execution-only. Write asynchronous execution explicitly; the transform does not run Effects or await promises automatically.
- Prefer asserting the API call directly. Keep bindings only for reuse, mutation, identity checks, or meaningful multi-step setup; put a blank line before a separate assertion block.
- Keep calls on one line when the complete line is at most 120 characters. Format expected arrays densely (`[1, 2]`, `[[1], [2]]`, `Option.some([1, 2])`) while retaining normal object spacing.
- Assert semantic constructors such as `Option.some`, `Result.succeed`, and `Exit.fail`, not rendered console output. Preserve runnable markers on type-level examples without adding fake runtime assertions.
- Keep runnable examples complete, deterministic, bounded, and independent of external services or machine-specific state. Await asynchronous work so failures and cleanup remain inside the doctest.
- Import public APIs and include all required setup. Do not use undeclared placeholders or rely on declarations from surrounding prose.
- Use `````ts import.meta.vitest suite`` for examples that register Vitest tests or suites, such as `@effect/vitest` helpers. Invoke the registration API directly; the `suite` marker runs the example during collection.
- Leave intentionally non-executable snippets as plain `````ts`` fences.
- Run `pnpm doctest` from the repository root after changing runnable examples. Also run `pnpm docgen` from the affected package directory because docgen typechecks examples while doctest executes marked examples.
- Run `pnpm doctest --run <source files>` from the repository root after changing runnable examples. Also run `pnpm docgen` from the affected package directory because docgen typechecks examples while doctest executes marked examples.
6 changes: 6 additions & 0 deletions packages/atom/react/src/ScopedAtom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export const TypeId: TypeId = "~@effect/atom-react/ScopedAtom"
* import { make, useAtomValue } from "@effect/atom-react"
* import { Atom } from "effect/unstable/reactivity"
* import * as React from "react"
* import { renderToStaticMarkup } from "react-dom/server"
*
* const Counter = make(() => Atom.make(0))
*
Expand All @@ -58,6 +59,8 @@ export const TypeId: TypeId = "~@effect/atom-react/ScopedAtom"
* export function App() {
* return React.createElement(Counter.Provider, null, React.createElement(View))
* }
*
* renderToStaticMarkup(React.createElement(App)) // => "<div>0</div>"
* ```
*
* @category models
Expand Down Expand Up @@ -96,6 +99,7 @@ export interface ScopedAtom<A extends Atom.Atom<any>, Input = never> {
* import { make, useAtomValue } from "@effect/atom-react"
* import { Atom } from "effect/unstable/reactivity"
* import * as React from "react"
* import { renderToStaticMarkup } from "react-dom/server"
*
* const User = make((name: string) => Atom.make(name))
*
Expand All @@ -112,6 +116,8 @@ export interface ScopedAtom<A extends Atom.Atom<any>, Input = never> {
* React.createElement(UserName)
* )
* }
*
* renderToStaticMarkup(React.createElement(App)) // => "<span>Ada</span>"
* ```
*
* @category constructors
Expand Down
1 change: 1 addition & 0 deletions packages/effect/docgen.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"module": "ES2022",
"target": "ES2022",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"types": ["node"],
"rewriteRelativeImportExtensions": true,
"allowImportingTsExtensions": true,
"paths": {
Expand Down
Loading
Loading