fix(session-context): add --root flag for opencode's true parent session#444
fix(session-context): add --root flag for opencode's true parent session#444rhuanbarreto wants to merge 1 commit into
Conversation
`archgate session-context opencode --skip 1` guessed the parent session by recency (the second most-recently-updated session sharing a project directory). That heuristic breaks in two ways specific to opencode: - The `Skill` tool executes inline in the current session — it creates no new session row, so "skip past my own session" instead skips past the actual parent. - Sibling sub-agent sessions fanned out from the same parent (e.g. the archgate:reviewer skill's parallel domain reviews) interleave with the parent in recency order, so `--skip 1` can land on any sibling. Verified against a real opencode database: a live archgate-lessons-learned run's `--skip 1` call returned the "General process ADR review" sub-agent's private transcript instead of the parent conversation. `--root` resolves the true top-level session directly via opencode's `session.parent_id` column instead of guessing by recency, and is correct regardless of nesting depth or sibling count. Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
📝 WalkthroughWalkthroughThis PR adds a Changes
Sequence Diagram(s)sequenceDiagram
participant CLI as opencode.ts
participant Helper as readOpencodeSession
participant DB as SQLite session table
CLI->>Helper: readOpencodeSession(projectRoot, { root: opts.root, skip })
Helper->>DB: query sessions incl. parent_id
DB-->>Helper: session rows
alt root is true
Helper->>Helper: filter candidates to parent_id IS NULL
end
Helper->>Helper: select target by sessionId or skip index
alt target found
Helper-->>CLI: session transcript
else target missing
Helper-->>CLI: error with scoped available list
end
Related Issues: None referenced. Related PRs: None referenced. Suggested labels: documentation, enhancement, cli Suggested reviewers: archgate-developer 🎀 A session that hides among its siblings, 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 7495901. Configure here.
| const target = options?.sessionId | ||
| ? matching.find((s) => s.id === options.sessionId) | ||
| : matching[skip]; | ||
| : candidates[skip]; |
There was a problem hiding this comment.
Wrong root with multiple conversations
Medium Severity
With --root (default --skip 0), selection uses the most recently updated row among all parent_id IS NULL sessions for the project, not the root of the active session tree. When two or more top-level sessions share the directory and the latest activity is a child of an older root, --root can return a different root’s transcript than the sub-agent or inline skill intended.
Reviewed by Cursor Bugbot for commit 7495901. Configure here.
Deploying archgate-cli with
|
| Latest commit: |
7495901
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://825ba74f.archgate-cli.pages.dev |
| Branch Preview URL: | https://fix-opencode-session-context-ra7h.archgate-cli.pages.dev |
Code Coverage
Full HTML report available in workflow artifacts. Per-directory breakdown
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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/helpers/session-context-opencode.ts`:
- Around line 127-154: The `available` result in
`src/helpers/session-context-opencode.ts` is using the wrong scope when
`options.sessionId` is set alongside `root`. Update the failure path in the
session selection logic so the `available` list matches the actual lookup set
used by the `target` resolution in that branch (`matching` for `sessionId`,
`candidates` for skip-based selection), and keep the scope label/error text
aligned with that choice in the `rootOnly`/`skip` handling.
In `@tests/helpers/session-context-opencode-root.test.ts`:
- Around line 109-126: The control assertion in readOpencodeSession test is
being skipped if skipped.ok is false, so the “before” case can silently no-op
and still let the test pass. Update the test case in test("root reads the true
parent even when sibling sub-agents are more recent") to assert skipped.ok
before checking skipped.data.sessionId, using the existing readOpencodeSession
result variable so the precondition is always enforced.
🪄 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: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 2ce22edf-0236-4c4d-99c4-9d286da997d8
📒 Files selected for processing (11)
.claude/agent-memory/archgate-developer/MEMORY.md.claude/agent-memory/archgate-developer/project_plugins_generated_skill_files.md.claude/agent-memory/archgate-developer/project_session_context_skip_root_fix.mddocs/public/llms-full.txtdocs/src/content/docs/nb/reference/cli/session-context.mdxdocs/src/content/docs/pt-br/reference/cli/session-context.mdxdocs/src/content/docs/reference/cli/session-context.mdxsrc/commands/session-context/opencode.tssrc/helpers/session-context-opencode.tstests/commands/session-context/opencode.test.tstests/helpers/session-context-opencode-root.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (6)
- GitHub Check: Cursor Bugbot
- GitHub Check: Smoke Test (Windows) / Windows
- GitHub Check: Lint, Test & Check
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Analyze (csharp)
- GitHub Check: Cloudflare Pages
⚠️ CI failures not shown inline (2)
GitHub Actions: DCO / DCO Sign-off Check: fix(session-context): add --root flag for opencode's true parent session
Conclusion: failure
##[group]Run base="a53b30507a139db915271095a5a29ccefbd7cecf"
�[36;1mbase="a53b30507a139db915271095a5a29ccefbd7cecf"�[0m
�[36;1mhead="749590188c8a687339fdf67e366565cf266710f2"�[0m
�[36;1mfailed=0�[0m
�[36;1m�[0m
�[36;1mfor sha in $(git rev-list --no-merges "$base".."$head"); do�[0m
�[36;1m if ! git log -1 --format='%B' "$sha" | grep -qiE '^Signed-off-by: .+ <.+>'; then�[0m
�[36;1m echo "::error::Commit $sha is missing a DCO Signed-off-by line."�[0m
GitHub Actions: DCO / 0_DCO Sign-off Check.txt: fix(session-context): add --root flag for opencode's true parent session
Conclusion: failure
##[group]Run base="a53b30507a139db915271095a5a29ccefbd7cecf"
�[36;1mbase="a53b30507a139db915271095a5a29ccefbd7cecf"�[0m
�[36;1mhead="749590188c8a687339fdf67e366565cf266710f2"�[0m
�[36;1mfailed=0�[0m
�[36;1m�[0m
�[36;1mfor sha in $(git rev-list --no-merges "$base".."$head"); do�[0m
�[36;1m if ! git log -1 --format='%B' "$sha" | grep -qiE '^Signed-off-by: .+ <.+>'; then�[0m
�[36;1m echo "::error::Commit $sha is missing a DCO Signed-off-by line."�[0m
🧰 Additional context used
📓 Path-based instructions (22)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use TypeScript strict mode, ESNext target, and ES modules throughout
UsesafeParse()when validating with Zod schemas
Use minimal dependencies; prefer Bun built-ins per ARCH-006
Files:
tests/helpers/session-context-opencode-root.test.tssrc/commands/session-context/opencode.tstests/commands/session-context/opencode.test.tssrc/helpers/session-context-opencode.ts
tests/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Test files must mirror
src/directory structure with fixtures intests/fixtures/
Files:
tests/helpers/session-context-opencode-root.test.tstests/commands/session-context/opencode.test.ts
**/*.{ts,tsx,json}
📄 CodeRabbit inference engine (CLAUDE.md)
Runtime must use Bun (>=1.2.21), not Node.js compatible
Files:
tests/helpers/session-context-opencode-root.test.tssrc/commands/session-context/opencode.tstests/commands/session-context/opencode.test.tssrc/helpers/session-context-opencode.ts
tests/**/*.test.ts
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-005-testing-standards.md)
tests/**/*.test.ts: Use Bun's built-in test runner (bun:test) for all test files, and place tests undertests/mirroring thesrc/directory structure with<module-name>.test.tsnaming.
Use temp directories (mkdtemp) for filesystem tests, and clean them up inafterEachorafterAll.
Close external SDK instances (servers, clients, transports, connections) inafterEachorafterAll, managing their lifecycle in hooks rather than inside individual test bodies.
When a test creates a temporary git repository and runsgit commit, configure localuser.emailanduser.nameimmediately aftergit init.
Test public module interfaces, not private implementation details.
Use descriptive test names that explain the expected behavior.
Every runnabletest()/it()must contain at least oneexpect()assertion; smoke tests must make the contract explicit withexpect(() => fn()).not.toThrow()orawait expect(promise).resolves.toBeUndefined().
Usetest.skip,test.skipIf, ortest.todofor intentionally empty or disabled tests; do not use barereturnor empty callbacks to skip work.
If the firstexpect()is being added to a previously assertion-less test file, addexpectto thebun:testimport.
When mockingfetchin tests, assign directly toglobalThis.fetchand restore the original or usemock.restore()afterward.
WrapspyOn()and inlinemockImplementation()usage intry/finally, or create and restore spies in hooks, somockRestore()always runs.
Only raise a per-test timeout above the globalbun test --timeout 60000; never set a shorter per-test timeout.
Mock first-party modules withimport * as modplusspyOn(mod, "fn"), notmock.module().
When a test needs to redirect user-scope paths, mockos.homedir()instead of relying onHOME/Bun.env.HOME; restore the spy in test hooks.
Do not depend on network access in unit tests.
Do not leave temp files after test runs.
Do not leave external SDK instances open after tests...
Files:
tests/helpers/session-context-opencode-root.test.tstests/commands/session-context/opencode.test.ts
**/*.{ts,tsx,js,jsx,mjs,cjs}
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-006-dependency-policy.md)
**/*.{ts,tsx,js,jsx,mjs,cjs}: Use Bun built-ins for file I/O (Bun.file,Bun.write), HTTP, subprocess execution (Bun.spawn), globbing (Bun.Glob), and testing (bun:test).
Do not use Node.js-specific APIs when Bun alternatives exist; for example, useBun.file()instead offs.readFile()for simple reads.
Prefernode:built-in modules such asnode:util,node:path, andnode:fsover npm alternatives.
Do not use utility libraries for single functions, such as importing lodash for one helper likepick.
Do not use path aliases (tsconfig paths); use relative imports with Bun's native module resolution.
Do not install packages globally during development; usebunxfor one-off tools.
Files:
tests/helpers/session-context-opencode-root.test.tssrc/commands/session-context/opencode.tstests/commands/session-context/opencode.test.tssrc/helpers/session-context-opencode.ts
tests/**/*.ts
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-009-platform-detection-helper.md)
In test files, use
_resetPlatformCache()to simulate different platforms instead of mocking or mutatingprocess.platformdirectly.
Files:
tests/helpers/session-context-opencode-root.test.tstests/commands/session-context/opencode.test.ts
{src,tests}/**/*.ts
📄 CodeRabbit inference engine (.archgate/adrs/LEGAL-001-spdx-license-headers.md)
{src,tests}/**/*.ts: Every TypeScript source file insrc/andtests/must begin with// SPDX-License-Identifier: Apache-2.0followed by// Copyright 2026 Archgate.
If a TypeScript file has a shebang line (for example#!/usr/bin/env buninsrc/cli.ts), the SPDX license header must appear immediately after the shebang.
Use single-line//comments for the SPDX header; do not use block comments (/* */) or alternate license identifiers.
Files:
tests/helpers/session-context-opencode-root.test.tssrc/commands/session-context/opencode.tstests/commands/session-context/opencode.test.tssrc/helpers/session-context-opencode.ts
**
⚙️ CodeRabbit configuration file
**: # CLAUDE.mdArchgate is a CLI tool for AI governance via Architecture Decision Records (ADRs) — combining human-readable docs with machine-checkable rules. The CLI dogfoods itself via ADRs in
.archgate/adrs/. AI features are delivered as a Claude Code plugin (../plugins/claude-code), not via direct API calls.Technology Stack
- Runtime: Bun (>=1.2.21) — not Node.js compatible
- Language: TypeScript (strict mode, ESNext, ES modules)
- CLI framework: Commander.js (
@commander-js/extra-typings)- Linter: Oxlint | Formatter: Oxfmt | Dead exports: Knip | Commits: Conventional Commits
Commands
bun run src/cli.ts <command> # run CLI locally bun run lint # oxlint bun run typecheck # tsc --build bun run format # oxfmt --write bun run format:check # oxfmt --check bun run test # all tests (not bare `bun test` — picks up --timeout; see GEN-003) bun run knip # dead export detection bun run validate # MANDATORY: lint + typecheck + format + test + ADR check + knip + build check bun run build:check # verify build compiles (CI builds binaries via release workflow) bun run commit # conventional commit wizardValidation Gate
bun run validatemust pass before any task is considered complete. Fail-fast pipeline: lint → typecheck → format → test → ADR check → knip → build check. Mirrors CI in.github/workflows/code-pull-request.yml.Git Hooks (Git 2.54+)
Config-based hooks in
.githooksrun validation locally before commits and pushes:
- pre-commit: lint + typecheck + format:check (~15s)
- pre-push: full
bun run validate(~60s, mirrors CI)Activate once per clone:
git config --local include.path ../.githooksOpt out of a specific hook:
git config --local hook.<name>.enabled false. Skip all hooks for a single commit:git commit --no-verify.GitHub Act...
Files:
tests/helpers/session-context-opencode-root.test.tsdocs/src/content/docs/nb/reference/cli/session-context.mdxsrc/commands/session-context/opencode.tstests/commands/session-context/opencode.test.tsdocs/public/llms-full.txtdocs/src/content/docs/pt-br/reference/cli/session-context.mdxsrc/helpers/session-context-opencode.tsdocs/src/content/docs/reference/cli/session-context.mdx
⚙️ CodeRabbit configuration file
**: This project uses Archgate — an AI governance framework based on
Architecture Decision Records (ADRs). The ADRs in.archgate/adrs/
are the authoritative rules for this codebase. Each ADR has a companion
.rules.tsfile with automated checks that run viaarchgate check.When reviewing, you must:
- Treat ADR violations as blocking issues, not suggestions.
- Cite the specific ADR ID when flagging a violation (e.g., "Violates ARCH-006").
- Focus on semantic and contextual violations that automated rules cannot catch —
the.rules.tsfiles already cover syntactic/structural patterns.- If you are unsure whether something violates an ADR, flag it as a question
rather than approving it.
Files:
tests/helpers/session-context-opencode-root.test.tsdocs/src/content/docs/nb/reference/cli/session-context.mdxsrc/commands/session-context/opencode.tstests/commands/session-context/opencode.test.tsdocs/public/llms-full.txtdocs/src/content/docs/pt-br/reference/cli/session-context.mdxsrc/helpers/session-context-opencode.tsdocs/src/content/docs/reference/cli/session-context.mdx
docs/src/content/docs/**/*.mdx
📄 CodeRabbit inference engine (.archgate/adrs/GEN-001-documentation-site.md)
docs/src/content/docs/**/*.mdx: Use MDX (.mdx) for all content pages underdocs/src/content/docs/.
Escape literal curly braces in MDX when showing template syntax; do not use bare{}in prose or code-fence labels.
Files:
docs/src/content/docs/nb/reference/cli/session-context.mdxdocs/src/content/docs/pt-br/reference/cli/session-context.mdxdocs/src/content/docs/reference/cli/session-context.mdx
{docs/src/content/docs/**/*.mdx,docs/astro.config.mjs}
📄 CodeRabbit inference engine (.archgate/adrs/GEN-001-documentation-site.md)
When adding a new documentation page, create the MDX file in
docs/src/content/docs/<category>/<slug>.mdxand add the page todocs/astro.config.mjssidebar configuration.
Files:
docs/src/content/docs/nb/reference/cli/session-context.mdxdocs/src/content/docs/pt-br/reference/cli/session-context.mdxdocs/src/content/docs/reference/cli/session-context.mdx
docs/src/content/docs/**
📄 CodeRabbit inference engine (.archgate/adrs/GEN-001-documentation-site.md)
Do not create documentation content files outside
docs/src/content/docs/; Starlight content must live in that exact directory structure.
Files:
docs/src/content/docs/nb/reference/cli/session-context.mdxdocs/src/content/docs/pt-br/reference/cli/session-context.mdxdocs/src/content/docs/reference/cli/session-context.mdx
docs/src/content/docs/**/*.{mdx,md}
📄 CodeRabbit inference engine (.archgate/adrs/GEN-002-docs-i18n.md)
docs/src/content/docs/**/*.{mdx,md}: For every English docs page underdocs/src/content/docs/, create a translated file in each locale directory with the exact same relative path and filename, and ensure every locale file corresponds to an existing root file (no orphan translations).
When adding or modifying English documentation content, update the corresponding locale files in the same pull request.
Translate all user-facing prose in docs pages, including titles, descriptions, headings, paragraphs, list items, table text, and admonition content.
Translate user-visible text props in Starlight components such as<Card title="...">and<LinkCard description="...">.
Keep code blocks, CLI commands, file paths, TypeScript identifiers, technical terms, import statements, component names, andlink/href/slugattribute values in English.
Keep internal links unchanged and do not add locale prefixes (for example, use/guides/...rather than/pt-br/guides/...).
Preserve MDX curly-brace escaping (\{\}) in translated content.
Preserve Starlight component import statements identically in translated files.
Files:
docs/src/content/docs/nb/reference/cli/session-context.mdxdocs/src/content/docs/pt-br/reference/cli/session-context.mdxdocs/src/content/docs/reference/cli/session-context.mdx
docs/src/content/docs/nb/**/*.{mdx,md}
📄 CodeRabbit inference engine (.archgate/adrs/GEN-002-docs-i18n.md)
Use Norwegian Bokmål (not Nynorsk) for Norwegian translations, with the informal
duform and correct Norwegian characters (æ,ø,å).
Files:
docs/src/content/docs/nb/reference/cli/session-context.mdx
src/commands/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
src/commands/**/*.ts: Commands must exportregister*Command(program)function that handles I/O only, with no business logic
UsestyleText()fromnode:utilfor output formatting; support--jsonflag for machine-readable output and auto-compact JSON in non-TTY/non-CI contexts
Exit with code 0 for success, 1 for violations, 2 for internal errors, and 130 for user cancellation (SIGINT)
src/commands/**/*.ts: Each command module undersrc/commands/must export aregister*Command(program)function, with one command per file (or one command group viaindex.ts).
Command files must be thin: they should only parse arguments, call engine/helpers, and format output; business logic must live insrc/engine/,src/helpers/, orsrc/formats/.
Command modules must not useexecutableDir()for command discovery, must not call.parse()themselves, and must not spawn child processes for subcommand execution.
Subcommand groups should live in nested directories with anindex.tsthat composes child commands (for example,src/commands/adr/index.ts).
src/commands/**/*.ts: In command files undersrc/commands/**/*.ts, options that need type narrowing beyond plain strings MUST usenew Option()with.addOption()instead of plain.option().
Insrc/commands/**/*.ts, importOptionfrom@commander-js/extra-typingsalongsideCommandso typed options get full type inference.
Insrc/commands/**/*.ts, enum-like options with a fixed set of allowed values must use.choices()on anOptionto provide runtime validation and compile-time type narrowing.
Insrc/commands/**/*.ts, options that require type conversion must use.argParser()on anOptionobject, not a parser function passed as the third argument to.option().
Insrc/commands/**/*.ts, register typed options with.addOption()rather than.option().
Insrc/commands/**/*.ts, pass both the choices array and default values withas constwhen using.choices()and.default()to pr...
Files:
src/commands/session-context/opencode.ts
src/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
AI features must be delivered as a Claude Code plugin (
../plugins/claude-code), not via direct API calls
src/**/*.ts: UselogError()fromsrc/helpers/log.tsfor user-facing errors instead of printing errors directly.
Exit with code1for expected failures such as missing config, invalid input, or validation violations.
Let unexpected errors crash naturally so they are treated as internal errors with exit code2.
Include actionable suggestions in user-facing error messages whenever possible.
Write user-facing errors to stderr vialogError(); do not send them to stdout.
WhenfindProjectRoot()returnsnullfor commands that do not require.archgate/, fall back toprocess.cwd()as the path key or working directory.
CatchExitPromptErrorfrom Inquirer in the top-level error boundary and treat it as user cancellation: exit with code130without logging an error or sending it to Sentry.
Do not useconsole.error()directly; uselogError()for consistent formatting.
Do not exit with codes other than0,1,2, or130.
Do not send user-cancellation errors such as InquirerExitPromptErrorto Sentry; filter them inbeforeSend.
src/**/*.ts: UsestyleText(format, text)fromnode:utilfor all terminal colors and formatting in CLI source files; do not use raw ANSI escape codes or third-party color libraries.
Commands that produce structured results and support--jsonmust emit machine-readable JSON to stdout with no colors or decorative formatting.
When a command supports--json, useformatJSON()fromsrc/helpers/output.tsfor JSON serialization, and passforcePretty: truewhen the user explicitly provided--json.
UseisAgentContext()fromsrc/helpers/output.tsto enable auto-JSON behavior for commands that support both human-readable and JSON output modes.
CLI output must not include emoji; use text symbols and colors instead.
Send normal command output to stdout withconsole.log(), and send errors, warnings, and de...
Files:
src/commands/session-context/opencode.tssrc/helpers/session-context-opencode.ts
src/**/!(*platform).ts
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-009-platform-detection-helper.md)
src/**/!(*platform).ts: Insrc/TypeScript source files, do not readprocess.platformdirectly; usesrc/helpers/platform.ts(isWindows(),isMacOS(),isLinux(),isWSL(),getPlatformInfo()) for all platform detection.
Use the centralized platform helper instead of duplicating OS/WSL detection logic inline anywhere insrc/TypeScript source.
When behavior differs between Linux and Windows, account for WSL by usingisWSL()rather than assuming `
Files:
src/commands/session-context/opencode.tssrc/helpers/session-context-opencode.ts
docs/src/content/docs/pt-br/**/*.{mdx,md}
📄 CodeRabbit inference engine (.archgate/adrs/GEN-002-docs-i18n.md)
Use correct Portuguese diacritical marks in Brazilian Portuguese translations; never write unaccented Portuguese.
Files:
docs/src/content/docs/pt-br/reference/cli/session-context.mdx
src/{helpers,engine}/**/*.ts
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-002-error-handling.md)
Do not use
console.log(),console.warn(), orconsole.info()directly in helper or engine files; uselogInfo()orlogWarn()instead. Command files are exempt because they are the I/O layer.
Files:
src/helpers/session-context-opencode.ts
docs/src/content/docs/reference/cli/!(index).mdx
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-015-cli-command-documentation-coverage.md)
Every top-level CLI command must have a corresponding reference page at
docs/src/content/docs/reference/cli/<name>.mdx, and every non-index.mdxpage in that directory must correspond to exactly one top-level command.
Files:
docs/src/content/docs/reference/cli/session-context.mdx
docs/src/content/docs/reference/cli/*.mdx
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-015-cli-command-documentation-coverage.md)
Do not create separate reference pages for subcommands; subcommand documentation must stay inline within the parent command’s
.mdxpage.Every parent CLI reference page in
docs/src/content/docs/reference/cli/must contain headings for its direct subcommands using the exactarchgate <parent> <sub>format, and must not keep headings for subcommands that no longer exist.
Files:
docs/src/content/docs/reference/cli/session-context.mdx
{src/commands/{*.ts,*/index.ts},docs/src/content/docs/reference/cli/!(index).mdx}
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-015-cli-command-documentation-coverage.md)
{src/commands/{*.ts,*/index.ts},docs/src/content/docs/reference/cli/!(index).mdx}: When a top-level command is added, its docs page must be added in the same change; when a top-level command is removed, its matching docs page must be deleted in the same change.
Keep command and documentation stems aligned when renaming a top-level command file; renamingsrc/commands/<name>.tsorsrc/commands/<name>/index.tsmust be paired with renamingdocs/src/content/docs/reference/cli/<name>.mdx.
Files:
docs/src/content/docs/reference/cli/session-context.mdx
docs/src/content/docs/reference/**/*.mdx
📄 CodeRabbit inference engine (.archgate/adrs/GEN-001-documentation-site.md)
Keep reference pages accurate to the CLI source code; when CLI APIs change, update the corresponding reference docs in the same PR.
Files:
docs/src/content/docs/reference/cli/session-context.mdx
🧠 Learnings (1)
📚 Learning: 2026-06-11T12:50:28.661Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 406
File: .claude/agent-memory/archgate-developer/feedback_prefer_tests_over_adr_rules.md:8-18
Timestamp: 2026-06-11T12:50:28.661Z
Learning: In `archgate/cli`, for markdown files under `.claude/agent-memory/`, follow the established convention: use YAML frontmatter (with a `name:` field used as the document title) and do not require a top-level `#` (H1) heading. During code review, do not flag missing first-line/first-top-level H1 headings (e.g., MD041) for these agent-memory files since markdownlint is not part of the repo’s `bun run validate` lint pipeline (oxlint/oxfmt only).
Applied to files:
.claude/agent-memory/archgate-developer/project_session_context_skip_root_fix.md.claude/agent-memory/archgate-developer/project_plugins_generated_skill_files.md.claude/agent-memory/archgate-developer/MEMORY.md
🪛 LanguageTool
docs/src/content/docs/pt-br/reference/cli/session-context.mdx
[style] ~65-~65: Evite abreviações de internet. Considere escrever “não” por extenso. Se quis dizer “n”, coloque entre aspas.
Context: ...----------------------------------- | | --max-entries <n> | Máximo de entradas a retornar (padr...
(INTERNET_ABBREVIATIONS)
[style] ~66-~66: Evite abreviações de internet. Considere escrever “não” por extenso. Se quis dizer “n”, coloque entre aspas.
Context: ... | | --skip <n> | Pular as N sessões mais rece...
(INTERNET_ABBREVIATIONS)
[grammar] ~66-~66: Possível erro de concordância de número.
Context: ...ne com --root para pular apenas entre sessões raiz) | ...
(GENERAL_NUMBER_AGREEMENT_ERRORS)
[uncategorized] ~70-~70: Pontuação duplicada
Context: ... de pai/filho entre sessões. Um simples --skip 1 seleciona a segunda sessão mais ...
(DOUBLE_PUNCTUATION_XML)
[uncategorized] ~70-~70: Esta locução deve ser separada por vírgulas.
Context: ...iretório, o que só corresponde à sessão pai quando exatamente uma outra sessão a to...
(VERB_COMMA_CONJUNCTION)
[style] ~70-~70: “uma outra” é um pleonasmo. É preferível dizer “outra”
Context: ...responde à sessão pai quando exatamente uma outra sessão a tocou — um leque de sub-agente...
(PT_REDUNDANCY_REPLACE_UMA_OUTRA)
[uncategorized] ~70-~70: Pontuação duplicada
Context: ...ip 1cair em um irmão não relacionado.--root` resolve diretamente a sessão de ní...
(DOUBLE_PUNCTUATION_XML)
🪛 markdownlint-cli2 (0.22.1)
.claude/agent-memory/archgate-developer/project_session_context_skip_root_fix.md
[warning] 8-8: First line in a file should be a top-level heading
(MD041, first-line-heading, first-line-h1)
.claude/agent-memory/archgate-developer/project_plugins_generated_skill_files.md
[warning] 8-8: First line in a file should be a top-level heading
(MD041, first-line-heading, first-line-h1)
🔇 Additional comments (13)
.claude/agent-memory/archgate-developer/MEMORY.md (1)
83-87: LGTM!.claude/agent-memory/archgate-developer/project_plugins_generated_skill_files.md (1)
1-13: LGTM!.claude/agent-memory/archgate-developer/project_session_context_skip_root_fix.md (1)
1-17: LGTM!docs/public/llms-full.txt (1)
4391-4404: LGTM!Also applies to: 4438-4443
docs/src/content/docs/nb/reference/cli/session-context.mdx (1)
57-70: LGTM!Also applies to: 103-108
docs/src/content/docs/pt-br/reference/cli/session-context.mdx (1)
57-70: LGTM!Also applies to: 103-108
docs/src/content/docs/reference/cli/session-context.mdx (2)
59-70: LGTM!Also applies to: 103-108
57-58: 🎯 Functional Correctness
directoryis the right session-matching field here. The opencode session-row schema usesdirectoryfor project matching.src/helpers/session-context-opencode.ts (2)
26-37: LGTM!
65-70: LGTM!Also applies to: 101-105
src/commands/session-context/opencode.ts (1)
19-19: LGTM!Also applies to: 31-42
tests/commands/session-context/opencode.test.ts (1)
73-80: LGTM!Also applies to: 195-213
tests/helpers/session-context-opencode-root.test.ts (1)
128-156: LGTM!
| // 3. Select session by ID, or by recency within the chosen scope (with | ||
| // optional skip). | ||
| // | ||
| // Opencode records true parent/child linkage via `parent_id` — a | ||
| // sub-agent session (or an inline Skill invocation, which appends to the | ||
| // *current* session row instead of creating a new one) can have any | ||
| // number of sibling sessions sharing this directory. Sorting every | ||
| // matching session by recency and taking the Nth (the plain `--skip` | ||
| // behavior) cannot reliably reach "the top-level session": once more | ||
| // than one sibling exists, whichever sibling was touched most recently | ||
| // occupies that slot, not the parent. `root: true` sidesteps the | ||
| // guesswork by filtering to sessions with no parent (the true root of | ||
| // the tree) before applying `skip`. | ||
| const skip = options?.skip ?? 0; | ||
| const rootOnly = options?.root ?? false; | ||
| const candidates = rootOnly | ||
| ? matching.filter((s) => s.parent_id === null) | ||
| : matching; | ||
| const target = options?.sessionId | ||
| ? matching.find((s) => s.id === options.sessionId) | ||
| : matching[skip]; | ||
| : candidates[skip]; | ||
|
|
||
| if (!target) { | ||
| const scope = rootOnly ? "root session(s)" : "session(s)"; | ||
| const error = options?.sessionId | ||
| ? `Session not found: ${options.sessionId}` | ||
| : `Only ${String(matching.length)} session(s) available but --skip ${String(skip)} requested`; | ||
| return { ok: false, error, available: matching.map((s) => s.id) }; | ||
| : `Only ${String(candidates.length)} ${scope} available but --skip ${String(skip)} requested`; | ||
| return { ok: false, error, available: candidates.map((s) => s.id) }; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
available list is inconsistent with the actual search scope when sessionId + root are combined.
When options.sessionId is set, lookup uses the unscoped matching list (Line 146), but on failure the available field always reports candidates (Line 154), which is root-filtered when root: true. If a user passes --session-id X --root and X exists in matching but isn't a root session, the error will misleadingly omit X from available even though it was actually searched against the full matching set.
🐛 Proposed fix
if (!target) {
const scope = rootOnly ? "root session(s)" : "session(s)";
const error = options?.sessionId
? `Session not found: ${options.sessionId}`
: `Only ${String(candidates.length)} ${scope} available but --skip ${String(skip)} requested`;
- return { ok: false, error, available: candidates.map((s) => s.id) };
+ const searched = options?.sessionId ? matching : candidates;
+ return { ok: false, error, available: searched.map((s) => s.id) };
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 3. Select session by ID, or by recency within the chosen scope (with | |
| // optional skip). | |
| // | |
| // Opencode records true parent/child linkage via `parent_id` — a | |
| // sub-agent session (or an inline Skill invocation, which appends to the | |
| // *current* session row instead of creating a new one) can have any | |
| // number of sibling sessions sharing this directory. Sorting every | |
| // matching session by recency and taking the Nth (the plain `--skip` | |
| // behavior) cannot reliably reach "the top-level session": once more | |
| // than one sibling exists, whichever sibling was touched most recently | |
| // occupies that slot, not the parent. `root: true` sidesteps the | |
| // guesswork by filtering to sessions with no parent (the true root of | |
| // the tree) before applying `skip`. | |
| const skip = options?.skip ?? 0; | |
| const rootOnly = options?.root ?? false; | |
| const candidates = rootOnly | |
| ? matching.filter((s) => s.parent_id === null) | |
| : matching; | |
| const target = options?.sessionId | |
| ? matching.find((s) => s.id === options.sessionId) | |
| : matching[skip]; | |
| : candidates[skip]; | |
| if (!target) { | |
| const scope = rootOnly ? "root session(s)" : "session(s)"; | |
| const error = options?.sessionId | |
| ? `Session not found: ${options.sessionId}` | |
| : `Only ${String(matching.length)} session(s) available but --skip ${String(skip)} requested`; | |
| return { ok: false, error, available: matching.map((s) => s.id) }; | |
| : `Only ${String(candidates.length)} ${scope} available but --skip ${String(skip)} requested`; | |
| return { ok: false, error, available: candidates.map((s) => s.id) }; | |
| if (!target) { | |
| const scope = rootOnly ? "root session(s)" : "session(s)"; | |
| const error = options?.sessionId | |
| ? `Session not found: ${options.sessionId}` | |
| : `Only ${String(candidates.length)} ${scope} available but --skip ${String(skip)} requested`; | |
| const searched = options?.sessionId ? matching : candidates; | |
| return { ok: false, error, available: searched.map((s) => s.id) }; | |
| } |
🤖 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/helpers/session-context-opencode.ts` around lines 127 - 154, The
`available` result in `src/helpers/session-context-opencode.ts` is using the
wrong scope when `options.sessionId` is set alongside `root`. Update the failure
path in the session selection logic so the `available` list matches the actual
lookup set used by the `target` resolution in that branch (`matching` for
`sessionId`, `candidates` for skip-based selection), and keep the scope
label/error text aligned with that choice in the `rootOnly`/`skip` handling.
| test("root reads the true parent even when sibling sub-agents are more recent", async () => { | ||
| // Real-world fan-out: one parent spawns sibling sub-agent sessions | ||
| // against the same directory. Plain --skip 1 lands on whichever sibling | ||
| // sits second in recency order, not the parent — --root fixes this by | ||
| // filtering to parent_id IS NULL first. | ||
| const db = createDb(); | ||
| makeSession(db, "ses_parent", 1000); | ||
| makeSession(db, "ses_sibling_a", 3000, "ses_parent"); | ||
| makeSession(db, "ses_sibling_b", 2000, "ses_parent"); | ||
| db.close(); | ||
|
|
||
| const skipped = await readOpencodeSession(projectRoot, { skip: 1 }); | ||
| if (skipped.ok) expect(skipped.data.sessionId).toBe("ses_sibling_b"); | ||
|
|
||
| const rooted = await readOpencodeSession(projectRoot, { root: true }); | ||
| expect(rooted.ok).toBe(true); | ||
| if (rooted.ok) expect(rooted.data.sessionId).toBe("ses_parent"); | ||
| }); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win
Strengthen the "before" control assertion to avoid silently no-op'ing.
Line 121's expect is guarded by if (skipped.ok); if skipped.ok were unexpectedly false, this comparison — which documents the exact problem --root fixes — silently never runs, while the test still passes via the unconditional assertion on rooted.ok (Line 124).
♻️ Proposed fix
const skipped = await readOpencodeSession(projectRoot, { skip: 1 });
- if (skipped.ok) expect(skipped.data.sessionId).toBe("ses_sibling_b");
+ expect(skipped.ok).toBe(true);
+ if (skipped.ok) expect(skipped.data.sessionId).toBe("ses_sibling_b");📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| test("root reads the true parent even when sibling sub-agents are more recent", async () => { | |
| // Real-world fan-out: one parent spawns sibling sub-agent sessions | |
| // against the same directory. Plain --skip 1 lands on whichever sibling | |
| // sits second in recency order, not the parent — --root fixes this by | |
| // filtering to parent_id IS NULL first. | |
| const db = createDb(); | |
| makeSession(db, "ses_parent", 1000); | |
| makeSession(db, "ses_sibling_a", 3000, "ses_parent"); | |
| makeSession(db, "ses_sibling_b", 2000, "ses_parent"); | |
| db.close(); | |
| const skipped = await readOpencodeSession(projectRoot, { skip: 1 }); | |
| if (skipped.ok) expect(skipped.data.sessionId).toBe("ses_sibling_b"); | |
| const rooted = await readOpencodeSession(projectRoot, { root: true }); | |
| expect(rooted.ok).toBe(true); | |
| if (rooted.ok) expect(rooted.data.sessionId).toBe("ses_parent"); | |
| }); | |
| test("root reads the true parent even when sibling sub-agents are more recent", async () => { | |
| // Real-world fan-out: one parent spawns sibling sub-agent sessions | |
| // against the same directory. Plain --skip 1 lands on whichever sibling | |
| // sits second in recency order, not the parent — --root fixes this by | |
| // filtering to parent_id IS NULL first. | |
| const db = createDb(); | |
| makeSession(db, "ses_parent", 1000); | |
| makeSession(db, "ses_sibling_a", 3000, "ses_parent"); | |
| makeSession(db, "ses_sibling_b", 2000, "ses_parent"); | |
| db.close(); | |
| const skipped = await readOpencodeSession(projectRoot, { skip: 1 }); | |
| expect(skipped.ok).toBe(true); | |
| if (skipped.ok) expect(skipped.data.sessionId).toBe("ses_sibling_b"); | |
| const rooted = await readOpencodeSession(projectRoot, { root: true }); | |
| expect(rooted.ok).toBe(true); | |
| if (rooted.ok) expect(rooted.data.sessionId).toBe("ses_parent"); | |
| }); |
🤖 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/helpers/session-context-opencode-root.test.ts` around lines 109 - 126,
The control assertion in readOpencodeSession test is being skipped if skipped.ok
is false, so the “before” case can silently no-op and still let the test pass.
Update the test case in test("root reads the true parent even when sibling
sub-agents are more recent") to assert skipped.ok before checking
skipped.data.sessionId, using the existing readOpencodeSession result variable
so the precondition is always enforced.
- Port the sibling fan-out regression test from #444, adapted to the new semantics (--skip 1 errors honestly; default and --root resolve the parent) with unconditional control assertions - Enrich the root option JSDoc with the inline-skill / sibling fan-out rationale - Document the multi-top-level-session caveat and deterministic resolution via --session-id <id> --root in the CLI reference (en/nb/pt-br) and regenerate llms-full.txt - pt-br: add the missing "sempre" nuance to the --skip row - Port agent-memory captures from #444 (generated plugins skill files; session-context skip/root fix incl. the open claude-code/cursor follow-up) Claude-Session: https://claude.ai/code/session_01SGjSjrywTnZDcUqgHWGrTj Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
|
Closing in favor of #445, which solves the same bug at the root: recency selection now excludes sub-agent child sessions by default (instead of keeping the broken default behind an opt-in flag), and The valuable parts of this PR were incorporated into #445 (commit 918c087):
The companion plugin-side change from this effort stays and has been updated to match #445's final CLI semantics. |


Summary
archgate session-context opencode --skip 1guessed "the parent session" by recency — the second most-recently-updated session sharing a project directory. Verified against a real opencode database that this is wrong in two ways specific to opencode: theSkilltool runs inline in the current session (creates no new session row to skip past), and sibling sub-agent sessions fanned out from the same parent interleave with it in recency order.--rootflag toarchgate session-context opencodethat resolves the true top-level session directly via opencode'ssession.parent_idcolumn, instead of guessing by recency. Correct regardless of nesting depth or sibling fan-out.docs/public/llms-full.txt.--rootinstead of--skip 1.Why
Live-reproduced against the user's real opencode database: a
archgate-lessons-learnedskill run'sarchgate session-context opencode --skip 1 --max-entries 60call returned the "General process ADR review" sub-agent's private transcript instead of the actual parent development session — because 4 sibling sub-agents had fanned out from that parent, and--skip 1landed on a sibling instead of the parent.Known follow-up (not fixed here): the same class of bug likely affects Claude Code and Cursor's
--skip 1guidance too (live-reproduced for Claude Code during this session), since theirSkilltool also runs inline. They have noparent_id-equivalent, so--rootdoesn't directly transfer — a different fix would be needed. Captured in agent memory for a future session.Test plan
bun run validate— lint, typecheck, format, 1318 tests, ADR check (39/39), knip, build check all pass--rootresolving the parent through sibling fan-out,--root+--skipcombined, and the no-root-sessions-available error path@reviewerskill — APPROVED (0 violations, 1 warning investigated and dismissed as a pre-existing, unrelated pattern)@lessons-learnedskill — captured project memory, no new ADR needed (self-contained bug fix)https://claude.ai/code/session_01LUiXGugDB2g3WJXbtzzjVo