Skip to content

Commit 3b98fad

Browse files
easelclaude
andcommitted
feat(gateway): expose direction query param in HTTP traverse endpoint
Exposes TraverseDirection via a ?direction=forward|reverse query parameter on the GET /traverse/{collection}/{id} endpoint. Enables reverse link traversal for nexiq integration (findLinkedReverse for role-scoped access control). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent ce95af1 commit 3b98fad

2 files changed

Lines changed: 9 additions & 3 deletions

File tree

.ddx/beads.jsonl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
{"acceptance":"axon schema show \u003ccollection\u003e displays the schema; axon schema validate \u003ccollection\u003e \u003cfile\u003e validates a JSON file against the schema","claimed-at":"2026-04-06T01:58:31Z","claimed-machine":"sindri","claimed-pid":"1526266","created_at":"2026-04-06T01:58:25.219274128Z","id":"axon-1c02df72","issue_type":"task","labels":["helix","phase:build","kind:execution","area:cli"],"owner":"erik","priority":2,"status":"closed","title":"CLI: add schema show/validate subcommands","updated_at":"2026-04-06T02:04:16.791243828Z"}
2424
{"claimed-at":"2026-04-05T02:25:09Z","claimed-pid":"298878","created_at":"2026-04-05T01:57:29.786966191Z","description":"FEAT-001 says 'container of documents', FEAT-003 says 'document.create', FEAT-004 is titled 'Document Operations'. Code consistently uses 'entity'. Specs must match.","id":"axon-1c6adf26","issue_type":"task","labels":["helix","phase:frame","kind:execution","area:planning"],"owner":"erik","priority":1,"status":"closed","title":"Update FEAT-001 through FEAT-004 terminology: document → entity","updated_at":"2026-04-05T02:30:43.264528141Z"}
2525
{"acceptance":"gRPC client can create collection, CRUD entities, create links, query audit log; HTTP gateway serves same operations; errors are structured with field-level details","created_at":"2026-04-04T21:53:08.63851001Z","dependencies":[{"issue_id":"axon-1da8992c","depends_on_id":"axon-b0c2de0e","type":"blocks","created_at":"2026-04-04T21:53:16Z"},{"issue_id":"axon-1da8992c","depends_on_id":"axon-d13bf628","type":"blocks","created_at":"2026-04-04T21:53:16Z"}],"description":"gRPC server (tonic) exposing collection, entity, link, audit, and transaction operations. HTTP/JSON gateway. Protobuf definitions as source of truth. Structured error responses.","id":"axon-1da8992c","issue_type":"task","labels":["helix","blocked"],"notes":"BLOCKED [2026-04-04T18:50:14-04:00]: intractable after 4 attempts with exponential backoff","priority":2,"status":"closed","title":"Implement gRPC API server","updated_at":"2026-04-04T22:51:09.638535087Z"}
26-
{"acceptance":"1. Playwright tests start axon serve on port 4170 and hit the real API. 2. Tests create a tenant and collection with a schema via the UI. 3. Tests create, edit, and delete entities via the UI. 4. Tests navigate to the audit log and verify entries. 5. Tests verify entity list filtering and pagination. 6. All Playwright tests pass. 7. Any UI issues found are documented as new beads or fixed inline.","created_at":"2026-04-12T01:56:30.947326835Z","dependencies":[{"issue_id":"axon-1e0797af","depends_on_id":"axon-391bf0a1","type":"blocks","created_at":"2026-04-12T01:56:35Z"}],"description":"End-to-end validation using the built-in admin web UI via Playwright. Start axon serve, exercise the full UI: create tenants, define collection schemas, insert sample data, navigate entity browser, inspect audit log, traverse links. Verify the UI works against the real server (not mocked APIs). Update Playwright config to use port 4170. Write E2E tests that cover the core user journey: tenant creation, schema definition, entity CRUD, audit inspection. Fix any issues found.","id":"axon-1e0797af","issue_type":"task","labels":["helix","phase:build","area:testing"],"priority":2,"status":"open","title":"chore: dogfood via Playwright E2E — tenants, schemas, data, full UI navigation","updated_at":"2026-04-12T01:56:43.900629489Z"}
26+
{"acceptance":"1. Playwright tests start axon serve on port 4170 and hit the real API. 2. Tests create a tenant and collection with a schema via the UI. 3. Tests create, edit, and delete entities via the UI. 4. Tests navigate to the audit log and verify entries. 5. Tests verify entity list filtering and pagination. 6. All Playwright tests pass. 7. Any UI issues found are documented as new beads or fixed inline.","created_at":"2026-04-12T01:56:30.947326835Z","dependencies":[{"issue_id":"axon-1e0797af","depends_on_id":"axon-391bf0a1","type":"blocks","created_at":"2026-04-12T01:56:35Z"}],"description":"End-to-end validation using the built-in admin web UI via Playwright. Start axon serve, exercise the full UI: create tenants, define collection schemas, insert sample data, navigate entity browser, inspect audit log, traverse links. Verify the UI works against the real server (not mocked APIs). Update Playwright config to use port 4170. Write E2E tests that cover the core user journey: tenant creation, schema definition, entity CRUD, audit inspection. Fix any issues found.","id":"axon-1e0797af","issue_type":"task","labels":["helix","phase:build","area:testing"],"priority":2,"status":"closed","title":"chore: dogfood via Playwright E2E — tenants, schemas, data, full UI navigation","updated_at":"2026-04-12T02:56:09.893365128Z"}
2727
{"claimed-at":"2026-04-06T19:32:52Z","claimed-machine":"sindri","claimed-pid":"1136407","created_at":"2026-04-06T19:13:43.195623164Z","description":"After gate evaluation on write, persist gate pass/fail to storage. Add GateFilter to FilterNode. Enable queries like {type: gate, gate: complete, pass: true}.","id":"axon-1ffaf691","issue_type":"task","labels":["helix","p0","FEAT-019"],"owner":"erik","priority":0,"spec-id":"US-067","status":"closed","title":"Persist gate results and enable gate-based queries","updated_at":"2026-04-06T19:37:44.657727842Z"}
2828
{"claimed-at":"2026-04-05T12:36:34Z","claimed-machine":"sindri","claimed-pid":"3803093","created_at":"2026-04-05T01:57:29.685734045Z","dependencies":[{"issue_id":"axon-207e8829","depends_on_id":"axon-d03dd28c","type":"blocks","created_at":"2026-04-05T01:57:43Z"},{"issue_id":"axon-207e8829","depends_on_id":"axon-4cf3fa53","type":"blocks","created_at":"2026-04-05T01:57:44Z"}],"description":"Only INV-002 (cycle test) is fully implemented. INV-001 (no lost updates), INV-003 (audit completeness), INV-004 (audit immutability), INV-005 (schema enforcement), INV-006 (link integrity), INV-007 (version monotonicity), INV-008 (transaction atomicity) need dedicated workloads.","id":"axon-207e8829","issue_type":"task","labels":["helix","phase:test","kind:execution","priority:critical"],"owner":"erik","priority":0,"status":"closed","title":"Implement test plan L1: correctness invariant workloads (INV-001,003-008)","updated_at":"2026-04-05T12:52:22.31493903Z"}
2929
{"claimed-at":"2026-04-06T17:20:03Z","claimed-machine":"sindri","claimed-pid":"2937808","created_at":"2026-04-06T12:15:11.065598044Z","description":"Initial snapshot. Resumable. Boundary cursor. Consumer dedup by audit_id.","id":"axon-212d5343","issue_type":"task","labels":["helix","build","cdc"],"owner":"erik","parent":"axon-cf0472fd","priority":2,"spec-id":"FEAT-021","status":"closed","title":"US-075: Replay events from a point in time (FEAT-021)","updated_at":"2026-04-06T17:20:55.545794175Z"}
@@ -731,3 +731,4 @@
731731
{"acceptance":"axon-render exposes a reusable compiled-template API; the markdown GET path reuses compiled templates per collection instead of reparsing on every request; a test covers cache invalidation after template update","claimed-at":"2026-04-09T06:20:33Z","claimed-machine":"sindri","claimed-pid":"668010","created_at":"2026-04-08T21:33:46.696515701Z","description":"Review finding from fresh-eyes review.\nFile: crates/axon-render/src/lib.rs:260\nCategory: performance\nSeverity: high\nDescription: `axon_render::render` calls `Template::new(template)` on every render. FEAT-026 requires compiled templates to be cached per collection and the documented handler flow says `axon-api` should call `axon_render::render`. With the current public API, downstream code cannot satisfy the per-collection cache requirement without bypassing the crate abstraction and reparsing the template on every request.\nSuggested fix: Introduce a reusable compiled-template API in `axon-render` (for example a wrapper type around `ramhorns::Template` or a `compile()` + `render_compiled()` pair) and update the GET render path to cache compiled templates per collection, invalidating that cache on template updates.","id":"hx-ff473dcd","issue_type":"task","labels":["helix","phase:build","review-finding"],"owner":"erik","priority":0,"spec-id":"crates/axon-render/src/lib.rs","status":"closed","title":"performance: render() recompiles templates on every call","updated_at":"2026-04-09T06:25:36.987300375Z"}
732732
{"created_at":"2026-04-09T22:15:21.408208879Z","description":"\u003ccontext-digest\u003e\n\u003cscope\u003erepo.review.agent-protocol\u003c/scope\u003e\n\u003cgoverning\u003eGoverned by hx-c4b100d5; scope covers GraphQL, MCP, query/protocol behavior, and agent-facing discovery/read contracts against FEAT-016 and ADR-013.\u003c/governing\u003e\n\u003c/context-digest\u003e\nRepo-scope alignment review area governed by hx-c4b100d5. Reconcile GraphQL, MCP, query/protocol behavior, and agent-facing discovery/read contracts against FEAT-016 and ADR-013.","id":"hx-ff4a4bcc","issue_type":"task","labels":["helix","phase:review","kind:review","area:api"],"notes":"2026-04-09 repo alignment under hx-c4b100d5 classified this area ALIGNED. FEAT-016 and its current ADR surface match the shipped MCP HTTP and stdio implementation. No new execution follow-up was required. Durable report: docs/helix/06-iterate/alignment-reviews/AR-2026-04-09-repo.md.","parent":"hx-8a9fd3d3","priority":1,"spec-id":"FEAT-016","status":"closed","title":"Review: agent-facing query and protocol surfaces","updated_at":"2026-04-09T22:23:59.159094307Z"}
733733
{"acceptance":"All review passes complete; findings filed as beads; AGENTS.md updated if needed","claimed-at":"2026-04-09T22:10:29Z","claimed-machine":"sindri","claimed-pid":"2321818","created_at":"2026-04-09T22:10:26.076946715Z","description":"\u003ccontext-digest\u003e\n\u003creview-target\u003elast-commit\u003c/review-target\u003e\n\u003ccommit\u003e4ff8baedfe07d8683e1f551dda9cc06938fa97ec\u003c/commit\u003e\n\u003creviewed-issue\u003ehx-f08028c6\u003c/reviewed-issue\u003e\n\u003carea\u003eall\u003c/area\u003e\n\u003cprinciples\u003eSpecification Completeness · Audit is Not Optional · Continuous Validation · Feedback Integration · Simplicity Over Flexibility\u003c/principles\u003e\n\u003cconcerns\u003erust-cargo\u003c/concerns\u003e\n\u003cpractices\u003ePreserve durable tracker traceability; machine-readable evidence in tracker notes; rust-cargo quality gates are authoritative when Rust code changes, but tracker-only reviews may record them as scoped partial when no Rust source changed\u003c/practices\u003e\n\u003cgoverning\u003eReview target is commit 4ff8baedfe07d8683e1f551dda9cc06938fa97ec (fix(tracker): restore escaped tracker evidence [hx-f08028c6]). Governing artifacts are bead hx-f08028c6, refreshed bead hx-c94fad35, AGENTS.md, docs/helix/01-frame/principles.md, docs/helix/01-frame/concerns.md, and HELIX fresh-eyes/measure/report references. The change is expected to restore machine-readable escaping in tracker evidence without introducing new closure-auditability drift.\u003c/governing\u003e\n\u003c/context-digest\u003e\nFresh-eyes review of last commit.\nReview target: last-commit\nReviewed commit: 4ff8baedfe07d8683e1f551dda9cc06938fa97ec\nReviewed implementation bead: hx-f08028c6\nGoverning artifacts: hx-f08028c6, hx-c94fad35, AGENTS.md, principles, concerns, and HELIX review references.","id":"hx-ff690fdf","issue_type":"task","labels":["helix","kind:planning","action:review"],"notes":"\u003cmeasure-results\u003e\n \u003ctimestamp\u003e2026-04-09T22:11:23Z\u003c/timestamp\u003e\n \u003cstatus\u003eISSUES_FOUND\u003c/status\u003e\n \u003cacceptance\u003e\n \u003ccriterion name='All review passes complete; findings filed as beads; AGENTS.md updated if needed' status='pass' evidence='Completed correctness, integration, concern-aware quality, and operational-learning passes for commit 4ff8baedfe07d8683e1f551dda9cc06938fa97ec; filed medium review-finding hx-d705e82e; AGENTS.md remained current.'/\u003e\n \u003c/acceptance\u003e\n \u003cgates\u003e\n \u003cgate concern='tracker' command='git diff HEAD~1 -- .ddx/beads.jsonl' status='pass'/\u003e\n \u003cgate concern='tracker' command='ddx bead show hx-c94fad35 --json' status='pass'/\u003e\n \u003cgate concern='tracker' command='ddx bead show hx-f08028c6 --json' status='pass'/\u003e\n \u003cgate concern='tracker' command='python3 scripts/check_tracker_measure_timestamps.py .ddx/beads.jsonl --id hx-c94fad35 --id hx-f08028c6' status='pass'/\u003e\n \u003cgate concern='rust-cargo' command='concern-aware review: tracker-only scope touched no Rust source, dependency, or UI files' status='pass'/\u003e\n \u003c/gates\u003e\n \u003cfindings total='1' filed='1' critical='0' high='0' medium='1' low='0'/\u003e\n \u003cagents-md-updated\u003eNO\u003c/agents-md-updated\u003e\n \u003clearnings-filed\u003e0\u003c/learnings-filed\u003e\n \u003creview-passes\u003e\n \u003cpass name='correctness' status='pass' evidence='The committed hx-c94fad35 and hx-f08028c6 rows are machine-readable again and the scoped timestamp validator passes for both IDs.'/\u003e\n \u003cpass name='integration' status='issue' evidence='The repaired hx-f08028c6 row still closes its governing bead without a machine-readable closing commit tag, so later tracker queries cannot recover which commit performed the hx-f08028c6 closure. Filed hx-d705e82e.'/\u003e\n \u003cpass name='concern-aware-quality' status='pass' evidence='The change remained within tracker metadata; no rust-cargo or typescript-bun practice drift beyond the filed closure-auditability gap was introduced.'/\u003e\n \u003cpass name='operational-learnings' status='pass' evidence='No new repository instructions, commands, or conventions emerged from this patch, so AGENTS.md did not require updates and no separate learnings bead was needed.'/\u003e\n \u003c/review-passes\u003e\n \u003cevidence\u003e\n \u003ccommand cmd='git show --stat --summary HEAD' status='pass'/\u003e\n \u003ccommand cmd='git diff HEAD~1 -- .ddx/beads.jsonl' status='pass'/\u003e\n \u003ccommand cmd='ddx bead show hx-c94fad35 --json' status='pass'/\u003e\n \u003ccommand cmd='ddx bead show hx-f08028c6 --json' status='pass'/\u003e\n \u003ccommand cmd='python3 scripts/check_tracker_measure_timestamps.py .ddx/beads.jsonl --id hx-c94fad35 --id hx-f08028c6' status='pass'/\u003e\n \u003cfinding-ref id='hx-d705e82e' disposition='filed'/\u003e\n \u003c/evidence\u003e\n\u003c/measure-results\u003e\n\u003creport-summary\u003e\n \u003ctimestamp\u003e2026-04-09T22:11:23Z\u003c/timestamp\u003e\n \u003cstatus\u003ePASS\u003c/status\u003e\n \u003cissue\u003ehx-ff690fdf\u003c/issue\u003e\n \u003ccommit\u003e4ff8baedfe07d8683e1f551dda9cc06938fa97ec\u003c/commit\u003e\n \u003csummary\u003eFresh-eyes review of commit 4ff8baedfe07d8683e1f551dda9cc06938fa97ec found one new medium-severity tracker-governance defect: the repaired hx-f08028c6 closure record still omits the actual closing commit. Follow-on review-finding hx-d705e82e was created; AGENTS.md did not require changes.\u003c/summary\u003e\n\u003c/report-summary\u003e","owner":"erik","priority":0,"spec-id":"4ff8baedfe07d8683e1f551dda9cc06938fa97ec","status":"closed","title":"review: last-commit 4ff8bae tracker escaped evidence","updated_at":"2026-04-09T22:11:59.432384021Z"}
734+
{"id":"axon-d7e8f9a1","title":"feat(gateway): expose direction query param in HTTP traverse endpoint","description":"The HTTP traverse endpoint GET /traverse/{collection}/{id} currently ignores traversal direction (hardcodes Forward in gateway.rs). The TraverseRequest struct and AxonHandler::traverse() already support TraverseDirection::Reverse, but the HTTP handler passes `direction: Default::default()` unconditionally.\n\nAdd a `direction` query parameter to the traverse endpoint. When `direction=reverse` is supplied, follow inbound links (entities that link TO the starting entity). When absent or `direction=forward`, keep the existing forward behavior.\n\nChange required: In crates/axon-server/src/gateway.rs, read `params.get(\"direction\")` and deserialize to TraverseDirection using serde_json or manual match. Pass the parsed direction to TraverseRequest.\n\nThis unblocks the nexiq integration which uses findLinkedReverse(\"users\", userId, \"assigned-to\") for role-scoped access control and dashboard filtering.","acceptance":"GET /traverse/{collection}/{id}?direction=reverse&link_type=assigned-to returns entities whose forward 'assigned-to' links point TO the given entity; existing forward traverse behavior is unchanged (no direction param or direction=forward); at least one API contract test covers reverse traversal; bun test passes","created_at":"2026-04-12T00:00:00Z","updated_at":"2026-04-12T00:00:00Z","status":"open","issue_type":"task","labels":["helix","phase:build","kind:implementation","area:api","area:gateway"],"priority":1,"owner":"erik","spec_id":"crates/axon-server/src/gateway.rs"}

crates/axon-server/src/gateway.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ use axon_api::request::{
3838
ListCollectionsRequest, ListDatabasesRequest, ListNamespaceCollectionsRequest,
3939
ListNamespacesRequest, PutCollectionTemplateRequest, PutSchemaRequest, QueryAuditRequest,
4040
QueryEntitiesRequest, RevertEntityRequest, RollbackCollectionRequest, RollbackEntityRequest,
41-
RollbackEntityTarget, RollbackTransactionRequest, TraverseRequest, UpdateEntityRequest,
41+
RollbackEntityTarget, RollbackTransactionRequest, TraverseDirection, TraverseRequest,
42+
UpdateEntityRequest,
4243
};
4344
use axon_api::response::GetEntityMarkdownResponse;
4445
use axon_audit::AuditLog;
@@ -952,13 +953,17 @@ async fn traverse(
952953
) -> Response {
953954
let link_type = params.get("link_type").cloned();
954955
let max_depth = params.get("max_depth").and_then(|s| s.parse().ok());
956+
let direction = match params.get("direction").map(|s| s.as_str()) {
957+
Some("reverse") => TraverseDirection::Reverse,
958+
_ => TraverseDirection::Forward,
959+
};
955960

956961
match handler.lock().await.traverse(TraverseRequest {
957962
collection: qualify_collection_name(&collection, &current_database),
958963
id: EntityId::new(&id),
959964
link_type,
960965
max_depth,
961-
direction: Default::default(),
966+
direction,
962967
hop_filter: None,
963968
}) {
964969
Ok(resp) => {

0 commit comments

Comments
 (0)