- commit
- 9ca32bf67216492853ffb92af28fc71555b38608
- parent
- 87fede3fcfa50c87cf2e0709e80ae89d186f8c14
- author
- Dami <dami.etoile@gmail.com>
| M | internal/ | 3 | --- |
| M | internal/ | 9 | +++++++-- |
| M | internal/ | 3 | --- |
| M | internal/ | 11 | ----------- |
@@ -221,9 +221,6 @@ func TestDiffNilAndIdentical(t *testing.T) {
221221 // Re-using a change name after an earlier archive is allowed by the CLI222222 // (`new change` only checks changes/<name>, and archive dirs differ by date).223223 func TestDiffArchiveWithReusedName(t *testing.T) {224- t.Log("was BUG: diff.go:55-57 archivedNames maps Name -> ID and is filled from next.Archive (newest-first), so " +225- "an older archive with the same Name overwrites the new one; the check at diff.go:74 then finds the OLD " +226- "ID in prevArchive and reports EventRemoved \"change removed\" instead of EventArchived")227224 root := copyFixture(t)228225 changes := filepath.Join(root, "openspec", "changes")229226 touch(t, filepath.Join(changes, "archive", "2026-08-01-add-due-dates", "proposal.md"), "## Why\nFirst attempt.\n")@@ -15,11 +15,16 @@ type stamp struct {
1515 // used to poll for changes without holding a file descriptor per watched file.1616 type Fingerprint map[string]stamp1717 18-// Scan stats every file under dir. A missing dir yields an empty fingerprint.18+// Scan stats every file and directory under dir, so a new empty change19+// directory is noticed too. A missing dir yields an empty fingerprint.1920 func Scan(dir string) Fingerprint {2021 fp := Fingerprint{}2122 _ = filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {22- if err != nil || d.IsDir() {23+ if err != nil {24+ return nil25+ }26+ if d.IsDir() {27+ fp[path] = stamp{} // presence only; dir mtimes churn on temp files2328 return nil2429 }2530 fi, err := d.Info()@@ -337,9 +337,6 @@ func TestLoadSkipsDotDirsAndArchiveWithoutDatePrefix(t *testing.T) {
337337 // parseDeltaSpecs iterates directories under specs/). Verified:338338 // `openspec show <change> --json` reports deltaCount 0 for this layout.339339 func TestLoadDeltaNeedsCapabilityDir(t *testing.T) {340- t.Log("was BUG: load.go:217 classify treats \"specs/spec.md\" as KindDelta because it has the \"specs/\" prefix " +341- "and \"/spec.md\" suffix; the artifact gets Capability \"spec.md\" (label \"Δ spec.md\") and its " +342- "requirements are counted in Deltas, while the CLI ignores files without a capability dir")343340 root := t.TempDir()344341 writeFile(t, filepath.Join(root, "openspec", "changes", "loose", "specs", "spec.md"),345342 "## ADDED Requirements\n### Requirement: Loose\nThe system SHALL x.\n")@@ -180,8 +180,6 @@ func TestParseDelta(t *testing.T) {
180180 // require the "### Requirement:" part. Verified: `openspec show <change>181181 // --json` reports deltaCount 0 for this input.182182 func TestParseDeltaRenamedNeedsFromToPair(t *testing.T) {183- t.Log("was BUG: parse.go:18 renameFromRe `(?i)^\\s*-?\\s*FROM:` counts every FROM-ish line under RENAMED " +184- "(orphan FROM without TO, prose like \"- From: ...\"), so ParseDelta reports Renamed=2 where the CLI reports 0")185183 src := md("## RENAMED Requirements", "",186184 "- FROM: `### Requirement: Password login`",187185 "- From: the old wording was ambiguous")@@ -194,9 +192,6 @@ func TestParseDeltaRenamedNeedsFromToPair(t *testing.T) {
194192 // fence with the same marker character and at least the same length, so a195193 // ``` block nested inside a ~~~ (or ````) block stays masked.196194 func TestParseNestedFenceStaysMasked(t *testing.T) {197- t.Log("was BUG: parse.go:29 lines() toggles inFence on ANY ``` or ~~~ line, so an inner ``` inside a ~~~ " +198- "(or ````) block un-masks the example; ParseSpec/ParseDelta count the fenced \"### Requirement:\" " +199- "(spec: 2 vs CLI `spec list` requirementCount 1)")200195 example := []string{"~~~markdown", "```", "### Requirement: Fake inside nested fence", "#### Scenario: fake", "```", "~~~"}201196 spec := md(append([]string{"# S", "## Purpose", "p", "## Requirements", "### Requirement: Real", "The system SHALL x.", "#### Scenario: s"}, example...)...)202197 if got := ParseSpec(spec); got.Requirements != 1 || got.Scenarios != 1 {@@ -294,8 +289,6 @@ func TestParseSpec(t *testing.T) {
294289 // later "## Notes" section is a validation ERROR and is not counted.295290 // Verified: `openspec spec list --json` reports requirementCount 1 here.296291 func TestParseSpecIgnoresRequirementsOutsideRequirementsSection(t *testing.T) {297- t.Log("was BUG: parse.go:108 ParseSpec counts every \"### Requirement:\" header in the file regardless of section, " +298- "so a stray header under \"## Notes\" gives 2 where `openspec spec list` reports 1")299292 src := md("# T", "## Purpose", "p", "## Requirements", "### Requirement: Real", "The system SHALL x.",300293 "## Notes", "### Requirement: Stray", "The system SHALL not count this.")301294 if got := ParseSpec(src); got.Requirements != 1 {@@ -339,8 +332,6 @@ func TestParseSummary(t *testing.T) {
339332 }340333 341334 func TestParseSummaryKeepsLeadingBold(t *testing.T) {342- t.Log("was BUG: parse.go:135 strings.TrimLeft(text, \"-*> \") strips a cutset, not a bullet prefix, so a Why line " +343- "starting with bold markdown loses its opening ** and becomes \"Security:** sessions last too long.\"")344335 src := md("## Why", "", "**Security:** sessions last too long.")345336 if got, want := ParseSummary(src), "**Security:** sessions last too long."; got != want {346337 t.Errorf("ParseSummary() = %q, want %q", got, want)@@ -375,8 +366,6 @@ func TestParseSchema(t *testing.T) {
375366 }376367 377368 func TestParseSchemaStripsInlineComment(t *testing.T) {378- t.Log("was BUG: parse.go:153 ParseSchema does not strip YAML inline comments, so " +379- "\"schema: spec-driven # default\" yields \"spec-driven # default\" (the CLI's YAML parser yields \"spec-driven\")")380369 if got := ParseSchema("schema: spec-driven # default\n"); got != "spec-driven" {381370 t.Errorf("ParseSchema() = %q, want %q", got, "spec-driven")382371 }