feat: Sentiness v2 global spine (Phase V1)#17
Merged
Conversation
Implementable spec (CLAUDE.md task template) for the v2 evolution: a thin global launcher (@sentiness/cli) + version-pinned engine in a global cache, zero node_modules in target projects, sentiness.lock for determinism, zoned config for polyglot monorepos, override->host->fetch(npm) tool resolution, and one cross-language proof (check-clippy, detect-only). Drops the v1 node_modules model (no dual-mode). Supersedes docs/multi-language-spec.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bite-sized TDD plan for spec tasks TV1.1-TV1.7: config v2, cache + artifact store, sentiness.lock, registry-from-cache, npm-tool bundling, install command, thin launcher, plus a dogfood smoke task. V2/V3/V4 get their own plans. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace v1 config fixtures (schemaVersion 1.0, enabled: true) with valid v2 fixtures across reporter, runner, baseline, check, install-skill, pending, registry, doctor, and init-config test files. Export a DEFAULT_CONFIG constant from config.ts (minimal v2 ResolvedConfig with empty checks) so that programmatic test fixtures can spread it instead of constructing the full shape inline. Remaining failures deferred to module-logic tasks: - doctor.test.ts (3): registry.ts still gates on checkConfig.enabled (v1) - init-config.test.ts (5): same registry.ts enabled-gate - init.test.ts (4): init.ts writes schemaVersion 1.0 / enabled:true configs Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…dules CheckRegistry.fromConfig (which resolved @sentiness/check-<id> from the project's node_modules) is replaced by fromResolved, which imports each check from its cache slot (versioned entries) or a local package (path-linked entries). Callers go through a new buildRegistry helper; command test fixtures move from node_modules packages to path-linked checks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Each npm-tool check (biome, eslint, knip, jscpd) now declares its tool as an exact dependency so the tool is pinned by the check version and materialized into the check's cache slot. CheckContext/ExecFileOptions gain an optional binPaths, and NodeProcessRunner prepends those slot .bin dirs ahead of the cwd node_modules/.bin chain on PATH. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rypoint install resolves the engine + check version ranges to exact versions, writes sentiness.lock, and materializes the cache slots (npm tools included via the shared EXTERNAL_TOOL_PACKAGES map). --frozen requires an existing lock that satisfies the config. The engine entrypoint now extracts --cache-root from argv and threads it through CommandDeps; buildRegistry resolves slots under it. The global bin is removed from @sentiness/core (it moves to the launcher). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The launcher owns the global sentiness bin with no dependency on @sentiness/core. It reads the project's config + sentiness.lock, fetches the pinned engine into ~/.sentiness/cache (or SENTINESS_HOME), and spawns it with --cache-root, forwarding args and the exit code. SENTINESS_ENGINE_PATH bypasses the fetch for local engine development. Tiny local Node FileSystem/ProcessRunner (stdio inherit, for live streaming)/Logger implementations keep it self-contained. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
require.resolve of an absolute directory path ignores the package "exports" field (only main/index.js), but the check packages ship exports without main, so every path-linked check failed to load with "Cannot find module". linkedEntryFile now reads the linked package's own package.json and resolves the "." entry from exports (string or conditions) or main. Found while dogfooding V1 in this repo. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
sentiness.config.json moves to schema v2 with path-linked biome/knip/deps-diff checks and an engine pin; sentiness.lock is generated by `sentiness install`. .sentiness/home/ (the local dogfood cache) is gitignored and excluded from Biome. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This was referenced Jun 16, 2026
This was referenced Jun 16, 2026
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.
Summary
Implements Phase V1 (global spine) of the Sentiness v2 design — the engine now runs against a project with zero
node_modules, using version-pinned checks fetched into a global cache, with determinism guaranteed by a committedsentiness.lock.feat(core)!): catalog + zones +enginepin; cross-field validation (exactly one ofversion/path), zone/trigger checks; v1 configs rejected with a migration hint.npm installof a pinned artifact into a slot (marker + best-effort integrity), with directory-aware in-memory FS rename.sentiness.lock: Zod schema +LockManager(load/save sorted,satisfieswith a dependency-free caret/tilde/exact range check).feat(core)!):CheckRegistry.fromResolvedimports each check from its cache slot (versioned) or a local package (path-linked) instead of the project'snode_modules; callers go through abuildRegistryhelper.CheckContext/ExecFileOptionsgainbinPaths;NodeProcessRunnerprepends slot.bindirs to PATH.sentiness install+--cache-root: resolves ranges → exact, writes the lock, warms the cache (--frozenrequires a satisfying lock); the engine entrypoint threads--cache-root; the globalbinis removed from@sentiness/core.@sentiness/clilauncher: owns the globalsentinessbin with no dependency on@sentiness/core; fetches the pinned engine into~/.sentiness/cache(orSENTINESS_HOME) and spawns it, forwarding args + exit code;SENTINESS_ENGINE_PATHbypasses the fetch for local dev.sentiness.config.jsonis now v2 (path-linked biome/knip/deps-diff) with a generatedsentiness.lock.exports/main(require.resolve(<dir>)ignoresexports).Test Plan
pnpm typecheck— all packages cleanpnpm lint— cleanpnpm build— all packagespnpm test— green except 4 pre-existinginit.test.tsfailures (v1-init config gap, documented)pnpm check:release-packages— 15 public packages (incl. new@sentiness/cli)installwrites lock;install --frozenexits 0 (satisfying) / 3 (bad lock); launcher-drivencheck --tier=fastruns the local engine and forwards exit codeKnown out-of-scope debt (pre-existing v2-migration, not V1)
sentiness initstill emits a v1 config → 4 redinit.test.tscases.pnpm test:e2ered:examples/demo-projectconfig is still v1 / node_modules-based.Both predate this branch (config v2 landed earlier) and belong to a later phase (full E2E = TV4.3, V4). See
docs/progress.md.🤖 Generated with Claude Code