go-spec-writing-unblock
The fix: GAP-001 was spec-blocked (board gate: "needs implementation spec before dispatch"). Unblocked by committing the spec as foreman-direct non-code work — ~/specs/SPEC-IMPL-GAP-001-context-compiler.md — via the shortened loop (skip worker/guard/judge, commit docs). The spec defines internal/contextcompiler per the required template.
Exact Go interfaces (core contract, verified compilable):
type SourceKind string
const (
SourceBoard SourceKind = "board" // Hilo dispatch board state
SourceEnv SourceKind = "env" // environment variables
SourceSpec SourceKind = "spec" // implementation spec documents
SourceMessage SourceKind = "message" // session / message log
SourceToolSpec SourceKind = "toolspec" // tool schema definitions
)
type Source interface {
ID() string
Kind() SourceKind
Raw(ctx context.Context) (io.Reader, error) // must be concurrency-safe
}
type Options struct {
IncludeEnv bool
MaxDepth int // ref resolution cap; default 8
Timeout time.Duration // per-compile budget; default 5s
Required []SourceKind // fail fast if absent
SecretPattern string // regex; matched env values redacted
}
type Section struct {
ID, SourceID, Body, Digest string
Kind SourceKind
Refs []string
Depth int
Provenance map[string]string
}
type Compiled struct {
Sections []Section // stable order: source order, then ID
Digest string // sha256 over ordered section digests
CompiledAt time.Time
Elapsed time.Duration
SourceCount int
}
type Validator interface{ Validate(c *Compiled) error }
type Hook interface{ AfterCompile(ctx context.Context, c *Compiled) error }
type Store interface {
Save(ctx context.Context, c *Compiled) error
Load(ctx context.Context, digest string) (*Compiled, error)
}
type Compiler interface {
Compile(ctx context.Context, sources []Source, opts Options) (*Compiled, error)
}
func New(store Store, validators []Validator, hooks []Hook) Compiler
Ordered algorithm (deterministic):
1. Preflight — defaults (Timeout 5s, MaxDepth 8, secret regex); validate opts.
2. Fail fast — every Required kind must have ≥1 source → else ErrSourceMissing (never a partial compile).
3. Normalize — iterate sources in caller order; Raw(ctx) capped at 1 MiB; per-section sha256(ID+Kind+SourceID+Body).
4. Resolve refs — DFS with visiting set: cycle → ErrRefCycle, dangling → ErrRefDangling, depth > MaxDepth → ErrTooDeep; record Depth.
5. Validate — run Validators in order (unique IDs, required bodies non-empty).
6. Compose — final order = source order, then ID ascending; overall digest over ordered section digests.
7. Persist — store.Save best-effort (logged, not fatal).
8. Hooks — fire in order; errors logged, not fatal. Deadline at any step → ErrTimeout.
Error catalog (10 codes): SRC_MISSING, SRC_READ, PARSE, REF_CYCLE, REF_DANGLING, VAL, OP_TIMEOUT, DEPTH (all 422, OP_TIMEOUT 504), NOT_FOUND (404), INTERNAL (500) — via ErrorCode(err) sentinel mapping.
Test scenarios: 15 unit (U1 empty compile, U2 missing required, U3 order preserved, U4 dup IDs, U5 dangling ref, U6 self-cycle, U7 two-node cycle, U8 depth cap, U9 determinism, U10 checksum sensitivity, U11 timeout, U12 secret redaction, U13 provenance, U14 empty body, U15 concurrent independence) + 5 handler (H1 POST /compile 200, H2 missing kind 422 SRC_MISSING, H3 cycle 422 REF_CYCLE, H4 GET miss 404 NOT_FOUND, H5 /health 200/503).
Wiring: main builds contextcompiler.New(store, validators, hooks), handlers.Register(mux, cc) (POST /compile, GET /compile/{id}, GET /health); env: GAP_CC_TIMEOUT, GAP_CC_MAX_DEPTH, GAP_CC_SOURCE_ORDER.
Hilo impact: board gate satisfied by foreman-committed spec; GAP-001 spec-blocked ticks 3 → 0; dispatch proceeds next tick. Additive package, no dispatch/worker contract changes, low risk (deterministic, no network deps, redaction on), revert = delete package + spec.
1. **Spec committed** — `~/specs/SPEC-IMPL-GAP-001-context-compiler.md` written (11,559 bytes), all 9 template sections present (`#`, `## 1..9`, `### 6.1`, `### 6.2` headings verified via grep). 2. **Scenario counts** — awk extraction of the scenario tables confirms exactly **U1–U15 (15 unit)** and **H1–H5 (5 handler)**; 10 error-code rows in the catalog. 3. **Interfaces type-check** — extracted the exact declarations into a scratch module (`/tmp/cccheck`, `go mod init example.com/cccheck`): - `go vet ./...` → clean - `go build ./...` → **TYPECHECK-OK** - `gofmt -l .` → clean after `gofmt -w` (no formatting drift) 4. **Error catalog behavior** — added a unit test asserting all 10 `ErrorCode` mappings (`ErrSourceMissing→SRC_MISSING`, …, `ErrNotFound→NOT_FOUND`, unknown→`INTERNAL`); `go test ./...` → **ok, 0.002s**. 5. **Edge cases covered in spec** — zero sources, empty required body, self- and mutual cycles, dangling refs, depth overflow, deadline expiry, secret leakage, determinism/checksum sensitivity, concurrent compiles, store miss, store-down health. ---
{"model": "deepseek-v4-flash", "problem_class": "go-spec-writing-unblock", "result": "passed", "tests": 20}The fix: GAP-001 was spec-blocked (board gate: "needs implementation spec before dispatch"). Unblocked by committing the spec as foreman-direct non-code work — ~/specs/SPEC-IMPL-GAP-001-context-compiler.md — via the shortened loop (skip worker/guard/judge, commit docs). The spec defines internal/contextcompiler per the required template.
Exact Go interfaces (core contract, verified compilable):
type SourceKind string
const (
SourceBoard SourceKind = "board" // Hilo dispatch board state
SourceEnv SourceKind = "env" // environment variables
SourceSpec SourceKind = "spec" // implementation spec documents
SourceMessage SourceKind = "message" // session / message log
SourceToolSpec SourceKind = "toolspec" // tool schema definitions
)
type Source interface {
ID() string
Kind() SourceKind
Raw(ctx context.Context) (io.Reader, error) // must be concurrency-safe
}
type Options struct {
IncludeEnv bool
MaxDepth int // ref resolution cap; default 8
Timeout time.Duration // per-compile budget; default 5s
Required []SourceKind // fail fast if absent
SecretPattern string // regex; matched env values redacted
}
type Section struct {
ID, SourceID, Body, Digest string
Kind SourceKind
Refs []string
Depth int
Provenance map[string]string
}
type Compiled struct {
Sections []Section // stable order: source order, then ID
Digest string // sha256 over ordered section digests
CompiledAt time.Time
Elapsed time.Duration
SourceCount int
}
type Validator interface{ Validate(c *Compiled) error }
type Hook interface{ AfterCompile(ctx context.Context, c *Compiled) error }
type Store interface {
Save(ctx context.Context, c *Compiled) error
Load(ctx context.Context, digest string) (*Compiled, error)
}
type Compiler interface {
Compile(ctx context.Context, sources []Source, opts Options) (*Compiled, error)
}
func New(store Store, validators []Validator, hooks []Hook) Compiler
Ordered algorithm (deterministic):
1. Preflight — defaults (Timeout 5s, MaxDepth 8, secret regex); validate opts.
2. Fail fast — every Required kind must have ≥1 source → else ErrSourceMissing (never a partial compile).
3. Normalize — iterate sources in caller order; Raw(ctx) capped at 1 MiB; per-section sha256(ID+Kind+SourceID+Body).
4. Resolve refs — DFS with visiting set: cycle → ErrRefCycle, dangling → ErrRefDangling, depth > MaxDepth → ErrTooDeep; record Depth.
5. Validate — run Validators in order (unique IDs, required bodies non-empty).
6. Compose — final order = source order, then ID ascending; overall digest over ordered section digests.
7. Persist — store.Save best-effort (logged, not fatal).
8. Hooks — fire in order; errors logged, not fatal. Deadline at any step → ErrTimeout.
Error catalog (10 codes): SRC_MISSING, SRC_READ, PARSE, REF_CYCLE, REF_DANGLING, VAL, OP_TIMEOUT, DEPTH (all 422, OP_TIMEOUT 504), NOT_FOUND (404), INTERNAL (500) — via ErrorCode(err) sentinel mapping.
Test scenarios: 15 unit (U1 empty compile, U2 missing required, U3 order preserved, U4 dup IDs, U5 dangling ref, U6 self-cycle, U7 two-node cycle, U8 depth cap, U9 determinism, U10 checksum sensitivity, U11 timeout, U12 secret redaction, U13 provenance, U14 empty body, U15 concurrent independence) + 5 handler (H1 POST /compile 200, H2 missing kind 422 SRC_MISSING, H3 cycle 422 REF_CYCLE, H4 GET miss 404 NOT_FOUND, H5 /health 200/503).
Wiring: main builds contextcompiler.New(store, validators, hooks), handlers.Register(mux, cc) (POST /compile, GET /compile/{id}, GET /health); env: GAP_CC_TIMEOUT, GAP_CC_MAX_DEPTH, GAP_CC_SOURCE_ORDER.
Hilo impact: board gate satisfied by foreman-committed spec; GAP-001 spec-blocked ticks 3 → 0; dispatch proceeds next tick. Additive package, no dispatch/worker contract changes, low risk (deterministic, no network deps, redaction on), revert = delete package + spec.
1. **Spec committed** — `~/specs/SPEC-IMPL-GAP-001-context-compiler.md` written (11,559 bytes), all 9 template sections present (`#`, `## 1..9`, `### 6.1`, `### 6.2` headings verified via grep). 2. **Scenario counts** — awk extraction of the scenario tables confirms exactly **U1–U15 (15 unit)** and **H1–H5 (5 handler)**; 10 error-code rows in the catalog. 3. **Interfaces type-check** — extracted the exact declarations into a scratch module (`/tmp/cccheck`, `go mod init example.com/cccheck`): - `go vet ./...` → clean - `go build ./...` → **TYPECHECK-OK** - `gofmt -l .` → clean after `gofmt -w` (no formatting drift) 4. **Error catalog behavior** — added a unit test asserting all 10 `ErrorCode` mappings (`ErrSourceMissing→SRC_MISSING`, …, `ErrNotFound→NOT_FOUND`, unknown→`INTERNAL`); `go test ./...` → **ok, 0.002s**. 5. **Edge cases covered in spec** — zero sources, empty required body, self- and mutual cycles, dangling refs, depth overflow, deadline expiry, secret leakage, determinism/checksum sensitivity, concurrent compiles, store miss, store-down health. ---
{"model": "deepseek-v4-flash", "problem_class": "go-spec-writing-unblock", "result": "passed", "tests": 20}