-
Notifications
You must be signed in to change notification settings - Fork 6
feat(stories): engine, adapter + HITL checkpoints for folder+id dispatch (Phase 2) #77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feat/spec-to-loop-stories-phase1
Are you sure you want to change the base?
Changes from all commits
385902c
7e7d21c
813cdf8
ce2104c
060c828
f4fdce5
67b75e3
31566a4
b9eb346
2d018c5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -459,6 +459,12 @@ def _artifact_dirs(self, cwd: Path) -> list[Path]: | |
| return dirs | ||
|
|
||
| def _result_json(self, handle: SessionHandle, spec: SessionSpec, *, wait: bool) -> dict | None: | ||
| # Stories mode (folder+id dispatch): the story spec lives at a | ||
| # deterministic id-keyed path, so resolve it directly instead of the | ||
| # mtime-floor scan. The engine exports BMAD_LOOP_SPEC_FOLDER only for | ||
| # stories runs, so sprint/sweep runs keep the scan path below unchanged. | ||
| if spec.env.get("BMAD_LOOP_SPEC_FOLDER"): | ||
| return self._stories_result_json(handle, spec, wait=wait) | ||
| # Mirror the base _await_result poll: the skill's terminal spec may not be | ||
| # flushed to disk the instant the Stop event fires, so briefly await it when | ||
| # wait=True instead of reading once and mis-reporting a stall. | ||
|
|
@@ -481,6 +487,43 @@ def _result_json(self, handle: SessionHandle, spec: SessionSpec, *, wait: bool) | |
| return None | ||
| time.sleep(RESULT_POLL_S) | ||
|
|
||
| def _stories_result_json( | ||
| self, handle: SessionHandle, spec: SessionSpec, *, wait: bool | ||
| ) -> dict | None: | ||
| """Deterministic stories-mode read-back: resolve ``<spec-folder>/stories/ | ||
| <id>-*.md`` by id (never the mtime scan) and synthesize from it. | ||
|
|
||
| ``BMAD_LOOP_SPEC_FOLDER`` carries the project-relative (or absolute) spec | ||
| folder; rebase a relative one against ``spec.cwd`` exactly like | ||
| ``_artifact_dirs`` so worktree isolation resolves inside the live checkout. | ||
| A PRESENT or SENTINEL spec synthesizes (a blocked sentinel becomes a | ||
| CRITICAL escalation → PAUSE, same as any block); a still-PENDING or | ||
| AMBIGUOUS state is not-yet-terminal → keep waiting, then None (a | ||
| result-less Stop the dev-stall grace handles). | ||
|
|
||
| On a plan-halt leg (``BMAD_LOOP_PLAN_HALT`` set by the engine for a | ||
| spec_checkpoint story's first dispatch) the skill HALTs at | ||
| ``ready-for-dev``; pass ``plan_halt=True`` so synthesize treats that as a | ||
| successful terminal (marked ``plan_halt``) rather than died-mid-flight.""" | ||
| from .. import stories | ||
|
|
||
| story_key = spec.env.get("BMAD_LOOP_STORY_KEY") or "" | ||
| folder = Path(spec.env["BMAD_LOOP_SPEC_FOLDER"]) | ||
| base = folder if folder.is_absolute() else Path(spec.cwd) / folder | ||
| plan_halt = bool(spec.env.get("BMAD_LOOP_PLAN_HALT")) | ||
| deadline = time.monotonic() + RESULT_GRACE_S | ||
| while True: | ||
| state = stories.resolve_story_spec(base, story_key) | ||
| if state.kind in (stories.KIND_PRESENT, stories.KIND_SENTINEL) and state.path: | ||
| result = devcontract.synthesize_result( | ||
| state.path, story_key=story_key or None, plan_halt=plan_halt | ||
| ) | ||
| if result.result_json is not None: | ||
| return result.result_json | ||
| if not wait or time.monotonic() >= deadline: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. src/bmad_loop/adapters/generic.py:515: If Severity: medium 🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage. |
||
| return None | ||
| time.sleep(RESULT_POLL_S) | ||
|
|
||
|
|
||
| # Back-compat alias: the adapter was ``GenericTmuxAdapter`` before tmux moved | ||
| # behind the multiplexer seam. Keeps existing imports stable. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/bmad_loop/adapters/generic.py:510:
_stories_result_json()no longer useshandle.launched_ns(unlike the mtime-scan path), so an already-terminal story spec from a previous step (notably the pre-existingdonespec when starting a follow-up review session) could be misread as this session’s completion even if the session produced no new output.Severity: high
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.