Fin Extension System Review (2026-02-25)¶
This document is a thorough performer review of the contract-v1 fin adapter implemented in recent commits, focused on:
- compliance with the v1 spec/handoff requirements
- what external users can do without modifying Shoal source
- quality, risks, and concrete hardening steps
Reviewed commits:
c07731cfeat: add fin contract v1 runtime and clia806dd1docs: map extension capabilities and next milestones
Follow-up implementation status (2026-02-27):
- Added
shoal fin installandshoal fin configure - Added
shoal fin ls [--path <dir-or-fin.toml>] - Added
SHOAL_LOG_LEVELpropagation in fin runtime env handshake - Added cross-repo contract guard test using a scaffold generated by
fins-template
Spec references:
../fins-template/docs/fin-contract-v1.md../fins-template/docs/shoal-cli-fin-integration-handoff.md
Executive assessment¶
The Stage 1 adapter is implemented correctly and is usable for external,
path-based fins. Users can author and run fins from outside Shoal and invoke
them through shoal fin inspect|validate|run without editing Shoal code.
The biggest remaining limitations are lifecycle completeness (install,
configure) and discovery ergonomics (fin ls / install sources), not core
runtime correctness.
Spec compliance checklist¶
| Requirement | Status | Evidence |
|---|---|---|
Add shoal fin command group |
PASS | src/shoal/cli/__init__.py registers fin typer |
Implement inspect command |
PASS | src/shoal/cli/fin.py fin_inspect() |
Implement validate command with --strict |
PASS | src/shoal/cli/fin.py fin_validate(); strict forwarded in src/shoal/services/fin_runtime.py |
Implement run command with passthrough args after -- |
PASS | src/shoal/cli/fin.py fin_run() and args passthrough |
Parse fin.toml and validate schema |
PASS | src/shoal/services/fin_runtime.py + src/shoal/models/fin.py |
Enforce contract version v1 (fin_contract_version == 1) |
PASS | src/shoal/services/fin_runtime.py explicit version gate |
| Resolve entrypoints from fin root and ensure executable | PASS | resolve_entrypoint() checks existence/file/executable |
| Prevent entrypoint escaping fin root | PASS | resolve() + relative_to(fin_root) guard |
| Execute fin wrappers as subprocesses | PASS | subprocess.run() in execute_entrypoint() |
Invocation cwd is fin root |
PASS | subprocess.run(..., cwd=fin_root, ...) |
Set SHOAL_FIN_ROOT |
PASS | _build_env() sets required variable |
Set/unset SHOAL_FIN_CONFIG based on user input |
PASS | _build_env() sets when present; removes when absent |
Support SHOAL_OUTPUT_FORMAT text/json handshake |
PASS | _build_env() sets value from CLI option |
| Preserve wrapper non-zero exit code at CLI boundary | PASS | typer.Exit(result.exit_code) in validate/run |
| Show stdout/stderr from wrapper | PASS | CLI echoes captured stdout/stderr |
| Include tests for arg passthrough and exit propagation | PASS | tests/test_cli_fin.py, tests/test_services_fin_runtime.py |
Propagate SHOAL_LOG_LEVEL (recommended env var) |
PASS | _build_env() derives level via getEffectiveLevel() (numeric), parent env override preserved |
Stage 1 lifecycle completeness (install, configure) |
SHIPPED v0.22.0 | shoal fin install, shoal fin configure |
Discovery (fin ls, local registration) |
SHIPPED v0.22.0 | fin ls defaults to installed; fin install --no-register opt-out |
What users can do today without editing Shoal¶
Users can implement a fin externally (outside Shoal source), then:
- inspect manifest + resolved entrypoints:
shoal fin inspect <path> - run fin self-checks with strict mode:
shoal fin validate <path> --strict - run fin capability with optional config and passthrough args:
shoal fin run <path> --config <file> -- --arg1 --arg2
This is enough to support a practical external author workflow for path-based execution with a stable contract handshake.
Gaps affecting extension UX and platform completeness¶
- Lifecycle surface is partial
- ~~Missing first-class
shoal fin installandshoal fin configure~~. — shipped in v0.22.0 - Discovery and distribution are absent
- ~~Missing
shoal fin lsand local/remote source installation UX.~~fin lsand local registration shipped in v0.22.0; remote/registry sources remain open. - Completion ergonomics lag behind command surface
- Fish completions template does not yet advertise
finsubcommands. - Validation hardening can go deeper
- Current tests are good but do not yet cover malformed TOML and broader invalid-manifest permutations as explicitly called out in milestones.
Risk and quality notes¶
- Good security boundary for Stage 1: entrypoint path escape is blocked.
- Error quality is generally actionable and user-facing.
- Runtime currently buffers all output and has no timeout controls; hung fins can block invocation indefinitely.
- Manifest semantic constraints (like stricter identifier/version validation) are intentionally light for now.
Prioritized hardening plan (atomic)¶
P0: Close Stage 1 lifecycle parity¶
- ~~Add
shoal fin install <fin-path> [--config <path>]~~ — shipped in v0.22.0 - ~~Add
shoal fin configure <fin-path> [--config <path>]~~ — shipped - Ensure the same env/exit behavior contract as
runandvalidate - Add
shoal fin configure <fin-path> [--config <path>] - Ensure the same env/exit behavior contract as
runandvalidate
Acceptance:
- install/configure wrappers execute with
cwd=fin_root - exit-code propagation verified by tests
- help text + README command table updated
P1: Discovery and local source ergonomics¶
- ~~Add
shoal fin ls(start with local install roots)~~ — shipped in v0.22.0;fin lsdefaults to registered fins - ~~Add local source registration/install metadata (no marketplace yet)~~ —
fin installnow registers to~/.config/shoal/fins/;--no-registeropt-out available - Keep explicit docs on what is and is not supported
- Add local source registration/install metadata (no marketplace yet)
- Keep explicit docs on what is and is not supported
Acceptance:
- users can enumerate installed fins without path memorization
- docs describe source model and trust implications
P2: Validation and runtime hardening¶
- Expand tests for malformed TOML and missing keys
- Add optional subprocess timeout support (
--timeoutor config default) - Decide and document
SHOAL_LOG_LEVELpropagation behavior
Acceptance:
- malformed-manifest cases have deterministic errors and tests
- hung entrypoint behavior is bounded and documented
P3: Author ergonomics polish¶
- Update fish completions template for
fincommand group/subcommands - Improve diagnostics for common author mistakes (missing executable bit, contract mismatch, root path confusion)
Acceptance:
shoal setup fishinstalls completions that includefin- common failure modes have concise remediation hints
Recommendation on the core question¶
The current implementation does satisfy the immediate requirement of enabling external extension authors to use Shoal as an adapter without modifying Shoal source code for path-based workflows.
To satisfy the broader extension-system promise end-to-end, prioritize
install/configure and discovery next; these are now the main blockers, not
the contract runtime foundation.