Skip to content

Commit 1058ffa

Browse files
committed
Polish Task Passport continuity cadence
1 parent 30ed7e2 commit 1058ffa

9 files changed

Lines changed: 85 additions & 21 deletions

File tree

docs/CLI.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ agentpack task close
4949

5050
Write scopes are repo-relative paths. `.` means the repository root.
5151

52-
`task passport` prints the current `passport.json`. `task switch <id>` points the worktree at another open passport. `task update` patches objective, constraints, write scope, next actions, tags, and risk without changing lifecycle status; list fields are appended and deduplicated. Empty or no-op updates fail, and unknown risk values are rejected. `task audit` checks the current passport for stale source conclusions, branch/head drift, missing next actions, open verification, and closed-task anomalies. `task update-verification` writes verification status, evidence IDs, and summary into the passport; without flags it marks verification as `pending`, and with `--status`, `--evidence`, and `--summary` it can close the audit warning with evidence-backed verification. When a current passport exists, `resume` and MCP `load_context` include it above the repo-level ledger so agents can see the active task before broader history.
52+
`task start` refuses to replace an active, blocked, or verifying current task; park or close the current task first when starting unrelated work. Invalid risk values are rejected instead of being treated as unknown. `task passport` prints the current `passport.json`. `task switch <id>` points the worktree at another open passport. `task update` patches objective, constraints, write scope, next actions, tags, and risk without changing lifecycle status; list fields are appended and deduplicated. Empty or no-op updates fail, and unknown risk values are rejected. `task audit` checks the current passport for stale source conclusions, branch/head drift, missing next actions, open verification, and closed-task anomalies. `task update-verification` writes verification status, evidence IDs, and summary into the passport; without flags it marks verification as `pending`, and with `--status`, `--evidence`, and `--summary` it can close the audit warning with evidence-backed verification. When a current passport exists, `resume` and MCP `load_context` include it above the repo-level ledger so agents can see the active task before broader history.
5353

5454
## Record Durable State
5555

docs/DOGFOOD.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ Load a small context first:
88

99
```text
1010
load_context(preset: "quick")
11-
source_status()
1211
```
1312

1413
Use `agent` instead of `quick` when the task needs more history.
@@ -40,9 +39,11 @@ Record only durable context. Agentpack is not an activity logger, and it should
4039

4140
Default cadence:
4241

43-
- At task start, load Agentpack context and source status.
42+
- At task start, load Agentpack context.
43+
- Call source status only when you need a full stale-source check beyond the loaded context.
4444
- During normal coding, keep working locally; record only durable decisions, dead ends, source conclusions, and evidence.
45-
- At the end of a coherent step, record the useful sources/evidence, update status and next actions, then checkpoint.
45+
- Sequence state-changing Agentpack calls; do not run them in parallel with audit, status, or checkpoint calls.
46+
- At the end of a coherent step, record aggregated evidence, update status and next actions, then checkpoint.
4647
- Use full safe mode for risky or release-like changes: record important findings as they happen and run the full verification loop.
4748

4849
This keeps Agentpack useful without turning every micro-step into ledger traffic. The intended default cost is one context load near the start and one durable save near the end.
@@ -84,8 +85,10 @@ Git still owns code history. Agentpack owns task memory.
8485

8586
While dogfooding, look for friction:
8687

87-
- Did the agent call `load_context` and `source_status` early enough?
88+
- Did the agent call `load_context` early enough without repeating status checks unnecessarily?
8889
- Were unchanged sources avoided when recorded conclusions were enough?
90+
- Did `record_source` capture only reusable source conclusions instead of one event per changed file?
91+
- Were state-changing Agentpack calls sequenced so audits read the latest state?
8992
- Were decisions and dead ends recorded at useful moments?
9093
- Was evidence too noisy or too thin?
9194
- Was the checkpoint useful to the next session?

docs/MCP.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ Reference: [Model Context Protocol transports](https://modelcontextprotocol.io/d
1111
Generated Codex, Claude Code, and Cursor instructions tell connected agents to use the MCP tools as a small hybrid continuity loop:
1212

1313
1. Start by calling `load_context` with a focused query and compact preset.
14-
2. Call `source_status` before relying on previously recorded source conclusions.
14+
2. Call `source_status` only when you need a full stale-source check beyond the context you just loaded.
1515
3. Record durable decisions, dead ends, evidence, and reviewed source conclusions while working.
1616
4. Call `checkpoint` after meaningful progress so the next session inherits status, next actions, git state, and compact resume context.
1717

18+
For small tasks, prefer one aggregated evidence item plus one checkpoint summary. Do not call `source_status` repeatedly when `load_context`, `task_audit`, or a recent status check already answered the question. Do not call `record_source` for every changed file just to clear an audit warning; refresh a source record only when its durable conclusion changed.
19+
1820
The CLI exposes the same operations for setup, inspection, debugging, demos, and web-chat fallback. See [CLI.md](CLI.md) for manual command examples.
1921

2022
## Tools

docs/RELEASE_NOTES.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Release Notes Draft
2+
3+
## Next patch release
4+
5+
Draft scope since `0.1.10`:
6+
7+
- Add Task Passport update flow for objective, constraints, write scope, next actions, tags, and risk.
8+
- Expose `task_update` through MCP alongside `task_audit` and `task_update_verification`.
9+
- Preserve `.` as a repo-root write scope.
10+
- Protect the current-passport workflow: `task start` now refuses to replace active, blocked, or verifying work.
11+
- Reject invalid task risk values consistently in `task start`, `task update`, and MCP `task_update`.
12+
- Clarify generated Codex, Claude, and Cursor instructions so agents use compact ledger cadence instead of repeated status checks or per-file source-record spam.
13+
- Add collaboration modes and safety guidance to generated project instructions.
14+
- Align CLI, MCP, Dogfood, and Task Passport docs with the implemented behavior.
15+
16+
Verification before release:
17+
18+
- `npm test`
19+
- `npm run mcp:smoke`
20+
- `npm pack --dry-run`
21+
- privacy scan for local paths and secret-like values
22+
- `git diff --check`
23+
- `agentpack doctor`
24+
- `agentpack task audit`
25+
26+
Release gate:
27+
28+
- Complete final review of the accumulated local commits.
29+
- Push only after review is accepted.
30+
- Cut the npm release only after the pushed branch is stable and docs match the release scope.

docs/TASK-PASSPORT.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ agentpack task close
184184

185185
`resume` and MCP `load_context` read the current passport automatically when one exists, then show the broader repo-level ledger below it.
186186

187+
`task start` creates a new current passport only when there is no current task, the current task is closed, or the current task is parked. If the current task is active, blocked, or verifying, Agentpack asks you to park or close it first so unrelated work does not silently overwrite the handoff pointer. Invalid risk values are rejected instead of being treated as unknown.
188+
187189
`task audit` is a diagnostic pass for continuity risk. It checks the current passport for branch/head drift, missing next actions, open verification, stale source conclusions, and closed-current-task anomalies.
188190

189191
`task update` patches the current passport without changing lifecycle status. It can add objective, constraints, write scope, next actions, tags, and risk after the task has already started. List fields append and deduplicate; omitted fields are preserved. Empty or no-op updates fail, and unknown risk values are rejected.

src/cli/index.ts

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ Usage:
211211
agentpack set goal <text>
212212
agentpack set status <text>
213213
agentpack set next <item> [--next <item>]
214-
agentpack task start <title> [--objective <text>] [--write-scope <path>]
214+
agentpack task start <title> [--objective <text>] [--constraint <text>] [--write-scope <path>] [--next <item>] [--tag <tag>] [--risk low|medium|high]
215215
agentpack task update [--objective <text>] [--constraint <text>] [--write-scope <path>] [--next <item>] [--tag <tag>] [--risk low|medium|high]
216216
agentpack task list
217217
agentpack task passport
@@ -285,15 +285,17 @@ function taskCommand(root: string, rest: string[]): void {
285285
if (subcommand === "start") {
286286
const parsed = parseArgs(args);
287287
const title = parsed.positionals.join(" ").trim();
288-
const passport = startTask(root, {
288+
const startOptions = {
289289
title: redactForRoot(root, title),
290290
objective: redactForRoot(root, stringOption(parsed.options.objective)),
291291
constraints: toArray(parsed.options.constraint).map((item) => redactForRoot(root, item)),
292292
writeScope: toArray(parsed.options["write-scope"]),
293293
nextActions: toArray(parsed.options.next).map((item) => redactForRoot(root, item)),
294-
tags: toArray(parsed.options.tag),
295-
risk: riskOption(parsed.options.risk)
296-
});
294+
tags: toArray(parsed.options.tag)
295+
};
296+
const passport = startTask(root, optionValue(parsed.options, "risk")
297+
? { ...startOptions, risk: taskRiskOption(parsed.options.risk) }
298+
: startOptions);
297299
process.stdout.write(`Started task ${passport.id}\n`);
298300
return;
299301
}
@@ -663,14 +665,6 @@ function budgetOption(options: Record<string, ArgValue>, fallback = 0): number {
663665
}, fallback);
664666
}
665667

666-
function riskOption(value: ArgValue | undefined): "low" | "medium" | "high" | "unknown" {
667-
const risk = stringOption(value);
668-
if (risk === "low" || risk === "medium" || risk === "high") {
669-
return risk;
670-
}
671-
return "unknown";
672-
}
673-
674668
function taskRiskOption(value: ArgValue | undefined): "low" | "medium" | "high" | "unknown" {
675669
const risk = stringOption(value);
676670
if (risk === "unknown" || risk === "low" || risk === "medium" || risk === "high") {

src/core/tasks.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ export function startTask(root: string, options: TaskStartOptions): TaskPassport
7272

7373
return withPackWriteLock(root, () => {
7474
ensureTasksDir(root);
75+
const currentTask = getCurrentPassport(root);
76+
if (currentTask && !CLOSED_STATUSES.has(currentTask.status) && currentTask.status !== "parked") {
77+
throw new Error(
78+
`Current task ${currentTask.id} is ${currentTask.status}; park or close it before starting a new task.`
79+
);
80+
}
7581

7682
const now = new Date().toISOString();
7783
const git = getGitInfo(root);

src/integrations/install.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ ${collaborationModesSection()}
3030
3131
At the start of a task:
3232
- call \`load_context\` with a small preset first
33-
- call \`source_status\` before re-reading previously inspected files
33+
- call \`source_status\` only when you need a full stale-source check beyond the context you just loaded
3434
3535
During work:
3636
- call \`record_source\` only when you have a durable conclusion about an important file; avoid repeated records for the same file unless the conclusion changed
@@ -40,11 +40,14 @@ During work:
4040
4141
Avoid turning Agentpack into an activity log:
4242
- do not record every file read, mode switch, minor diff check, or routine command
43-
- for small tasks, prefer one final source record per touched important file plus one verification evidence and one checkpoint
43+
- do not call \`source_status\` repeatedly when \`load_context\`, \`task_audit\`, or a recent status check already answered the question
44+
- do not call \`record_source\` for every changed file just to make an audit warning disappear; prefer a checkpoint summary for batch changes and refresh source records only when the durable conclusion changed
45+
- for small tasks, prefer one aggregated verification evidence and one checkpoint; add source records only for important implementation files with reusable conclusions
4446
4547
Default cadence:
4648
- start with Agentpack context
4749
- work locally without recording every micro-step
50+
- sequence state-changing Agentpack calls; do not run them in parallel with audit/status/checkpoint calls
4851
- record durable findings and evidence before a checkpoint
4952
- use full safe mode for risky or release-like changes
5053

tests/agentpack.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,17 @@ test("manages a current task passport", () => {
446446
"low"
447447
]);
448448
assert.match(started, /Started task task_/);
449+
assert.match(
450+
runExpectError(dir, ["task", "start", "Overlapping task", "--write-scope", "src/index.ts"]),
451+
/Current task .* is active; park or close it before starting a new task\./
452+
);
453+
assert.match(runExpectError(dir, [
454+
"task",
455+
"start",
456+
"Invalid risk task",
457+
"--risk",
458+
"urgent"
459+
]), /Unknown task risk: urgent/);
449460

450461
const taskId = readFileSync(path.join(dir, ".agentpack", "tasks", "current"), "utf8").trim();
451462
const passportPath = path.join(dir, ".agentpack", "tasks", taskId, "passport.json");
@@ -552,6 +563,15 @@ test("manages a current task passport", () => {
552563
const repoWide = JSON.parse(run(dir, ["task", "passport"]));
553564
assert.deepEqual(repoWide.writeScope, ["."]);
554565
assert.doesNotMatch(run(dir, ["task", "audit"]), /Task has no write scope/);
566+
567+
assert.match(run(dir, ["task", "close"]), /Closed task/);
568+
assert.match(runExpectError(dir, [
569+
"task",
570+
"start",
571+
"Invalid risk task",
572+
"--risk",
573+
"urgent"
574+
]), /Unknown task risk: urgent/);
555575
});
556576

557577
test("redacts secrets from stored context and handoff outputs", () => {
@@ -846,6 +866,10 @@ test("previews and writes project-local MCP client install files", () => {
846866
assert.match(readFileSync(path.join(dir, "AGENTS.md"), "utf8"), /checkpoint mode: summarize what was decided/);
847867
assert.match(readFileSync(path.join(dir, "AGENTS.md"), "utf8"), /Avoid turning Agentpack into an activity log/);
848868
assert.match(readFileSync(path.join(dir, "AGENTS.md"), "utf8"), /record_source` only when you have a durable conclusion/);
869+
assert.match(readFileSync(path.join(dir, "AGENTS.md"), "utf8"), /source_status` only when you need a full stale-source check/);
870+
assert.match(readFileSync(path.join(dir, "AGENTS.md"), "utf8"), /prefer one aggregated verification evidence and one checkpoint/);
871+
assert.match(readFileSync(path.join(dir, "AGENTS.md"), "utf8"), /sequence state-changing Agentpack calls/);
872+
assert.doesNotMatch(readFileSync(path.join(dir, "AGENTS.md"), "utf8"), /source_status` before re-reading/);
849873
const codexConfig = readFileSync(path.join(dir, ".codex", "config.toml"), "utf8");
850874
assert.match(codexConfig, new RegExp(`\\[mcp_servers\\.${escapeRegExp(serverName)}\\]`));
851875
assert.doesNotMatch(codexConfig, /\[mcp_servers\.agentpack\]/);

0 commit comments

Comments
 (0)