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
33 changes: 33 additions & 0 deletions openspec/changes/document-system-architecture/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,36 @@ pseudo-version continue to resolve until they move.
rename breaks it without warning.
- Should `gohai` be consumed by `osapi`, as its README describes? That is a
design question, not a documentation one, and is out of scope here.

## Correction: `replace` was specified as a single thing

The requirement first stated that a `replace` directive may be used locally but
never merged, and that no repository contained one. Applying it found thirteen,
merged, across four repositories:

| Repository | Directives | Target |
| -------------------- | ---------- | ----------------------- |
| `nats-client` | 5 | its own repository root |
| `nats-server` | 4 | its own repository root |
| `osapi` | 2 | its own repository root |
| `osapi-orchestrator` | 2 | its own repository root |

Every one belongs to a nested example module pointing at the repository that
holds it. None crosses a repository boundary.

The requirement had collapsed two unrelated uses of the same keyword. A
`replace` reaching into a sibling repository stands in for a version that should
be pinned, and leaks a developer's local layout into a merged tree. A `replace`
reaching into its own repository root is how a nested module refers to the
source beside it — remove it and the example silently compiles against whatever
version the proxy last published, which is the failure the pattern exists to
prevent.

The corrected requirement distinguishes them by direction rather than by
mechanism.

*Alternative considered:* exempt example modules by path convention, keying the
rule to a directory named `examples/`. Rejected — the property that matters is
whether the directive crosses a repository boundary, and a rule written against
a directory name would miss a nested tool module and would not survive anyone
choosing a different name.
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,36 @@ A Go module SHALL declare a path matching the repository that holds it.

### Requirement: Dependencies are declared by version

A repository SHALL depend on another by a released or pinned version in
`go.mod`.
A repository SHALL depend on another repository by a released or pinned version
in `go.mod`.

A `replace` directive MAY be used locally while developing against an unreleased
change in a sibling repository. It SHALL NOT be merged.
A `replace` directive pointing outside the repository that holds it MAY be used
locally while developing against an unreleased change in a sibling repository.
It SHALL NOT be merged.

No repository contains one today, and the documentation that described `replace`
as the linking mechanism was wrong.
A `replace` directive pointing within the repository that holds it is a
different thing and SHALL be merged. A nested module — an example or tool module
under a repository that already declares its own — resolves its parent through
`replace` so it compiles against the working tree rather than a published
version. Without it the example cannot demonstrate the code it ships beside.

The distinction is direction, not mechanism: a `replace` crossing a repository
boundary substitutes for a version that should be pinned; one staying inside it
is how a nested module refers to its own repository.

#### Scenario: Consumer builds without the sibling checked out

- **WHEN** a repository is built by someone who has not cloned its sibling
repositories
- **THEN** the build resolves every dependency from the module proxy

#### Scenario: An example module ships beside the code it demonstrates

- **WHEN** a repository carries a nested example module
- **THEN** that module declares a `replace` pointing at its own repository root,
and it is merged, because the example must build against the source it sits
next to

#### Scenario: Developing against an unreleased sibling change

- **WHEN** a developer adds a `replace` directive to test against a sibling
Expand Down
6 changes: 4 additions & 2 deletions openspec/changes/document-system-architecture/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ Sequenced: `osapi` renames and publishes before `osapi-orchestrator` moves.
- [ ] 3.1 `gohai` — describe the consumer relationship it has, not the one
intended
- [ ] 3.2 Confirm no repository documents a `replace`-based linkage
- [ ] 3.3 Confirm no `go.mod` contains a `replace` directive
- [ ] 3.3 Confirm no `go.mod` contains a `replace` directive pointing outside
its own repository

## 4. Verification

- [ ] 4.1 Confirm every module path matches its repository location
- [ ] 4.2 Confirm no `replace` directive points outside its own module
- [ ] 4.2 Confirm every `replace` directive points within the repository that
holds it
- [ ] 4.3 Confirm every documented dependency appears in the corresponding
`go.mod`