When two packages need the same state, do not make one package read another package’s private object. Put the shared product fact in Bitfield and let each package read the view it needs.
A file tree chooses a file, then the editor, breadcrumbs, command palette, and AI context panel all need the active file. That state is no longer a private file-tree detail. It is product coordination state.Every package should see the same selected file without importing the file tree, sharing memory with it, or duplicating its private store.Traditional app shape
// file-tree/store.ts
export const fileTreeStore = {
selectedFile: '/app/components/Header.tsx',
};
// editor/open-file.ts
import { fileTreeStore } from '../file-tree/store';
export function openEditor() {
return openFile(fileTreeStore.selectedFile);
}
This works until it does not. The editor now imports the file tree store. If the file tree changes its store shape, moves packages, or becomes a different UI, the editor breaks.Bitfield shape
Bitfield state:
selected-file = /app/components/Header.tsx
file tree updates selected-file
file editor reads selected-file
breadcrumbs read selected-file
AI context reads selected-file
No package reads the file tree’s private object. They all read the shared Bitfield fact.Code translation
Traditional path
import { fileTreeStore } from '../file-tree/store';
const selected = fileTreeStore.selectedFile;
Bitfield path
const selectedFile = readInput('selected-file');
React adapter example
import { useBitfieldData } from '@bitfield/runtime-kit/react';
export function FileEditorShell() {
const selectedFile = useBitfieldData<string>('selected-file');
if (selectedFile.loading) return <p>Loading file</p>;
if (selectedFile.error) return <p>Could not read the selected file.</p>;
if (!selectedFile.data) return <p>No file selected.</p>;
return <Editor filePath={selectedFile.data} />;
}
React is only the adapter shown here. The same rule applies to any shell: read the named Bitfield input instead of importing the file tree.Review checklist
| Check | Good answer |
|---|
| Does more than one package need the value? | It is a shared product fact or data name, not one package’s private object. |
| Does the code import another package store? | Replace it with a named read. |
| Is the value only visual? | Keep it private instead of sharing it. |
| Can a non-React shell read the same value? | Yes. The name is Runtime Kit-level, not React-only. |
More examples
| Shared product fact | Traditional mistake | Bitfield shape |
|---|
| Current project | Every panel imports projectStore.current. | Packages read current-project. |
| Selected agent | Keyboard, chat, and timeline import one agent store. | Packages read selected-agent. |
| Active conversation | Chat, sidebar, and notifications pass props through the shell. | Packages read active-conversation. |
| Preview status | Dashboard imports preview service private code. | Packages read project-preview-status. |
One product fact, many readers
The mental model is simple: a product fact is not owned by the first UI that touched it. The file tree might be the place where a user selected a file, but “the selected file” is not the file tree’s private object once the editor, AI panel, breadcrumbs, and command palette all need it.Traditional code turns the first UI into the source every other package imports:import { fileTreeStore } from '../file-tree/store';
export function buildContext() {
return { filePath: fileTreeStore.selectedFile };
}
Bitfield code turns the fact into a named product value:const selectedFile = readInput('selected-file');
React adapter example
const selectedFile = useBitfieldData<string>('selected-file');
The AI panel does not import the tree, command palette, recent-files list, command-line launch, or another device path. It reads the product fact it needs.Four shared-state situations
Selected file
Private file-tree read
import { selectedFile } from '../file-tree/state';
Read the public selected file
const selectedFile = readInput('selected-file');
Use this when multiple packages react to the same selected file.Current project
Private workspace read
import { currentProject } from '../workspace/project-store';
Read the public project
const project = readInput('current-project');
Use this when panels, package filters, build actions, and account surfaces all need the same project context.Selected agent
Private agent setter
selectedAgentStore.set(agentId);
Request the selected-agent change
await sendRequestToBitfieldTarget({
target: 'selected-agent.update',
payload: { agentId },
});
Then readers use the selected-agent product fact instead of importing the setter package.Notification mode
Private notification object
import { notificationSettings } from '../notifications/settings';
notificationSettings.mode = 'quiet';
Request the mode change
await sendRequestToBitfieldTarget({
target: 'notification-mode.update',
payload: { mode: 'quiet' },
});
Readers should observe the public mode value. Callers should not mutate notification package objects.What not to store as shared state
| Value | Keep private or share? | Reason |
|---|
| Row hover | Private | It is only visual. |
| Editor scroll position | Usually private | It belongs to the current surface unless another package truly needs it. |
| Selected file | Share | Editor, AI context, breadcrumbs, and commands may need it. |
| Current project | Share | It changes what many packages show. |
| Modal open state | Usually private | The modal package can own it unless other packages coordinate around it. |
| Notification mode | Share through a target and fact | Multiple callers can ask for the change, and multiple readers may show it. |
What this prevents
| Bad shape | Why it breaks | Better shape |
|---|
| Package A imports Package B’s store. | Package A now depends on Package B’s private code. | Package A reads a named Bitfield input. |
| Each package keeps its own copy. | Copies drift and disagree. | Packages read the same Bitfield fact. |
| The shell passes the value through every layer. | The shell becomes the coordinator for shared product state. | Packages read the shared fact directly. |
| A package stores every click in Bitfield. | Temporary UI noise becomes product state. | Only coordination state goes into Bitfield. |
Common mistake
Do not put every piece of state in Bitfield.private hover state -> keep private
dropdown open state -> keep private
draft field text before submit -> usually private
selected file used by many packages -> Bitfield state
current project used by many packages -> Bitfield state
Review check
When code imports another package’s store, stop. Ask whether the value is a shared product fact. If yes, read a Bitfield input. If no, keep it private to the package that uses it.Next