@deltafleet/codex-goalkeeper 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/ROADMAP.md CHANGED
@@ -8,7 +8,7 @@ Goalkeeper should stay boring: a short checkpoint, a medium-density context pack
8
8
 
9
9
  ## MVP
10
10
 
11
- Ship a root-level Codex skill that manages project-local state:
11
+ Ship a Codex skill under `src/codex-goalkeeper/` that manages project-local state:
12
12
 
13
13
  ```text
14
14
  .goalkeeper/
@@ -41,7 +41,7 @@ Keep these scripts central:
41
41
 
42
42
  Keep this optional and maintainer-oriented:
43
43
 
44
- - `test-goalkeeper-update-checkpoint.mjs`
44
+ - `tests/test-goalkeeper-update-checkpoint.mjs`
45
45
 
46
46
  ## Non-Goals
47
47
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deltafleet/codex-goalkeeper",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "A small Codex skill for keeping long-running goals oriented across compaction, resumes, and handoffs.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -22,16 +22,14 @@
22
22
  "goal"
23
23
  ],
24
24
  "bin": {
25
- "codex-goalkeeper-init": "src/scripts/goalkeeper-init.mjs",
26
- "codex-goalkeeper-turn-start": "src/scripts/goalkeeper-turn-start.mjs",
27
- "codex-goalkeeper-append-event": "src/scripts/goalkeeper-append-event.mjs",
28
- "codex-goalkeeper-update-checkpoint": "src/scripts/goalkeeper-update-checkpoint.mjs",
29
- "codex-goalkeeper-doctor": "src/scripts/goalkeeper-doctor.mjs"
25
+ "codex-goalkeeper-init": "src/codex-goalkeeper/scripts/goalkeeper-init.mjs",
26
+ "codex-goalkeeper-turn-start": "src/codex-goalkeeper/scripts/goalkeeper-turn-start.mjs",
27
+ "codex-goalkeeper-append-event": "src/codex-goalkeeper/scripts/goalkeeper-append-event.mjs",
28
+ "codex-goalkeeper-update-checkpoint": "src/codex-goalkeeper/scripts/goalkeeper-update-checkpoint.mjs",
29
+ "codex-goalkeeper-doctor": "src/codex-goalkeeper/scripts/goalkeeper-doctor.mjs"
30
30
  },
31
31
  "files": [
32
- "SKILL.md",
33
- "agents",
34
- "src",
32
+ "src/codex-goalkeeper",
35
33
  "examples",
36
34
  "docs",
37
35
  "README.md",
@@ -45,8 +43,8 @@
45
43
  "CODE_OF_CONDUCT.md"
46
44
  ],
47
45
  "scripts": {
48
- "check:scripts": "find src/scripts -name '*.mjs' -print0 | xargs -0 -n1 node --check",
49
- "test": "node src/scripts/test-goalkeeper-update-checkpoint.mjs",
46
+ "check:scripts": "find src/codex-goalkeeper/scripts tests -name '*.mjs' -print0 | xargs -0 -n1 node --check",
47
+ "test": "node tests/test-goalkeeper-update-checkpoint.mjs",
50
48
  "validate:skill": "npx skills add . --list",
51
49
  "validate:examples": "find examples -name '*.jsonl' -print0 | xargs -0 -n1 jq -c . >/dev/null",
52
50
  "validate": "npm run check:scripts && npm test && npm run validate:examples && npm run validate:skill"
@@ -45,7 +45,7 @@ Use a project-local `.goalkeeper/` directory. Each long-running goal session get
45
45
 
46
46
  Use a stable, readable `<goal-session-id>` such as `2026-05-17-codex-goalkeeper-roadmap` or `ads-ops-release-hardening`.
47
47
 
48
- If any core file is missing during long goal work, create it from the templates in `src/templates/`.
48
+ If any core file is missing during long goal work, create it from the templates in `templates/`.
49
49
  Use `.goalkeeper/active-session` when a workspace has one active Goalkeeper session and the agent should not have to reconstruct the session id after compaction.
50
50
 
51
51
  The directory is created inside the active project workspace, not in a global Codex directory. Example:
@@ -57,19 +57,19 @@ The directory is created inside the active project workspace, not in a global Co
57
57
  Bundled scripts live in the skill package. When the script is not located inside the target workspace, pass the target workspace explicitly:
58
58
 
59
59
  ```bash
60
- node <skill-path>/src/scripts/goalkeeper-turn-start.mjs --workspace <workspace> --session <goal-session-id>
60
+ node <skill-path>/scripts/goalkeeper-turn-start.mjs --workspace <workspace> --session <goal-session-id>
61
61
  ```
62
62
 
63
63
  If `<workspace>/.goalkeeper/active-session` points to the session id, `--session` may be omitted:
64
64
 
65
65
  ```bash
66
- node <skill-path>/src/scripts/goalkeeper-turn-start.mjs --workspace <workspace>
66
+ node <skill-path>/scripts/goalkeeper-turn-start.mjs --workspace <workspace>
67
67
  ```
68
68
 
69
69
  To create a new session deterministically, run:
70
70
 
71
71
  ```bash
72
- node <skill-path>/src/scripts/goalkeeper-init.mjs --workspace <workspace> --session <goal-session-id> --goal "<active goal>"
72
+ node <skill-path>/scripts/goalkeeper-init.mjs --workspace <workspace> --session <goal-session-id> --goal "<active goal>"
73
73
  ```
74
74
 
75
75
  ## Recovery Rule
@@ -109,7 +109,7 @@ Update Goalkeeper state when any of these change:
109
109
  - handoff boundary
110
110
 
111
111
  Append the event first, then update the session's `checkpoint.md` when the event changes the current working state.
112
- Use `src/scripts/goalkeeper-update-checkpoint.mjs` when you want a bounded canonical checkpoint instead of manual Markdown edits.
112
+ Use `scripts/goalkeeper-update-checkpoint.mjs` when you want a bounded canonical checkpoint instead of manual Markdown edits.
113
113
 
114
114
  ## Keep It Short
115
115
 
@@ -148,14 +148,14 @@ Read it when checkpoint recovery is not enough. Keep raw transcripts and long co
148
148
 
149
149
  ## Reference Map
150
150
 
151
- - Read `src/references/workflow.md` for lifecycle rules and examples.
152
- - Read `src/references/event-schema.md` before adding or validating event records.
153
- - Read `src/references/guardrail.md` when you need stronger always-on behavior in a target repository.
154
- - Run `src/scripts/goalkeeper-init.mjs` when a long-running goal needs a new `.goalkeeper/sessions/<goal-session-id>/` directory.
155
- - Run `src/scripts/goalkeeper-turn-start.mjs --context` when checkpoint recovery needs the larger context pack too.
156
- - Run `src/scripts/goalkeeper-append-event.mjs` instead of hand-writing JSONL when recording decisions, verification, failures, risks, or handoffs; it can use `.goalkeeper/active-session` when `--session` is omitted.
157
- - Run `src/scripts/goalkeeper-update-checkpoint.mjs` after appending a meaningful event when checkpoint state should be refreshed in a short canonical shape.
158
- - Run `src/scripts/goalkeeper-doctor.mjs` after creating or changing Goalkeeper state to verify the target workspace is ready.
151
+ - Read `references/workflow.md` for lifecycle rules and examples.
152
+ - Read `references/event-schema.md` before adding or validating event records.
153
+ - Read `references/guardrail.md` when you need stronger always-on behavior in a target repository.
154
+ - Run `scripts/goalkeeper-init.mjs` when a long-running goal needs a new `.goalkeeper/sessions/<goal-session-id>/` directory.
155
+ - Run `scripts/goalkeeper-turn-start.mjs --context` when checkpoint recovery needs the larger context pack too.
156
+ - Run `scripts/goalkeeper-append-event.mjs` instead of hand-writing JSONL when recording decisions, verification, failures, risks, or handoffs; it can use `.goalkeeper/active-session` when `--session` is omitted.
157
+ - Run `scripts/goalkeeper-update-checkpoint.mjs` after appending a meaningful event when checkpoint state should be refreshed in a short canonical shape.
158
+ - Run `scripts/goalkeeper-doctor.mjs` after creating or changing Goalkeeper state to verify the target workspace is ready.
159
159
 
160
160
  ## Safety Boundary
161
161
 
@@ -11,7 +11,7 @@ Default path:
11
11
  Prefer the append helper when available:
12
12
 
13
13
  ```bash
14
- node <skill-path>/src/scripts/goalkeeper-append-event.mjs --workspace <workspace> --session <goal-session-id> --type decision --text "<summary>"
14
+ node <skill-path>/scripts/goalkeeper-append-event.mjs --workspace <workspace> --session <goal-session-id> --type decision --text "<summary>"
15
15
  ```
16
16
 
17
17
  When `<workspace>/.goalkeeper/active-session` points to the current session, `--session` may be omitted. The helper validates existing JSONL schema before writing and reports the appended line number.
@@ -5,7 +5,7 @@ The skill body is not enough to guarantee checkpoint-first behavior after compac
5
5
  For high-stakes long-running work, add the AGENTS guardrail template to the target workspace:
6
6
 
7
7
  ```text
8
- src/templates/AGENTS.goalkeeper.md
8
+ templates/AGENTS.goalkeeper.md
9
9
  ```
10
10
 
11
11
  Use it in one of these ways:
@@ -29,34 +29,34 @@ For an active Goalkeeper-managed goal:
29
29
  3. Use `events.jsonl` only when exact evidence is needed.
30
30
  4. Append `recovery_violation` if the agent continued after compaction or resume before reading the checkpoint.
31
31
 
32
- If `src/scripts/goalkeeper-turn-start.mjs` is present, it can be used as the first recovery action:
32
+ If `scripts/goalkeeper-turn-start.mjs` is present, it can be used as the first recovery action:
33
33
 
34
34
  ```bash
35
- node src/scripts/goalkeeper-turn-start.mjs --session <goal-session-id>
35
+ node scripts/goalkeeper-turn-start.mjs --session <goal-session-id>
36
36
  ```
37
37
 
38
38
  If `.goalkeeper/active-session` contains the current session id, this shorter form is valid:
39
39
 
40
40
  ```bash
41
- node src/scripts/goalkeeper-turn-start.mjs
41
+ node scripts/goalkeeper-turn-start.mjs
42
42
  ```
43
43
 
44
44
  If the helper is being run from an installed skill package rather than from the target repository, pass the target workspace explicitly:
45
45
 
46
46
  ```bash
47
- node <skill-path>/src/scripts/goalkeeper-turn-start.mjs --workspace <workspace> --session <goal-session-id>
47
+ node <skill-path>/scripts/goalkeeper-turn-start.mjs --workspace <workspace> --session <goal-session-id>
48
48
  ```
49
49
 
50
50
  Add `--context` when the medium-density context pack is needed:
51
51
 
52
52
  ```bash
53
- node <skill-path>/src/scripts/goalkeeper-turn-start.mjs --workspace <workspace> --session <goal-session-id> --context
53
+ node <skill-path>/scripts/goalkeeper-turn-start.mjs --workspace <workspace> --session <goal-session-id> --context
54
54
  ```
55
55
 
56
56
  Before starting a high-stakes long run, use the read-only doctor to verify the target workspace has the required state and guardrail:
57
57
 
58
58
  ```bash
59
- node <skill-path>/src/scripts/goalkeeper-doctor.mjs --workspace <workspace> --session <goal-session-id> --strict
59
+ node <skill-path>/scripts/goalkeeper-doctor.mjs --workspace <workspace> --session <goal-session-id> --strict
60
60
  ```
61
61
 
62
62
  Parallel calls are still subject to checkpoint-first ordering. It is acceptable to batch `pwd`, `.goalkeeper/sessions` discovery, and `goalkeeper-turn-start.mjs`; it is not acceptable to include normal project files or verification in that same first post-compact parallel call.
@@ -17,7 +17,7 @@ When a user starts a long-running goal:
17
17
  Use the init helper when available:
18
18
 
19
19
  ```bash
20
- node <skill-path>/src/scripts/goalkeeper-init.mjs --workspace <workspace> --session <goal-session-id> --goal "<active goal>"
20
+ node <skill-path>/scripts/goalkeeper-init.mjs --workspace <workspace> --session <goal-session-id> --goal "<active goal>"
21
21
  ```
22
22
 
23
23
  Pass repeated `--constraint "<text>"` flags for known durable constraints. The helper refuses to overwrite an existing session unless `--force` is explicitly provided.
@@ -37,7 +37,7 @@ During normal work:
37
37
  Use the append helper for routine event writes:
38
38
 
39
39
  ```bash
40
- node <skill-path>/src/scripts/goalkeeper-append-event.mjs --workspace <workspace> --session <goal-session-id> --type verification --text "<summary>"
40
+ node <skill-path>/scripts/goalkeeper-append-event.mjs --workspace <workspace> --session <goal-session-id> --type verification --text "<summary>"
41
41
  ```
42
42
 
43
43
  If `.goalkeeper/active-session` points to the target session, `--session` may be omitted. The helper reports the appended JSONL line number so later checkpoint evidence can cite the event precisely.
@@ -45,7 +45,7 @@ If `.goalkeeper/active-session` points to the target session, `--session` may be
45
45
  When the event changes the recoverable working state, refresh the checkpoint in the same working segment:
46
46
 
47
47
  ```bash
48
- node <skill-path>/src/scripts/goalkeeper-update-checkpoint.mjs \
48
+ node <skill-path>/scripts/goalkeeper-update-checkpoint.mjs \
49
49
  --workspace <workspace> \
50
50
  --session <goal-session-id> \
51
51
  --goal "<active goal>" \
@@ -84,19 +84,19 @@ sed -n '1,220p' .goalkeeper/sessions/<goal-session-id>/checkpoint.md
84
84
  If the turn-start helper is available, use it instead of manually reading the checkpoint:
85
85
 
86
86
  ```bash
87
- node <skill-path>/src/scripts/goalkeeper-turn-start.mjs --workspace <workspace> --session <goal-session-id>
87
+ node <skill-path>/scripts/goalkeeper-turn-start.mjs --workspace <workspace> --session <goal-session-id>
88
88
  ```
89
89
 
90
90
  If `.goalkeeper/active-session` points to the correct session id, omit `--session`:
91
91
 
92
92
  ```bash
93
- node <skill-path>/src/scripts/goalkeeper-turn-start.mjs --workspace <workspace>
93
+ node <skill-path>/scripts/goalkeeper-turn-start.mjs --workspace <workspace>
94
94
  ```
95
95
 
96
96
  If checkpoint recovery is too thin, include the context pack:
97
97
 
98
98
  ```bash
99
- node <skill-path>/src/scripts/goalkeeper-turn-start.mjs --workspace <workspace> --context
99
+ node <skill-path>/scripts/goalkeeper-turn-start.mjs --workspace <workspace> --context
100
100
  ```
101
101
 
102
102
  ## Resume After Compaction
@@ -133,7 +133,7 @@ If an agent violates this order, append a `recovery_violation` event, read the c
133
133
  Before relying on a workspace for a long run, run the read-only doctor:
134
134
 
135
135
  ```bash
136
- node <skill-path>/src/scripts/goalkeeper-doctor.mjs --workspace <workspace> --session <goal-session-id> --strict
136
+ node <skill-path>/scripts/goalkeeper-doctor.mjs --workspace <workspace> --session <goal-session-id> --strict
137
137
  ```
138
138
 
139
139
  ## Goal Session Directory
@@ -22,7 +22,7 @@ const EVENT_TYPES = new Set([
22
22
  const STATUSES = new Set(["open", "done", "failed", "blocked", "superseded"]);
23
23
 
24
24
  const USAGE = `Usage:
25
- node src/scripts/goalkeeper-append-event.mjs --type <event-type> --text <summary> [--session <goal-session-id>] [--workspace <path>] [--goal <text>] [--reason <text>] [--evidence <text>] [--status <status>] [--file <path> ...] [--command <cmd> ...] [--ts <iso>] [--json]
25
+ node scripts/goalkeeper-append-event.mjs --type <event-type> --text <summary> [--session <goal-session-id>] [--workspace <path>] [--goal <text>] [--reason <text>] [--evidence <text>] [--status <status>] [--file <path> ...] [--command <cmd> ...] [--ts <iso>] [--json]
26
26
 
27
27
  Appends one validated JSONL event to <workspace>/.goalkeeper/sessions/<goal-session-id>/events.jsonl.
28
28
  This script writes only to the target events.jsonl file.
@@ -28,7 +28,7 @@ const CONTEXT_PACK_TARGET_BYTES = 30_000;
28
28
  const CONTEXT_PACK_MAX_BYTES = 60_000;
29
29
 
30
30
  const USAGE = `Usage:
31
- node src/scripts/goalkeeper-doctor.mjs --session <goal-session-id> [--workspace <path>] [--strict] [--json]
31
+ node scripts/goalkeeper-doctor.mjs --session <goal-session-id> [--workspace <path>] [--strict] [--json]
32
32
 
33
33
  Checks whether a target workspace has enough Goalkeeper state and guardrails for long-running Codex goal work.
34
34
  This script is read-only.
@@ -4,7 +4,7 @@ import fs from "node:fs";
4
4
  import path from "node:path";
5
5
 
6
6
  const USAGE = `Usage:
7
- node src/scripts/goalkeeper-init.mjs --session <goal-session-id> --goal <text> [--workspace <path>] [--constraint <text> ...] [--no-activate] [--force] [--json]
7
+ node scripts/goalkeeper-init.mjs --session <goal-session-id> --goal <text> [--workspace <path>] [--constraint <text> ...] [--no-activate] [--force] [--json]
8
8
 
9
9
  Creates a project-local Goalkeeper session with checkpoint.md, context-pack.md, and events.jsonl.
10
10
  This script writes only under <workspace>/.goalkeeper/sessions/<goal-session-id>/.
@@ -4,7 +4,7 @@ import fs from "node:fs";
4
4
  import path from "node:path";
5
5
 
6
6
  const USAGE = `Usage:
7
- node src/scripts/goalkeeper-turn-start.mjs [--session <goal-session-id>] [--workspace <path>] [--events <n>] [--context] [--json]
7
+ node scripts/goalkeeper-turn-start.mjs [--session <goal-session-id>] [--workspace <path>] [--events <n>] [--context] [--json]
8
8
 
9
9
  Reads the active Goalkeeper checkpoint at the start of a Codex turn.
10
10
  This script reads only .goalkeeper state.
@@ -7,7 +7,7 @@ const DEFAULT_MAX_BYTES = 8_000;
7
7
  const HARD_MAX_BYTES = 16_000;
8
8
 
9
9
  const USAGE = `Usage:
10
- node src/scripts/goalkeeper-update-checkpoint.mjs --goal <text> --next <text> [--session <goal-session-id>] [--workspace <path>] [--done <text>] [--status <text>] [--throughline <text>] [--why <text>] [--constraint <text> ...] [--forbidden <text> ...] [--decision <text> ...] [--attempt <text> ...] [--file <path> ...] [--verified <text> ...] [--unverified <text> ...] [--risk <text> ...] [--evidence <text> ...] [--max-bytes <n>] [--dry-run] [--json]
10
+ node scripts/goalkeeper-update-checkpoint.mjs --goal <text> --next <text> [--session <goal-session-id>] [--workspace <path>] [--done <text>] [--status <text>] [--throughline <text>] [--why <text>] [--constraint <text> ...] [--forbidden <text> ...] [--decision <text> ...] [--attempt <text> ...] [--file <path> ...] [--verified <text> ...] [--unverified <text> ...] [--risk <text> ...] [--evidence <text> ...] [--max-bytes <n>] [--dry-run] [--json]
11
11
 
12
12
  Replaces checkpoint.md with a bounded, canonical recovery checkpoint.
13
13
  Append the corresponding event first with goalkeeper-append-event.mjs; this script writes only checkpoint.md.
@@ -10,22 +10,22 @@ At the start of each new assistant turn, before reading normal project files or
10
10
  4. If the checkpoint is unclear or too thin, read `.goalkeeper/sessions/<goal-session-id>/context-pack.md`.
11
11
  5. If exact evidence is needed, inspect recent `.goalkeeper/sessions/<goal-session-id>/events.jsonl` entries.
12
12
 
13
- If this repository includes `src/scripts/goalkeeper-turn-start.mjs`, you may use:
13
+ If this repository includes `scripts/goalkeeper-turn-start.mjs`, you may use:
14
14
 
15
15
  ```bash
16
- node src/scripts/goalkeeper-turn-start.mjs --session <goal-session-id>
16
+ node scripts/goalkeeper-turn-start.mjs --session <goal-session-id>
17
17
  ```
18
18
 
19
19
  If `.goalkeeper/active-session` contains the current session id, this shorter form is also valid:
20
20
 
21
21
  ```bash
22
- node src/scripts/goalkeeper-turn-start.mjs
22
+ node scripts/goalkeeper-turn-start.mjs
23
23
  ```
24
24
 
25
25
  If the helper comes from an installed skill path instead of this repository, pass the target workspace:
26
26
 
27
27
  ```bash
28
- node <skill-path>/src/scripts/goalkeeper-turn-start.mjs --workspace <workspace> --session <goal-session-id>
28
+ node <skill-path>/scripts/goalkeeper-turn-start.mjs --workspace <workspace> --session <goal-session-id>
29
29
  ```
30
30
 
31
31
  If checkpoint recovery needs the larger context pack too, add `--context`.
@@ -36,9 +36,9 @@ Allowed before reading the checkpoint:
36
36
  - listing `.goalkeeper/sessions/`
37
37
  - reading `.goalkeeper/active-session`
38
38
  - minimal filename inspection needed to choose the active session
39
- - running `node src/scripts/goalkeeper-turn-start.mjs --session <goal-session-id>`
40
- - running `node src/scripts/goalkeeper-turn-start.mjs`
41
- - running `node <skill-path>/src/scripts/goalkeeper-turn-start.mjs --workspace <workspace> --session <goal-session-id>`
39
+ - running `node scripts/goalkeeper-turn-start.mjs --session <goal-session-id>`
40
+ - running `node scripts/goalkeeper-turn-start.mjs`
41
+ - running `node <skill-path>/scripts/goalkeeper-turn-start.mjs --workspace <workspace> --session <goal-session-id>`
42
42
  - adding `--context` to the turn-start command when the checkpoint is too thin
43
43
 
44
44
  Do not read project docs, source files, examples, tests, or make edits before the checkpoint read.
@@ -1,236 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { spawnSync } from "node:child_process";
4
- import fs from "node:fs";
5
- import path from "node:path";
6
- import { fileURLToPath } from "node:url";
7
-
8
- const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
9
- const REPO_ROOT = path.resolve(SCRIPT_DIR, "..", "..");
10
- const TMP_ROOT = path.join(REPO_ROOT, ".goalkeeper", "tmp", "checkpoint-update");
11
- const WORKSPACE = path.join(TMP_ROOT, "workspace");
12
- const SESSION_ID = "checkpoint-update-poc";
13
-
14
- function run(args, options = {}) {
15
- const result = spawnSync(process.execPath, args, {
16
- cwd: REPO_ROOT,
17
- encoding: "utf8",
18
- ...options,
19
- });
20
- return {
21
- ...result,
22
- stdout: result.stdout.trim(),
23
- stderr: result.stderr.trim(),
24
- };
25
- }
26
-
27
- function script(name) {
28
- return path.join(SCRIPT_DIR, name);
29
- }
30
-
31
- function assert(condition, message) {
32
- if (!condition) {
33
- throw new Error(message);
34
- }
35
- }
36
-
37
- function setupWorkspace() {
38
- fs.rmSync(TMP_ROOT, { recursive: true, force: true });
39
- fs.mkdirSync(WORKSPACE, { recursive: true });
40
- fs.writeFileSync(
41
- path.join(WORKSPACE, "AGENTS.md"),
42
- [
43
- "# Goalkeeper Guardrail",
44
- "",
45
- "At the start of each new assistant turn, before source work, read .goalkeeper/sessions/<goal-session-id>/checkpoint.md.",
46
- "This checkpoint-first rule applies after compaction, compact, start, resume, and before normal project work.",
47
- "",
48
- ].join("\n"),
49
- );
50
- }
51
-
52
- function main() {
53
- setupWorkspace();
54
-
55
- const init = run([
56
- script("goalkeeper-init.mjs"),
57
- "--workspace",
58
- WORKSPACE,
59
- "--session",
60
- SESSION_ID,
61
- "--goal",
62
- "Validate canonical checkpoint updates.",
63
- "--constraint",
64
- "Keep checkpoints short.",
65
- "--json",
66
- ]);
67
- assert(init.status === 0, `init failed:\n${init.stderr}\n${init.stdout}`);
68
-
69
- const append = run([
70
- script("goalkeeper-append-event.mjs"),
71
- "--workspace",
72
- WORKSPACE,
73
- "--type",
74
- "decision",
75
- "--text",
76
- "Use the update helper to rewrite checkpoint.md canonically.",
77
- "--json",
78
- ]);
79
- assert(append.status === 0, `append failed:\n${append.stderr}\n${append.stdout}`);
80
- const parsedAppend = JSON.parse(append.stdout);
81
- assert(parsedAppend.sessionId === SESSION_ID, "append helper should recover the session id from active-session");
82
- assert(parsedAppend.lineNumber === 4, "append helper should report the appended JSONL line number");
83
-
84
- const contextPackPath = path.join(WORKSPACE, ".goalkeeper", "sessions", SESSION_ID, "context-pack.md");
85
- assert(fs.existsSync(contextPackPath), "init should create context-pack.md");
86
-
87
- const turnStartWithContext = run([
88
- script("goalkeeper-turn-start.mjs"),
89
- "--workspace",
90
- WORKSPACE,
91
- "--context",
92
- "--json",
93
- ]);
94
- assert(turnStartWithContext.status === 0, `turn-start --context failed:\n${turnStartWithContext.stderr}\n${turnStartWithContext.stdout}`);
95
- const parsedTurnStart = JSON.parse(turnStartWithContext.stdout);
96
- assert(parsedTurnStart.contextPackPath === contextPackPath, "turn-start should report context-pack.md path");
97
- assert(parsedTurnStart.contextPack.includes("Context Pack"), "turn-start --context should include context pack content");
98
-
99
- const noActiveWorkspace = path.join(TMP_ROOT, "no-active-workspace");
100
- fs.mkdirSync(path.join(noActiveWorkspace, ".goalkeeper", "sessions", SESSION_ID), { recursive: true });
101
- fs.writeFileSync(path.join(noActiveWorkspace, ".goalkeeper", "sessions", SESSION_ID, "events.jsonl"), "");
102
- const appendWithoutActive = run([
103
- script("goalkeeper-append-event.mjs"),
104
- "--workspace",
105
- noActiveWorkspace,
106
- "--type",
107
- "decision",
108
- "--text",
109
- "This should fail because no active-session pointer exists.",
110
- "--json",
111
- ]);
112
- assert(appendWithoutActive.status === 1, "append without --session should fail when active-session is missing");
113
-
114
- const schemaInvalidWorkspace = path.join(TMP_ROOT, "schema-invalid-workspace");
115
- const schemaInvalidSessionDir = path.join(schemaInvalidWorkspace, ".goalkeeper", "sessions", SESSION_ID);
116
- fs.mkdirSync(schemaInvalidSessionDir, { recursive: true });
117
- fs.writeFileSync(path.join(schemaInvalidWorkspace, ".goalkeeper", "active-session"), `${SESSION_ID}\n`);
118
- fs.writeFileSync(
119
- path.join(schemaInvalidSessionDir, "events.jsonl"),
120
- `${JSON.stringify({ ts: "2026-05-18T00:00:00Z", type: "unknown", text: "bad existing event" })}\n`,
121
- );
122
- const appendToSchemaInvalid = run([
123
- script("goalkeeper-append-event.mjs"),
124
- "--workspace",
125
- schemaInvalidWorkspace,
126
- "--type",
127
- "decision",
128
- "--text",
129
- "This should fail because the existing event log is schema-invalid.",
130
- "--json",
131
- ]);
132
- assert(appendToSchemaInvalid.status === 1, "append should refuse an existing schema-invalid event log");
133
-
134
- const update = run([
135
- script("goalkeeper-update-checkpoint.mjs"),
136
- "--workspace",
137
- WORKSPACE,
138
- "--goal",
139
- "Validate canonical checkpoint updates.",
140
- "--done",
141
- "Doctor passes after a helper-rendered checkpoint.",
142
- "--status",
143
- "Helper under test.",
144
- "--throughline",
145
- "Use deterministic CLI rendering instead of manual checkpoint Markdown.",
146
- "--why",
147
- "Long sessions need bounded state that can be safely refreshed after compacted turns.",
148
- "--constraint",
149
- "Keep checkpoint under the routine-read budget.",
150
- "--forbidden",
151
- "Do not paste long command output into checkpoint.md.",
152
- "--decision",
153
- "Render a canonical checkpoint from CLI fields.",
154
- "--attempt",
155
- "Manual checkpoint edits remain possible but are not the default path.",
156
- "--file",
157
- "src/scripts/goalkeeper-update-checkpoint.mjs",
158
- "--file",
159
- "docs/path with spaces.md",
160
- "--evidence",
161
- "This test rewrites checkpoint.md in a guarded temporary workspace.",
162
- "--verified",
163
- "Update helper exits 0.",
164
- "--unverified",
165
- "No real compact boundary is generated by this unit test.",
166
- "--risk",
167
- "Overlong input should fail before writing.",
168
- "--next",
169
- "Run strict doctor against the updated temporary workspace.",
170
- "--json",
171
- ]);
172
- assert(update.status === 0, `update failed:\n${update.stderr}\n${update.stdout}`);
173
-
174
- const parsedUpdate = JSON.parse(update.stdout);
175
- assert(parsedUpdate.bytes > 0 && parsedUpdate.bytes <= 8_000, "updated checkpoint should fit the default budget");
176
-
177
- const checkpointPath = path.join(WORKSPACE, ".goalkeeper", "sessions", SESSION_ID, "checkpoint.md");
178
- const checkpoint = fs.readFileSync(checkpointPath, "utf8");
179
- assert(checkpoint.includes("## Active Goal"), "checkpoint should include Active Goal section");
180
- assert(checkpoint.includes("## Context Pack"), "checkpoint should include Context Pack section");
181
- assert(checkpoint.includes("## Next Action"), "checkpoint should include Next Action section");
182
- assert(checkpoint.includes("src/scripts/goalkeeper-update-checkpoint.mjs"), "checkpoint should include important files");
183
- assert(checkpoint.includes("docs/path with spaces.md"), "checkpoint should preserve spaces in file paths");
184
-
185
- const beforeOversize = checkpoint;
186
- const oversize = run([
187
- script("goalkeeper-update-checkpoint.mjs"),
188
- "--workspace",
189
- WORKSPACE,
190
- "--goal",
191
- "Validate oversize refusal.",
192
- "--decision",
193
- "x".repeat(2_000),
194
- "--next",
195
- "This should not be written.",
196
- "--max-bytes",
197
- "1000",
198
- "--json",
199
- ]);
200
- assert(oversize.status === 1, "oversize checkpoint update should exit 1");
201
- assert(fs.readFileSync(checkpointPath, "utf8") === beforeOversize, "oversize failure should not rewrite checkpoint.md");
202
-
203
- const doctor = run([
204
- script("goalkeeper-doctor.mjs"),
205
- "--workspace",
206
- WORKSPACE,
207
- "--session",
208
- SESSION_ID,
209
- "--strict",
210
- "--json",
211
- ]);
212
- assert(doctor.status === 0, `doctor failed:\n${doctor.stderr}\n${doctor.stdout}`);
213
- const parsedDoctor = JSON.parse(doctor.stdout);
214
- assert(parsedDoctor.ok === true, "doctor JSON should be ok");
215
-
216
- console.log(
217
- JSON.stringify(
218
- {
219
- ok: true,
220
- workspace: WORKSPACE,
221
- sessionId: SESSION_ID,
222
- checkpointBytes: parsedUpdate.bytes,
223
- doctor: parsedDoctor.summary,
224
- },
225
- null,
226
- 2,
227
- ),
228
- );
229
- }
230
-
231
- try {
232
- main();
233
- } catch (error) {
234
- console.error(error.message);
235
- process.exit(1);
236
- }