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
7 changes: 7 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ Review agents must not run Git or GitHub commands against this repository.
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`.
- For Python conversions, preserve required public behavior rather than
translating Bash verbatim. Simplify the design with idiomatic Python,
standard-library operations, and focused reuse within the owning package.
Remove unnecessary Bash shims, obsolete aliases, and compatibility branches;
update callers and tests to invoke the frozen package entrypoints directly.
Retain an adapter only for an explicitly required external contract or an
actual host protocol, not merely because the old script path existed.
- 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
Expand Down
3 changes: 3 additions & 0 deletions docs/specs/discovery.md
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,9 @@ skill.
from the installed skill directory and invoke the same locked package
entrypoint directly. Fresh-artifact checks exercise that installed layout
without repository-relative references or platform-specific launchers.
Internal simplification preserves literal flag values, last-wins scalar
options, ordered repeated option labels, validation order, and removal of
exactly one terminal period before rendering prose.

## Evaluation requirements

Expand Down
22 changes: 22 additions & 0 deletions docs/specs/python-quality.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,28 @@ Automation cannot judge every aspect of those rules. Reviewers inspect names,
module cohesion, function focus, error boundaries, and whether assertions
verify observable behavior rather than implementation details.

## Converting existing mechanics

Treat a Bash implementation as evidence of required behavior, not a Python
design template. Establish the public inputs, outputs, refusals, side effects,
and ordering constraints before refactoring. Prefer idiomatic Python and
standard-library operations over hand-written equivalents; replace shell-style
control flow and duplicated mechanics with direct operations and focused reuse
within the owning module or package. Keep abstractions proportional to their
actual callers and keep plugins independently installable.

Audit the complete invocation surface during the conversion. Remove unnecessary
Bash shims, obsolete aliases, and backward-compatibility branches. Update skill
instructions, documentation, callers, fixtures, and tests to the canonical
frozen package entrypoints. Retain an adapter only when an explicitly required
external contract or actual host protocol needs it; an old script path alone
is not such a requirement. Keep shell regression tests as tests, not runtime
entrypoints, and verify the copied plugin without the removed paths.

Use behavioral and differential checks to preserve the required interface while
changing the internals. High coverage is a guard for simplification, not a reason
to keep redundant code or mirror the old implementation in new tests.

## Tests and coverage

Each package keeps fast unit tests for local behavior and integration tests for
Expand Down
12 changes: 12 additions & 0 deletions docs/specs/skill-authoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ checkable, and recorded evidence for trigger behavior and workflow quality.
contract inputs instead of importing them from the authoring skill's source
repository. Do not add a script when existing tools and instructions are
already reliable.
A migration preserves the required public behavior, not the source
implementation's structure. When Python is selected, use idiomatic Python
and standard-library operations, simplify control flow, and reuse focused
mechanics within the package instead of translating shell code verbatim.
Remove obsolete wrappers, aliases, and compatibility branches, updating
callers and tests to the canonical entrypoints. A legacy path alone does not
establish a requirement to retain a shim.
- **SA-C6 — Fail closed and identify evidence.** A required unreadable skill,
manifest, configuration, instruction, or referenced local resource blocks the
dependent result. Model-facing script output identifies inspected inputs and
Expand All @@ -106,6 +113,11 @@ checkable, and recorded evidence for trigger behavior and workflow quality.
their executing interpreter in nested implementation calls. A packaged skill
remains useful with no MCP server or other Darrow plugin installed unless its
own manifest declares and supplies that dependency.
This plugin exposes its inspector and shell-test matrix only through their
frozen UV console entrypoints, without legacy launchers or module-execution
aliases. Internal simplification preserves argument consumption, interpreter
selection, diagnostic order, output records, and exit statuses. Regression
shell suites live with the backend tests, not as runtime skill scripts.
- **SA-C8 — Evaluation separates prompt from criteria.** Participant-visible
eval prompts contain the task and repository evidence but not their pass
criteria. Hidden deterministic checks or rubrics cover workflow behavior,
Expand Down
3 changes: 3 additions & 0 deletions docs/specs/verification.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ delivery under #155. It does not execute QA or create reviewer-facing evidence p
skill directory and invoke the same frozen package entrypoint directly.
Fresh copied-artifact checks MUST exercise that installed layout without
repository-relative or sibling-plugin references.
Internal simplification MUST preserve assessment-before-report validation,
exact diagnostics and exit statuses, assessment bytes, and the existing
seven-character destination escape set without double-escaping replacements.

## Consumer handoff

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "darrow-discovery",
"description": "Explicit grilling, feature discovery, and implementation planning",
"version": "0.2.0",
"version": "0.2.1",
"license": "BUSL-1.1",
"author": {
"name": "Björn Rochel",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "darrow-discovery",
"version": "0.2.0",
"version": "0.2.1",
"description": "Explicit grilling, feature discovery, and implementation planning",
"author": {
"name": "Björn Rochel",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "darrow-discovery"
version = "0.2.0"
version = "0.2.1"
description = "Deterministic helpers for Darrow discovery and planning"
requires-python = ">=3.10,<3.14"
dependencies = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
from io import TextIOWrapper
from typing import cast

PROGRAM_NAME = "darrow-render-plan-frontier"
USAGE = (
"usage: {program} --evidence TEXT --question TEXT --option LABEL "
f"usage: {PROGRAM_NAME} --evidence TEXT --question TEXT --option LABEL "
"--option LABEL [--option LABEL ...] --choice LABEL --rationale TEXT "
"--deferred TEXT"
)
PROGRAM_NAME = "darrow-render-plan-frontier"
FIELD_FLAGS = {
"--evidence": "evidence",
"--question": "question",
Expand Down Expand Up @@ -47,18 +47,16 @@ def parse(arguments: Sequence[str]) -> Frontier:
"""Parse the renderer's stable flag-and-value command contract."""
fields = dict.fromkeys(FIELD_FLAGS.values(), "")
options: list[str] = []
index = 0
while index < len(arguments):
if index + 1 >= len(arguments):
raise UsageError
if len(arguments) % 2:
raise UsageError
for index in range(0, len(arguments), 2):
flag, value = arguments[index : index + 2]
if flag == "--option":
options.append(value)
elif flag in FIELD_FLAGS:
fields[FIELD_FLAGS[flag]] = value
else:
raise UsageError
index += 2
return Frontier(options=tuple(options), **fields)


Expand Down Expand Up @@ -128,15 +126,11 @@ def validate(frontier: Frontier) -> None:
_validate_choice(frontier)


def _without_one_period(value: str) -> str:
return value[:-1] if value.endswith(".") else value


def render(frontier: Frontier) -> str:
"""Render an already validated frontier in the canonical Markdown shape."""
evidence = _without_one_period(frontier.evidence)
rationale = _without_one_period(frontier.rationale)
deferred = _without_one_period(frontier.deferred)
evidence = frontier.evidence.removesuffix(".")
rationale = frontier.rationale.removesuffix(".")
deferred = frontier.deferred.removesuffix(".")
return (
f"Evidence: {evidence}.\n\n"
f"Q1 — {frontier.question}\n\n"
Expand All @@ -146,14 +140,13 @@ def render(frontier: Frontier) -> str:
)


def main(arguments: Sequence[str] | None = None, *, program: str | None = None) -> int:
def main(arguments: Sequence[str] | None = None) -> int:
"""Run the renderer and return its public process status."""
actual_arguments = tuple(sys.argv[1:] if arguments is None else arguments)
actual_program = PROGRAM_NAME if program is None else program
try:
frontier = parse(actual_arguments)
except UsageError:
print(USAGE.format(program=actual_program), file=sys.stderr)
print(USAGE, file=sys.stderr)
return 2
try:
validate(frontier)
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "darrow-verification",
"version": "0.2.0",
"version": "0.2.1",
"description": "Bounded acceptance verification through replaceable independent code review",
"license": "BUSL-1.1",
"author": { "name": "Björn Rochel", "email": "bjoern@bjro.de" }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "darrow-verification",
"version": "0.2.0",
"version": "0.2.1",
"description": "Bounded acceptance verification through replaceable independent code review",
"author": { "name": "Björn Rochel", "email": "bjoern@bjro.de" },
"skills": "./skills/",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "darrow-verification"
version = "0.2.0"
version = "0.2.1"
description = "Deterministic helpers for Darrow verification"
requires-python = ">=3.10,<3.14"
dependencies = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@
"--provider-report ABSOLUTE_FILE"
)
ERROR_PREFIX = "render-assessment"
DESTINATION_ESCAPES = str.maketrans(
{
"%": "%25",
" ": "%20",
"#": "%23",
"?": "%3F",
"<": "%3C",
">": "%3E",
"\\": "%5C",
}
)


class BinaryWriter(Protocol):
Expand Down Expand Up @@ -82,13 +93,8 @@ def canonical_input(value: str, label: str) -> Path:


def escape_destination(value: str) -> str:
"""Escape the Markdown URI delimiters escaped by the original renderer."""
destination = value.replace("%", "%25")
destination = destination.replace(" ", "%20")
destination = destination.replace("#", "%23")
destination = destination.replace("?", "%3F")
destination = destination.replace("<", "%3C").replace(">", "%3E")
return destination.replace("\\", "%5C")
"""Escape only the Markdown URI delimiters in the handoff contract."""
return value.translate(DESTINATION_ESCAPES)


def validate(arguments: Sequence[str]) -> AssessmentInputs:
Expand Down Expand Up @@ -124,17 +130,13 @@ def main(
"""Run the renderer and return its public process status."""
try:
inputs = validate(arguments)
output = render(inputs)
except UsageError:
_write_error(stderr, USAGE)
return 2
except InputError as error:
_write_error(stderr, str(error))
return 2
try:
output = render(inputs)
except InputError as error:
_write_error(stderr, str(error))
return 2
stdout.write(output)
return 0

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "darrow-skill-authoring",
"description": "Create and improve reliable cross-runtime agent skills",
"version": "0.2.1",
"version": "0.2.3",
"license": "BUSL-1.1",
"author": {
"name": "Björn Rochel",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "darrow-skill-authoring",
"version": "0.2.1",
"version": "0.2.3",
"description": "Create and improve reliable cross-runtime agent skills",
"author": {
"name": "Björn Rochel",
Expand Down
8 changes: 4 additions & 4 deletions plugins/foundation/darrow-skill-authoring/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,16 @@ uv sync --quiet --frozen --no-dev --project skills/author-agent-skill/backend
```

From the repository root, run the Python quality checks with
`bun run check:python`. From this plugin directory, run the compatibility shell
`bun run check:python`. From this plugin directory, run the shell regression
suites through the version-aware matrix:

```text
uv run --quiet --frozen --no-dev \
--project skills/author-agent-skill/backend \
verify-shell-tests -- \
skills/author-agent-skill/scripts/inspect-skill.test.sh \
skills/author-agent-skill/scripts/verify-shell-tests.test.sh \
skills/author-agent-skill/scripts/interpreter-routing.test.sh
skills/author-agent-skill/backend/tests/shell/inspect-skill.test.sh \
skills/author-agent-skill/backend/tests/shell/verify-shell-tests.test.sh \
skills/author-agent-skill/backend/tests/shell/interpreter-routing.test.sh
```

The matrix reports observed interpreter versions. Exit `3` leaves an unavailable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ backend:
uv run --quiet --frozen --no-dev --project "<skill-dir>/backend" verify-shell-tests -- "<test-script>"...
```

Base every shell-version claim on its emitted evidence. The small scripts with
the same command names preserve compatibility for existing POSIX callers.
Base every shell-version claim on its emitted evidence. Invoke both packaged
commands directly; this plugin has no runtime shell launchers.

## Workflow

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "darrow-skill-authoring"
version = "0.1.0"
version = "0.1.1"
description = "Portable deterministic helpers for Darrow skill authoring"
requires-python = ">=3.10,<3.14"
dependencies = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,22 +113,14 @@ def _validate_metadata(metadata: Metadata, skill_directory: Path) -> Metadata:
return Metadata(name, description)


def _is_within(path: Path, root: Path) -> bool:
try:
path.relative_to(root)
except ValueError:
return False
return True


def _skill_directory(skill_input: str, plugin_root: Path) -> Path:
attempted = _attempted_path(skill_input)
if not attempted.is_dir():
raise InspectionError(
f"skill file is not a readable regular file: {attempted / 'SKILL.md'}"
)
skill_directory = _readable_directory(skill_input, "skill directory")
if not _is_within(skill_directory, plugin_root):
if not skill_directory.is_relative_to(plugin_root):
raise InspectionError(f"skill directory escapes plugin root: {skill_directory}")
return skill_directory

Expand Down Expand Up @@ -182,7 +174,7 @@ def _local_reference(target: str, skill_directory: Path, plugin_root: Path) -> P
f"local reference parent is not readable: {parent_input}"
) from error
reference = parent / relative.name
if not _is_within(reference, plugin_root):
if not reference.is_relative_to(plugin_root):
raise InspectionError(f"local reference escapes plugin root: {reference}")
if not reference.is_file() or not os.access(reference, os.R_OK):
raise InspectionError(
Expand Down Expand Up @@ -297,7 +289,3 @@ def run(arguments: list[str], *, stdout: TextIO, stderr: TextIO) -> int:

def entrypoint() -> None:
raise SystemExit(run(sys.argv[1:], stdout=sys.stdout, stderr=sys.stderr))


if __name__ == "__main__":
entrypoint()
Loading
Loading