@minhduydev/pi-core
v0.3.1
Published
Shared contract layer for the pi-* packages: one taggedDigest, one project identity, one set of event payloads with make*/parse* pairs, and the knowledge-signal/replay-port types. Zero runtime dependencies; ships compiled dist only.
Readme
@minhduydev/pi-core
The shared contract layer for the pi-* packages (pi-harness,
pi-subagents, pi-learning, pi-todo). One digest, one project identity,
one event vocabulary, one knowledge-signal shape.
What's new in 0.3.1
- Add a canonical
npm run checkentrypoint covering typecheck, build, declarations, ESM exports, and the complete test suite. - No runtime API or protocol changes;
PI_CORE_PROTOCOL_VERSIONremains1.
What's new in 0.3.0
- V2 learning intents separate launch-time claims from completion-time evidence, with canonical claim IDs and strict parsing.
- V2 context requests carry evidence-free intents while retaining the V1 pre-bound evidence contract.
- Verifier support receipts bind completion evidence to the exact claim, artifact, subject, and reviewer invocation.
The additions are backward-compatible; PI_CORE_PROTOCOL_VERSION stays 1. See CHANGELOG.md.
Why this package exists
An audit (2026-07-26) of the four packages found that their shared contract was duplicated instead of shared, and the duplicates had already diverged:
- 9 copies of
taggedDigest, one of which serialized{a: 1, b: undefined}differently from the other eight — so a producer's digest never matched the consumer's expectation, and 100% of raw payloads would have been silently dropped. - 3 algorithms for
projectKey— the identity that binds learning records to a project meant three different answers to "which project is this?". - 11 duplicated type names across the repos, at least 4 of which had drifted.
- A required
confidencefield that the producer never emitted and only a third package injected in place — install two packages without the third and the integration is a silent no-op.
pi-core makes that class of bug structural rather than behavioral:
producers emit through make*, consumers verify through parse*, both
imported from this package. Two sides cannot hash different preimages when
there is only one preimage function.
Rules
- Zero runtime dependencies. Only
node:crypto,node:fs,node:path. - Ships compiled
dist/only. Raw TS would break every non-jiti consumer (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPINGis a hard Node limit). peerDependencyin the four packages, neverdependency— if two copies resolve, digests can diverge again one level deeper. Each package callsassertPiCoreProtocolVersion(N)at startup to catch exactly that.- Parsers fail closed at the boundary. Required or partially-present protocol fields must be valid; known collections cannot be silently truncated or filtered. Unknown additive fields remain semantically ignored, but still count toward shared byte, depth, node, key, and array limits. Parsing is performed from a detached descriptor snapshot: inherited fields, accessors, sparse arrays, exotic objects, unsafe integers, non-canonical identifiers, and non-canonical UTC timestamps are rejected without invoking caller getters.
- Canonicalization is versioned by the digest tag (
sha256:v1:). The rules (sorted keys,undefined-valued keys omitted,nullkept) are bound by test intest/digest.test.js. Changing them requires a new tag. - Lifecycle axes must agree. Execution phase, child-reported semantic
outcome, proof result, review wait state, and terminal outcome remain
separate fields, but contradictory combinations are rejected. A failed
proof cannot be represented as terminal success, and a decision ID exists
only on an
awaiting-decisionsettlement.
Modules
| Module | Contents |
|---|---|
| digest | taggedDigest, canonical, TaggedSha256V1, validators |
| identity | resolveProjectIdentity, projectKey (git-remote-based) |
| claims | LearningClaimV1, LearningClaimIntentV2, UsageReceiptV1, make*/parse* |
| events | channel names, V1/V2 context requests, proof/accepted/served payloads, make*/parse*, clamps |
| knowledge | KnowledgeSignalV1, request/ack, StreamCursorV1, ReplayPort<T> |
| task-lifecycle | authoritative task-started/task-settled channels, semantic outcomes, bounded parsers |
| workflow | durable foundation verdict, reconcile checkpoint, and fourteen-section handoff contracts |
The binding model
requestDigest covers the producer's fields only and never changes after
emission. A downstream party that binds a request to a project identity adds
projectId / trustEpoch / sessionGeneration plus a separate
bindingDigest over {requestDigest, ...binding} via
withContextRequestBinding — a pure function returning a new value. Nothing
ever rewrites another package's payload in place, and nothing depends on
listener ordering.
