Skip to content

feat(3.1.1): drop build shims, add publint to CI verify#1

Merged
codeweiz merged 1 commit into
mainfrom
feat/3.1.1-script-cleanup
May 27, 2026
Merged

feat(3.1.1): drop build shims, add publint to CI verify#1
codeweiz merged 1 commit into
mainfrom
feat/3.1.1-script-cleanup

Conversation

@codeweiz
Copy link
Copy Markdown
Owner

@codeweiz codeweiz commented May 27, 2026

Summary

3.1.1 release-pipeline cleanup. Two pieces:

  • Delete two shim scripts. scripts/build-cli.ts (12 lines) and scripts/build-native.ts (9 lines) were each a single spawnSync wrapping one command. build:native was already inlined as a package.json script; build:cli is now too. Net -21 lines.
  • Add publint to the CI verify job. The script iterates packages/* and apps/* via shell glob — same shape bun resolves workspaces — so new packages get manifest-lint coverage without editing a list. The 3.1.0 verify regression was exactly the class of bug this catches at this layer.

Deliberately not doing in this PR (covered in the design discussion):

  • Replacing release-publish-npm.ts with bunx changeset publish@changesets/cli@2.31.0 calls npm publish, which does not rewrite workspace:* references in our manifests. bun publish (current) does. Documented at docs/STATUS.md:266.
  • Adopting changesets/action — would still need a separate Cargo.toml lockstep step, so layering it on top of release.ts adds complexity rather than removes it.
  • Deleting the topo-sort in workspace-packages.ts — algorithmically derived, not hand-maintained, and keeps publish window safe against half-published states. Codex review endorsed keeping it.

Test plan

  • `bun run check` — 27/27 packages typecheck
  • `bun test` — 514 pass / 0 fail
  • `bun run boundaries` — 314 modules / 0 violations
  • `bun run publint` — all 14 workspace packages green at baseline
  • `bun scripts/smoke-release.ts --packed` — packed tarball installs and runs under npm
  • CI verify job picks up the new `publint` step on push

🤖 Generated with Claude Code

Two scripts deleted (`build-cli.ts` 12 lines, `build-native.ts` 9 lines) —
both were `spawnSync` wrappers around a single command. `build:native` was
already inlined in package.json#scripts; `build:cli` is now too.

publint added to .github/workflows/release.yml verify job. The script
iterates `packages/*` and `apps/*` via shell glob — same shape bun resolves
workspaces — so new packages get manifest-lint coverage without editing a
list. The 3.1.0 verify regression was exactly the class of bug this catches
at this layer (missing-from-publish-list shows up as a pack/manifest issue).

Baseline: all 14 workspace packages pass publint clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@codeweiz codeweiz merged commit 8f561e7 into main May 27, 2026
2 checks passed
@codeweiz codeweiz deleted the feat/3.1.1-script-cleanup branch May 27, 2026 01:58
codeweiz added a commit that referenced this pull request May 28, 2026
When the model's context fills up, chances-cli now transparently
shrinks it. Three triggers (manual `/compact` + threshold + Anthropic-
only reactive overflow), two-stage (allowlist tool-output pruning
first then LLM summary). NO idle (pi + claude-code both skip;
oh-my-pi recommends v1 skip too).

`Compactor` (~270 LoC) in `packages/core/src/compaction/`:

- `shouldCompact({ lastRequestInputTokens, model })` — pure
  predicate, threshold = `contextWindow − max(16384, floor(window
  × 0.10))`. Signal source is per-stream `lastRequestInputTokens`,
  NOT the turn aggregate (a multi-step tool loop would otherwise
  trip threshold too early; codex Round-1 MUST-FIX #1).
- `compact(reason, signal)` — two-stage. Stage 1 prunes
  `tool-result.output` for allowlisted tools (default
  `compactableTools = ["bash", "grep"]`), preserving `callId` /
  `name` / `ok` / `type` so Anthropic `tool_use ↔ tool_result`
  pairing survives (codex Round-1 MUST-FIX #2). Stage 2 runs
  `summarizeConversation` + `SessionManager.compactTurns(summary,
  keepLastN=5)`.
- 3-strike circuit breaker. Manual `/compact` + Anthropic-overflow
  bypass `enabled:false` AND tripped breaker (a 413 is a hard
  signal we must try; codex Round-2 MUST-FIX #1). Manual additionally
  resets the breaker on success.
- Cancellation classification via `signal.aborted` (the AI-SDK
  wraps `AbortError` as a `provider:error` event, so naive
  `AppError(Cancelled)` matching silently absorbed user Ctrl-C
  as a compactor fault; codex Round-2 MUST-FIX #2).
- CJK-aware token estimator `max(char/4, cjk/2)` so Doubao 32K /
  Kimi 32K / DeepSeek 65K windows stay sane (codex Round-1
  SHOULD-FIX #1).

Engine wiring (`packages/core/src/engine.ts`):

- Per provider-stream-call, tracks `attemptLastInputTokens` (reset
  per attempt, captured on success). After the outer loop,
  `lastRequestInputTokens` holds the LAST successful stream's last
  `usage.inputTokens`.
- New emit order: `appendTurn` → `ack notifications` → `usage:turn`
  → `await compact("threshold")` → `token.throwIfCancelled()` →
  `turn:end`. TUI subscribers never see "turn over" while session
  is still mutating; a cancelled compaction propagates to the
  caller (codex Round-1 MUST-FIX #4 + Round-2 MUST-FIX #2).
- Anthropic-only reactive overflow recovery: detects `prompt is
  too long` / `request_too_large` / `maximum.*context.*length`
  AND `route.adapter.id === "anthropic"`, runs `compact("overflow")`,
  retries the inner stream once. `recoveredFromOverflow` per-turn
  flag prevents loops. Other 10 providers stay deferred (codex
  Round-1 SHOULD-FIX #4).
- `suppressLifecycleEvents` (set by subagent engines) suppresses
  `usage:turn` too — child engines stay invisible on the parent
  bus's lifecycle channel.

New `SessionManager.pruneTurns(keepRecentTurns, transform)` for
in-place per-turn rewriting. Honors keep-recent at turn-boundary
granularity. Idempotent if the transform is.

New bus events: `usage:turn` (carries `lastRequestInputTokens` —
3.6 OTel will consume this), `compaction:start`, `compaction:end`.

`apps/cli/src/boot/create-app.ts` — `buildEngine` constructs the
`Compactor` only when `ctx.backgroundTasks` is set (interactive
chat gating proxy: `-p` one-shot mode has no "next turn" for
threshold compaction). Exposes via `ctx.compactor`. Subagent
engines never receive one.

`apps/cli/src/slash/compact.ts` — refactored to route through
`ctx.compactor.compact("manual", ...)` when present. Defensive
direct-summarizer fallback preserved for test setups that build
`BuiltinSlashDeps` without going through `buildEngine`. Slash
signal plumbing deferred to 3.x.later (codex Round-1 MUST-FIX #3:
`SlashCommand.run` has no `AbortSignal`).

New `CompactionConfig` under `AgentConfig`: 5 knobs (`enabled`,
`reserveTokens`, `keepRecentTurns`, `pruneMinSavings`,
`compactableTools`) + 4 env overrides (`compactableTools` is
config-only — comma-parsing of tool names invites quoting bugs).

Prune marker sanitizes the display name (controls stripped,
64-char cap) so a malicious plugin tool name can't corrupt the
synthetic marker sent back to the model — `part.name` itself
preserved unchanged for provider tool_use pairing (codex Round-2
SHOULD-FIX #3).

Codex review folded into the same commit:

- Round-1 (design, 4 MUST-FIX + 4 SHOULD-FIX + 2 NICE): idle
  deferred; `lastRequestInputTokens` not turn-aggregate; prune
  output-field-only; slash uncancellable; usage:turn → await
  compact → turn:end ordering; CJK estimator; allowlist not
  denylist; circuit-breaker scope honest + manual bypasses;
  Anthropic-only overflow; module layout OK; single-huge-turn
  + second-compaction edge tests.
- Round-2 (impl, 2 MUST-FIX + 3 SHOULD-FIX + 2 NICE):
  `enabled:false` was disabling manual too; cancellation via
  `signal.aborted` because AI-SDK wraps AbortError as provider
  error; stats/doctor metrics promised but unimplemented —
  explicitly deferred to 3.x.later; overflow test wasn't
  pinning `lastRequestInputTokens` semantics; prune marker
  leaked raw plugin tool names; stale "denylist" wording fixed;
  single-huge-turn test was vanity, strengthened.

`@chances-ai/core` promoted `@chances-ai/config` from devDep to
runtime dep (compactor `import type { CompactionConfig }`).
`.dependency-cruiser.cjs` allows `core → config`.

843 tests / 0 fail / 2107 expect calls. 29/29 typecheck. 405
boundary modules / 0 violations.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
codeweiz added a commit that referenced this pull request May 31, 2026
…, serve skeleton

Phase A (M0 foundations) of the unified multi-platform client (docs/6.1). v17
ships M0+M1 together as one major; this lands the M0 half (staged for the
eventual v17.0.0 release with M1).

- ui-core: browser-safe extraction of the 6 pure view modules from tui
  (highlight-to-segments/diff-model/view-model/frame-scheduler/tool-line/theme);
  process.*/node:os injected; tui re-exports via a thin theme shim — zero
  behavior change, apps/cli untouched.
- design-system: Tamagui (@tamagui/core 2.1.0) skeleton; tokens/themes derived
  from ui-core (single source, res #1); compiles on Bun + tsc -b + React 19 and
  runs createTamagui in a no-DOM runtime (TOP RISK resolved).
- serve: `chances serve` relay skeleton — loopback-only (no 0.0.0.0 until M3
  auth) + GET /health; reuses ui-core highlightToSegments so clients ship no
  highlighter (res #2).
- cruiser: no-client-to-tui (covers apps/{web,desktop,mobile}) +
  ui-core-is-browser-safe (dependencyTypes:core + grep-based global scan) +
  design-system-consumers, all with regression fixtures.
- AppEvent: compile-time-exhaustive serialization fixtures
  (satisfies Record<AppEventType, AppEvent>) + round-trip + golden snapshot.
- docs: STATUS/ROADMAP reflect v17 M0 progress (Phase A done, Phase B next).

codex R1 folded: 2 MUST (loopback-only bind; ChatViewModel home injection to
preserve ~-folding) + 3 SHOULD (browser-safe globals scan; apps/{web,desktop,
mobile} cruiser coverage; AppEvent edge fixtures).

Gates: test 46, typecheck 44, boundaries 829/0, cargo fmt clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
codeweiz added a commit that referenced this pull request Jun 3, 2026
… (Step 3)

Focused port of oh-my-pi pi-natives grep.rs: a #[napi] async grep() on the
libuv pool (so a serve fan-out can't freeze the event loop), keeping the
load-bearing pieces and the facade's option set, dropping pi's context
lines / type filters / output modes / in-memory search+has_match (no
consumer yet — R1 #5).

Native (src/grep.rs):
- ripgrep stack: grep-regex + grep-searcher + globset filter + ignore
  parallel walker (gitignore-aware, hidden included, node_modules/.git
  pruned); per-128 heartbeat; mmap for files > 128 KiB, skip > 4 MiB.
- two-step sanitize: sanitize_braces (`${x}`→`$\{x\}`, valid quantifiers
  and \p{}/\x{}/\u{} pass through) + escape_unescaped_parentheses retry on
  a group-syntax error (so `fn(` searches literally instead of erroring).
- matched line text trimmed both ends to match jsGrep; results sorted by
  (path, line) then truncated to maxResults.

Facade (breaking, async flip): grep(pattern, root, opts) is now
Promise<GrepMatch[]>; the native addon returns {path,lineNumber,line} and
the facade adapts to the UNCHANGED {file,line,text} contract (R1 #2). Added
addonOrForced() — the unified CHANCES_NATIVE_FORCE_FALLBACK switch (R1 #8).
Repoint: grep.ts awaits; native package grep tests await + go async.

Tests: 7 Rust unit (sanitize/paren-recovery/relative+line/per-file-limit/
glob/gitignore) + 4 native↔JS facade equivalence (literal / regex+case+glob
/ perFileLimit+maxResults / sanitized brace; non-multiline on a clean tree
where both walkers agree — §9 #1/R1 #22). Multiline divergence documented:
the native stack coalesces ADJACENT multiline match-blocks (ripgrep
semantics, verified) — the "multiple matches" test spaces blocks out; the
grep-tool invalid-regex test now uses an unclosed char class (malformed on
both backends) since bare parens are recovered.

Gates: cargo fmt + clippy -D warnings clean + cargo test 47/0 · native
61/0 · check 19/19 · boundaries 1057/0 · full test 19/19. .node 752 KiB →
2.3 MiB (ripgrep stack). deps: globset/grep-matcher/grep-regex/
grep-searcher/memmap2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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