Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ This project follows [Common Changelog](https://common-changelog.org/) and [Sema

- Write tracker issues and comments for people. Issues open with one to three plain sentences on where they came from, the problem and why it matters, followed by short `###` sections for Definition of Done, Out of Scope, Verification and Risks, leaving out empty ones. Detail only agents need moves into collapsed agent notes below a divider, with fixed labels and no status, dates, assignees or Loaf naming. Comments and Ship's review lead with one to three plain lines. Records link issues natively, write exact moments in UTC, point only to places everyone with tracker access can reach, and keep magic words in PR descriptions and commit messages. The GitHub and Linear skills describe each tracker's syntax, and migrated records keep their source marker in the agent notes instead of a visible line ([#391](https://github.com/levifig/loaf/issues/391)).
- Ship confirms every Verification box on an issue is ticked before marking it Done or merging a PR that closes it, and does so before the merge because merging can close linked issues automatically; the merge hooks check the same boxes. An open box blocks closing and merging unless a reason from a person with authority over the work is written on it; a reason can defer only a check that cannot run before the merge or that only a person can do, and Ship reports that criterion as deferred rather than proven. Whoever ran a check ticks its box, an agent only after the check ran and its result is recorded; checks only a person can do stay for that person, and Implement hands off with the boxes it ran already ticked. Implement and Ship now declare that bounded body write in the Flow contract ([#392](https://github.com/levifig/loaf/issues/392)).
- Keep the continuity digest small enough for harnesses to inject it whole. Claude Code saves hook context over 10,000 characters to a file and injects only a 2,000-character preview, and Codex does the same past about 2,500 tokens, so a busy journal reached agents as a preview of one old wrap. The default `loaf journal context`, every SessionStart hook and post-compaction resumption now print a compact digest. Each entry is one line with its date, id and a shortened message, and `loaf journal show <id>` prints it in full. Pagination cursors and empty layers are gone, repeated diagnostics collapse to one line per code, and a layer showing fewer items than it has names a command that lists them. The digest stays under 8,000 bytes; when that is tight, every layer keeps its first items in priority order before any layer gets more, and the digest says how many entries it left out. Each target's injection limit, its source and the date it was checked are recorded in `config/target-capabilities.json`. `--json` and single-`--layer` output are unchanged ([#345](https://github.com/levifig/loaf/issues/345)).

### Fixed

Expand Down
40 changes: 40 additions & 0 deletions config/target-capabilities.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@
"root_policy": "deliver the bounded digest to the root session",
"subagent_policy": "suppress when agent_id or agent_type identifies a child session",
"background_policy": "suppress background and delegated agents",
"injection_limit": {
"status": "documented",
"limit": 10000,
"unit": "characters",
"source": "https://code.claude.com/docs/en/hooks",
"checked": "2026-10-21",
"summary": "Each additionalContext string and plain stdout is capped at 10,000 characters; larger output is saved to a file and only its path and a 2,000-character preview are injected. No setting raises the cap."
},
"modes": [
{
"name": "startup",
Expand Down Expand Up @@ -95,6 +103,12 @@
"root_policy": "deliver the bounded digest to the root session",
"subagent_policy": "no subagentStart context wiring; subagent delivery is outside sessionStart",
"background_policy": "silently suppress native is_background_agent=true payloads",
"injection_limit": {
"status": "undocumented",
"source": "https://cursor.com/docs/agent/hooks",
"checked": "2026-10-21",
"summary": "sessionStart additional_context joins the initial system context; no size limit is documented, so the tightest documented limit applies."
},
"modes": [
{
"name": "new-composer",
Expand Down Expand Up @@ -174,6 +188,12 @@
"root_policy": "deliver the bounded digest to the root session",
"subagent_policy": "no subagentStart context wiring; subagent delivery is outside sessionStart",
"background_policy": "silently suppress native is_background_agent=true payloads",
"injection_limit": {
"status": "undocumented",
"source": "https://cursor.com/docs/agent/hooks",
"checked": "2026-10-21",
"summary": "sessionStart additional_context joins the initial system context; no size limit is documented, so the tightest documented limit applies."
},
"modes": [
{
"name": "new-composer",
Expand Down Expand Up @@ -253,6 +273,14 @@
"root_policy": "deliver the bounded digest to the root session only",
"subagent_policy": "suppress subagent context and capture",
"background_policy": "suppress background and delegated agents",
"injection_limit": {
"status": "documented",
"limit": 2500,
"unit": "approximate-tokens",
"source": "https://learn.chatgpt.com/docs/hooks",
"checked": "2026-10-21",
"summary": "Each model-visible hook-output message is capped at about 2,500 tokens, estimated as UTF-8 bytes divided by 4; larger output is saved to a file and a head-and-tail preview is injected. additionalContextLimit on a hook changes the cap."
},
"modes": [
{
"name": "startup",
Expand Down Expand Up @@ -335,6 +363,12 @@
"root_policy": "look up the root session without parentID and fail closed when identity is unavailable",
"subagent_policy": "use parentID to classify child sessions and suppress child context and capture",
"background_policy": "use parentID to classify background sessions and suppress background context and capture",
"injection_limit": {
"status": "undocumented",
"source": "https://opencode.ai/docs/plugins/",
"checked": "2026-10-21",
"summary": "No size limit is documented for plugin-injected context, so the tightest documented limit applies."
},
"modes": [
{
"name": "request",
Expand Down Expand Up @@ -417,6 +451,12 @@
"root_policy": "disabled: root-only identity cannot be proven",
"subagent_policy": "disabled: foreground child is indistinguishable",
"background_policy": "disabled: foreground child is indistinguishable",
"injection_limit": {
"status": "undocumented",
"source": "https://ampcode.com/docs/plugin-api",
"checked": "2026-10-21",
"summary": "session.start cannot inject context and agent.start documents no size limit, so the tightest documented limit applies."
},
"modes": [
{
"name": "foreground-turn",
Expand Down
4 changes: 3 additions & 1 deletion docs/knowledge/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ Before the first journal write, select the command in [Journal Commands](#journa

Each layer reports `source_available`, `available_count`, `shown_count`, `truncated`, and an exact `expand_command`; paginated layers also return a cursor. `source_available: false` means the source could not be derived and is not an empty result. In particular, an unavailable Change source marks both `active-changes` and `active-lineage` unavailable and emits a diagnostic.

Use `--branch` to select `branch-recency` scope and bind state cursors. It does not override active Change provenance or reasons, which always use the actual Git branch. Use `--layer` to request one canonical layer. `--limit` accepts 1 through 100 only with `--layer`; `--cursor` also requires `--layer` and cannot expand the intrinsic one-item `project-synthesis` or `scoped-checkpoint` layers. Reuse the returned `expand_command` verbatim: cursors are bound to their layer, project, branch, snapshot, and limit. `--json` is the stable machine surface; human output retains the same counts, unavailable markers, and expansion command.
Use `--branch` to select `branch-recency` scope and bind state cursors. It does not override active Change provenance or reasons, which always use the actual Git branch. Use `--layer` to request one canonical layer. `--limit` accepts 1 through 100 only with `--layer`; `--cursor` also requires `--layer` and cannot expand the intrinsic one-item `project-synthesis` or `scoped-checkpoint` layers. Reuse the returned `expand_command` verbatim: cursors are bound to their layer, project, branch, snapshot, and limit. `--json` is the stable machine surface. Human output of one `--layer` retains the same counts, unavailable markers, and expansion command.

Without `--layer`, human output is the compact digest that every harness hook and post-compaction resumption also receives. Each entry is one line with its date, id, label, and a shortened message; `loaf journal show <id>` prints it in full. Empty layers are left out, and pagination cursors never appear. A layer showing fewer items than it has names a cursor-free `--layer` command with an explicit `--limit` that lists them. The digest, including any drift nudge, stays under an 8,000-byte budget, below every harness injection limit recorded in `config/target-capabilities.json`. Repeated diagnostics collapse to one line per code. The canonical layer order above is also the priority order: the latest deliberate synthesis first, then live obligations, then branch recency and transitional tasks, which the layer commands and `loaf journal recent` recover cheaply. When the budget is tight, items are admitted one round at a time in that order, so every layer keeps its first items before any layer gets more, and the digest says how many entries it left out.

## Command Index

Expand Down
2 changes: 1 addition & 1 deletion docs/knowledge/hook-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ Registered under `session:` in hooks.yaml with an `event:` field. These hooks em

| Hook | Event | Dispatch | Purpose |
|------|-------|----------|---------|
| `session-start-loaf` | SessionStart | command (`loaf journal context --from-hook`) | Emit the layered continuity digest (latest wrap + branch entries + open tasks); silent for subagents |
| `session-start-loaf` | SessionStart | command (`loaf journal context --from-hook`) | Emit the compact layered continuity digest, kept under every harness injection limit; silent for subagents |
| `session-context-inject` | UserPromptSubmit | command (`loaf journal context for-prompt`) | Inject implementation conventions on prompt submit |
| `pre-compact` | PreCompact | command (`loaf journal context for-compact`) | Reinforce ordinary capture and selective checkpoints; writes nothing itself |
| `post-compact` | PostCompact | command (`loaf journal context for-resumption`) | Re-emit the continuity digest for post-compaction resumption |
Expand Down
4 changes: 3 additions & 1 deletion internal/cli/cli_reference.go
Original file line number Diff line number Diff line change
Expand Up @@ -997,7 +997,9 @@ The Loaf operating manual for agents: how to discover commands, diagnose project
"",
"Each layer reports `source_available`, `available_count`, `shown_count`, `truncated`, and an exact `expand_command`; paginated layers also return a cursor. `source_available: false` means the source could not be derived and is not an empty result. In particular, an unavailable Change source marks both `active-changes` and `active-lineage` unavailable and emits a diagnostic.",
"",
"Use `--branch` to select `branch-recency` scope and bind state cursors. It does not override active Change provenance or reasons, which always use the actual Git branch. Use `--layer` to request one canonical layer. `--limit` accepts 1 through 100 only with `--layer`; `--cursor` also requires `--layer` and cannot expand the intrinsic one-item `project-synthesis` or `scoped-checkpoint` layers. Reuse the returned `expand_command` verbatim: cursors are bound to their layer, project, branch, snapshot, and limit. `--json` is the stable machine surface; human output retains the same counts, unavailable markers, and expansion command.",
"Use `--branch` to select `branch-recency` scope and bind state cursors. It does not override active Change provenance or reasons, which always use the actual Git branch. Use `--layer` to request one canonical layer. `--limit` accepts 1 through 100 only with `--layer`; `--cursor` also requires `--layer` and cannot expand the intrinsic one-item `project-synthesis` or `scoped-checkpoint` layers. Reuse the returned `expand_command` verbatim: cursors are bound to their layer, project, branch, snapshot, and limit. `--json` is the stable machine surface. Human output of one `--layer` retains the same counts, unavailable markers, and expansion command.",
"",
"Without `--layer`, human output is the compact digest that every harness hook and post-compaction resumption also receives. Each entry is one line with its date, id, label, and a shortened message; `loaf journal show <id>` prints it in full. Empty layers are left out, and pagination cursors never appear. A layer showing fewer items than it has names a cursor-free `--layer` command with an explicit `--limit` that lists them. The digest, including any drift nudge, stays under an 8,000-byte budget, below every harness injection limit recorded in `config/target-capabilities.json`. Repeated diagnostics collapse to one line per code. The canonical layer order above is also the priority order: the latest deliberate synthesis first, then live obligations, then branch recency and transitional tasks, which the layer commands and `loaf journal recent` recover cheaply. When the budget is tight, items are admitted one round at a time in that order, so every layer keeps its first items before any layer gets more, and the digest says how many entries it left out.",
"",
"## Command Index",
"",
Expand Down
6 changes: 5 additions & 1 deletion internal/cli/journal.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func writeJournalDeferHelp(out io.Writer) {
}

func writeJournalContextHelp(out io.Writer) {
writeUsageHelp(out, "loaf journal context [options]", "Emit the contract-v2 active-truth continuity digest.",
writeUsageHelp(out, "loaf journal context [options]", "Emit the contract-v2 active-truth continuity digest. Without --layer it prints the compact digest hooks inject: one line per entry, no empty layers, under 8,000 bytes.",
"--branch Branch scope (defaults to the current git branch)",
"--layer Select one layer: project-synthesis, scoped-checkpoint, active-lineage, unresolved-blockers, deferred-intent, exploration-checkpoints, active-changes, branch-recency, transitional-tasks",
"--limit Maximum 1..100 items for the selected layer (requires --layer)",
Expand Down Expand Up @@ -1061,6 +1061,10 @@ func (r Runner) runJournalContextDigestWithHookInput(args []string, out io.Write
if options.jsonOutput {
return writeJSON(out, output)
}
if options.layer == "" {
fmt.Fprintln(out, renderJournalContextDigest(output, journalContextDigestBudget))
return nil
}
writeJournalContextHuman(out, output)
return nil
}
Expand Down
Loading
Loading