Skip to content

feat: add FileWriter service and refactor waza init inventory #48#63

Merged
github-actions[bot] merged 10 commits into
mainfrom
squad/48-filewriter-init-inventory
Mar 5, 2026
Merged

feat: add FileWriter service and refactor waza init inventory #48#63
github-actions[bot] merged 10 commits into
mainfrom
squad/48-filewriter-init-inventory

Conversation

@wbreza

@wbreza wbreza commented Mar 4, 2026

Copy link
Copy Markdown
Collaborator

Closes #48

What changed

  • Created internal/scaffold/writer.go with FileWriter service
  • Create-if-missing / skip-if-exists with structured inventory
  • Always-visible inventory output (not gated behind --verbose)
  • Refactored cmd_init.go to use FileWriter (replaced ~65 lines of inline loop)
  • Inventory symbols: ➕ created, ✅ exists

Testing

  • 8 new writer tests (create, skip, inventory, directory creation)
  • All 20 existing init tests pass
  • go test ./... green

wbreza and others added 2 commits March 4, 2026 14:31
- Create internal/scaffold/writer.go with FileWriter type that encapsulates
  the create-if-missing + skip-if-exists pattern
- FileWriter returns structured Inventory with per-entry outcomes (created/skipped)
- Inventory.Fprint() renders aligned table with emoji indicators:
  ➕ for created, ✅ (already exists) for skipped
- Refactor cmd/waza/cmd_init.go to use FileWriter instead of inline write loop
- Inventory is always visible (not gated behind --verbose)
- Add 8 tests in writer_test.go covering: create-if-missing, skip-if-exists,
  mixed outcomes, parent directory creation, inventory output, relative paths,
  empty content handling, and CreatedCount

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 4, 2026 22:44
@github-actions github-actions Bot enabled auto-merge (squash) March 4, 2026 22:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a reusable scaffolding writer service to standardize waza init’s create-if-missing / skip-if-exists behavior and to make the “project structure” inventory consistently visible and testable.

Changes:

  • Added internal/scaffold/FileWriter with structured Inventory output (➕ created, ✅ exists).
  • Refactored cmd/waza/cmd_init.go Phase 5 to use FileWriter instead of an inline write loop.
  • Added unit tests covering create/skip behavior, inventory output formatting, and parent directory creation.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
internal/scaffold/writer.go New FileWriter + Inventory implementation for safe, repeatable scaffolding.
internal/scaffold/writer_test.go New tests validating writer behavior and inventory output.
cmd/waza/cmd_init.go Replaces inline scaffolding loop with FileWriter and uses Inventory for summary/output.
.squad/agents/linus/history.md Documents the #48 refactor work for team history.
Comments suppressed due to low confidence (1)

internal/scaffold/writer.go:113

  • In the directory branch, any os.Stat error (including permission errors) falls through to MkdirAll. This can mask the real cause and can attempt a write in a path that exists but is inaccessible. Consider handling stat errors explicitly (skip only when err==nil && info.IsDir(); create only when os.IsNotExist(err); otherwise return an error that wraps the original stat failure).
		if entry.IsDir {
			if info, err := os.Stat(entry.Path); err == nil && info.IsDir() {
				item.Outcome = OutcomeSkipped
			} else {
				if err := os.MkdirAll(entry.Path, 0o755); err != nil {
					return nil, fmt.Errorf("failed to create %s: %w", entry.Path, err)
				}
				item.Outcome = OutcomeCreated
			}

Comment thread internal/scaffold/writer.go Outdated
* feat: refactor waza new to use shared FileWriter #58

Replace the inline write loop in cmd_new.go with the shared FileWriter
from internal/scaffold/writer.go. Malformed SKILL.md detection still runs
before FileWriter — the file is removed so FileWriter creates it fresh.

Inventory now uses consistent ➕/✅ emoji indicators (always visible,
not gated behind --verbose), matching the waza init behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: update squad state for #58

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@wbreza wbreza left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Architectural review: FileWriter extraction + always-visible inventory is the right direction, and the cmd_init complexity reduction is meaningful. Blocking issue: cmd_new.go currently does '_ = os.Remove(...)' in malformed SKILL.md repair paths; if remove fails (permissions/readonly), FileWriter will mark SKILL.md as ✅ skipped and the malformed file is left unrepaired. Fix: handle remove errors explicitly (ignore only IsNotExist, return all other errors) and add a regression test for remove/write error propagation in overwrite-repair flow.

wbreza and others added 5 commits March 4, 2026 15:15
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Directory branch: explicitly handle IsNotExist vs other stat errors
- File branch: detect directory-at-file-path type mismatch
- Both branches: return errors on permission failures instead of masking
- Add regression tests for type-mismatch error paths

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Directory branch: explicitly handle IsNotExist vs other stat errors
- File branch: detect directory-at-file-path type mismatch
- Both branches: return errors on permission failures instead of masking
- Add regression tests for type-mismatch error paths

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 4, 2026 23:17
- Directory branch: explicitly handle IsNotExist vs other stat errors
- Directory branch: error when path exists but is not a directory
- File branch: detect directory-at-file-path type mismatch
- Both branches: return errors on permission failures instead of masking
- Add regression tests for type-mismatch error paths

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@wbreza

wbreza commented Mar 4, 2026

Copy link
Copy Markdown
Collaborator Author

Addressing review feedback (d8d7630):

Rusty, Copilot — both stat-handling issues are fixed in the FileWriter:

Directory branch: Now uses three-way dispatch: \�rr == nil && IsDir()\ → skip, \�rr == nil && !IsDir()\ → error (type mismatch), \os.IsNotExist\ → create, otherwise → return the stat error (catches permission errors).

File branch: Now checks \info.IsDir()\ when stat succeeds (errors if a directory sits where a file should be), and returns stat errors that aren't \IsNotExist\ instead of masking them.

New regression tests: \TestFileWriter_DirEntry_FileExistsAtPath\ and \TestFileWriter_FileEntry_DirExistsAtPath\ cover both type-mismatch paths.

Re: SKILL.md repair in cmd_new.go — agreed that's on the #58 branch scope, not this PR. The FileWriter now properly surfaces all stat/permission errors, so downstream consumers like cmd_new.go will get proper error propagation.

All scaffold tests pass (9/9). — Linus

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (3)

internal/scaffold/writer.go:117

  • For file entries, if the path exists but is a directory, Write() currently marks it as OutcomeSkipped. That leaves the project in a broken state (required file missing) without surfacing an error. Consider checking the file type and returning a clear error when an unexpected directory exists at a file path.
				}
				item.Outcome = OutcomeCreated
			} else {
				return nil, fmt.Errorf("failed to stat %s: %w", entry.Path, err)

cmd/waza/cmd_new_test.go:165

  • Same issue here: "✅" is also present in the summary footer, so this assertion can pass even if no inventory entry was skipped. Prefer asserting the skipped-line suffix ("already exists") and/or the specific file path/label.
	require.NoError(t, err)
	assert.Equal(t, validSkillMD, string(data), "existing SKILL.md should not be overwritten")

	// Output should mention ✅ for skipped file
	assert.Contains(t, buf.String(), "✅")

cmd/waza/cmd_new_test.go:197

  • This assertion checks for "✅", but that string will always appear in the footer (e.g. "✅ Project up to date.") even if the inventory formatting regresses. Consider asserting for the per-item skipped marker plus "(already exists)" to ensure the inventory output is validated.
	output := buf.String()
	assert.Contains(t, output, "✅")
	assert.Contains(t, output, "Project up to date")
}

Comment thread internal/scaffold/writer.go
Comment thread internal/scaffold/writer.go
Comment thread cmd/waza/cmd_new_test.go
Comment thread internal/scaffold/writer.go
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot merged commit e89407b into main Mar 5, 2026
6 checks passed
spboyer pushed a commit to spboyer/waza-fk that referenced this pull request Mar 5, 2026
…ft#48 (microsoft#63)

* feat: add FileWriter service and refactor waza init inventory microsoft#48

- Create internal/scaffold/writer.go with FileWriter type that encapsulates
  the create-if-missing + skip-if-exists pattern
- FileWriter returns structured Inventory with per-entry outcomes (created/skipped)
- Inventory.Fprint() renders aligned table with emoji indicators:
  ➕ for created, ✅ (already exists) for skipped
- Refactor cmd/waza/cmd_init.go to use FileWriter instead of inline write loop
- Inventory is always visible (not gated behind --verbose)
- Add 8 tests in writer_test.go covering: create-if-missing, skip-if-exists,
  mixed outcomes, parent directory creation, inventory output, relative paths,
  empty content handling, and CreatedCount

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* docs: update Linus history with FileWriter work (microsoft#48)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat: refactor waza new to use shared FileWriter microsoft#58 (microsoft#66)

* feat: refactor waza new to use shared FileWriter microsoft#58

Replace the inline write loop in cmd_new.go with the shared FileWriter
from internal/scaffold/writer.go. Malformed SKILL.md detection still runs
before FileWriter — the file is removed so FileWriter creates it fresh.

Inventory now uses consistent ➕/✅ emoji indicators (always visible,
not gated behind --verbose), matching the waza init behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: update squad state for microsoft#58

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: remove .squad/ files from PR branch

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: remove .squad/ files from PR branch

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: improve error handling in FileWriter stat checks microsoft#48

- Directory branch: explicitly handle IsNotExist vs other stat errors
- File branch: detect directory-at-file-path type mismatch
- Both branches: return errors on permission failures instead of masking
- Add regression tests for type-mismatch error paths

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: improve error handling in FileWriter stat checks microsoft#48

- Directory branch: explicitly handle IsNotExist vs other stat errors
- File branch: detect directory-at-file-path type mismatch
- Both branches: return errors on permission failures instead of masking
- Add regression tests for type-mismatch error paths

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: improve error handling in FileWriter stat checks microsoft#48

- Directory branch: explicitly handle IsNotExist vs other stat errors
- Directory branch: error when path exists but is not a directory
- File branch: detect directory-at-file-path type mismatch
- Both branches: return errors on permission failures instead of masking
- Add regression tests for type-mismatch error paths

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: gofmt writer_test.go

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@spboyer spboyer mentioned this pull request Mar 12, 2026
4 tasks
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.

Review init code, make sure we always prompt if data needs to be overwritten

3 participants