Skip to content

RFC 005: package-bounded component resolution#37

Open
irl-dan wants to merge 1 commit into
mainfrom
rfc/flexible-component-resolution
Open

RFC 005: package-bounded component resolution#37
irl-dan wants to merge 1 commit into
mainfrom
rfc/flexible-component-resolution

Conversation

@irl-dan
Copy link
Copy Markdown
Contributor

@irl-dan irl-dan commented Apr 21, 2026

Summary

Adds a package-bounded recursive walk as Forme's resolution rule (3), between the entry point's own directory and .deps/. Authors organize files however they want; Forme finds components by name within the package.

  • Full proposal and rationale: rfcs/005-flexible-component-resolution.md
  • Spec patch: skills/open-prose/forme.md §Step 2

Note: This PR was previously an ### Paths explicit-declaration proposal (v1). Revised to a convention-based walk after review — the walk version is lighter on authors, aligns tighter with T2 ("trust the model") and T16 ("Forme does the discovery"), and requires no per-program boilerplate. The escape-hatch path: field on service entries stays.

Why

Forme's current resolution is ./name.md./name/index.md.deps/ → registry. Works for flat trees and externalized-as-dep modules; does not accommodate the common layered shape that appears once a customer project grows past a handful of files:

customers/acme/
  responsibilities/     # orchestration programs
  services/             # leaf services
    firehose/           # categorized subdirectories
  delivery/             # cadence+channel wrappers

A delivery composite in delivery/ that references a responsibility in responsibilities/ (which delegates to services in services/firehose/) has no resolution path today. Authors work around this by flattening the tree, forcing ./name/index.md subdirs, externalizing every module as a git dep, or relying on runtime fuzzy matching — all unsatisfying.

What changes

1. New resolution rule (3): package-bounded walk. From the entry point, walk up to the nearest ancestor containing .prose/ or prose.lock (the package root). Recursively scan every .md file in the subtree, matching by frontmatter name: (or filename stem if no name: frontmatter). Excludes .deps/, .prose/, .git/, node_modules/, .venv/, dist/, build/, target/, and any _-prefixed directory.

2. Structured path: field on service entries. Escape hatch for cross-package references or forced disambiguation. path: is an absolute override — skips all other resolution rules.

3. Ambiguity across the walk is a hard error. Two .md files with the same name in one package fails loudly, listing both paths. No silent "first wins." Authors resolve by renaming or pinning with path:.

Convention

Within a package, a component name is a unique identifier. Pick a name that reflects purpose; Forme finds the file. Zero per-program configuration.

Tenet check

  • T2 (trust the model; no deterministic fallbacks) — strengthened. The walk leans into the intelligent-container framing. Ambiguity is still a hard error.
  • T7 (two things — component and container) — preserved. No new component kind.
  • T11 (interpreter-spec pattern) — preserved. Walk algorithm lives in forme.md.
  • T12 (Forme owns opinions about program structure) — strengthened. Package boundary and name uniqueness are Forme's opinions, expressed where they belong.
  • T16 (components don't discover each other) — strengthened. Only Forme reads the package marker. Components never know anything about the filesystem.

Compatibility

Fully backward compatible. Existing rules (1), (2), (4), (5) are unchanged. The walk rule (3) only fires when (1) and (2) don't match. Flat programs continue to resolve identically.

Trade-offs

In favor: zero per-program boilerplate, convention-over-configuration aligns with the "intelligent container" bet, name collisions surface as healthy early signals.

Against: Phase 1 cost is O(files-in-package) — typically hundreds of .md files, well under a second; collisions at scale (500+ components) require naming discipline; greenfield projects need a package marker (.prose/ or prose.lock), a one-time setup cost.

Open questions (also listed in the RFC)

  1. Any std/-relative names we should exclude from the walk explicitly? (Today they're reachable via .deps/openprose/std/.)
  2. kind: test files — the walk finds them by name. Acceptable or worth calling out?
  3. .proseignore file for author-level additions to the exclusion list? My lean: not in V1.
  4. Should this RFC explicitly forbid runtime fuzzy fallback, making Phase 1 the single source of truth for resolution? My lean: yes.

Test plan

  • Review the walk semantics against the tenets — are there workflow shapes this breaks?
  • Sanity-check the exclusion list — anything missing, or too aggressive?
  • Walk through the Niural example (in the RFC) — does resolution land where expected?
  • Consider performance at 1000+ .md package size (probably fine for Phase 1; worth flagging if ever a hot path)
  • Confirm no conflict with contract-markdown.md or deps.md

🤖 Generated with Claude Code

Forme's current resolution rules (./name.md, ./name/index.md, .deps/,
registry) force authors into flat layouts or require every internal
module to be externalized as a git dep. Layered/categorized trees
(responsibilities/, services/, services/firehose/, delivery/) have
no resolution path today.

Adds a package-bounded recursive walk as resolution rule (3). The
package root is the nearest ancestor containing .prose/ or prose.lock.
The walk finds .md files by frontmatter name: or filename stem, and
skips .deps/, .prose/, .git/, build dirs, and _-prefixed directories.

Ambiguity across the walk is a hard error — preserves T2 and T16. A
structured path: field on the service entry remains as an escape
hatch for cross-package refs and forced disambiguation.

Convention over configuration: within a package, component names are
unique identifiers. No per-program boilerplate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@irl-dan irl-dan force-pushed the rfc/flexible-component-resolution branch from 0ba5053 to 0529063 Compare April 21, 2026 17:20
@irl-dan irl-dan changed the title RFC 005: flexible component resolution via ### Paths RFC 005: package-bounded component resolution Apr 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant