Skip to content

feat: add decision readiness gate#101

Merged
richard-devbot merged 5 commits into
mainfrom
feat/decision-readiness-gate-70
Jun 12, 2026
Merged

feat: add decision readiness gate#101
richard-devbot merged 5 commits into
mainfrom
feat/decision-readiness-gate-70

Conversation

@richard-devbot

@richard-devbot richard-devbot commented Jun 12, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds a run-level Decision Queue (decisions.json) with pending/resolved/waived decisions and stale-decision summaries.
  • Adds Definition-of-Ready reports (dor-report.json, readiness.json) and profile-aware gate behavior: business-flex warns, enterprise-webapp blocks.
  • Exposes decision/readiness controls through Pi tools, CLI commands, exports, and the Business Hub dashboard.
  • Documents the workflow and includes the new docs page in the package allowlist.

Test Plan

  • npx tsx --test tests/validate-extension.test.js tests/validate-references.test.js tests/decisions-readiness.test.js
  • npm run lint
  • npm test
  • npm run validate
  • git diff --check
  • npm pack --dry-run --json verified decision-readiness.mdx and docs.json ship

Closes #70

Summary by CodeRabbit

  • New Features

    • Decision queue for tracking run-level decisions with add/list/resolve actions
    • CLI subcommands to manage decisions and run Definition-of-Ready (DoR) checks
    • Dashboard page showing decision queue and readiness/DoR status
    • Build pipeline now performs DoR gating before approval steps
  • Documentation

    • Added Decision Queue & Definition-of-Ready reference page and nav entry
    • README updated with link to the new docs page
    • Package publish now includes the new documentation assets
  • Tests

    • Expanded suite covering decision lifecycle, concurrency, DoR, dashboard, and CLI integration

@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a run-level Decision Queue and a Definition-of-Ready readiness gate: decision persistence and CRUD, DoR stage/profile checks with report persistence, CLI subcommands and Pi tools, dashboard state/UI, documentation and package updates, plus end-to-end tests and validation tweaks.

Changes

Decision Queue and Definition-of-Ready Feature

Layer / File(s) Summary
Decision queue domain model and persistence
src/core/harness/decisions.js
Decision normalization and deterministic DEC-### ids, atomic read/write to decisions.json, per-run locking for concurrent writes, addDecision/decide, readDecisions/writeDecisions, and summarizeDecisions including stale detection.
Definition-of-Ready readiness checks and stage ordering
src/core/harness/readiness.js
Fixed STAGE_ORDER, readinessModeForProfile, isRequiredBefore logic, dorCheck computing status/score/pending/stale lists, optional dor-report.json/readiness.json writes, and assertReadyForStage.
CLI entrypoints: decisions and dor subcommands
bin/rstack-agents.js
New CLI subcommands: decisions (list/add/resolve/waive) and dor (run DoR, emit JSON, exit non-zero on FAIL).
Public API exports for decisions and readiness
src/index.js
Re-exports decision and readiness helpers for programmatic use.
SDLC Pi tools and build gating integration
src/integrations/pi/rstack-sdlc.ts
Registers sdlc_decisions, sdlc_decide, sdlc_dor_check Pi tools; adds DoR gating to sdlc_build_next to block or warn builds before approval.
Dashboard state building from decisions and readiness
src/observability/dashboard/state/decisions.js, src/observability/dashboard/state/index.js
buildDecisionState(runs) loads per-run decisions and DoR reports, builds entries and totals, and integrates decisions into the snapshot state.
Dashboard UI: decisions page, rendering, scoping
src/observability/dashboard/ui/client.js, src/observability/dashboard/ui/pages/index.js
Adds Decisions / Readiness page, state scoping for decisions, and renderDecisions to show pending decision cards and DoR readiness cards with counts and empty states.
Documentation, navigation, and package manifest updates
README.md, docs/mintlify/reference/decision-readiness.mdx, docs/mintlify/docs.json, package.json
New MDX reference page and Mintlify navigation entries; README link added; package files whitelist extended to include docs.
E2E and validation tests
tests/decisions-readiness.test.js, tests/validate-extension.test.js, tests/validate-package-assets.test.js, tests/validate-references.test.js
End-to-end coverage: decision lifecycle and concurrency, stale computation, DoR behavior by profile and negative paths, dashboard integration and robustness, CLI workflows, and updated validation tests for extensions/package assets/references.

🎯 4 (Complex) | ⏱️ ~60 minutes


🐰 A queue of choices, staged just right,
Before the build begins, let's see the light.
Pending, resolved—each one gets a say,
RStack's Ready checks keep the risk at bay.
Decisions first, then agents dance away.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add decision readiness gate' accurately captures the main feature introduced—the Definition-of-Ready (DoR) gate for decisions that gate build stage progression based on pending decisions.
Linked Issues check ✅ Passed The PR successfully implements all acceptance criteria from issue #70: decision queue persistence, CLI commands (rstack-agents decisions/dor), profile-aware gating (business-flex warns, enterprise-webapp blocks), DoR reports, dashboard integration, comprehensive tests covering pending/resolved/waived/stale cases, and documentation.
Out of Scope Changes check ✅ Passed All changes are directly aligned with issue #70 objectives: decision queue module, readiness gating, CLI integration, Pi tool registration, dashboard state/UI updates, documentation, and comprehensive tests. No extraneous changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/decision-readiness-gate-70

Comment @coderabbitai help to get the list of available commands and usage tips.

@mintlify

mintlify Bot commented Jun 12, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
evoke-f0bfabff 🟢 Ready View Preview Jun 12, 2026, 1:49 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/core/harness/decisions.js`:
- Around line 68-85: The read-modify-write of decisions.json in
writeDecisions/addDecision (and decide) is racy; implement a per-run atomic
update (either a filesystem-backed per-run lock or a compare-and-swap retry
loop) so ID assignment and persistence are serialized. Change addDecision to
acquire the run lock (or loop: read file, compute next DEC-### from the latest
contents, create normalized entry, attempt atomic replace by writing to a temp
file and renaming or by checking updated_at and retrying on mismatch) and
release the lock (or break on success); do the same for decide and any code path
that calls writeDecisions so you always recompute the next number after
acquiring the lock/confirming no concurrent update, retry a bounded number of
times, and surface an error if retries are exhausted.

In `@src/core/harness/readiness.js`:
- Around line 22-29: The current stageIndex maps unknown IDs to
STAGE_ORDER.length which lets mistyped or new required_before_stage values
bypass readiness; update stageIndex(stageId) to return -1 for unknown IDs
(instead of STAGE_ORDER.length) and change isRequiredBefore(decision,
targetStage) to explicitly reject unknown stage ids by throwing an Error when
stageIndex(decision.required_before_stage) === -1 or when targetStage is
provided and stageIndex(targetStage) === -1; this enforces failing fast for
unknown stages (or alternatively treat unknown required_before_stage as always
required by returning true) and references the existing functions stageIndex and
isRequiredBefore and the STAGE_ORDER constant.

In `@src/integrations/pi/rstack-sdlc.ts`:
- Around line 1480-1491: The DoR gating should follow assertReadyForStage's
contract: only FAIL (ok===false) blocks, and WARN may be informational when
there are no pending decisions; modify the warning logic in rstack-sdlc.ts so
that appendEvent(..., { type: "dor_gate_warning", ... }) and any WARN-specific
handling only runs when readiness.report.status === "WARN" AND
readiness.report.pending_required?.length > 0; keep the existing FAIL branch
that appends dor_gate_blocked and returns the blocked response (using
readiness.report.pending_required as before). Ensure you reference
assertReadyForStage, readiness.report, pending_required, appendEvent,
dor_gate_warning and dor_gate_blocked when making the change.

In `@src/observability/dashboard/state/decisions.js`:
- Around line 21-29: In the catch block where entries.push is used, stop
treating read/check failures as a PASS: change the catch to capture the
exception (catch (err)) and set the fallback readiness object to indicate
non-success (e.g., readiness.status = 'WARN' or 'UNKNOWN') and add an error
marker and message (e.g., readiness.error = true and readiness.errorMessage =
String(err) or err.message) so operators can distinguish unavailable runs; keep
or adjust readiness.score appropriately (e.g., null or 0) and ensure you
reference the readiness object created inside the entries.push call and the
caught error variable (err).

In `@src/observability/dashboard/ui/client.js`:
- Around line 268-272: The copy currently filters copy.decisions.runs by runIds
but leaves copy.decisions.totals unchanged, causing totals to not reflect the
scoped rows; after creating copy.decisions.runs (the filtered array), recompute
copy.decisions.totals from that filtered array (e.g., recalc counts/metrics used
in totals based on the items in copy.decisions.runs) so totals align with the
displayed rows; update the block that handles s.decisions / copy.decisions to
replace the existing totals with the derived totals computed from the filtered
runs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ce5eba1f-ccae-495d-991a-3a7ac26f5c3a

📥 Commits

Reviewing files that changed from the base of the PR and between b339ec6 and cad7ada.

📒 Files selected for processing (17)
  • README.md
  • bin/rstack-agents.js
  • docs/mintlify/docs.json
  • docs/mintlify/reference/decision-readiness.mdx
  • package.json
  • src/core/harness/decisions.js
  • src/core/harness/readiness.js
  • src/index.js
  • src/integrations/pi/rstack-sdlc.ts
  • src/observability/dashboard/state/decisions.js
  • src/observability/dashboard/state/index.js
  • src/observability/dashboard/ui/client.js
  • src/observability/dashboard/ui/pages/index.js
  • tests/decisions-readiness.test.js
  • tests/validate-extension.test.js
  • tests/validate-package-assets.test.js
  • tests/validate-references.test.js

Comment thread src/core/harness/decisions.js Outdated
Comment thread src/core/harness/readiness.js Outdated
Comment thread src/integrations/pi/rstack-sdlc.ts Outdated
Comment thread src/observability/dashboard/state/decisions.js Outdated
Comment thread src/observability/dashboard/ui/client.js
richardsongunde and others added 4 commits June 12, 2026 19:25
- Serialize Decision Queue writes with a per-run filesystem lock\n- Fail closed on unknown readiness stages\n- Surface readiness load failures as warnings\n- Recompute scoped dashboard decision totals\n\nAddresses CodeRabbit comments on #101
…test stage

Review follow-ups on #101:
- withDecisionLock now steals locks orphaned by a crashed holder (mtime
  older than 30s) instead of timing out forever, and the EEXIST retry is
  scoped to the lock acquire so errors from the locked operation are not
  swallowed or re-executed.
- resolveRunId rejects run ids containing path separators or traversal
  so --run-id cannot write state outside .rstack.
- sdlc_build_next gates on the latest stage of a bundled task via
  latestStageId() instead of the first, so decisions required before a
  mid-bundle stage actually block.
- Tests for all three; merged main (CRLF frontmatter fixes from #100).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@richard-devbot

Copy link
Copy Markdown
Owner Author

Review + hardening pass

Reviewed the full diff. All four earlier CodeRabbit findings genuinely landed (lock + atomic rename, fail-closed unknown stages, WARN fallback on read errors, scoped totals recompute). Windows path handling, XSS escaping in the new dashboard rendering, and test isolation all check out.

Pushed two follow-up commits addressing what the review surfaced:

  1. Stale-lock recovery (merge-blocking): withDecisionLock acquired via mkdir but had no recovery if the holder crashed before rm — every later decision write would spin 60 retries and throw, permanently wedging the run''s decision queue. It now steals locks older than 30s, and the EEXIST retry is scoped to the acquire so errors thrown by the locked operation are no longer swallowed.
  2. Run-id validation: resolveRunId now rejects ids with path separators/traversal, so --run-id ..\..\x can''t create state outside .rstack.
  3. Bundle gating: sdlc_build_next gated on the first stage of a bundled task, so a decision required_before_stage: 04-planning never blocked the task bundling 02-requirements → 04-planning → 05-jira. New latestStageId() gates on the latest stage in the bundle.

Also merged main (picks up the #100 CRLF frontmatter fixes) and added tests for all three. Non-blocking follow-ups worth tracking later: STAGE_ORDER duplicated in readiness.js vs stages.js; approval readiness mode behaves identically to warn (not wired to the approvals queue); dor-report.json/readiness.json writes are not atomic.

🤖 Generated with Claude Code

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/core/harness/readiness.js (1)

15-20: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Map business-flex to warn, not approval.

Line 19 currently reports approval for every non-enterprise-webapp profile, which includes business-flex. That conflicts with the PR objective that business-flex should default to warn, and it leaks the wrong mode into dor-report.json, CLI/Pi responses, and the new test expectations.

Proposed fix
 export function readinessModeForProfile(profile) {
   const name = typeof profile === 'string' ? profile : profile?.profile;
   if (name === 'enterprise-webapp') return 'blocking';
   if (name === 'lean-mvp') return 'warn';
-  return 'approval';
+  return 'warn';
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/harness/readiness.js` around lines 15 - 20, The
readinessModeForProfile function currently defaults all non-enterprise-webapp
profiles to 'approval', which misclassifies 'business-flex'; update
readinessModeForProfile to explicitly return 'warn' for the 'business-flex'
profile (e.g., add an if (name === 'business-flex') return 'warn' before the
final return) so that 'business-flex' maps to 'warn' while keeping
enterprise-webapp => 'blocking', lean-mvp => 'warn', and all others =>
'approval'.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/core/harness/readiness.js`:
- Around line 15-20: The readinessModeForProfile function currently defaults all
non-enterprise-webapp profiles to 'approval', which misclassifies
'business-flex'; update readinessModeForProfile to explicitly return 'warn' for
the 'business-flex' profile (e.g., add an if (name === 'business-flex') return
'warn' before the final return) so that 'business-flex' maps to 'warn' while
keeping enterprise-webapp => 'blocking', lean-mvp => 'warn', and all others =>
'approval'.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9a183296-a378-4c17-a3e4-3d9a5775be67

📥 Commits

Reviewing files that changed from the base of the PR and between ce439b5 and 75cc161.

📒 Files selected for processing (6)
  • README.md
  • bin/rstack-agents.js
  • src/core/harness/decisions.js
  • src/core/harness/readiness.js
  • src/integrations/pi/rstack-sdlc.ts
  • tests/decisions-readiness.test.js
🚧 Files skipped from review as they are similar to previous changes (4)
  • README.md
  • bin/rstack-agents.js
  • src/integrations/pi/rstack-sdlc.ts
  • src/core/harness/decisions.js

@richard-devbot richard-devbot merged commit 7a8ddc9 into main Jun 12, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Roadmap: Add RStack Decision Queue and Definition-of-Ready readiness gate

2 participants