Skip to content

chore: upgrade Trellis dogfood to 0.6.12 - #520

Closed
lifan-builds wants to merge 1 commit into
mindfold-ai:mainfrom
lifan-builds:codex/upgrade-trellis-dogfood-0612
Closed

lifan-builds wants to merge 1 commit into
mindfold-ai:mainfrom
lifan-builds:codex/upgrade-trellis-dogfood-0612

Conversation

@lifan-builds

@lifan-builds lifan-builds commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Upgrades the repository-local dogfood installation from 0.6.2 to 0.6.12, adopts native Codex SubagentStart context injection and recovery, refreshes cross-platform hooks/skills, preserves the monorepo package map, and completes the Pi shared-skill migration to .agents/skills.\n\nValidation: pnpm lint, pnpm typecheck, pnpm build, 333 core tests passed (1 skipped), and 1,586 CLI tests passed.

Summary by CodeRabbit

  • New Features

    • Added broader support for Codex, Kiro, Trae, ZCode, Snow CLI, Kimi Code, and other platform integrations.
    • Improved sub-agent context injection with task-aware prompts, safer content handling, and native event support.
    • Added configurable context limits and a per-turn prompt-injection bypass keyword.
    • Added a Trellis session-start command for OpenCode and Pi.
  • Documentation

    • Updated platform paths, hooks, bundled skills, agent behavior, and distribution guidance.
  • Bug Fixes

    • Improved handling of binary files, malformed input, blocked streams, and platform-specific output.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds Codex sub-agent context injection, expands platform detection and hook output handling, updates Pi and OpenCode context management, adds session-start workflows, and synchronizes platform documentation, workflow requirements, configuration, and template metadata.

Changes

Platform integration documentation

Layer / File(s) Summary
Platform paths and integration documentation
.agents/skills/..., .claude/skills/..., .cursor/skills/..., .opencode/skills/...
Documentation now covers updated ZCode, Pi Agent, Kimi Code, Snow CLI, Trae, Grok Build, and Oh My Pi paths and behaviors.

Hook and sub-agent context flow

Layer / File(s) Summary
Shared platform hook behavior
.claude/hooks/*, .cursor/hooks/*, .codex/hooks/inject-workflow-state.py
Hooks add platform detection, bounded stdin handling, Codex dispatch normalization, language-aware notices, native sub-agent routing, binary-file notices, and platform-specific output formats.
Codex sub-agent context injection
.codex/agents/*, .codex/hooks.json, .codex/hooks/inject-subagent-context.py
Codex sub-agent configurations and hooks load curated, bounded task context for implement, check, finish, research, and native sub-agent workflows.

Pi and OpenCode context lifecycle

Layer / File(s) Summary
Pi and OpenCode context lifecycle
.pi/extensions/trellis/index.ts, .pi/prompts/trellis-start.md, .opencode/commands/trellis/start.md, .opencode/lib/*, .opencode/plugins/*
Pi adds bounded and cached context injection, model propagation, and runtime-context updates. OpenCode adds binary-file handling, prompt suppression, and a session-start workflow.

Workflow and release metadata

Layer / File(s) Summary
Workflow requirements and release metadata
.claude/skills/trellis-brainstorm/SKILL.md, .cursor/skills/trellis-brainstorm/SKILL.md, .opencode/skills/trellis-brainstorm/SKILL.md, .trellis/*
Sub-agent workflows require real manifest entries. Configuration documents context limits and prompt suppression. The Trellis version, update hint, and template hashes are updated.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Session as SessionStart
  participant Hook as Platform hook
  participant Task as Trellis task context
  participant Agent as Sub-agent
  Session->>Hook: Start session and detect platform
  Hook->>Task: Resolve active task and context
  Task-->>Hook: Return bounded context
  Hook-->>Agent: Inject session or sub-agent context
  Agent->>Hook: Request workflow-state update
  Hook-->>Agent: Return platform-specific output
Loading

Possibly related issues

Possibly related PRs

  • mindfold-ai/Trellis#445 — Both PRs implement native Codex SubagentStart dispatch and modify related context hooks and agent templates.
  • mindfold-ai/Trellis#454 — The Codex hook uses the same bounded, manifest-based context injection approach.
  • mindfold-ai/Trellis#368 — Both PRs extend Pi context injection with limits, persistence, and model propagation.

Suggested reviewers: taosu0216

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% 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 clearly summarizes the repository-local Trellis upgrade to version 0.6.12, which is the primary change.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (2)
.pi/extensions/trellis/index.ts (2)

1164-1172: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Directory entries in the curated JSONL produce no context on Pi.

Line 1168 skips every entry whose type is directory. The Python hook materializes those entries through _materialize_directory, which inlines up to 20 .md files under the referenced directory. A developer who curates a directory entry in implement.jsonl therefore gets that context on Codex and Claude but not on Pi, with no notice.

Either materialize directory entries here, or emit a notice so the divergence is visible.

♻️ Proposed change to inline directory entries
   if (jsonlName) {
     for (const entry of readJsonlEntries(dir, jsonlName)) {
-      if (entry.type === "directory") continue;
-      const block = materializeFile(root, entry.file, entry.reason, limits, budget);
-      if (block) specBlocks.push(block);
+      if (entry.type === "directory") {
+        for (const block of materializeDirectory(
+          root,
+          entry.file,
+          entry.reason,
+          limits,
+          budget,
+        ))
+          specBlocks.push(block);
+        continue;
+      }
+      const block = materializeFile(root, entry.file, entry.reason, limits, budget);
+      if (block) specBlocks.push(block);
     }
   }

Add the helper next to materializeFile:

function materializeDirectory(
  basePath: string,
  dirPath: string,
  reason: string,
  limits: ContextInjectionLimits,
  budget: ContextBudget,
  maxFiles = 20,
): string[] {
  const full = join(basePath, dirPath);
  const blocks: string[] = [];
  try {
    if (!statSync(full).isDirectory()) return blocks;
    const mdFiles = readdirSync(full)
      .filter((f) => f.endsWith(".md"))
      .sort()
      .slice(0, maxFiles);
    for (const filename of mdFiles) {
      const block = materializeFile(
        basePath,
        `${dirPath}/${filename}`,
        reason,
        limits,
        budget,
      );
      if (block) blocks.push(block);
    }
  } catch {}
  return blocks;
}
🤖 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 @.pi/extensions/trellis/index.ts around lines 1164 - 1172, Update the curated
JSONL processing in the block that builds specBlocks so directory entries are
materialized instead of skipped. Add a materializeDirectory helper alongside
materializeFile that validates the referenced directory, selects up to 20 sorted
Markdown files, and reuses materializeFile for each; append all returned blocks
while preserving the existing file-entry behavior.

1674-1693: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

The three new context caches grow without eviction.

startupCtxCache, taskCtxSnapshot, lastSentTaskCtx, and lastSentRuntimeCtx add one entry per context key and never remove entries. taskCtxSnapshot and lastSentTaskCtx each hold a full task-context string, which reaches max_total_bytes (131072 bytes by default). The session_shutdown handler at Line 1878 clears nativeCards but leaves these maps in place, so a long-lived process that spans many context keys retains every snapshot.

Clear the entries for a context key on session_shutdown, or cap the maps.

♻️ Proposed cleanup on session shutdown
   pi.on?.("session_shutdown", () => {
     nativeCards.clear();
     activeSubagentToolCallId = null;
+    startupCtxCache.clear();
+    taskCtxSnapshot.clear();
+    lastSentTaskCtx.clear();
+    lastSentRuntimeCtx.clear();
   });
🤖 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 @.pi/extensions/trellis/index.ts around lines 1674 - 1693, Update the
session_shutdown handler to delete the matching context-key entries from
startupCtxCache, taskCtxSnapshot, lastSentTaskCtx, and lastSentRuntimeCtx when a
session ends. Preserve cleanup of nativeCards and ensure the key used matches
the one used by getStartupCtx and the other context maps.
🤖 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
@.claude/skills/trellis-meta/references/customize-local/change-skills-or-commands.md:
- Line 90: Update the shared-layer platform list in the routing guidance to
include Pi Agent alongside Codex and Gemini CLI, matching the platform names
used in platform-map.md and the .agents/skills/ deployment entry.

In @.codex/hooks/inject-subagent-context.py:
- Around line 191-222: Update truncate_utf8 in
.codex/hooks/inject-subagent-context.py lines 191-222 to count removed trailing
continuation bytes and restore them when trailing + 1 equals the complete
sequence length, preserving characters that end exactly at the cap; otherwise
continue dropping an incomplete lead byte. Apply the identical logic to the
mirrored implementation in .pi/extensions/trellis/index.ts lines 763-779 so both
implementations remain aligned.
- Around line 1011-1012: Update the tool_name extraction in the PreToolUse
handling path to pass both host-provided values through the existing
_string_value helper before calling lower(). Preserve the current fallback
between tool_name and toolName, and keep the existing task/agent/subagent
matching behavior.

In @.cursor/hooks/session-start.py:
- Around line 198-200: Unify project-root resolution so both _detect_platform()
and main() use the same precedence, with ZCODE_PROJECT_DIR classified as zcode
before CLAUDE_PROJECT_DIR as claude and the remaining existing entries
preserved. Remove the conflicting direct CLAUDE_PROJECT_DIR-first resolution and
ensure platform classification does not override the shared root choice; add a
regression test using different ZCODE_PROJECT_DIR and CLAUDE_PROJECT_DIR values
to verify the ZCode root is selected.

In @.opencode/lib/trellis-context.js:
- Around line 260-264: Update the binary-content branch in materializeFile to
call budget.hasRoom() before adding and returning the binaryNotice result. If no
capacity remains, return the bounded-context fallback instead of emitting the
notice; preserve the existing budget.add behavior when room is available.

In @.opencode/plugins/inject-workflow-state.js:
- Around line 91-93: Update readSkipKeyword() to parse the YAML value and return
DEFAULT_PROMPT_INJECTION_SKIP_KEYWORD for null, booleans, numbers, and
collections; preserve only explicitly quoted empty strings as the disable value,
while continuing to return unquoted non-empty strings normally.

In @.pi/extensions/trellis/index.ts:
- Line 12: Update the Node engine floor for the Pi extension and its
corresponding package metadata from >=18.17.0 to >=19.4.0, ensuring the
declarations covering packages/cli and packages/core are consistent with the
isUtf8 dependency used by isBinaryContent.

In @.trellis/.template-hashes.json:
- Around line 5-14: Regenerate the manifest entries in
.trellis/.template-hashes.json from the current working files, including
restoring the missing .opencode/package.json entry, so every referenced template
has its current hash before merge.

---

Nitpick comments:
In @.pi/extensions/trellis/index.ts:
- Around line 1164-1172: Update the curated JSONL processing in the block that
builds specBlocks so directory entries are materialized instead of skipped. Add
a materializeDirectory helper alongside materializeFile that validates the
referenced directory, selects up to 20 sorted Markdown files, and reuses
materializeFile for each; append all returned blocks while preserving the
existing file-entry behavior.
- Around line 1674-1693: Update the session_shutdown handler to delete the
matching context-key entries from startupCtxCache, taskCtxSnapshot,
lastSentTaskCtx, and lastSentRuntimeCtx when a session ends. Preserve cleanup of
nativeCards and ensure the key used matches the one used by getStartupCtx and
the other context maps.
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b9c06402-177a-44f3-b26c-74e407aeb819

📥 Commits

Reviewing files that changed from the base of the PR and between 516b34e and 66adcf0.

📒 Files selected for processing (104)
  • .agents/skills/trellis-meta/references/customize-local/change-agents.md
  • .agents/skills/trellis-meta/references/customize-local/change-hooks.md
  • .agents/skills/trellis-meta/references/customize-local/change-skills-or-commands.md
  • .agents/skills/trellis-meta/references/local-architecture/bundled-skills.md
  • .agents/skills/trellis-meta/references/local-architecture/generated-files.md
  • .agents/skills/trellis-meta/references/platform-files/agents.md
  • .agents/skills/trellis-meta/references/platform-files/overview.md
  • .agents/skills/trellis-meta/references/platform-files/platform-map.md
  • .claude/hooks/inject-subagent-context.py
  • .claude/hooks/inject-workflow-state.py
  • .claude/hooks/session-start.py
  • .claude/skills/trellis-brainstorm/SKILL.md
  • .claude/skills/trellis-meta/references/customize-local/change-agents.md
  • .claude/skills/trellis-meta/references/customize-local/change-hooks.md
  • .claude/skills/trellis-meta/references/customize-local/change-skills-or-commands.md
  • .claude/skills/trellis-meta/references/local-architecture/bundled-skills.md
  • .claude/skills/trellis-meta/references/local-architecture/generated-files.md
  • .claude/skills/trellis-meta/references/platform-files/agents.md
  • .claude/skills/trellis-meta/references/platform-files/overview.md
  • .claude/skills/trellis-meta/references/platform-files/platform-map.md
  • .codex/agents/trellis-check.toml
  • .codex/agents/trellis-implement.toml
  • .codex/agents/trellis-research.toml
  • .codex/hooks.json
  • .codex/hooks/inject-subagent-context.py
  • .codex/hooks/inject-workflow-state.py
  • .codex/hooks/session-start.py
  • .cursor/hooks/inject-subagent-context.py
  • .cursor/hooks/session-start.py
  • .cursor/skills/trellis-brainstorm/SKILL.md
  • .cursor/skills/trellis-meta/references/customize-local/change-agents.md
  • .cursor/skills/trellis-meta/references/customize-local/change-hooks.md
  • .cursor/skills/trellis-meta/references/customize-local/change-skills-or-commands.md
  • .cursor/skills/trellis-meta/references/local-architecture/bundled-skills.md
  • .cursor/skills/trellis-meta/references/local-architecture/generated-files.md
  • .cursor/skills/trellis-meta/references/platform-files/agents.md
  • .cursor/skills/trellis-meta/references/platform-files/overview.md
  • .cursor/skills/trellis-meta/references/platform-files/platform-map.md
  • .opencode/commands/trellis/start.md
  • .opencode/lib/session-utils.js
  • .opencode/lib/trellis-context.js
  • .opencode/plugins/inject-workflow-state.js
  • .opencode/skills/trellis-brainstorm/SKILL.md
  • .opencode/skills/trellis-meta/SKILL.md
  • .opencode/skills/trellis-meta/references/customize-local/change-agents.md
  • .opencode/skills/trellis-meta/references/customize-local/change-hooks.md
  • .opencode/skills/trellis-meta/references/customize-local/change-skills-or-commands.md
  • .opencode/skills/trellis-meta/references/local-architecture/bundled-skills.md
  • .opencode/skills/trellis-meta/references/local-architecture/generated-files.md
  • .opencode/skills/trellis-meta/references/platform-files/agents.md
  • .opencode/skills/trellis-meta/references/platform-files/hooks-and-settings.md
  • .opencode/skills/trellis-meta/references/platform-files/overview.md
  • .opencode/skills/trellis-meta/references/platform-files/platform-map.md
  • .opencode/skills/trellis-meta/references/platform-files/skills-and-commands.md
  • .pi/extensions/trellis/index.ts
  • .pi/prompts/trellis-start.md
  • .pi/settings.json
  • .pi/skills/trellis-before-dev/SKILL.md
  • .pi/skills/trellis-brainstorm/SKILL.md
  • .pi/skills/trellis-break-loop/SKILL.md
  • .pi/skills/trellis-channel/SKILL.md
  • .pi/skills/trellis-channel/references/command-reference.md
  • .pi/skills/trellis-channel/references/forum.md
  • .pi/skills/trellis-channel/references/progress-debugging.md
  • .pi/skills/trellis-channel/references/workers.md
  • .pi/skills/trellis-channel/references/workflows.md
  • .pi/skills/trellis-check/SKILL.md
  • .pi/skills/trellis-meta/SKILL.md
  • .pi/skills/trellis-meta/references/customize-local/add-project-local-conventions.md
  • .pi/skills/trellis-meta/references/customize-local/change-agents.md
  • .pi/skills/trellis-meta/references/customize-local/change-context-loading.md
  • .pi/skills/trellis-meta/references/customize-local/change-hooks.md
  • .pi/skills/trellis-meta/references/customize-local/change-skills-or-commands.md
  • .pi/skills/trellis-meta/references/customize-local/change-spec-structure.md
  • .pi/skills/trellis-meta/references/customize-local/change-task-lifecycle.md
  • .pi/skills/trellis-meta/references/customize-local/change-workflow.md
  • .pi/skills/trellis-meta/references/customize-local/overview.md
  • .pi/skills/trellis-meta/references/local-architecture/bundled-skills.md
  • .pi/skills/trellis-meta/references/local-architecture/context-injection.md
  • .pi/skills/trellis-meta/references/local-architecture/generated-files.md
  • .pi/skills/trellis-meta/references/local-architecture/multi-agent-channel.md
  • .pi/skills/trellis-meta/references/local-architecture/overview.md
  • .pi/skills/trellis-meta/references/local-architecture/spec-system.md
  • .pi/skills/trellis-meta/references/local-architecture/task-system.md
  • .pi/skills/trellis-meta/references/local-architecture/workflow.md
  • .pi/skills/trellis-meta/references/local-architecture/workspace-memory.md
  • .pi/skills/trellis-meta/references/platform-files/agents.md
  • .pi/skills/trellis-meta/references/platform-files/hooks-and-settings.md
  • .pi/skills/trellis-meta/references/platform-files/overview.md
  • .pi/skills/trellis-meta/references/platform-files/platform-map.md
  • .pi/skills/trellis-meta/references/platform-files/skills-and-commands.md
  • .pi/skills/trellis-session-insight/SKILL.md
  • .pi/skills/trellis-session-insight/references/cli-quick-reference.md
  • .pi/skills/trellis-session-insight/references/triggering-patterns.md
  • .pi/skills/trellis-spec-bootstrap/SKILL.md
  • .pi/skills/trellis-spec-bootstrap/references/mcp-setup.md
  • .pi/skills/trellis-spec-bootstrap/references/repository-analysis.md
  • .pi/skills/trellis-spec-bootstrap/references/spec-task-planning.md
  • .pi/skills/trellis-spec-bootstrap/references/spec-writing.md
  • .pi/skills/trellis-update-spec/SKILL.md
  • .trellis/.template-hashes.json
  • .trellis/.version
  • .trellis/config.yaml
  • .trellis/scripts/common/session_context.py
💤 Files with no reviewable changes (44)
  • .pi/skills/trellis-session-insight/references/cli-quick-reference.md
  • .pi/skills/trellis-meta/references/local-architecture/generated-files.md
  • .pi/skills/trellis-meta/references/local-architecture/task-system.md
  • .pi/skills/trellis-meta/references/customize-local/change-hooks.md
  • .pi/skills/trellis-channel/references/progress-debugging.md
  • .pi/skills/trellis-before-dev/SKILL.md
  • .pi/skills/trellis-meta/references/customize-local/overview.md
  • .pi/skills/trellis-meta/references/customize-local/change-agents.md
  • .pi/skills/trellis-session-insight/SKILL.md
  • .pi/skills/trellis-meta/references/local-architecture/context-injection.md
  • .pi/skills/trellis-spec-bootstrap/references/repository-analysis.md
  • .pi/settings.json
  • .pi/skills/trellis-meta/references/platform-files/skills-and-commands.md
  • .pi/skills/trellis-brainstorm/SKILL.md
  • .pi/skills/trellis-meta/references/customize-local/add-project-local-conventions.md
  • .pi/skills/trellis-session-insight/references/triggering-patterns.md
  • .pi/skills/trellis-break-loop/SKILL.md
  • .pi/skills/trellis-channel/references/workers.md
  • .pi/skills/trellis-check/SKILL.md
  • .pi/skills/trellis-update-spec/SKILL.md
  • .pi/skills/trellis-meta/references/customize-local/change-workflow.md
  • .pi/skills/trellis-spec-bootstrap/references/spec-writing.md
  • .pi/skills/trellis-meta/references/platform-files/platform-map.md
  • .pi/skills/trellis-meta/references/customize-local/change-task-lifecycle.md
  • .pi/skills/trellis-meta/references/platform-files/overview.md
  • .pi/skills/trellis-meta/references/local-architecture/spec-system.md
  • .pi/skills/trellis-channel/SKILL.md
  • .pi/skills/trellis-channel/references/workflows.md
  • .pi/skills/trellis-meta/references/local-architecture/workspace-memory.md
  • .pi/skills/trellis-meta/references/platform-files/agents.md
  • .pi/skills/trellis-meta/references/local-architecture/overview.md
  • .pi/skills/trellis-meta/references/customize-local/change-skills-or-commands.md
  • .pi/skills/trellis-meta/references/platform-files/hooks-and-settings.md
  • .pi/skills/trellis-channel/references/command-reference.md
  • .pi/skills/trellis-meta/references/local-architecture/workflow.md
  • .pi/skills/trellis-meta/SKILL.md
  • .pi/skills/trellis-spec-bootstrap/references/mcp-setup.md
  • .pi/skills/trellis-meta/references/customize-local/change-spec-structure.md
  • .pi/skills/trellis-spec-bootstrap/SKILL.md
  • .pi/skills/trellis-meta/references/local-architecture/bundled-skills.md
  • .pi/skills/trellis-spec-bootstrap/references/spec-task-planning.md
  • .pi/skills/trellis-meta/references/customize-local/change-context-loading.md
  • .pi/skills/trellis-meta/references/local-architecture/multi-agent-channel.md
  • .pi/skills/trellis-channel/references/forum.md

| GitHub Copilot | `.github/skills/`, `.github/prompts/` |
| Factory Droid | `.factory/skills/`, `.factory/commands/` |
| Pi Agent | `.pi/skills/` |
| Pi Agent | `.agents/skills/` |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the shared-layer platform list to include Pi Agent.

Line 90 now deploys Pi Agent skills to .agents/skills/. Line 106 still names only Codex and Gemini CLI as shared-layer platforms. platform-map.md line 76 names Codex, Gemini CLI, Pi Agent, and Kimi Code. Align line 106 so routing guidance stays correct.

📝 Proposed documentation fix
-For multi-platform projects, add equivalent versions in each platform skill directory, or use `.agents/skills/` on platforms that support the shared layer (Codex, Gemini CLI).
+For multi-platform projects, add equivalent versions in each platform skill directory, or use `.agents/skills/` on platforms that support the shared layer (Codex, Gemini CLI, Pi Agent, Kimi Code).
🤖 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
@.claude/skills/trellis-meta/references/customize-local/change-skills-or-commands.md
at line 90, Update the shared-layer platform list in the routing guidance to
include Pi Agent alongside Codex and Gemini CLI, matching the platform names
used in platform-map.md and the .agents/skills/ deployment entry.

Comment on lines +191 to +222
def truncate_utf8(data: bytes, cap: int) -> bytes:
"""Truncate ``data`` to at most ``cap`` bytes without splitting a UTF-8
multi-byte sequence.

``cap <= 0`` means "no limit" — returns ``data`` unchanged.
"""
if cap <= 0 or len(data) <= cap:
return data

truncated = data[:cap]
i = len(truncated)
# Back off over continuation bytes (10xxxxxx) to find the lead byte.
while i > 0 and (truncated[i - 1] & 0xC0) == 0x80:
i -= 1
if i == 0:
return b""

lead = truncated[i - 1]
if lead & 0x80:
if (lead & 0xE0) == 0xC0:
seq_len = 2
elif (lead & 0xF0) == 0xE0:
seq_len = 3
elif (lead & 0xF8) == 0xF0:
seq_len = 4
else:
seq_len = 1
# Drop the lead byte too if its full sequence didn't fit.
if (i - 1) + seq_len > len(truncated):
i -= 1

return truncated[:i]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

UTF-8 truncation drops a complete character at an exact cap boundary in both mirrored implementations. Each version strips every trailing continuation byte before it checks whether the sequence is complete. When a multi-byte character ends exactly at cap, the index lands on the byte after the lead byte, the completeness check compares 0 + seq_len > cap and evaluates false, and only the lead byte survives. The decode step then emits a replacement character into the injected context. Track how many continuation bytes you removed and restore them when the sequence is complete.

  • .codex/hooks/inject-subagent-context.py#L191-L222: count the continuation bytes removed by the loop at Line 203, then restore them instead of dropping the lead byte when trailing + 1 == seq_len.
  • .pi/extensions/trellis/index.ts#L763-L779: apply the same change to the loop at Line 767 and the check at Line 776 so the two implementations stay byte-for-byte aligned, as the header comment at Lines 749-751 requires.
📍 Affects 2 files
  • .codex/hooks/inject-subagent-context.py#L191-L222 (this comment)
  • .pi/extensions/trellis/index.ts#L763-L779
🤖 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 @.codex/hooks/inject-subagent-context.py around lines 191 - 222, Update
truncate_utf8 in .codex/hooks/inject-subagent-context.py lines 191-222 to count
removed trailing continuation bytes and restore them when trailing + 1 equals
the complete sequence length, preserving characters that end exactly at the cap;
otherwise continue dropping an incomplete lead byte. Apply the identical logic
to the mirrored implementation in .pi/extensions/trellis/index.ts lines 763-779
so both implementations remain aligned.

Comment on lines +1011 to +1012
tool_name = input_data.get("tool_name", "") or input_data.get("toolName", "")
if tool_name.lower() in ("task", "agent", "subagent"):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Coerce tool_name before calling .lower().

Line 1011 reads tool_name straight from host JSON. If a host sends a non-string value, Line 1012 raises AttributeError. The PreToolUse path in main has no top-level guard, so the hook exits non-zero and emits no JSON. The rest of this file already routes host fields through _string_value.

🛡️ Proposed fix
-    tool_name = input_data.get("tool_name", "") or input_data.get("toolName", "")
+    tool_name = _string_value(input_data.get("tool_name")) or _string_value(
+        input_data.get("toolName")
+    )
     if tool_name.lower() in ("task", "agent", "subagent"):
📝 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.

Suggested change
tool_name = input_data.get("tool_name", "") or input_data.get("toolName", "")
if tool_name.lower() in ("task", "agent", "subagent"):
tool_name = _string_value(input_data.get("tool_name")) or _string_value(
input_data.get("toolName")
)
if tool_name.lower() in ("task", "agent", "subagent"):
🤖 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 @.codex/hooks/inject-subagent-context.py around lines 1011 - 1012, Update the
tool_name extraction in the PreToolUse handling path to pass both host-provided
values through the existing _string_value helper before calling lower().
Preserve the current fallback between tool_name and toolName, and keep the
existing task/agent/subagent matching behavior.

Comment on lines +198 to +200
# ZCode may set both ZCODE_PROJECT_DIR and CLAUDE_PROJECT_DIR; check
# ZCODE first so ZCode sessions aren't misdetected as claude.
"ZCODE_PROJECT_DIR": "zcode",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file exists and line counts =="
wc -l .cursor/hooks/session-start.py || true

echo "== relevant source sections =="
sed -n '160,220p' .cursor/hooks/session-start.py
printf '\n---\n'
sed -n '730,775p' .cursor/hooks/session-start.py

echo "== tests mentioning ZCODE/CLAUDE roots =="
git ls-files | xargs rg -n "ZCODE_PROJECT_DIR|CLAUDE_PROJECT_DIR|detect_platform|PROJECT_ROOT|trellis" || true

echo "== git diff stat/name status =="
git diff --stat HEAD || true
git diff -- .cursor/hooks/session-start.py | sed -n '1,220p' || true

Repository: mindfold-ai/Trellis

Length of output: 50378


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== relevant project-start.py sections =="
sed -n '168,210p' .cursor/hooks/session-start.py
printf '\n---\n'
sed -n '725,765p' .cursor/hooks/session-start.py

echo "== focused project-root references in file =="
rg -n "PROJECT_ROOT|root|ZCODE_PROJECT_DIR|CLAUDE_PROJECT_DIR|\.trellis" .cursor/hooks/session-start.py || true

echo "== tests/repo references exactly for ZCODE/CLAUDE env and diff roots =="
rg -n "ZCODE_PROJECT_DIR|CLAUDE_PROJECT_DIR|PROJECT_ROOT" --glob '!packages/**' --glob '!docs/**' --glob '!src/**' . || true

Repository: mindfold-ai/Trellis

Length of output: 5478


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== platform detection exact block =="
sed -n '190,218p' .cursor/hooks/session-start.py

echo "== project root selection exact block =="
sed -n '745,775p' .cursor/hooks/session-start.py

echo "== behavioral probe of precedence with different roots =="
python3 - <<'PY'
from pathlib import Path

env_map = {
    "ZCODE_PROJECT_DIR": "zcode",
    "CLAUDE_PROJECT_DIR": "claude",
}

project_dir_env_vars = [
    "CLAUDE_PROJECT_DIR",
    "ZCODE_PROJECT_DIR",
]

os = type("os", (), {"environ": {"ZCODE_PROJECT_DIR": "z_root", "CLAUDE_PROJECT_DIR": "c_root"}})

def current_detect_platform(input_data):
    for var, platform in env_map.items():
        if os.environ.get(var):
            return platform
    return None

def current_resolve_project_directory():
    for var in project_dir_env_vars:
        val = os.environ.get(var)
        if val:
            return Path(val).resolve()

print("detect:", current_detect_platform({}))
print("project_dir:", current_resolve_project_directory())
print("trellis_dir:", current_resolve_project_directory() / ".trellis")
print("repo_root_from_project_dir:", current_resolve_project_directory())

def intended_detect_platform(input_data):
    for var, platform in project_dir_env_vars:
        if var in env_map and os.environ.get(var):
            return env_map[var]

print("intended_detect:", intended_detect_platform({}))
PY

Repository: mindfold-ai/Trellis

Length of output: 2837


Use one shared project-root precedence for ZCode root resolution.

_detect_platform() prefers ZCODE_PROJECT_DIR, but main() resolves CLAUDE_PROJECT_DIR first and also classifies by hook path before calling that function. With both variables set to different roots, ZCode can read .trellis/... from the Claude root. Extract a shared precedence list with {ZCODE_PROJECT_DIR: "zcode", CLAUDE_PROJECT_DIR: "claude", ...}, and add a regression test that feeds different ZCODE_PROJECT_DIR and CLAUDE_PROJECT_DIR values.

🤖 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 @.cursor/hooks/session-start.py around lines 198 - 200, Unify project-root
resolution so both _detect_platform() and main() use the same precedence, with
ZCODE_PROJECT_DIR classified as zcode before CLAUDE_PROJECT_DIR as claude and
the remaining existing entries preserved. Remove the conflicting direct
CLAUDE_PROJECT_DIR-first resolution and ensure platform classification does not
override the shared root choice; add a regression test using different
ZCODE_PROJECT_DIR and CLAUDE_PROJECT_DIR values to verify the ZCode root is
selected.

Comment on lines +260 to +264
if (isBinaryContent(data)) {
const notice = binaryNotice(filePath, size, reason)
budget.add(Buffer.byteLength(notice, "utf-8"))
return notice
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 8 'class ContextBudget|function ContextBudget|hasRoom\(|add\(' .opencode/lib/trellis-context.js
rg -n -C 6 'materializeFile\(' .opencode/lib/trellis-context.js
rg -n -C 4 'binary file|not inlined|max_total_bytes|max.*bytes' -g '*test*' .

Repository: mindfold-ai/Trellis

Length of output: 50375


🏁 Script executed:

#!/bin/bash
set -euo pipefail

wc -l .opencode/lib/trellis-context.js
sed -n '1,280p' .opencode/lib/trellis-context.js
sed -n '298,345p' .opencode/lib/trellis-context.js

python3 - <<'PY'
class ContextBudget:
    def __init__(self,maxTotalBytes):
        self.maxTotalBytes=maxTotalBytes
        self.used=0
    def hasRoom(self,size):
        if self.maxTotalBytes<=0:
            return True
        return self.used+size<=self.maxTotalBytes
    def add(self,size):
        self.used+=size

def binary_notice_bytes():
    return len(f"\n[Trellis: not inlined (binary file) — path.bin ({1000} bytes): reason]\n")

for max_total_bytes in [50, None, 0]:
    max_total_bytes = max_total_bytes if max_total_bytes is not None else -1
    b=ContextBudget(max_total_bytes if max_total_bytes>=0 else -1)
    for i in range(3):
        b.add(binary_notice_bytes())
    print("max=", max_total_bytes, "used=", b.used, "has_room_next=", b.hasRoom(binary_notice_bytes()))
PY

Repository: mindfold-ai/Trellis

Length of output: 10672


Check capacity before emitting binary notices.

ContextBudget.add() only increments the used byte count and does not enforce max_total_bytes. In materializeFile(), use budget.hasRoom() before returning binaryNotice() so bounded contexts do not exceed the limit.

🤖 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 @.opencode/lib/trellis-context.js around lines 260 - 264, Update the
binary-content branch in materializeFile to call budget.hasRoom() before adding
and returning the binaryNotice result. If no capacity remains, return the
bounded-context fallback instead of emitting the notice; preserve the existing
budget.add behavior when room is available.

Comment on lines +91 to +93
const m = trimmed.match(/^skip_keyword\s*:\s*(.*)$/)
if (!m) continue
return unquoteYaml(stripInlineComment(m[1]).trim())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve the default for non-string YAML values.

readSkipKeyword() returns the raw text for every scalar. For example, skip_keyword: false enables false as the escape hatch, and skip_keyword: disables the escape hatch. Parsed YAML treats both values as non-strings, so the shared Python behavior retains no-trellis.

Return DEFAULT_PROMPT_INJECTION_SKIP_KEYWORD for null, boolean, numeric, and collection values. Keep only an explicitly quoted empty string as the disable setting.

🤖 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 @.opencode/plugins/inject-workflow-state.js around lines 91 - 93, Update
readSkipKeyword() to parse the YAML value and return
DEFAULT_PROMPT_INJECTION_SKIP_KEYWORD for null, booleans, numbers, and
collections; preserve only explicitly quoted empty strings as the disable value,
while continuing to return unquoted non-empty strings normally.

resolve,
} from "node:path";
import { spawn, spawnSync } from "node:child_process";
import { isUtf8 } from "node:buffer";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect the declared Node version floor for the repository and the Pi extension.
set -euo pipefail

fd -H -t f -g 'package.json' -E node_modules | while IFS= read -r f; do
  node -e '
    const fs = require("fs");
    const p = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
    if (p.engines || p.volta) console.log(process.argv[1], JSON.stringify({engines: p.engines, volta: p.volta}));
  ' "$f"
done

fd -H -t f -g '.nvmrc' -E node_modules --exec cat -n
fd -H -t f -g '.node-version' -E node_modules --exec cat -n
fd -H -t f -g '.tool-versions' -E node_modules --exec cat -n
rg -n 'node-version' -g '*.yml' -g '*.yaml' .github 2>/dev/null || true

Repository: mindfold-ai/Trellis

Length of output: 457


🌐 Web query:

Which Node.js version added isUtf8 to node:buffer?

💡 Result:

The isUtf8 function in the node:buffer module was added to Node.js in version 19.4.0 [1]. It was introduced to provide a way to validate UTF-8-encoded data [2][1].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Relevant Node dependency/version files:"
fd -H -t f -E node_modules -E dist -E build '^(package-lock\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb|package\.json|\.nvmrc|\.node-version|\.tool-versions)$' . | sed 's#^\./##' | sort | head -200

echo
echo "Root/required version constraints mentioning Node:"
rg -n '"node"|node[': -g 'package.json' -g 'biome.json' -g 'tsconfig.json' -g '.npmrc' -g 'README.md' -g 'pnpm-workspace.yaml' -g 'package-lock.json' -g 'pnpm-lock.yaml' -g 'yarn.lock' . | head -200

echo
echo "Relevant extension file imports and function:"
wc -l .pi/extensions/trellis/index.ts
ast-grep outline .pi/extensions/trellis/index.ts --match isUtf8 --view compact || true
ast-grep outline .pi/extensions/trellis/index.ts --match isBinaryContent --view compact || true
sed -n '1,80p' .pi/extensions/trellis/index.ts
sed -n '835,870p' .pi/extensions/trellis/index.ts

echo
echo "Resolved npm package metadata for `@types/node` and node (if network/package metadata available):"
npm view `@types/node`@latest version dist.tarball 2>/dev/null || true
npm view node@latest engines 2>/dev/null || true

Repository: mindfold-ai/Trellis

Length of output: 504


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Relevant Node dependency/version files:"
fd -H -t f -E node_modules -E dist -E build '^(package-lock\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb|package\.json|\.nvmrc|\.node-version|\.tool-versions)$' . | sed 's#^\./##' | sort | head -200

echo
echo "Root/required version constraints mentioning Node:"
rg -n '"node"|node:' -g 'package.json' -g 'biome.json' -g 'tsconfig.json' -g '.npmrc' -g 'README.md' -g 'pnpm-workspace.yaml' -g 'package-lock.json' -g 'pnpm-lock.yaml' -g 'yarn.lock' . | head -200 || true

echo
echo "Relevant extension file imports and function:"
wc -l .pi/extensions/trellis/index.ts
ast-grep outline .pi/extensions/trellis/index.ts --match isUtf8 --view compact || true
ast-grep outline .pi/extensions/trellis/index.ts --match isBinaryContent --view compact || true
sed -n '1,80p' .pi/extensions/trellis/index.ts
sed -n '835,870p' .pi/extensions/trellis/index.ts

echo
echo "Resolved npm package metadata for node (if network/package metadata available):"
npm view node@latest engines 2>/dev/null || true
npm view `@types/node`@latest version dist.tarball 2>/dev/null || true

Repository: mindfold-ai/Trellis

Length of output: 14783


Update the Node floor to >=19.4.0.

isUtf8 from node:buffer is not available in Node 18.17.0, but packages/cli/package.json and packages/core/package.json declare node >=18.17.0, so the Pi extension can run on a runtime where isUtf8 is undefined and isBinaryContent throws on file reads.

🤖 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 @.pi/extensions/trellis/index.ts at line 12, Update the Node engine floor for
the Pi extension and its corresponding package metadata from >=18.17.0 to
>=19.4.0, ensuring the declarations covering packages/cli and packages/core are
consistent with the isUtf8 dependency used by isBinaryContent.

Comment on lines +5 to +14
".claude/skills/trellis-meta/references/customize-local/change-agents.md": "4216ac3cc570038fd8ce3319a85932bb537fea9e0a7f4d11fa315cfa645c9c85",
".claude/skills/trellis-meta/references/customize-local/change-context-loading.md": "c7be53e038eac99ff4d0abb3fafc67cfdfd90352744ce09ad11e7ef5085cf933",
".claude/skills/trellis-meta/references/customize-local/change-hooks.md": "91892f2cff53ae003736007e95172945acabf48b2fc889bd627cd2406ce449c4",
".claude/skills/trellis-meta/references/customize-local/change-skills-or-commands.md": "a6994e2418cdf5bcad10b4236b02741179ae794bc4fdd811a64f443293b69268",
".claude/skills/trellis-meta/references/customize-local/change-hooks.md": "4c18b134f05d5ba1609c517ec94c97c9442b5f0670aa2211c487c31ed2a4f358",
".claude/skills/trellis-meta/references/customize-local/change-skills-or-commands.md": "5b942e9f512e049a75e8dd9e8cc4d49786f6da61b22afc82bb447acf808f9fe2",
".claude/skills/trellis-meta/references/customize-local/change-spec-structure.md": "a3fc9da294448b4a3525ae1ab7c8c9b895ef8f3b53bcf37a13ce6afbdc040fe6",
".claude/skills/trellis-meta/references/customize-local/change-task-lifecycle.md": "60ff9efb93604b87a461a4af30322d76750402a51e40f31531a7ff88d309996d",
".claude/skills/trellis-meta/references/customize-local/change-workflow.md": "43fa780a2ca580de121b10893d49b99f978873deebbf45008c466e5ac6651519",
".claude/skills/trellis-meta/references/customize-local/overview.md": "ce8f09e9f93ce9a48500763fb3a4db2b3908a5fbf4f985ab71dacebb404cf8f4",
".claude/skills/trellis-meta/references/local-architecture/context-injection.md": "31286b9c05e600db7d179100eca533f9b8a4aab3a9c255cb69e8dccacb4e8375",
".claude/skills/trellis-meta/references/local-architecture/generated-files.md": "ca4211b43aff79cf263c83255f557994386723d10da344c0c4e1ec211c7fcd37",
".claude/skills/trellis-meta/references/local-architecture/generated-files.md": "7eb2d452eddb4f4226f7578c2ec6d5ee0434ed172ba4c36107cc8bdff7554dc6",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import hashlib
import json
from pathlib import Path

manifest = json.loads(Path(".trellis/.template-hashes.json").read_text())
failures = []

for relative_path, expected_hash in manifest["hashes"].items():
    path = Path(relative_path)
    if not path.is_file():
        failures.append(f"missing: {relative_path}")
        continue

    actual_hash = hashlib.sha256(path.read_bytes()).hexdigest()
    if actual_hash != expected_hash:
        failures.append(
            f"mismatch: {relative_path}: expected {expected_hash}, got {actual_hash}"
        )

if failures:
    print("\n".join(failures))
    raise SystemExit(1)

print("All template hashes match.")
PY

Repository: mindfold-ai/Trellis

Length of output: 14622


Regenerate .trellis/.template-hashes.json before merge.

Many manifest entries no longer match their referenced files, including .opencode/package.json as missing. Update the hashes from the working files so trellis update does not restore stale files or skip changed files.

🤖 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 @.trellis/.template-hashes.json around lines 5 - 14, Regenerate the manifest
entries in .trellis/.template-hashes.json from the current working files,
including restoring the missing .opencode/package.json entry, so every
referenced template has its current hash before merge.

@lifan-builds

Copy link
Copy Markdown
Contributor Author

Closing at the repository owner's request. The fleet rollout should exclude the Trellis source repository itself.

@lifan-builds
lifan-builds deleted the codex/upgrade-trellis-dogfood-0612 branch August 4, 2026 22:04
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.

1 participant