Skip to content

refactor(cli): enforce Effect platform boundaries#3859

Closed
Alberto Schiabel (jkomyno) wants to merge 53 commits into
nextfrom
refactor/cli-effect-platform-guardrails
Closed

refactor(cli): enforce Effect platform boundaries#3859
Alberto Schiabel (jkomyno) wants to merge 53 commits into
nextfrom
refactor/cli-effect-platform-guardrails

Conversation

@jkomyno

@jkomyno Alberto Schiabel (jkomyno) commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

This PR:

  • prepares the CLI runtime boundaries for the Effect v4 work in chore(cli): prepare Effect v4 migration #3851 without changing current command behavior
  • prevents new try/catch blocks and direct node:fs, node:os, or node:path imports in CLI production sources
  • moves config discovery, session artifacts, and telemetry onto Effect FileSystem, Path, layers, and typed recovery
  • preserves detached telemetry delivery, API/session headers, artifact-root precedence, symlink handling, and non-fatal failure behavior
  • establishes a staged lint ratchet for the 92 legacy violations that remain after this first cleanup slice
  • adds regression coverage for telemetry producers, Codact delivery, session correlation, artifact storage, skill-root discovery, and HOME-less environments

Second slice: Effect v4 readiness

  • isolates the Symbol.for('effect/SpanAnnotation') internal read behind a shape-validating adapter, with a canary test that fails a real effect through the runtime so an effect upgrade that drops the mechanism breaks CI instead of silently losing span timelines
  • pins the v3 Cause traversal (reduceWithContext over the six-constructor tree) and rendered error output with characterization tests, giving the v4 migration an executable equivalence oracle
  • converts all 33 remaining thunk-form Effect.tryPromise sites to typed Data.TaggedError failures that preserve their cause; UnknownException no longer enters the CLI error channel from src/, and best-effort recoveries switch from catchAll to catchTag

Second slice: boundary and correctness fixes

  • Security fix: during this review we discovered that parse-json-ish.ts fell back to new Function() evaluation for any tools execute --data, listen --params, or proxy body value that failed JSON and comment-JSON parsing — an in-process arbitrary-code-execution surface. The CLI is agent-facing and --data also accepts @file and stdin, so crafted or prompt-injected input reached the eval. Replaced with JSON5, which covers the documented "JSON or JS-style object literal" contract (unquoted keys, single quotes, trailing commas, comments) without evaluating anything; code-execution probes are now pinned by unit tests. Intentional narrowing: genuine JS expressions (1+1, new Date()) that only worked because of the eval now fail with a parse error.
  • surfaces realtime subscription failures as typed errors: composio listen and composio dev triggers listen no longer Effect.orDie expected network/auth failures into captured-defect dumps; they print one actionable line ("Could not subscribe to realtime trigger events: … Check your network connection or run composio login."), clean up temporary triggers, and exit 1
  • decodes trigger-log payloads once at the API boundary through a tolerant Schema.transform that documents the camelCase/snake_case/meta-nested alias precedence, replacing as unknown as Record cast ladders duplicated across format.ts and logs.triggers.cmd.ts; raw payloads still go to stdout unchanged
  • consolidates all CommandDescriptor/Usage introspection into command-introspection.ts — the @effect/cli v4 redesign seam — so the v4 command-tree rewrite touches one module; a no-restricted-imports rule keeps the seam sealed

Validation

  • pnpm typecheck - 12/12 tasks passed
  • clean-environment pnpm --filter @composio/cli test - 889 passed, 1 skipped
  • pnpm --filter @composio/cli build - passed
  • scoped ESLint - 0 errors; 6 pre-existing warnings
  • git diff --check - passed

New concepts

ESLint bulk-suppression ratchet

ESLint records the current violation count for each file and rule. A later change fails lint when it adds a violation, and cleanup also fails until the now-stale count is pruned.

That lets this PR activate strict Effect boundaries immediately without adding roughly 92 inline disable comments or forcing an all-at-once rewrite of the CLI. The migrated telemetry, artifact, and config paths reduce those baseline counts as part of the same change.

The tradeoff is identity: counts are not tied to exact source locations, so replacing one legacy violation with another in the same file can remain green. This is a migration tool, not the end state; the baseline should disappear as the remaining files move to Effect Platform.

Third slice: fail-closed gating and tolerant decoding

  • Security hardening: the tool-permission gate failed open — when the org config reported enhanced controls enabled but the permission-policy resolve failed or did not decode, gateToolExecution skipped entirely, silently executing tools the org policy may explicitly deny (always_deny). The gate now fails closed with evidence: a failed policy resolve on a controls-enabled org synthesizes an ask_every_call snapshot (never persisted to cache), so the worst case is a spurious prompt instead of an unauthorized side-effecting tool call. A failed org-config probe still skips the gate — it carries no evidence controls exist, and prompting there would break older backends and offline runs.
  • extracts API error details field by field: a present-but-mistyped field (numeric message, string code) drops only itself instead of discarding the node's valid slug and request_id
  • decodes GitHub release listings per element: one deviant entry (a draft mid-upload with a null asset URL) no longer breaks composio upgrade or the background update check, and skipped entries are logged
  • decodes both persisted caches (consumer short-term cache, tool-permissions cache) per entry: one version-skewed entry drops only itself instead of emptying the whole cache — which the next write then persisted, permanently destroying the good entries
  • renders the deepest cause message as a Caused by: line in pretty error output, so wrappers like UpgradeBinaryError no longer swallow the actionable ParseError specifics
  • fixes two generate-command tests whose only message assertion was guarded behind an if that could silently skip
  • removes the repo-wide *.test.ts allowlist from .gitleaks.toml (added alongside per-fingerprint suppressions that already covered the findings; CI has since moved to GitHub Advanced Security, so the rule only disarmed local and future gitleaks runs)

Validation for this slice: pnpm typecheck passes; clean-environment full CLI suite — 908 passed, 1 skipped.

Related: #3832 (merged separately) makes whoami report the org selected via orgs switch instead of the API key's home org.

@vercel

vercel Bot commented Jul 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment Jul 17, 2026 7:48am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Security Audit Warning

The pnpm audit --prod check found security vulnerabilities in production dependencies.

Please review and fix the vulnerabilities. You can try running:

pnpm audit --fix --prod
Audit output
┌─────────────────────┬────────────────────────────────────────────────────────┐
│ low                 │ @ai-sdk/provider-utils has an Uncontrolled Resource    │
│                     │ Consumption issue                                      │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Package             │ @ai-sdk/provider-utils                                 │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Vulnerable versions │ <=3.0.97                                               │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Patched versions    │ >=3.0.98                                               │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Paths               │ ts__e2e-tests__runtimes__node__mastra-tool-router-zod- │
│                     │ v3>@mastra/core>@ai-sdk/provider-utils                 │
│                     │                                                        │
│                     │ ts__e2e-tests__runtimes__node__mastra-tool-router-zod- │
│                     │ v3>@mastra/mcp>@mastra/core>@ai-sdk/provider-utils     │
│                     │                                                        │
│                     │ ts__e2e-tests__runtimes__node__mastra-tool-router-zod- │
│                     │ v4>@mastra/core>@ai-sdk/provider-utils                 │
│                     │                                                        │
│                     │ ... Found 14 paths, run `pnpm why                      │
│                     │ @ai-sdk/provider-utils` for more information           │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ More info           │ https://github.com/advisories/GHSA-866g-f22w-33x8      │
└─────────────────────┴────────────────────────────────────────────────────────┘
1 vulnerabilities found
Severity: 1 low

@jkomyno

This comment was marked as outdated.

Route cached-schema decoding and validation errors through Effect Schema, prevent direct Zod imports in the validator, and reuse the CLI autocorrect distance with bounded suggestions.
Replace private tag and descriptor inspection with public Effect APIs, model
expected failures as typed errors, and decode untrusted runtime data at schema
boundaries. Preserve the behavior with focused regression coverage and CLI
migration guidance.
…eterministic clock

Un-ignore ts/packages/cli/test (fixtures and the test/__utils__ harness glue
stay unlinted) and ban Effect.run* and Date.now() in test sources: Effects
run through @effect/vitest (it.effect / it.scoped / it.live) so failures
surface as typed Exits, and time is pinned via TestClock-relative fixtures,
vi.setSystemTime, or crypto.randomUUID() for unique names. Migrate the ~90
existing Effect.run* call sites across 18 suites, strengthen throw-based
failure assertions to typed error checks, relax max-lines-per-function and
no-explicit-any for test code, and fix a raw ESC control byte that made one
suite unparseable.
…ffect idioms

Consolidate repeated concepts across ts/packages/cli/src/services into
single canonical definitions, verified behavior-preserving:

- export JsonRecordSchema from effects/json and drop 9 local re-declarations
- share checksum parsing/hashing (utils/checksums), djb2 (utils/djb2), and
  the tolerant per-entry cache decode (utils/collect-decoded-entries)
- reuse toolkitFromToolSlug, compareNewestFirst, hasEnvPrefix, detectMaster,
  and getAncestors instead of inline re-implementations
- delete the orphaned EnvLangDetector service (superseded by
  ProjectEnvironmentDetector, zero consumers)
- extract sortBySlug, filterBySlugPrefixes, toSetupTargetResult,
  runSetupTargets, getFreshCacheEntry, and DEFAULT_CLI_USER_CONFIG to unify
  copy-pasted blocks
- convert ToolInputValidationError to Data.TaggedError (same tag/message),
  narrow two catchAll recoveries to catchTag, derive the connected-account
  status list from the model schema
- tool-input-validation: fetch tool detail and latest version concurrently,
  reuse the fetched definition on the stale path instead of re-reading the
  cache file, and drop exists-then-read/remove TOCTOU checks

Verified: pnpm typecheck, pnpm lint, CLI vitest (916 passed), pnpm build.
…cross CLI source

Convert every node:path usage behind a no-restricted-imports disable to the
@effect/platform Path service: plain path helpers become Effects where their
callers are Effect-hosted (getAncestors, safeOutputPath, cachePath), sync
boundaries receive the Path.Path instance as a plain parameter
(ts.CompilerHost, promise-queue cache helpers), and self-provided layer
stacks gain Path.layer. run-companion-modules.ts is fully Effect-ified over
FileSystem/Path/Config, dropping its fs/os/path import disables and the
process.env/try-finally syntax disables with behavior parity.
…ct idioms

Convert the remaining fixable disables: try/catch JSON and URL probes become
Either.try helpers (parseJsonRecord for user-supplied flag payloads),
promise-queue cache and upload helpers receive a threaded FileSystem instance
(write queue now serialized by a module-level semaphore), update-check is
Effect-ified end to end behind its single pre-runtime runPromiseExit boundary,
upgrade-binary uses FileSystem copy/remove, and the no-explicit-any and
max-lines-per-function disables are removed with precise types and mechanical
helper extraction.
…ble manifest

Every eslint-disable in CLI src must be a single-line disable with a reason
and be registered in eslint-boundaries.json with an exact per-file, per-rule
count; validate-eslint-boundaries.ts (wired into pnpm test, so CI-blocking)
rejects anything else. Document the boundary policy — service table,
conversion patterns, sanctioned runtime boundaries, manifest ritual — in
AGENTS.md and the cli-command skill reference.
The suite landed on next before this branch's lint rule banning Effect.run*
in CLI tests; the PR merge combines both, so convert the two cases to
it.effect with the decode-ignoring effect yielded directly.
@jkomyno

Copy link
Copy Markdown
Collaborator Author

Closing in favor of a reviewable stack that reproduces this branch's final tree (the refactor/cli-effect-platform-guardrails branch stays intact as the reference):

  1. chore(cli): land Effect-first guidelines and inert guardrail rule groups #3877 — guidelines + inert rule groups
  2. fix(cli): remove new Function() eval from JSON-ish input parsing #3878new Function() eval security fix
  3. fix(cli): fail closed on permission gaps and tolerate skewed payloads #3879 — behavioral fix pack
  4. refactor(cli): migrate platform access to @effect/platform services #3880 — platform-imports rule + migration
  5. refactor(cli): route terminal output through the TerminalUI boundary #3881 — terminal-streams rule + TerminalUI boundary
  6. refactor(cli): seal the CommandDescriptor seam and drop zod for Schema #3882 — descriptor seam + zod→Schema
  7. test(cli): lint the test tree and enforce deterministic @effect/vitest suites #3883 — test-tree lint + deterministic suites
  8. refactor(cli): enforce typed Effect error boundaries and v4 readiness seams #3884 — typed error boundaries + v4 seams
  9. refactor(cli): activate the try/catch and env ban and pin the boundary ratchet #3885 — try/catch+env ban + boundary ratchet

The only intentional content deviation from this branch's tip: the stack drops .changeset/cli-effect-safety-boundaries.md@composio/cli is in the changeset ignore list, so that file would make changeset version fail on next.

Alberto Schiabel (jkomyno) added a commit that referenced this pull request Jul 21, 2026
…ups (#3877)

This PR:
- opens the CLI Effect-guardrails stack that re-slices
#3859 into reviewable PRs
- rewrites the CLI `AGENTS.md` Effect guidance: typed `Data.TaggedError`
failures, `Schema`/`Predicate` decoding at trust boundaries, and Effect
platform services over Node builtins — policy for new code effective now
- adds the matching "Effect Platform Boundaries" section to the
`cli-command` skill reference
- defines every new ESLint rule group (platform imports, terminal
streams, descriptor seam, zod ban, test rules, try/catch +
`process.env`) as live constants in the root `eslint.config.mjs`, with
the entries applying them commented out and annotated with the stack PR
that activates each group
- bumps the vendored `ts/vendor/effect` reference to the pin used across
the stack (CI never initializes submodules)

No CLI source is touched; `pnpm lint` stays green with the rules inert.
---

**Stack** (re-slice of #3859;
each PR targets its parent and auto-retargets to `next` as parents
merge):

1. #3877 — guidelines + inert
rule groups ← **this PR**
2. #3878 — `new Function()`
eval security fix
3. #3879 — behavioral fix
pack
4. #3880 — platform-imports
rule + migration
5. #3881 — terminal-streams
rule + TerminalUI boundary
6. #3882 — descriptor seam +
zod→Schema
7. #3883 — test-tree lint +
deterministic suites
8. #3884 — typed error
boundaries + v4 seams
9. #3885 — try/catch+env ban
+ boundary ratchet

On stacked bases CI runs lint/build and the CLI Docker e2e job; unit
tests and typecheck are verified locally per PR and re-verified by full
CI when each PR is retargeted to `next`.
Alberto Schiabel (jkomyno) added a commit that referenced this pull request Jul 21, 2026
…3878)

This PR:
- removes an in-process arbitrary-code-execution surface:
`parse-json-ish.ts` fell back to `Function("return (…)")()` for any
`tools execute --data`, `listen --params`, or `proxy` body value that
failed JSON and comment-JSON parsing — the CLI is agent-facing and
`--data` also accepts `@file` and stdin, so crafted or prompt-injected
input reached the eval
- lands the parser in its final consolidated shape:
`src/utils/parse-json.ts` parses JSON5 (unquoted keys, single quotes,
trailing commas, comments) without evaluating anything and returns
`Either` with a typed `JsonParsingError`
- consolidates the `effect-errors` read-json duplicates onto the same
parser and swaps `comment-json` for `json5` (the stack's only lockfile
change)
- pins code-execution probes in unit tests (`new Date()`, computed
expressions, `globalThis` writes are rejected, never evaluated)
- intentional narrowing: JS expressions such as `1+1` that only worked
because of the eval now fail with a parse error; proxy bodies that are
not JSON5 records fall back to the raw string
---

**Stack** (re-slice of #3859;
each PR targets its parent and auto-retargets to `next` as parents
merge):

1. #3877 — guidelines + inert
rule groups
2. #3878 — `new Function()`
eval security fix ← **this PR**
3. #3879 — behavioral fix
pack
4. #3880 — platform-imports
rule + migration
5. #3881 — terminal-streams
rule + TerminalUI boundary
6. #3882 — descriptor seam +
zod→Schema
7. #3883 — test-tree lint +
deterministic suites
8. #3884 — typed error
boundaries + v4 seams
9. #3885 — try/catch+env ban
+ boundary ratchet

On stacked bases CI runs lint/build and the CLI Docker e2e job; unit
tests and typecheck are verified locally per PR and re-verified by full
CI when each PR is retargeted to `next`.
Alberto Schiabel (jkomyno) added a commit that referenced this pull request Jul 21, 2026
…#3879)

This PR:
- lands ten behavioral fixes as one commit each, so `git show` reviews
standalone
- fails the tool-permission gate closed: an unknown snapshot state or a
failed policy resolve on a controls-enabled org now synthesizes an
`ask_every_call` snapshot (never persisted) instead of silently skipping
the gate, and interactive approval UI never spawns where nobody can
answer it (CI/Vitest)
- surfaces realtime subscription failures as typed errors: `composio
listen` prints one actionable line mentioning `composio login`, cleans
up temporary triggers, and exits 1 instead of dumping a captured defect
- tolerates newer connected-account payloads with a fallback decode
whose field allowlist keeps credential-bearing fields (`state`, `data`)
out of `link --list` output
- decodes persisted caches and GitHub release listings per entry, so one
corrupt or version-skewed entry drops only itself
- extracts API error details field by field, renders the deepest cause
in pretty errors, and accepts `null` optional strings in agent API
responses
---

**Stack** (re-slice of #3859;
each PR targets its parent and auto-retargets to `next` as parents
merge):

1. #3877 — guidelines + inert
rule groups
2. #3878 — `new Function()`
eval security fix
3. #3879 — behavioral fix
pack ← **this PR**
4. #3880 — platform-imports
rule + migration
5. #3881 — terminal-streams
rule + TerminalUI boundary
6. #3882 — descriptor seam +
zod→Schema
7. #3883 — test-tree lint +
deterministic suites
8. #3884 — typed error
boundaries + v4 seams
9. #3885 — try/catch+env ban
+ boundary ratchet

On stacked bases CI runs lint/build and the CLI Docker e2e job; unit
tests and typecheck are verified locally per PR and re-verified by full
CI when each PR is retargeted to `next`.
Alberto Schiabel (jkomyno) added a commit that referenced this pull request Jul 21, 2026
…3880)

This PR:
- activates the platform-imports rule group: `node:fs`, `node:path`,
`node:os`, and `child_process` are now ESLint-banned in CLI source
- migrates ~36 files to `FileSystem`/`Path`/`Command` from
`@effect/platform`, introducing the `NodeOs` and `DetachedProcess`
service boundaries and Effect-native run-companion module resolution
- keeps the sanctioned permanent boundaries (`node-os.ts`,
`detached-process.ts`, `run-subagent-output-mcp.ts`,
`cli-user-config.ts`, the child-process companion runtime) on inline `--
reason` disables
- files whose compliant rewrite belongs to a later slice carry temporary
inline disables naming the owning PR; a small sync bridge in
`run-companion-modules.ts` keeps the not-yet-migrated Promise-based
callers working until the typed-error slice removes it
- adds A-theme coverage: `NodeOs` platform info through Effect, session
artifacts via `FileSystem` with symlink and HOME-less handling,
skill-root discovery, and safe-output-path traversal guards
---

**Stack** (re-slice of #3859;
each PR targets its parent and auto-retargets to `next` as parents
merge):

1. #3877 — guidelines + inert
rule groups
2. #3878 — `new Function()`
eval security fix
3. #3879 — behavioral fix
pack
4. #3880 — platform-imports
rule + migration ← **this PR**
5. #3881 — terminal-streams
rule + TerminalUI boundary
6. #3882 — descriptor seam +
zod→Schema
7. #3883 — test-tree lint +
deterministic suites
8. #3884 — typed error
boundaries + v4 seams
9. #3885 — try/catch+env ban
+ boundary ratchet

On stacked bases CI runs lint/build and the CLI Docker e2e job; unit
tests and typecheck are verified locally per PR and re-verified by full
CI when each PR is retargeted to `next`.

## Review follow-up

- `DetachedProcess` now waits for Node’s `spawn` or `error` event, so
ENOENT/EACCES become typed failures instead of false success; focused
tests cover failure and success.
- the platform-import rule now rejects bare `fs`, `fs/*`, `os`, and
`path` specifiers as well as their `node:` forms; the two exposed path
imports now use Effect `Path`.
- run-companion self-repair again honors unprefixed `GITHUB_*` values
first, with `COMPOSIO_GITHUB_*` fallback and regression coverage for
both contracts.

Verification: CLI tests (95 files, 834 passed, 1 skipped), workspace
TypeScript typecheck, scoped ESLint (0 errors), CLI build, Prettier, and
`git diff --check`.
Alberto Schiabel (jkomyno) added a commit that referenced this pull request Jul 21, 2026
…3881)

This PR:
- activates the terminal-streams rule group:
`process.stdout`/`process.stderr` are now ESLint-banned in CLI source,
with `services/terminal-ui.ts` as the sole stream boundary (that file is
itself held to the try/catch + `process.env` restrictions early)
- extends `TerminalUI` with `capabilities` (TTY/interactivity/decoration
detection) and a raw `error()` writer, replacing `utils/stdio.ts`
(deleted)
- makes analytics dispatch Effect-native and delivers detached telemetry
through `DetachedProcess`, staying non-fatal on failure
- consolidates runtime debug logging into a `RuntimeDebugLogger` service
and rebuilds `cli-main` around `showUpdateNotice`/telemetry as Effects
with errors reported via `ui.error`
- the update checker reads terminal capabilities from the service and
keeps its fire-and-forget contract
---

**Stack** (re-slice of #3859;
each PR targets its parent and auto-retargets to `next` as parents
merge):

1. #3877 — guidelines + inert
rule groups
2. #3878 — `new Function()`
eval security fix
3. #3879 — behavioral fix
pack
4. #3880 — platform-imports
rule + migration
5. #3881 — terminal-streams
rule + TerminalUI boundary ← **this PR**
6. #3882 — descriptor seam +
zod→Schema
7. #3883 — test-tree lint +
deterministic suites
8. #3884 — typed error
boundaries + v4 seams
9. #3885 — try/catch+env ban
+ boundary ratchet

On stacked bases CI runs lint/build and the CLI Docker e2e job; unit
tests and typecheck are verified locally per PR and re-verified by full
CI when each PR is retargeted to `next`.
Alberto Schiabel (jkomyno) added a commit that referenced this pull request Jul 21, 2026
#3882)

This PR:
- activates the descriptor-seam rule group:
`commands/command-introspection.ts` becomes the only module allowed to
import `CommandDescriptor`/`Usage` from `@effect/cli` — the redesign
seam the Effect CLI v4 migration will reimplement
- routes root command assembly, preflight parsing, help routing, and
value-option collection through the seam helpers instead of descriptor
walks
- replaces the Zod-backed tool-input adapter with an internal
`@composio/json-schema-to-effect-schema` compiler backed by the
eval-free `@cfworker/json-schema` interpreter
- normalizes the OpenAPI and Composio extensions accepted by the
previous validator, while preserving typed `ToolInputValidationError`
failures, field paths, multi-error reporting, and typo suggestions
- extends the descriptor and Zod boundaries to reject static imports,
dynamic `import()`, and `require()` bypasses
- adds compiler parity tests plus CLI regression coverage for multiple
simultaneous field and unknown-key failures
- introspection tests pin that the helpers yield identical descriptors
and usage strings

---

**Stack** (re-slice of #3859;
each PR targets its parent and auto-retargets to `next` as parents
merge):

1. #3877 — guidelines + inert
rule groups
2. #3878 — `new Function()`
eval security fix
3. #3879 — behavioral fix
pack
4. #3880 — platform-imports
rule + migration
5. #3881 — terminal-streams
rule + TerminalUI boundary
6. #3882 — descriptor seam +
Zod→Schema ← **this PR**
7. #3883 — test-tree lint +
deterministic suites
8. #3884 — typed error
boundaries + v4 seams
9. #3885 — try/catch+env ban
+ boundary ratchet

On stacked bases CI runs lint/build and the CLI Docker e2e job; unit
tests and typecheck are verified locally per PR and re-verified by full
CI when each PR is retargeted to `next`.
Alberto Schiabel (jkomyno) added a commit that referenced this pull request Jul 21, 2026
…t suites (#3883)

This PR:
- un-ignores `ts/packages/cli/test` in the root ESLint config (fixtures
and the `test/__utils__` harness glue stay unlinted) and activates the
test rule group: no `Effect.run*` in suites (use
`it.effect`/`it.scoped`/`it.live`) and no `Date.now()`
- adds `test/__utils__/vitest.setup.ts`, pointing HOME and
`COMPOSIO_CACHE_DIR` at per-run temp directories so suites never touch
the developer's real `~/.composio` state
- migrates the remaining suites onto @effect/vitest runners with
layer-provided platforms, pins wall-clock reads via
`vi.setSystemTime`/TestClock-relative fixtures, and uses
`crypto.randomUUID()` for unique temp names
- lands the v3 Cause characterization suite pinning `reduceWithContext`
traversal over the six-constructor tree and rendered error output — the
executable equivalence oracle for the effect v4 migration
- tests arriving in later stack PRs must comply from birth
---

**Stack** (re-slice of #3859;
each PR targets its parent and auto-retargets to `next` as parents
merge):

1. #3877 — guidelines + inert
rule groups
2. #3878 — `new Function()`
eval security fix
3. #3879 — behavioral fix
pack
4. #3880 — platform-imports
rule + migration
5. #3881 — terminal-streams
rule + TerminalUI boundary
6. #3882 — descriptor seam +
zod→Schema
7. #3883 — test-tree lint +
deterministic suites ← **this PR**
8. #3884 — typed error
boundaries + v4 seams
9. #3885 — try/catch+env ban
+ boundary ratchet

On stacked bases CI runs lint/build and the CLI Docker e2e job; unit
tests and typecheck are verified locally per PR and re-verified by full
CI when each PR is retargeted to `next`.
Alberto Schiabel (jkomyno) added a commit that referenced this pull request Jul 21, 2026
… seams (#3884)

This PR:
- converts every remaining thunk-form `Effect.tryPromise` in CLI source
to a purpose-named `Data.TaggedError` preserving its cause —
`UnknownException` no longer surfaces from `src/` — and switches
best-effort recoveries from `catchAll` to `catchTag`
- isolates the `Symbol.for('effect/SpanAnnotation')` internal read
behind a shape-validating adapter, with a canary test that fails a real
effect through the runtime so an `effect` upgrade that drops the
mechanism breaks CI instead of silently losing span timelines
- decodes trigger-log payloads once at the API boundary through a
tolerant schema documenting camelCase/snake_case/meta-nested alias
precedence
- deduplicates the per-entry tolerant decoders into
`utils/collect-decoded-entries.ts`, replaces locally re-declared record
schemas with `JsonRecordSchema`, deletes the unused `EnvLangDetector`
service, and removes the temporary sync bridge from the platform-imports
slice
- the largest PR of the stack by design: generic refactors land late so
the fix and rule slices before it stay small; one commit per sub-theme
keeps `git show` reviewable
---

**Stack** (re-slice of #3859;
each PR targets its parent and auto-retargets to `next` as parents
merge):

1. #3877 — guidelines + inert
rule groups
2. #3878 — `new Function()`
eval security fix
3. #3879 — behavioral fix
pack
4. #3880 — platform-imports
rule + migration
5. #3881 — terminal-streams
rule + TerminalUI boundary
6. #3882 — descriptor seam +
zod→Schema
7. #3883 — test-tree lint +
deterministic suites
8. #3884 — typed error
boundaries + v4 seams ← **this PR**
9. #3885 — try/catch+env ban
+ boundary ratchet

On stacked bases CI runs lint/build and the CLI Docker e2e job; unit
tests and typecheck are verified locally per PR and re-verified by full
CI when each PR is retargeted to `next`.
Alberto Schiabel (jkomyno) added a commit that referenced this pull request Jul 21, 2026
…y ratchet (#3885)

This PR:
- activates the final rule group: `TryStatement` and `process.env` are
now ESLint-banned in CLI source, completing the enforcement ramp started
in the first PR of this stack; the env guard covers dot access, bracket
access, and object destructuring
- migrates the residual files (auth-config create, trigger create,
toolkit search, runtime debug flags, master detection, import-time UI
setup) off try/catch and ad-hoc env reads
- every surviving violation sits at a declared runtime boundary (bin.ts
bootstrap, child-process companion runtime, import-time UI setup,
environment writes and whole-environment enumeration, spawn-time env
handshakes) with an inline `// eslint-disable-next-line <rule> --
<reason>` justification
- adds `eslint-boundaries.json` — the checked-in manifest of the 46
sanctioned disables across 22 files — and `validate:boundaries` (part of
`pnpm test`), which scans both `.ts` and `.tsx` sources and fails on
undeclared, missing, relocated, bare, or file-wide disables
- pins each registered boundary by rule, justification, and governed
next-line target, with regression coverage for TSX discovery, same-file
retargeting, and duplicate semantic sites
- documents the Effect Boundary Policy in the CLI `AGENTS.md` and the
`cli-command` skill reference, and drops the gitleaks test-file
allowlist
---

**Stack** (re-slice of #3859;
each PR targets its parent and auto-retargets to `next` as parents
merge):

1. #3877 — guidelines + inert
rule groups
2. #3878 — `new Function()`
eval security fix
3. #3879 — behavioral fix
pack
4. #3880 — platform-imports
rule + migration
5. #3881 — terminal-streams
rule + TerminalUI boundary
6. #3882 — descriptor seam +
zod→Schema
7. #3883 — test-tree lint +
deterministic suites
8. #3884 — typed error
boundaries + v4 seams
9. #3885 — try/catch+env ban
+ boundary ratchet ← **this PR**

On stacked bases CI runs lint/build and the CLI Docker e2e job; unit
tests and typecheck are verified locally per PR and re-verified by full
CI when each PR is retargeted to `next`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant