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
23 changes: 22 additions & 1 deletion docs/architecture/sdlc-pipeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ The contract is a JSON document tracking the complete state of an issue through

```json
{
"schemaVersion": "1.0",
"schemaVersion": "1.1",
"issue": { "number": 133, "title": "...", "url": "..." },
"current_phase": "implement",
"slices": [{
Expand Down Expand Up @@ -116,6 +116,27 @@ The contract is a JSON document tracking the complete state of an issue through
> existing imports. See [Slice-DAG Implement Phase](slice-dag.md) for
> the full design.

> **Schema 1.1 (#2548)**: `schemaVersion` was bumped from `1.0` to `1.1`
> to track the addition of four optional `pr.context_*` fields on
> `PRMetadata` (`context_title`, `context_description`, `context_branch`,
> `context_pr_number`) used by the dedicated context-PR mechanism. The
> bump is purely additive — pre-1.1 contracts load transparently via a
> Pydantic `model_validator(mode="after")` migration that stamps
> `schemaVersion = "1.1"` on every load when the on-disk value is exactly
> `"1.0"`; the migration is silent (no audit-log entry) and idempotent.
> `context_title` / `context_description` are planner-emitted optional
> framing for the strategic-plan PR; `context_branch` /
> `context_pr_number` are populated by the orchestrator after the context
> branch is created and the context PR is opened.
>
> **As of slice-1 (#2548 part 1)**, only the schema fields and the
> planner-prompt advertisement are wired. The orchestrator
> branch-creation and PR-opening hooks land in #2548 slices 3-4 — until
> those slices merge, the four `pr.context_*` fields are
> forward-compatibly inert: planners may emit `context_title` /
> `context_description` and the values flow into `PRMetadata`, but
> nothing acts on them yet.

## HITL (Human-in-the-Loop) Mechanism

For detailed HITL workflow documentation, see [HITL Decisions](../hitl-decisions.md).
Expand Down
6 changes: 5 additions & 1 deletion docs/guides/concurrent-execution.md
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,11 @@ When the orchestrator auto-creates the PR (during the PR phase), it includes a o

> _Per-phase BRC transcripts: [`refine`](./.egg-state/brc-history/42-refine.md), [`plan`](./.egg-state/brc-history/42-plan.md), [`implement`](./.egg-state/brc-history/42-implement.md)._

Phases are ordered by canonical execution order (`refine` → `plan` → `implement` → `pr`); any non-canonical names sort alphabetically after. The line is omitted entirely when no transcript files exist on disk or the identifier is `None`. See [#1828](https://github.com/jwbron/egg/issues/1828) for why the old inline BRC Consensus Summary was removed.
In slice-aware mode (issue mode with `contract.slices`, #2548 hard switchover), the implement phase is partitioned per slice — the writer produces `{identifier}-implement-slice-<N>.md` (one file per slice) plus `{identifier}-implement-unattributed.md` for cross-cutting messages without canonical slice scope (HEARTBEAT, OVERSEER_ALERT, AGENT_FAILED, …). The aggregate `{identifier}-implement.md` file is **not** produced in slice mode. The link line clusters the per-slice files at the canonical `implement` rank in natural-sort order, with the unattributed sibling rendered last:

> _Per-phase BRC transcripts: [`refine`](./.egg-state/brc-history/42-refine.md), [`plan`](./.egg-state/brc-history/42-plan.md), [`implement-slice-1`](./.egg-state/brc-history/42-implement-slice-1.md), [`implement-slice-2`](./.egg-state/brc-history/42-implement-slice-2.md), [`implement-unattributed`](./.egg-state/brc-history/42-implement-unattributed.md)._

Babysit_pr and other non-slice implement runs continue to emit the aggregate `{identifier}-implement.md` file. Phases are ordered by canonical execution order (`refine` → `plan` → `implement` → `pr`); any non-canonical names sort alphabetically after. The line is omitted entirely when no transcript files exist on disk or the identifier is `None`. See [#1828](https://github.com/jwbron/egg/issues/1828) for why the old inline BRC Consensus Summary was removed and [#2548](https://github.com/jwbron/egg/issues/2548) for the per-slice partition.

### Consensus Check

Expand Down
9 changes: 8 additions & 1 deletion docs/guides/sdlc-pipeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ The local orchestrator handles concurrent contract updates through `orchestrator

```json
{
"schemaVersion": "1.0",
"schemaVersion": "1.1",
"issue": {
"number": 123,
"title": "Add feature X",
Expand Down Expand Up @@ -483,6 +483,13 @@ The local orchestrator handles concurrent contract updates through `orchestrator
}
```

> **Schema 1.1 (#2548)**: The default `schemaVersion` is now `"1.1"`, which
> additively introduces four optional `pr.context_*` fields
> (`context_title`, `context_description`, `context_branch`,
> `context_pr_number`). Pre-1.1 contract JSON loads cleanly — a Pydantic
> `model_validator` silently promotes `"1.0"` to `"1.1"` on load and the
> bumped value is persisted on the next save.

### Role-Based Field Ownership

The `shared/egg_contracts/roles.py` module defines field ownership:
Expand Down
25 changes: 25 additions & 0 deletions docs/templates/plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ pr:
manual_steps: |
Pre-merge: [any required steps before merging, e.g. migrations, config changes]
Post-merge: [any required steps after merging, e.g. deployments]
# Optional context-PR framing (#2548); omit to reuse pr.title / pr.description.
# context_title: |-
# Strategic plan for #<issue> — refine/plan analysis + BRC history
# context_description: |-
# Carries the refine analysis, the plan, the BRC consensus
# history that approved each, and the agent transcripts —
# so reviewers approaching the slice stack can see the strategic
# narrative on a PR that targets the configured base branch.
phases:
- id: 1
name: |-
Expand Down Expand Up @@ -125,6 +133,23 @@ phases:
> the task's files — see [Agent Roles Reference](../reference/agent-roles.md#role-aware-task-assignment)
> for the file-to-role mapping. Tasks without a `role` default to the coder.

> **Context-PR framing (#2548)**: `pr.context_title` and `pr.context_description`
> are *optional* keys planners may emit to give the dedicated context PR a
> different framing from the slice PRs (e.g. "Strategic plan for #N" vs the
> slice's "Implement …"). When omitted the orchestrator falls back to
> `pr.title` / `pr.description`. Two sibling fields — `pr.context_branch` and
> `pr.context_pr_number` — exist on the contract but are populated by the
> orchestrator after the context branch is created and the context PR is
> opened; planners must NOT emit them.
>
> **As of slice-1 (#2548 part 1)**, only the schema fields and this
> planner-prompt guidance are wired. The orchestrator branch-creation
> and PR-opening hooks land in #2548 slices 3-4 — until those slices
> merge, any `context_title` / `context_description` a planner emits
> flows through the parser into `PRMetadata` but nothing acts on it
> yet, so emitting them now is forward-compatibly safe but does not
> change the rendered PR.

> **Slices vs. phases (#2137)**: The plan parser accepts either `slices:`
> (canonical, post-#2137) or `phases:` (legacy alias) at the top of the
> `# yaml-tasks` block. New plans should emit `slices:` so they ingest as
Expand Down
8 changes: 7 additions & 1 deletion orchestrator/routes/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -666,11 +666,17 @@ def post_heartbeat(pipeline_id: str) -> tuple[Response, int]:

# Emit as a normal HEARTBEAT message on the bus so downstream
# consumers (HealthMonitor, overseer, UI) see it.
metadata = {"state": state}
metadata: dict[str, Any] = {"state": state}
if waiting_on:
metadata["waiting_on"] = waiting_on
if body.get("since"):
metadata["since"] = body["since"]
# Tag with slice_id so the implement-phase BRC writer can partition
# this HEARTBEAT into the correct per-slice transcript (#2548).
# Pipeline-level (non-slice) heartbeats leave the metadata off
# entirely.
if slice_id:
metadata["slice_id"] = slice_id

msg = Message(
pipeline_id=pipeline_id,
Expand Down
Loading
Loading