Skip to content

Align persistence defaults, harden prediction contracts, and clean up package surface#105

Open
Copilot wants to merge 2 commits into
mainfrom
copilot/review-codebase-for-optimizations
Open

Align persistence defaults, harden prediction contracts, and clean up package surface#105
Copilot wants to merge 2 commits into
mainfrom
copilot/review-codebase-for-optimizations

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 30, 2026

This change closes the Phase 1 contract gaps in core behavior and removes a few packaging ambiguities that were drifting from the documented API surface. It aligns persistence defaults, makes predictive prefetching fail closed, standardizes state normalization validation, and formalizes which packages/exports are actually supported.

  • Core contract alignment

    • Unified the default persistence key across IntentManager, IntentEngine, createBrowserIntent(), and IntentManager.createAsync() to passive-intent.
    • Centralized tracked-state normalization/validation so IntentManager and IntentEngine now apply the same rules for:
      • built-in route normalization
      • custom stateNormalizer
      • empty-string drops
      • non-string return rejection
  • Prediction safety

    • Changed predictNextStates() to fail closed when sanitize is omitted.
    • Preserved ergonomic call sites by keeping the parameter optional at the type boundary, but returning [] and surfacing VALIDATION when no sanitizer is provided.
    • Updated React wrapper paths and docs to reflect the explicit-sanitizer contract.
  • Public package surface

    • Added a formal @passiveintent/core/plugins/web export for the browser microkernel adapters.
    • Added package verification coverage for that subpath so docs/tests/package contents stay aligned.
  • Repo/package cleanup

    • Removed archived placeholder packages from active npm workspaces and release flow:
      • packages/angular
      • packages/vanilla
    • Kept them as archived placeholders in-tree, but no longer as active workspaces.
  • Docs/examples/test alignment

    • Updated docs, demos, and tests to reflect:
      • the unified storage key default
      • required sanitizer semantics for predictions
      • standardized stateNormalizer behavior
      • the supported web-plugin import path

Example of the tightened prediction contract:

const intent = createBrowserIntent();

const hints = intent.predictNextStates(0.3, (state) => {
  return state.startsWith('/docs') || state.startsWith('/pricing');
});

// No sanitizer => fail closed, returns []
const unsafe = intent.predictNextStates(0.3);

@purushpsm147 purushpsm147 marked this pull request as ready for review April 30, 2026 04:50
@purushpsm147 purushpsm147 self-requested a review as a code owner April 30, 2026 04:50
Copilot AI review requested due to automatic review settings April 30, 2026 04:50
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR tightens and standardizes core runtime contracts (persistence defaults, state normalization, prediction safety) while also formalizing the supported package surface (notably @passiveintent/core/plugins/web) and removing archived packages from active workspaces.

Changes:

  • Unified default persistence key/namespace handling and centralized tracked-state normalization/validation across IntentManager and IntentEngine.
  • Hardened prediction APIs to fail closed when sanitize is omitted, and updated React hooks/tests/docs accordingly.
  • Added an official @passiveintent/core/plugins/web export (plus package verification), and removed archived packages from active npm workspaces.

Reviewed changes

Copilot reviewed 29 out of 30 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/vanilla/package.json Marks vanilla package as archived placeholder (non-workspace).
packages/angular/package.json Marks angular package as archived placeholder (non-workspace).
package.json Restricts workspaces to core/react/remix + demos.
package-lock.json Updates lockfile to reflect workspace removal and dependency graph changes.
packages/core/src/defaults.ts Introduces shared defaults (DEFAULT_STORAGE_KEY, DEFAULT_NAMESPACE).
packages/core/src/engine/config-normalizer.ts Uses centralized defaults for persistence/namespace normalization.
packages/core/src/factory.ts Aligns createBrowserIntent() storage key default with shared constant.
packages/core/src/utils/tracked-state.ts Adds centralized tracked-state normalization/validation helper.
packages/core/src/engine/intent-manager.ts Uses shared tracked-state resolution; makes predictNextStates fail closed without sanitizer.
packages/core/src/engine/intent-engine.ts Aligns default storage key; uses shared tracked-state resolution.
packages/core/src/types/microkernel.ts Updates docs for IntentEngineConfig.storageKey default.
packages/core/src/types/events.ts Updates stateNormalizer contract docs (reject non-string).
packages/core/src/plugins/web/index.ts Adds a formal web-plugins barrel export.
packages/core/package.json Exposes ./plugins/web export and broadens published dist/** file globs.
packages/core/tsup.config.ts Builds the new src/plugins/web/index.ts entrypoint.
packages/core/scripts/verify-package.mjs Extends package smoke test to import/instantiate web plugins via subpath export.
packages/core/tests/unit-fast.test.mjs Updates and adds unit coverage for sanitize-required predictions and stricter stateNormalizer behavior.
packages/core/tests/microkernel.test.mjs Updates microkernel test imports and persistence key usage.
packages/core/cypress/e2e/intent.cy.ts Updates e2e calls to predictNextStates to pass a sanitizer.
packages/react/src/hooks.ts Ensures internal calls to predictNextStates supply a sanitizer or deny-all fallback.
packages/react/src/types.ts Updates public API docs for the stricter prediction contract.
packages/react/src/tests/use-passive-intent.test.ts Updates tests to call predictNextStates with sanitizer.
packages/react/tests/ssr-safety.test.ts Updates SSR safety tests to call predictNextStates with sanitizer.
packages/react/README.md Updates React docs for sanitizer-required prediction semantics.
packages/core/README.md Updates core docs for unified storage key and stricter prediction/stateNormalizer semantics.
packages/core/docs/architecture.md Updates architecture docs to reflect sanitizer-required predictions + new plugins export path.
README.md Clarifies repo contains active packages + archived in-tree placeholders.
demo/DEMO.md Updates demo documentation to reflect new prediction signature.
demo-react/DEMO.md Updates demo-react documentation to reflect new prediction signature.
CALIBRATION_GUIDE.md Updates calibration guide example to include sanitizer in prediction calls.
Comments suppressed due to low confidence (2)

packages/core/docs/architecture.md:876

  • This section now describes predictNextStates(threshold, sanitize) as already filtered by the required sanitize allowlist, but the compliance guardrail immediately below still says developers “MUST filter the states returned by predictNextStates()”. Consider updating the guardrail to explicitly refer to providing a restrictive sanitize allowlist (and/or additional filtering) so it doesn’t imply post-filtering a call that can be made without a sanitizer.
`intent.predictNextStates(threshold, sanitize)` returns `{ state: string; probability: number }[]` — the outgoing states from the current node whose transition probability exceeds `threshold`, filtered through your required `sanitize` allowlist and sorted descending by probability. After 5–10 navigations, the Markov graph has enough signal to predict the next page with high accuracy on well-worn paths (e.g., `/dashboard` → `/billing` → `/upgrade`). Combining this with a framework router's prefetch API means those bundles are already in the browser's memory before the user clicks.

The result is not just a performance gain — it is a **retention signal**. A fast, responsive app has measurably lower bounce rates. PassiveIntent turns behavioral data that was already being collected for churn detection into a free performance dividend.

> ⚠️ **COMPLIANCE GUARDRAIL — REQUIRED READING FOR ALL DEVELOPERS**

packages/react/src/types.ts:63

  • The JSDoc says a sanitize predicate is required, but the UsePassiveIntentReturn.predictNextStates type still marks sanitize as optional. Either make the parameter required in the type (breaking change) or adjust the comment to reflect the actual signature while still documenting the fail-closed runtime behavior.
  /**
   * Returns `{ state, probability }[]` sorted descending by probability for
   * all next states whose transition probability exceeds `threshold` (default
   * `0.3`). A `sanitize` predicate is required so callers fail closed unless
   * they explicitly approve returned routes for their use case.
   *
   * Returns an empty array during SSR or before the first `track()` call.
   */
  predictNextStates: (
    threshold?: number,
    sanitize?: (state: string) => boolean,
  ) => { state: string; probability: number }[];

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

* during the same render return the same `number` (refs don't change
* mid-render; `Object.is` comparisons on scalars are exact).
* - The computation is **pure and cheap** — a single `predictNextStates(0)`
* - The computation is **pure and cheap** — a single `predictNextStates(0, allowAll)`
Comment on lines 1195 to 1199
| `track` | `(event: string) => void` | no-op before mount / after unmount |
| `on` | `(event, handler) => () => void` | returns a NOOP unsubscribe on SSR |
| `getTelemetry` | `() => PassiveIntentTelemetry` | empty object cast before mount |
| `predictNextStates` | `(threshold?, sanitize?) => { state: string; probability: number }[]` | `[]` before first mount |
| `predictNextStates` | `(threshold?, sanitize) => { state: string; probability: number }[]` | `[]` before first mount |
| `hasSeen` | `(route: string) => boolean` | `false` before first mount |
ContinuousGraphModel,
LocalStorageAdapter,
MouseKinematicsAdapter,
} from '../dist/plugins/web/index.js';
Comment thread packages/core/README.md
Comment on lines 413 to 416
| Method | Signature | Description |
| ---------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `predictNextStates` | `(threshold?: number, sanitize?: (s: string) => boolean) => { state, probability }[]` | Top-N Markov predictions above `threshold` (default `0.3`). Always provide a `sanitize` guard in production to exclude sensitive routes. |
| `predictNextStates` | `(threshold?: number, sanitize: (s: string) => boolean) => { state, probability }[]` | Top-N Markov predictions above `threshold` (default `0.3`). `sanitize` is required so prediction consumers fail closed unless they explicitly approve returned routes. |
| `hasSeen` | `(state: string) => boolean` | Bloom filter membership test — O(k), no false negatives. |
Comment thread packages/react/README.md
Comment on lines 171 to 177
| Method | Signature | Notes |
| ------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------ |
| `track` | `(state: string) => void` | Records a page view or custom state transition. |
| `on` | `(event, listener) => () => void` | Typed subscription API. Returns a no-op unsubscribe during SSR. |
| `getTelemetry` | `() => PassiveIntentTelemetry` | Returns a fully shaped zero-value object until the engine is live. |
| `predictNextStates` | `(threshold?, sanitize?) => { state: string; probability: number }[]` | Sorted Markov predictions. |
| `predictNextStates` | `(threshold?, sanitize) => { state: string; probability: number }[]` | Sorted Markov predictions. `sanitize` is required so callers fail closed by default. |
| `hasSeen` | `(state: string) => boolean` | Bloom filter membership test. |
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.

3 participants