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
40 changes: 39 additions & 1 deletion tools/trajectories/autonomous-pickup.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { describe, expect, test } from "bun:test";
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import type { TrajectoryPacket } from "./autonomous-pickup";
import { selectNextTrajectory } from "./autonomous-pickup";
import { readTrajectoryPackets, selectNextTrajectory } from "./autonomous-pickup";

function packet(partial: Partial<TrajectoryPacket> & Pick<TrajectoryPacket, "slug" | "title">): TrajectoryPacket {
return {
Expand Down Expand Up @@ -124,3 +127,38 @@ describe("selectNextTrajectory", () => {
expect(selection.blocked[0]?.reason).toContain("claim/factory-trajectory-surface");
});
});

describe("readTrajectoryPackets", () => {
test("keeps wrapped top-level next-action fields together", () => {
const repoRoot = mkdtempSync(join(tmpdir(), "zeta-trajectory-pickup-"));
try {
const packetDir = join(repoRoot, "docs", "trajectories", "typescript-bun-migration");
mkdirSync(packetDir, { recursive: true });
writeFileSync(
join(packetDir, "RESUME.md"),
[
"# TypeScript / Bun migration",
"",
"**Status:** active",
"**Next concrete action:** Claim the smallest TypeScript/Bun migration slice and",
"preserve the wrapped continuation text in the generated prompt.",
"**Current blocker:** none",
"",
"## Next Child Packets",
"",
"- none currently selected",
].join("\n"),
);

const packets = readTrajectoryPackets(repoRoot);

expect(packets).toHaveLength(1);
expect(packets[0]?.nextAction).toBe(
"Claim the smallest TypeScript/Bun migration slice and preserve the wrapped continuation text in the generated prompt",
);
expect(packets[0]?.blocker).toBe("none");
} finally {
rmSync(repoRoot, { recursive: true, force: true });
}
});
});
36 changes: 34 additions & 2 deletions tools/trajectories/autonomous-pickup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,42 @@ function fieldValue(line: string, label: string): string | null {
return stripMarkdown(stripped.slice(prefix.length));
}

function isFieldLikeLine(line: string): boolean {
const stripped = line.trim().replaceAll("**", "");
const colonIndex = stripped.indexOf(":");
if (colonIndex <= 0 || colonIndex > 80) {
return false;
}
const label = stripped.slice(0, colonIndex).trim();
return /^[A-Za-z][A-Za-z0-9 /_-]*$/.test(label);
}

function fieldWithContinuations(lines: readonly string[], index: number, label: string): string | null {
const value = fieldValue(lines[index] ?? "", label);
if (value === null) {
return null;
}
const parts = [value];
for (let nextIndex = index + 1; nextIndex < lines.length; nextIndex++) {
const trimmed = lines[nextIndex]?.trim() ?? "";
if (
trimmed === "" ||
trimmed.startsWith("#") ||
trimmed.startsWith("- ") ||
trimmed.startsWith("|") ||
isFieldLikeLine(trimmed)
) {
break;
}
parts.push(stripMarkdown(trimmed));
}
return stripMarkdown(parts.join(" "));
}

function firstField(lines: readonly string[], labels: readonly string[]): string | null {
for (const line of lines) {
for (let index = 0; index < lines.length; index++) {
for (const label of labels) {
const value = fieldValue(line, label);
const value = fieldWithContinuations(lines, index, label);
if (value !== null && value.length > 0) {
return value;
}
Expand Down
Loading