Skip to content

Commit 35291ff

Browse files
authored
feat(ttlc): centralize and improve user-facing diagnostics/errors (#294)
## Summary - centralize ttlc user-facing diagnostics and command errors with template-driven message helpers - rewrite lexer/parser/sema/compiler/cli/source/logging user-facing messages in consistent English wording - add table-driven message template tests and update message-sensitive ttlc tests - sync ttlc docs/contracts and cmds domain policy with the new message contract ## Testing - go test ./cmds/ttlc/...
1 parent 500ae11 commit 35291ff

18 files changed

Lines changed: 554 additions & 171 deletions

cmds/AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
- Keep configuration schemas documented and synchronized with implementation.
2424
- Add enough structured logging for step-level debugging and failure diagnosis.
2525
- Do not log secret values for sensitive workflows (including thenv operations).
26+
- For `cmds/ttlc`, keep user-facing diagnostics/errors in centralized template helpers (`cmds/ttlc/internal/messages`) with stable enum-like IDs.
2627

2728
### Integration Rules
2829

cmds/ttlc/internal/cli/root.go

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/delinoio/oss/cmds/ttlc/internal/contracts"
1414
"github.com/delinoio/oss/cmds/ttlc/internal/diagnostic"
1515
"github.com/delinoio/oss/cmds/ttlc/internal/logging"
16+
"github.com/delinoio/oss/cmds/ttlc/internal/messages"
1617
)
1718

1819
const (
@@ -51,7 +52,7 @@ func execute(args []string, stdout io.Writer, stderr io.Writer) int {
5152
case contracts.TtlCommandRun:
5253
return executeRun(args[1:], stdout, stderr)
5354
default:
54-
_, _ = fmt.Fprintf(stderr, "unknown command: %s\n", args[0])
55+
_, _ = fmt.Fprintf(stderr, "unknown command %q\n", args[0])
5556
printUsage(stderr)
5657
return 2
5758
}
@@ -71,7 +72,7 @@ func executeCheck(args []string, stdout io.Writer, stderr io.Writer) int {
7172

7273
logger, err := logging.NewWithWriter(stderr, logging.Options{Level: "info", NoColor: noColor})
7374
if err != nil {
74-
_, _ = fmt.Fprintf(stderr, "init logger: %v\n", err)
75+
_, _ = fmt.Fprintf(stderr, "Failed to initialize logger: %v\n", err)
7576
if envelopeErr := writeCommandFailureEnvelope(stdout, contracts.TtlCommandCheck, map[string]any{
7677
"entry": entry,
7778
"cache_analysis": make([]compiler.CacheAnalysis, 0),
@@ -84,7 +85,7 @@ func executeCheck(args []string, stdout io.Writer, stderr io.Writer) int {
8485
service := newCompilerService(logger)
8586
result, err := service.Check(context.Background(), compiler.CheckOptions{Entry: entry})
8687
if err != nil {
87-
_, _ = fmt.Fprintf(stderr, "check failed: %v\n", err)
88+
_, _ = fmt.Fprintf(stderr, "Check command failed: %v\n", err)
8889
if envelopeErr := writeCommandFailureEnvelope(stdout, contracts.TtlCommandCheck, map[string]any{
8990
"entry": entry,
9091
"cache_analysis": make([]compiler.CacheAnalysis, 0),
@@ -128,7 +129,7 @@ func executeBuild(args []string, stdout io.Writer, stderr io.Writer) int {
128129

129130
logger, err := logging.NewWithWriter(stderr, logging.Options{Level: "info", NoColor: noColor})
130131
if err != nil {
131-
_, _ = fmt.Fprintf(stderr, "init logger: %v\n", err)
132+
_, _ = fmt.Fprintf(stderr, "Failed to initialize logger: %v\n", err)
132133
if envelopeErr := writeCommandFailureEnvelope(stdout, contracts.TtlCommandBuild, map[string]any{
133134
"entry": entry,
134135
"out_dir": outDir,
@@ -142,7 +143,7 @@ func executeBuild(args []string, stdout io.Writer, stderr io.Writer) int {
142143
service := newCompilerService(logger)
143144
result, err := service.Build(context.Background(), compiler.BuildOptions{Entry: entry, OutDir: outDir})
144145
if err != nil {
145-
_, _ = fmt.Fprintf(stderr, "build failed: %v\n", err)
146+
_, _ = fmt.Fprintf(stderr, "Build command failed: %v\n", err)
146147
if envelopeErr := writeCommandFailureEnvelope(stdout, contracts.TtlCommandBuild, map[string]any{
147148
"entry": entry,
148149
"out_dir": outDir,
@@ -189,7 +190,7 @@ func executeExplain(args []string, stdout io.Writer, stderr io.Writer) int {
189190

190191
logger, err := logging.NewWithWriter(stderr, logging.Options{Level: "info", NoColor: noColor})
191192
if err != nil {
192-
_, _ = fmt.Fprintf(stderr, "init logger: %v\n", err)
193+
_, _ = fmt.Fprintf(stderr, "Failed to initialize logger: %v\n", err)
193194
if envelopeErr := writeCommandFailureEnvelope(stdout, contracts.TtlCommandExplain, map[string]any{
194195
"entry": entry,
195196
"task": task,
@@ -203,7 +204,7 @@ func executeExplain(args []string, stdout io.Writer, stderr io.Writer) int {
203204
service := newCompilerService(logger)
204205
result, err := service.Explain(context.Background(), compiler.ExplainOptions{Entry: entry, Task: task})
205206
if err != nil {
206-
_, _ = fmt.Fprintf(stderr, "explain failed: %v\n", err)
207+
_, _ = fmt.Fprintf(stderr, "Explain command failed: %v\n", err)
207208
if envelopeErr := writeCommandFailureEnvelope(stdout, contracts.TtlCommandExplain, map[string]any{
208209
"entry": entry,
209210
"task": task,
@@ -251,12 +252,13 @@ func executeRun(args []string, stdout io.Writer, stderr io.Writer) int {
251252
parsedArgs, parseErr := parseRunArgsJSON(rawArgs)
252253
if parseErr != nil {
253254
diagnostics := []diagnostic.Diagnostic{
254-
{
255-
Kind: contracts.DiagnosticKindTypeError,
256-
Message: parseErr.Error(),
257-
Line: 1,
258-
Column: 1,
259-
},
255+
messages.NewDiagnostic(
256+
contracts.DiagnosticKindTypeError,
257+
messages.DiagnosticInvalidRunArgsJSON,
258+
1,
259+
1,
260+
parseErr.Error(),
261+
),
260262
}
261263
payload := map[string]any{
262264
"entry": entry,
@@ -274,12 +276,7 @@ func executeRun(args []string, stdout io.Writer, stderr io.Writer) int {
274276

275277
if strings.TrimSpace(task) == "" {
276278
diagnostics := []diagnostic.Diagnostic{
277-
{
278-
Kind: contracts.DiagnosticKindTypeError,
279-
Message: "--task is required for run command",
280-
Line: 1,
281-
Column: 1,
282-
},
279+
messages.NewDiagnostic(contracts.DiagnosticKindTypeError, messages.DiagnosticRunTaskRequired, 1, 1),
283280
}
284281
payload := map[string]any{
285282
"entry": entry,
@@ -297,7 +294,7 @@ func executeRun(args []string, stdout io.Writer, stderr io.Writer) int {
297294

298295
logger, err := logging.NewWithWriter(stderr, logging.Options{Level: "info", NoColor: noColor})
299296
if err != nil {
300-
_, _ = fmt.Fprintf(stderr, "init logger: %v\n", err)
297+
_, _ = fmt.Fprintf(stderr, "Failed to initialize logger: %v\n", err)
301298
if envelopeErr := writeCommandFailureEnvelope(stdout, contracts.TtlCommandRun, map[string]any{
302299
"entry": entry,
303300
"task": task,
@@ -318,7 +315,7 @@ func executeRun(args []string, stdout io.Writer, stderr io.Writer) int {
318315
Args: parsedArgs,
319316
})
320317
if err != nil {
321-
_, _ = fmt.Fprintf(stderr, "run failed: %v\n", err)
318+
_, _ = fmt.Fprintf(stderr, "Run command failed: %v\n", err)
322319
if envelopeErr := writeCommandFailureEnvelope(stdout, contracts.TtlCommandRun, map[string]any{
323320
"entry": entry,
324321
"task": task,
@@ -376,12 +373,14 @@ func writeEnvelope(stdout io.Writer, command contracts.TtlCommand, status contra
376373

377374
func writeCommandFailureEnvelope(stdout io.Writer, command contracts.TtlCommand, data any, commandErr error) error {
378375
return writeEnvelope(stdout, command, contracts.TtlResponseStatusFailed, []diagnostic.Diagnostic{
379-
{
380-
Kind: contracts.DiagnosticKindIOError,
381-
Message: commandErr.Error(),
382-
Line: 1,
383-
Column: 1,
384-
},
376+
messages.NewDiagnostic(
377+
contracts.DiagnosticKindIOError,
378+
messages.DiagnosticCommandFailure,
379+
1,
380+
1,
381+
command,
382+
commandErr.Error(),
383+
),
385384
}, data)
386385
}
387386

@@ -405,15 +404,15 @@ func parseRunArgsJSON(raw string) (map[string]any, error) {
405404

406405
var decoded any
407406
if err := decoder.Decode(&decoded); err != nil {
408-
return nil, fmt.Errorf("parse --args JSON object: %w", err)
407+
return nil, messages.WrapError(messages.ErrorParseRunArgsDecode, err)
409408
}
410409
parsed, ok := decoded.(map[string]any)
411410
if !ok || parsed == nil {
412-
return nil, fmt.Errorf("parse --args JSON object: expected JSON object")
411+
return nil, messages.NewError(messages.ErrorParseRunArgsExpectedObject)
413412
}
414413
trailing := struct{}{}
415414
if err := decoder.Decode(&trailing); err != io.EOF {
416-
return nil, fmt.Errorf("parse --args JSON object: unexpected trailing tokens")
415+
return nil, messages.NewError(messages.ErrorParseRunArgsTrailingTokens)
417416
}
418417
return parsed, nil
419418
}

0 commit comments

Comments
 (0)