Commit Diff


commit - 323a47d9cab66665f0a5e7b0432a69d1c942642f
commit + 83f2c983cb279c2381c039c3947997ebde1ce2e1
blob - f278d6717aee499f26d1b62c8196bc9d57ed0acd
blob + d583c71ff2f09d11db233da953a5df273a6cee84
--- regress/Makefile
+++ regress/Makefile
@@ -1,7 +1,7 @@
 # Long integration suites lead so scripts/check can overlap their tails with
 # shorter proofs; this order is intentionally not alphabetical.
 SUBDIR=	journal turn print agent agentcfg anthropic buf check claude_subscription compaction conf deploy diff editor generation http imsgev json \
-	local_port log markdown model_window openai output sandbox skills sse term tool_display tools \
+	local_port log markdown model_window openai output sandbox skills sse system_epoch term tool_display tools \
 	turn_mechanics turn_txn ui_config ui_layout web welcome xmalloc
 
 .include <bsd.subdir.mk>
blob - /dev/null
blob + 9cd0d4146353034b117cb93bcd35bcfd27028f78 (mode 644)
--- /dev/null
+++ regress/system_epoch/Makefile
@@ -0,0 +1,8 @@
+PROG=	system_epoch_test
+SRCS=	system_epoch_test.c system_epoch.c buf.c log.c xmalloc.c
+NOMAN=	yes
+
+.PATH:		${.CURDIR}/../../src/fugu
+CFLAGS+=	-I${.CURDIR}/../../src/fugu
+
+.include <bsd.regress.mk>
blob - /dev/null
blob + 03f2f68bd17979f0b2124a0f8c79a36b18a06687 (mode 644)
--- /dev/null
+++ regress/system_epoch/system_epoch_test.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2026 Isaac <isaac@itm.works>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "system_epoch.h"
+
+#include "regress.h"
+
+static int
+buf_is(const struct buf *b, const void *want, size_t len)
+{
+	return (b->len == len && (len == 0 || memcmp(b->data, want, len) == 0));
+}
+
+static void
+test_exact_variants(void)
+{
+	static const char full[] =
+	    "BASE\n\nPERSONAL\n\nPROJECT\n\n"
+	    "Summary of the conversation so far (your own continuation notes):"
+	    "\n\nSUMMARY";
+	static const char restricted[] =
+	    "BASE\n\nSummary of the conversation so far "
+	    "(your own continuation notes):\n\nSUMMARY";
+	struct system_epoch_input input;
+	struct system_epoch epoch;
+
+	system_epoch_init(&epoch);
+	memset(&input, 0, sizeof(input));
+	input.reason = SYSTEM_EPOCH_START;
+	input.base.data = "BASE";
+	input.base.len = 4;
+	input.personal.data = "PERSONAL";
+	input.personal.len = 8;
+	input.project.data = "PROJECT";
+	input.project.len = 7;
+	input.summary.data = "SUMMARY";
+	input.summary.len = 7;
+	CHECK(system_epoch_build(&epoch, &input) == 0);
+	CHECK(epoch.version == SYSTEM_EPOCH_VERSION);
+	CHECK(epoch.reason == SYSTEM_EPOCH_START);
+	CHECK(buf_is(&epoch.base, "BASE", 4));
+	CHECK(buf_is(&epoch.personal, "PERSONAL", 8));
+	CHECK(buf_is(&epoch.project, "PROJECT", 7));
+	CHECK(buf_is(&epoch.summary, "SUMMARY", 7));
+	CHECK(buf_is(&epoch.full, full, sizeof(full) - 1));
+	CHECK(buf_is(&epoch.restricted, restricted,
+	    sizeof(restricted) - 1));
+	CHECK(system_epoch_select(&epoch, 1) == &epoch.full);
+	CHECK(system_epoch_select(&epoch, 0) == &epoch.restricted);
+	system_epoch_free(&epoch);
+}
+
+static void
+test_owned_state(void)
+{
+	char base[] = "owned-base";
+	char personal[] = "owned-personal";
+	char project[] = "owned-project";
+	char summary[] = "owned-summary";
+	struct system_epoch_input input;
+	struct system_epoch epoch, copy;
+
+	system_epoch_init(&epoch);
+	system_epoch_init(&copy);
+	memset(&input, 0, sizeof(input));
+	input.reason = SYSTEM_EPOCH_CLEAR;
+	input.base.data = base;
+	input.base.len = strlen(base);
+	input.personal.data = personal;
+	input.personal.len = strlen(personal);
+	input.project.data = project;
+	input.project.len = strlen(project);
+	input.summary.data = summary;
+	input.summary.len = strlen(summary);
+	CHECK(system_epoch_build(&epoch, &input) == 0);
+	base[0] = personal[0] = project[0] = summary[0] = 'X';
+	CHECK(buf_is(&epoch.base, "owned-base", 10));
+	CHECK(buf_is(&epoch.personal, "owned-personal", 14));
+	CHECK(buf_is(&epoch.project, "owned-project", 13));
+	CHECK(buf_is(&epoch.summary, "owned-summary", 13));
+	CHECK(system_epoch_select(&epoch, 0) == &epoch.restricted);
+	CHECK(buf_is(&epoch.personal, "owned-personal", 14));
+	CHECK(buf_is(&epoch.project, "owned-project", 13));
+	CHECK(system_epoch_copy(&copy, &epoch) == 0);
+	epoch.base.data[0] = epoch.personal.data[0] = 'Y';
+	CHECK(buf_is(&copy.base, "owned-base", 10));
+	CHECK(buf_is(&copy.personal, "owned-personal", 14));
+	CHECK(copy.reason == SYSTEM_EPOCH_CLEAR);
+	system_epoch_free(&copy);
+	system_epoch_free(&epoch);
+}
+
+static void
+test_invalid_input_is_transactional(void)
+{
+	static const char base_nul[] = { 'b', '\0', 'x' };
+	static const char personal_nul[] = { 'p', '\0', 'x' };
+	static const char project_nul[] = { 'r', '\0', 'x' };
+	struct system_epoch_input input;
+	struct system_epoch epoch;
+
+	system_epoch_init(&epoch);
+	memset(&input, 0, sizeof(input));
+	input.reason = SYSTEM_EPOCH_MIGRATION;
+	input.base.data = "KEEP";
+	input.base.len = 4;
+	input.personal.data = "P";
+	input.personal.len = 1;
+	CHECK(system_epoch_build(&epoch, &input) == 0);
+
+	input.reason = (enum system_epoch_reason)99;
+	CHECK(system_epoch_build(&epoch, &input) == -1);
+	input.reason = SYSTEM_EPOCH_CLEAR;
+	input.base.data = NULL;
+	CHECK(system_epoch_build(&epoch, &input) == -1);
+	input.base.data = base_nul;
+	input.base.len = sizeof(base_nul);
+	CHECK(system_epoch_build(&epoch, &input) == -1);
+	input.base.data = "B";
+	input.base.len = 1;
+	input.personal.data = personal_nul;
+	input.personal.len = sizeof(personal_nul);
+	CHECK(system_epoch_build(&epoch, &input) == -1);
+	input.personal.data = "P";
+	input.personal.len = 1;
+	input.project.data = project_nul;
+	input.project.len = sizeof(project_nul);
+	CHECK(system_epoch_build(&epoch, &input) == -1);
+
+	input.project.data = NULL;
+	input.project.len = 0;
+	input.base.data = "x";
+	input.base.len = SYSTEM_EPOCH_BASE_MAX + 1;
+	CHECK(system_epoch_build(&epoch, &input) == -1);
+	input.base.len = 1;
+	input.personal.data = "x";
+	input.personal.len = SYSTEM_EPOCH_EXTERNAL_MAX + 1;
+	CHECK(system_epoch_build(&epoch, &input) == -1);
+	input.personal.data = NULL;
+	input.personal.len = 0;
+	input.summary.data = "x";
+	input.summary.len = SYSTEM_EPOCH_SUMMARY_MAX + 1;
+	CHECK(system_epoch_build(&epoch, &input) == -1);
+	input.summary.data = NULL;
+	input.summary.len = 0;
+	input.base.data = "x";
+	input.base.len = SYSTEM_EPOCH_VARIANT_MAX;
+	input.personal.data = "y";
+	input.personal.len = 1;
+	CHECK(system_epoch_build(&epoch, &input) == -1);
+
+	CHECK(epoch.version == SYSTEM_EPOCH_VERSION);
+	CHECK(epoch.reason == SYSTEM_EPOCH_MIGRATION);
+	CHECK(buf_is(&epoch.base, "KEEP", 4));
+	CHECK(buf_is(&epoch.personal, "P", 1));
+	CHECK(buf_is(&epoch.full, "KEEP\n\nP", 7));
+	CHECK(buf_is(&epoch.restricted, "KEEP", 4));
+	system_epoch_free(&epoch);
+}
+
+static void
+test_all_reasons_and_binary_summary(void)
+{
+	static const u_char base[] = { 'B', 0xff };
+	static const u_char personal[] = { 'P', 0xfe };
+	static const u_char project[] = { 'R', 0xfd };
+	static const u_char summary[] = { 'S', '\0', 0xfc };
+	static const char marker[] =
+	    "\n\nSummary of the conversation so far "
+	    "(your own continuation notes):\n\n";
+	static const enum system_epoch_reason reasons[] = {
+		SYSTEM_EPOCH_START,
+		SYSTEM_EPOCH_CLEAR,
+		SYSTEM_EPOCH_COMPACT,
+		SYSTEM_EPOCH_MIGRATION
+	};
+	struct system_epoch_input input;
+	struct system_epoch epoch, copy;
+	size_t i, off;
+
+	system_epoch_init(&epoch);
+	system_epoch_init(&copy);
+	memset(&input, 0, sizeof(input));
+	input.base.data = base;
+	input.base.len = sizeof(base);
+	input.personal.data = personal;
+	input.personal.len = sizeof(personal);
+	input.project.data = project;
+	input.project.len = sizeof(project);
+	input.summary.data = summary;
+	input.summary.len = sizeof(summary);
+	for (i = 0; i < sizeof(reasons) / sizeof(reasons[0]); i++) {
+		input.reason = reasons[i];
+		CHECK(system_epoch_build(&epoch, &input) == 0);
+		CHECK(epoch.reason == reasons[i]);
+		CHECK(epoch.version == SYSTEM_EPOCH_VERSION);
+	}
+	CHECK(buf_is(&epoch.base, base, sizeof(base)));
+	CHECK(buf_is(&epoch.personal, personal, sizeof(personal)));
+	CHECK(buf_is(&epoch.project, project, sizeof(project)));
+	CHECK(buf_is(&epoch.summary, summary, sizeof(summary)));
+	off = sizeof(base);
+	CHECK(epoch.full.len == sizeof(base) + 2 + sizeof(personal) + 2 +
+	    sizeof(project) + sizeof(marker) - 1 + sizeof(summary));
+	CHECK(memcmp(epoch.full.data, base, sizeof(base)) == 0);
+	CHECK(memcmp(epoch.full.data + off, "\n\n", 2) == 0);
+	off += 2 + sizeof(personal);
+	CHECK(memcmp(epoch.full.data + off, "\n\n", 2) == 0);
+	off += 2 + sizeof(project);
+	CHECK(memcmp(epoch.full.data + off, marker, sizeof(marker) - 1) == 0);
+	CHECK(memcmp(epoch.full.data + epoch.full.len - sizeof(summary),
+	    summary, sizeof(summary)) == 0);
+	CHECK(epoch.restricted.len == sizeof(base) + sizeof(marker) - 1 +
+	    sizeof(summary));
+	CHECK(memcmp(epoch.restricted.data, base, sizeof(base)) == 0);
+	CHECK(memcmp(epoch.restricted.data + sizeof(base), marker,
+	    sizeof(marker) - 1) == 0);
+	CHECK(memcmp(epoch.restricted.data + epoch.restricted.len -
+	    sizeof(summary), summary, sizeof(summary)) == 0);
+	CHECK(system_epoch_copy(&copy, &epoch) == 0);
+	CHECK(buf_is(&copy.summary, summary, sizeof(summary)));
+	CHECK(buf_is(&copy.full, epoch.full.data, epoch.full.len));
+	CHECK(buf_is(&copy.restricted, epoch.restricted.data,
+	    epoch.restricted.len));
+	system_epoch_free(&copy);
+	system_epoch_free(&epoch);
+}
+
+static void
+test_copy_failure_is_transactional(void)
+{
+	struct system_epoch_input input;
+	struct system_epoch dst, src;
+
+	system_epoch_init(&dst);
+	system_epoch_init(&src);
+	memset(&input, 0, sizeof(input));
+	input.reason = SYSTEM_EPOCH_START;
+	input.base.data = "DESTINATION";
+	input.base.len = 11;
+	CHECK(system_epoch_build(&dst, &input) == 0);
+	input.reason = SYSTEM_EPOCH_COMPACT;
+	input.base.data = "SOURCE";
+	input.base.len = 6;
+	CHECK(system_epoch_build(&src, &input) == 0);
+	src.full.data[0] = 'X';
+	CHECK(system_epoch_copy(&dst, &src) == -1);
+	CHECK(dst.reason == SYSTEM_EPOCH_START);
+	CHECK(buf_is(&dst.base, "DESTINATION", 11));
+	CHECK(buf_is(&dst.full, "DESTINATION", 11));
+	src.version = SYSTEM_EPOCH_VERSION + 1;
+	CHECK(system_epoch_copy(&dst, &src) == -1);
+	CHECK(buf_is(&dst.full, "DESTINATION", 11));
+	system_epoch_free(&src);
+	system_epoch_free(&dst);
+}
+
+int
+main(void)
+{
+	test_exact_variants();
+	test_owned_state();
+	test_invalid_input_is_transactional();
+	test_all_reasons_and_binary_summary();
+	test_copy_failure_is_transactional();
+	REGRESS_END();
+}
blob - /dev/null
blob + 14e9c5e26ee7067784bd3d946062195c7e02ef71 (mode 644)
--- /dev/null
+++ src/fugu/system_epoch.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2026 Isaac <isaac@itm.works>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "buf.h"
+#include "system_epoch.h"
+
+static const char summary_prefix[] =
+    "\n\nSummary of the conversation so far "
+    "(your own continuation notes):\n\n";
+
+static int
+reason_valid(enum system_epoch_reason reason)
+{
+	return (reason == SYSTEM_EPOCH_START || reason == SYSTEM_EPOCH_CLEAR ||
+	    reason == SYSTEM_EPOCH_COMPACT ||
+	    reason == SYSTEM_EPOCH_MIGRATION);
+}
+
+static int
+span_valid(const struct system_epoch_span *span)
+{
+	return (span->data != NULL || span->len == 0);
+}
+
+static int
+size_add(size_t *total, size_t add)
+{
+	if (SIZE_MAX - *total < add)
+		return (-1);
+	*total += add;
+	return (0);
+}
+
+static int
+input_valid(const struct system_epoch_input *input)
+{
+	size_t full, restricted, summary = 0;
+
+	if (input == NULL || !reason_valid(input->reason) ||
+	    !span_valid(&input->base) || !span_valid(&input->personal) ||
+	    !span_valid(&input->project) || !span_valid(&input->summary) ||
+	    input->base.len > SYSTEM_EPOCH_BASE_MAX ||
+	    input->personal.len > SYSTEM_EPOCH_EXTERNAL_MAX ||
+	    input->project.len > SYSTEM_EPOCH_EXTERNAL_MAX ||
+	    input->summary.len > SYSTEM_EPOCH_SUMMARY_MAX)
+		return (0);
+	full = restricted = input->base.len;
+	if (input->personal.len > 0 &&
+	    (size_add(&full, 2) == -1 ||
+	    size_add(&full, input->personal.len) == -1))
+		return (0);
+	if (input->project.len > 0 &&
+	    (size_add(&full, 2) == -1 ||
+	    size_add(&full, input->project.len) == -1))
+		return (0);
+	if (input->summary.len > 0) {
+		summary = sizeof(summary_prefix) - 1;
+		if (size_add(&summary, input->summary.len) == -1 ||
+		    size_add(&full, summary) == -1 ||
+		    size_add(&restricted, summary) == -1)
+			return (0);
+	}
+	if (full > SYSTEM_EPOCH_VARIANT_MAX ||
+	    restricted > SYSTEM_EPOCH_VARIANT_MAX)
+		return (0);
+	if ((input->base.len > 0 && memchr(input->base.data, '\0',
+	    input->base.len) != NULL) ||
+	    (input->personal.len > 0 && memchr(input->personal.data, '\0',
+	    input->personal.len) != NULL) ||
+	    (input->project.len > 0 && memchr(input->project.data, '\0',
+	    input->project.len) != NULL))
+		return (0);
+	return (1);
+}
+
+static void
+add_span(struct buf *out, const struct system_epoch_span *span)
+{
+	if (span->len > 0)
+		buf_add(out, span->data, span->len);
+}
+
+static void
+add_external(struct buf *out, const struct system_epoch_span *span)
+{
+	if (span->len == 0)
+		return;
+	buf_addstr(out, "\n\n");
+	add_span(out, span);
+}
+
+static void
+add_summary(struct buf *out, const struct system_epoch_span *summary)
+{
+	if (summary->len == 0)
+		return;
+	buf_add(out, summary_prefix, sizeof(summary_prefix) - 1);
+	add_span(out, summary);
+}
+
+static int
+buf_equal(const struct buf *a, const struct buf *b)
+{
+	return (a->len == b->len &&
+	    (a->len == 0 || memcmp(a->data, b->data, a->len) == 0));
+}
+
+void
+system_epoch_init(struct system_epoch *epoch)
+{
+	memset(epoch, 0, sizeof(*epoch));
+	buf_init(&epoch->base);
+	buf_init(&epoch->personal);
+	buf_init(&epoch->project);
+	buf_init(&epoch->summary);
+	buf_init(&epoch->full);
+	buf_init(&epoch->restricted);
+}
+
+void
+system_epoch_free(struct system_epoch *epoch)
+{
+	buf_free(&epoch->base);
+	buf_free(&epoch->personal);
+	buf_free(&epoch->project);
+	buf_free(&epoch->summary);
+	buf_free(&epoch->full);
+	buf_free(&epoch->restricted);
+	memset(epoch, 0, sizeof(*epoch));
+}
+
+int
+system_epoch_build(struct system_epoch *epoch,
+    const struct system_epoch_input *input)
+{
+	struct system_epoch next;
+
+	if (epoch == NULL || !input_valid(input))
+		return (-1);
+
+	system_epoch_init(&next);
+	next.version = SYSTEM_EPOCH_VERSION;
+	next.reason = input->reason;
+	add_span(&next.base, &input->base);
+	add_span(&next.personal, &input->personal);
+	add_span(&next.project, &input->project);
+	add_span(&next.summary, &input->summary);
+
+	add_span(&next.full, &input->base);
+	add_external(&next.full, &input->personal);
+	add_external(&next.full, &input->project);
+	add_summary(&next.full, &input->summary);
+	add_span(&next.restricted, &input->base);
+	add_summary(&next.restricted, &input->summary);
+
+	system_epoch_free(epoch);
+	*epoch = next;
+	return (0);
+}
+
+int
+system_epoch_copy(struct system_epoch *dst, const struct system_epoch *src)
+{
+	struct system_epoch_input input;
+	struct system_epoch next;
+
+	if (dst == NULL || src == NULL || src->version != SYSTEM_EPOCH_VERSION)
+		return (-1);
+	if (dst == src)
+		return (0);
+	memset(&input, 0, sizeof(input));
+	input.reason = src->reason;
+	input.base.data = src->base.data;
+	input.base.len = src->base.len;
+	input.personal.data = src->personal.data;
+	input.personal.len = src->personal.len;
+	input.project.data = src->project.data;
+	input.project.len = src->project.len;
+	input.summary.data = src->summary.data;
+	input.summary.len = src->summary.len;
+	system_epoch_init(&next);
+	if (system_epoch_build(&next, &input) == -1 ||
+	    !buf_equal(&next.full, &src->full) ||
+	    !buf_equal(&next.restricted, &src->restricted)) {
+		system_epoch_free(&next);
+		return (-1);
+	}
+	system_epoch_free(dst);
+	*dst = next;
+	return (0);
+}
+
+const struct buf *
+system_epoch_select(const struct system_epoch *epoch, int project_context)
+{
+	if (epoch == NULL)
+		return (NULL);
+	return (project_context ? &epoch->full : &epoch->restricted);
+}
blob - /dev/null
blob + cfe01a1a5d4bb697e413f1e2ed212a8a04a603fa (mode 644)
--- /dev/null
+++ src/fugu/system_epoch.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2026 Isaac <isaac@itm.works>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef SYSTEM_EPOCH_H
+#define SYSTEM_EPOCH_H
+
+#include <sys/types.h>
+
+#include <stdint.h>
+
+#include "buf.h"
+#include "proto.h"
+
+#define SYSTEM_EPOCH_VERSION	1U
+#define SYSTEM_EPOCH_BASE_MAX	FUGU_REQUEST_MAX
+#define SYSTEM_EPOCH_EXTERNAL_MAX FUGU_CTX_MAX
+#define SYSTEM_EPOCH_SUMMARY_MAX	FUGU_TURN_DATA_MAX
+#define SYSTEM_EPOCH_VARIANT_MAX	FUGU_REQUEST_MAX
+
+enum system_epoch_reason {
+	SYSTEM_EPOCH_START,
+	SYSTEM_EPOCH_CLEAR,
+	SYSTEM_EPOCH_COMPACT,
+	SYSTEM_EPOCH_MIGRATION
+};
+
+struct system_epoch_span {
+	const void	*data;
+	size_t		 len;
+};
+
+struct system_epoch_input {
+	enum system_epoch_reason	 reason;
+	struct system_epoch_span	 base;
+	struct system_epoch_span	 personal;
+	struct system_epoch_span	 project;
+	struct system_epoch_span	 summary;
+};
+
+/*
+ * One owned immutable System baseline epoch.  base, personal, and project
+ * are NUL-free; summary and the derived variants may contain NUL.  All spans
+ * are otherwise opaque bytes: this Module deliberately does not validate or
+ * normalize UTF-8.  Each external span is capped at FUGU_CTX_MAX, summary at
+ * FUGU_TURN_DATA_MAX, and base and either complete variant at
+ * FUGU_REQUEST_MAX.  A Provider codec still checks final JSON expansion.
+ */
+struct system_epoch {
+	uint32_t		 version;
+	enum system_epoch_reason reason;
+	struct buf		 base;
+	struct buf		 personal;
+	struct buf		 project;
+	struct buf		 summary;
+	struct buf		 full;
+	struct buf		 restricted;
+};
+
+void	 system_epoch_init(struct system_epoch *);
+void	 system_epoch_free(struct system_epoch *);
+/* On failure, a previously built destination remains byte-identical. */
+int	 system_epoch_build(struct system_epoch *,
+	    const struct system_epoch_input *);
+int	 system_epoch_copy(struct system_epoch *, const struct system_epoch *);
+/* Borrowed until the epoch is rebuilt or freed. */
+const struct buf *system_epoch_select(const struct system_epoch *, int);
+
+#endif /* SYSTEM_EPOCH_H */