Skip to content

feat: ponytail L1 integration — port runtime to Rust - #45

Merged
getappz merged 13 commits into
masterfrom
feature/ponytail-l1-integration
Jul 7, 2026
Merged

feat: ponytail L1 integration — port runtime to Rust#45
getappz merged 13 commits into
masterfrom
feature/ponytail-l1-integration

Conversation

@getappz

@getappz getappz commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Summary

Ports ponytail runtime logic (config, state, instructions, switcher, platform detection, sub-skills) from Node.js hooks into agentflare Rust. Prompt content stays embedded as fallback.

Closes #42

Changes

  • src/ponytail/ module — 7 files, 700 LOC, 20 tests
  • config.rs — mode resolution (env -> config.json -> default), validation
  • state.rs — flag file read/write for active mode tracking
  • instructions.rs — SKILL.md loading, intensity filtering, fallback
  • switcher.rs — mode switch detection from user input
  • platform.rs — agent detection + per-platform hook output formatting
  • sub_skills.rs — embedded skill content (review, audit, debt, gain, help)
  • 6 embedded SKILL.md files via include_str!()
  • CLI: \�gentflare ponytail status|set|default|off|update|review|audit|debt|gain|info|hook\
  • Hook events: session-start, subagent-start, prompt-submit, statusline

Follow-up PR (feature/agent-detector-ponytail)

  • Process-tree agent detection via sysinfo
  • Auto-wiring of ponytail hooks via init --agent X
  • Existing plugin detection + uninstall prompt

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a new src/ponytail/ Rust runtime, wires a ponytail CLI entrypoint and hook handling into src/main.rs, embeds skill markdown content, and adds design/spec/progress documentation for the integration.

Changes

Ponytail Rust Runtime Port

Layer / File(s) Summary
Plan, design spec, and progress docs
docs/superpowers/plans/..., docs/superpowers/specs/..., .superpowers/sdd/progress.md
Adds the implementation plan, design specification, and progress entry for the ponytail integration work.
Mode config and active state
src/ponytail/config.rs, src/ponytail/state.rs
Adds mode normalization, default-mode persistence, config path resolution, and filesystem-backed active-mode flag handling with tests.
Instruction building and skill content
src/ponytail/instructions.rs, src/ponytail/sub_skills.rs, src/ponytail/skill.md
Adds instruction construction, skill download and caching, mode-based filtering, embedded skill cards, and fallback instruction text with tests.
Switch parsing and platform output
src/ponytail/switcher.rs, src/ponytail/platform.rs
Adds prompt switch parsing, platform detection, and per-platform hook output formatting with tests.
Public API surface and CLI wiring
src/ponytail/mod.rs, src/main.rs
Declares the ponytail module surface and wires the new ponytail command, subcommands, and hook dispatch into the main CLI.
Sub-skill markdown documentation
src/ponytail/skill-review.md, skill-audit.md, skill-debt.md, skill-gain.md, skill-help.md
Adds the review, audit, debt, gain, help, and primary ponytail skill markdown documents.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Main as src/main.rs
  participant Switcher as ponytail::switcher
  participant Config as ponytail::config
  participant State as ponytail::state
  participant Instructions as ponytail::instructions
  participant Platform as ponytail::platform

  User->>Main: agentflare ponytail hook <event>
  Main->>Switcher: detect(input)
  Switcher-->>Main: SwitchAction
  alt SetMode
    Main->>State: set_active(mode)
  else SetDefault
    Main->>Config: set_default_mode(mode)
  else Off
    Main->>State: clear_active()
  end
  Main->>Instructions: build(mode, skill_path)
  Instructions-->>Main: Instructions{mode, body}
  Main->>Platform: detect()
  Platform-->>Main: AgentPlatform
  Main->>Platform: format_hook_output(event, body, platform)
  Platform-->>Main: formatted output
  Main-->>User: hook output
Loading

Possibly related issues

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR implements the requested Rust ponytail modules, CLI hook entrypoint, env-based platform detection, and external skill fallback.
Out of Scope Changes check ✅ Passed No clear out-of-scope code changes are evident; the added docs and sub-skill files support the requested ponytail integration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title clearly and concisely summarizes the main change: porting ponytail L1 integration runtime logic to Rust.
Description check ✅ Passed The description covers the summary, changes, and follow-up context, but it omits the template's test plan and reviewer notes sections.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/ponytail-l1-integration

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

🧹 Nitpick comments (4)
src/main.rs (2)

616-624: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Inconsistent path for clear_active.

Here you call ponytail::state::clear_active() via the full module path, while Off (Line 589) and PromptSubmit (Line 664) use the re-exported ponytail::clear_active(). Both compile, but aligning on the re-exported alias reads more consistently.

🤖 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/main.rs` around lines 616 - 624, The `SessionStart` branch in
`PonytailAction::Hook` is calling `clear_active` through
`ponytail::state::clear_active()` while `Off` and `PromptSubmit` use the
re-exported `ponytail::clear_active()`. Update this branch to use the same
re-exported alias so the `PonytailAction::Hook` handling is consistent across
cases and easier to read.

670-682: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Hardcoded/duplicated ANSI statusline formatting.

The color escape \x1b[38;5;108m is duplicated and the badge is emitted inline here rather than going through platform's output helpers. Consider moving statusline rendering into a small helper (e.g. alongside format_hook_output) to keep ANSI formatting in one place.

🤖 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/main.rs` around lines 670 - 682, The Statusline branch in
PonytailHookEvent is formatting the badge inline and duplicating the ANSI color
escape, so move this rendering into a small helper near format_hook_output or
the platform output helpers. Use that helper from the Statusline match arm to
build the full [PONYTAIL] / [PONYTAIL:MODE] string and keep the ANSI formatting
centralized in one place.
src/ponytail/switcher.rs (1)

50-57: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Normalized value discarded, raw user token persisted instead.

config::normalize_config_mode(dmode)? is called purely for its Option short-circuiting; the (possibly canonicalized) return value is discarded and the original, unnormalized dmode is stored in SwitchAction::SetDefault. Same pattern at lines 40-41 for mode. If normalize_config_mode ever does more than strict whitelist validation (e.g. trims/aliases), the persisted value could diverge from what other code expects as canonical (e.g. the lm == effective comparisons in instructions::filter_skill_body). Using the normalized return value directly would be more robust against future changes to normalize_config_mode.

🤖 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/ponytail/switcher.rs` around lines 50 - 57, The parsing in
SwitchAction::SetDefault (and the similar mode branch) validates with
config::normalize_config_mode but still stores the original token, so the
normalized value is being discarded. Update the switcher logic to use the value
returned by config::normalize_config_mode directly when constructing the
SwitchAction, so the persisted mode/default matches the canonical form expected
elsewhere such as instructions::filter_skill_body.
src/ponytail/platform.rs (1)

1-59: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No unit tests for platform detection/hook formatting.

Unlike switcher.rs and instructions.rs, this module has no tests covering detect() env-var branches or the per-platform format_hook_output JSON shapes (especially the Codex systemMessage conditional and Copilot's event-gated output).

🤖 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/ponytail/platform.rs` around lines 1 - 59, Add unit tests for platform
detection and hook formatting in the platform module. Cover the detect()
branches for CLAUDE_CONFIG_DIR, COPILOT_PLUGIN_DATA, PLUGIN_DATA, and the
fallback case, and verify format_hook_output() for AgentPlatform::Claude,
AgentPlatform::Codex, AgentPlatform::Copilot, and AgentPlatform::Fallback. Make
sure the tests assert the Codex systemMessage only appears for SessionStart and
that Copilot returns output only for SessionStart, using the detect() and
format_hook_output() symbols to locate the behavior.
🤖 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/ponytail/instructions.rs`:
- Around line 52-61: The cache-path logic is duplicated in the skill-loading
fallback instead of reusing the existing `skill_cache_path()` helper. Update the
`instructions.rs` loading branch that builds `skill_body` to call
`skill_cache_path()` directly, so the path construction stays centralized and
consistent with the helper used elsewhere.
- Around line 12-37: The skill fetch is currently using a mutable `main` URL and
a blocking `ureq` call without any timeout, so update `SKILL_URL` in
`download_skill()` to point to an immutable tag or commit and configure an
explicit request timeout on the `ureq::get(...).call()` flow. Keep the existing
cache/write logic in `skill_cache_path()` and `download_skill()`, but ensure the
source is pinned and the network request cannot hang indefinitely.

In `@src/ponytail/platform.rs`:
- Around line 33-49: The Codex branch in platform.rs hardcodes the SessionStart
system message to PONYTAIL:FULL, so it ignores the active Ponytail mode. Update
the AgentPlatform::Codex handling to use the effective configured mode for
SessionStart instead of the literal string, and keep the systemMessage field
conditional on that value being present. Use the existing event check and the
output construction in this branch to locate the fix.

In `@src/ponytail/skill-debt.md`:
- Around line 17-20: The grep command in the ponytail scan still traverses
excluded directories, so update the repo search instruction to actually skip
node_modules, .git, and build output when running the comment-marker scan.
Adjust the command in the skill-debt guidance so the exclusions are enforced in
the grep invocation itself, keeping the scan focused on source files and
reducing noise; refer to the repo-scanning instruction that mentions grep and
comment markers.

In `@src/ponytail/state.rs`:
- Around line 27-29: The clear_active helper is swallowing every remove_file
failure instead of only ignoring a missing flag file. Update clear_active to
handle the std::fs::remove_file(flag_path()) result explicitly: ignore only
NotFound, and surface or log any other I/O error such as permission denied so
callers know the active flag was not actually cleared.

In `@src/ponytail/sub_skills.rs`:
- Around line 1-5: The compile-time includes in sub_skills.rs are depending on
markdown files that belong to a later/undeclared layer, which causes build
failures when this layer is compiled in isolation. Update the dependency
structure so the layer containing SKILL_REVIEW, SKILL_AUDIT, SKILL_DEBT,
SKILL_GAIN, and SKILL_HELP is declared after or dependent on the sub-skill
markdown documentation layer, or otherwise move these include_str! references to
a layer where the files are guaranteed to exist at compile time. Verify the
constants in sub_skills.rs only reference files that are available when the
module is built.

---

Nitpick comments:
In `@src/main.rs`:
- Around line 616-624: The `SessionStart` branch in `PonytailAction::Hook` is
calling `clear_active` through `ponytail::state::clear_active()` while `Off` and
`PromptSubmit` use the re-exported `ponytail::clear_active()`. Update this
branch to use the same re-exported alias so the `PonytailAction::Hook` handling
is consistent across cases and easier to read.
- Around line 670-682: The Statusline branch in PonytailHookEvent is formatting
the badge inline and duplicating the ANSI color escape, so move this rendering
into a small helper near format_hook_output or the platform output helpers. Use
that helper from the Statusline match arm to build the full [PONYTAIL] /
[PONYTAIL:MODE] string and keep the ANSI formatting centralized in one place.

In `@src/ponytail/platform.rs`:
- Around line 1-59: Add unit tests for platform detection and hook formatting in
the platform module. Cover the detect() branches for CLAUDE_CONFIG_DIR,
COPILOT_PLUGIN_DATA, PLUGIN_DATA, and the fallback case, and verify
format_hook_output() for AgentPlatform::Claude, AgentPlatform::Codex,
AgentPlatform::Copilot, and AgentPlatform::Fallback. Make sure the tests assert
the Codex systemMessage only appears for SessionStart and that Copilot returns
output only for SessionStart, using the detect() and format_hook_output()
symbols to locate the behavior.

In `@src/ponytail/switcher.rs`:
- Around line 50-57: The parsing in SwitchAction::SetDefault (and the similar
mode branch) validates with config::normalize_config_mode but still stores the
original token, so the normalized value is being discarded. Update the switcher
logic to use the value returned by config::normalize_config_mode directly when
constructing the SwitchAction, so the persisted mode/default matches the
canonical form expected elsewhere such as instructions::filter_skill_body.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 16d50f42-4ae8-4a18-951d-ec291d912e37

📥 Commits

Reviewing files that changed from the base of the PR and between 8e9d46f and 5c325a1.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (17)
  • .superpowers/sdd/progress.md
  • docs/superpowers/plans/2026-07-07-ponytail-l1-integration.md
  • docs/superpowers/specs/2026-07-07-ponytail-l1-integration-design.md
  • src/main.rs
  • src/ponytail/config.rs
  • src/ponytail/instructions.rs
  • src/ponytail/mod.rs
  • src/ponytail/platform.rs
  • src/ponytail/skill-audit.md
  • src/ponytail/skill-debt.md
  • src/ponytail/skill-gain.md
  • src/ponytail/skill-help.md
  • src/ponytail/skill-review.md
  • src/ponytail/skill.md
  • src/ponytail/state.rs
  • src/ponytail/sub_skills.rs
  • src/ponytail/switcher.rs

Comment on lines +12 to +37
const SKILL_URL: &str =
"https://raw.githubusercontent.com/DietrichGebert/ponytail/main/skills/ponytail/SKILL.md";

pub fn skill_cache_path() -> std::path::PathBuf {
dirs::cache_dir()
.unwrap_or_else(|| std::path::PathBuf::from("."))
.join("agentflare")
.join("ponytail")
.join("SKILL.md")
}

pub fn download_skill() -> Result<String, String> {
let resp = ureq::get(SKILL_URL)
.call()
.map_err(|e| format!("fetch failed: {e}"))?;
if resp.status() != 200 {
return Err(format!("HTTP {}", resp.status()));
}
let body = resp.into_string().map_err(|e| format!("read failed: {e}"))?;
let path = skill_cache_path();
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent).map_err(|e| format!("mkdir: {e}"))?;
}
std::fs::write(&path, &body).map_err(|e| format!("write: {e}"))?;
Ok(path.display().to_string())
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

What is the current recommended way to set a request timeout with ureq 2.x, and does ureq::get()/.call() block indefinitely by default with no timeout configured?

💡 Result:

In ureq 2.x, the recommended way to set request timeouts is by using the AgentBuilder to configure the agent or by calling the.timeout method directly on a Request object [1][2][3][4]. When creating an agent, you can define various timeouts using AgentBuilder, such as.timeout_connect,.timeout_read,.timeout_write, and the overall.timeout [1]. The.timeout method covers the entire request lifecycle (DNS resolution, connection, redirects, and reading the response body), and it takes precedence over read and write timeouts [1]. Regarding whether ureq::get/.call blocks indefinitely by default: Ureq uses blocking I/O [5][6]. While ureq includes some default behaviors, relying on it to block indefinitely is generally not the case for network operations, as the underlying operating system and TCP stack often impose their own default timeouts [7]. However, from the perspective of the ureq API configuration, if no explicit timeouts are set, the request may wait for the network operations to complete or until the OS times out the connection [7]. It is best practice to always configure explicit timeouts via the AgentBuilder or Request methods to ensure predictable behavior and prevent potential hangs [1][4]. Summary of methods: - Agent-wide configuration: Use ureq::AgentBuilder to set defaults for all requests made by that agent [1][4]. - Per-request configuration: Use the.timeout method on an individual request to override the agent's settings [2][3]. Note: As documented, the.timeout method on the AgentBuilder takes precedence over.timeout_read and.timeout_write, but not.timeout_connect [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== src/ponytail/instructions.rs ==\n'
cat -n src/ponytail/instructions.rs | sed -n '1,120p'

printf '\n== dependency versions ==\n'
rg -n 'ureq|dirs' Cargo.toml **/Cargo.toml -S

printf '\n== any use of download_skill/skill_cache_path ==\n'
rg -n 'download_skill|skill_cache_path|SKILL_URL' src -S

Repository: getappz/agentflare

Length of output: 1959


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== inspect ureq API usage in lock/Cargo ==\n'
rg -n 'ureq' Cargo.lock Cargo.toml -S

printf '\n== inspect instructions module scope ==\n'
ast-grep outline src/ponytail/instructions.rs --view expanded

Repository: getappz/agentflare

Length of output: 1959


Pin the skill source and add an explicit timeout. SKILL_URL points at main, so this fetch can pull mutable instruction content into agent behavior; download_skill() also makes a blocking ureq request without any timeout configured. Pin the URL to a tag/commit and set a request timeout.

🤖 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/ponytail/instructions.rs` around lines 12 - 37, The skill fetch is
currently using a mutable `main` URL and a blocking `ureq` call without any
timeout, so update `SKILL_URL` in `download_skill()` to point to an immutable
tag or commit and configure an explicit request timeout on the
`ureq::get(...).call()` flow. Keep the existing cache/write logic in
`skill_cache_path()` and `download_skill()`, but ensure the source is pinned and
the network request cannot hang indefinitely.

Comment thread src/ponytail/instructions.rs
Comment thread src/ponytail/platform.rs
Comment on lines +33 to +49
AgentPlatform::Codex => {
let sys_msg = if event == "SessionStart" {
"PONYTAIL:FULL"
} else {
""
};
let mut output = json!({
"hookSpecificOutput": {
"hookEventName": event,
"additionalContext": ctx,
}
});
if !sys_msg.is_empty() {
output["systemMessage"] = json!(sys_msg);
}
output.to_string()
}

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant file and surrounding symbols first.
ast-grep outline src/ponytail/platform.rs --view expanded

# Show the file with line numbers if it's small enough.
wc -l src/ponytail/platform.rs
cat -n src/ponytail/platform.rs | sed -n '1,220p'

# Find where format_hook_output is called and how mode is passed around.
rg -n "format_hook_output|AgentPlatform::Codex|PONYTAIL:FULL|SessionStart|lite|ultra|mode" src

Repository: getappz/agentflare

Length of output: 1959


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the exact file and its callers if the repository is available.
wc -l src/ponytail/platform.rs
cat -n src/ponytail/platform.rs | sed -n '1,220p'

echo "---- callers ----"
rg -n "format_hook_output|AgentPlatform::Codex|PONYTAIL:FULL|SessionStart|lite|ultra|mode" src

Repository: getappz/agentflare

Length of output: 1959


🌐 Web query:

getappz agentflare src/ponytail/platform.rs format_hook_output PONYTAIL:FULL SessionStart

💡 Result:

The term "format_hook_output" and the prefix "PONYTAIL:FULL SessionStart" are associated with the "ponytail" project by Dietrich Gebert, an open-source tool designed to provide "rulesets" or "steering" for AI coding agents (such as those running in Claude Code, Cursor, Windsurf, and other environments) [1]. The project uses lifecycle hooks—specifically the SessionStart event—to inject instructions into the agent's context [2]. The specific construct PONYTAIL:FULL SessionStart relates to the operational logic of these hooks [3][2]. When a session begins, the hook ponytail-activate.js is triggered [2]. In the context of the ponytail project, "FULL" refers to the intensity or mode setting of the ruleset being applied [1]. The hook mechanism, particularly for events like SubagentStart, often uses a JSON-formatted output or specific string prefixes to communicate instructions to the host agent, ensuring that the rules governing the agent's behavior are injected during the startup phase [2]. It is important to distinguish this from "Agentflare" (or "agentflare"), which is a separate infrastructure platform for AI agents providing observability, cost tracking, and deployment tools [4][5][6]. There is no technical overlap between the ponytail project's hook system and Agentflare's core functionality based on the available documentation [4][1][5]. The mention of "getappz" in your query does not appear to be a recognized part of the official ponytail or Agentflare repositories; it may be a misidentified entity or a specific local naming convention [1][5][3]. In summary, the command or log format you referenced is part of the ponytail ruleset injection mechanism used to steer coding agents [1][2].

Citations:


🌐 Web query:

ponytail PONYTAIL:FULL lite ultra ruleset mode SessionStart

💡 Result:

Ponytail is a "lazy senior dev" mode/ruleset for AI coding assistants (such as Claude Code and OpenCode) designed to prioritize minimal, efficient, and YAGNI (You Ain't Gonna Need It) compliant code solutions [1][2][3]. The SessionStart hook is the mechanism used by the Ponytail plugin to automatically inject its ruleset and activate the desired intensity level whenever a new coding session begins [4][5][6]. Key intensity levels for the Ponytail ruleset include: - Lite: Builds what is requested but provides the lazier/simpler alternative in a single line for the user to choose [2][7][8]. - Full (Default): Follows a strict hierarchy: YAGNI -> standard library -> native platform features -> one-liner -> minimum necessary code [2][7][3]. - Ultra: A YAGNI-extremist mode that prioritizes code deletion over addition and challenges requirements before implementation [2][7][8]. You can configure the default behavior via an environment variable (PONYTAIL_DEFAULT_MODE) or a configuration file located at ~/.config/ponytail/config.json (or %APPDATA%\ponytail\config.json on Windows) [7][8][9]. Setting the mode to "off" disables automatic activation on session start, requiring manual activation via the /ponytail command [7][10]. Newer versions also include a SubagentStart hook to ensure the ruleset propagates to subagents during development tasks [4][11].

Citations:


Emit the configured Ponytail mode here
SessionStart currently always sends PONYTAIL:FULL, so Codex will advertise the default mode even when the active ruleset is lite or ultra. Pass the effective mode through instead of hardcoding the literal.

🤖 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/ponytail/platform.rs` around lines 33 - 49, The Codex branch in
platform.rs hardcodes the SessionStart system message to PONYTAIL:FULL, so it
ignores the active Ponytail mode. Update the AgentPlatform::Codex handling to
use the effective configured mode for SessionStart instead of the literal
string, and keep the systemMessage field conditional on that value being
present. Use the existing event check and the output construction in this branch
to locate the fix.

Comment on lines +17 to +20
Grep the repo for comment markers, skipping `node_modules`, `.git`, and build
output:

`grep -rnE '(#|//) ?ponytail:' .` (add other comment prefixes if your stack uses them)

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

Make the scan honor the exclusions it advertises.

The current grep command still walks node_modules, .git, and build output, so the debt ledger will pick up noise and slow down on larger repos.

Suggested fix
-`grep -rnE '(#|//) ?ponytail:' .`  (add other comment prefixes if your stack uses them)
+`grep -rnE --exclude-dir={node_modules,.git,build} '(#|//) ?ponytail:' .`  (add other comment prefixes if your stack uses them)
📝 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
Grep the repo for comment markers, skipping `node_modules`, `.git`, and build
output:
`grep -rnE '(#|//) ?ponytail:' .` (add other comment prefixes if your stack uses them)
Grep the repo for comment markers, skipping `node_modules`, `.git`, and build
output:
`grep -rnE --exclude-dir={node_modules,.git,build} '(#|//) ?ponytail:' .` (add other comment prefixes if your stack uses them)
🤖 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/ponytail/skill-debt.md` around lines 17 - 20, The grep command in the
ponytail scan still traverses excluded directories, so update the repo search
instruction to actually skip node_modules, .git, and build output when running
the comment-marker scan. Adjust the command in the skill-debt guidance so the
exclusions are enforced in the grep invocation itself, keeping the scan focused
on source files and reducing noise; refer to the repo-scanning instruction that
mentions grep and comment markers.

Comment thread src/ponytail/state.rs
Comment on lines +27 to +29
pub fn clear_active() {
let _ = std::fs::remove_file(flag_path());
}

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

clear_active silently swallows all I/O errors, not just "not found".

Any error (e.g., permission denied) is discarded, leaving the active-mode flag file in place while callers assume it was cleared. This could leave the runtime stuck in a mode the user tried to deactivate.

Proposed fix: surface non-NotFound errors
-pub fn clear_active() {
-    let _ = std::fs::remove_file(flag_path());
-}
+pub fn clear_active() -> io::Result<()> {
+    match std::fs::remove_file(flag_path()) {
+        Ok(()) => Ok(()),
+        Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(()),
+        Err(e) => Err(e),
+    }
+}
📝 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
pub fn clear_active() {
let _ = std::fs::remove_file(flag_path());
}
pub fn clear_active() -> io::Result<()> {
match std::fs::remove_file(flag_path()) {
Ok(()) => Ok(()),
Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(()),
Err(e) => Err(e),
}
}
🤖 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/ponytail/state.rs` around lines 27 - 29, The clear_active helper is
swallowing every remove_file failure instead of only ignoring a missing flag
file. Update clear_active to handle the std::fs::remove_file(flag_path()) result
explicitly: ignore only NotFound, and surface or log any other I/O error such as
permission denied so callers know the active flag was not actually cleared.

Comment on lines +1 to +5
pub const SKILL_REVIEW: &str = include_str!("skill-review.md");
pub const SKILL_AUDIT: &str = include_str!("skill-audit.md");
pub const SKILL_DEBT: &str = include_str!("skill-debt.md");
pub const SKILL_GAIN: &str = include_str!("skill-gain.md");
pub const SKILL_HELP: &str = include_str!("skill-help.md");

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 | 🔴 Critical | ⚡ Quick win

Compile-time dependency on files from an undeclared/later layer — matches pipeline failures.

The stack context lists skill-review.md, skill-audit.md, skill-debt.md, skill-gain.md, skill-help.md under a separate layer ("Sub-skill markdown documentation") with no declared dependency link back to this layer ("Instruction building and skill downloading"). Yet these include_str! macros require those exact files to exist at compile time. The pipeline failure logs confirm this: macro expansion failed for include_str! on SKILL_DEBT, SKILL_GAIN, and SKILL_HELP (lines 3-5), consistent with those markdown files being missing when this layer is built in isolation.

If layers are merged/reviewed independently, this file will fail to compile until the sub-skill markdown layer lands first — the stack dependency graph should reflect that.

#!/bin/bash
fd -a . src/ponytail -e md
🧰 Tools
🪛 GitHub Actions: PR `#45` / Analyze (rust)

[warning] 3-3: CodeQL/Rust extraction warning: macro expansion failed for 'include_str'.


[warning] 4-4: CodeQL/Rust extraction warning: macro expansion failed for 'include_str'.


[warning] 5-5: CodeQL/Rust extraction warning: macro expansion failed for 'include_str'.

🤖 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/ponytail/sub_skills.rs` around lines 1 - 5, The compile-time includes in
sub_skills.rs are depending on markdown files that belong to a later/undeclared
layer, which causes build failures when this layer is compiled in isolation.
Update the dependency structure so the layer containing SKILL_REVIEW,
SKILL_AUDIT, SKILL_DEBT, SKILL_GAIN, and SKILL_HELP is declared after or
dependent on the sub-skill markdown documentation layer, or otherwise move these
include_str! references to a layer where the files are guaranteed to exist at
compile time. Verify the constants in sub_skills.rs only reference files that
are available when the module is built.

Source: Pipeline failures

@getappz
getappz merged commit ef8d5e7 into master Jul 7, 2026
10 checks passed
@getappz
getappz deleted the feature/ponytail-l1-integration branch July 7, 2026 15:23
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 7, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

L1 ponytail integration — port runtime hooks to Rust

1 participant