Align persistence defaults, harden prediction contracts, and clean up package surface#105
Open
Copilot wants to merge 2 commits into
Open
Align persistence defaults, harden prediction contracts, and clean up package surface#105Copilot wants to merge 2 commits into
Copilot wants to merge 2 commits into
Conversation
Agent-Logs-Url: https://github.com/passiveintent/core/sessions/c67fad16-12cd-428a-a0e3-663ec1ab11b1 Co-authored-by: purushpsm147 <[email protected]>
Agent-Logs-Url: https://github.com/passiveintent/core/sessions/c67fad16-12cd-428a-a0e3-663ec1ab11b1 Co-authored-by: purushpsm147 <[email protected]>
Copilot created this pull request from a session on behalf of
purushpsm147
April 30, 2026 04:49
View session
Contributor
There was a problem hiding this comment.
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
IntentManagerandIntentEngine. - Hardened prediction APIs to fail closed when
sanitizeis omitted, and updated React hooks/tests/docs accordingly. - Added an official
@passiveintent/core/plugins/webexport (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 requiredsanitizeallowlist, but the compliance guardrail immediately below still says developers “MUST filter the states returned bypredictNextStates()”. Consider updating the guardrail to explicitly refer to providing a restrictivesanitizeallowlist (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
sanitizepredicate is required, but theUsePassiveIntentReturn.predictNextStatestype still markssanitizeas 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 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 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. | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
IntentManager,IntentEngine,createBrowserIntent(), andIntentManager.createAsync()topassive-intent.IntentManagerandIntentEnginenow apply the same rules for:stateNormalizerPrediction safety
predictNextStates()to fail closed whensanitizeis omitted.[]and surfacingVALIDATIONwhen no sanitizer is provided.Public package surface
@passiveintent/core/plugins/webexport for the browser microkernel adapters.Repo/package cleanup
packages/angularpackages/vanillaDocs/examples/test alignment
stateNormalizerbehaviorExample of the tightened prediction contract: