xo-umbrella2/docs/superpowers/plans/2026-07-25-xo-ppsink.md
Roland Conybeare d2e5b8d33e docs: xo-ppsink carve-out implementation plan
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MrL8i59N6i3M5A48rJ3u4J
2026-07-25 23:54:58 -04:00

21 KiB
Raw Permalink Blame History

xo-ppsink Carve-Out Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Extract the arena-free pretty-print front-end (PpSink/FlatSink/Pretty/FunctionStyle) from xo-indentlog2 into a new low-level repo xo-ppsink, so subsystems below xo-arena (e.g. xo-arena, xo-facet) can pretty-print without depending on the arena-backed engine.

Architecture: xo-ppsink is a small shared library (namespace xo::print, include stem xo/ppsink/) depending only on xo-cmake (build tooling) + xo-timeutil (propagated, anticipating a later xo::scope migration). xo-indentlog2 keeps the arena-backed engine (PpState, PrettySink, PpToken*, PpConfig, and the Pretty specializations) and gains a dependency on xo-ppsink. End-state level order: xo-timeutil → xo-ppsink → … → xo-arena → xo-indentlog2.

Tech Stack: C++20, CMake + xo-cmake macros, Nix (pkgs/*.nix + xo.nix/ci.nix/shells.nix), Catch2 for tests, git-subrepo for the umbrella.

Global Constraints

  • Namespace stays xo::print — do NOT rename to xo::ppsink.
  • Cross-repo includes use angle brackets <xo/…>; same-repo includes use quotes "…".
  • Test .cpp files: no anonymous namespaces (unity-build friendly); give test-local types unique suffixed names (e.g. Point_PpsinkTest) to avoid ODR clashes.
  • xo-ppsink sits below xo-testutil, so its utest uses plain Catch2 (#define CATCH_CONFIG_MAIN), never xo/testutil.
  • The carve set is exactly: headers PpSink.hpp, FlatSink.hpp, Pretty.hpp, FunctionStyle.hpp; sources PpSink.cpp, FlatSink.cpp. Everything else in xo-indentlog2/.../print/ stays.
  • After any change to xo-indentlog2 inter-repo deps, the find_dependency() set in xo-indentlog2/cmake/xo_indentlog2Config.cmake.in and the nix inputs in pkgs/xo-indentlog2.nix must stay consistent with the xo_dependency() calls.

File Structure

Task 1 creates (xo-ppsink):

  • xo-ppsink/CMakeLists.txt — top-level shared-lib project (scaffolded)
  • xo-ppsink/cmake/xo_ppsinkConfig.cmake.in — exported config (find_dependency(xo_timeutil))
  • xo-ppsink/include/xo/ppsink/{PpSink,FlatSink,Pretty,FunctionStyle}.hpp — copies of the carve set (adjusted includes)
  • xo-ppsink/src/ppsink/{PpSink,FlatSink}.cpp + src/ppsink/CMakeLists.txt — the shared lib xo_ppsink
  • xo-ppsink/utest/{ppsink_utest_main,FlatSink.test,Pretty.test}.cpp + utest/CMakeLists.txt — new arena-free tests
  • pkgs/xo-ppsink.nix — nix derivation
  • xo.nix, shells.nix, ci.nix, umbrella CMakeLists.txt — register xo-ppsink

Task 2 modifies (xo-indentlog2 consumes xo-ppsink):

  • Delete xo-indentlog2/include/xo/indentlog2/print/{PpSink,FlatSink,Pretty,FunctionStyle}.hpp
  • Delete xo-indentlog2/src/indentlog2/{PpSink,FlatSink}.cpp
  • xo-indentlog2/src/indentlog2/CMakeLists.txt — drop the two sources, add xo_dependency(xo_ppsink)
  • xo-indentlog2/include/xo/indentlog2/print/{PrettySink,PrettyVector,PrettyFunctionStyle}.hpp — rewrite moved-header includes to <xo/ppsink/…>
  • xo-indentlog2/utest/{FlatSink,Pretty,PrettyVector}.test.cpp — rewrite moved-header includes to <xo/ppsink/…>
  • xo-indentlog2/cmake/xo_indentlog2Config.cmake.in — add find_dependency(xo_ppsink)
  • pkgs/xo-indentlog2.nix — add xo-ppsink to propagatedBuildInputs

Task 3 finalizes:

  • Regenerate xo-cmake/etc/xo/subsystem-edges from the umbrella; verify consistency and full builds.

Task 1: Create and wire xo-ppsink (standalone, green)

Files: as listed under "Task 1 creates" above.

Interfaces:

  • Produces: shared lib target xo_ppsink; headers under xo/ppsink/; namespace xo::print with PpSink, PpSinkInserter, FlatSink, Pretty<T>, has_pretty<T>, pp_write(PpSink&, const T&), FunctionStyle. Nix attr xo.ppsink.

  • Consumes: nothing new (xo-timeutil already exists).

  • Step 1: Scaffold the shared-lib skeleton into the existing subrepo dir

xo-ppsink/ already exists (subrepo: .gitrepo, README.md, cmake/xo-bootstrap-macros.cmake). Run the scaffolder to add the shared-lib skeleton (CMakeLists.txt, cmake/xo_ppsinkConfig.cmake.in, include/xo/ppsink/, src/ppsink/, utest/).

Run: xo-cmake/bin/scaffold-subdir --mode=shared ppsink (from the umbrella root; scaffold-subdir is a source-tree tool, not installed to $PREFIX/bin) Expected: creates xo-ppsink/CMakeLists.txt, xo-ppsink/cmake/xo_ppsinkConfig.cmake.in, xo-ppsink/src/ppsink/CMakeLists.txt, xo-ppsink/utest/CMakeLists.txt, and empty include/xo/ppsink/ / src/ppsink/ trees. Target name is xo_ppsink.

Inspect the generated xo-ppsink/CMakeLists.txt and confirm it follows the xo-indentlog2 shape: project(xo_ppsink VERSION 1.0), include(cmake/xo-bootstrap-macros.cmake), xo_cxx_toplevel_options3(), add_subdirectory(src/ppsink), add_subdirectory(utest), and xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets). If the scaffold omits any of these, add them to match xo-indentlog2/CMakeLists.txt.

  • Step 2: Copy the four carve headers into include/xo/ppsink/, adjusting includes

Copy each header from xo-indentlog2/include/xo/indentlog2/print/ to xo-ppsink/include/xo/ppsink/, then apply the include edits below.

cd ~/proj/xo-umbrella2
mkdir -p xo-ppsink/include/xo/ppsink
cp xo-indentlog2/include/xo/indentlog2/print/PpSink.hpp        xo-ppsink/include/xo/ppsink/PpSink.hpp
cp xo-indentlog2/include/xo/indentlog2/print/FlatSink.hpp      xo-ppsink/include/xo/ppsink/FlatSink.hpp
cp xo-indentlog2/include/xo/indentlog2/print/Pretty.hpp        xo-ppsink/include/xo/ppsink/Pretty.hpp
cp xo-indentlog2/include/xo/indentlog2/print/FunctionStyle.hpp xo-ppsink/include/xo/ppsink/FunctionStyle.hpp

Edit xo-ppsink/include/xo/ppsink/PpSink.hpp — replace the include block

#include <string_view>
#include <cstdint>

with (add <iosfwd> so std::ostream is only forward-declared; callers that stream pull in <ostream> themselves):

#include <string_view>
#include <iosfwd>
#include <cstdint>

FlatSink.hpp, Pretty.hpp, FunctionStyle.hpp need no edits: their #include "PpSink.hpp" still resolves (same directory), and FunctionStyle.hpp has no includes.

  • Step 3: Copy the two carve sources into src/ppsink/, fixing the header path
cd ~/proj/xo-umbrella2
mkdir -p xo-ppsink/src/ppsink
cp xo-indentlog2/src/indentlog2/PpSink.cpp   xo-ppsink/src/ppsink/PpSink.cpp
cp xo-indentlog2/src/indentlog2/FlatSink.cpp xo-ppsink/src/ppsink/FlatSink.cpp

In xo-ppsink/src/ppsink/PpSink.cpp change #include "print/PpSink.hpp" → #include <xo/ppsink/PpSink.hpp>. In xo-ppsink/src/ppsink/FlatSink.cpp change #include "print/FlatSink.hpp" → #include <xo/ppsink/FlatSink.hpp>.

  • Step 4: Write src/ppsink/CMakeLists.txt

Replace the scaffolded xo-ppsink/src/ppsink/CMakeLists.txt with:

# xo-ppsink/src/ppsink/CMakeLists.txt

set(SELF_LIB xo_ppsink)
set(SELF_SRCS
    PpSink.cpp
    FlatSink.cpp
)

xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS})
xo_install_include_tree3(include/xo/ppsink)

# ----------------------------------------------------------------
# dependencies
#
# NOTE: dependency set here must be kept consistent with
#       xo-ppsink/cmake/xo_ppsinkConfig.cmake.in

xo_headeronly_dependency(${SELF_LIB} xo_timeutil)

# end CMakeLists.txt
  • Step 5: Write cmake/xo_ppsinkConfig.cmake.in

Replace the scaffolded xo-ppsink/cmake/xo_ppsinkConfig.cmake.in with:

@PACKAGE_INIT@

include(CMakeFindDependencyMacro)

# note: changes to find_dependency() calls here
#       must coordinate with xo_(headeronly_)dependency() calls
#       in xo-ppsink/src/ppsink/CMakeLists.txt
#
find_dependency(xo_timeutil)

include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Share.cmake")
check_required_components("@PROJECT_NAME@")
  • Step 6: Write the plain-catch2 utest main

Create xo-ppsink/utest/ppsink_utest_main.cpp:

/* @file ppsink_utest_main.cpp */

#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"

/* end ppsink_utest_main.cpp */
  • Step 7: Write the FlatSink test (arena-free)

Create xo-ppsink/utest/FlatSink.test.cpp:

/* @file FlatSink.test.cpp */

#include <xo/ppsink/FlatSink.hpp>
#include <catch2/catch.hpp>
#include <sstream>

using xo::print::FlatSink;
using xo::print::PpSink;

TEST_CASE("flatsink-put-and-noop-structure", "[flatsink]") {
    /* FlatSink writes put() text straight through; begin/split/end are no-ops */
    std::stringstream ss;
    FlatSink sink(ss);

    sink.put("hello").begin().put("a").split().put("b").end().put("!");

    REQUIRE(ss.str() == "helloab!");
}

TEST_CASE("flatsink-stream-open", "[flatsink]") {
    /* stream_open returns an inserter that writes straight to the ostream */
    std::stringstream ss;
    FlatSink sink(ss);
    {
        auto ins = sink.stream_open(64);
        ins << 42 << ' ' << "x";
        ins.finish();
    }
    REQUIRE(ss.str() == "42 x");
}
  • Step 8: Write the Pretty/pp_write test (arena-free)

Create xo-ppsink/utest/Pretty.test.cpp. Note the unique _PpsinkTest type names and no anonymous namespace (unity-build friendly):

/* @file Pretty.test.cpp */

#include <xo/ppsink/Pretty.hpp>
#include <xo/ppsink/FlatSink.hpp>
#include <catch2/catch.hpp>
#include <ostream>
#include <sstream>

using xo::print::FlatSink;
using xo::print::PpSink;
using xo::print::Pretty;
using xo::print::pp_write;
using xo::print::has_pretty;

/* a type that opts in to Pretty */
struct Point_PpsinkTest { int x; int y; };

/* a type that does NOT opt in -> pp_write falls back to operator<< */
struct Plain_PpsinkTest { int v; };

inline std::ostream &
operator<<(std::ostream & os, const Plain_PpsinkTest & p) {
    return os << "Plain{" << p.v << "}";
}

namespace xo::print {
    template <>
    struct Pretty<Point_PpsinkTest> {
        static void print(PpSink & sink, const Point_PpsinkTest & p) {
            sink.begin().put("(");
            pp_write(sink, p.x);
            sink.put(",");
            pp_write(sink, p.y);
            sink.put(")").end();
        }
    };
}

TEST_CASE("pp_write-uses-specialization", "[pretty]") {
    static_assert(has_pretty<Point_PpsinkTest>);

    std::stringstream ss;
    FlatSink sink(ss);
    pp_write(sink, Point_PpsinkTest{1, 2});   /* begin/end are no-ops in FlatSink */

    REQUIRE(ss.str() == "(1,2)");
}

TEST_CASE("pp_write-falls-back-to-operator<<", "[pretty]") {
    static_assert(!has_pretty<Plain_PpsinkTest>);

    std::stringstream ss;
    FlatSink sink(ss);
    pp_write(sink, Plain_PpsinkTest{7});      /* stream_open -> operator<< */

    REQUIRE(ss.str() == "Plain{7}");
}
  • Step 9: Write utest/CMakeLists.txt

Replace the scaffolded xo-ppsink/utest/CMakeLists.txt with (mirrors xo-timeutil/utest, plain Catch2, no testutil):

# xo-ppsink/utest/CMakeLists.txt

set(SELF_EXECUTABLE_NAME utest.ppsink)
set(SELF_SOURCE_FILES
    ppsink_utest_main.cpp
    FlatSink.test.cpp
    Pretty.test.cpp)

if (ENABLE_TESTING)
    xo_add_utest_executable(${SELF_EXECUTABLE_NAME} ${SELF_SOURCE_FILES})
    xo_self_dependency(${SELF_EXECUTABLE_NAME} xo_ppsink)
    xo_external_target_dependency(${SELF_EXECUTABLE_NAME} Catch2 Catch2::Catch2)
endif()

# end CMakeLists.txt
  • Step 10: Write pkgs/xo-ppsink.nix

Create pkgs/xo-ppsink.nix:

{
  # dependencies
  lib, stdenv, cmake, catch2,

  xo-cmake, xo-timeutil,

  doCheck ? true,
} :

stdenv.mkDerivation (finalattrs:
  {
    name = "xo-ppsink";
    version = "1.0";

    src = ../xo-ppsink;

    cmakeFlags = ["-DCMAKE_MODULE_PATH=${xo-cmake}/share/cmake"];

    inherit doCheck;

    # xo-ppsink's exported config + public headers pull in xo-timeutil,
    # so consumers must receive it transitively.
    propagatedBuildInputs = [ xo-timeutil ];

    nativeBuildInputs = [ cmake
                          catch2
                          xo-cmake ];
  })
  • Step 11: Register xo-ppsink in nix + umbrella

In xo.nix, add after the xo-timeutil line (line ~29):

    xo-ppsink         = callPackage pkgs/xo-ppsink.nix         { stdenv = jitStdenv; };

In shells.nix, add after the timeutil line (line ~325):

    ppsink         = pkgs.xo-ppsink;

In ci.nix, add xo-ppsink to the inherit (xoPkgs) list, immediately after xo-timeutil:

      xo-timeutil
      xo-ppsink
      xo-subsys

In the umbrella CMakeLists.txt, add after add_subdirectory(xo-timeutil …) (so ppsink precedes indentlog2/arena):

add_subdirectory(xo-ppsink)       # arena-free pretty-print front-end (PpSink/FlatSink/Pretty)

xo-ppsink is already present in xo-cmake/etc/xo/subsystem-list (line 3) — no edit needed there.

  • Step 12: Build + test xo-ppsink via nix

Run: nix-build -A xo.ppsink --no-out-link Expected: builds and doCheck runs utest.ppsink — All tests passed (2 test files, 4 cases). Final output is a /nix/store/…-xo-ppsink path.

  • Step 13: Verify the umbrella still configures (ppsink added, indentlog2 untouched)

Run: cmake -S . -B .build >/tmp/ppsink-reconf.log 2>&1; echo exit=$? Expected: exit=0. (indentlog2 still has its own copies and does not yet depend on ppsink, so nothing is broken; ppsink is now an added subdir.)

  • Step 14: Commit
cd ~/proj/xo-umbrella2
git add xo-ppsink pkgs/xo-ppsink.nix xo.nix shells.nix ci.nix CMakeLists.txt
git commit -m "xo-ppsink: new arena-free pretty-print front-end (PpSink/FlatSink/Pretty) [carve-out]"

Task 2: Migrate xo-indentlog2 onto xo-ppsink

Files: as listed under "Task 2 modifies" above.

Interfaces:

  • Consumes: xo_ppsink target + <xo/ppsink/{PpSink,FlatSink,Pretty,FunctionStyle}.hpp> (namespace xo::print, unchanged) from Task 1.

  • Produces: xo-indentlog2 with no duplicate copies of the carve set; depends on xo_ppsink.

  • Step 1: Delete the carve set from xo-indentlog2

cd ~/proj/xo-umbrella2
git rm xo-indentlog2/include/xo/indentlog2/print/PpSink.hpp \
       xo-indentlog2/include/xo/indentlog2/print/FlatSink.hpp \
       xo-indentlog2/include/xo/indentlog2/print/Pretty.hpp \
       xo-indentlog2/include/xo/indentlog2/print/FunctionStyle.hpp \
       xo-indentlog2/src/indentlog2/PpSink.cpp \
       xo-indentlog2/src/indentlog2/FlatSink.cpp
  • Step 2: Rewrite includes in the staying headers

In xo-indentlog2/include/xo/indentlog2/print/PrettySink.hpp: change #include "PpSink.hpp" → #include <xo/ppsink/PpSink.hpp>.

In xo-indentlog2/include/xo/indentlog2/print/Pretty — the primary Pretty.hpp was moved; the staying PrettyVector.hpp includes it: change #include "Pretty.hpp" → #include <xo/ppsink/Pretty.hpp> in xo-indentlog2/include/xo/indentlog2/print/PrettyVector.hpp.

In xo-indentlog2/include/xo/indentlog2/print/PrettyFunctionStyle.hpp: change

#include "Pretty.hpp"
#include "FunctionStyle.hpp"

to

#include <xo/ppsink/Pretty.hpp>
#include <xo/ppsink/FunctionStyle.hpp>
  • Step 3: Rewrite includes in the staying tests

In xo-indentlog2/utest/FlatSink.test.cpp: change #include "print/FlatSink.hpp" → #include <xo/ppsink/FlatSink.hpp>. In xo-indentlog2/utest/Pretty.test.cpp: change #include "print/Pretty.hpp" → #include <xo/ppsink/Pretty.hpp>, and #include "print/FlatSink.hpp" → #include <xo/ppsink/FlatSink.hpp>. In xo-indentlog2/utest/PrettyVector.test.cpp: change #include "print/FlatSink.hpp" → #include <xo/ppsink/FlatSink.hpp>. (Its #include "print/PrettyFunctionStyle.hpp" stays — that header remains in indentlog2.)

  • Step 4: Drop the carve sources from the indentlog2 library

In xo-indentlog2/src/indentlog2/CMakeLists.txt, remove PpSink.cpp and FlatSink.cpp from SELF_SRCS, and add the ppsink dependency. The SELF_SRCS block becomes:

set(SELF_SRCS
    LogStreambuf.cpp
	LogConfig.cpp
    LogBuffer.cpp
    PrettySink.cpp
	PpState.cpp
    LineState.cpp
	PpTokenStreambuf.cpp
    PpToken.cpp
    PpConfig.cpp
)

and the dependencies section becomes:

xo_dependency(${SELF_LIB} xo_arena)
xo_dependency(${SELF_LIB} xo_ppsink)
  • Step 5: Add the ppsink find_dependency to the exported config

In xo-indentlog2/cmake/xo_indentlog2Config.cmake.in, add below find_dependency(xo_arena):

find_dependency(xo_arena)
find_dependency(xo_ppsink)
  • Step 6: Add xo-ppsink to the indentlog2 nix inputs

In pkgs/xo-indentlog2.nix, add xo-ppsink to the function args and to propagatedBuildInputs.

Change the xo-deps arg line

# xo dependencies
  xo-arena,
  xo-cmake,

to

# xo dependencies
  xo-arena,
  xo-ppsink,
  xo-cmake,

Change

    propagatedBuildInputs = [ xo-arena ];

to

    propagatedBuildInputs = [ xo-arena xo-ppsink ];
  • Step 7: Build + test xo-indentlog2 via nix

Run: nix-build -A xo.indentlog2 --no-out-link Expected: builds; doCheck runs utest.indentlog2 (LineState, LogBuffer, PpState, PpTokenStreambuf, PrettySink, FlatSink, Pretty, PrettyVector) — all pass. Confirms indentlog2 now resolves the carve set from xo-ppsink with no duplicate definitions.

  • Step 8: Verify the umbrella links indentlog2 against the ppsink sibling

(Does not use subsystem-edges — that regen is Task 3. This checks the in-umbrella sibling wiring, since xo-ppsink was add_subdirectory'd in Task 1.)

Run:

cd ~/proj/xo-umbrella2
cmake -S . -B .build >/dev/null 2>&1 && \
cmake --build .build --target utest.indentlog2 -j && \
ctest --test-dir .build -R '^utest.indentlog2$' --output-on-failure

Expected: xo_indentlog2 links xo_ppsink, utest.indentlog2 builds and passes.

  • Step 9: Commit
cd ~/proj/xo-umbrella2
git add xo-indentlog2 pkgs/xo-indentlog2.nix
git commit -m "xo-indentlog2: depend on xo-ppsink for PpSink/FlatSink/Pretty [levelize]"

Task 3: Regenerate the dependency graph and final verification

Files: xo-cmake/etc/xo/subsystem-edges (regenerated).

Interfaces:

  • Consumes: the finished xo-ppsink/xo-indentlog2 dependency wiring from Tasks 1–2.

  • Produces: subsystem-edges reflecting the new xo-ppsink edges, consistent with subsystem-list.

  • Step 1: Reconfigure the umbrella to emit fresh edges

Run: cmake -S . -B .build >/tmp/edges.log 2>&1; echo exit=$?; grep xo_emit_dependency_edges /tmp/edges.log Expected: exit=0; the emit message prints .build/xo-subsystem-edges.txt was written.

  • Step 2: Confirm xo-ppsink edges appear

Run: grep -E 'ppsink' .build/xo-subsystem-edges.txt Expected: at least xo-timeutil xo-ppsink (ppsink depends on timeutil) and xo-ppsink xo-indentlog2 (indentlog2 depends on ppsink).

  • Step 3: Update the checked-in edges file

Run:

cd ~/proj/xo-umbrella2
cp .build/xo-subsystem-edges.txt xo-cmake/etc/xo/subsystem-edges
git diff --stat xo-cmake/etc/xo/subsystem-edges

Expected: the file gains the ppsink edges.

  • Step 4: Verify subsystem-list is still a valid topological order for the new edges

Run: tsort xo-cmake/etc/xo/subsystem-edges >/dev/null; echo "tsort exit=$?" Expected: tsort exit=0 (no cycle). xo-ppsink already sits early in subsystem-list (after xo-timeutil, before xo-indentlog2), so no reorder is required. If tsort reports a loop, stop and investigate — a cycle means an accidental back-edge was introduced.

  • Step 5: Final whole-graph sanity via nix

Run: nix-build -A xo.ppsink -A xo.indentlog2 --no-out-link Expected: both build green.

  • Step 6: Verify the docker closure now resolves ppsink

(Now that subsystem-edges carries xo-ppsink xo-indentlog2, --with-deps pulls ppsink into indentlog2's closure. Reinstall the tooling first so the container snapshot has the new edges.)

Run:

cd ~/proj/xo-umbrella2
cmake --install .build/xo-cmake 2>/dev/null || cmake -S xo-cmake -B .build/xo-cmake -DCMAKE_INSTALL_PREFIX=$HOME/local >/dev/null && cmake --install .build/xo-cmake >/dev/null
xo-docker-build --xo-root-dir xo-indentlog2 2>&1 | grep -E '::group::xo-ppsink|::group::xo-indentlog2|tests passed|Error' | tail

Expected: closure includes xo-ppsink before xo-indentlog2; both build; utest.indentlog2 passes.

  • Step 7: Commit
cd ~/proj/xo-umbrella2
git add xo-cmake/etc/xo/subsystem-edges
git commit -m "xo-cmake: regenerate subsystem-edges after xo-ppsink carve-out [BUILD]"

Notes for the executor

  • The carve headers are copied in Task 1 and the originals deleted in Task 2, deliberately: it keeps each task at a green, independently-reviewable state (Task 1 leaves indentlog2 self-contained; no translation unit ever includes both copies, so there is no ODR clash in the intermediate state).
  • PpSink.cpp implements PpSinkInserter (ctor/dtor/finish); FlatSink.cpp implements the flat PpSink. Both are out-of-line, which is why xo-ppsink is a shared library rather than header-only.
  • If nix-build complains it cannot find -lxo_timeutil / xo_timeutilConfig.cmake while building a consumer, the propagation is wrong — ensure xo-timeutil is in propagatedBuildInputs of pkgs/xo-ppsink.nix (not nativeBuildInputs), mirroring the ratio→reflectutil fix.