pm-brief
Token-budgeted agent briefs and next-work plans for pm workspaces
Install
With pm installed
pm install npm:pm-brief --projectWith npm, without installing pm
npx -y @unbrained/pm-cli install npm:pm-brief --projectWith Bun, without installing pm
bunx @unbrained/pm-cli install npm:pm-brief --projectREADME
pm-brief generates compact, deterministic project briefs for coding agents.
It turns pm items into an execution-oriented summary: what to work on next,
what is blocked, what context is stale, and which pm updates should be made.
Project management is context management. pm-brief makes that practical when
an agent needs a low-token handoff instead of a full project dump.
Install
pm install npm:pm-brief --project
The
npm:prefix is required. A barepm install pm-briefresolves only a local directory or a bundled alias, never the registry, and agithub.com/unbraind/pm-briefsource cannot work either — pm copies a GitHub source as-is without building it, and this repository does not commitdist/.
Usage
pm brief
pm brief prompt --focus pm-1234 --max-tokens 2500
pm brief --max-tokens 4000 --format markdown
pm brief --dependency-order --format slack
pm brief --focus pm-1234 --include-closed --format json
pm brief --include-history --format slack
pm brief --focus type:Decision --format json
pm brief prompt --include-history --history-limit 20
pm brief next --count 5 --dependency-order --explain --confidence
pm brief next --count 5 --format json
pm brief next --count 5 --explain --format text
pm brief stale --days 7
pm brief since 7d
pm brief since 2026-07-20 --format json
pm brief since 2026-07-20T00:00:00Z --until 2026-07-22 --author alice
Commands
pm briefrenders a markdown or JSON project brief.pm brief promptrenders a compact copy-pasteable agent handoff prompt.pm brief nextreturns the ranked next items only.pm brief stalereturns stale open or in-progress items.pm brief since <checkpoint>renders a delta brief of what changed since a checkpoint.pm brief diverge [base]previews pm item collisions between two branches before merging.pm brief duplicatessweeps the merged tracker for near-duplicate items the create-time advisory cannot see across branches.pm brief alternatives <id> <id> [more ids]compares recorded proposals, outcomes and evidence with exact citations and no recommendation.
Ranking and Budget Flags
--max-tokensis an alias for--token-budget: a ceiling on the chosen rendered brief, estimated by the SDK asceil(UTF-8 bytes / 4)(not a model tokenizer).--dependency-orderprefers prerequisite items before dependent work in next-work ranking.--focushighlights specific item ids, ortype:Typeto highlight every item of a type (repeatable or comma-separated).--include-historyadds a Recent Activity section sourced frompm activityto briefs and prompts;--history-limitcontrols the entry count (default 10).--formatrendersmarkdown(default),jsonfor machine-readable briefs, orslackfor Slack-formatted briefs.pm brief next --explainadds transparent score and dependency signals for each ranked item.pm briefemits aBrief Insightssection when focus ids are missing, closed focus items are excluded, or active filters hide all open work.--explainonpm brief nextincludes compact ranking evidence such as unblockability, stale age, dependency fanout, release/deadline proximity, and linked docs/files.--confidenceonpm brief nextincludes the confidence score behind each recommendation.
For pm brief (default 4000) and pm brief prompt (default 2500), the ceiling
includes headings, pretty JSON whitespace, the final newline, merge receipts,
and omission/disclosure lines. Compaction caps ancillary sections, shortens
display fields with …, then drops lower-priority entries. Retained items keep
their IDs and the highest-ranked next item remains available. omissions in
JSON, or the rendered omission notice, gives per-section entry counts and
pm list --all / pm get <id> retrieval hints. Counts are section entries, so
an item appearing in both focus and next can be counted twice. Merge compromise
IDs and pending totals remain complete; pm merge report retrieves hidden receipts.
If the smallest actionable brief cannot fit, the command fails before writing
an output file. The error states the minimum required budget and a command to
retry. The API throws CommandError with EXIT_CODE.USAGE (2), matching existing
usage failures; the host CLI controls process error rendering and exit handling.
API callers can choose the rendering before building:
import { buildBrief, renderBrief } from "pm-brief";
const brief = buildBrief(items, { tokenBudget: 1000, format: "markdown" });
const output = renderBrief(brief, "markdown");
buildBrief defaults to budgeting pretty JSON. renderBrief,
renderMarkdownBrief, renderSlackBrief and renderAgentPrompt enforce the
selected rendering again if callers change format or modify the returned object.
budget.estimatedTokens measures the rendering chosen at build time, including
its own digit count and trailing newline, using the SDK UTF-8 byte estimator. An unchanged ordinary tracker that already fits keeps its
sections and fields; only the estimate disclosure can differ from older versions.
See budget evidence and recovery for the regression matrix.
Next-work recommendations exclude items with unresolved prerequisites, including
reverse blocks edges and missing targets. Held work remains visible in explicit
focus, blocker and risk evidence. External targets (source_kind=global, its
external alias, or external reference syntax) stay separate from local IDs:
external reverse edges cannot block local namesakes, and external references
cannot add local dependent context or ranking credit. Workspace terminal roles govern lifecycle
filters; library callers can supply BriefOptions.statusRegistry from
resolveRuntimeStatusRegistry((await readSettings(pmRoot)).schema). Omitted
registries use SDK core defaults plus the legacy terminal done spelling, so
custom terminal names need their registry. An explicitly configured done role
takes precedence over that compatibility fallback. Native disclosure reads the
workspace registry too. See relationship readiness
for the graph-shape matrix.
Agent Brief Contents
- workspace and item counts
- top next items with
whyNowreasons - evidence-weighted next-work score, confidence, and ranking reasons
- blocker relationships and dependency context
- stale context findings
- recent activity from
pm activity(when--include-historyis set) - decision items that need human or agent follow-up
- brief insights with actionable command hints when filters or focus ids need attention
- safe suggested pm commands, never auto-applied
- deterministic token-budget trimming
Agent Handoff Prompt
pm brief prompt turns the same structured brief into direct next-turn
instructions for coding agents: ranked work, focus context, blockers, risks,
safe pm commands, and working rules. It is designed for handoffs where the next
agent needs executable context rather than a full project dump.
Delta briefs (pm brief since)
pm brief since <checkpoint> produces a token-budgeted delta brief: a
categorized summary of what changed since a checkpoint, instead of a full
project re-read. It is purpose-built for the multi-agent merge workflow — when
an agent resumes a project after other agents worked in parallel branches and
merged, it should orient on what changed, not re-read everything. Project
management is context management, and a delta brief is the smallest context
that captures the difference.
Checkpoint formats (lower bound, passed to pm activity --from):
- Relative windows treated as "ago":
7d,12h,30m,2w. A leading-is optional —pm brief since 7dandpm brief since -7dare equivalent (pm activity --fromaccepts bare windows directly since pm-cli 2026.7.29). - ISO timestamps / dates:
2026-07-20,2026-07-20T00:00:00Z.
Filters:
--until <checkpoint>— upper bound (passed topm activity --to).--author <name>— only include changes by this author.--limit <n>— max activity entries to scan (default 1000, clamped 1–5000).
Budget & shape:
--max-items <n>(default 40) and--token-budget/--max-tokens(default- trim the lowest-ranked changes to fit;
truncated/omittedItemsare reported in JSON and markdown when trimming occurs.
- trim the lowest-ranked changes to fit;
--format markdown(default),text,json, orslack;--output <file>writes the result to a file.
Categories (each a section, deterministic order, most decision-relevant first): Created · Closed · Canceled · Reopened · Status changes (incl. started, newly blocked, unblocked labels) · Reprioritized · Dependencies · Discussion (notes + comments) · Other. Items are ranked by change kind, then current priority, then recency, then id for a fully deterministic order.
Use case — multi-agent merge re-orientation: after merging parallel
branches that touched the pm tracker, run pm brief since <last-sync> to get a
precise list of created / closed / reprioritized / re-blocked items and new
notes, so the resuming agent can update its plan without re-reading the whole
workspace. The brief ends with a ## Refresh block showing the exact command
that reproduces it.
Pre-merge collision preview (pm brief diverge)
pm brief since is the post-merge half of the multi-agent workflow. pm brief diverge [base] is the pre-merge half: it answers "which pm items did both
branches touch, and will the field-aware merge driver resolve them cleanly?"
before you run git merge — rather than after, when pm merge reconcile is
repairing the damage.
pm brief diverge # HEAD vs origin/HEAD (else main, else master)
pm brief diverge main # explicit base
pm brief diverge --base main --head feat/other # neither side has to be checked out
pm brief diverge --base main --format json # machine-readable for agents
pm brief diverge --base main --include-clean # also list one-sided items individually
It reads each item's append-only history ledger at the merge base, the base tip and the head tip, diffs the JSON-Patch paths each side added, and classifies every item:
| Classification | Severity | Meaning |
|---|---|---|
duplicate-id |
high | Both branches minted the same item id after the merge base. The driver cannot fix this — one side must be re-keyed. |
delete-vs-edit |
high | One side deleted the item while the other edited it. |
field-collision |
medium | Both sides wrote the same field. The driver resolves it via --prefer, so review the losing value. |
union-safe |
low | Both sides touched the item but disjoint fields — the driver merges both and unions the history. |
head-only / base-only |
— | Only one side touched it; nothing to resolve. |
/metadata/updated_at is treated as benign: it changes on every write and never
produces a collision verdict on its own.
The verdict is clean (no item touched by both sides), union-safe (all
both-sided items have disjoint fields), or review-required (any high/medium
finding).
Merge fence check. The report also verifies the field-aware driver is
actually installed — the .gitattributes entries and the merge.*.driver git
config. Without both, even union-safe items hard-conflict, so a missing fence
is reported as a warning with pm merge install as the first recommended step.
Unrelated histories. When the refs share no merge base, unrelatedHistories
is true in the JSON (an explicit boolean, because JSON.stringify drops the
undefined ancestorSha), every changed item is reported one-sided, and the
recommended merge command becomes git merge --allow-unrelated-histories <base>
— the bare form git refuses outright.
Each report ends with an ordered Recommended next steps block containing only
the commands that apply, including pm history <id> --verify per high-severity
item and a pm brief since <merge-base-date> for post-merge re-orientation.
Default governance scan cost
pm brief and pm brief prompt run the shared SDK governance scanners by
default: duplicate clusters, stale in-progress history, storage integrity, and
credential-shaped item text. The independent asynchronous scans run
concurrently and fail independently, so an advisory scanner cannot suppress the
core brief. Use --no-governance for latency-sensitive calls that do not need
these signals.
Measured on a real 481-item tracker with pm CLI 2026.7.26, loading item bodies and collecting the complete governance summary took 1.48 seconds with 137 MB peak RSS. The report remains bounded to 3/5/5/5 findings before the normal token-budget compaction tightens it further. Runtime depends on tracker size and history volume; use the governance-free flag when sub-second context is more important than the audit signals.
Post-merge duplicate sweep (pm brief duplicates)
pm brief diverge closes the pre-merge gap. There is still one hole it cannot
reach: two agents who each create a new item on their own branch both land
cleanly on main — the field-aware merge driver is deliberately conflict-free
for that — and neither create-time duplicate advisory can see the other, because
the other item does not exist in that branch's tracker yet. main now holds two
items for one problem and nothing ever flags it.
pm brief duplicates is the post-merge sweep that closes that loop. It enumerates
the merged tracker, asks the shared SDK findSimilarItems primitive (the same
one pm create advisory mode uses, so the two agree exactly) for near-duplicate
matches per candidate, and collapses bidirectional matches into unordered pairs
ranked by score.
pm brief duplicates # default threshold 0.6, all statuses
pm brief duplicates --threshold 0.5 # looser match cutoff (0..1 inclusive)
pm brief duplicates --since 2026-07-25 # post-merge mode: only items created at/after this merge
pm brief duplicates --status open,closed --limit 5 # filter candidates, cap pairs reported
pm brief duplicates --format json # bare object, no envelope
For each pair it emits both ids, titles, statuses, types, the score rounded to three decimals, the SDK match reason, and an advisory remediation command (never executed by this command):
- when exactly one of the pair is closed, it suggests linking the open item to
the closed one:
pm update <open-id> --dep id=<closed-id>,kind=related; - when both are open (or both closed), it keeps the older item by
created_atas canonical and relates the newer to it.
A clean tracker is a success, not an error — the command exits 0 with an explicit
No likely duplicate items found line.
Choosing a threshold. This matters more than it looks: the default 0.6 is
deliberately conservative, and real cross-agent paraphrases often score below
it. The worked example below scores 0.5 — two agents describing the same flaky
test — so a default-threshold sweep would not report it. title_token_jaccard
compares token sets, so paraphrases that agree on the problem but not the wording
score lower than an intuition calibrated on "these are obviously the same bug"
would suggest.
Practical guidance:
--threshold 0.4for a genuine post-merge sweep, where a handful of false pairs to eyeball costs far less than a duplicate that survives onmain.0.6(default) when you want only high-confidence pairs, e.g. in automation that acts without a human reading the output.- Exact-title collisions surface as
reason: exact_titleand score1.0, so any threshold catches them; the tuning only affects paraphrases.
For comparison, create-time advisory mode uses governance.duplicate_detection_threshold,
which defaults to 0.8 — this sweep is already the more sensitive of the two.
Cost, and why --since is the mode you usually want. Scoring runs through the
shared SDK primitive per candidate, so an unscoped sweep is inherently
candidates x tracker-scan. Measured on a real 1,934-item tracker (pm-cli's own):
| invocation | candidates | elapsed | peak RSS |
|---|---|---|---|
pm brief duplicates (full sweep) |
1,934 | 36.3s | 268 MB |
pm brief duplicates --since <merge-date> |
42 | 2.7s | 271 MB |
Scans run with bounded concurrency (8 in flight). That was measured rather than
assumed, and the honest result is a modest wall-clock gain — 39.1s to 36.3s,
about 7% — because findSimilarItems is CPU-bound, and concurrency cannot
parallelize CPU work on a single-threaded event loop. What it does buy is peak
memory: 526 MB down to 268 MB, because results are collapsed as they arrive
instead of N full result sets accumulating.
The full sweep is a whole-tracker audit — fine to run occasionally, but it is not
what this command is for. --since <merge-timestamp> is the post-merge mode
and the one that keeps the cost proportional to what the merge actually
introduced. Trackers of a few hundred items complete quickly either way.
The cost is not avoidable from outside the SDK today: findSimilarItems is a
per-candidate query with no batch entry point, and the exported
scoreItemSimilarity re-tokenizes both titles on every call, so precomputing
tokens and pre-filtering on jaccardSimilarity would silently drop issue_code
matches (two titles sharing an issue code score ~0.99 while their token overlap
can sit well below any useful threshold). Reimplementing that signal locally would
break the exact agreement with create-time advisory that this command depends on,
so it is deliberately not done. Filed upstream as
pm-cli#709.
Example output on a tracker where two agents each filed the same flaky test from different branches:
$ pm brief duplicates --threshold 0.3
pm brief duplicates — 1 likely duplicate pair(s) (threshold 0.3, scanned 3)
pm-p800|pm-plbh score 0.5 title_token_jaccard
pm-p800: flaky auth test fails intermittently (Task, open)
pm-plbh: Fix flaky auth test (Task, open)
→ pm update pm-p800 --dep id=pm-plbh,kind=related
TypeScript API
import { buildBrief, renderMarkdownBrief } from "pm-brief";
const brief = buildBrief(items, {
tokenBudget: 4000,
focusIds: ["pm-1234"],
generatedAt: "2026-06-06T00:00:00Z",
});
console.log(renderMarkdownBrief(brief));
Complete-corpus safety
Every brief command starts from the canonical all-status reader:
pm --pm-path <tracker> list --all --json --include-body --strict-read --no-truncate \
--output-budget unbounded --output-limit unbounded --output-include full
pm-brief passes that envelope through the public pm SDK complete-list certifier, then rejects any missing or contradictory source, omission, projection, pagination, universal-output, count, or item-identity receipt. It never follows a partial cursor or falls back to a bare row array. The narrow checks supplementing SDK 2026.8.21 are tracked upstream in pm-cli#1078.
Release Readiness
This package uses TypeScript, pm-changelog, and the same daily release shape
as the other public pm packages. npm run release:check runs typecheck, build,
tests, docstring coverage, production audit, dry-run packing, installed-package
acceptance, and changelog validation. The packed acceptance matrix loads real
tracker data through npm/npx and Bun/bunx on the current host, plus npm/npx on
the declared minimum host. Changelog and release-note reads let pm-changelog
resolve the installed pm CLI entry through Node (portable to Windows) and
explicitly disable both host output bounds. Toolchain development pins come from
package.json; tests require exact versions and matching lockfile root entries,
locked resolutions, and installed package metadata. Routine toolchain updates
need only the manifest and lockfile changes. Consumer host requirements remain
minimum floors, separate from the development pins.
Multi-agent merge safety
This repo tracks its project management in .agents/pm/ and ships a committed .gitattributes
that maps those tracker artifacts to pm-cli's field-aware Git merge drivers, so concurrent-branch
tracker edits merge cleanly instead of hard-conflicting. The driver definitions live in
per-clone Git config; npm install / npm ci wires them automatically via the prepare script, scripts/prepare-merge-driver.ts: a launcher that delegates to the installed pm-ops installer. Behavioral tests enforce driver registration, omit-dev and missing-CLI skips, and failure propagation without requiring template equality. The installer calls pm merge install when the pm CLI is on PATH and skips with a notice when it is not. A production install of a clone (npm ci --omit=dev) has no pm-ops, so the launcher skips with one notice, while a stale or broken pm-ops fails the install. Registry installs of this package never run prepare. Being Node-based, it behaves identically on POSIX shells and Windows cmd.exe. To (re)run manually: npm run merge:install.
After merging a branch that touched .agents/pm/, reconcile any residual history-hash drift with
pm merge reconcile (pm-cli ≥ 2026.7.22): preview with pm merge reconcile --dry-run, apply with
pm merge reconcile --message "post-merge reconcile", then confirm with pm validate, which scans the
whole tracker and flags remaining history drift across every affected item (pm merge reconcile
itself lists each affected stream in its output; pm history --verify <id> spot-checks one item). The field-aware driver already unions every author's
content, so reconcile only re-greens the hash chain (no data loss) — see the authoritative
pm-cli merge-safety guide. The
older blunt pm history-repair --all remains available as a lower-level primitive.
Universal read output controls
Requires pm CLI/SDK 2026.10.13 or later. Read commands accept the host's
--output-include, --output-limit, --output-budget, --output-format,
--output-session, --output-cursor, and --output-row-contract controls.
Use pm contracts --command "brief next" --full to discover the row paths.
Ordinary document and diagram formats retain their existing rendering. When
universal controls are supplied, handlers return structured results so the host
owns projection, omission receipts, budgets, and continuation. --output file
writing cannot be combined with these controls. Writers remain undeclared.
An infeasible budget returns output_budget_exceeded and CLI exit 2; increase
the budget before using the result. Budget-compacted declared rows include
per-path continuation cursors: repeat the same semantic inputs with
--output-cursor, keeping the tracker unchanged, until no cursor remains.
--output-limit is an amount ceiling; cursor recovery applies to budget
compaction. Command-local selection limits and document budgets still apply.
Adoption recipe
- Pin the development SDK to 2026.10.13 and align the peer and manifest floor.
- Declare
read_output: { row_keys: [...] }on readers only; use actual, non-overlapping array or object-map paths, or[]for a singleton. - Return a structured object. Keep raw document rendering on its ordinary path, declining the output service when canonical controls are requested. Prevent file writes and unbounded diagram stdout on that path.
- Test the packed extension through its installed host against real temporary trackers: projection, budget omission, every-row cursor recovery, and writer rejection. Remove only the declarations, rebuild, and prove the tests fail.
- Run unchanged npm and Bun release gates and document their exact results.
Acknowledged handoff contracts
pm brief handoff records what each receiving agent understood against exact
item content revisions. A packet includes explicit questions, unresolved
assumptions, required evidence and the transitive declared blocker closure.
A changed blocker, source item or evidence item invalidates its old receipts.
Two receivers append independent receipts to the same tracker; identical retries
by the same author add nothing. Claims continue to own work ownership.
# Set an explicit identity for each sender or receiver.
export PM_AUTHOR=sender
# Replace the focus/evidence item ids in docs/examples/handoff.json.
pm brief handoff preview --input docs/examples/handoff.json --json
pm brief handoff create --input docs/examples/handoff.json --json
# Save the returned id and digest. Then write response.json, as shown below.
PM_AUTHOR=receiver-a pm brief handoff ack <packet-id> --input response.json --json
pm brief handoff show <packet-id> --receiver receiver-a
A receiver supplies every obligation id, including evidence that is still missing:
{
"digest": "<64-character digest from the packet>",
"questions": { "fixture": "I will use the public tracker fixture." },
"assumptions": { "review": "Unresolved; I will verify the blocker before proceeding." },
"evidence": { "report": "The report link is missing; validation remains outstanding." }
}
Every collection is explicit; use [] in contracts and {} in responses when
there are no obligations. Each input collection has at most 32 entries; the
blocker/evidence closure has at most 64 items. Keys use ASCII letters, digits,
dots, underscores or hyphens and start with a letter or digit. Obligation and actor
keys have at most 128 characters; native item identifiers retain their full length. Obligation text and evidence references have at most 2,000 code units.
Native source titles and blocker references retain their full text under the aggregate budget.
Native TOON is the default; --json uses the host's
structured JSON output. The default conservative output ceiling is 4,000 tokens,
measured from UTF-8 bytes with twice pretty-JSON size plus framing headroom.
--token-budget accepts up to 64,000. Oversized output refuses with a remedy;
it never silently drops questions, assumptions, evidence or receiver receipts.
A receiver filter reduces the selected receipt set without changing storage.
Universal host output controls can intentionally project or paginate read commands;
retain their omission receipts and use normal output for a complete contract.
Evidence requirements select exact native docs, files or tests links by
item, kind and ref (path, or test command). missingEvidence remains visible
in the packet and each receipt. Linkage proves a reference was recorded; it does
not prove that a file exists, its contents are correct, or a test passed. Receiver
statements are self-reported understanding, including disagreement or uncertainty.
valid means the source revisions still match; ready additionally means all
required links are present. Neither field grants approval or claims the work.
Packets are native Decision items tagged pm-brief-handoff; their JSON bodies are
immutable protocol artifacts. Receipts are append-only native comments protected
by the host's comment lock and author/text idempotency. Native lock contention
is retried up to three times for reads and idempotent appends; creates are never
automatically retried. Persistent contention refuses with a retry remedy. Do not edit contract bodies
or receipt comments. Use repair/renew after source revisions change; create a fresh packet when the contract obligations change. A receipt
racing a source edit is reassessed on return and every subsequent read. All source
metadata, bodies and linked evidence contribute to content revisions, so even an
unrelated source comment conservatively invalidates old understanding. Reads are
certified full-corpus snapshots, not a transaction spanning arbitrary source writers.
The SDK exports previewHandoff, createHandoff, acknowledgeHandoff,
inspectHandoff, their typed contracts, and HandoffError (code, remedy).
Pass { pmRoot, author, tokenBudget? }, optionally with the active native PmClient.
CLI expected refusals exit 2 (3 for a stale packet), with a stable handoff_<code>
JSON category and a required remedy.
Categories cover invalid input, identity, missing packets/items, response coverage,
integrity, budget and tracker reads. Validation and budget checks precede writes.
The commands never change source items or claims; preview and show are read-only.
This complements pm-context's
context delivery/usage receipts and the since-delta candidate
(feat/since-delta-pack)
(pm context-pack since --from <timestamp> or --cursor <cursor>). That candidate
owns cursor progress, changed-context selection and omission receipts. Use its delta to
refresh changed context, then repair existing receiver understanding here. No history delta engine
is duplicated. pm-memory continues to own standing knowledge; brief prompt
continues to generate prose. See the contract example.
Handoff repair deltas
brief handoff repair <packet-id> --receiver <agent> returns only groups whose
sources changed since that receiver's last acknowledgment. --receiver defaults
to the active author. Each focus item is one obligation group, scoped to its
transitive declared blockers. Evidence requirements belong to each focus closure
that contains their item; evidence outside every focus closure gets its own group.
The existing contract has no per-question scope, so questions and assumptions
conservatively apply to every focus group. A shared blocker can affect several
groups. A 50-row packet with 25 independent focus/dependency pairs returns one
group when one dependency changes, preserving the other 24 acknowledgments.
PM_AUTHOR=receiver-a pm brief handoff repair <packet-id> --json
PM_AUTHOR=receiver-a pm brief handoff renew <packet-id> --input renewal.json --json
The delta includes changed source coordinates, current obligation text, affected
native evidence references, missing-link disclosure and exact group targets.
Unchanged obligations are represented by validAcknowledgments and totalGroups.
An empty groups array means that receiver has no pending repairs. Evidence link
presence and receiver understanding retain their separate meanings from show.
Legacy show continues to assess the original whole-packet acknowledgment;
repair assesses the receiver's current understanding at group granularity.
Renewal JSON supplies the immutable contract digest and a nonempty groups
array: each row has id, target, questions, assumptions and evidence maps.
Answer exactly that group's obligation keys. Partial group selection is supported.
Targets bind the contract and SDK-read source revisions, including native update
coordinates, so replaying older content does not renew a newer revision. Unchanged
groups need no response. Matching retries and a peer's completed renewal return
current validity without adding a duplicate receipt. Renewals append native comments
to the existing contract; no contract copies, sources, claims or separate stores
are written. Foreign or malformed receipt-shaped comments are skipped with an
indexed reason by repair/renew and never edited or deleted.
Each new acknowledgment stores dependencies, an array of per-group snapshots:
{ id, items, edges, evidenceLinks }. Renewals store items, edges and
evidenceLinks alongside that group's responses and revision target. items
records every covered item id, full SDK-row revision (or null for a missing item),
status, title and blocker edges. Native edge coordinates (from, to, kind)
and native evidence-link coordinates (item, kind, optional ref) each carry
SHA-256 revisions of their complete SDK-read metadata. These inventories come
from the same certified source read that validates the acknowledgment or renewal.
Repair compares the recorded dependency set plus newly reachable dependencies; it never drops an already covered item because today's blocker edge disappeared. Coverage identities persist across renewals, including transitive dependencies, missing items and evidence outside the current focus closure. For example, acknowledge A blocked by B with required evidence on B, remove A's blocker edge, and renew A: later edits, link removal or deletion of B still invalidate A's understanding. A new blocker can add further evidence obligations to a group. Known missing evidence remains distinct from readiness, as in the original handoff contract. The 64-item coverage limit and token refusal still apply; create a new focused contract when retained coverage outgrows that limit.
Older acknowledgments without explicit inventories remain readable using the
immutable contract's item snapshots. Older renewal snapshots are conservatively
combined with prior coverage so they cannot erase previously covered identities.
New acknowledgments and renewals append the full inventory; stored history is
never rewritten to manufacture historical metadata. See the recorded-coverage
regression and independent graph model in test/handoff-coverage.test.ts.
The SDK exports inspectHandoffRepair, renewHandoff, parseHandoffRenewal and
HandoffRepair, HandoffRepairGroup, HandoffRepairChange, HandoffRenewal,
HandoffRenewalGroup, HandoffDependencySet, HandoffAcknowledgedGroup,
HandoffCoveredEdge and HandoffCoveredEvidence. Use the existing HandoffOptions with pmRoot, author,
optional native client and tokenBudget. Output uses native TOON/JSON and the
same 4,000-token default ceiling (maximum 64,000). Oversized deltas refuse rather
than discard obligations. An unacknowledged receiver gets handoff_unacknowledged
and must first acknowledge a fresh contract. Other typed refusals retain the
existing HandoffError codes and actionable remedies; stale renewal exits 3.
Renewal holds an SDK token-owned lease from its initial read through append and
verification, and uses native comments({ ifAbsent: true }) for atomic append.
The SDK releases only its owned lease, last. Arbitrary source writers do not join
this lease: sources are rechecked after append, and a raced renewal refuses stale;
its historical comment cannot validate the newer revision. A later edit after the
final verified read appears on the next repair read. See the
complete example and
validation evidence.
Decision follow-through briefs
pm brief observation measurement-id --json
pm brief follow-through decision-id
pm brief follow-through decision-id --json --token-budget 1500
Follow declared consequences after a choice has been explicitly adopted. Store a
version 1 brief_follow_through object on a native Decision, using native
pm update --field brief_follow_through='<json>' (or the SDK custom-field
options). The extension registers this optional field. The object contains an
explicit adopted boolean and 1–128 outcomes, each with a unique id, expected
text, observations and actions. See the complete example.
An observation is an explicit {item, revision, verdict} judgement: observed
or contradicted. Read its revision with brief observation, review that item's
native actual_result, then record the judgement on the associated outcome.
Revisions hash the complete SDK-read native item and its verified append-only
history; an edit, relationship change or replay invalidates earlier judgements,
including an edit that restores old text. Empty or missing observations provide
no support. The brief never interprets prose, opens linked files or runs tests.
Every outcome retains its own unsupported, observed, contradicted or
conflicted state. Conflicted means current observations contain both explicit
verdicts; neither disappears. A changed observation invalidates only outcomes
citing it. Unrelated outcomes retain their evidence. unsupported identifies
what remains untested by current observations. Action states use the tracker's
configured SDK status roles (pending, blocked, done, canceled, terminal
or missing), and completing work never supplies an outcome judgement. Closing
a Decision does not adopt it. Malformed outcome records are isolated with an
invalid_outcome remedy while other outcomes remain readable.
The default 1500-token ceiling (integer 1–64000) preserves every outcome id and
state. Optional expected text, observations and actions are included as whole
detail groups. Each row's omitted counts absent details: one for expected text,
one per observation and one per action. restore reads the original declaration;
use native pm get <observation-id> --full for source measurements. The SDK UTF-8
estimator covers twice the complete pretty JSON size plus 256 framing bytes,
including the estimate and omissions. It bounds normal native JSON/TOON, not a
model tokenizer or extra host accounting receipts. For larger briefs also use
--output-budget unbounded --output-limit unbounded. If mandatory summaries
cannot fit, the command refuses instead of silently removing outcomes.
import {
buildDecisionFollowThroughBrief, readFollowThroughObservation,
parseFollowThroughDeclaration, FollowThroughError,
} from "pm-brief";
const options = { pmRoot: ".agents/pm" }; // Pass the host-bound client when available.
const source = await readFollowThroughObservation("measurement-id", options);
const declaration = parseFollowThroughDeclaration({
version: 1, adopted: true,
outcomes: [{ id: "latency", expected: "Lower latency", observations: [
{ ...source, verdict: "observed" },
], actions: ["next-experiment-id"] }],
});
// Persist declaration with native pm mutation APIs, then read current state:
try {
const brief = await buildDecisionFollowThroughBrief({ decision: "decision-id" }, options);
console.log(brief.outcomes);
} catch (error) {
if (error instanceof FollowThroughError) console.error(error.code, error.remedy);
else throw error;
}
parseFollowThroughDeclaration is the strict producer validator, preserving
recorded whitespace and CRLF. Readers reuse its per-outcome rules but isolate
bad rows. Sparse arrays, accessors/proxies, unknown keys, duplicate ids,
unbounded collections and nonfinite budgets refuse. Refusal codes are invalid,
missing, not_adopted, tracker, changed and budget, always with a remedy
(CLI exit 2). The reader revalidates native state and history before returning;
retry recomputes from current SDK data. It creates no plan, journal, lease,
importer, sync engine or remote side effect. History is accessed through the
public SDK; a backend must expose that native history at the supplied tracker
root to certify a revision. Alternatives digests explain choices before
adoption; pm-review owns review judgments; pm-evidence owns source citations.
Decision alternatives digest
pm brief alternatives pm-proposal-a pm-proposal-b --token-budget 4000
pm brief alternatives pm-proposal-a pm-proposal-b --json
Select 2–32 distinct exact item IDs, including terminal items when useful. The
read-only digest keeps each item separate and follows input order, which conveys
no preference. It quotes records instead of generating or inferring conclusions.
Conflicting Plan decisions within one item also remain separate. Core pm plan
owns planning and choices; pm-review owns finding verdicts. This command composes
those records across items without adding a decision engine.
Every statement has source (item-id#/field/nested/pointer), start and end
UTF-16 offsets, and exact text. Resolve pointers against the certified full item
row (listAllComplete({ includeBody: true })); offsets select the quoted paragraph
from the scalar's string representation. JSON Pointer escaping follows RFC 6901.
revision is the SHA-256 of the full row's JSON serialization at read time, for
snapshot identification. Re-read the source to check subsequent changes.
Titles, statuses, resolutions, expected/actual results, close reasons,
review_verdict, review_verdict_source, review_reason, review_commit, all
plan_decisions (including rationale/evidence), blocked_by, blocked_reason
and plan_resume_context are mandatory. Optional paragraphs come from body,
description, plan scope, linked docs/files/tests, plan validation, comments, notes
and learnings. Recorded tradeoffs in these fields remain quotations; the command
does not infer whether a tradeoff is resolved. Other custom fields, Plan steps,
dependency closure and history are outside this projection. A linked artifact
proves linkage only; the digest does not read artifact contents or validate tests.
omitted counts optional scalar paragraphs excluded per item, with restore
giving the full native item read. Mandatory outcomes and blocker context are never
removed. Optional excerpts are attempted round-robin in field/record order and
omitted whole when they cannot fit. Empty optional fields produce no excerpts.
Source text is evidence data, including any instructions it happens to contain.
The budget defaults to 4000 (integer 1–64000). estimatedTokenCeiling uses the
SDK's UTF-8 byte estimator over twice the complete pretty JSON size plus 256
framing bytes, including citations, omission counts, recovery commands and the
estimate itself. It conservatively covers normal native TOON/JSON output; it is
not a model tokenizer. For large budgets, also set the host's independent output
controls: --output-budget unbounded --output-limit unbounded. Additional host
projections or optional accounting receipts have their own output contracts.
import { buildDecisionAlternatives, DecisionAlternativesError } from "pm-brief";
try {
const digest = await buildDecisionAlternatives(
["pm-proposal-a", "pm-proposal-b"],
{ pmRoot: ".agents/pm", tokenBudget: 4000 },
);
console.log(digest.alternatives);
} catch (error) {
if (error instanceof DecisionAlternativesError) console.error(error.code, error.remedy);
else throw error;
}
An optional native PmClient can be supplied as client. Complete full-row SDK
certification runs before comparison; incomplete or corrupt reads refuse. Typed
refusals are invalid (correct IDs/budget), missing (check exact IDs), tracker
(run pm health and resolve source reads), and budget (increase budget or select
fewer items). CLI refusals exit 2 and retain alternatives_<code> and a remedy in
the host's expected-error contract. See the worked example.
Stakeholder disclosure profiles
Render a brief for an explicit audience using native compact TOON (default) or JSON. This is a read-only operation and does not require git:
pm brief disclose --audience public
pm --json brief disclose --audience external --dry-run
pm --json brief disclose --policy disclosure.json --token-budget 8000
| Audience | Permitted brief fields |
|---|---|
public |
Generation time, item count, and ranked item IDs, titles, types, statuses and priorities |
external |
Public fields plus ranking explanations, tags, dependencies, blockers, risks and staleness |
internal |
External fields plus workspace metadata, assignees, required context, activity, recommendations, insights and source budget metadata |
All profiles exclude unmodeled fields, including custom fields, momentum, governance and merge receipts. Recognized credential-shaped values are removed using the core SDK guard. The tracker path applies this guard before ranking and compaction so shortening cannot conceal a recognized secret. This reuses core detection; it is not a universal credential scanner or a guarantee that arbitrary prose contains no confidential information. Review permitted titles and labels before sharing them.
Policies can only narrow a profile. For example, disclosure.json:
{"profile":"public","exclude":["focus.*.title","next.*.title","decisionsNeeded.*.title"]}
Only profile and exclude are accepted. Unknown keys, audiences or field
selectors refuse before reading the tracker. DisclosureField in the SDK
enumerates supported selectors; * denotes an array index. There is no override
for adding custom fields or bypassing the credential guard.
Every result contains an exclusion manifest grouped by reason (profile,
policy, secret, unknown_field, invalid_value). Each entry maps a
JSON Pointer path to an occurrence count. Profile/policy paths aggregate array
indices as *; secret paths identify individual rendered occurrences. Unknown
field names are replaced by @N, their zero-based position in their parent
object's property enumeration. Pre-compaction secret exclusions use
/source/N/@M for record/field positions; /source/N means a secret-bearing
identity caused the whole record to be withheld. These paths describe the
snapshot and never contain field values, item identifiers or unknown key text.
Dry runs return the same manifest without any brief values.
import { buildBrief, renderDisclosureBrief, buildDisclosureBrief, DisclosureError } from "pm-brief";
const brief = buildBrief(items); // One source; multiple independent renderings.
const publicView = renderDisclosureBrief(brief, { profile: "public" });
const reviewPreview = renderDisclosureBrief(brief, { profile: "external" }, { dryRun: true });
// Or read a complete, certified native snapshot using an optional host PmClient:
const view = await buildDisclosureBrief({ profile: "public" }, { pmRoot: ".agents/pm" });
The native tracker path reuses the existing 4000-token source brief selection;
sourceTruncated discloses source compaction. The default disclosure ceiling is
8000 estimated tokens, covering native JSON/TOON framing and every exclusion.
Use renderDisclosureBrief with your own buildBrief controls for a different
source selection. Rendering never silently drops manifest entries to fit.
DisclosureError provides code and remedy: policy (correct the policy),
invalid (correct controls/input), tracker (resolve the incomplete/unreadable
source), or budget (increase the ceiling or supply a smaller brief). CLI
refusals exit 2 with disclosure_<code> and a remedy, without source text or
nested error details. See the worked example and public-tracker replay.
Action prerequisite briefs
Ask for one intended native action and one exact item before attempting it:
pm brief action close project-123
pm --json brief action close project-123 --request '{"input":{"reason":"Verified delivery"},"requiredEvidence":[{"kind":"docs","ref":"docs/results.md"}]}'
pm brief action start-task project-123 --request '{"actor":"delivery-agent"}'
One call combines native command availability, SDK readiness and blocker
resolution, close evidence fields (resolution, expected_result,
actual_result), reason governance, workflow rules and explicitly declared
native evidence links. Satisfied fields are JSON Pointer references; their
bodies are never repeated. Link presence records linkage, not successful tests
or artifact verification. This composes core admission facts; pm-lint remains
the item quality diagnostic, and this command never chooses work.
Readiness is scoped to the action: explicit starting composes native claim and
update admission, including SDK-resolved type command_option_policies.
Dispatch roles, unfinished children and nonterminal status classifications do
not veto an explicit start. Closing uses native terminal-state and direct-child
facts. A decision, outcome gate, draft or blocked status alone does not prevent
an evidenced close. A claim principal without an assignee is historical metadata,
not live ownership; both actions check current assignee state.
Policy entries expose the SDK's effective effect. Advisory and warning findings
have blocking: false: their missing/unresolved states stay visible while
ready remains true if all mandatory prerequisites are satisfied. An omitted
blocking means the entry is mandatory. Workspace refusal requires both a
refusing rule and workspace enforcement; warning-only type transitions never
become required inputs. Type workflows govern the native start update, not a
direct close. Close evidence fields and unfinished children follow the native
close_validation_default: strict gates them, while warn/off retain non-blocking
diagnostics. Explicitly requested evidence and mandatory close reasons still gate.
Required update options are invocation inputs, even when the corresponding item
field already has a value. Start supplies its native status automatically;
actor supplies the author option. Other required start flags appear as missing
option:<key> entries with typed unsupported refusals and native remedies,
because this request does not accept arbitrary mutation options. Disabled
generated options and inconsistent policies also prevent readiness. Satisfied
generated status references use native:start-task#/update/status.
The typed SDK entry point is buildActionPrerequisiteBrief(request, { pmRoot, client }). Pass the host's injected client when embedding. A standalone caller
can omit it to construct a tracker-bound public SDK client lazily after request
validation. No executable-path or symlink discovery is involved.
parseActionPrerequisiteRequest applies ACTION_PREREQUISITE_SCHEMA to both
SDK and CLI requests. Unknown keys, sparse arrays, nonfinite/fractional budgets,
duplicate evidence declarations and oversized input refuse before tracker IO.
Records must be plain JSON data: proxies, accessors and inherited/sparse array
entries are rejected without executing caller-provided callbacks.
requiredEvidence accepts at most 64 exact {kind, ref} pairs for docs,
files and tests; strings are bounded (reference 1024, input field 16000,
actor 128 characters). The complete TOON/JSON estimate defaults to 4000 tokens
and accepts integer ceilings 1..64000. Requirements are never silently truncated.
The result has preview: true, ready, checklist and typed refusals.
Close and start-task previews are supported. Other commands, approval-history
certification, matching workspace field/writer policies and composed claim-stage
policies return unsupported when their effective response can refuse the action,
with a remedy alongside known facts. Advisory certification gaps remain
non-blocking checklist entries. Policy
field references describe current/proposed input availability; native actions
can generate or clear other metadata, so those references never certify the
complete post-action record. Use native semantic validation for those policies.
Operational failures throw ActionPrerequisiteError with code and remedy:
invalid, missing, tracker, changed or budget. CLI usage failures exit 2
with action_<code> (missing positionals use the native argument refusal); a successfully produced preview exits 0 even when its
checklist is incomplete. Inspect ready and refusals before continuing.
No native mutation is invoked: items, histories, claims, policies and session focus are preserved. Native reads can initialize rebuildable runtime metadata caches on a writable cold tracker. On a physically read-only tracker, the same preview works without changing any bytes. Two certified SDK reads reject observed concurrent changes; this is an observation, never a lease or a plan to replay. Native actions must revalidate current state, actor permissions and execution-time hooks themselves. There is no new import, export, sync, journal, checkpoint, idempotency-key or shared writer path.
See the worked example and validation evidence.
Without git
Briefs, prompts, ranking, history deltas, governance, duplicate checks and all
handoff, decision alternatives, decision follow-through, observation revision,
stakeholder disclosure and action prerequisite commands work in plain initialized
trackers without a git executable.
Handoff storage uses the native SDK dispatcher and can reuse a host client.
brief diverge needs a git repository and git refs. Handoff receipts have no
branch, commit or repository dependency.
From the package's README on GitHub. View on GitHub