Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 127 additions & 11 deletions .github/workflows/python-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,79 @@ permissions:
contents: read

jobs:
changes:
name: Python change scopes
runs-on: ubuntu-latest
outputs:
packages: ${{ steps.scopes.outputs.packages }}
package_selected: ${{ steps.scopes.outputs.package_selected }}
skill_authoring: ${{ steps.scopes.outputs.skill_authoring }}
observability_langfuse: ${{ steps.scopes.outputs.observability_langfuse }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- id: scopes
name: Select changed Python packages
env:
EVENT_NAME: ${{ github.event_name }}
BEFORE_SHA: ${{ github.event.before }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
if [ "$EVENT_NAME" = workflow_dispatch ]; then
scripts/detect-python-quality-changes --all >>"$GITHUB_OUTPUT"
exit 0
fi

if [ "$EVENT_NAME" = pull_request ]; then
scripts/detect-python-quality-changes "$BASE_SHA" "$HEAD_SHA" >>"$GITHUB_OUTPUT"
exit 0
fi

zero=0000000000000000000000000000000000000000
if [ "$BEFORE_SHA" = "$zero" ]; then
scripts/detect-python-quality-changes --all >>"$GITHUB_OUTPUT"
else
scripts/detect-python-quality-changes "$BEFORE_SHA" "$GITHUB_SHA" >>"$GITHUB_OUTPUT"
fi

package:
name: Python ${{ matrix.python-version }} on ${{ matrix.os }}
name: ${{ matrix.package }} Python ${{ matrix.python-version }} on ${{ matrix.os }}
needs: changes
if: needs.changes.outputs.package_selected == 'true'
strategy:
fail-fast: false
matrix:
package: ${{ fromJSON(needs.changes.outputs.packages) }}
os: [ubuntu-latest, macos-latest]
python-version: ["3.10", "3.11", "3.12", "3.13"]
runs-on: ${{ matrix.os }}
env:
PACKAGE: ${{ matrix.package }}
UV_PYTHON: ${{ matrix.python-version }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- uses: astral-sh/setup-uv@v6
with:
enable-cache: true
- run: scripts/check-python --package "$PACKAGE"

skill-authoring-windows:
name: Skill authoring Python ${{ matrix.python-version }} on windows-latest
needs: changes
if: needs.changes.outputs.skill_authoring == 'true'
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
runs-on: windows-latest
defaults:
run:
shell: bash
env:
UV_PYTHON: ${{ matrix.python-version }}
steps:
Expand All @@ -28,17 +93,50 @@ jobs:
- uses: astral-sh/setup-uv@v6
with:
enable-cache: true
- run: scripts/check-python
- run: scripts/check-python --package plugins/foundation/darrow-skill-authoring/skills/author-agent-skill/backend

inventory:
name: Python inventory guard
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: scripts/test-python-inventory
- run: scripts/test-python-quality-changes

skill-authoring-fresh-install:
name: Fresh install skill-authoring on ${{ matrix.os }}
needs: changes
if: needs.changes.outputs.skill_authoring == 'true'
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
shell: bash
test: plugins/foundation/darrow-skill-authoring/tests/fresh-install.test.sh
- os: macos-latest
shell: bash
test: plugins/foundation/darrow-skill-authoring/tests/fresh-install.test.sh
- os: windows-latest
shell: pwsh
test: plugins/foundation/darrow-skill-authoring/tests/fresh-install.test.ps1
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- uses: astral-sh/setup-uv@v6
- if: matrix.shell == 'bash'
run: bash "${{ matrix.test }}"
- if: matrix.shell == 'pwsh'
shell: pwsh
run: "& '${{ matrix.test }}'"

fresh-install:
name: Python fresh plugin install
observability-fresh-install:
name: Fresh install observability-langfuse on ubuntu-latest
needs: changes
if: needs.changes.outputs.observability_langfuse == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -50,6 +148,8 @@ jobs:

performance:
name: Python performance invariants
needs: changes
if: needs.changes.outputs.observability_langfuse == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -62,17 +162,33 @@ jobs:
aggregate:
name: Python quality
if: always()
needs: [package, inventory, fresh-install, performance]
needs:
- changes
- package
- skill-authoring-windows
- inventory
- skill-authoring-fresh-install
- observability-fresh-install
- performance
runs-on: ubuntu-latest
steps:
- name: Require every Python quality job
- uses: actions/checkout@v4
- name: Require every selected Python quality job
env:
CHANGES_RESULT: ${{ needs.changes.result }}
PACKAGE_SELECTED: ${{ needs.changes.outputs.package_selected }}
PACKAGE_RESULT: ${{ needs.package.result }}
SKILL_AUTHORING_CHANGED: ${{ needs.changes.outputs.skill_authoring }}
OBSERVABILITY_CHANGED: ${{ needs.changes.outputs.observability_langfuse }}
WINDOWS_RESULT: ${{ needs.skill-authoring-windows.result }}
INVENTORY_RESULT: ${{ needs.inventory.result }}
INSTALL_RESULT: ${{ needs.fresh-install.result }}
SKILL_INSTALL_RESULT: ${{ needs.skill-authoring-fresh-install.result }}
OBSERVABILITY_INSTALL_RESULT: ${{ needs.observability-fresh-install.result }}
PERFORMANCE_RESULT: ${{ needs.performance.result }}
run: |
test "$PACKAGE_RESULT" = success
test "$INVENTORY_RESULT" = success
test "$INSTALL_RESULT" = success
test "$PERFORMANCE_RESULT" = success
scripts/verify-python-quality-results \
"$CHANGES_RESULT" "$INVENTORY_RESULT" \
"$PACKAGE_SELECTED" "$PACKAGE_RESULT" \
"$SKILL_AUTHORING_CHANGED" "$WINDOWS_RESULT" "$SKILL_INSTALL_RESULT" \
"$OBSERVABILITY_CHANGED" "$OBSERVABILITY_INSTALL_RESULT" \
"$PERFORMANCE_RESULT"
20 changes: 12 additions & 8 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,22 @@ intent-level inputs instead of reproducing call details.

Review agents must not run Git or GitHub commands against this repository.

## Shell portability

- Write plugin-shipped executable mechanics in portable Bash, using baseline
Unix utilities and the host CLIs the capability wraps. Do not add Python,
JavaScript/TypeScript, Ruby, JVM, or compiled runtime dependencies. Shared
repository development and eval infrastructure such as `evals/runner/` is
outside this plugin-runtime boundary.
## Plugin mechanics

- Use a contained Python package managed by UV for substantial deterministic
plugin mechanics when Python materially improves clarity or native Windows
support. Commit `pyproject.toml` and `uv.lock`, keep runtime and development
dependencies separate, invoke runtime entrypoints with frozen resolution, and
register the package in `python-packages.txt`.
- Keep genuinely small host-specific launchers and command glue in portable
Bash, using baseline Unix utilities and the host CLIs the capability wraps.
Do not introduce JavaScript/TypeScript, Ruby, JVM, compiled, or shared Darrow
runtime dependencies inside a plugin.
- Avoid early-exit pipelines under `pipefail`; use here-strings for bounded
matching.
- Avoid `${var//pat/}` on unbounded input and `awk -v` for backslash-bearing
values.
- Support old BSD awk and Bash 3.2 in plugin scripts.
- Support old BSD awk and Bash 3.2 in plugin shell scripts.
- Refuse unreadable configuration rather than silently skipping it.
- Use absolute paths in model-facing output.
- Resolve the primary repository via the first `git worktree list --porcelain`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# ADR-0005: Use portable Bash facades for plugin mechanics

Status: Accepted
Status: Superseded
Date: 2026-08-13
Summary: Put deterministic plugin mechanics behind narrow portable Bash facades while keeping authority and contextual judgment in skills.
Revisit when: Every supported Claude Code and Codex host provides a common, independently installable runtime that is more portable than Bash 3.2 plus baseline Unix utilities, or the supported macOS boundary no longer includes Bash 3.2.
Superseded by: ADR-0009

## Context

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# ADR-0009: Adopt Python and UV for substantial plugin mechanics

Status: Accepted
Date: 2026-09-17
Summary: Adopt contained Python packages managed by UV incrementally for substantial cross-platform plugin mechanics while retaining portable Bash for small host-specific glue.
Supersedes: ADR-0005
Revisit when: UV and supported Python cannot provide independently installable helpers across every supported native host, or a lighter common runtime offers materially better portability and containment.

## Context

ADR-0005 standardized portable Bash because it was available on Darrow's
original macOS and Linux hosts without another runtime. That kept plugins
independent, but substantial parsers and filesystem validators accumulated
conservative shell code and remained unavailable on native Windows.

The Langfuse plugin established the repository's contained Python and UV
pattern: a plugin-local package and lock, frozen execution, separate runtime and
development dependencies, fresh-artifact validation, and repository-wide
Python quality gates. Issue #168 selects incremental migration so each plugin
can adopt that established pattern without creating a shared Darrow runtime.

## Decision

Use a contained Python package managed by UV for substantial deterministic
plugin mechanics when Python materially improves clarity, testing, or native
Windows support.

- Keep the package, `pyproject.toml`, `uv.lock`, entrypoints, dependencies,
environments, and state inside the owning plugin.
- Invoke public runtime entrypoints with frozen resolution and without
development dependencies. Keep development tools in the locked development
group and register each package in the repository Python inventory.
- Validate cross-platform entrypoints from a fresh copied plugin artifact on
Linux, macOS, and native Windows. Apply the repository Python quality gates
across the declared Python support range.
- Retain portable Bash for genuinely small host-specific launchers and command
glue. Such scripts continue to support Bash 5 and macOS `/bin/bash` 3.2.
- Adopt Python incrementally. A plugin without substantial cross-platform
mechanics does not need a Python package, and no plugin may depend on a
sibling package or shared Darrow runtime.
- Keep contextual judgment, authority, and material choices in `SKILL.md`;
language choice does not expand a helper's responsibility.

ADR-0008 remains the authoritative record for the Langfuse plugin's hook and
failure semantics. This decision generalizes the established packaging and
quality model to later plugin migrations.

## Consequences

- Migrated helpers can run through the same locked console entrypoints on
native Windows, macOS, and Linux.
- Substantial mechanics gain strict typing, focused unit and integration tests,
property tests where useful, and separate line and branch coverage gates.
- Adopters of a Python-backed plugin need UV and a supported UV-managed Python;
plugins that have not migrated keep their existing prerequisites.
- Small POSIX launchers remain inspectable and compatible with legacy hosts,
while their substantial logic no longer carries Bash and BSD utility limits.
- Every migration must preserve the plugin's independent installation boundary
and prove behavior at its existing public entrypoints.
7 changes: 5 additions & 2 deletions docs/decisions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,19 @@ Rebuild it with `decision catalog rebuild` and verify it with `decision catalog
| [ADR-0002: Separate capabilities from orchestration](ADR-0002-separate-capabilities-from-orchestration.md) | Accepted | 2026-08-13 | Keep capabilities intent-matched and independently selectable while starting continuation-owning orchestration only through explicit user invocation. | None |
| [ADR-0003: Treat plugins as optionality boundaries](ADR-0003-treat-plugins-as-optionality-boundaries.md) | Accepted | 2026-08-13 | Treat each plugin as a self-contained unit of adoption, compatibility, and ownership that composes through host-visible skill intent rather than sibling dependencies or a separate capability registry. | None |
| [ADR-0004: Use native goal ownership for core orchestration](ADR-0004-use-native-goal-ownership-for-core-orchestration.md) | Accepted | 2026-08-13 | Use Darrow to compile and launch one host-native goal owner instead of operating a second execution controller or general workflow runtime. | Revisit when: Matched multi-trial evidence on supported hosts shows that a Darrow-owned execution controller materially improves task outcomes over native goal ownership after accounting for wall time, model usage, child invocations, and human interruptions. |
| [ADR-0005: Use portable Bash facades for plugin mechanics](ADR-0005-use-portable-bash-facades-for-plugin-mechanics.md) | Accepted | 2026-08-13 | Put deterministic plugin mechanics behind narrow portable Bash facades while keeping authority and contextual judgment in skills. | Revisit when: Every supported Claude Code and Codex host provides a common, independently installable runtime that is more portable than Bash 3.2 plus baseline Unix utilities, or the supported macOS boundary no longer includes Bash 3.2. |
| [ADR-0006: Keep decisions with their authoritative owners](ADR-0006-keep-decisions-with-their-authoritative-owners.md) | Accepted | 2026-08-13 | Keep each decision at the narrowest durable authoritative owner its consumers obey, with one canonical sink per effect and honest gaps for inaccessible owners. | None |
| [ADR-0007: Separate skill evaluation evidence dimensions](ADR-0007-separate-skill-evaluation-evidence-dimensions.md) | Accepted | 2026-08-13 | Represent invariant coverage, task outcomes, matched skill ablation, and skill activation as separate evaluation evidence dimensions. | None |
| [ADR-0008: Allow Python and UV for Langfuse observability](ADR-0008-allow-python-and-uv-for-langfuse-observability.md) | Accepted | 2026-08-24 | Permit the independently installable Langfuse observability plugin to use a locked Python backend managed by UV while retaining a portable Bash hook launcher and keeping the exception scoped to that plugin. | Revisit when: Codex exposes equivalent native Langfuse export, the Langfuse SDK no longer requires Python, or the plugin can meet its rollout-reconstruction and export contract with the portable Bash baseline alone. |
| [ADR-0009: Adopt Python and UV for substantial plugin mechanics](ADR-0009-adopt-python-and-uv-for-substantial-plugin-mechanics.md) | Accepted | 2026-09-17 | Adopt contained Python packages managed by UV incrementally for substantial cross-platform plugin mechanics while retaining portable Bash for small host-specific glue. | Supersedes: ADR-0005; Revisit when: UV and supported Python cannot provide independently installable helpers across every supported native host, or a lighter common runtime offers materially better portability and containment. |

<!-- darrow-source: 1a57cf608fb4cfa4b770abebf34ca450e79c0160 2711262332 330 ADR-0001-eval-runner.md -->
<!-- darrow-source: cd9996efb2f66b1602ead9a405a48686e14f67a9 1820997609 370 ADR-0002-separate-capabilities-from-orchestration.md -->
<!-- darrow-source: 452327e39a9dff558c6897356d0e1e85cf65906f 3673420192 418 ADR-0003-treat-plugins-as-optionality-boundaries.md -->
<!-- darrow-source: 42bc1095831a4d097a6bda6462d29315bc0f0529 2382776868 628 ADR-0004-use-native-goal-ownership-for-core-orchestration.md -->
<!-- darrow-source: 1aa2b5acc9ffef09063953d78bfe04c26cab4089 3642678449 590 ADR-0005-use-portable-bash-facades-for-plugin-mechanics.md -->
<!-- darrow-source: 739b2fa46ad8b0ed220c4d341317bf670d3d7a41 243106577 398 ADR-0006-keep-decisions-with-their-authoritative-owners.md -->
<!-- darrow-source: ae5948d48fa4b0b0ce2c80ea76e23a71d582aa71 4074249863 369 ADR-0007-separate-skill-evaluation-evidence-dimensions.md -->
<!-- darrow-source: aab600b869cf357f81f4424f1813a75596d0a64c 2479862079 646 ADR-0008-allow-python-and-uv-for-langfuse-observability.md -->
<!-- darrow-source: 24304903fc02764a9db10fa7614f159e33c7718c 1605599603 622 ADR-0009-adopt-python-and-uv-for-substantial-plugin-mechanics.md -->

## Rejected

Expand All @@ -51,3 +51,6 @@ Rebuild it with `decision catalog rebuild` and verify it with `decision catalog
<!-- prettier-ignore -->
| Decision | Status | Date | Summary | Relationships |
| --- | --- | --- | --- | --- |
| [ADR-0005: Use portable Bash facades for plugin mechanics](ADR-0005-use-portable-bash-facades-for-plugin-mechanics.md) | Superseded | 2026-08-13 | Put deterministic plugin mechanics behind narrow portable Bash facades while keeping authority and contextual judgment in skills. | Superseded by: ADR-0009 |

<!-- darrow-source: 4121fc1ae3de0d07c230af478cf687fca0ff8766 3926343988 378 ADR-0005-use-portable-bash-facades-for-plugin-mechanics.md -->
Loading
Loading