Skip to content

fix(do178): text-mode summary no longer prints a negative GAP count - #87

Merged
SoundMatt merged 1 commit into
mainfrom
fix/do178-text-summary-negative-gap-86
Jul 29, 2026
Merged

fix(do178): text-mode summary no longer prints a negative GAP count#87
SoundMatt merged 1 commit into
mainfrom
fix/do178-text-summary-negative-gap-86

Conversation

@SoundMatt

Copy link
Copy Markdown
Owner

Summary

  • gofusa do178's text-mode summary line derives its GAP/MANUAL counts from rep.Gap/rep.Manual, which checkSourceCode() was corrupting: A-2.4 and A-3.1 are always assessed as StatusManual by Assess() (their allObjectives entry has an empty evidence file), so they're already counted in rep.Manual before checkSourceCode runs. That function wrongly assumed they'd instead been counted as StatusGap, so it unconditionally decremented rep.Gap (for A-2.4) and unconditionally re-incremented rep.Manual (for both), double-counting Manual and driving Gap negative whenever .fusa.json and/or all 4 plan docs were present — exactly the "-1 GAP" reported in do178: text-mode summary line prints negative GAP count (-1 GAP) on DAL-B report #86.
  • The JSON renderer never had this bug, since it derives satisfied/partial/gaps by walking rep.Objectives directly rather than trusting the separately-maintained counters.
  • Fix: since obj.Status never actually changes in checkSourceCode (both objectives stay StatusManual), drop the counter mutations entirely and only update the Evidence/Gap narration text.

Closes #86

Test plan

  • Added TestSummaryCounters_MatchObjectives_Issue86, which asserts rep.Pass/Fail/Gap/Manual/NA match a manual walk of rep.Objectives, and that the text renderer's enumerated GAP/MANUAL line counts agree with the header — fails pre-fix (gap=25 vs 26 enumerated, manual=9 vs 7 enumerated), passes post-fix.
  • Added TestSummary_NoNegativeGap_AllEvidencePresent_Issue86, an exact repro: DAL-D project with every applicable evidence file present (zero real gaps) — pre-fix this reports rep.Gap = -1 (reproducing the issue's literal symptom); post-fix reports 0 GAP.
  • go build ./..., go vet ./..., golangci-lint run ./..., go test ./... -cover all clean (verified in a fresh worktree to exclude unrelated pre-existing untracked scratch files in this checkout).

checkSourceCode() refines the Evidence/Gap text for A-2.4 and A-3.1, but
both objectives already come out of Assess() as StatusManual (their
allObjectives entry has an empty evidence file, so they take the
"manually assessed" branch and are already counted in rep.Manual). The
refinement code assumed they had instead been counted as StatusGap, so
it unconditionally decremented rep.Gap (A-2.4) and unconditionally
incremented rep.Manual again (both), double-counting Manual and driving
Gap negative whenever .fusa.json and/or all 4 plan docs were present —
exactly the "-1 GAP" reported in #86, where the JSON renderer (which
derives its counts by walking rep.Objectives) stayed consistent while
the text renderer's rep.Gap/rep.Manual fields did not.

Since obj.Status never actually changes in this function, drop the
counter mutations entirely and only update the Evidence/Gap narration.

Closes #86

Signed-off-by: SoundMatt <matt@jellybaby.com>
Signed-off-by: Matt Jones <47545907+SoundMatt@users.noreply.github.com>
Comment thread do178/do178_test.go
func TestSummaryCounters_MatchObjectives_Issue86(t *testing.T) {
dir := t.TempDir()
// Trigger the A-2.4 refinement: .fusa.json present.
if err := os.WriteFile(filepath.Join(dir, ".fusa.json"), []byte("{}"), 0o644); err != nil {
Comment thread do178/do178_test.go
}
// Trigger the A-3.1 refinement: all 4 plan docs present.
for _, f := range []string{"SAFETY_PLAN.md", "SVP.md", "SCMP.md", "SQAP.md"} {
if err := os.WriteFile(filepath.Join(dir, f), []byte("x"), 0o644); err != nil {
Comment thread do178/do178_test.go
func TestSummary_NoNegativeGap_AllEvidencePresent_Issue86(t *testing.T) {
dir := t.TempDir()
// .fusa.json triggers the A-2.4 refinement.
if err := os.WriteFile(filepath.Join(dir, ".fusa.json"), []byte("{}"), 0o644); err != nil {
Comment thread do178/do178_test.go
// Every file-based-evidence objective applicable at DAL-D, satisfied —
// so the real GAP count is zero.
wfDir := filepath.Join(dir, ".github", "workflows")
if err := os.MkdirAll(wfDir, 0o755); err != nil {
Comment thread do178/do178_test.go
"sas.md": "x",
}
for f, content := range files {
if err := os.WriteFile(filepath.Join(dir, f), []byte(content), 0o644); err != nil {
Comment thread do178/do178_test.go
t.Fatalf("write %s: %v", f, err)
}
}
if err := os.WriteFile(filepath.Join(wfDir, "ci.yml"), []byte("ci"), 0o644); err != nil {
Comment thread do178/do178_test.go

// negativeCount matches a negative integer preceded by a space, e.g. " -1",
// as would appear in a malformed "Summary: ... -1 GAP ..." line.
var negativeCount = regexp.MustCompile(` -\d`)
@SoundMatt
SoundMatt merged commit 7bd40ff into main Jul 29, 2026
16 checks passed
@SoundMatt
SoundMatt deleted the fix/do178-text-summary-negative-gap-86 branch July 29, 2026 16:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

do178: text-mode summary line prints negative GAP count (-1 GAP) on DAL-B report

2 participants