Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions docs/api-error-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@ txid once the owner daemon lands). A stale/mismatched cursor returns a
| `add_observations` | unknown entity → `ENTITY_NOT_FOUND`; limit breaches → `LIMIT_EXCEEDED` (atomic, all offenders) | per-entity `{entityName, addedObservations, alreadyPresent}` |
| `delete_entities` / `delete_relations` / `delete_observations` | nothing errors — absence is `notFound` (R4) | `{deleted, notFound}` ledgers |
| `sequentialthinking` | unknown `previousCtxId` → `ENTITY_NOT_FOUND` (chain integrity); limits → `LIMIT_EXCEEDED` | `{ctxId, linkedTo}` |
| `kb_load` | bad extension / unreadable file → `INVALID_FILE`; entity & relation stages per their rows | per-stage ledger (document, stats, entity/relation outcomes) |
| `kb_load` | bad extension / unreadable file → `INVALID_FILE`; entity & relation stages per their rows | counts per stage: `entities {created, existing}`, `relations {created, skippedDuplicates}` |
| `open_nodes` | ALL requested names missing → `ENTITY_NOT_FOUND` | graph result + `missing[]` for partial misses |
| `get_neighbors` | unknown start → `ENTITY_NOT_FOUND` | neighbors (depth 0 = immediate) |
| `search_nodes` | invalid ERE → `INVALID_REGEX`; literal query, zero matches → `NO_MATCHES` | paginated graph |
| `get_entities_by_type` | type absent from the data-derived type set → `TYPE_NOT_FOUND` | entities of that type |
| `random_walk` | unknown start → `ENTITY_NOT_FOUND` | `{entity, path, modeUsed, stopReason}` |
| `random_walk` | unknown start → `ENTITY_NOT_FOUND` | `{entity, path, modeUsed, fallbackSteps}` (`modeUsed` = `merw` \| `uniform` \| `merw+fallback`) |
| `find_path` | — (β-contract already reports `targetReached` / `budgetExhausted` / `note`) | unchanged — the reference pattern for R1-style reporting |
| reads (`get_entity_types`, `get_relation_types`, `get_stats`, `get_orphaned_entities`, `validate_graph`, `decode_timestamp`) | unchanged | unchanged |

Expand All @@ -78,12 +78,16 @@ faults; Q1 = atomic-fail on missing endpoints; Q2/Q3 = error.

## Status

- **This change (envelope PR):** R1 (envelope + codes + dispatch conversion),
R2 (validate-first atomicity for `create_entities`, `create_relations`,
`add_observations`, `sequentialthinking`), R4 (drops→errors:
`ENDPOINT_MISSING`, all-missing `open_nodes`, `TYPE_NOT_FOUND`, unknown
starts), `INVALID_FILE`, `INVALID_REGEX`, `NO_MATCHES` envelope.
- **Ledger PR (next):** R3 result ledgers + report fields (`missing[]`,
`modeUsed`, `alreadyPresent`), R5 cursor fingerprinting.
- **Envelope PR (#247, merged):** R1 (envelope + codes + dispatch
conversion), R2 (validate-first atomicity for `create_entities`,
`create_relations`, `add_observations`, `sequentialthinking`), R4
(drops→errors: `ENDPOINT_MISSING`, all-missing `open_nodes`,
`TYPE_NOT_FOUND`, unknown starts), `INVALID_FILE`, `INVALID_REGEX`,
`NO_MATCHES` envelope.
- **Ledger PR:** R3 result ledgers + report fields
(`created`/`existing`/`skippedDuplicates`/`notFound`/`alreadyPresent`,
`open_nodes.missing[]`, `random_walk.modeUsed`/`fallbackSteps`,
`sequentialthinking.linkedTo`).
- **Next:** R5 cursor fingerprinting (`CURSOR_STALE`).
- **v4 line (`feat/libsegstore`):** inherits this policy at reconciliation;
kbd4's batch=txn=commit model is the natural home for R2 atomicity.
6 changes: 5 additions & 1 deletion native/graph.c
Original file line number Diff line number Diff line change
Expand Up @@ -866,9 +866,11 @@ u32 graph_compute_merw_psi(graph_t *g, double alpha, u32 max_iter, double tol) {
}

u32 graph_random_walk(graph_t *g, u64 start, u32 depth, u32 direction, int merw_mode,
u64 seed, int avoid_cycles, u64 *out_path, u32 max_path) {
u64 seed, int avoid_cycles, u64 *out_path, u32 max_path,
u32 *out_uniform_steps) {
u64 st = seed ? seed : g_rng;
u32 plen = 0;
u32 uniform_steps = 0;
if (max_path >= 1) out_path[plen] = start;
plen = 1;
u64 cur = start;
Expand Down Expand Up @@ -905,6 +907,7 @@ u32 graph_random_walk(graph_t *g, u64 start, u32 depth, u32 direction, int merw_
double r = rng_d(&st) * total_psi, cum = 0; chosen = cand[nc - 1];
for (u32 j = 0; j < nc; j++) { cum += cpsi[j]; if (r <= cum) { chosen = cand[j]; break; } }
} else {
if (merw_mode) uniform_steps++; /* psi unavailable at this step: fallback */
u32 ix = (u32)(rng_d(&st) * nc); if (ix >= nc) ix = nc - 1; chosen = cand[ix];
}
free(cand); free(cpsi);
Expand All @@ -913,6 +916,7 @@ u32 graph_random_walk(graph_t *g, u64 start, u32 depth, u32 direction, int merw_
plen++;
}
if (!seed) g_rng = st;
if (out_uniform_steps) *out_uniform_steps = uniform_steps;
return plen;
}

Expand Down
7 changes: 5 additions & 2 deletions native/graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,11 @@ u32 graph_structural_sample(graph_t *g, u32 iterations, double damping); /*
u32 graph_compute_merw_psi(graph_t *g, double alpha, u32 max_iter, double tol); /* iters run */
/* random walk; mode: 1=merw (weighted by psi), 0=uniform; seed 0 = use global rng.
* avoid_cycles: 1 = self-avoiding (never revisits a node; stops early when every
* neighbor is already on the path). Returns path node count. */
* neighbor is already on the path). Returns path node count.
* out_uniform_steps (optional): number of merw-requested steps that fell back to
* uniform sampling because psi weighting was unavailable at that step. */
u32 graph_random_walk(graph_t *g, u64 start, u32 depth, u32 direction, int merw_mode,
u64 seed, int avoid_cycles, u64 *out_path, u32 max_path);
u64 seed, int avoid_cycles, u64 *out_path, u32 max_path,
u32 *out_uniform_steps);

#endif /* GRAPH_H */
8 changes: 6 additions & 2 deletions native/graphbind.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,12 @@ static napi_value n_merw(napi_env env, napi_callback_info info) { ARG
static napi_value n_seed(napi_env env, napi_callback_info info) { ARGS(2); (void)unwrap(env, argv[0]); graph_seed_rng(getU64(env, argv[1])); return NULL; }
static napi_value n_random_walk(napi_env env, napi_callback_info info) {
ARGS(7); STORE; u32 depth = getU32(env, argv[2]); u32 cap = depth + 1; u64 *out = malloc((size_t)cap * 8);
u32 n = graph_random_walk(s->g, getU64(env, argv[1]), depth, getU32(env, argv[3]), getU32(env, argv[4]), getU64(env, argv[5]), getU32(env, argv[6]), out, cap);
napi_value r = u64arr(env, out, n < cap ? n : cap); free(out); return r;
u32 uniform_steps = 0;
u32 n = graph_random_walk(s->g, getU64(env, argv[1]), depth, getU32(env, argv[3]), getU32(env, argv[4]), getU64(env, argv[5]), getU32(env, argv[6]), out, cap, &uniform_steps);
napi_value r; napi_create_object(env, &r);
napi_set_named_property(env, r, "path", u64arr(env, out, n < cap ? n : cap));
napi_set_named_property(env, r, "uniformSteps", mkU32(env, uniform_steps));
free(out); return r;
}

/* ---- validate ---- */
Expand Down
2 changes: 1 addition & 1 deletion native/op_bench.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ int main(int argc, char **argv) {
ADAPT("find_path_d6", sink32 = graph_find_path(g, off[idx[_k]], off[idx2[_k]], 6, DIR_ANY, out, cap));
ADAPT("search", sink32 = graph_search(g, pat[_k], out, cap));
ADAPT("entities_by_type", sink32 = graph_entities_by_type(g, (const u8 *)tynames[tpix[_k]], tylen[tpix[_k]], out, cap));
ADAPT("random_walk_d5", sink32 = graph_random_walk(g, off[idx[_k]], 5, DIR_ANY, 1, seed[_k], 0, out, 8));
ADAPT("random_walk_d5", sink32 = graph_random_walk(g, off[idx[_k]], 5, DIR_ANY, 1, seed[_k], 0, out, 8, NULL));
ADAPT("inc_walker_visit", graph_inc_walker_visit(g, off[idx[_k]]));
ADAPT("structural_sample", graph_structural_sample(g, 1, 0.85));
ADAPT("compute_merw_psi", graph_compute_merw_psi(g, 0.85, 100, 1e-8));
Expand Down
Loading
Loading