Propose boundary
Skill KhurrumMahmood/senior-vibe-engineer/.claude/skills/propose-boundary
Turn a confirmed or suspected missing-boundary into a read-only boundary-extraction proposal. Family-local evidence supports Python, TypeScript/checked-JavaScript, Go, Java 17, Kotlin/JVM, C#, Swift 6.3.3, PHP, Ruby, bounded Rust, and bounded Dart. It emits reports/propose-boundary/<target-slug>/proposal.md with candidate seams, public API, compatibility plan, caller impact, and characterization/native-verification plan. Read-only — no edits. Hands off to /refactor-subsystem (decomposition mode).From its SKILL.md
npx -y skills add KhurrumMahmood/senior-vibe-engineer --skill propose-boundaryAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
31.9 KB, ~7.3k tokens by cl100k_base, as published. Nobody here has run it
/propose-boundary
Swift 6.3.3 accepted branch
Use scripts/propose_swift.py only for one exact accepted Swift
confirmed-omnibus domain, one current complete swift-semantic-facts-v2 pack,
and separate hash-bound candidate and proposal verdicts. Keep the copied
_swift-semantic-readonly directory beside this selected skill. Pass
--omnibus, --facts, --acceptance, --output-dir, --target-name, the
configuration and native product/output expectations, and absolute --swift,
--swiftc, and --swift-format paths; run --help for the exact CLI.
Consume the final producer selection as authority. Never rerun scope detection
or join an older subsystem map. The schema-v2 provider validates the current
pack, then runs only as a native verifier on disposable current and exact
after trees. For the supported seam, propose exactly three public static export
methods behind internal ExportOperations; retain the original
DomainOperations public signatures as same-target/module compatibility shims
and require their exact resolved callers to remain unchanged.
Emit only proposal.md, evidence.json, and scope.json. Refuse incomplete,
stale, tampered, unsafe, or native-failing evidence and Swift/Xcode,
mixed-language, protocol/actor, dynamic/reflection, macro/conditional, or
ABI-sensitive conditions. Compiler call facts do not prove external callers,
runtime behavior beyond the named native checks, ABI/release compatibility, or
mutation authority.
C# 14 / .NET 10 accepted branch
Use scripts/propose_csharp.py only for one exact accepted confirmed-omnibus
domain, a current complete integrated C# subsystem map and Roslyn fact pack,
and a content-addressed csharp-structure-acceptance-v1 candidate/proposal
verdict. Keep copied _csharp, _csharp-semantic, and map-subsystem
libraries beside the selected skill; run the script with --help for its exact
artifact paths and pinned --dotnet argument. Never rerun omnibus detection or
mapping inside this consumer.
The branch emits only proposal.md, evidence.json, and scope.json. It
preserves the accepted namespace and assembly identities, retains public
compatibility shims, records exact resolved caller impact, and proves both the
current tree and an independently applied disposable after-tree with native
build/test/smoke. Reflection/runtime names, delegates, dispatch, generated or
vendor inputs, external callers, conditional variants, framework registration,
behavioral equivalence, ABI/release compatibility, and mutation authority stay
outside the claim.
Kotlin/JVM 2.4.10 accepted branch
Trigger this branch only for one explicitly scouted Kotlin omnibus domain with
a current complete Kotlin map and content-addressed human acceptance. Read
../_kotlin-semantic/GUIDE.md, keep _kotlin-semantic beside the selected
skill, and run scripts/propose_kotlin.py with --omnibus, --map-evidence,
--acceptance, --output-dir, absolute Kotlin/JVM 2.4.10 --kotlinc, and
absolute JDK 17 --java paths; run --help for the exact CLI.
The branch emits only proposal.md, evidence.json, and scope.json, then
proves the accepted manifest on the current tree and an exact disposable
after-tree. K1 direct facts remain insufficient for overload ambiguity,
override/runtime dispatch, reflection/callable references, delegation,
generated/KAPT/KSP or plugin inputs, Gradle variants, Java/external callers,
behavior, JVM ABI, mutation, or release authority.
C++20 branch
Use scripts/propose_cpp.py with accepted omnibus evidence, a current complete
C++20 map, and hash-bound human acceptance; run it with --help for the exact
CLI. It emits a read-only plan for one private compiler-owned free-function
seam, preserving namespace-qualified signatures, the complete overload set,
and direct caller impact. Templates/specializations, dispatch/callbacks,
ODR/ABI, external consumers, variants, and mutation are not inferred.
C17 branch
Use scripts/propose_c.py with accepted omnibus scout evidence, a current
complete C map, and a hash-bound human acceptance; run the script with --help
for the exact CLI. It emits a read-only ready or cohesive-defer outcome.
Function pointers, aliases, runtime reachability, alternate variants, external
consumers, ABI/layout, linkage, undefined behavior, and mutation remain outside
the claim.
PHP and Ruby
For PHP, read _php-proposal/GUIDE.md; for Ruby, read
_ruby-semantic/PROPOSAL-GUIDE.md. Consume one accepted upstream selection,
preserve its limits, and never rerun boundary detection.
Dart v1
Dart v1 consumes one accepted D4 query pack and human selection. It cites the public seam, callers, barrel compatibility, test surface, and an exact disposable after-tree; cohesive or unresolved targets defer rather than force an extraction.
SKILL_ROOT=".agents/skills/on-demand/propose-boundary"
python3 "${SKILL_ROOT}/scripts/propose_dart.py" \
--project-root "$PWD" --evidence-dir reports/dart-boundary-evidence \
--acceptance reports/dart-boundary-evidence/acceptance.json \
--inspection "$PWD/reports/propose-boundary/dart/inspection.json" \
--proposal "$PWD/reports/propose-boundary/dart/proposal.md"
Rust v1
Read knowledge/rust-v1.md. The Rust adapter inventories one selected Cargo
target, emits distinct JSON/Markdown evidence, and stops at
complete/review_boundary; the human still chooses the seam. Macros, build or
generated inputs, include contents, cfg variants, traits/generics, unsafe/FFI,
and public/semver compatibility stay partial or deferred.
SKILL_ROOT=".agents/skills/on-demand/propose-boundary"
python3 "${SKILL_ROOT}/scripts/propose_rust.py" \
--project-root "$PWD" --target crates/example \
--inspection "$PWD/reports/propose-boundary/rust/inspection.json" \
--proposal "$PWD/reports/propose-boundary/rust/proposal.md"
The canonical producer is
.claude/skills/_common/scripts/rust_proposal_evidence.py; a copied projection
places the same bytes beside the adapter under the alias
rust_project_evidence.py.
You are the orchestrator for turning a suspected missing-boundary
target into a read-only boundary-extraction proposal. The user has named
a file or directory where distinct concerns live together without an
explicit contract; your job is to score the candidate seams, propose
the public API surface, sketch the backward-compat shim, and emit a
proposal the human reviews before handing off to /refactor-subsystem.
You do NOT move files, rename modules, edit any source, or touch
imports in this skill. The only artifact you produce is
reports/propose-boundary/<target-slug>/proposal.md plus its
supporting inspection.json.
How success is judged
proposal.mdis complete per the template: candidate seams with raw scores, proposed public API table, backward-compat shim shape, caller-impact summary, and characterization-test matrix.- When
inspection.jsoncarriesdefer_signals, the proposal front matter recordsrecommendation: defer_<reason>— never a forced refactor recommendation. - Zero production edits — the run writes only under
reports/propose-boundary/<target-slug>/; the hand-off to/refactor-subsystemis named, not executed.
Core beliefs
- This is boundary proposal, not boundary decision. The skill
surfaces candidate seams + their evidence; the human picks. Over-
surface and let the human prune — same posture as
/find-orphaned-ideas --todo. - Heuristics are evidence, not verdict. Co-edit frequency, mutual import directionality, naming alignment, and cross-cluster call-edge density are signals. Each candidate seam carries the raw scores so the human can judge whether the cluster is coherent or coincidental.
- Backward-compat shim is the migration's safety net. Every
proposal sketches a re-export
__init__.pythat keeps existing call sites working after the move. If a caller reaches into a private helper across the proposed boundary, the shim won't cover it — that caller becomes a Phase 1 blocker, not a Phase 2 cleanup. - Characterization tests pin behavior before the move. Every public-API symbol gets 2–4 pin tests (input → expected output / side-effect) that must pass before AND after the refactor. Same discipline as a behavior-preserving two-commit refactor.
- The skill ecosystem is a valid target. Large composite skills
(a seven-phase orchestrator, a cross-cutting aggregator) are
themselves boundary-design candidates. When the target is a skill
directory, sub-phases (parsed from SKILL.md
## Phase Nheadings) are treated as symbols, the phase number as the cluster signal, and the proposal includes an "Orchestration shim shape" section sketching how the original skill name keeps working after split.
TypeScript / TSX and checked-JavaScript v1
The inherited TypeScript subset was scans: [python, typescript]; the
frontmatter declaration adds JavaScript only for the checked-JavaScript path
described here.
Use this branch only when the target host supplies one named, project-local tsconfig.json
and its own installed typescript package. The bundled runner
uses that host's Compiler API to resolve eligible static module specifiers,
top-level target symbols, and target-local call targets. The final artifact is
an inspection.json plus proposal.md, not a lexical suggestion: it cites
the resolved direct, alias, and barrel import evidence it used.
This is the minimum framework-neutral typed-source contract. Checked JavaScript
uses --language javascript, a named jsconfig.json or tsconfig.json, and
compilerOptions.allowJs plus checkJs set to true. It accepts .js, .jsx,
.mjs, and .cjs (including a mixed JS/TS host) and records selected files
outside that named config as partial, never a clean proposal:
- Propose a boundary only when two or more coherent top-level symbol domains form a partition within the target. Public API candidates are exported, non-underscore symbols; underscore-prefixed reaches are explicit Phase 1 blockers rather than compatibility coverage.
- Record resolved inbound and outbound static imports, target-local resolved
calls, direct/alias/barrel caller impact, and a compatibility plan that keeps
the existing TypeScript
index.ts/index.tsxor JavaScriptindex.js/index.jsx/index.mjs/index.cjsbarrel as a temporary re-export surface. - In checked JavaScript, treat only same-name, top-level declaration references
assigned through literal
exports.name,module.exports.name, ormodule.exports = { name }forms as resolved CommonJS public API evidence. Computed, spread, aliased, or expression-backed CommonJS exports make the proposalpartialuntil source/runtime confirmation establishes the public contract. - Give a characterization matrix and cite the host's native typecheck/test commands for the human-approved move. The proposal never edits source or runs a codemod.
- Defer explicitly when the target has unresolved or ambiguous module/symbol facts. A cohesive one-domain target also defers rather than inventing a split. Excluded generated/vendor/test/declaration/minified/build trees stay excluded even when named directly.
This v1 does not infer framework semantics: React, Node, ORM, route,
dependency-injection, runtime loading, dynamic import, reflection, decorator,
or other framework behavior. It does not follow directory symlinks and rejects a direct symlink
target. A missing or invalid tsconfig, missing project-local TypeScript, or
syntax error stops clearly with exit code 2. Type errors outside the selected
proposal graph are not converted into framework facts.
The resolver lives in this skill because its output contract is specific to boundary proposals. Do not extract it into a shared TypeScript platform until another accepted consumer proves the same resolution and deferral contract.
Installed TypeScript proposal command
Run this from the target host root after installing only this selected skill.
It writes only under reports/propose-boundary/ and has no repository-level
Python, _common, or sibling-skill import.
To make the stock Codex location from a released source, set
PROPOSE_BOUNDARY_SOURCE to that pinned source/ref and run:
: "${PROPOSE_BOUNDARY_SOURCE:?Set this to the pinned skill source/ref}"
npx --yes [email protected] add "${PROPOSE_BOUNDARY_SOURCE}" \
--skill propose-boundary --agent codex --copy -y
<!-- installed-command:stock-install:end -->
<!-- installed-command:typescript-proposal:start -->
PROPOSE_TARGET="${PROPOSE_TARGET:-src/legacy}"
PROPOSE_LANGUAGE="${PROPOSE_LANGUAGE:-typescript}" # typescript | javascript
PROPOSE_TSCONFIG="${PROPOSE_TSCONFIG:-tsconfig.json}"
PROPOSE_NAME="${PROPOSE_NAME:-${PROPOSE_LANGUAGE}-legacy}"
SKILL_ROOT=""
for SKILL_CANDIDATE in \
".agents/skills/propose-boundary" \
".claude/skills/propose-boundary"
do
if [ -f "${SKILL_CANDIDATE}/SKILL.md" ]; then
SKILL_ROOT="$(cd "${SKILL_CANDIDATE}" && pwd)"
break
fi
done
if [ -z "${SKILL_ROOT}" ]; then
printf '%s\n' "propose-boundary is not installed in .agents/skills or .claude/skills" >&2
exit 2
fi
node "${SKILL_ROOT}/scripts/propose_typescript.mjs" \
--target "${PROPOSE_TARGET}" \
--project-root "$(pwd)" \
--language "${PROPOSE_LANGUAGE}" \
--tsconfig "${PROPOSE_TSCONFIG}" \
--candidates 2 \
--inspection "reports/propose-boundary/${PROPOSE_NAME}/inspection.json" \
--proposal "reports/propose-boundary/${PROPOSE_NAME}/proposal.md"
<!-- installed-command:typescript-proposal:end -->
Go v1
The former scans: [python, typescript, javascript] declaration is superseded
by the frontmatter Go entry; existing Python and TypeScript/checked-JavaScript
contracts are unchanged.
Use this branch for one package directory in one Go module. It is a deliberately
narrow, read-only proposal: host go list -e -json -mod=readonly ./...
establishes the module's active package/import facts, while the bundled
standard-library Go program uses go/parser/go/ast for top-level declarations
and syntax-level local-call candidates. It never downloads or imports
go/packages, go/types, a language server, or any third-party module.
Go v1 may recommend an extraction only when all of these facts are available:
- a PATH-discovered Go tool is at least Go 1.22 and the target host is one
root
go.modmodule with no activego.workworkspace and noreplacedirective (preflighted withgo env GOWORKandgo mod edit -json); go listestablishes the target package import path and every first-party direct or alias importer;- the target has two or more named top-level symbol domains, each with at least two declarations; and
- no build-tag/cgo source, unresolved package graph, dot/blank importer, or generated/vendor/test target makes the evidence incomplete.
Uppercase identifiers are the only public API candidates. Go cannot import an
unexported identifier from another package, so package-private cross-domain
calls are listed as migration blockers rather than a TypeScript-style external
private-import claim. Those calls are AST syntax candidates, not go/types
call identities. For each selected exported function, the proposal also lists
same-package exported named types found in its syntax-level signature so the
human can explicitly preserve their identity with a temporary type alias. The
proposal preserves the old package import path only as a human-approved
temporary forwarding/type-alias facade; it never writes one.
This v1 explicitly defers active go.work workspaces, modules using
replace, build-tagged or cgo target source, dot/blank importers, unresolved
packages, generated/vendor/test targets, and cohesive one-domain packages.
The bundled runner avoids Go 1.18-only syntax so an older toolchain reaches
the explicit Go-version deferral rather than failing to compile the runner.
Dynamic loading, reflection,
interfaces, build matrices, workspaces, external consumers, and runtime
reachability remain outside the claim. A missing or old Go tool emits an
unsupported inspection outcome rather than a clean proposal. Malformed Go
source emits a failed syntax outcome.
Installed Go proposal command
Run this from the Go module root after the router has exposed the on-demand library. It uses only the selected skill's bundled Go source and the host Go toolchain; it does not require the toolkit Python environment, sibling skills, or a network dependency.
<!-- installed-command:go-proposal:start -->PROPOSE_TARGET="${PROPOSE_TARGET:-internal/legacy}"
PROPOSE_NAME="${PROPOSE_NAME:-go-legacy}"
SKILL_ROOT=""
for SKILL_CANDIDATE in \
".agents/skills/propose-boundary" \
".claude/skills/propose-boundary"
do
if [ -f "${SKILL_CANDIDATE}/SKILL.md" ]; then
SKILL_ROOT="$(cd "${SKILL_CANDIDATE}" && pwd)"
break
fi
done
if [ -z "${SKILL_ROOT}" ]; then
printf '%s\n' "propose-boundary is not installed in .agents/skills or .claude/skills" >&2
exit 2
fi
if ! command -v go >/dev/null 2>&1; then
printf '%s\n' '{"status":"unsupported","failure_kind":"go_tool_missing"}'
exit 0
fi
GO_VERSION_OUTPUT="$(go version 2>/dev/null || true)"
GO_VERSION_TOKEN="${GO_VERSION_OUTPUT#* go}"
GO_VERSION_TOKEN="${GO_VERSION_TOKEN%% *}"
GO_VERSION_MAJOR="${GO_VERSION_TOKEN%%.*}"
GO_VERSION_REST="${GO_VERSION_TOKEN#*.}"
GO_VERSION_MINOR="${GO_VERSION_REST%%.*}"
case "${GO_VERSION_MAJOR}:${GO_VERSION_MINOR}" in
*[!0-9:]*|:*)
printf '%s\n' '{"status":"unsupported","failure_kind":"go_version_unreadable","minimum_go":"1.22"}'
exit 0
;;
esac
if [ -z "${GO_VERSION_MAJOR}" ] || [ -z "${GO_VERSION_MINOR}" ] || \
[ "${GO_VERSION_MAJOR}" -lt 1 ] || \
{ [ "${GO_VERSION_MAJOR}" -eq 1 ] && [ "${GO_VERSION_MINOR}" -lt 22 ]; }
then
printf '%s\n' '{"status":"unsupported","failure_kind":"go_version_too_old","minimum_go":"1.22"}'
exit 0
fi
go run "${SKILL_ROOT}/scripts/propose_go.go" \
--target "${PROPOSE_TARGET}" \
--project-root "$(pwd)" \
--candidates 2 \
--inspection "reports/propose-boundary/${PROPOSE_NAME}/inspection.json" \
--proposal "reports/propose-boundary/${PROPOSE_NAME}/proposal.md"
<!-- installed-command:go-proposal:end -->
Java v1
Use this branch for one named Java package directory in a standalone host that
can compile with JDK 17 without Maven, Gradle, annotation processors, or
third-party dependencies. The bundled single-file Java runner invokes the JDK
compiler tree API with parsing and attribution enabled. Only after all eligible
production sources attribute successfully does it report direct imports or
fully-qualified type references as compiler-resolved caller evidence.
Java v1 proposes a seam only when at least two leading type-name domains each contain at least two top-level types. Public top-level types are proposed as the compatibility surface; the human still chooses the boundary. A one-domain package defers instead of inventing a split. Generated, vendor, build, test, malformed, default-package, mixed-package, and symlink targets never produce a clean proposal.
This is deliberately a source/package pilot. It does not infer Spring, Jakarta, Android, reflection, dependency injection, runtime class loading, module-path, annotation-processor, Kotlin, Maven, or Gradle semantics. Hosts requiring those facts defer until a framework- or build-aware contract is justified by a real project. The runner stays family-local and uses no repository Python runtime, language server, or third-party JAR.
Installed Java proposal command
Run this from the host root after the router exposes the on-demand skill. Both
java and javac must resolve from PATH; source-file mode executes the copied
runner directly.
PROPOSE_TARGET="${PROPOSE_TARGET:-src/main/java/example/legacy}"
PROPOSE_NAME="${PROPOSE_NAME:-java-legacy}"
SKILL_ROOT=""
for SKILL_CANDIDATE in \
".agents/skills/propose-boundary" \
".claude/skills/propose-boundary"
do
if [ -f "${SKILL_CANDIDATE}/SKILL.md" ]; then
SKILL_ROOT="$(cd "${SKILL_CANDIDATE}" && pwd)"
break
fi
done
if [ -z "${SKILL_ROOT}" ]; then
printf '%s\n' "propose-boundary is not installed in .agents/skills or .claude/skills" >&2
exit 2
fi
if ! command -v java >/dev/null 2>&1 || ! command -v javac >/dev/null 2>&1; then
printf '%s\n' '{"status":"unsupported","failure_kind":"jdk_tool_missing","minimum_jdk":17}'
exit 0
fi
java "${SKILL_ROOT}/scripts/propose_java.java" \
--target "${PROPOSE_TARGET}" \
--project-root "$(pwd)" \
--minimum-jdk 17 \
--candidates 2 \
--inspection "reports/propose-boundary/${PROPOSE_NAME}/inspection.json" \
--proposal "reports/propose-boundary/${PROPOSE_NAME}/proposal.md"
<!-- installed-command:java-proposal:end -->
Python scope
- Project root: the working directory.
- Python: the host project's venv python (
.venv/bin/pythonor equivalent) for the helper script — it routes Python parsing through the shared per-language adapter registry (ADR 0032; Python-only here, skipping files whose adapter can't supply the raw AST) and shells out togit logfor co-edit frequency; stdlib + git only. - Output:
reports/propose-boundary/<target-slug>/. Never touches any other file. - Read-only. No file moves, no edits, no Edit tool. The
orchestrator's
allowed-toolslist intentionally excludes Edit.
Argument parsing
Two forms.
Form A — file or directory target
/propose-boundary <path>
e.g. /propose-boundary <project>/tasks/shared_helpers.py or
/propose-boundary <project>/services/sites/. The target may be a
single Python file, a directory containing Python files, or a skill
directory (.claude/skills/<name>/). The helper auto-detects which.
Form B — subsystem name
/propose-boundary <subsystem-name>
If .engineering/docs/subsystems/<subsystem-name>.md exists, the helper
reads it for the file list and uses that as the target. Otherwise the
orchestrator treats the argument as a path and falls through to Form A.
Optional flag — --candidates N
/propose-boundary <target> --candidates 3
Requests N alternative boundary cuts (default: 1). The helper scores
every candidate seam against the full criterion set and returns the top N plus
every candidate tied at the cutoff. candidate_selection records the requested,
eligible, and returned counts, cutoff score, whether ties expanded the result,
and every lower-scored omitted candidate. Returned tie identities and scores
remain in candidate_seams, so a limit never silently hides a cutoff-equivalent
seam. The human picks one (or none) before hand-off.
If the target doesn't exist, has fewer than 2 Python files AND fewer
than 6 public symbols total, the orchestrator stops, logs
target_not_found or target_below_threshold to the proposal, and
returns.
Pipeline
Stage 0 — resolve target. Parse the argument. Detect target kind:
file | directory | skill_directory. Compute the target slug
(replace / with -, drop leading dots and .py).
Stage 1 — inspect. Run the helper:
.venv/bin/python .claude/skills/propose-boundary/scripts/propose.py \
--target <path> \
--project-root . \
--candidates <N> \
--output reports/propose-boundary/<target-slug>/inspection.json
The helper writes inspection.json with:
target— resolved path + target kind.files— every Python file in scope with line count and public symbols list.symbols—{name, file, kind, public}for every top-level symbol (functions, classes, module-level constants). For skill-directory targets, also includes virtualphaseN_*/phaseN_M_*symbols parsed from SKILL.md## Phase N/### N.Mheadings.co_edit_pairs— top symbol pairs by co-edit frequency, from agit log --name-onlywalk over the last 90 days (configurable).naming_clusters— lexical-prefix clusters; each cluster names members and a proposed cluster name.call_edges—{caller, callee, count}for every cross-symbol call resolved within the target.candidate_seams— list of{cluster_id, members, rationale, proposed_public_api, callers_into_private_helpers, scores}for the top N candidate boundary cuts plus every seam tied at the cutoff.candidate_selection—{requested, eligible, returned, cutoff_score, ties_included, omitted_count, omitted}. Each omitted row names itscluster_idand score.defer_signals— guardrail trips (target_below_threshold,single_cluster_no_seam,scratch_code).
Stage 2 — scout callers (optional). For each proposed_public_api
symbol in every returned candidate seam, the orchestrator dispatches a cheap
read-only scout via .claude/skills/_common/dispatch_scout_cheap.sh
(Bash + grep — no Agent tool; the allowed-tools list stays
read-only-tight) to confirm the call sites in
callers_into_private_helpers. The orchestrator may skip this if the
helper's static analysis already covered the project root. This cheap-dispatch path requires the host tools.code_agent backend (<!-- host-adapter -->); use inline scouting when that backend is absent.
Stage 3 — synthesize. The orchestrator reads inspection.json and
writes proposal.md per the template below. This is judgment work, not
mechanical — Claude does it, not a script.
Stage 4 — stop or hand off. If defer_signals are non-empty, the
proposal records recommendation: defer_<reason> in the front matter.
Otherwise the proposal recommends recommendation: refactor and names
/refactor-subsystem --target <target-slug> as the next step.
Proposal template
The proposal markdown has these sections, in order:
# Boundary proposal — <target>
> **Detected by:** `/propose-boundary` (read-only; no edits applied)
> **Decided in:** <host project's ADR on staged boundary
> rearchitecting, if one exists — see the host decision registry>
> **Executed by:** `/refactor-subsystem` (decomposition mode)
**Recommendation:** refactor | defer_<reason>
**Target kind:** file | directory | skill_directory
**Total LOC:** <N>
**Public symbols:** <K>
**Candidate seams:** requested <N>; returned <R> of <C> eligible;
cutoff <S>; ties included: yes | no; omitted <O>
## Current shape
(File tree + documented or detected domains.)
## Candidate seam 1 — <cluster_id> (score: <S>)
**Members.** <symbol list>.
**Rationale.** <why this cluster is coherent — naming alignment,
co-edit frequency above threshold, low cross-cluster call density,
docstring evidence>.
**Proposed public API.**
| Symbol | Signature | Purpose |
|---|---|---|
**Backward-compat shim shape.**
(Python `__init__.py` re-export block keeping existing call sites
working after extraction.)
**Caller-impact summary.**
| Importer | Statement | Shim covers? |
|---|---|---|
**Characterization-test matrix.**
| Public-API symbol | Pin test 1 | Pin test 2 |
|---|---|---|
## Candidate seam 2 — <cluster_id> (score: <S>)
(Repeat this section for every row in `candidate_seams`, including ties that
expanded the result beyond requested N.)
## Orchestration shim shape (skill-directory targets only)
(How the original skill name keeps working after split — thin
orchestrator that invokes new sub-skills in sequence.)
## Migration sequencing (per candidate seam)
1. Pre-move pin-tests.
2. Phase 1: migrate reach-into-private callers.
3. Phase 2: extract cluster (`/refactor-subsystem` decomposition mode).
4. Phase 3: clean up dead re-exports.
## Stop condition
(Behavior preservation + test suite green + every importer routed
through new boundary.)
## Spec hand-off (for /refactor-subsystem)
\`\`\`yaml
---
spec_id: boundary-<target-slug>-<cluster_id>
status: STUB
target: refactor-subsystem
code_roots:
- <target>
strategy: decomposition
boundary_proposal: reports/propose-boundary/<target-slug>/proposal.md
---
\`\`\`
## Notes (orchestrator judgment)
A short prose section.
Defer signals
target_below_threshold— fewer than 2 Python files AND fewer than 6 public symbols. Proposal recommendsdefer_below_threshold.single_cluster_no_seam— every candidate seam scores below the threshold; the target's symbols are tightly co-coupled (high cross- cluster call density everywhere). Proposal recommendsdefer_no_seamand points the human at the SUSPECT step (/find-omnibusfor an intra-file split).scratch_code— the target matches a known scratch-code path (host-project-configured prefix list). Proposal recommendsdefer_scratch_code.
Calibration
- Co-edit window defaults to 90 days. Override with
--co-edit-days N. - Naming-cluster minimum defaults to 3 members. Override with
--min-cluster-size N. - Seam-score threshold defaults to 0.4. Override with
--seam-threshold T. - The skill is intentionally over-permissive on emit; the human
prunes. Same posture as
/find-orphaned-ideas --todo.
Re-runs are idempotent
The helper reads the filesystem and git log only. Re-running with
the same target overwrites inspection.json and is safe. The proposal
is NOT auto-overwritten — Claude inspects the existing proposal first
and only rewrites the sections that changed.
Repository layout
.claude/skills/propose-boundary/
├── SKILL.md # this file — orchestrator
└── scripts/
└── propose.py # detector + scoring helper (stdlib + git only)
Next skills
/refactor-subsystemin decomposition mode — executes the proposal under a behavior-preserving two-commit discipline. One candidate seam per PR./decideif the proposal surfaces a tradeoff the host project's decision registry doesn't yet cover./map-subsystemto refresh the subsystem map after the extraction lands.
Related
architectural-smells.mdsmell 9 (missing-boundary) — detector entry that names this skill as the SUSPECT./map-subsystem— Stage 4 dep-graph output hints at low-density inter-cluster edges that may indicate a missing boundary.- Host-project ADR on staged boundary rearchitecting (when authored) — names the when-to-phase decision framework this skill pairs with.
What ships with it: 14 files
260.0 KB alongside SKILL.md, 11 of them executable
knowledge/
- rust-v1.md730 B
scripts/
- propose_cpp.pyruns35.7 KB
- propose_c.pyruns31.1 KB
- propose_csharp.pyruns986 B
- propose_dart.pyruns26.5 KB
- propose_go.go36.8 KB
- propose_java.java31.2 KB
- propose_kotlin.pyruns1002 B
- propose_php.pyruns6.1 KB
- propose.pyruns29.8 KB
- propose_ruby.pyruns5.1 KB
- propose_rust.pyruns11.5 KB
- propose_swift.pyruns1001 B
- propose_typescript.mjsruns42.5 KB