Project Synthesis
Project synthesis groups one project’s per-session evidence chains into a small set of project-level work items. It is the noise-reduction layer between evidence extraction and daily report synthesis. A single day can produce on the order of a hundred evidence chains across a project’s sessions; feeding them to daily synthesis raw would bury the signal. Project synthesis groups related chains, cites them by reference, and summarizes them, so daily synthesis reads a handful of work items instead of a hundred chains.
This step runs from the prepared report workspace root and operates on one prepared project scope at
a time, identified by project_key.
Role: Group, Cite, Summarize
A work item is a summary node over a group of evidence chains. It never copies chain content.
- Group. Collect the evidence chains that belong to the same line of work.
- Cite, do not paste. Reference grouped chains by
(session_ref, turn_ref). Never embed quoted messages, observed-check text, or line citations. Detail stays in the evidence cards and is reached by reference. The citation chain isreport.md -> work item -> evidence card -> turn_ref + lines. - Summarize. Describe the work item at a higher altitude than any single chain. A card summarizes one turn; a work item summarizes the whole line of work.
The work item is therefore a compact index plus narrative. Daily synthesis works from these summaries and opens evidence cards only to pull the exact lines for a claim it decides to promote.
Inputs And Outputs
Inputs, under projects/<project_key>/:
project.json— project identity for the work-item envelope.evidence/<session_ref>.json— the per-session evidence cards. The orchestrator trims these to summaries (no line citations or quoted text) and pastes them into the synthesizer prompt; the synthesis agent works only from that inline content and has no file access.sessions.index.jsonl— the coverage universe. Thewrite_work_itemtool reads it to report uncovered turns.
The pasted chains are grouped by session under a #### Session <session_ref> heading, and each chain
is labelled <session_ref>/<turn_ref> — turn_ref restarts per session and the work item references
turns as {session_ref, turn_ref}, so the session must be unambiguous for every chain. Each chain
keeps its trigger, reaction, outcome (with category), and terminal (with type) summaries plus
materiality; citations and quoted text are dropped.
Output:
projects/<project_key>/project-synthesis.json— a work-item envelope
Project synthesis artifacts stay inside the prepared report workspace and must not change the
preparation layout or the meaning of sessions.index.jsonl.
Boundary: What Project Synthesis Does Not Own
Project synthesis owns grouping and coverage only. It does not produce:
- executive or project progress summaries
- cross-project blocker prioritization
- reusable agent-driving patterns or antipatterns
- engagement verdicts
- day-level verification or evidence-quality conclusions
These belong to Daily Report Synthesis because the signals only become meaningful after comparing work items across every project. One weak prompt or one missing verification in a single project may be noise, while the same pattern repeated across projects is a real day-level lesson. Project synthesis preserves the local, evidence-backed material those judgments need; it does not make the judgments itself.
Grouping
Group by coherent line of work, not by session. Merge evidence chains into one work item when they share:
- the same user goal
- the same artifact
- the same bug, blocker, or validation loop
- the same design decision
- a correction loop around the same output
- a test-fix-test sequence
- an interruption followed by a human continue or resume for the same goal
Keep chains in separate work items when they pursue unrelated goals, independent decisions, separate blockers, different artifacts, or different project areas.
The session boundary is irrelevant in both directions:
- One line of work may span several sessions, so
covered_turnsandevidence_refsmay list turns from differentsession_refs. - One long session may contain several unrelated lines of work, which become several work items.
Supporting turns fold in. A low-value turn that fed a material line of work — a clarification, an approval, a resume — is covered inside the work item it supports, not split out.
Trivial turns bucket. Turns with no material outcome that support no line of work — a connectivity
ping, a throwaway question — are grouped into a single no_material_work_item for the project rather
than producing many tiny items.
Outcome Consolidation
A work item’s outcomes are consolidated claims, not copies of card outcomes. Merge the card-level
outcomes that describe the same achievement into one work-item outcome, and cite the set of turns
that support it. The number of outcomes on a work item should be far smaller than the summed outcomes
of its covered chains.
Reuse the category already present on the evidence-card outcomes you consolidate, and the type on
their terminal states; do not invent new values. The controlled outcome categories and terminal-state
types are defined by the Evidence Contract.
No Prescriptions
A work item describes blocked or unfinished state through a blocker_outcome; it does not recommend a
next action. This boundary is local to project synthesis so it stays focused on grouping; pairing
blockers with supported next actions is the job of Daily Report Synthesis.
Coverage Invariant
Every indexed turn is accounted for:
Every
(session_ref, turn_ref)in the project’ssessions.index.jsonlappears in exactly one work item’scovered_turns.
This includes material, minor, interrupted, clarification-only, failed, blocked, and trivial turns,
as well as evidence gaps. A turn that has a committed evidence chain is grouped into a normal work
item by its content. A turn that is indexed but has no committed chain — its content is unknowable to
synthesis — is collected into an evidence_gap_item instead. Turns intentionally left unreported,
such as duplicate evidence already represented elsewhere, go into an excluded_with_reason item that
records the reason. Nothing is dropped silently.
Work Item Kinds
kind is the work item’s coverage disposition. It is one of:
material_work_item— grouped work that produced material progress.no_material_work_item— reportable low-value or negative turns with no material output, including the trivial-turn bucket.evidence_gap_item— accounts for indexed turns that have no extractable evidence.excluded_with_reason— turns intentionally left out of reportable work items; requiresreason.
kind is deliberately small and mutually exclusive. Finer signals that can co-occur are not kinds:
an interruption is a terminal_states[].type, and a blocker is an outcomes[].category of
blocker_outcome. A single work item can be material, interrupted, and contain a blocker at once; daily
synthesis routes its sections off these finer fields.
kind is maintained as controlled values in the prompt API (PROJECT_WORK_ITEM_KINDS) and rendered
into the Project Synthesizer Prompt, so it has one source of
truth.
Schema
Envelope
{
"schema_version": 1,
"project_key": "ReportGenerator-e6ff7eeda632",
"project_label": "ReportGenerator",
"work_items": [],
"source_user_messages": []
}
References inside the file are {"session_ref": "...", "turn_ref": "..."}. project_key is implied
by the envelope and re-attached by daily synthesis when it loads the file, matching how a session
evidence card carries session_ref once on the envelope and a bare turn_ref on each chain.
work_items are agent-authored. source_user_messages is tool-populated: write_work_item
fills it once, on the first write, and the synthesizer agent neither reads nor writes it — so the
Project Synthesizer Prompt needs no change. It carries the
original user-message content per indexed turn, copied verbatim from the text of each extracted
chain’s trigger.quoted_messages in evidence/<session_ref>.json:
"source_user_messages": [
{
"session_ref": "S0001",
"turn_ref": "T0001",
"messages": ["<redacted user-authored text>"]
}
]
Each turn’s messages is a plain list of the verbatim user-message strings. It is messages-only —
content, not structure: just the text, with no line citations, trigger_type, terminal_state, or
check information, because daily synthesis reopens the card (which keeps the full quoted_messages
with citations) for committed structure when it needs it. The text is already secret-redacted by the
extractor; the tool copies it verbatim and does not re-redact. There is one entry per indexed turn
whose chain has at least one user message; turns with no extractable user text are simply absent,
still accounted for through covered_turns and the coverage invariant. Entries are ordered by
(session_ref, turn_ref). This block is the user-message content substrate for daily synthesis’s
engagement and team-learning readings.
Work Item
{
"work_item_ref": "W0001",
"kind": "material_work_item",
"title": "Finalize and freeze the evidence-extraction contract",
"covered_turns": [
{"session_ref": "S0001", "turn_ref": "T0001"}
],
"trigger": {
"summary": "User drove the evidence-extraction surface to top-level turn_ref, ordered a consistency review, and finalized the design choices.",
"evidence_refs": [
{"session_ref": "S0001", "turn_ref": "T0001"},
{"session_ref": "S0001", "turn_ref": "T0006"}
]
},
"agent_reaction": {
"summary": "Migrated the contract, MCP tools, and prompt to turn_ref identity, ran review subagents, implemented the finalized choices, and froze with a commit.",
"main_actions": ["turn_ref migration", "consistency review", "implement finalized choices", "freeze commit"]
},
"outcomes": [
{
"category": "document_outcome",
"summary": "Evidence contract and MCP tool docs moved to top-level turn_ref; chain_ref removed.",
"evidence_refs": [{"session_ref": "S0001", "turn_ref": "T0001"}],
"confidence": "high"
},
{
"category": "process_outcome",
"summary": "Froze the agreed contract as a checkpoint commit.",
"evidence_refs": [{"session_ref": "S0001", "turn_ref": "T0010"}],
"confidence": "high"
}
],
"terminal_states": [
{
"type": "interrupted",
"summary": "Prompt-test verification of the placeholder edit was interrupted; test ownership left to concurrent agents.",
"evidence_refs": [{"session_ref": "S0001", "turn_ref": "T0008"}]
}
],
"limits": ["Prompt-test suite not confirmed green within these turns."],
"confidence": "high"
}
Fields
work_item_ref— project-local handle,W0001,W0002, and so on, assigned in work-item order.kind— coverage disposition (see Work Item Kinds).title— a one-line name for the work item. There is deliberately no fused work-itemsummary: thetrigger,agent_reaction,outcomes, andterminal_statessummaries are the work item’s summary, kept separable so each stays independently citable and daily synthesis can recompose them.covered_turns[]— every turn this item accounts for, as{session_ref, turn_ref}. The union across all work items covers the session index exactly once.trigger— the earliest meaningful human trigger for the work item, as{summary, evidence_refs}. Later corrections, approvals, and resumes are summarized inagent_reactionand remain incovered_turns.agent_reaction— what the agent actually did across the work item, as{summary, main_actions}.outcomes[]— consolidated achievements, as{category, summary, evidence_refs, confidence}.categoryreuses the Evidence Contract outcome categories. A blocker is an outcome with categoryblocker_outcome.terminal_states[]— how the work item or its notable branches ended, as{type, summary, evidence_refs}.typereuses the Evidence Contract terminal-state types, includinginterrupted,blocked, andfailed.limits[]— short honesty notes: what the work item did not verify or could not confirm.reason— required forexcluded_with_reason; why the covered turns are not reportable, such as duplicate evidence already represented in another work item.confidence—high,medium, orlowfor the work item as synthesized evidence.
Required Fields Per Kind
- All kinds:
work_item_ref,kind,title, a non-emptycovered_turns, andconfidence. material_work_item: alsotrigger,agent_reaction, and at least one ofoutcomesorterminal_states.no_material_work_item:trigger,agent_reaction, andoutcomesmay be empty;titlepluscovered_turnscarry it.evidence_gap_item: covers only turns that have no committed evidence chain; narrative fields are empty;confidenceis usuallylow.excluded_with_reason: requiresreason; narrative fields are empty.
Project Synthesizer Prompt
This contract is developer-facing: it documents the design for repository developers and readers. The project synthesizer agent never reads it. At runtime the agent sees only the rendered prompt below and the workspace files it opens. Any decision in this contract that the agent must act on has to be restated as explicit instructions in that prompt source; a cross-reference to this contract does not reach the agent.
Prompt source: src/prompt_diary/generate/prompts/project-synthesizer.md — loaded at runtime by the
orchestrator.
The orchestrator runs the synthesizer in one main pass, then — if write_work_item still reports
uncovered turns — exactly one bounded continuation that names the remaining turns and asks the agent
to cover them (group a turn that has an evidence chain into a work item; cover one that does not with
an evidence_gap_item). Those continuation-only instructions live in
src/prompt_diary/generate/prompts/project-synthesizer-next.md (project_synthesizer_next_prompt);
the task fails only if turns remain uncovered after that single continuation. Because the
continuation names the turn references explicitly, it also recovers a project whose paste was empty —
every indexed turn an evidence gap.
See Project Synthesizer Prompt.
Write Tool
Work items are committed through the write_work_item MCP tool, which also populates
source_user_messages on first write. Its input schema, validation rules, and result shape are
defined in Project Synthesis Tools.