Skip to content

fix: scope ambient events + destructive approvals to the session run; init --fresh (#98, #99)#100

Open
richardsongunde wants to merge 3 commits into
richard-devbot:mainfrom
richardsongunde:fix/session-run-scoping-98-99
Open

fix: scope ambient events + destructive approvals to the session run; init --fresh (#98, #99)#100
richardsongunde wants to merge 3 commits into
richard-devbot:mainfrom
richardsongunde:fix/session-run-scoping-98-99

Conversation

@richardsongunde

@richardsongunde richardsongunde commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Changes

  • src/integrations/pi/rstack-sdlc.ts: new module-scoped sessionRunId set by sdlc_start. Ambient hooks, destructiveApprovalExists(), and delegate model-escalation now use sessionRun() instead of latestRun(). With no session-owned run they stay silent rather than polluting an old run. Explicit run_id parameters on user-facing tools (sdlc_status, sdlc_rollback, …) are unchanged.
  • src/integrations/init.js: prior-run count in the init report; --fresh archival (non-destructive, rename into .rstack/archive/<ts>/).
  • bin/rstack-agents.js: --fresh CLI flag.
  • tests/integrations-init.test.js: 3 new subtests (prior-run reporting, --fresh archival preserves everything, --fresh on a clean project).
  • README.md: documents adoption behavior + --fresh.

Design note

Session adoption happens only on sdlc_start. A session that resumes an old run via explicit run_id params still writes its stage events (those use explicit appendEvent calls), but ambient hook logging stays off — accurate attribution was preferred over completeness. Follow-up could add an explicit sdlc_resume-style adoption.

Validation

  • npx tsx --test tests/integrations-init.test.js — 18 pass, 0 fail (3 new)
  • npm test — same 7 pre-existing Windows-environment failures as unmodified main (ESM file:// dynamic-import in tests, operator-bridge spawn, memory path); zero new failures
  • npm run lint — clean
  • npm run validate — all 196 agents pass
  • node bin/rstack-agents.js init --help shows --fresh

Real-world repro this fixes

On a workspace with a run from 2026-06-01 (manifest stuck STARTED), a session on 2026-06-09 appended its bash tool calls and even its own sdlc_start invocation into the June 1 run's events.jsonl before the new run folder existed — polluting Run Analytics and the Business Hub timeline for both runs.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added --fresh flag to init command to archive existing state and start with a clean configuration.
    • Introduced session-scoped run tracking to prevent using stale runs during operations.
  • Bug Fixes

    • Fixed line-ending normalization for consistent cross-platform behavior.
  • Documentation

    • Added documentation for --fresh flag usage.
  • Tests

    • Added integration tests for init workflow with prior state.

richardsongunde and others added 3 commits June 9, 2026 15:36
parseFrontmatter (CLI + tests) split on \n and matched lines with a
line-anchored regex; '.' does not match \r and '$' does not precede a
trailing \r, so on CRLF checkouts every field line failed to parse and
all 196 agents reported missing name/description. Normalize CRLF/CR
before parsing. Add .gitattributes to keep text assets LF on checkout.

Fixes: validate (196/196 pass), test 'valid frontmatter' + 'valid unique
Pi skill names' now pass.

Closes #1
…n; add init --fresh

Fixes richard-devbot#98: tool_call/tool_result/session_shutdown hooks and
destructiveApprovalExists resolved the target run via latestRun(),
which returns whatever run directory sorts last — so a new session
appended its events into a stale run from days earlier, and a
destructive-action approval granted in an old run silently authorized
destructive commands in a new session. Ambient hooks, approval checks,
and model escalation now use a session-scoped run set by sdlc_start;
with no session run they stay silent instead of polluting old runs.

Fixes richard-devbot#99: init on an existing .rstack/ now reports how many prior
runs it adopted, and a new --fresh flag archives runs, approvals,
memory, registry, and config to .rstack/archive/<timestamp>/ (nothing
deleted) before initializing clean. Documented in README.

Tests: 3 new init subtests (prior-run reporting, --fresh archival,
--fresh on clean project). npm test shows the same 7 pre-existing
Windows-environment failures as unmodified main; lint and validate
pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@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 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR extends SDLC reliability through cross-platform line-ending normalization in validation, adds a --fresh flag to init for non-destructive state archiving, wires the feature through the CLI and tests, and introduces session-scoped run tracking in SDLC to prevent event routing from using stale runs.

Changes

SDLC Reliability and Init Workflow

Layer / File(s) Summary
Line-ending normalization in validation
.gitattributes, src/commands/validate.js, tests/validate-agents.test.js, tests/validate-markdown-frontmatter.test.js
Git attributes and validation code normalize Windows/macOS line endings (CRLF, CR) to LF before parsing frontmatter and detecting delimiters, ensuring consistent behavior across checkout environments.
Init fresh feature with archiving
bin/rstack-agents.js, src/integrations/init.js, README.md, tests/integrations-init.test.js
Adds --fresh flag to init command that archives existing .rstack/runs, approvals.jsonl, registry.json, and rstack.config.json to a timestamped .rstack/archive/ directory, recreates .rstack/runs/, and reports prior run count when state already exists.
Session-scoped run tracking in SDLC
src/integrations/pi/rstack-sdlc.ts
Introduces sessionRunId module variable and sessionRun() helper to restrict event logging and approval checks to the current SDLC session's run instead of the latest run on disk, preventing stale-run event bleed.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • #99: Implements the requested init UX with fresh option and run-counting/archiving behavior in src/integrations/init.js, wired through CLI and tested.
  • #98: Adds session-scoped run tracking to replace latestRun() calls in SDLC event logging and approval checks, directly addressing stale-run bleed across sessions.

Poem

🐰 Fresh starts and sessions clean,
With line-break harmony serene,
Archive state, no data lost,
Just platform paths we'll safely crossed.
A rabbit's init, swift and lean! 🎯

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.67% 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 accurately describes the main changes: fixing session-scoped ambient events/destructive approvals and adding the init --fresh feature with issue references.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@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: 2

🧹 Nitpick comments (1)
tests/integrations-init.test.js (1)

176-201: ⚡ Quick win

Consider expanding test coverage to validate archival of all ARCHIVABLE_STATE entries.

The test currently verifies archival of runs, approvals.jsonl, and rstack.config.json, but ARCHIVABLE_STATE also includes memory, registry, and budget.json. While not critical for the current PR (the core archival mechanism is validated), adding assertions for these entries would strengthen confidence that the feature works comprehensively.

🤖 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 `@tests/integrations-init.test.js` around lines 176 - 201, The test "init
--fresh archives prior state non-destructively and starts clean" currently
checks runs, approvals.jsonl, and rstack.config.json but misses other
ARCHIVABLE_STATE entries; update the test (the async function with tmpProject
root and call to initFramework(root, 'custom', { profile: 'lean-mvp', fresh:
true })) to create representative files for memory, registry, and budget.json
under root/.rstack before init, then after init assert those files no longer
exist in root and assert they exist under join(archiveRoot, stamp, ...) (use the
same archiveRoot and stamp variables already in the test) to validate archival
of each ARCHIVABLE_STATE entry.
🤖 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 `@README.md`:
- Around line 22-26: Update the README snippet to accurately list all items
moved during a fresh init by matching the implementation constant
ARCHIVABLE_STATE in src/integrations/init.js: include "runs, approvals,
registry, config, memory, budget.json" (or "memory and budget.json") in the
archival description so the docs reflect the actual archived state.

In `@src/commands/validate.js`:
- Around line 27-33: Both other frontmatter parsers must apply the same CRLF/CR
normalization as validate.js: in the parseFrontmatter function in
src/commands/list.js replace direct uses of content.indexOf and content.slice
with a normalized string (e.g., const normalized = content.replace(/\r\n?/g,
'\n')) and then use normalized.startsWith, normalized.indexOf('\n---', 3) and
normalized.slice(...) so fences with \r\n are detected; likewise in
src/integrations/pi/rstack-sdlc.ts normalize the raw input before calling
raw.indexOf("\n---", 3) (use a normalized variable and use
normalized.indexOf(...) and normalized.slice(...)) so all frontmatter searches
consistently operate on CRLF-normalized text.

---

Nitpick comments:
In `@tests/integrations-init.test.js`:
- Around line 176-201: The test "init --fresh archives prior state
non-destructively and starts clean" currently checks runs, approvals.jsonl, and
rstack.config.json but misses other ARCHIVABLE_STATE entries; update the test
(the async function with tmpProject root and call to initFramework(root,
'custom', { profile: 'lean-mvp', fresh: true })) to create representative files
for memory, registry, and budget.json under root/.rstack before init, then after
init assert those files no longer exist in root and assert they exist under
join(archiveRoot, stamp, ...) (use the same archiveRoot and stamp variables
already in the test) to validate archival of each ARCHIVABLE_STATE entry.
🪄 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: a21547e9-53b1-43d7-a226-5a2d3b09c07a

📥 Commits

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

📒 Files selected for processing (9)
  • .gitattributes
  • README.md
  • bin/rstack-agents.js
  • src/commands/validate.js
  • src/integrations/init.js
  • src/integrations/pi/rstack-sdlc.ts
  • tests/integrations-init.test.js
  • tests/validate-agents.test.js
  • tests/validate-markdown-frontmatter.test.js

Comment thread README.md
Comment on lines +22 to +26
If `.rstack/` already exists, `init` adopts it and preserves all prior runs — it reports how many it found. To start clean instead, archive the old state (nothing is deleted):

```bash
npx rstack-agents init --fresh # moves runs, approvals, registry, config to .rstack/archive/<timestamp>/
```

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Documentation is incomplete.

Line 26 lists archived items as "runs, approvals, registry, config" but the implementation (ARCHIVABLE_STATE in src/integrations/init.js:49) also archives memory and budget.json. Update the comment to include all archived items for accuracy.

📝 Suggested fix
-npx rstack-agents init --fresh   # moves runs, approvals, registry, config to .rstack/archive/<timestamp>/
+npx rstack-agents init --fresh   # moves runs, approvals, memory, registry, config, budget to .rstack/archive/<timestamp>/
🤖 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 `@README.md` around lines 22 - 26, Update the README snippet to accurately list
all items moved during a fresh init by matching the implementation constant
ARCHIVABLE_STATE in src/integrations/init.js: include "runs, approvals,
registry, config, memory, budget.json" (or "memory and budget.json") in the
archival description so the docs reflect the actual archived state.

Comment thread src/commands/validate.js
Comment on lines 27 to +33
function parseFrontmatter(content) {
if (!content.startsWith('---')) return null;
const end = content.indexOf('\n---', 3);
// Normalize CRLF/CR so the line-anchored regex below matches on Windows checkouts.
const normalized = content.replace(/\r\n?/g, '\n');
if (!normalized.startsWith('---')) return null;
const end = normalized.indexOf('\n---', 3);
if (end === -1) return null;
const block = content.slice(3, end);
const block = normalized.slice(3, end);

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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find all parseFrontmatter and frontmatterBlock implementations
echo "=== Searching for frontmatter parsing functions ==="
rg -n -A 5 'function (parseFrontmatter|frontmatterBlock)' --type js --type ts

echo -e "\n=== Checking which implementations normalize CRLF/CR ==="
rg -n 'replace\(/\\r\\n\?/g' --type js --type ts

echo -e "\n=== Finding delimiter searches that may fail on CRLF ==="
rg -n "indexOf\(['\"]\\\\n---['\"]" --type js --type ts

Repository: richard-devbot/SDLC-rstack

Length of output: 3394


Apply CRLF normalization consistently to all frontmatter parsers.

  • src/commands/list.js: parseFrontmatter does not normalize CRLF/CR (still uses content.indexOf('\n---', 3)), unlike src/commands/validate.js.
  • src/integrations/pi/rstack-sdlc.ts: parseFrontmatter also lacks CRLF normalization and searches for raw.indexOf("\n---", 3) (plus another raw.indexOf("\n---", 3) around line ~758), which will fail when fences use \r\n.
🤖 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/commands/validate.js` around lines 27 - 33, Both other frontmatter
parsers must apply the same CRLF/CR normalization as validate.js: in the
parseFrontmatter function in src/commands/list.js replace direct uses of
content.indexOf and content.slice with a normalized string (e.g., const
normalized = content.replace(/\r\n?/g, '\n')) and then use
normalized.startsWith, normalized.indexOf('\n---', 3) and normalized.slice(...)
so fences with \r\n are detected; likewise in src/integrations/pi/rstack-sdlc.ts
normalize the raw input before calling raw.indexOf("\n---", 3) (use a normalized
variable and use normalized.indexOf(...) and normalized.slice(...)) so all
frontmatter searches consistently operate on CRLF-normalized text.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant