chore: sanitize tests, add CONTEXT.md + ADRs, track tests/docs
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
# Indexing splits into a shared parse core and binding-specific persist layers
|
||||
|
||||
**Context.** Obelisk had two divergent full indexers — `scripts/indexer.mjs`
|
||||
(791 lines, `node:sqlite`, for the skill/runtime) and `app/indexer.js` (1170
|
||||
lines, `better-sqlite3`, for the Electron app) — both parsing the same Claude and
|
||||
Codex JSONL into the same schema. Keeping two implementations contradicts the
|
||||
"single source of truth / infra" goal, but forcing one SQLite binding is also
|
||||
bad: `node:sqlite` is zero-native-dep and ideal for a clone-and-run skill
|
||||
artifact, while `better-sqlite3` is the battle-tested choice inside Electron.
|
||||
|
||||
**Decision.** Unify at the logic layer, not the binding layer. Extract a pure
|
||||
**parse core** (`jsonl -> records`) with no database dependency, and keep two
|
||||
thin **persist layers** (`node:sqlite` for skill/CLI, `better-sqlite3` for the
|
||||
app) that consume the same records. The parse core is a **streaming iterator**
|
||||
(`parseJsonl(file, fromLine)` yielding records), not a batched `records[]`, to
|
||||
preserve the existing memory-friendly line-by-line indexing and the
|
||||
`lines_processed` resume-from-line semantics recorded in `index_state`.
|
||||
|
||||
**Two indexing modes** share this parse core and differ only in trigger:
|
||||
**daemon indexing mode** (app/CLI watches and keeps the index fresh) and
|
||||
**passive pull mode** (skill indexes on invocation when no daemon is active).
|
||||
They never write concurrently because the passive mode detects a fresh daemon via
|
||||
heartbeat markers in `index_state` (**daemon arbitration**) and skips its own
|
||||
indexing.
|
||||
|
||||
**Consequences.** Golden tests anchor on the parse core: feed a fixture JSONL,
|
||||
assert the yielded record sequence — independent of binding. The refactor's real
|
||||
work is disentangling parse from persist inside the current indexers, where
|
||||
prepared-statement writes are today interleaved into the parse loop. The app's
|
||||
richer incremental-discovery logic must be folded *into* the shared parse core,
|
||||
not dropped.
|
||||
@@ -0,0 +1,26 @@
|
||||
# The runtime contract is two-tier, with api-reference.md authoritative
|
||||
|
||||
**Context.** Before the TypeScript migration and module extraction, we need to
|
||||
pin what "the contract" is so refactoring cannot silently change observable
|
||||
behavior. The four verbs (`build`/`search`/`query`/`attune`) are only the entry
|
||||
surface; agents actually depend on the *return shapes* of the sandbox helpers
|
||||
(`search`, `overview`, `memories`, …), which are already documented in
|
||||
`references/api-reference.md` and relied on by every example in
|
||||
`references/query-patterns.md`. Current behavior is good and there is no reason to
|
||||
change it during migration.
|
||||
|
||||
**Decision.** Freeze the contract in two tiers. **Tier 1 (hard freeze, golden
|
||||
tests):** the four-verb CLI I/O envelope (file/args → pretty JSON on stdout,
|
||||
`{error, stack}` error envelope, exit codes) and the sandbox contract (`sql()`
|
||||
read-only enforcement, `attune` exposing only `remember`/`forget`, the set of
|
||||
globals/helpers available inside `query`/`attune`). **Tier 2 (locked to
|
||||
api-reference.md):** each helper's documented return shape — not frozen forever,
|
||||
but never allowed to drift silently; contract tests assert the live shape matches
|
||||
`references/api-reference.md`, so changing a helper forces a doc change plus a
|
||||
deliberate version bump. `references/api-reference.md` is therefore promoted from
|
||||
description to authoritative contract, and Phase 1 becomes "make it authoritative
|
||||
and enforce it," not "write a new contract doc."
|
||||
|
||||
**Consequences.** Behavior is preserved across the TS/module refactor by
|
||||
construction: the golden and contract tests fail if any observable shape moves.
|
||||
The cost is that helper shapes can no longer be reshaped casually mid-migration.
|
||||
@@ -0,0 +1,22 @@
|
||||
# Core is authored in TypeScript, shipped as precompiled ESM JavaScript
|
||||
|
||||
**Context.** The extracted Obelisk Core must serve two consumers — the ESM skill
|
||||
runtime (`node:sqlite`) and the CommonJS Electron app (`better-sqlite3`) — while
|
||||
the skill artifact must install with **zero build step** on the user's machine
|
||||
(the clone-and-run, "low-friction skill" goal). Authoring in TS gives the infra
|
||||
its checkable contracts, but raises how the compiled output is shipped and which
|
||||
module format it targets.
|
||||
|
||||
**Decision.** Author all of Core in TypeScript and compile it ahead-of-time to
|
||||
**ESM JavaScript plus `.d.ts`**. The skill/CLI runtime ships the *precompiled*
|
||||
ESM JS, so installing the skill never runs a build. Rather than have Core
|
||||
dual-publish CJS+ESM, the Electron main process migrates to ESM at Phase 5 so it
|
||||
can `import` the same compiled Core. TypeScript source is the single source of
|
||||
truth; the build step lives in the main repo (`build:skill`), never on the user's
|
||||
machine.
|
||||
|
||||
**Consequences.** A one-time ESM migration of the Electron main process (Phase 5),
|
||||
in exchange for no dual-build maintenance and a single module format across skill,
|
||||
CLI, and app. The shipped skill artifact contains compiled JS, not TS. The
|
||||
renderer (Vue) is out of scope and stays JavaScript. Phase 3's TS baseline only
|
||||
adds root tooling (package.json, tsconfig, ESLint); it does not touch the app.
|
||||
@@ -0,0 +1,22 @@
|
||||
# The skill artifact ships readable compiled JS, deliberately not bundled
|
||||
|
||||
**Context.** Obelisk reads a user's entire local Claude Code and Codex history,
|
||||
so auditability is the foundation of trust: before a user lets the skill loose on
|
||||
their data, they must be able to read what it does. The obvious way to shrink a
|
||||
clone-and-run skill artifact is to bundle/minify Core into a single `runtime.js`,
|
||||
but that ships an opaque blob into `.claude/skills` / `.agents/skills`. The
|
||||
"don't drag the whole repo into the user's skills dir" concern is real but
|
||||
separate — it is solved by shipping *only Core*, not by bundling.
|
||||
|
||||
**Decision.** The skill artifact ships **readable, non-bundled, non-minified**
|
||||
compiled JavaScript emitted straight from `tsc` (module structure and comments
|
||||
preserved, ~1:1 with the TypeScript source), plus `schema.sql`, `SKILL.md`, and
|
||||
`references/`. It excludes `app/`, `release/`, `renderer/`, Electron code, and
|
||||
`tests/`, which is what keeps it small. Bundling into one file is deliberately
|
||||
rejected: it trades auditability for marginal size, the wrong trade for a
|
||||
history-reading tool. The public TS source in the main repo allows cross-checking.
|
||||
|
||||
**Consequences.** The installed skill is a few readable files rather than one
|
||||
blob; a future contributor may be tempted to "optimize" by bundling — this ADR
|
||||
records that the un-bundled form is intentional. Small artifact size comes from
|
||||
scoping the artifact to Core, handled by `build:skill`, not from a bundler.
|
||||
Reference in New Issue
Block a user