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
20 changes: 16 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ nucleus validate
nucleus plan --task "change request" --json
nucleus gen
nucleus scenario --json
nucleus serve --check --json
nucleus lint
nucleus verify
nucleus report --json
Expand All @@ -89,6 +90,16 @@ explicit opt-in HTTP scenario evidence. Network execution is never implicit:
`--run-http` and `--cases` require `--base-url`, and captured headers are
redacted for sensitive keys such as authorization and cookies.

`serve` exposes local metadata endpoints for manual probes and automation
preflight checks. Use `nucleus serve --check --json` to inspect the service
without opening a listener, or `nucleus serve --addr 127.0.0.1:8080` to expose
`/healthz`, `/readyz`, and `/.well-known/nucleus.json`. The command is
metadata-only: it does not auto-wire provider SDKs or generated business
handlers. JSON output uses `result_kind: "nucleus.serve_result"`,
`schema_version: "serve.v1"`, `schema_ref:
"contract/schema/serve.schema.json"`, `ok`, `mode`, `summary`, `diagnostics`,
and `server`.

`report` summarizes AI change quality and platform readiness without making
network calls. By default it reads AI task result JSON files from
`artifacts/nucleus/ai-tasks`; pass `--ai-tasks` for an explicit directory, or
Expand All @@ -99,9 +110,10 @@ a stable envelope with `result_kind: "nucleus.report_result"`,
"contract/schema/report.schema.json"`, `ok`, `mode`, `summary`, and
`diagnostics`.

For the bundled example, run `nucleus validate --dir example/hello-http`.
Successful human output includes a short validation summary; `--json` emits the
same result with stable `ok`, `summary`, and `diagnostics` fields.
For any service directory containing `nucleus.yaml` and contract sources, run
`nucleus validate --dir <service>`. Successful human output includes a short
validation summary; `--json` emits the same result with stable `ok`, `summary`,
and `diagnostics` fields.

## Project Shape

Expand All @@ -110,7 +122,7 @@ The main repository hosts the CLI, examples, and public documentation:
```text
api/ Contract files
cmd/nucleus/ CLI implementation
example/ Runnable examples
examples/ Runnable examples and templates
docs/ Concepts, ADRs, plans, and platform mapping
```

Expand Down
2 changes: 1 addition & 1 deletion bridge
Submodule bridge updated 0 files
2 changes: 1 addition & 1 deletion cap
Submodule cap updated 0 files
4 changes: 1 addition & 3 deletions cmd/nucleus/internal/root/capability_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ package root
import (
"bytes"
"encoding/json"
"path/filepath"
"testing"
)

func TestCapabilityCommandWithGlobalDir(t *testing.T) {
repoRoot := repositoryRoot(t)
exampleDir := filepath.Join(repoRoot, "example", "hello-http")
exampleDir := writeRootExampleService(t)

cmd := New()
var stdout bytes.Buffer
Expand Down
3 changes: 1 addition & 2 deletions cmd/nucleus/internal/root/describe_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import (
)

func TestDescribeCommandWithHelloHTTPExample(t *testing.T) {
repoRoot := repositoryRoot(t)
exampleDir := filepath.Join(repoRoot, "example", "hello-http")
exampleDir := writeRootExampleService(t)

cmd := New()
var stdout bytes.Buffer
Expand Down
4 changes: 1 addition & 3 deletions cmd/nucleus/internal/root/plan_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ package root
import (
"bytes"
"encoding/json"
"path/filepath"
"testing"
)

func TestPlanCommandWithHelloHTTPExample(t *testing.T) {
repoRoot := repositoryRoot(t)
exampleDir := filepath.Join(repoRoot, "example", "hello-http")
exampleDir := writeRootExampleService(t)

cmd := New()
var stdout bytes.Buffer
Expand Down
5 changes: 4 additions & 1 deletion cmd/nucleus/internal/root/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/nucleuskit/nucleus/cmd/nucleus/internal/repair"
"github.com/nucleuskit/nucleus/cmd/nucleus/internal/report"
"github.com/nucleuskit/nucleus/cmd/nucleus/internal/scenario"
"github.com/nucleuskit/nucleus/cmd/nucleus/internal/serve"
"github.com/nucleuskit/nucleus/cmd/nucleus/internal/validate"
"github.com/nucleuskit/nucleus/cmd/nucleus/internal/verify"
"github.com/spf13/cobra"
Expand All @@ -37,7 +38,6 @@ func New() *cobra.Command {
cmd.PersistentFlags().StringVar(&opts.dir, "dir", ".", "service root directory")
cmd.PersistentFlags().BoolVar(&opts.verbose, "verbose", false, "enable verbose output")
cmd.PersistentFlags().StringVar(&opts.schema, "schema", "", "override describe schema version")

cmd.AddCommand(describe.NewCommand(describe.Config{
Dir: &opts.dir,
SchemaOverride: &opts.schema,
Expand Down Expand Up @@ -81,5 +81,8 @@ func New() *cobra.Command {
cmd.AddCommand(migrate.NewCommand(migrate.Config{
Dir: &opts.dir,
}))
cmd.AddCommand(serve.NewCommand(serve.Config{
Dir: &opts.dir,
}))
return cmd
}
4 changes: 1 addition & 3 deletions cmd/nucleus/internal/root/scenario_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ package root
import (
"bytes"
"encoding/json"
"path/filepath"
"testing"
)

func TestScenarioCommandWithHelloHTTPExample(t *testing.T) {
repoRoot := repositoryRoot(t)
exampleDir := filepath.Join(repoRoot, "example", "hello-http")
exampleDir := writeRootExampleService(t)

cmd := New()
var stdout bytes.Buffer
Expand Down
64 changes: 64 additions & 0 deletions cmd/nucleus/internal/root/serve_example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package root

import (
"bytes"
"encoding/json"
"os"
"path/filepath"
"testing"
)

func TestServeCommandJSONRootWiring(t *testing.T) {
dir := t.TempDir()
writeRootServeFile(t, dir, "nucleus.yaml", `schema_version: "1.0"
service:
name: demo
version: "0.1.0"
capabilities:
- http
`)
writeRootServeFile(t, dir, "api/openapi.yaml", `openapi: 3.0.3
paths:
/healthz:
get:
operationId: getHealthz
responses:
"200":
description: ok
`)
writeRootServeFile(t, dir, "api/errors.yaml", "errors: []\n")

cmd := New()
var stdout bytes.Buffer
var stderr bytes.Buffer
cmd.SetOut(&stdout)
cmd.SetErr(&stderr)
cmd.SetArgs([]string{"--dir", dir, "serve", "--check", "--json"})

if err := cmd.Execute(); err != nil {
t.Fatalf("execute serve: %v\nstderr=%s", err, stderr.String())
}

var output map[string]any
if err := json.Unmarshal(stdout.Bytes(), &output); err != nil {
t.Fatalf("decode serve output: %v\n%s", err, stdout.String())
}
assertString(t, output, "result_kind", "nucleus.serve_result")
assertString(t, output, "schema_ref", "contract/schema/serve.schema.json")
assertString(t, output, "mode", "check")
assertBool(t, output, "ok", true)
summary := assertMap(t, output, "summary")
assertString(t, summary, "service", "demo")
assertNumber(t, summary, "endpoint_count", 1)
}

func writeRootServeFile(t *testing.T, dir string, name string, data string) {
t.Helper()
path := filepath.Join(dir, name)
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, []byte(data), 0o600); err != nil {
t.Fatal(err)
}
}
119 changes: 119 additions & 0 deletions cmd/nucleus/internal/root/service_fixture_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package root

import (
"os"
"path/filepath"
"testing"
)

func writeRootExampleService(t *testing.T) string {
t.Helper()
dir := t.TempDir()
writeRootFixtureFile(t, dir, "nucleus.yaml", `schema_version: "1.0"
service:
name: hello-http
version: "0.1.0"
env: test
owner: nucleus-maintainers
tier: example
namespace: examples
description: Contract-first HTTP service used by root command tests.
capabilities:
- http
- log
dependencies:
- name: greeting-profile
contract: api/openapi.yaml#/paths/~1hello~1{name}
required: false
ai:
intent: Exercise root command wiring against a stable service fixture.
allowed_changes:
- api/**
- configs/**
- internal/**
readonly:
- internal/adapter/http/gen/**
generated:
- contract/gen
- internal/adapter/http/gen
forbidden:
- configs/*.local.yaml
nucleus:
providers:
log:
provider: noop
`)
writeRootFixtureFile(t, dir, "api/openapi.yaml", `openapi: 3.1.0
info:
title: Hello HTTP Example
version: 0.1.0
paths:
/hello/{name}:
parameters:
- name: name
in: path
required: true
schema:
type: string
get:
operationId: get_hello
x-nucleus-priority: 10
parameters:
- name: trace_id
in: header
required: false
schema:
type: string
responses:
"200":
description: Greeting response.
"404":
description: Greeting target was not found.
`)
writeRootFixtureFile(t, dir, "api/errors.yaml", `errors:
- code: 4001
message: invalid_name
http_status: 400
- code: 4041
message: greeting_not_found
http_status: 404
`)
writeRootFixtureFile(t, dir, "configs/app.yaml", `http:
address: "${HTTP_ADDR:-127.0.0.1:8080}"
greeting:
prefix: hello
`)
writeRootFixtureFile(t, dir, "go.mod", "module example.com/hello-http\n\ngo 1.26.3\n")
writeRootFixtureFile(t, dir, "internal/app/routes.go", `package app

import "net/http"

type Router struct{}

func (Router) Handle(method string, path string, handler func()) {}

type Logger struct{}

func (Logger) Info() {}

var log Logger

func RegisterRoutes(router Router) {
router.Handle(http.MethodGet, "/hello/{name}", func() {
log.Info()
})
}
`)
return dir
}

func writeRootFixtureFile(t *testing.T, dir string, name string, data string) {
t.Helper()
path := filepath.Join(dir, filepath.FromSlash(name))
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, []byte(data), 0o600); err != nil {
t.Fatal(err)
}
}
3 changes: 1 addition & 2 deletions cmd/nucleus/internal/root/validate_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ import (
)

func TestValidateCommandWithHelloHTTPExample(t *testing.T) {
repoRoot := repositoryRoot(t)
exampleDir := filepath.Join(repoRoot, "example", "hello-http")
exampleDir := writeRootExampleService(t)

cmd := New()
var stdout bytes.Buffer
Expand Down
Loading
Loading