neotoma 0.22.0 → 0.22.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/dist/actions.d.ts +13 -0
  2. package/dist/actions.d.ts.map +1 -1
  3. package/dist/actions.js +236 -93
  4. package/dist/actions.js.map +1 -1
  5. package/dist/cli/index.d.ts.map +1 -1
  6. package/dist/cli/index.js +11 -0
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/config.d.ts +13 -0
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/config.js +90 -0
  11. package/dist/config.js.map +1 -1
  12. package/dist/docs/api/rest_api.md +81 -0
  13. package/dist/docs/developer/getting_started.md +45 -0
  14. package/dist/docs/developer/mcp/proxy.md +48 -28
  15. package/dist/docs/developer/npm_scripts.md +10 -4
  16. package/dist/docs/infrastructure/client_instance_deployment.md +50 -2
  17. package/dist/docs/operations/configuration.md +44 -1
  18. package/dist/docs/security/advisories/2026-08-26-jwt-bearer-unverified-fallback-auth-bypass.md +59 -0
  19. package/dist/docs/security/advisories/README.md +1 -0
  20. package/dist/docs/security/practices.md +5 -1
  21. package/dist/docs/subsystems/agent_session_architecture.md +69 -0
  22. package/dist/docs/subsystems/usage_digest.md +127 -0
  23. package/dist/docs/testing/automated_test_catalog.md +49 -26
  24. package/dist/docs/testing/testing_standard.md +1 -1
  25. package/dist/middleware/aauth_admission.js +1 -1
  26. package/dist/middleware/aauth_admission.js.map +1 -1
  27. package/dist/middleware/db_abort_context.d.ts +24 -0
  28. package/dist/middleware/db_abort_context.d.ts.map +1 -0
  29. package/dist/middleware/db_abort_context.js +41 -0
  30. package/dist/middleware/db_abort_context.js.map +1 -0
  31. package/dist/proxy/mcp_stdio_proxy.d.ts +134 -6
  32. package/dist/proxy/mcp_stdio_proxy.d.ts.map +1 -1
  33. package/dist/proxy/mcp_stdio_proxy.js +373 -17
  34. package/dist/proxy/mcp_stdio_proxy.js.map +1 -1
  35. package/dist/repositories/sqlite/local_db_adapter.d.ts.map +1 -1
  36. package/dist/repositories/sqlite/local_db_adapter.js +20 -0
  37. package/dist/repositories/sqlite/local_db_adapter.js.map +1 -1
  38. package/dist/repositories/sqlite/sqlite_client.d.ts.map +1 -1
  39. package/dist/repositories/sqlite/sqlite_client.js +10 -0
  40. package/dist/repositories/sqlite/sqlite_client.js.map +1 -1
  41. package/dist/repositories/worker/worker_file_database.d.ts +135 -1
  42. package/dist/repositories/worker/worker_file_database.d.ts.map +1 -1
  43. package/dist/repositories/worker/worker_file_database.js +726 -27
  44. package/dist/repositories/worker/worker_file_database.js.map +1 -1
  45. package/dist/server.d.ts.map +1 -1
  46. package/dist/server.js +182 -77
  47. package/dist/server.js.map +1 -1
  48. package/dist/services/attachment_resolution.d.ts +149 -0
  49. package/dist/services/attachment_resolution.d.ts.map +1 -0
  50. package/dist/services/attachment_resolution.js +206 -0
  51. package/dist/services/attachment_resolution.js.map +1 -0
  52. package/dist/services/attachment_resolution_sqlite.d.ts +57 -0
  53. package/dist/services/attachment_resolution_sqlite.d.ts.map +1 -0
  54. package/dist/services/attachment_resolution_sqlite.js +75 -0
  55. package/dist/services/attachment_resolution_sqlite.js.map +1 -0
  56. package/dist/services/entity_queries.d.ts.map +1 -1
  57. package/dist/services/entity_queries.js +85 -23
  58. package/dist/services/entity_queries.js.map +1 -1
  59. package/dist/services/entity_snapshot_at_time.d.ts.map +1 -1
  60. package/dist/services/entity_snapshot_at_time.js +23 -3
  61. package/dist/services/entity_snapshot_at_time.js.map +1 -1
  62. package/dist/services/file_input_diagnostics.d.ts +89 -0
  63. package/dist/services/file_input_diagnostics.d.ts.map +1 -0
  64. package/dist/services/file_input_diagnostics.js +174 -0
  65. package/dist/services/file_input_diagnostics.js.map +1 -0
  66. package/dist/services/interpretation.d.ts.map +1 -1
  67. package/dist/services/interpretation.js +22 -8
  68. package/dist/services/interpretation.js.map +1 -1
  69. package/dist/services/mcp_auth.d.ts +19 -3
  70. package/dist/services/mcp_auth.d.ts.map +1 -1
  71. package/dist/services/mcp_auth.js +23 -33
  72. package/dist/services/mcp_auth.js.map +1 -1
  73. package/dist/services/mcp_oauth.d.ts +14 -1
  74. package/dist/services/mcp_oauth.d.ts.map +1 -1
  75. package/dist/services/mcp_oauth.js +86 -1
  76. package/dist/services/mcp_oauth.js.map +1 -1
  77. package/dist/services/raw_storage.d.ts.map +1 -1
  78. package/dist/services/raw_storage.js +14 -2
  79. package/dist/services/raw_storage.js.map +1 -1
  80. package/dist/services/readiness.d.ts +85 -0
  81. package/dist/services/readiness.d.ts.map +1 -0
  82. package/dist/services/readiness.js +93 -0
  83. package/dist/services/readiness.js.map +1 -0
  84. package/dist/services/route_shadowing.d.ts +67 -0
  85. package/dist/services/route_shadowing.d.ts.map +1 -0
  86. package/dist/services/route_shadowing.js +126 -0
  87. package/dist/services/route_shadowing.js.map +1 -0
  88. package/dist/services/schema_definitions.d.ts.map +1 -1
  89. package/dist/services/schema_definitions.js +171 -0
  90. package/dist/services/schema_definitions.js.map +1 -1
  91. package/dist/services/schema_lag_repair.d.ts.map +1 -1
  92. package/dist/services/schema_lag_repair.js +32 -12
  93. package/dist/services/schema_lag_repair.js.map +1 -1
  94. package/dist/services/schema_registry.d.ts.map +1 -1
  95. package/dist/services/schema_registry.js +12 -5
  96. package/dist/services/schema_registry.js.map +1 -1
  97. package/dist/services/snapshot_computation.d.ts.map +1 -1
  98. package/dist/services/snapshot_computation.js +12 -8
  99. package/dist/services/snapshot_computation.js.map +1 -1
  100. package/dist/services/subscriptions/sse_hub.d.ts.map +1 -1
  101. package/dist/services/subscriptions/sse_hub.js +19 -1
  102. package/dist/services/subscriptions/sse_hub.js.map +1 -1
  103. package/dist/shared/action_handlers/entity_handlers.d.ts.map +1 -1
  104. package/dist/shared/action_handlers/entity_handlers.js +110 -26
  105. package/dist/shared/action_handlers/entity_handlers.js.map +1 -1
  106. package/dist/shared/action_schemas.d.ts +6 -6
  107. package/dist/shared/contract_mappings.d.ts.map +1 -1
  108. package/dist/shared/contract_mappings.js +8 -1
  109. package/dist/shared/contract_mappings.js.map +1 -1
  110. package/dist/shared/openapi_types.d.ts +108 -3
  111. package/dist/shared/openapi_types.d.ts.map +1 -1
  112. package/dist/tool_definitions.d.ts.map +1 -1
  113. package/dist/tool_definitions.js +18 -13
  114. package/dist/tool_definitions.js.map +1 -1
  115. package/openapi.yaml +83 -3
  116. package/package.json +2 -1
  117. package/skills/status/SKILL.md +0 -1
  118. package/skills/where/SKILL.md +315 -0
@@ -0,0 +1,59 @@
1
+ # MCP session token validation trusted an unverified JWT's own claims (v0.22.1 fix)
2
+
3
+ - **Date disclosed:** 2026-08-26
4
+ - **GHSA:** _draft; publish after tag per release process_
5
+ - **CVE:** _not requested_
6
+ - **Severity:** High — a remote caller could authenticate as any user without a valid session.
7
+ - **Affected:** all versions carrying this fallback in `validateSessionToken` prior to `0.22.1`.
8
+ - **Fixed in:** `0.22.1`
9
+ - **Reporter:** internal security review.
10
+ - **CWEs:** [CWE-287](https://cwe.mitre.org/data/definitions/287.html) (Improper Authentication), [CWE-347](https://cwe.mitre.org/data/definitions/347.html) (Improper Verification of Cryptographic Signature).
11
+
12
+ ## Summary
13
+
14
+ `validateSessionToken` (`src/services/mcp_auth.ts`) looked up the bearer token against `mcp_oauth_connections`. When no row matched, it fell back to decoding the bearer as an **unverified** JWT via a local `decodeJWTUnverified` helper and trusted the token's own `sub` (and `email`) claims as the authenticated identity — no signature, issuer, or expiry check. The accompanying comment described this as "local-only compatibility," but the fallback carried no local-only gate, so it ran for any caller, local or remote.
15
+
16
+ ## Impact
17
+
18
+ A caller who could construct a syntactically valid but unsigned JWT (`{alg:"none"}` header, arbitrary payload, no verifiable signature) and present it as a `Bearer` token that matched no live OAuth connection was authenticated as the `sub` named in that token's own payload — including a specific victim's `user_id`, which is derivable as `sha256(email)`. Net effect: full authentication bypass on the MCP session-token path, independent of any real OAuth grant.
19
+
20
+ ## Root cause
21
+
22
+ The fallback branch in `validateSessionToken` assumed that any bearer reaching this point without a matching connection row was a legitimate but differently-issued local token, and decoded-and-trusted it rather than rejecting it. This is the same regression class as the Ed25519 bearer path fixed in `0.21.4` (`2026-08-07-ed25519-bearer-forged-key-auth-bypass`): an unresolved bearer must fail closed, not fall through to trusting caller-supplied claims.
23
+
24
+ ## Fix
25
+
26
+ `validateSessionToken` now fails closed: a bearer that resolves to no live `mcp_oauth_connections` row throws `Invalid session token`, unconditionally. The unused `decodeJWTUnverified` helper was removed so the unverified-decode code path no longer exists to regress into.
27
+
28
+ Regression tests (`src/services/__tests__/mcp_oauth.test.ts`, `tests/services/mcp_auth.test.ts`) assert:
29
+
30
+ - A forged `alg:none` JWT naming a real victim user's `user_id` is rejected.
31
+ - A forged `alg:none` JWT naming an arbitrary/nonexistent `user_id` is rejected.
32
+ - A random non-JWT bearer is rejected.
33
+ - A legitimate, live OAuth connection token still authenticates correctly (no over-correction).
34
+
35
+ ## Operator action
36
+
37
+ - Upgrade to `>= 0.22.1`.
38
+ - No data migration required.
39
+ - If an instance was reachable by untrusted callers prior to `0.22.1` with the MCP session-token path enabled, review any authentication logs for bearer tokens with a `{alg:"none"}` or otherwise unverifiable header shape.
40
+
41
+ ## Detection
42
+
43
+ `src/services/__tests__/mcp_oauth.test.ts` and `tests/services/mcp_auth.test.ts` detect regressions of this class going forward.
44
+
45
+ ## Gates that catch this regression class going forward
46
+
47
+ - **G3b auth topology matrix** (`tests/security/auth_topology_matrix.test.ts`) — asserts protected routes reject unauthenticated/unresolved callers.
48
+ - **Regression tests above** — directly assert the fail-closed behavior for unresolved bearers on the MCP session-token path.
49
+ - **Change guardrails rule MUST 5** — authorization must go through a resolved principal; caller-supplied claims are never trusted directly for access control.
50
+
51
+ ## Timeline
52
+
53
+ | Date | Event |
54
+ |------|-------|
55
+ | 2026-08-26 | Vulnerability identified during internal security review |
56
+ | 2026-08-26 | Fix PR #2232 merged |
57
+ | 2026-08-26 | v0.22.1 supplement prepared |
58
+ | TBD | v0.22.1 tagged and released |
59
+ | TBD | GHSA published |
@@ -14,6 +14,7 @@ This index is the canonical cross-link target for:
14
14
 
15
15
  | Date | Title | GHSA | CVE | Affected | Fixed in | Gate that catches the regression class |
16
16
  |------|-------|------|-----|----------|----------|----------------------------------------|
17
+ | 2026-08-26 | [MCP session token validation trusted an unverified JWT's own claims](2026-08-26-jwt-bearer-unverified-fallback-auth-bypass.md) | _draft_ | _not requested_ | all versions prior to `0.22.1` | `0.22.1` | `src/services/__tests__/mcp_oauth.test.ts`, `tests/services/mcp_auth.test.ts`, G3 auth topology matrix |
17
18
  | 2026-08-09 | [Stored XSS in rendered_page HTML route via author-supplied html_body](2026-08-09-rendered-page-stored-xss-csp.md) | [GHSA-qp63-9r52-4q25](https://github.com/markmhendrickson/neotoma/security/advisories/GHSA-qp63-9r52-4q25) | _not requested_ | all versions prior to `0.21.5` | `0.21.5` | `tests/security/rendered_page_csp.test.ts` |
18
19
  | 2026-08-07 | [Ed25519 bearer auth: unresolved principal returned caller-supplied user_id](2026-08-07-ed25519-bearer-forged-key-auth-bypass.md) | [GHSA-33x4-v5cf-2hfj](https://github.com/markmhendrickson/neotoma/security/advisories/GHSA-33x4-v5cf-2hfj) | _not requested_ | all versions prior to `0.21.4` | `0.21.4` | `tests/security/ed25519_forged_key_auth_bypass.test.ts`, G3 auth topology matrix |
19
20
  | 2026-08-07 | [sort_by / snapshot_filters ORDER BY SQL injection](2026-08-07-sort-by-order-by-sql-injection.md) | [GHSA-8f95-jfm5-jjmr](https://github.com/markmhendrickson/neotoma/security/advisories/GHSA-8f95-jfm5-jjmr) | _not requested_ | all versions prior to `0.21.4` | `0.21.4` | `tests/security/sort_by_sql_injection.test.ts`, `normalizeColumnName` fail-closed backstop |
@@ -137,10 +137,14 @@ Every advisory in `docs/security/advisories/` MUST result in one of:
137
137
  1. A new gate, rule, or checklist category (documented in this file under the relevant section)
138
138
  2. An explicit waiver with a written reason (documented in the advisory's Gate Gap section)
139
139
 
140
- The two advisories filed to date have both produced gate or checklist work:
140
+ Advisories filed to date have each produced gate or regression-test work:
141
141
 
142
142
  - `2026-05-11-inspector-auth-bypass` → G2 rules, G3a manifest, G3b matrix test, checklist categories 1–5 (all landed in v0.11.1)
143
143
  - `2026-05-21-relationship-endpoint-tenant-isolation` → G3b extension, checklist category 6 (pending, tracked in #372)
144
+ - `2026-08-07-ed25519-bearer-forged-key-auth-bypass` → fail-closed fix on the Ed25519 bearer path plus `tests/security/ed25519_forged_key_auth_bypass.test.ts` (landed in v0.21.4)
145
+ - `2026-08-07-sort-by-order-by-sql-injection` → `normalizeColumnName` fail-closed backstop plus `tests/security/sort_by_sql_injection.test.ts` (landed in v0.21.4)
146
+ - `2026-08-09-rendered-page-stored-xss-csp` → CSP hardening plus `tests/security/rendered_page_csp.test.ts` (landed in v0.21.5)
147
+ - `2026-08-26-jwt-bearer-unverified-fallback-auth-bypass` → fail-closed fix on the MCP session-token bearer path (removed the unverified-JWT decode fallback), plus regression tests in `src/services/__tests__/mcp_oauth.test.ts` and `tests/services/mcp_auth.test.ts` (landed in v0.22.1). Same regression class as the Ed25519 bearer path above — an unresolved bearer must fail closed rather than fall through to trusting caller-supplied claims.
144
148
 
145
149
  ### Single-tenant assumption
146
150
 
@@ -0,0 +1,69 @@
1
+ # Subsystem: agent_session
2
+
3
+ Runtime and resume record for a coding-agent session, across harnesses (Claude Code, Codex, Cursor) and across devices. An `agent_session` captures where a session's transcript lives, the git environment needed to reconstruct it, and (for autonomous swarm runs) the trigger and AAuth attribution. It is the resume-oriented sibling of the semantic `conversation`: `conversation` answers "what was discussed", `agent_session` answers "where is the transcript and how do I resume it".
4
+
5
+ Neotoma owns the State Layer parts of this subsystem (the entity types, deterministic capture, durable transcript storage). The resume orchestration (materialize transcript, fetch git, recreate worktree, launch or re-dispatch) is Operational Layer work and lives in ateles, not here.
6
+
7
+ ## Entity model
8
+
9
+ ### `agent_session` (`category: agent_runtime`)
10
+
11
+ - **Identity**: joint `canonical_name_fields: ["harness", "native_session_id"]` with `name_collision_policy: "reject"`. A native session id is only unique within its harness.
12
+ - **`kind`**: `interactive` | `autonomous` | `subagent`.
13
+ - **Runtime / git fields**: `cwd`, `repo`, `repo_remote_url`, `source_branch`, `branch`, `git_head_sha`, `worktree_path`, `origin_device`, `parent_session_id`. Paths and host are context only and are excluded from identity because they move and embed usernames.
14
+ - **Activity**: `title`, `summary`, `model`, `status`, `message_count`, `created_at`, `last_activity_at`.
15
+ - **Per-device app state**: `is_archived`, `auto_archive_exempt`, `is_pinned`, `is_bridged`, `bridge_session_ids`.
16
+ - **Autonomous trigger and attribution block**: `trigger_kind`, `trigger_ref`, `dispatch_parent`, `aauth_sub`, `workflow_definition_ref`, `gate_name`. These are denormalized for discovery and filtering; the authoritative record stays in the linked orchestration entities (see below).
17
+ - **`resume_command`**: literal harness-specific command to resume.
18
+
19
+ ### `session_transcript` (`category: agent_runtime`)
20
+
21
+ The raw, lossless transcript artifact. Specialized asset type, modeled like `image_asset` and `audio_asset`: content-addressed bytes in the `sources` bucket at `{user_id}/{content_hash}`, surfaced via `retrieve_file_url`.
22
+
23
+ - **Identity**: `canonical_name_fields: ["content_hash"]` (SHA-256 of the transcript bytes).
24
+ - **Fields**: `source_id`, `mime_type`, `file_size`, `storage_url`, `harness`, `format` (`claude_code_jsonl` | `codex_rollout` | `cursor_sqlite`), `format_version`, `turn_count`, `transcript_kind` (`main` | `subagent`), `agent_session_id`.
25
+
26
+ The raw transcript is the byte-exact resume artifact and the durable backup. The structured turns of the same session live separately in `conversation_message`. The two fidelities are complementary, not alternatives.
27
+
28
+ ## Relationship to existing entities
29
+
30
+ `agent_session` links, it does not duplicate.
31
+
32
+ - `agent_session` is `PART_OF` linked from a `conversation` (the semantic record already carries `session_id`, `client_name`/`harness`, and `repository_*`).
33
+ - `agent_session` relates to its `session_transcript` (raw bytes) and to the `conversation_message` rows (structured turns).
34
+ - For autonomous swarm runs, `agent_session` relates to the existing ateles orchestration entities rather than re-deriving them: `harness_event` (lifecycle plus `agent_sub` AAuth identity and `task_entity_id`), `participation_record` (gate dispatch and satisfaction), and the `agent_task` to `agent_attempt` to `agent_outcome` chain. The `agent_attempt` type already claims the alias `agent_run`, so `agent_session` deliberately does not introduce a competing run type; it is the per-session unifying record that the orchestration entities lacked.
35
+
36
+ ## Deterministic capture
37
+
38
+ Capture does not depend on the model calling the MCP `store` tool. Neotoma already runs deterministic harness hooks (`claude-code-plugin`, `cursor-hooks`, `codex-hooks`, `opencode-plugin`, `claude-agent-sdk-adapter`) that record structured turns. This subsystem extends that path with:
39
+
40
+ 1. A raw-transcript tail in the Stop and SessionEnd hooks that reads transcript lines appended since a per-session offset cursor and appends the delta to a durable local queue, then exits fast with no synchronous Neotoma call.
41
+ 2. An async drainer that ships queued deltas to Neotoma: `storeRawContent` for the `session_transcript` blob plus the structured `conversation_message` rows. Idempotent via the JSONL `uuid` and `parentUuid` natural keys plus the offset cursor.
42
+ 3. A filesystem-watcher backstop for sessions whose harness has no hooks installed, or that die before Stop fires.
43
+
44
+ A backfill scanner provides historical coverage and repair. The scanner reads `cwd` and `gitBranch` from the message lines of the transcript, not from line one (line one is a hook or summary event without `cwd`). Validation across 1288 desktop sessions showed reading only line one left `cwd` null on 91 percent of sessions; reading the message lines raised resolution to 99.5 percent.
45
+
46
+ ## Autonomous and swarm sessions
47
+
48
+ The ateles swarm runs T2, T3, and T4 agents headlessly through `claude --print` subprocesses, with no interactive UI harness. These are still Claude Code sessions: they write standard JSONL transcripts under `~/.claude/projects` and fire the same hooks, because the daemon sets no `CLAUDE_CONFIG_DIR` override.
49
+
50
+ Empirical hook firing under `--print` (60 sampled swarm sessions): `SessionStart` 60 of 60, `PostToolUse` 60 of 60, `Stop` 59 of 60 (the miss was a run killed mid-execution), `PreCompact` 1 of 60 (only on compaction), `UserPromptSubmit` 15 of 60. `UserPromptSubmit` is unreliable headlessly because the prompt is piped rather than interactively submitted, so capture reads the prompt and turns from the transcript, not from that hook. The roughly 2 percent of runs that die before `Stop`, plus daemon timeouts, are covered by the watcher and scanner backstops.
51
+
52
+ Sub-agent runs spawned by the Task tool write their own nested `~/.claude/projects/<session>/subagents/*.jsonl` transcripts. These are modeled as child `agent_session` rows with `kind: subagent` and `parent_session_id` set, each with its own `session_transcript`.
53
+
54
+ ## Layer boundaries and invariants
55
+
56
+ - State Layer only. No resume orchestration, dispatch logic, or scheduled runs live in Neotoma. Resume and re-dispatch live in ateles.
57
+ - Immutability. The drainer writes via observations; corrections create new observations. Content-addressed transcript storage makes re-ingest of identical bytes a no-op.
58
+ - Determinism. Entity and event ids are reproducible; the offset cursor and content hash make capture idempotent.
59
+ - Schema-first. Behavior that varies by type is declared on the schema, not branched in code.
60
+
61
+ ## Resume fidelity ladder (ateles, Operational Layer)
62
+
63
+ The `/resume-session` flow auto-selects the highest available tier; `--mode` can force one.
64
+
65
+ 1. **Native**: materialize the transcript from the `session_transcript` blob (or a reachable peer device), then `claude --resume` or `codex resume`. Exact fidelity, same session id.
66
+ 2. **Context injection**: start a fresh session in any harness and inject a reconstructed context block (summary plus the last N turns verbatim plus key entities), restoring model and effort from the `agent_session`. Portable, new session id.
67
+ 3. **Re-dispatch** (autonomous): re-fire the trigger or re-run the skill with the same task entity. Used for swarm runs, which have no UI to reopen. Builds on the existing ateles idempotent re-dispatch and gate-state recovery.
68
+
69
+ Cross-harness transcript translation is intentionally not built.
@@ -412,6 +412,132 @@ await emitUsageDigest(
412
412
  );
413
413
  ```
414
414
 
415
+ ## `/usage` HTTP API endpoint
416
+
417
+ `GET /usage` is a separate, complementary surface that exposes **pre-aggregated dashboard metrics** computed on the fly from the live database. It is _not_ the same as querying `usage_digest` entities: the endpoint returns ephemeral per-request aggregates scoped to the authenticated user, while `usage_digest` entities are durable, periodic telemetry records submitted by external observers.
418
+
419
+ ### Purpose
420
+
421
+ The endpoint powers the Neotoma Inspector dashboard (the summary tiles showing total entities, recent ingestion, and schema coverage). It can also be called directly by operators or agents that want a quick snapshot of their local deployment's current state without parsing raw entity records.
422
+
423
+ ### Endpoint
424
+
425
+ ```
426
+ GET /usage
427
+ ```
428
+
429
+ `operationId: getUsage` — declared in `openapi.yaml` and mapped in `src/shared/contract_mappings.ts` with `adapter: "cli"` (CLI-accessible via `neotoma request --operation getUsage`).
430
+
431
+ ### Authentication
432
+
433
+ **Authentication is required.** All figures are scoped to the authenticated user.
434
+
435
+ Supported auth methods (same as all other authenticated endpoints):
436
+
437
+ | Method | How to supply |
438
+ | --------------- | ----------------------------------------------- |
439
+ | Bearer token | `Authorization: Bearer <NEOTOMA_BEARER_TOKEN>` |
440
+ | AAuth grant | Agent identity admitted via `X-AAuth-*` headers |
441
+ | MCP OAuth token | Bearer token issued through MCP OAuth flow |
442
+
443
+ Guest principals cannot call this endpoint. A missing or invalid token returns `401 AUTH_REQUIRED`.
444
+
445
+ Optional query parameter: `user_id` (UUID string). When supplied, the authenticated user's ID must match — useful for CLI tooling that passes its local user ID explicitly. AAuth-admitted agents cannot override `user_id` to a different user.
446
+
447
+ ### Response shape
448
+
449
+ **`200 OK`** — `application/json` — `UsageStats` object:
450
+
451
+ | Field | Type | Description |
452
+ | ------------------------------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
453
+ | `entities_by_type` | `Record<string, number>` | Count of active (non-merged) entities per `entity_type`, sorted by count descending. |
454
+ | `total_entities` | `number` | Total active entities across all types. |
455
+ | `observations_by_source` | `Record<string, number>` | Count of observations grouped by `observation_source` value, sorted by count descending. Sources not present on any observation appear as `"unclassified"`. |
456
+ | `total_observations` | `number` | Total observation records for this user. |
457
+ | `entities_created_last_7_days` | `number` | Entities whose `created_at` timestamp falls within the last 7 days. |
458
+ | `entities_created_last_30_days` | `number` | Entities whose `created_at` timestamp falls within the last 30 days. |
459
+ | `entity_types_with_schema` | `number` | Count of distinct `entity_type` values present in the entities table that also have an active entry in `schema_registry`. |
460
+ | `entity_types_total` | `number` | Total distinct `entity_type` values present across all active entities for this user. |
461
+ | `last_updated` | `string` (ISO-8601) | ISO timestamp generated at the moment the aggregates were computed. Reflects when the response was assembled, not a database watermark. |
462
+
463
+ All figures are computed fresh on each request — there is no server-side cache. The `last_updated` timestamp reflects the time of the request, not a cache expiry.
464
+
465
+ ### Example
466
+
467
+ **Request:**
468
+
469
+ ```http
470
+ GET /usage HTTP/1.1
471
+ Authorization: Bearer <NEOTOMA_BEARER_TOKEN>
472
+ ```
473
+
474
+ **Response:**
475
+
476
+ ```json
477
+ {
478
+ "entities_by_type": {
479
+ "conversation": 214,
480
+ "task": 156,
481
+ "contact": 88,
482
+ "harness_event": 42
483
+ },
484
+ "total_entities": 500,
485
+ "observations_by_source": {
486
+ "human": 310,
487
+ "llm_summary": 140,
488
+ "sensor": 50
489
+ },
490
+ "total_observations": 500,
491
+ "entities_created_last_7_days": 34,
492
+ "entities_created_last_30_days": 112,
493
+ "entity_types_with_schema": 6,
494
+ "entity_types_total": 10,
495
+ "last_updated": "2026-06-19T09:00:00.000Z"
496
+ }
497
+ ```
498
+
499
+ **curl example:**
500
+
501
+ ```bash
502
+ curl -s \
503
+ -H "Authorization: Bearer $NEOTOMA_BEARER_TOKEN" \
504
+ http://localhost:3080/usage | jq .
505
+ ```
506
+
507
+ ### Error responses
508
+
509
+ | Status | `error_code` | Cause |
510
+ | ------ | ----------------- | --------------------------------------------------------------------- |
511
+ | `401` | `AUTH_REQUIRED` | Missing or invalid Bearer token; guest principal attempted. |
512
+ | `403` | `FORBIDDEN` | Supplied `user_id` query param does not match the authenticated user. |
513
+ | `500` | `DB_QUERY_FAILED` | Database query failed; check server logs for `APIError:usage_stats`. |
514
+
515
+ ### Inspector / dashboard consumption
516
+
517
+ The Neotoma Inspector dashboard polls `GET /usage` on page load via React Query (`queryKey: ["usage"]`). The response drives the four summary tiles:
518
+
519
+ - **Total entities** — `total_entities` with `entity_types_total` entity types as a subtitle.
520
+ - **Total observations** — `total_observations`.
521
+ - **New (7 days)** — `entities_created_last_7_days` with `entities_created_last_30_days` last 30 days as a subtitle.
522
+ - **Schema coverage** — derived from `entity_types_with_schema` / `entity_types_total`.
523
+
524
+ The dashboard does not poll on an interval; it refetches on manual navigation or page reload. There is no server-sent staleness header.
525
+
526
+ ### Distinction from `usage_digest` entities
527
+
528
+ | | `GET /usage` | `usage_digest` entities |
529
+ | -------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------- |
530
+ | **Durability** | Ephemeral — computed on each request, not stored. | Durable — persisted entities queryable over time. |
531
+ | **Scope** | Current live state for the authenticated user. | Historical rollup for a bounded time window from an external observer. |
532
+ | **Writer** | Neotoma server, on each HTTP call. | External agent / observer daemon via `store` MCP operation. |
533
+ | **Auth** | Requires authenticated user (Bearer / AAuth / OAuth). | Can accept guest `store` writes via `submit_only` guest policy. |
534
+ | **PII risk** | No free-text fields; counts only. | Contains `friction_notes` / `notes` — redacted on ingest. |
535
+ | **Primary consumer** | Inspector dashboard, CLI operators. | Telemetry dashboards, cross-deployment trend analysis. |
536
+
537
+ ### OpenAPI reference
538
+
539
+ The full schema for `UsageStats` and the `getUsage` operation are declared in `openapi.yaml`. The generated TypeScript types live in `src/shared/openapi_types.ts` under `components["schemas"]["UsageStats"]` and `operations["getUsage"]`.
540
+
415
541
  ## Related
416
542
 
417
543
  - [`daemon_report`](../../src/services/schema_definitions.ts) — per-incident anomaly reports; the drill-down companion to usage digests.
@@ -419,3 +545,4 @@ await emitUsageDigest(
419
545
  - [`guest_access_policy.md`](guest_access_policy.md) — configuring keyless write access for telemetry sinks.
420
546
  - [`aauth.md`](aauth.md) — issuing agent grants for authenticated digest submission.
421
547
  - [`subscriptions.md`](subscriptions.md) — subscribing to `UsageDigestClosed` timeline events for downstream alerting.
548
+ - [`docs/api/rest_api.md`](../api/rest_api.md) — top-level REST API reference including the `/usage` endpoint entry.
@@ -27,7 +27,7 @@ This document does not cover:
27
27
  ## Definitions
28
28
  - **Automated test file**: A repo test source matched by this catalog's scanner (`tests/**`, `src/**`, `frontend/src/**`, `playwright/tests/**`).
29
29
  - **Catalog generator**: `scripts/generate-automated-test-catalog.ts`, the only source allowed to rewrite this file.
30
- - **Catalog validator**: `npm run validate:test-catalog`, which fails when this file drifts from the repo tree.
30
+ - **Catalog validator**: `npm run validate:test-catalog`, which fails when this file drifts from the repo tree. It is the local, pre-PR advisory gate. CI does not use it: the baseline lane runs `npm run generate:test-catalog`, which renders this file rather than failing on drift, so a stale copy never blocks a pull request.
31
31
 
32
32
  ## Data models or schemas
33
33
  None.
@@ -52,8 +52,8 @@ flowchart TD
52
52
 
53
53
  ## Testing requirements
54
54
  - `npm run generate:test-catalog` must be run when automated test inventory changes.
55
- - `npm run validate:test-catalog` must pass before merge.
56
- - CI runs `npm run validate:test-catalog` in the baseline lane.
55
+ - `npm run validate:test-catalog` should pass before merge, but does not block it.
56
+ - CI runs `npm run generate:test-catalog` in the baseline lane, which renders this file rather than failing on drift.
57
57
 
58
58
  ## Maintenance
59
59
  - Canonical policy doc: `docs/testing/testing_standard.md`.
@@ -61,22 +61,22 @@ flowchart TD
61
61
  - Do not hand-edit suite inventory entries in this file. Update the generator or the repository tree, then regenerate.
62
62
 
63
63
  ## Repo-wide summary
64
- - Total automated test files: **582**
65
- - Backend and repo Vitest files: **547**
64
+ - Total automated test files: **605**
65
+ - Backend and repo Vitest files: **570**
66
66
  - Frontend Vitest files: **9**
67
67
  - Playwright spec files: **26**
68
68
 
69
69
  ### Suite counts
70
70
  | Suite | Files |
71
71
  |---|---:|
72
- | Vitest unit tests | 162 |
73
- | Vitest service tests | 43 |
74
- | Source-adjacent tests | 65 |
75
- | Vitest integration tests | 165 |
76
- | Vitest CLI tests | 77 |
77
- | Vitest contract tests | 15 |
78
- | Vitest security tests | 7 |
79
- | Vitest subscription tests | 5 |
72
+ | Vitest unit tests | 166 |
73
+ | Vitest service tests | 44 |
74
+ | Source-adjacent tests | 66 |
75
+ | Vitest integration tests | 172 |
76
+ | Vitest CLI tests | 78 |
77
+ | Vitest contract tests | 19 |
78
+ | Vitest security tests | 9 |
79
+ | Vitest subscription tests | 6 |
80
80
  | Vitest agent tests | 1 |
81
81
  | Vitest fixture tests | 1 |
82
82
  | Vitest helper tests | 1 |
@@ -84,8 +84,8 @@ flowchart TD
84
84
  | Frontend Vitest tests | 9 |
85
85
  | Playwright E2E tests | 22 |
86
86
  | Playwright Inspector E2E tests | 4 |
87
- | Tests Performance | 1 |
88
- | Tests Scripts | 3 |
87
+ | Tests Performance | 2 |
88
+ | Tests Scripts | 4 |
89
89
 
90
90
  ## Primary validation commands
91
91
  - `npm test`
@@ -97,7 +97,7 @@ flowchart TD
97
97
  - `npm run validate:doc-deps`
98
98
 
99
99
  ## CI lanes
100
- - Baseline CI runs `type-check`, `lint`, `lint:site-copy`, `npm test`, `validate:coverage`, `validate:test-catalog`, and `validate:doc-deps`.
100
+ - Baseline CI runs `type-check`, `lint`, `lint:site-copy`, `npm test`, `validate:coverage`, `generate:test-catalog`, and `validate:doc-deps`.
101
101
  - Frontend CI runs `npm run test:frontend`.
102
102
  - Site/export CI runs route, locale, and export validation tasks.
103
103
  - Python SDK CI runs `pytest packages/client-python/tests/ -v` on Python 3.12.
@@ -109,7 +109,7 @@ flowchart TD
109
109
  **Runner:** `vitest`
110
110
  **Command:** `npm test -- tests/unit`
111
111
  **Requirements:** Basic `.env` if required by the module under test.
112
- **Files (162):**
112
+ **Files (166):**
113
113
  - `tests/unit/aauth_admission.test.ts`
114
114
  - `tests/unit/aauth_attestation_apple_se.test.ts`
115
115
  - `tests/unit/aauth_attestation_revocation.test.ts`
@@ -152,6 +152,7 @@ flowchart TD
152
152
  - `tests/unit/config_data_dir_resolution.test.ts`
153
153
  - `tests/unit/config_db_backend.test.ts`
154
154
  - `tests/unit/config_sqlite_path.test.ts`
155
+ - `tests/unit/config_trusted_callbacks.test.ts`
155
156
  - `tests/unit/content_field_store_warning.test.ts`
156
157
  - `tests/unit/conversation_schema_bootstrap.test.ts`
157
158
  - `tests/unit/conversation_session_uuid_bridge.test.ts`
@@ -181,6 +182,7 @@ flowchart TD
181
182
  - `tests/unit/external_actor_provenance.test.ts`
182
183
  - `tests/unit/features/FU-2026-Q3-aauth-inspector-attestation-viz/agent_badge_tier_icon.test.ts`
183
184
  - `tests/unit/field_constraints.test.ts`
185
+ - `tests/unit/file_input_diagnostics.test.ts`
184
186
  - `tests/unit/github_issue_thread.test.ts`
185
187
  - `tests/unit/github_mirror_guidance.test.ts`
186
188
  - `tests/unit/github_pages_asset_paths.test.ts`
@@ -228,6 +230,7 @@ flowchart TD
228
230
  - `tests/unit/product_feedback_schema.test.ts`
229
231
  - `tests/unit/protected_entity_types.test.ts`
230
232
  - `tests/unit/pull_request_schema.test.ts`
233
+ - `tests/unit/readiness_probe.test.ts`
231
234
  - `tests/unit/recover_json_array_string.test.ts`
232
235
  - `tests/unit/relationship_batch_schemas.test.ts`
233
236
  - `tests/unit/relationship_reducer.test.ts`
@@ -264,6 +267,7 @@ flowchart TD
264
267
  - `tests/unit/submit_issue_dx.test.ts`
265
268
  - `tests/unit/subscription_types.test.ts`
266
269
  - `tests/unit/substrate_event_bus.test.ts`
270
+ - `tests/unit/test_prerequisites.test.ts`
267
271
  - `tests/unit/timeline_events.test.ts`
268
272
  - `tests/unit/timeline_query.test.ts`
269
273
  - `tests/unit/turn_summary_widget.test.ts`
@@ -278,7 +282,7 @@ flowchart TD
278
282
  **Runner:** `vitest`
279
283
  **Command:** `npm test -- tests/services`
280
284
  **Requirements:** Basic `.env` if required by the module under test.
281
- **Files (43):**
285
+ **Files (44):**
282
286
  - `tests/services/auto_enhancement_converter_detection.test.ts`
283
287
  - `tests/services/auto_enhancement_processor.test.ts`
284
288
  - `tests/services/capability_registry.test.ts`
@@ -317,6 +321,7 @@ flowchart TD
317
321
  - `tests/services/schema_registry_bootstrap.test.ts`
318
322
  - `tests/services/schema_registry_incremental.test.ts`
319
323
  - `tests/services/schema_seeding_fresh_instance_gap.test.ts`
324
+ - `tests/services/schema_seeding_preserves_custom_identity.test.ts`
320
325
  - `tests/services/session_seed_schema.test.ts`
321
326
  - `tests/services/summary.test.ts`
322
327
  - `tests/services/sync_issues_from_github.test.ts`
@@ -328,7 +333,7 @@ flowchart TD
328
333
  **Runner:** `vitest`
329
334
  **Command:** `npm test -- src`
330
335
  **Requirements:** Basic `.env` if required by the module under test.
331
- **Files (65):**
336
+ **Files (66):**
332
337
  - `src/cli/parse_cli_corrected_value.test.ts`
333
338
  - `src/crypto/crypto.test.ts`
334
339
  - `src/proxy/mcp_stdio_proxy.test.ts`
@@ -382,6 +387,7 @@ flowchart TD
382
387
  - `src/services/plans/seed_schema.test.ts`
383
388
  - `src/services/rendered_page/conformance.test.ts`
384
389
  - `src/services/rendered_page/publish.test.ts`
390
+ - `src/services/route_shadowing.test.ts`
385
391
  - `src/services/sync/peer_health.test.ts`
386
392
  - `src/shared/action_handlers/entity_handlers.test.ts`
387
393
  - `src/shared/action_schemas.test.ts`
@@ -400,7 +406,7 @@ flowchart TD
400
406
  **Runner:** `vitest`
401
407
  **Command:** `npm run test:integration` or `npx vitest run tests/integration`
402
408
  **Requirements:** Database configured; remote-dependent subsets additionally need `RUN_REMOTE_TESTS=1`.
403
- **Files (165):**
409
+ **Files (172):**
404
410
  - `tests/integration/aauth_attribution_stamping.test.ts`
405
411
  - `tests/integration/aauth_mcp_capability_parity.test.ts`
406
412
  - `tests/integration/aauth_mcp_initialize_admission.test.ts`
@@ -417,6 +423,7 @@ flowchart TD
417
423
  - `tests/integration/agentic_eval_matrix.test.ts`
418
424
  - `tests/integration/agents_directory_api.test.ts`
419
425
  - `tests/integration/anonymous_write_policy.test.ts`
426
+ - `tests/integration/attachment_resolution_equivalence.test.ts`
420
427
  - `tests/integration/attribution_parity.test.ts`
421
428
  - `tests/integration/auto_link_retraction_organization_change.test.ts`
422
429
  - `tests/integration/cli_init_bootstrap.test.ts`
@@ -434,6 +441,7 @@ flowchart TD
434
441
  - `tests/integration/csp_local_http.test.ts`
435
442
  - `tests/integration/cursor_hook_stop_backfill.test.ts`
436
443
  - `tests/integration/dashboard_stats.test.ts`
444
+ - `tests/integration/db_abort_client_disconnect_survival.test.ts`
437
445
  - `tests/integration/describe_entity_type.test.ts`
438
446
  - `tests/integration/describe_instance_policy_auth.test.ts`
439
447
  - `tests/integration/docs_route.test.ts`
@@ -443,6 +451,7 @@ flowchart TD
443
451
  - `tests/integration/entity_queries_cursor.test.ts`
444
452
  - `tests/integration/entity_queries_status_column.test.ts`
445
453
  - `tests/integration/entity_queries.test.ts`
454
+ - `tests/integration/entity_query_deleted_count.test.ts`
446
455
  - `tests/integration/entity_search_mode.test.ts`
447
456
  - `tests/integration/events_stream.test.ts`
448
457
  - `tests/integration/field_converters.test.ts`
@@ -484,9 +493,11 @@ flowchart TD
484
493
  - `tests/integration/mcp_graph_variations.test.ts`
485
494
  - `tests/integration/mcp_handler_cross_user_scoping.test.ts`
486
495
  - `tests/integration/mcp_invalid_bearer_auth.test.ts`
496
+ - `tests/integration/mcp_list_relationships_entity_filters.test.ts`
487
497
  - `tests/integration/mcp_npm_check_update_capability_delta.test.ts`
488
498
  - `tests/integration/mcp_npm_check_update.test.ts`
489
499
  - `tests/integration/mcp_oauth_token_endpoint.test.ts`
500
+ - `tests/integration/mcp_oauth_trusted_callback.test.ts`
490
501
  - `tests/integration/mcp_query_variations.test.ts`
491
502
  - `tests/integration/mcp_relationship_variations.test.ts`
492
503
  - `tests/integration/mcp_resource_variations.test.ts`
@@ -496,6 +507,7 @@ flowchart TD
496
507
  - `tests/integration/mcp_schema_variations.test.ts`
497
508
  - `tests/integration/mcp_session_404_reconnect.test.ts`
498
509
  - `tests/integration/mcp_stdio_attribution.test.ts`
510
+ - `tests/integration/mcp_store_attribution_policy.test.ts`
499
511
  - `tests/integration/mcp_store_canonical_name_unknown_fields.test.ts`
500
512
  - `tests/integration/mcp_store_intra_batch_relationships.test.ts`
501
513
  - `tests/integration/mcp_store_parquet.test.ts`
@@ -536,6 +548,7 @@ flowchart TD
536
548
  - `tests/integration/seed_then_works_at_e2e.test.ts`
537
549
  - `tests/integration/session_introspection.test.ts`
538
550
  - `tests/integration/snapshot_ingestion_cutoff.test.ts`
551
+ - `tests/integration/snapshot_seam_callers.test.ts`
539
552
  - `tests/integration/standing_rules_initialize_effect.test.ts`
540
553
  - `tests/integration/store_builtin_identity_opt_out_schemas.test.ts`
541
554
  - `tests/integration/store_canonical_name_recompute.test.ts`
@@ -572,7 +585,7 @@ flowchart TD
572
585
  **Runner:** `vitest`
573
586
  **Command:** `npm test -- tests/cli`
574
587
  **Requirements:** Basic `.env`; some tests provision temp config homes automatically.
575
- **Files (77):**
588
+ **Files (78):**
576
589
  - `tests/cli/api_client_offline_fallback.test.ts`
577
590
  - `tests/cli/backup_verify.test.ts`
578
591
  - `tests/cli/cli_access_commands.test.ts`
@@ -628,6 +641,7 @@ flowchart TD
628
641
  - `tests/cli/discovery_codex_sessions.test.ts`
629
642
  - `tests/cli/discovery_harness.test.ts`
630
643
  - `tests/cli/extract_user_cli_args.test.ts`
644
+ - `tests/cli/ingest_agent_sessions.test.ts`
631
645
  - `tests/cli/instance_scripts.test.ts`
632
646
  - `tests/cli/instance_skills_client.test.ts`
633
647
  - `tests/cli/instance_skills.test.ts`
@@ -656,19 +670,23 @@ flowchart TD
656
670
  **Runner:** `vitest`
657
671
  **Command:** `npm test -- tests/contract`
658
672
  **Requirements:** Generated contract artifacts present when the suite expects them.
659
- **Files (15):**
673
+ **Files (19):**
660
674
  - `tests/contract/cli_handler_dist_smoke.test.ts`
661
675
  - `tests/contract/contract_mapping.test.ts`
662
676
  - `tests/contract/contract_mcp_cli_parity.test.ts`
677
+ - `tests/contract/deploy_workflow_config.test.ts`
678
+ - `tests/contract/fly_deploy_config.test.ts`
663
679
  - `tests/contract/get_entities_alias.test.ts`
664
680
  - `tests/contract/ironclaw_integration.test.ts`
665
681
  - `tests/contract/legacy_payloads/replay.test.ts`
682
+ - `tests/contract/mcp_oauth_authorize_responses.test.ts`
666
683
  - `tests/contract/mcp_stdio_output_safety.test.ts`
667
684
  - `tests/contract/mcp_tool_dispatch_coverage.test.ts`
668
685
  - `tests/contract/openapi_schema.test.ts`
669
686
  - `tests/contract/openclaw_plugin.test.ts`
670
687
  - `tests/contract/package_contents.test.ts`
671
688
  - `tests/contract/package_scripts.test.ts`
689
+ - `tests/contract/relationship_type_enum_parity.test.ts`
672
690
  - `tests/contract/sdk_client_store_shape.test.ts`
673
691
  - `tests/contract/update_schema_incremental_canonical_parity_2018.test.ts`
674
692
  - `tests/contract/vite_config.test.ts`
@@ -678,12 +696,14 @@ flowchart TD
678
696
  **Runner:** `vitest`
679
697
  **Command:** `npx vitest run tests/security`
680
698
  **Requirements:** Use alongside the dedicated security validation scripts when changing auth or route protection.
681
- **Files (7):**
699
+ **Files (9):**
682
700
  - `tests/security/auth_topology_matrix.test.ts`
683
701
  - `tests/security/cross_user_read_scoping.test.ts`
684
702
  - `tests/security/ed25519_forged_key_auth_bypass.test.ts`
703
+ - `tests/security/mcp_resource_tenant_isolation.test.ts`
685
704
  - `tests/security/rendered_page_csp.test.ts`
686
705
  - `tests/security/sandbox_mode_resolver.test.ts`
706
+ - `tests/security/schemas_per_type_scope_guard.test.ts`
687
707
  - `tests/security/sort_by_sql_injection.test.ts`
688
708
  - `tests/security/tenant_isolation_matrix.test.ts`
689
709
 
@@ -692,9 +712,10 @@ flowchart TD
692
712
  **Runner:** `vitest`
693
713
  **Command:** `npx vitest run tests/subscriptions`
694
714
  **Requirements:** Basic `.env`; some tests start an in-process HTTP server.
695
- **Files (5):**
715
+ **Files (6):**
696
716
  - `tests/subscriptions/durable_event_log.test.ts`
697
717
  - `tests/subscriptions/guest_write_rate_limit_routing.test.ts`
718
+ - `tests/subscriptions/sse_hub_dead_client_eviction.test.ts`
698
719
  - `tests/subscriptions/sse_ring_gap_detection.test.ts`
699
720
  - `tests/subscriptions/subscription_guest_auth.test.ts`
700
721
  - `tests/subscriptions/subscription_loop_prevention.test.ts`
@@ -792,7 +813,8 @@ flowchart TD
792
813
  **Runner:** `vitest`
793
814
  **Command:** `npx vitest run tests/performance`
794
815
  **Requirements:** Basic `.env` if required by the module under test.
795
- **Files (1):**
816
+ **Files (2):**
817
+ - `tests/performance/entity_query_count.bench.test.ts`
796
818
  - `tests/performance/graph_traversal.bench.test.ts`
797
819
 
798
820
  ### Tests Scripts
@@ -800,8 +822,9 @@ flowchart TD
800
822
  **Runner:** `vitest`
801
823
  **Command:** `npx vitest run tests/scripts`
802
824
  **Requirements:** Basic `.env` if required by the module under test.
803
- **Files (3):**
825
+ **Files (4):**
804
826
  - `tests/scripts/bundles_scaffold.test.ts`
827
+ - `tests/scripts/generate_automated_test_catalog.test.ts`
805
828
  - `tests/scripts/launchd_cli_sync_tooling.test.ts`
806
829
  - `tests/scripts/validate_libsql_migration.test.ts`
807
830
 
@@ -121,7 +121,7 @@ See `docs/testing/automated_test_catalog.md` for the canonical repo-wide invento
121
121
  - The catalog is generated by `npm run generate:test-catalog`.
122
122
  - When automated test files are added, removed, moved, or renamed, regenerate the catalog in the same change.
123
123
  - When test commands, CI lanes, or default-vs-optional execution modes change, update both the catalog and this policy doc in the same change.
124
- - `npm run validate:test-catalog` must pass before merge.
124
+ - `npm run validate:test-catalog` should pass before merge, but does not block it: the baseline CI lane runs `npm run generate:test-catalog`, which renders the catalog rather than failing on drift.
125
125
 
126
126
  ## High-risk surface classes (mandatory coverage)
127
127
 
@@ -19,7 +19,7 @@ import { admitFromAAuthContext } from "../services/aauth_admission.js";
19
19
  import { getAAuthContextFromRequest } from "./aauth_verify.js";
20
20
  import { getRequestContext, runWithRequestContext } from "../services/request_context.js";
21
21
  /** Skip admission lookups on cheap public discovery endpoints. */
22
- const PUBLIC_DISCOVERY_PREFIXES = ["/.well-known/", "/health"];
22
+ const PUBLIC_DISCOVERY_PREFIXES = ["/.well-known/", "/health", "/ready"];
23
23
  function isPublicDiscovery(req) {
24
24
  const path = req.path || req.url || "";
25
25
  return PUBLIC_DISCOVERY_PREFIXES.some((prefix) => path.startsWith(prefix));
@@ -1 +1 @@
1
- {"version":3,"file":"aauth_admission.js","sourceRoot":"","sources":["../../src/middleware/aauth_admission.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAG1F,kEAAkE;AAClE,MAAM,yBAAyB,GAA0B,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC;AAEtF,SAAS,iBAAiB,CAAC,GAAY;IACrC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC;IACvC,OAAO,yBAAyB,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc;IAC5B,OAAO,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAQ,EAAE;QAC/D,IAAI,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3B,IAAI,EAAE,CAAC;YACP,OAAO;QACT,CAAC;QACD,MAAM,GAAG,GAA+B,0BAA0B,CAAC,GAAG,CAAC,CAAC;QACxE,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,mEAAmE;YACnE,4DAA4D;YAC5D,gDAAgD;YAChD,MAAM,QAAQ,GAA0B;gBACtC,QAAQ,EAAE,KAAK;gBACf,MAAM,EAAE,YAAY;aACrB,CAAC;YACF,cAAc,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;YAC9B,MAAM,KAAK,GAAG,iBAAiB,EAAE,CAAC;YAClC,qBAAqB,CACnB;gBACE,aAAa,EAAE,KAAK,EAAE,aAAa,IAAI,IAAI;gBAC3C,mBAAmB,EAAE,KAAK,EAAE,mBAAmB,IAAI,IAAI;gBACvD,cAAc,EAAE,QAAQ;aACzB,EACD,GAAG,EAAE;gBACH,IAAI,EAAE,CAAC;YACT,CAAC,CACF,CAAC;YACF,OAAO;QACT,CAAC;QAED,KAAK,qBAAqB,CAAC,GAAG,CAAC;aAC5B,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE;YACjB,MAAM,SAAS,GAA0B;gBACvC,QAAQ,EAAE,QAAQ,CAAC,QAAQ;gBAC3B,MAAM,EAAE,QAAQ,CAAC,MAAM;gBACvB,OAAO,EAAE,QAAQ,CAAC,OAAO;gBACzB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;gBAC3B,WAAW,EAAE,QAAQ,CAAC,WAAW;gBACjC,YAAY,EAAE,QAAQ,CAAC,YAAY;aACpC,CAAC;YACF,cAAc,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;YAC/B,IAAI,SAAS,CAAC,QAAQ,IAAI,SAAS,CAAC,OAAO,EAAE,CAAC;gBAC3C,GAAkD,CAAC,mBAAmB;oBACrE,SAAS,CAAC,OAAO,CAAC;YACtB,CAAC;YACD,MAAM,KAAK,GAAG,iBAAiB,EAAE,CAAC;YAClC,qBAAqB,CACnB;gBACE,aAAa,EAAE,KAAK,EAAE,aAAa,IAAI,IAAI;gBAC3C,mBAAmB,EAAE,KAAK,EAAE,mBAAmB,IAAI,IAAI;gBACvD,cAAc,EAAE,SAAS;aAC1B,EACD,GAAG,EAAE;gBACH,IAAI,EAAE,CAAC;YACT,CAAC,CACF,CAAC;QACJ,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YACb,gEAAgE;YAChE,+DAA+D;YAC/D,8CAA8C;YAC9C,MAAM,QAAQ,GAA0B;gBACtC,QAAQ,EAAE,KAAK;gBACf,MAAM,EAAE,UAAU;aACnB,CAAC;YACF,cAAc,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;YAC9B,MAAM,KAAK,GAAG,iBAAiB,EAAE,CAAC;YAClC,qBAAqB,CACnB;gBACE,aAAa,EAAE,KAAK,EAAE,aAAa,IAAI,IAAI;gBAC3C,mBAAmB,EAAE,KAAK,EAAE,mBAAmB,IAAI,IAAI;gBACvD,cAAc,EAAE,QAAQ;aACzB,EACD,GAAG,EAAE;gBACH,IAAI,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YACrD,CAAC,CACF,CAAC;QACJ,CAAC,CAAC,CAAC;IACP,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,GAAY,EAAE,SAAgC;IACnE,GAA4D,CAAC,cAAc,GAAG,SAAS,CAAC;AAC3F,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,4BAA4B,CAAC,GAAY;IACvD,OAAQ,GAA4D,CAAC,cAAc,IAAI,IAAI,CAAC;AAC9F,CAAC"}
1
+ {"version":3,"file":"aauth_admission.js","sourceRoot":"","sources":["../../src/middleware/aauth_admission.ts"],"names":[],"mappings":"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"}