diff --git a/STRUKTLY_README.md b/STRUKTLY_README.md new file mode 100644 index 0000000..750509f --- /dev/null +++ b/STRUKTLY_README.md @@ -0,0 +1,49 @@ +# Struktly + +**Struktly** is a governed, fail-first, deterministic code generation system. + +It operates on the principle that **no code is better than bad code**. + +## Core Principles + +1. **Fail-First**: If the generated code does not compile, pass tests, or satisfy policy, it is **discarded**. There are no silent retries. +2. **Determinism**: The same inputs (Spec + Policy) always produce the same scaffolding. AI is only used for bounded logic synthesis within strict templates. +3. **Auditability**: Every run produces an audit trail (`audit.json`, `policy.json`, `verification.json`). If verification fails, you get an audit log explaining why, but no code. +4. **No Hallucinated Intent**: If the system is unsure (simulated by "ambiguous" input), it returns `NEEDS_HUMAN_REVIEW` rather than guessing. + +## Architecture + +- **CLI**: The entry point. +- **Orchestrator**: Manages the pipeline (Input -> Policy -> Generate -> Synthesize -> Verify -> Output). +- **Generator**: Uses `text/template` to create deterministic `main.go` and handler skeletons. +- **Synthesizer**: Fills in handler logic and tests. In a real system, this calls an LLM. Here, it is mocked to demonstrate success/failure modes. +- **Gates**: Runs `go fmt`, `go vet`, and `go test` inside a sandbox (temp directory). +- **Policy Engine**: Enforces constraints (e.g., max handlers). + +## Usage + +Build the tool: +```bash +go build -o struktly ./cmd/struktly +``` + +Run a generation: +```bash +./struktly --spec examples/struktly/petstore.yaml --policy examples/struktly/policy.yaml --out output_dir +``` + +## Failure Scenarios (Demo) + +The included `demo_struktly.sh` script demonstrates three scenarios: + +1. **Success**: Valid inputs produce a working Go service with passing tests. +2. **Failure**: Inputs that trigger "bad code" generation (simulated) result in **no output code**, only audit logs. + - *Why?* Because `go vet` or `go test` failed. +3. **Ambiguity**: Inputs that trigger uncertainty result in `NEEDS_HUMAN_REVIEW`. + - *Why?* The system refuses to guess business logic. + +## What is NOT Supported + +- **Retries**: If it fails, fix the input or the prompt. Do not loop until it "looks right". +- **AI Architecture**: The AI cannot add new files, change the router, or pick dependencies. It only fills the function bodies it is assigned. +- **Framework Magic**: The generated code is standard library Go. diff --git a/cmd/struktly/main.go b/cmd/struktly/main.go new file mode 100644 index 0000000..941c488 --- /dev/null +++ b/cmd/struktly/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "fmt" + "os" + + "github.com/froppa/stackkit/internal/struktly/orchestrator" + "github.com/spf13/cobra" +) + +func main() { + var specPath string + var policyPath string + var outDir string + + var rootCmd = &cobra.Command{ + Use: "struktly", + Short: "Struktly Code Generation", + Run: func(cmd *cobra.Command, args []string) { + if err := orchestrator.Run(specPath, policyPath, outDir); err != nil { + // We don't necessarily exit 1 if it's a "controlled failure" (e.g. policy violation), + // but generally if the tool fails to produce the expected output, exit !0 is good for scripts. + // However, the prompt says "OR a failure report". If we produce a failure report, maybe we exit 0? + // "FAIL-FIRST IS A FEATURE... DISCARD IT". + // "If verification fails, the run fails regardless of AI confidence" + // I will print the error and exit 1 so CI pipelines know it failed. + fmt.Fprintf(os.Stderr, "Struktly Run Failed: %v\n", err) + os.Exit(1) + } + fmt.Println("Struktly Run Successful.") + }, + } + + rootCmd.Flags().StringVar(&specPath, "spec", "", "Path to OpenAPI spec") + rootCmd.Flags().StringVar(&policyPath, "policy", "", "Path to Policy file") + rootCmd.Flags().StringVar(&outDir, "out", "output", "Output directory") + _ = rootCmd.MarkFlagRequired("spec") + _ = rootCmd.MarkFlagRequired("policy") + + if err := rootCmd.Execute(); err != nil { + os.Exit(1) + } +} diff --git a/demo_struktly.sh b/demo_struktly.sh new file mode 100644 index 0000000..df20e8e --- /dev/null +++ b/demo_struktly.sh @@ -0,0 +1,40 @@ +#!/bin/bash +set -e + +# Build +echo "Building struktly..." +go build -o struktly ./cmd/struktly + +echo ">>> RUN 1: SUCCESS CASE" +rm -rf out_success +./struktly --spec examples/struktly/petstore.yaml --policy examples/struktly/policy.yaml --out out_success +ls -l out_success +# Check for code +if [ ! -f out_success/main.go ]; then echo "FAIL: main.go missing"; exit 1; fi +if [ ! -f out_success/audit.json ]; then echo "FAIL: audit.json missing"; exit 1; fi + +echo ">>> RUN 2: FAILURE CASE (Bad Code)" +rm -rf out_fail +set +e +./struktly --spec examples/struktly/petstore_fail.yaml --policy examples/struktly/policy.yaml --out out_fail +EXIT_CODE=$? +set -e +if [ $EXIT_CODE -eq 0 ]; then echo "FAIL: Should have failed"; exit 1; fi +# Check NO code +if [ -f out_fail/main.go ]; then echo "FAIL: main.go should NOT exist"; exit 1; fi +# Check Audit exists +if [ ! -f out_fail/audit.json ]; then echo "FAIL: audit.json missing"; exit 1; fi +grep "FAIL" out_fail/audit.json > /dev/null +echo "Run 2 Verified Correctly" + +echo ">>> RUN 3: AMBIGUOUS CASE" +rm -rf out_ambiguous +set +e +./struktly --spec examples/struktly/petstore_ambiguous.yaml --policy examples/struktly/policy.yaml --out out_ambiguous +EXIT_CODE=$? +set -e +if [ $EXIT_CODE -eq 0 ]; then echo "FAIL: Should have failed"; exit 1; fi +grep "NEEDS_HUMAN_REVIEW" out_ambiguous/audit.json > /dev/null +echo "Run 3 Verified Correctly" + +echo "ALL DEMOS PASSED" diff --git a/examples/struktly/petstore.yaml b/examples/struktly/petstore.yaml new file mode 100644 index 0000000..b890ab3 --- /dev/null +++ b/examples/struktly/petstore.yaml @@ -0,0 +1,9 @@ +openapi: 3.0.0 +info: + title: Petstore Service + version: 1.0.0 +paths: + /pets: + get: + operationId: ListPets + summary: List all pets diff --git a/examples/struktly/petstore_ambiguous.yaml b/examples/struktly/petstore_ambiguous.yaml new file mode 100644 index 0000000..f1f4a9e --- /dev/null +++ b/examples/struktly/petstore_ambiguous.yaml @@ -0,0 +1,9 @@ +openapi: 3.0.0 +info: + title: Petstore ambiguous Service + version: 1.0.0 +paths: + /pets: + get: + operationId: ListPets + summary: List all pets diff --git a/examples/struktly/petstore_fail.yaml b/examples/struktly/petstore_fail.yaml new file mode 100644 index 0000000..71efa41 --- /dev/null +++ b/examples/struktly/petstore_fail.yaml @@ -0,0 +1,9 @@ +openapi: 3.0.0 +info: + title: Petstore fail Service + version: 1.0.0 +paths: + /pets: + get: + operationId: ListPets + summary: List all pets diff --git a/examples/struktly/policy.yaml b/examples/struktly/policy.yaml new file mode 100644 index 0000000..28db4c5 --- /dev/null +++ b/examples/struktly/policy.yaml @@ -0,0 +1,3 @@ +max_handlers: 5 +required: + - tests diff --git a/internal/struktly/audit/types.go b/internal/struktly/audit/types.go new file mode 100644 index 0000000..4163cad --- /dev/null +++ b/internal/struktly/audit/types.go @@ -0,0 +1,34 @@ +package audit + +import ( + "time" +) + +type AuditLog struct { + Timestamp time.Time `json:"timestamp"` + Inputs InputHashes `json:"inputs"` + Policy PolicyResult `json:"policy"` + Generation GenerationResult `json:"generation"` + Verification VerificationResult `json:"verification"` + Outcome string `json:"outcome"` // SUCCESS, FAIL, REVIEW +} + +type InputHashes struct { + SpecHash string `json:"spec_hash"` + PolicyHash string `json:"policy_hash"` +} + +type PolicyResult struct { + Passed bool `json:"passed"` + Reason string `json:"reason,omitempty"` +} + +type GenerationResult struct { + Prompts []string `json:"prompts"` + Model string `json:"model"` +} + +type VerificationResult struct { + Passed bool `json:"passed"` + Output string `json:"output,omitempty"` +} diff --git a/internal/struktly/gates/runner.go b/internal/struktly/gates/runner.go new file mode 100644 index 0000000..06e3112 --- /dev/null +++ b/internal/struktly/gates/runner.go @@ -0,0 +1,63 @@ +package gates + +import ( + "bytes" + "fmt" + "os/exec" +) + +type Runner struct{} + +func New() *Runner { + return &Runner{} +} + +// Verify runs go mod init, go fmt, go vet, and go test. +// Returns the combined output and an error if any step fails. +func (r *Runner) Verify(dir string) (string, error) { + var combinedOutput bytes.Buffer + + // Helper to run and log + run := func(name string, args ...string) error { + cmd := exec.Command(name, args...) + cmd.Dir = dir + var out bytes.Buffer + var stderr bytes.Buffer + cmd.Stdout = &out + cmd.Stderr = &stderr + + combinedOutput.WriteString(fmt.Sprintf("Running: %s %v\n", name, args)) + err := cmd.Run() + + combinedOutput.Write(out.Bytes()) + combinedOutput.Write(stderr.Bytes()) + combinedOutput.WriteString("\n") + + if err != nil { + return fmt.Errorf("%s failed: %w", name, err) + } + return nil + } + + // 1. Init Module + if err := run("go", "mod", "init", "generated"); err != nil { + return combinedOutput.String(), err + } + + // 2. Fmt + if err := run("go", "fmt", "./..."); err != nil { + return combinedOutput.String(), err + } + + // 3. Vet + if err := run("go", "vet", "./..."); err != nil { + return combinedOutput.String(), err + } + + // 4. Test + if err := run("go", "test", "./..."); err != nil { + return combinedOutput.String(), err + } + + return combinedOutput.String(), nil +} diff --git a/internal/struktly/generator/renderer.go b/internal/struktly/generator/renderer.go new file mode 100644 index 0000000..180a0b8 --- /dev/null +++ b/internal/struktly/generator/renderer.go @@ -0,0 +1,48 @@ +package generator + +import ( + "bytes" + "fmt" + "text/template" + + "github.com/froppa/stackkit/internal/struktly/ir" +) + +func Render(svc *ir.Service) (map[string]string, error) { + files := make(map[string]string) + + // Render Main + mainContent, err := renderTemplate("main", MainTemplate, svc) + if err != nil { + return nil, err + } + files["main.go"] = mainContent + + // Render Handlers + handlersContent, err := renderTemplate("handlers", HandlersTemplate, svc) + if err != nil { + return nil, err + } + files["handlers.go"] = handlersContent + + // Render Tests + testContent, err := renderTemplate("main_test", TestTemplate, svc) + if err != nil { + return nil, err + } + files["main_test.go"] = testContent + + return files, nil +} + +func renderTemplate(name, tmplStr string, data interface{}) (string, error) { + tmpl, err := template.New(name).Parse(tmplStr) + if err != nil { + return "", fmt.Errorf("failed to parse template %s: %w", name, err) + } + var buf bytes.Buffer + if err := tmpl.Execute(&buf, data); err != nil { + return "", fmt.Errorf("failed to execute template %s: %w", name, err) + } + return buf.String(), nil +} diff --git a/internal/struktly/generator/templates.go b/internal/struktly/generator/templates.go new file mode 100644 index 0000000..0c2313e --- /dev/null +++ b/internal/struktly/generator/templates.go @@ -0,0 +1,51 @@ +package generator + +const MainTemplate = `package main + +import ( + "log" + "net/http" +) + +func main() { + mux := http.NewServeMux() +{{- range .Handlers }} + mux.HandleFunc("{{ .Path }}", {{ .Name }}) +{{- end }} + + log.Println("Starting {{ .Name }} on :8080") + if err := http.ListenAndServe(":8080", mux); err != nil { + log.Fatal(err) + } +} +` + +const HandlersTemplate = `package main + +import ( + "net/http" +) +{{ range .Handlers }} +// {{ .Name }} handles {{ .Method }} {{ .Path }} +// {{ .Description }} +func {{ .Name }}(w http.ResponseWriter, r *http.Request) { + // AI_LOGIC_START: {{ .Name }} + // AI_LOGIC_END: {{ .Name }} +} +{{ end }} +` + +const TestTemplate = `package main + +import ( + "net/http" + "net/http/httptest" + "testing" +) +{{ range .Handlers }} +func Test{{ .Name }}(t *testing.T) { + // AI_TEST_START: {{ .Name }} + // AI_TEST_END: {{ .Name }} +} +{{ end }} +` diff --git a/internal/struktly/ir/parser.go b/internal/struktly/ir/parser.go new file mode 100644 index 0000000..fc47875 --- /dev/null +++ b/internal/struktly/ir/parser.go @@ -0,0 +1,77 @@ +package ir + +import ( + "fmt" + "os" + "strings" + "unicode" + + "gopkg.in/yaml.v3" +) + +// Simplified structures for parsing YAML +type openApiSpec struct { + Info struct { + Title string `yaml:"title"` + } `yaml:"info"` + Paths map[string]map[string]struct { + Summary string `yaml:"summary"` + OperationID string `yaml:"operationId"` + } `yaml:"paths"` +} + +func Parse(filepath string) (*Service, error) { + data, err := os.ReadFile(filepath) + if err != nil { + return nil, fmt.Errorf("failed to read spec: %w", err) + } + + var spec openApiSpec + if err := yaml.Unmarshal(data, &spec); err != nil { + return nil, fmt.Errorf("failed to parse yaml: %w", err) + } + + svc := &Service{ + Name: spec.Info.Title, + } + + // Normalize name + svc.Name = strings.ReplaceAll(svc.Name, " ", "") + + for path, methods := range spec.Paths { + for method, op := range methods { + h := Handler{ + Name: op.OperationID, + Method: strings.ToUpper(method), + Path: path, + Description: op.Summary, + } + if h.Name == "" { + // Fallback name if operationId is missing + cleanPath := strings.ReplaceAll(path, "/", "_") + cleanPath = strings.Trim(cleanPath, "_") + h.Name = fmt.Sprintf("%s_%s", h.Method, cleanPath) + } + h.Name = sanitize(h.Name) + svc.Handlers = append(svc.Handlers, h) + } + } + + return svc, nil +} + +func sanitize(s string) string { + // Simple camel casing for identifiers + // e.g. list-pets -> ListPets + parts := strings.FieldsFunc(s, func(r rune) bool { + return !unicode.IsLetter(r) && !unicode.IsNumber(r) + }) + for i, p := range parts { + if len(p) > 0 { + r := []rune(p) + r[0] = unicode.ToUpper(r[0]) + parts[i] = string(r) + } + } + return strings.Join(parts, "") +} diff --git a/internal/struktly/ir/types.go b/internal/struktly/ir/types.go new file mode 100644 index 0000000..c88d490 --- /dev/null +++ b/internal/struktly/ir/types.go @@ -0,0 +1,13 @@ +package ir + +type Service struct { + Name string + Handlers []Handler +} + +type Handler struct { + Name string + Method string + Path string + Description string +} diff --git a/internal/struktly/orchestrator/run.go b/internal/struktly/orchestrator/run.go new file mode 100644 index 0000000..2cc948b --- /dev/null +++ b/internal/struktly/orchestrator/run.go @@ -0,0 +1,141 @@ +package orchestrator + +import ( + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "os" + "path/filepath" + "time" + + "github.com/froppa/stackkit/internal/struktly/audit" + "github.com/froppa/stackkit/internal/struktly/gates" + "github.com/froppa/stackkit/internal/struktly/generator" + "github.com/froppa/stackkit/internal/struktly/ir" + "github.com/froppa/stackkit/internal/struktly/policy" + "github.com/froppa/stackkit/internal/struktly/synthesizer" +) + +func Run(specPath, policyPath, outDir string) error { + log := audit.AuditLog{ + Timestamp: time.Now(), + Outcome: "FAIL", + } + + // Ensure outDir exists + if err := os.MkdirAll(outDir, 0755); err != nil { + return fmt.Errorf("failed to create output dir: %w", err) + } + + defer func() { + // Write Audit Artifacts + writeJSON(outDir, "audit.json", log) + writeJSON(outDir, "policy.json", log.Policy) + writeJSON(outDir, "verification.json", log.Verification) + writeJSON(outDir, "generation.json", log.Generation) + }() + + // 1. Hash Inputs + specBytes, err := os.ReadFile(specPath) + if err != nil { + return fmt.Errorf("read spec failed: %w", err) + } + policyBytes, err := os.ReadFile(policyPath) + if err != nil { + return fmt.Errorf("read policy failed: %w", err) + } + log.Inputs.SpecHash = hash(specBytes) + log.Inputs.PolicyHash = hash(policyBytes) + + // 2. Parse & Policy + svc, err := ir.Parse(specPath) + if err != nil { + log.Policy.Reason = "Parse Error: " + err.Error() + return err + } + + pol, err := policy.Load(policyPath) + if err != nil { + log.Policy.Reason = "Policy Load Error: " + err.Error() + return err + } + + if err := policy.Check(pol, svc); err != nil { + log.Policy.Passed = false + log.Policy.Reason = err.Error() + return err + } + log.Policy.Passed = true + + // 3. Generate Skeleton + files, err := generator.Render(svc) + if err != nil { + return fmt.Errorf("generation failed: %w", err) + } + + // 4. Synthesize + synth := synthesizer.New() + for name, content := range files { + newContent, err := synth.FillBlocks(name, content, svc.Name) // Use svc.Name as intent + if err != nil { + if err == synthesizer.ErrNeedsHumanReview { + log.Outcome = "NEEDS_HUMAN_REVIEW" + return err + } + return fmt.Errorf("synthesis failed: %w", err) + } + files[name] = newContent + } + log.Generation.Model = "mock-v1" + + // 5. Write Temp + tmpDir, err := os.MkdirTemp("", "struktly-*") + if err != nil { + return fmt.Errorf("temp dir failed: %w", err) + } + defer func() { _ = os.RemoveAll(tmpDir) }() + + for name, content := range files { + if err := os.WriteFile(filepath.Join(tmpDir, name), []byte(content), 0644); err != nil { + return fmt.Errorf("write temp file failed: %w", err) + } + } + + // 6. Verify + gateRunner := gates.New() + output, err := gateRunner.Verify(tmpDir) + log.Verification.Output = output + if err != nil { + log.Verification.Passed = false + log.Outcome = "FAIL" + return fmt.Errorf("verification failed: %w", err) + } + log.Verification.Passed = true + log.Outcome = "SUCCESS" + + // 7. Copy to Output + for name, content := range files { + if err := os.WriteFile(filepath.Join(outDir, name), []byte(content), 0644); err != nil { + return fmt.Errorf("write output file failed: %w", err) + } + } + + return nil +} + +func hash(b []byte) string { + h := sha256.Sum256(b) + return hex.EncodeToString(h[:]) +} + +func writeJSON(dir, filename string, v interface{}) { + f, err := os.Create(filepath.Join(dir, filename)) + if err != nil { + return + } + defer func() { _ = f.Close() }() + enc := json.NewEncoder(f) + enc.SetIndent("", " ") + _ = enc.Encode(v) +} diff --git a/internal/struktly/policy/checker.go b/internal/struktly/policy/checker.go new file mode 100644 index 0000000..fb488e9 --- /dev/null +++ b/internal/struktly/policy/checker.go @@ -0,0 +1,33 @@ +package policy + +import ( + "fmt" + "os" + + "github.com/froppa/stackkit/internal/struktly/ir" + "gopkg.in/yaml.v3" +) + +type Policy struct { + MaxHandlers int `yaml:"max_handlers"` + Required []string `yaml:"required"` // e.g. "tests" +} + +func Load(path string) (*Policy, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + var p Policy + if err := yaml.Unmarshal(data, &p); err != nil { + return nil, err + } + return &p, nil +} + +func Check(p *Policy, svc *ir.Service) error { + if p.MaxHandlers > 0 && len(svc.Handlers) > p.MaxHandlers { + return fmt.Errorf("policy violation: too many handlers (%d > %d)", len(svc.Handlers), p.MaxHandlers) + } + return nil +} diff --git a/internal/struktly/synthesizer/ai.go b/internal/struktly/synthesizer/ai.go new file mode 100644 index 0000000..95a3ffd --- /dev/null +++ b/internal/struktly/synthesizer/ai.go @@ -0,0 +1,69 @@ +package synthesizer + +import ( + "errors" + "fmt" + "regexp" + "strings" +) + +var ( + ErrNeedsHumanReview = errors.New("NEEDS_HUMAN_REVIEW") +) + +type Synthesizer struct{} + +func New() *Synthesizer { + return &Synthesizer{} +} + +// FillBlocks replaces AI markers with code based on the intent (service name for mocking). +func (s *Synthesizer) FillBlocks(filename, content, intent string) (string, error) { + if strings.Contains(intent, "ambiguous") { + return "", ErrNeedsHumanReview + } + + shouldFail := strings.Contains(intent, "fail") + + // Regex for logic blocks + // // AI_LOGIC_START: Name + // ... + // // AI_LOGIC_END: Name + reLogic := regexp.MustCompile(`(?s)// AI_LOGIC_START: (\w+).*?// AI_LOGIC_END: \w+`) + + content = reLogic.ReplaceAllStringFunc(content, func(match string) string { + submatches := reLogic.FindStringSubmatch(match) + if len(submatches) < 2 { + return match + } + funcName := submatches[1] + + if shouldFail { + return fmt.Sprintf(`w.Write("Syntax Error Here %s)`, funcName) // Missing quote + } + return fmt.Sprintf(`w.WriteHeader(http.StatusOK) + w.Write([]byte("Hello from %s"))`, funcName) + }) + + // Regex for test blocks + reTest := regexp.MustCompile(`(?s)// AI_TEST_START: (\w+).*?// AI_TEST_END: \w+`) + content = reTest.ReplaceAllStringFunc(content, func(match string) string { + submatches := reTest.FindStringSubmatch(match) + if len(submatches) < 2 { + return match + } + funcName := submatches[1] + + if shouldFail { + return `if true { t.Fail() } syntax error` + } + return fmt.Sprintf(`req := httptest.NewRequest("GET", "http://example.com/foo", nil) + w := httptest.NewRecorder() + %s(w, req) + if w.Result().StatusCode != http.StatusOK { + t.Errorf("Expected 200") + }`, funcName) + }) + + return content, nil +} diff --git a/out_ambiguous/audit.json b/out_ambiguous/audit.json new file mode 100644 index 0000000..c84dce8 --- /dev/null +++ b/out_ambiguous/audit.json @@ -0,0 +1,18 @@ +{ + "timestamp": "2026-01-31T01:10:56.342749306Z", + "inputs": { + "spec_hash": "87260417d96bfca455a0cc5f2394f0c8ec85f0ee05c3ba0b0f1b3511d9a2e832", + "policy_hash": "19a244d8372b039bf2863fc83ec5779c6618b44c39e3f1d14b66531d77ad6d8b" + }, + "policy": { + "passed": true + }, + "generation": { + "prompts": null, + "model": "" + }, + "verification": { + "passed": false + }, + "outcome": "NEEDS_HUMAN_REVIEW" +} diff --git a/out_ambiguous/generation.json b/out_ambiguous/generation.json new file mode 100644 index 0000000..665882a --- /dev/null +++ b/out_ambiguous/generation.json @@ -0,0 +1,4 @@ +{ + "prompts": null, + "model": "" +} diff --git a/out_ambiguous/policy.json b/out_ambiguous/policy.json new file mode 100644 index 0000000..fcaa704 --- /dev/null +++ b/out_ambiguous/policy.json @@ -0,0 +1,3 @@ +{ + "passed": true +} diff --git a/out_ambiguous/verification.json b/out_ambiguous/verification.json new file mode 100644 index 0000000..e41511f --- /dev/null +++ b/out_ambiguous/verification.json @@ -0,0 +1,3 @@ +{ + "passed": false +} diff --git a/out_fail/audit.json b/out_fail/audit.json new file mode 100644 index 0000000..9d6e77d --- /dev/null +++ b/out_fail/audit.json @@ -0,0 +1,19 @@ +{ + "timestamp": "2026-01-31T01:10:56.212087789Z", + "inputs": { + "spec_hash": "ccc51c1f8c98ec702620412f62425c72081530a183565f72ca1f191f9b1f35d4", + "policy_hash": "19a244d8372b039bf2863fc83ec5779c6618b44c39e3f1d14b66531d77ad6d8b" + }, + "policy": { + "passed": true + }, + "generation": { + "prompts": null, + "model": "mock-v1" + }, + "verification": { + "passed": false, + "output": "Running: go [mod init generated]\ngo: creating new go.mod: module generated\ngo: to add module requirements and sums:\n\tgo mod tidy\n\nRunning: go [fmt ./...]\nhandlers.go:10:10: string literal not terminated\nhandlers.go:11:1: expected operand, found '}'\nhandlers.go:12:2: missing ',' in argument list\nmain_test.go:10:23: expected ';', found syntax\nmain_test.go:12:2: expected '}', found 'EOF'\nexit status 2\n\n" + }, + "outcome": "FAIL" +} diff --git a/out_fail/generation.json b/out_fail/generation.json new file mode 100644 index 0000000..4c54c16 --- /dev/null +++ b/out_fail/generation.json @@ -0,0 +1,4 @@ +{ + "prompts": null, + "model": "mock-v1" +} diff --git a/out_fail/policy.json b/out_fail/policy.json new file mode 100644 index 0000000..fcaa704 --- /dev/null +++ b/out_fail/policy.json @@ -0,0 +1,3 @@ +{ + "passed": true +} diff --git a/out_fail/verification.json b/out_fail/verification.json new file mode 100644 index 0000000..70a0d0a --- /dev/null +++ b/out_fail/verification.json @@ -0,0 +1,4 @@ +{ + "passed": false, + "output": "Running: go [mod init generated]\ngo: creating new go.mod: module generated\ngo: to add module requirements and sums:\n\tgo mod tidy\n\nRunning: go [fmt ./...]\nhandlers.go:10:10: string literal not terminated\nhandlers.go:11:1: expected operand, found '}'\nhandlers.go:12:2: missing ',' in argument list\nmain_test.go:10:23: expected ';', found syntax\nmain_test.go:12:2: expected '}', found 'EOF'\nexit status 2\n\n" +} diff --git a/out_success/audit.json b/out_success/audit.json new file mode 100644 index 0000000..ae8091a --- /dev/null +++ b/out_success/audit.json @@ -0,0 +1,19 @@ +{ + "timestamp": "2026-01-31T01:10:53.360675527Z", + "inputs": { + "spec_hash": "a7b6b4b36058b50ccf3484ca72ce54307244dda9c89e6bfde390aedabf256b1e", + "policy_hash": "19a244d8372b039bf2863fc83ec5779c6618b44c39e3f1d14b66531d77ad6d8b" + }, + "policy": { + "passed": true + }, + "generation": { + "prompts": null, + "model": "mock-v1" + }, + "verification": { + "passed": true, + "output": "Running: go [mod init generated]\ngo: creating new go.mod: module generated\ngo: to add module requirements and sums:\n\tgo mod tidy\n\nRunning: go [fmt ./...]\nhandlers.go\nmain_test.go\n\nRunning: go [vet ./...]\n\nRunning: go [test ./...]\nok \tgenerated\t0.029s\n\n" + }, + "outcome": "SUCCESS" +} diff --git a/out_success/generation.json b/out_success/generation.json new file mode 100644 index 0000000..4c54c16 --- /dev/null +++ b/out_success/generation.json @@ -0,0 +1,4 @@ +{ + "prompts": null, + "model": "mock-v1" +} diff --git a/out_success/handlers.go b/out_success/handlers.go new file mode 100644 index 0000000..17fdbe0 --- /dev/null +++ b/out_success/handlers.go @@ -0,0 +1,12 @@ +package main + +import ( + "net/http" +) + +// ListPets handles GET /pets +// List all pets +func ListPets(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte("Hello from ListPets")) +} diff --git a/out_success/main.go b/out_success/main.go new file mode 100644 index 0000000..979491c --- /dev/null +++ b/out_success/main.go @@ -0,0 +1,16 @@ +package main + +import ( + "log" + "net/http" +) + +func main() { + mux := http.NewServeMux() + mux.HandleFunc("/pets", ListPets) + + log.Println("Starting PetstoreService on :8080") + if err := http.ListenAndServe(":8080", mux); err != nil { + log.Fatal(err) + } +} diff --git a/out_success/main_test.go b/out_success/main_test.go new file mode 100644 index 0000000..cea5102 --- /dev/null +++ b/out_success/main_test.go @@ -0,0 +1,16 @@ +package main + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +func TestListPets(t *testing.T) { + req := httptest.NewRequest("GET", "http://example.com/foo", nil) + w := httptest.NewRecorder() + ListPets(w, req) + if w.Result().StatusCode != http.StatusOK { + t.Errorf("Expected 200") + } +} diff --git a/out_success/policy.json b/out_success/policy.json new file mode 100644 index 0000000..fcaa704 --- /dev/null +++ b/out_success/policy.json @@ -0,0 +1,3 @@ +{ + "passed": true +} diff --git a/out_success/verification.json b/out_success/verification.json new file mode 100644 index 0000000..efc3d97 --- /dev/null +++ b/out_success/verification.json @@ -0,0 +1,4 @@ +{ + "passed": true, + "output": "Running: go [mod init generated]\ngo: creating new go.mod: module generated\ngo: to add module requirements and sums:\n\tgo mod tidy\n\nRunning: go [fmt ./...]\nhandlers.go\nmain_test.go\n\nRunning: go [vet ./...]\n\nRunning: go [test ./...]\nok \tgenerated\t0.029s\n\n" +} diff --git a/struktly b/struktly new file mode 100755 index 0000000..adf5ab4 Binary files /dev/null and b/struktly differ