v0.18.4
Install
npm install -g neotoma@0.18.4| npm | https://www.npmjs.com/package/neotoma/v/0.18.4 |
| Compare | v0.18.3 → v0.18.4 — view diff |
Five fixes from an external evaluator stress-testing Neotoma as a deterministic audit ledger in offline/local mode: two new store-time warnings that surface silent data-integrity footguns, a corrected store response on deduplicated replays, documented migration guidance for the observation_source enum, and a local-transport fix so neotoma issues create works offline without a bearer token.
Highlights
- You now get warned before a silent overwrite under
highest_priority. Two new non-blockingstore_warningsfire at write time:SOURCE_PRIORITY_ESCALATIONwhen an unprioritized write (the defaultsource_priority=100) could outrank an explicit lower priority, andNULL_CLEARED_FIELDwhen an incomingnullactually clears a prior non-null value. Both surface footguns that were previously invisible until you noticed the data had changed. - Deduplicated stores return the real snapshot, not
{}. A verbatim re-store now returns the populatedentity_snapshot_afterplus adeduplicated: truemarker, instead of an empty object, so replay-driven pipelines can trust the store response without a follow-upgetEntitySnapshot. neotoma issues createworks offline. Local requests to/issues/submitand/issues/add_messageno longer require an API bearer token, so file-an-issue works in offline/local SQLite mode the same waygh issue createdoes — without weakening auth for any remote caller.observation_sourcemigration is now documented and the CLI matches the API. The v0.17→v0.18 enum restriction is now in the release notes and CLI help with a concrete mapping (custom labels move todata_source), and the CLI accepts the full enum includingsync, ending a silent CLI/API drift.
What changed for npm package users
Runtime / data layer
SOURCE_PRIORITY_ESCALATIONstore warning (#1838). When a write omits--source-priority(defaulting to100) and targets a field whose schema uses thehighest_prioritystrategy, the store response now includes aSOURCE_PRIORITY_ESCALATIONwarning. The default100beats any explicit priority1–99, so an unprioritized write could silently overwrite a trusted lower-priority value; the warning names the field and the effective strategy. Behavior is unchanged — this is warn-only. The default value of100is intentionally not changed (that would be breaking). Mitigation documented indocs/subsystems/conflict_resolution.md: pass an explicit--source-priorityon every write that participates in ahighest_priorityfield.NULL_CLEARED_FIELDstore warning (#1839). Underhighest_priority, anullis an explicit tombstone (onlyundefinedis dropped). When anullis the resolved winner and actually clears a prior non-null value, the store response now emits aNULL_CLEARED_FIELDwarning. Clearing semantics are unchanged; if you do not want a source to be able to clear a field, have it omit the field rather than sendnull. The warning fires only when the field genuinely clears (the HTTP path persists the typednull; the MCP path strips typednulltoraw_fragmentsand retains the prior value, so no false warning there).- Deduplicated store response now carries the snapshot + a marker (#1840). Re-storing a content-identical observation (same entity, fields, and priority — which the CLI keys via a deterministic content hash) trips the idempotency-replay path. That path previously rebuilt the response without
entity_snapshot_after, so callers saw{}even though the persisted snapshot (andgetEntitySnapshot) was correct. The response now reads the persisted snapshot intoentity_snapshot_afterand setsdeduplicated: true. No storage semantics changed; observation count still stays at 1 on replay.
CLI
observation_sourceCLI/API parity + migration guidance (#1841). The CLI enum now matches the API exactly:sensor,llm_summary,workflow_state,human,import,sync(the CLI previously omittedsync, which the API and OpenAPI already accepted). The invalid-value error and--observation-sourcehelp text now list the valid values and direct custom v0.17 labels (e.g.cboe_live,stale_cache,computed_greeks) to the free-formdata_sourcefield. See Breaking changes for the migration.neotoma issues create/add_messagework in offline/local mode (#1842). These commands post to the API submit endpoint, which gates on a bearer token, whileneotoma issues authonly configuresgh_cli(GitHub-mirror auth). Local/offline users hitAUTH_REQUIRED. Local requests to/issues/submitand/issues/add_messagenow resolve to the local user without a bearer, matching how local entity reads already work. Remote callers are unaffected and still require auth.
API surface & contracts
No OpenAPI route or schema changes. observation_source already declared the full enum (including sync) in openapi.yaml; this release brings the CLI validator into line with it. The store response gains an optional deduplicated boolean and reliably populated entity_snapshot_after on replay; both are additive. The two new store_warnings codes (SOURCE_PRIORITY_ESCALATION, NULL_CLEARED_FIELD) are additive entries in the existing store_warnings array.
Behavior changes
- Stores that would silently escalate priority or clear a field via
nullnow carry astore_warning. The underlying merge result is unchanged; only the response gains a warning. - A deduplicated store returns the current snapshot and
deduplicated: trueinstead of an emptyentity_snapshot_after. - Local/offline
issues createandissues add_messagesucceed without a bearer token. Remote behavior is unchanged.
Fixes
- #1838 — default
source_priority=100was an undocumented silent trust-escalation footgun. Documented indocs/subsystems/conflict_resolution.md+ CLI help; newSOURCE_PRIORITY_ESCALATIONstore warning. (PR #1853) - #1839 —
highest_priorityreducer cleared a field on a winningnullwith no warning. NewNULL_CLEARED_FIELDstore warning, fired only when the field actually clears; clearing semantics unchanged. (PR #1854) - #1840 — store response returned an empty
entity_snapshot_afteron a deduplicated observation replay. Response now populates the snapshot and adds adeduplicatedmarker; root cause was the CLI content-hash idempotency-key tripping the replay early-return insrc/server.ts. (PR #1852) - #1841 —
observation_sourceenum restriction (v0.17→v0.18) was undocumented and the CLI rejectedsync. CLI now matches the API; migration todata_sourcedocumented in CLI help, the error message, and the migration docs. (PR #1851) - #1842 —
neotoma issues createfailed withAUTH_REQUIREDon local/offline installs. Local-loopback trust extended to/issues/submit+/issues/add_message. (PR #1850)
Tests and validation
tests/integration/store_source_priority_ignored_warning.test.ts— escalation warning on both MCP and HTTP store paths (#1838).tests/unit/null_cleared_field_warning.test.ts+tests/integration/store_null_cleared_field_warning.test.ts—NULL_CLEARED_FIELDwarning fires only on a genuine clear (#1839).tests/integration/store_dedup_snapshot_after.test.ts— identical re-store returnsdeduplicated: true+ a populatedentity_snapshot_afterequal togetEntitySnapshot, observation count stays 1 (#1840).tests/unit/cli_observation_source_flag.test.ts— CLI acceptssync; invalid value's error includes thedata_sourcemigration hint (#1841).tests/integration/issues_local_auth_fallback.test.ts— local/issues/submitwith no bearer creates an issue; a remote request with no bearer still returns 401AUTH_REQUIRED(#1842).docs/testing/automated_test_catalog.md— regenerated.- All PRs passed the
security_gatesandbaselineCI lanes before merge.
Security hardening
This release is security-sensitive: npm run security:classify-diff -- --base v0.18.3 --head HEAD reports sensitive=true because the #1842 fix touches src/actions.ts (the route principal resolver).
- Surface affected: the auth gate for
/issues/submitand/issues/add_message. The change adds a local-loopback fallback so a local request (perisLocalRequest) with noAuthorization: Bearerheader resolves to the local dev/sandbox user instead of returningAUTH_REQUIRED, scoped to exactly those two issue-write routes via a dedicated allow-list helper. - Exposure shape if a regression had landed: if the fallback were reachable by remote traffic (e.g. via a spoofable
isLocalRequest) or matched routes beyond the two issue-write paths, an unauthenticated remote caller could write issue entities. The change is gated on all three conditions simultaneously (local request AND no bearer AND one of the two exact routes), and the remote-no-bearer path still returnsAUTH_REQUIRED. - Gate that catches this class going forward: G1 (
security:classify-diff) flagssrc/actions.tschanges for review; G2 (security:lint, including thelocal-dev-user-wideningrule) passed with 0 errors; G3 (security:manifest:check+test:security:auth-matrix) confirmed the protected-routes manifest stayed in sync and the auth matrix passed. A dedicated negative test asserts a remote/untrusted request with no bearer is still rejected. - Operator action: upgrade to v0.18.4. No token rotation required. Operators running Neotoma behind a reverse proxy should confirm their proxy does not cause
isLocalRequestto return true for external traffic (see the security review for the trust-boundary analysis).
Security review artifact: docs/releases/in_progress/v0.18.4/security_review.md — adversarial review of the #1842 local-loopback change.
Breaking changes
No new breaking changes are introduced in v0.18.4. This release documents a breaking change that already shipped in v0.18.x:
observation_sourceis a fixed enum since v0.18.0 (#1841). In v0.17,--observation-sourceaccepted arbitrary strings; v0.18.x restricts it tosensor,llm_summary,workflow_state,human,import,sync. This was previously undocumented.- Before/after:
neotoma store … --observation-source cboe_livesucceeded in v0.17; in v0.18.x it errors withInvalid --observation-source "cboe_live". Expected one of: sensor, llm_summary, workflow_state, human, import, sync. - Error/hint: the error message now appends migration guidance directing custom labels to the
data_sourcefield. - Migration: map your custom value to the closest enum member (
importis the safe fallback for ETL/ingestion sources) and store the original label in the free-formdata_sourcefield. Example:--observation-source sensor --data-source cboe_live.
- Before/after:
The two new store_warnings and the additive deduplicated response field do not tighten any request shape and are not breaking. Patch bump is correct per SemVer.
Commits (v0.18.3 → v0.18.4)
2fe6463chore(release): bump version to v0.18.4 + supplement (#1855)f2f4ddcfix(issues): allow localissues create/add_messagewithout a Bearer token (#1842) (#1850)c5c4394fix(cli): align observation_source enum with API + document v0.17→v0.18 breaking change (#1841) (#1851)1289cc1fix(store): populate entity_snapshot_after + deduplicated marker on dedup replay (#1840) (#1852)e0bda42fix(reducer): warn (NULL_CLEARED_FIELD) when a null clears a highest_priority field (#1854)05f7bb6fix(conflict-resolution): document default source_priority=100 footgun + SOURCE_PRIORITY_ESCALATION warning (#1838) (#1853)4d785c1test(ci): gate cross-surface store/reference contract parity on the PR lane (#1849)
Full compare: v0.18.3...v0.18.4