When the user proposes a new feature, follow this workflow where executable tests specify behavior and types + doc comments specify technical design:
Write natural language Gherkin scenarios and iterate with the user:
Given a source file with a file link
And a target file exists
When I request definition at the link position
Then it should return the target file locationTranslate to BDD tests in integration/<featurename>_test.go - this is the living behavioral spec:
func TestFileLinkDefinition(t *testing.T) {
Given("a source file with a file link", t, setupFunc, func(t *testing.T, tc *LSPTestContext) {
When(t, tc, "requesting definition at link position", "textDocument/definition", params,
func(t *testing.T, locs []protocol.Location) {
Then("returns target file location", t, func(t *testing.T) {
testza.AssertEqual(t, 1, len(locs))
})
})
})
}Each top level Test* function should correspond to the collection of Gherkin "Given" scenarios that describe a total feature. Put each such function in its own file.
Run the test to confirm it fails (red): just test TestFileLinkDefinition
Thread-Safety Verification Note: All just test* commands run with:
-raceflag enabled (detects data races at runtime)-parallel=4(limited parallelism to catch concurrency bugs)-timeout=60s(catches deadlocks/hangs)
This means your tests automatically verify thread-safety! If you introduce a data race or deadlock, the tests will fail with a detailed report. Keep tests deterministic and avoid shared mutable state between parallel tests.
Define data types and function signatures with doc comments. The types are the spec, the documentation comments define specific algorithms and semantics:
// IDLinkResolver finds target headings by UUID property.
// It searches the UUID index built by orgscanner during file scanning.
type IDLinkResolver struct {
scanner *orgscanner.OrgScanner
}
// Resolve returns the location of the heading with the given ID.
// Returns nil if no heading with that ID exists.
//
// Algorithm: look up the HeaderLocation on scanner.ProcessedFiles.UuidIndex map.
func (r *IDLinkResolver) Resolve(id string) *orgscanner.HeaderLocationView generated docs: go doc github.com/alexispurslane/org-lsp/orgscanner IDLinkResolver
Now implement to make tests pass:
- Add types with doc comments (technical spec)
- Add handler method to server.go
- Update capabilities in initialize()
- Run tests:
just test TestFeatureName - Debug with:
ORG_LSP_LOG_LEVEL=DEBUG just test TestFeatureName
- Run full test suite:
just test - Update doc comments if implementation diverged from spec
- The code now contains both executable behavior spec (tests) and technical spec (types + comments)
Key Principle: The only "spec documents" are:
integration/*_test.go- executable behavior specs- Go doc comments in the code - technical specs
ARCHITECTURE.md- high-level navigation (optional)
No separate SPEC.md needed - the spec lives in the code and is always up-to-date!