Retirement — leaving the graph empties a vertex, it does not erase it (L4 graph)

retire marks a vertex and its whole subtree logically absent (RFC-0009 §A.1 / §B): the path then reads NOT_FOUND, identical to never-existed. There is no distinct retired status — no such code is allocated, and the collapse is an accepted cost rather than an oversight (reference 02 §Retirement notification).

What to notice

  • The object is not freed. The vertex map is pinned and insert-only (ADR-0057), so an outstanding vertex_handle_t stays dereferenceable after the retirement — the example keeps using it. What tells a holder its cached resolution went stale is retire_generation, bumped by the retirement (ADR-0062). A generation match says the vertex is the same one; it never says the caller may still act on it, so an authorization decision must not be cached this way.

  • Retirement takes the subtree. /zone/air/humidity goes with /zone/air.

  • It is idempotent and silent. Retiring an already-retired vertex succeeds and does nothing; the retirement delivers along no edge and wakes no await (§B.5), which is why disappearance is observable only by polling.

  • Revival inherits nothing. A later local write-creates revives the address, and the revived vertex takes its live ancestor’s ACL policy, never the retired owner’s (§B.6) — a stale grant cannot outlive the retirement.

  • collect() is the embedder’s, and is not needed here. A retired vertex parks its detached value seam iff a handler was installed at registration; these vertices carry none, so nothing is parked. A bus node with peer churn must call graph_t::collect(), and graph_t::parked_seam_count() makes an uncollected park observable (reference 02 §Vertex lifecycle).

Source

 1/*
 2 * SPDX-License-Identifier: Apache-2.0
 3 * SPDX-FileCopyrightText: Copyright 2026 avatarsd LLC
 4 */
 5
 6/**
 7 * @file
 8 * @brief ONE CONCEPT — retirement: leaving the graph is emptying a vertex, not erasing it.
 9 *
10 * `retire` marks a vertex and its whole subtree logically absent (RFC-0009 §A.1/§B): the
11 * path then reads `NOT_FOUND`, identical to never-existed — there is no distinct `retired`
12 * status. The vertex OBJECT is not freed, so an outstanding `vertex_handle_t` stays
13 * dereferenceable (ADR-0057, insert-only); what tells a holder its cached resolution went
14 * stale is `retire_generation`, which is bumped by the retirement (ADR-0062).
15 *
16 * Retirement delivers nothing and wakes no `await` (§B.5): a composite subscriber is never
17 * told a child went away, so disappearance is observable only by re-reading
18 * (`docs/reference/02-graph-model.md` §Retirement notification).
19 *
20 * Runs under ctest as `example_graph_retire`; returns non-zero on any failed check.
21 */
22
23#include <cstdint>
24#include <cstdio>
25#include <span>
26#include <string_view>
27
28#include "libtracer/tracer.hpp"
29
30namespace {
31
32using tr::graph::path_t;
33using tr::graph::role_t;
34using tr::graph::status_t;
35
36/** @brief An owned one-segment view over @p text. */
37tr::view::view_t value_of(std::string_view text) {
38    return *tr::view::over_bytes(std::as_bytes(std::span<const char>(text.data(), text.size())));
39}
40
41/** @brief Report expectation @p what and record a failure on @p ok. */
42void check(bool& ok, bool cond, const char* what) {
43    std::printf("  [%s] %s\n", cond ? "ok" : "FAIL", what);
44    ok = ok && cond;
45}
46
47}  // namespace
48
49int main() {
50    tr::graph::graph_t g;
51    bool ok = true;
52
53    const auto air = g.register_vertex(path_t("/zone/air"), role_t::STORED_VALUE);
54    (void)g.register_vertex(path_t("/zone/air/humidity"), role_t::STORED_VALUE);
55    (void)g.write(air, value_of("ok"));
56    const std::uint32_t gen_before = g.retire_generation(air);
57
58    check(ok, g.retire(air).has_value(), "retire succeeds");
59    check(ok, !g.find(path_t("/zone/air").key()), "the retired path no longer resolves");
60    check(ok, !g.find(path_t("/zone/air/humidity").key()), "retirement takes the whole subtree");
61
62    const auto r = g.read(path_t("/zone/air"));
63    check(ok, !r && r.error() == status_t::NOT_FOUND,
64          "a retired path reads NOT_FOUND, exactly like never-existed");
65    check(ok, g.retire_generation(air) != gen_before,
66          "the handle stays usable, and its generation moved");
67    check(ok, g.retire(air).has_value(),
68          "retiring an already-retired vertex is a no-op, not an error");
69
70    // A later LOCAL write revives the address; the revived vertex inherits nothing (§B.6).
71    check(ok, g.write(path_t("/zone/air"), value_of("fresh")).has_value(),
72          "write-creates revives a retired address");
73    std::printf("generation %u -> %u across one retirement\n", gen_before,
74                g.retire_generation(air));
75    return ok ? 0 : 1;
76}

See also: graph module · write-creates (the other half of the lifecycle) · graph model reference.