Skip to content
Closed
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
32 changes: 21 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ jobs:
name: Lint
runs-on: ubuntu-latest
timeout-minutes: 15
env:
GOCACHE: ${{ runner.temp }}/go-build
GOLANGCI_LINT_CACHE: ${{ runner.temp }}/golangci-lint
steps:
- name: Check out code
uses: actions/checkout@v6
Expand All @@ -30,8 +27,18 @@ jobs:
go-version: "1.23.x"
cache: true

- name: Verify generated parser
env:
GOCACHE: ${{ runner.temp }}/go-build
run: |
go generate ./compiler/parse
git diff --exit-code -- compiler/parse/parser.go

- name: Run golangci-lint
uses: golangci/golangci-lint-action@v9.2.0
env:
GOCACHE: ${{ runner.temp }}/go-build
GOLANGCI_LINT_CACHE: ${{ runner.temp }}/golangci-lint
with:
version: v2.8.0
only-new-issues: false
Expand All @@ -45,8 +52,6 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
env:
GOCACHE: ${{ runner.temp }}/go-build
steps:
- name: Check out code
uses: actions/checkout@v6
Expand All @@ -58,14 +63,14 @@ jobs:
cache: true

- name: Run tests
env:
GOCACHE: ${{ runner.temp }}/go-build
run: go test -timeout 120s ./...

race:
name: Race Tests
runs-on: ubuntu-latest
timeout-minutes: 40
env:
GOCACHE: ${{ runner.temp }}/go-build
steps:
- name: Check out code
uses: actions/checkout@v6
Expand All @@ -78,15 +83,14 @@ jobs:

- name: Run race tests
env:
GOCACHE: ${{ runner.temp }}/go-build
GORACE: "halt_on_error=1"
run: go test -race -timeout 120s ./...

fuzz:
name: Fuzz
runs-on: ubuntu-latest
timeout-minutes: 20
env:
GOCACHE: ${{ runner.temp }}/go-build
steps:
- name: Check out code
uses: actions/checkout@v6
Expand All @@ -98,20 +102,24 @@ jobs:
cache: true

- name: Fuzz type decode
env:
GOCACHE: ${{ runner.temp }}/go-build
run: go test -fuzz=FuzzTypeDecodeToValidation -fuzztime=60s -timeout=120s

- name: Fuzz Lua source types
env:
GOCACHE: ${{ runner.temp }}/go-build
run: go test -fuzz=FuzzLuaTypeValidation -fuzztime=60s -timeout=120s

- name: Fuzz Lua with manifest
env:
GOCACHE: ${{ runner.temp }}/go-build
run: go test -fuzz=FuzzLuaWithManifestTypes -fuzztime=60s -timeout=120s

bench:
name: Benchmarks
runs-on: ubuntu-latest
timeout-minutes: 15
env:
GOCACHE: ${{ runner.temp }}/go-build
steps:
- name: Check out code
uses: actions/checkout@v6
Expand All @@ -123,6 +131,8 @@ jobs:
cache: true

- name: Run benchmarks
env:
GOCACHE: ${{ runner.temp }}/go-build
run: go test -bench="Benchmark(Validate|Is)" -benchmem -count=1 -timeout=60s | tee bench.txt

- name: Upload benchmark results
Expand Down
16 changes: 16 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
version: "2"

linters:
exclusions:
warn-unused: true
rules:
# These test assertion helpers return diagnostic.Diagnostic so callers
# can inspect the selected value. Diagnostic also implements error for
# production consumers, which makes ignored assertion results look like
# unchecked errors to errcheck even though the helpers fail through T.
- linters:
- errcheck
source: '^\s*requireDiagnostic(Code)?\('
- linters:
- errcheck
source: '^\s*requireDiagnosticShape\('
9 changes: 9 additions & 0 deletions analysis/architecture/SCHEMA_VERSIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ closed or negotiate before emitting a surface newer than the consumer supports.

| Surface | Constant | Current | Covers | Bump when |
| --- | --- | --- | --- | --- |
| Canonical manifest wire | `manifest.WireFormatVersion` | v1 | Top-level JSON manifest envelope and its type, signature, effect, typestate, callback-phase, and ambient-global fields. | A wire field is removed, renamed, reinterpreted, or made newly required; additive optional fields still require a compatibility review. |
| Checker embedding | `embedding.EmbeddingSchemaVersion` | v1 | Stable document/source/resolution identity DTOs: `DocumentID`, digest-bound source locations and snapshots, unit plans/imports, resolution snapshots, `SolveSeq`, and `BodyInputDigest`. | An exported embedding DTO field, initial document scheme, or its identity/versioning semantics changes. |
| Judgment IR | `judgment.JIRSchemaVersion` | v11 | Judgment code registry and exported judgment record shape. | A judgment code, code metadata, or exported judgment/evidence/subject field shape changes. |
| Signature escape vocabulary | `signature.EscapeVocabVersion` | v1 | Signature `EscapeKind` labels and audited ownership capability labels synced with arena CallArgEscape/Ownership. | An escape/ownership label is added, removed, renamed, or changes boundary meaning. Requires joint cross-repo signoff per fence #1425. |
Expand All @@ -32,6 +33,14 @@ constant + journal a D-entry`.

## Journal

- D21: Registered canonical manifest JSON wire v1. The decoder continues to
accept the short-lived unversioned canonical JSON shape as v1, rejects future
versions closed, and identifies the pre-abstract-interpreter binary `INAM`
format with a typed migration error. `INAM` embeds removed type/effect
domains and cannot be converted losslessly; cache owners must treat that
error as a cache miss and rebuild the manifest from source. Standalone legacy
binary type payloads use the equivalent `types/io.ErrLegacyTypeWire` signal.

- D20: Post-union repin: Judgment IR v11 is the single consumer contract for
the declared typestate-requirement codes, `BodyInputDigest` and digest-bound
`SourceLocation` spans, and exported repair descriptors. Artifact debug maps
Expand Down
2 changes: 1 addition & 1 deletion analysis/architecture/promptmap_meta_audit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func TestPromptmapMetaAuditMatrixIsPresentAndWellFormed(t *testing.T) {
if err != nil {
t.Fatalf("open promptmap meta audit matrix: %v", err)
}
defer f.Close()
t.Cleanup(func() { _ = f.Close() })

reader := csv.NewReader(f)
rows, err := reader.ReadAll()
Expand Down
4 changes: 0 additions & 4 deletions analysis/check/body/result_version.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,6 @@ func (w *bodyDigestWriter) writeType(label string, t typ.Type) {
w.writeString(label+":display", t.String())
}

func (w *bodyDigestWriter) writeProduct(label string, value product.Value) {
w.writeUint64(label, w.stableProductHash(value))
}

func (w *bodyDigestWriter) stableProductHash(value product.Value) uint64 {
h := internalhash.NewWriter()
_, _ = h.WriteString("product:")
Expand Down
3 changes: 2 additions & 1 deletion analysis/check/diagnostics/advice.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ func renderAdviceJudgmentWithPolicy(ctx judgmentRenderContext, item judgment.Jud
return diagnostic.Diagnostic{}, false
}
return diagnostic.New(diagnostic.DiagnosticSpec{
File: item.Spans[0].File,
Location: item.Spans[0].Location,
File: item.Spans[0].DisplayFile(),
Span: span,
Code: code,
Severity: severity,
Expand Down
3 changes: 2 additions & 1 deletion analysis/check/diagnostics/call_argument_proof_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ func (ProofContext) DirectCallArgument(item judgment.Judgment, primary diagnosti
Help: help,
Evidence: directCallArgumentJudgmentEvidence(display, item, proof, wording, primary),
Labels: []diagnostic.Label{{
File: item.Spans[0].File,
Location: item.Spans[0].Location,
File: item.Spans[0].DisplayFile(),
Span: primary,
Message: labelArgumentValue,
Placement: diagnostic.LabelPlacementBelow,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ func renderDiscriminatedUnionJudgmentWithPolicy(ctx judgmentRenderContext, item
return diagnostic.Diagnostic{}, false
}
return diagnostic.New(diagnostic.DiagnosticSpec{
File: item.Spans[0].File,
Location: item.Spans[0].Location,
File: item.Spans[0].DisplayFile(),
Span: span,
Code: code,
Severity: severity,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ func renderOptionalJudgmentWithPolicy(ctx judgmentRenderContext, item judgment.J
return diagnostic.Diagnostic{}, false
}
return diagnostic.New(diagnostic.DiagnosticSpec{
File: item.Spans[0].File,
Location: item.Spans[0].Location,
File: item.Spans[0].DisplayFile(),
Span: span,
Code: code,
Severity: severity,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ func renderRegistrationJudgmentWithPolicy(ctx judgmentRenderContext, item judgme
return diagnostic.Diagnostic{}, false
}
return diagnostic.New(diagnostic.DiagnosticSpec{
File: item.Spans[0].File,
Location: item.Spans[0].Location,
File: item.Spans[0].DisplayFile(),
Span: primary,
Code: code,
Severity: severity,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ func renderResultShapeJudgmentWithPolicy(ctx judgmentRenderContext, item judgmen
return diagnostic.Diagnostic{}, false
}
return diagnostic.New(diagnostic.DiagnosticSpec{
File: item.Spans[0].File,
Location: item.Spans[0].Location,
File: item.Spans[0].DisplayFile(),
Span: span,
Code: code,
Severity: severity,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ func renderTableDispatchJudgmentWithPolicy(ctx judgmentRenderContext, item judgm
return diagnostic.Diagnostic{}, false
}
return diagnostic.New(diagnostic.DiagnosticSpec{
File: item.Spans[0].File,
Location: item.Spans[0].Location,
File: item.Spans[0].DisplayFile(),
Span: lookupSpan,
Code: code,
Severity: severity,
Expand Down
9 changes: 7 additions & 2 deletions analysis/check/diagnostics/judgment_adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,13 @@ func RenderJudgments(items []judgment.Judgment, config Config) []diagnostic.Diag
if !ok {
continue
}
if d.Position.File == "" && len(item.Spans) != 0 {
d.Position.File = item.Spans[0].File
if len(item.Spans) != 0 {
if !d.Location.Document.Valid() {
d.Location = item.Spans[0].Location
}
if d.Position.File == "" {
d.Position.File = item.Spans[0].DisplayFile()
}
}
out = append(out, d)
}
Expand Down
3 changes: 2 additions & 1 deletion analysis/check/diagnostics/judgment_channel_lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ func renderChannelLifecycleJudgmentWithPolicy(ctx judgmentRenderContext, item ju
return diagnostic.Diagnostic{}, false
}
return diagnostic.New(diagnostic.DiagnosticSpec{
File: item.Spans[0].File,
Location: item.Spans[0].Location,
File: item.Spans[0].DisplayFile(),
Span: span,
Code: code,
Severity: severity,
Expand Down
3 changes: 2 additions & 1 deletion analysis/check/diagnostics/judgment_channel_select.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ func renderChannelSelectJudgmentWithPolicy(ctx judgmentRenderContext, item judgm
return diagnostic.Diagnostic{}, false
}
return diagnostic.New(diagnostic.DiagnosticSpec{
File: item.Spans[0].File,
Location: item.Spans[0].Location,
File: item.Spans[0].DisplayFile(),
Span: span,
Code: code,
Severity: severity,
Expand Down
3 changes: 2 additions & 1 deletion analysis/check/diagnostics/judgment_dead_assignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ func renderDeadAssignmentJudgmentWithPolicy(ctx judgmentRenderContext, item judg
span := diagnosticSpanFromJudgment(item.Spans[0])
presentation := ctx.proof.DeadAssignment(item, span, name)
return diagnostic.New(diagnostic.DiagnosticSpec{
File: item.Spans[0].File,
Location: item.Spans[0].Location,
File: item.Spans[0].DisplayFile(),
Span: span,
Code: code,
Severity: severity,
Expand Down
3 changes: 2 additions & 1 deletion analysis/check/diagnostics/judgment_direct_call.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ func renderDirectCallArgumentJudgmentWithPolicy(ctx judgmentRenderContext, item
return diagnostic.Diagnostic{}, false
}
return diagnostic.New(diagnostic.DiagnosticSpec{
File: item.Spans[0].File,
Location: item.Spans[0].Location,
File: item.Spans[0].DisplayFile(),
Span: span,
Code: code,
Severity: severity,
Expand Down
3 changes: 2 additions & 1 deletion analysis/check/diagnostics/judgment_direct_call_arity.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ func renderCallArityJudgmentWithPolicy(ctx judgmentRenderContext, item judgment.
return diagnostic.Diagnostic{}, false
}
return diagnostic.New(diagnostic.DiagnosticSpec{
File: item.Spans[0].File,
Location: item.Spans[0].Location,
File: item.Spans[0].DisplayFile(),
Span: span,
Code: presentation.Code,
Severity: severity,
Expand Down
3 changes: 2 additions & 1 deletion analysis/check/diagnostics/judgment_direct_call_callee.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ func renderCallCalleeJudgmentWithPolicy(ctx judgmentRenderContext, item judgment
return diagnostic.Diagnostic{}, false
}
return diagnostic.New(diagnostic.DiagnosticSpec{
File: item.Spans[0].File,
Location: item.Spans[0].Location,
File: item.Spans[0].DisplayFile(),
Span: span,
Code: presentation.Code,
Severity: severity,
Expand Down
3 changes: 2 additions & 1 deletion analysis/check/diagnostics/judgment_frozen_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ func renderFrozenTableJudgmentWithPolicy(ctx judgmentRenderContext, item judgmen
span := diagnosticSpanFromJudgment(item.Spans[0])
presentation := ctx.proof.FrozenTable(item, span)
return diagnostic.New(diagnostic.DiagnosticSpec{
File: item.Spans[0].File,
Location: item.Spans[0].Location,
File: item.Spans[0].DisplayFile(),
Span: span,
Code: code,
Message: presentation.Message,
Expand Down
3 changes: 2 additions & 1 deletion analysis/check/diagnostics/judgment_member_read.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ func renderMemberReadJudgmentWithPolicy(ctx judgmentRenderContext, item judgment
return diagnostic.Diagnostic{}, false
}
return diagnostic.New(diagnostic.DiagnosticSpec{
File: item.Spans[0].File,
Location: item.Spans[0].Location,
File: item.Spans[0].DisplayFile(),
Span: span,
Code: code,
Severity: severity,
Expand Down
3 changes: 2 additions & 1 deletion analysis/check/diagnostics/judgment_nonnil_assertion.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ func renderNonNilAssertionJudgmentWithPolicy(ctx judgmentRenderContext, item jud
span := diagnosticSpanFromJudgment(item.Spans[0])
presentation := ctx.proof.NonNilAssertion(item, span)
return diagnostic.New(diagnostic.DiagnosticSpec{
File: item.Spans[0].File,
Location: item.Spans[0].Location,
File: item.Spans[0].DisplayFile(),
Span: span,
Code: code,
Severity: severity,
Expand Down
3 changes: 2 additions & 1 deletion analysis/check/diagnostics/judgment_numeric_for.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ func renderNumericForJudgmentWithPolicy(ctx judgmentRenderContext, item judgment
return diagnostic.Diagnostic{}, false
}
return diagnostic.New(diagnostic.DiagnosticSpec{
File: item.Spans[0].File,
Location: item.Spans[0].Location,
File: item.Spans[0].DisplayFile(),
Span: span,
Code: code,
Severity: severity,
Expand Down
3 changes: 2 additions & 1 deletion analysis/check/diagnostics/judgment_return.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ func renderReturnJudgmentWithPolicy(ctx judgmentRenderContext, item judgment.Jud
sourceName := item.Actual.Label
presentation := ctx.proof.Return(item, label, sourceName, got, want, span)
return diagnostic.New(diagnostic.DiagnosticSpec{
File: item.Spans[0].File,
Location: item.Spans[0].Location,
File: item.Spans[0].DisplayFile(),
Span: span,
Code: code,
Severity: severity,
Expand Down
3 changes: 2 additions & 1 deletion analysis/check/diagnostics/judgment_send_safety.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ func renderSendIsolationJudgmentWithPolicy(_ judgmentRenderContext, item judgmen
labels = append(labels, sourceLabel(proofSpan, labelSendSafetyProof))
}
return diagnostic.New(diagnostic.DiagnosticSpec{
File: item.Spans[0].File,
Location: item.Spans[0].Location,
File: item.Spans[0].DisplayFile(),
Span: span,
Code: code,
Message: sendSafetyMessage(item),
Expand Down
4 changes: 2 additions & 2 deletions analysis/check/diagnostics/judgment_typestate_requirement.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ func renderTypestateRequirementJudgmentWithPolicy(ctx judgmentRenderContext, ite
}
if item.Code == judgment.CodeTypestateInvalidRequirement {
message := fmt.Sprintf("invalid typestate requirement for resource %s in protocol %s: expected %s, found %s", codeName(resource), protocol, codeName(item.Expected.Label), codeName(item.Actual.Label))
return diagnostic.New(diagnostic.DiagnosticSpec{File: item.Spans[0].File, Span: span, Code: diagnosticCodeForJudgment(item), Severity: severity,
return diagnostic.New(diagnostic.DiagnosticSpec{Location: item.Spans[0].Location, File: item.Spans[0].DisplayFile(), Span: span, Code: diagnosticCodeForJudgment(item), Severity: severity,
Message: message, Explanation: diagnostic.NewExplanation(diagnostic.Evidence{Kind: diagnostic.EvidenceAbstractFact, Trust: diagnosticTrustFromJudgmentEvidence(item, judgment.EvidenceAbstractFact, diagnostic.TrustProven), Span: diagnosticEvidenceSpanOr(item, judgment.EvidenceAbstractFact, span), Message: fmt.Sprintf("this call requires %s to be in %s, but solved state is %s", codeName(resource), codeName(item.Expected.Label), codeName(item.Actual.Label))}),
Labels: []diagnostic.Label{sourceLabel(span, "invalid typestate requirement")}, Help: fmt.Sprintf("Call this operation only when %s is in %s state.", codeName(resource), codeName(item.Expected.Label))}), true
}
message := fmt.Sprintf("cannot prove typestate requirement for resource %s: expected %s", codeName(resource), codeName(item.Expected.Label))
return diagnostic.New(diagnostic.DiagnosticSpec{File: item.Spans[0].File, Span: span, Code: diagnosticCodeForJudgment(item), Severity: severity,
return diagnostic.New(diagnostic.DiagnosticSpec{Location: item.Spans[0].Location, File: item.Spans[0].DisplayFile(), Span: span, Code: diagnosticCodeForJudgment(item), Severity: severity,
Message: message, Explanation: diagnostic.NewExplanation(diagnostic.Evidence{Kind: diagnostic.EvidenceMissingProof, Trust: diagnosticTrustFromJudgmentEvidence(item, judgment.EvidenceMissingProof, diagnostic.TrustRefuted), Span: diagnosticEvidenceSpanOr(item, judgment.EvidenceMissingProof, span), Message: fmt.Sprintf("no proof establishes %s in %s state at this call", codeName(resource), codeName(item.Expected.Label))}),
Labels: []diagnostic.Label{sourceLabel(span, "unproven typestate requirement")}, Help: fmt.Sprintf("Establish that %s is in %s state before this call.", codeName(resource), codeName(item.Expected.Label))}), true
}
3 changes: 2 additions & 1 deletion analysis/check/diagnostics/judgment_typestate_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ func renderTypestateInvalidTransitionJudgmentWithPolicy(ctx judgmentRenderContex
message := fmt.Sprintf("invalid transition for resource %s in protocol %s: expected %s, found %s", codeName(resource), evidence.Detail.Protocol, codeName(evidence.Detail.FromState), codeName(evidence.Detail.CurrentState))
explanation := fmt.Sprintf("this transition requires %s to be in %s, but solved state is %s", codeName(resource), codeName(evidence.Detail.FromState), codeName(evidence.Detail.CurrentState))
return diagnostic.New(diagnostic.DiagnosticSpec{
File: item.Spans[0].File,
Location: item.Spans[0].Location,
File: item.Spans[0].DisplayFile(),
Span: span,
Code: diagnosticCodeForJudgment(item),
Severity: severity,
Expand Down
Loading
Loading