- Tool profiles come from a committed `.napseer-tools.json` at the
project root (schema napseer.tools.v1). The unreleased tool_profiles
field in .napseer/project.json is removed: released clients reject
unknown locator fields, and 0.4.0 treats any extra file inside .napseer/
as private state needing migration (a test loads the released 0.4.0
check from c427e24 to prove the root file is invisible to it).
Environment still overrides; unusable files warn and fall back to core.
- CLI release 0.5.0 with distribution contract 2026-09-29; the minimum
supported contract stays 2026-09-10. The newer-contract rejection test
now derives its date from the installer's own contract.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Crypto backend: use native AES-GCM from `cryptography` when importable
(~1.5 GiB/s), falling back to the pure-Python implementation with a
byte-identical format and identical authentication failures.
- E2E shard: decrypted E2E text is indexed in an in-memory SQLite
database, persisted only as serialize() -> AES-256-GCM with an HKDF key
bound to the project and memory-secret identity, authenticated header,
fresh nonce, 0600 atomic writes. It loads only while the project is
unlocked; tampered, foreign, wrong-key or rotated shards fail closed and
rebuild. Search merges shard hits (search_source local_e2e) and reports
locked/unavailable/slow states explicitly.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Track a feed cursor in the local index: reindex takes the head cursor
before listing and commits it with the snapshot; reads refresh from
/changes (bounded, throttled) and apply creates, updates, archives,
restores and hard deletes atomically with the cursor. Existing
cursorless indexes bootstrap tracking on first read. Failures answer
locally with explicit freshness warnings; 400 triggers a full
reindex, 404 falls back to timestamp sync.
- Rank by a weighted score: exact identity (including aliases and
paths), term coverage including body, intent, bm25, authority, a
status penalty for superseded/completed records and mild recency.
- Return query-centred FTS5 snippets (never for E2E nodes).
- FTS schema v2: porter stemming, prefix index, title column, rowid
deletes, two-character terms, domain words no longer stopwords;
v1 indexes upgrade in place.
- Hyphenated alias/path literals match as adjacent-token phrases and
rank as exact identity.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
In a checkout whose private project credential had been revoked, `nap
auth login` refreshed only the account login while the dead project
override kept being selected, and attach fell through to a second,
project-scoped browser consent. Login now proves an OAuth project grant
dead (tombstoned, expired, or refresh rejected; unknown outcomes keep
it), selects the account login for this worktree, and attaches the
project named by .napseer/project.json without another consent. A
still-valid override is kept and reported. Attach takes the same fast
path, and a rejected project grant names the recovery step.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Send Idempotency-Key on node create/patch/update writes (including bulk,
link, move, kanban and agent-node tools) and return the key used, so an
ambiguous failure can be retried without duplicating the write.
- Record a non-secret marker when login renewal is rejected; nap_whoami
reports auth_required ahead of gateway state, and local-index answers
carry stale warnings instead of silently serving old memory.
- Add nap_kanban_claim_next: pick and claim in one call, skipping cards
held by another operation, never replaying an ambiguous claim.
- Add nap_changes_since over /v1/projects/{id}/changes with a
per-worktree cursor that only advances after a successful read.
- Report real links_count/backlinks_count from indexed edges.
- Accept optional tool_profiles in .napseer/project.json when
NAPSEER_TOOL_PROFILES is unset.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The dispatcher in `napseer_mcp_server.py::cli_main` had grown a duplicated
fragment of the project-command dispatcher (the attach/create/claim/status/
encryption block) that survived an earlier refactor. Because the fragment
was left at function-body indent after the `return` that closes the
canonical block, every `nap` subcommand that hands off to the MCP server
crashed at import time:
File "/home/matias/.local/share/napseer/napseer_mcp_server.py",
line 13561
"frontend_url": cli_option(argv[3:], "--frontend-url",
default=None),
IndentationError: unexpected indent
That broke `nap project attach`, `nap project create`, `nap gateway
start`, and every other `nap *` command that dispatches to the MCP
server, surfacing as an "unknown command" / 401-style failure at the
caller. The pinned published revision `cbdc1840` still ships the broken
file; the next backend build needs to bump to this commit.
Rewrite `cli_main` so each `if command == "<cmd>":` block has a
single, complete dispatcher with no leftover fragments. Promote
`init` (first-time directory setup, no browser) to a canonical
`nap project init` subcommand, leave `attach` calling
`operator_loopback_login`, and `create` / `claim` unchanged.
Drop deprecated aliases (`ls`, `new`, `send`, `rm`, `set-status`,
`admin`, `global-list`, positional-state shortcut for
`nap project encryption set`, `process --all` shortcut) with clear
RuntimeError pointers to the canonical verbs.
Tests: `tests/test_cli_dispatch_consolidation.py` asserts canonical
verbs route correctly and dropped aliases raise with a clear pointer;
`tests/test_project_init_wizard.py` exercises the
`nap project init` wizard end-to-end;
`tests/test_nap_install_gateway_routes.py` updated for the
`process --all` removal. 19/19 pytest cases pass.
See /incidents/2026-06-15/napseer-mcp-server-py-13561-indentation-error-upstream
in Napseer memory for the prior incident writeup.