Skip to content

SPDD 2026-07-08: close spec sync gaps and enforce add-flow rollback semantics#44358

Merged
pelikhan merged 6 commits into
mainfrom
copilot/spdd-daily-spec-work-plan-2026-07-08
Jul 9, 2026
Merged

SPDD 2026-07-08: close spec sync gaps and enforce add-flow rollback semantics#44358
pelikhan merged 6 commits into
mainfrom
copilot/spdd-daily-spec-work-plan-2026-07-08

Conversation

Copilot AI commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

This PR closes the daily SPDD gaps across safe-outputs, guard-policies, model-alias, and repository-package specs, plus adds missing behavioral coverage where the specs identified implementation uncertainty. It also hardens gh aw add by ensuring same-operation writes are rolled back when a later write fails.

  • Safe Outputs spec sync (P0)

    • Added a new §7 handler cross-reference table mapping safe-output types to exact createHandlers() functions in safe_outputs_handlers.cjs.
    • Added §8.3 Sync Notes mappings for MCE1–MCE5 to concrete MCP wiring and validation call sites (safe_outputs_mcp_server.cjs, safe_outputs_tools_loader.cjs, safe_outputs_handlers.cjs).
    • Replaced stale “[Open]” sync follow-ups with a maintenance note to keep mappings current.
  • Guard policy cross-field consistency (GP-11)

    • Added explicit test coverage that allowed-repos: "public" without min-integrity fails validation.
    • Updated scratchpad sync mapping to reference this GP-11 coverage.
    • Replaced the deprecation note’s “tentative v2.0.0” with a concrete tracking reference.
  • Model Alias spec completeness

    • Added normative §16 Operations with caller-facing lifecycle steps (invoke, guard init, recursive resolution, depth enforcement, deterministic result/error).
    • Updated TOC and refreshed §14 “Last verified” date.
  • Repository package manifest spec clarity

    • Clarified §5.3 removal scope: package-installed README.md is removed under R-PKG-R001, with modified-file behavior governed by R-PKG-R002.
    • Added §12 sync mapping entry for R-PKG-003 rollback coverage tied to add-flow behavior.
  • Add flow rollback behavior (R-PKG-003 alignment)

    • addWorkflowsWithTracking() now rolls back files written in the current operation when a subsequent workflow write fails, and returns joined error context if rollback also fails.
    • Added targeted test coverage to assert prior successful writes are removed when a later write fails.
if err := addWorkflowWithTracking(...); err != nil {
    if tracker != nil {
        if rollbackErr := tracker.RollbackAllFiles(opts.Verbose); rollbackErr != nil {
            return fmt.Errorf("failed to add workflow '%s' (rollback also failed): %w",
                resolved.Spec.String(), errors.Join(err, rollbackErr))
        }
    }
    return fmt.Errorf("failed to add workflow '%s': %w", resolved.Spec.String(), err)
}

Copilot AI linked an issue Jul 8, 2026 that may be closed by this pull request
10 tasks
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Resolve implementation gaps in daily spec work plan SPDD 2026-07-08: close spec sync gaps and enforce add-flow rollback semantics Jul 8, 2026
Copilot AI requested a review from pelikhan July 8, 2026 17:02
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

🤖 PR Triage

Field Value
Category chore
Risk 🟡 medium
Score 34/100 (impact:18 urgency:8 quality:8)
Action ⏸ defer

Status: DRAFT — needs undraft + CI before review.

Daily SPDD spec sync: closes gaps in safe-outputs, guard-policies, model-alias, and repository-package specs. Also hardens gh aw add rollback for same-operation write failures. Mixed scope (spec docs + behavior).

Run §28966928999

Generated by 🔧 PR Triage Agent · 106.9 AIC · ⌖ 10.9 AIC · ⊞ 5.4K ·

@pelikhan pelikhan marked this pull request as ready for review July 9, 2026 01:07
Copilot AI review requested due to automatic review settings July 9, 2026 01:07
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

🤖 PR Triage

Field Value
Category chore (spec sync)
Risk 🟡 Medium
Priority Score 45 / 100
Action defer

Score breakdown: Impact 20 + Urgency 15 + Quality 10

Rationale: DRAFT. SPDD spec-sync work — closes spec gaps and enforces add-flow rollback semantics. Useful maintenance but no user-visible urgency. Defer until undraft + CI.

Run §28986426320

Generated by 🔧 PR Triage Agent · 74.6 AIC · ⌖ 8.8 AIC · ⊞ 5.4K ·

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR #44358 does not have the 'implementation' label and has only 55 new lines of code in business logic directories (threshold: 100).

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions github-actions Bot mentioned this pull request Jul 9, 2026

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

Updates several project specifications to close SPDD sync gaps and hardens gh aw add behavior by rolling back files written in the same add operation when a later workflow write fails.

Changes:

  • Synced multiple specs (safe-outputs, guard-policies scratchpad, model-alias, repository-package manifest) with explicit implementation cross-references and refreshed verification dates.
  • Added guard-policy validation coverage for allowed-repos: "public" without min-integrity.
  • Implemented and tested rollback semantics in addWorkflowsWithTracking() when a later workflow write fails.
Show a summary per file
File Description
scratchpad/guard-policies-specification.md Updates deprecation tracking reference and adds GP-11 implementation/test mapping entry.
pkg/workflow/tools_validation_test.go Adds regression test for allowed-repos: public requiring min-integrity.
pkg/cli/add_command.go Adds rollback-on-failure behavior during multi-workflow add operations.
pkg/cli/add_command_test.go Adds test asserting earlier successful writes are removed when a later write fails.
docs/src/content/docs/specs/safe-outputs-specification.md Adds handler-function cross-reference table and expands §8.3 sync mappings for MCE1–MCE5.
docs/src/content/docs/specs/repository-package-manifest-specification.md Clarifies removal scope for package README.md and maps R-PKG-003 rollback coverage to implementation/tests.
docs/src/content/docs/specs/model-alias-specification.md Adds normative “Operations” lifecycle section and updates TOC / last-verified date.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 7/7 changed files
  • Comments generated: 3
  • Review effort level: Low

Comment thread docs/src/content/docs/specs/safe-outputs-specification.md
Comment on lines +2030 to +2037
| `update_release` | `defaultHandler("update_release")` |
| `upload_asset` | `uploadAssetHandler` |
| `dispatch_workflow` | `defaultHandler("dispatch_workflow")` |
| `create_code_scanning_alert` | `defaultHandler("create_code_scanning_alert")` |
| `autofix_code_scanning_alert` | `defaultHandler("autofix_code_scanning_alert")` |
| `create_check_run` | `defaultHandler("create_check_run")` |
| `create_agent_session` | `defaultHandler("create_agent_session")` |
| `missing_tool` | `defaultHandler("missing_tool")` |
Comment thread pkg/cli/add_command.go
Comment on lines 307 to 313
if err := addWorkflowWithTracking(ctx, resolved, tracker, opts); err != nil {
if tracker != nil {
if rollbackErr := tracker.RollbackAllFiles(opts.Verbose); rollbackErr != nil {
return fmt.Errorf("failed to add workflow '%s' (rollback also failed): %w", resolved.Spec.String(), errors.Join(err, rollbackErr))
}
}
return fmt.Errorf("failed to add workflow '%s': %w", resolved.Spec.String(), err)
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 90/100 — Excellent

Analyzed 2 test(s): 2 design, 0 implementation, 0 violation(s).

📊 Metrics (2 tests)
Metric Value
Analyzed 2 (Go: 2, JS: 0)
✅ Design 2 (100%)
⚠️ Implementation 0 (0%)
Edge/error coverage 2 (100%)
Duplicate clusters 0
Inflation Yes (add_command_test.go: 8:1 — contextually justified)
🚨 Violations 0
Test File Classification Issues
TestAddWorkflowsWithTracking_RollsBackWrittenFilesOnWriteFailure pkg/cli/add_command_test.go:623 behavioral_contract / high_value / design_test Inflation 8:1 (40 test lines vs 5 prod lines — justified for rollback scenario)
TestValidateGitHubGuardPolicy"allowed-repos non-all without min-integrity fails" pkg/workflow/tools_validation_test.go:481 behavioral_contract / high_value / design_test None

Verdict

Passed. 0% implementation tests (threshold: 30%). Both tests enforce behavioral contracts: the first verifies add-flow rollback atomicity; the second closes a spec sync gap requiring min-integrity for non-all allowed-repos values. Build tags present on both files, no mock library usage.

Score formula: (2/2 × 40) + (2/2 × 30) + (20 − 0) + 0 [inflation penalty] = 90

References:

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • awmgmcpg

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

🧪 Test quality analysis by Test Quality Sentinel · 42 AIC · ⌖ 7.7 AIC · ⊞ 6.8K ·
Comment /review to run again

@github-actions github-actions Bot 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.

✅ Test Quality Sentinel: 90/100. 0% implementation tests (threshold: 30%). Both new tests enforce behavioral contracts with edge-case coverage and no guideline violations.

@github-actions github-actions Bot 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.

Skills-Based Review 🧠

Applied /diagnosing-bugs, /tdd, and /grill-with-docs — requesting changes on one correctness issue and three quality gaps.

📋 Key Themes & Highlights

Key Themes

  • Double-rollback hazard (correctness): The new inner rollback in addWorkflowsWithTracking and the existing outer rollback in add_workflow_pr.go operate on the same FileTracker instance. FileTracker does not clear its lists after rollback, so the outer call redundantly re-writes original content. This is the highest-priority finding.
  • Rollback-failure test gap: The test verifies the happy-path rollback (file is gone) but has no coverage for partial-rollback failure, and the errors.Join wrapping in the rollback-failure branch may not behave as expected for callers using errors.Is.
  • Implicit test fixture: The rollback test relies on an undocumented implementation detail (nested dir not existing) to trigger the write failure; a future auto-mkdir change would silently break the test contract.
  • Spec: normative prose without IDs: The new README-removal paragraph in the repository-package spec adds normative behaviour without a requirement ID, making it un-referenceable from the norms table.
  • Spec: §16 placement and cycle-check wording: The Operations section is appended after the norms/appendices rather than in the main body, and the cycle-detection step has ambiguous wording that could mislead implementors.

Positive Highlights

  • ✅ Rollback semantics are now aligned with R-PKG-003 — a meaningful hardening of gh aw add.
  • ✅ The §7.0.2 handler cross-reference table is a high-value maintenance aid.
  • ✅ GP-11 test coverage closes a real spec-to-code sync gap cleanly.
  • ✅ Replacing stale "[Open]" tasks with a concrete maintenance note reduces future drift.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 102.6 AIC · ⌖ 6.46 AIC · ⊞ 6.6K
Comment /matt to run again

Comment thread pkg/cli/add_command.go

if err := addWorkflowWithTracking(ctx, resolved, tracker, opts); err != nil {
if tracker != nil {
if rollbackErr := tracker.RollbackAllFiles(opts.Verbose); rollbackErr != nil {

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.

[/diagnosing-bugs] Double-rollback risk: addWorkflowsWithTracking now calls tracker.RollbackAllFiles internally, but add_workflow_pr.go (line 93) also calls tracker.RollbackAllFiles on the same tracker when the error returns. Because FileTracker does not clear its lists after rollback, RollbackModifiedFiles re-writes the original content a second time — harmless today but semantically broken and fragile if tracker state is checked post-rollback.

💡 Suggested fix

Clear the tracker lists after a successful rollback so a second call is a no-op:

// After rollback logic in RollbackAllFiles:
ft.CreatedFiles = nil
ft.ModifiedFiles = nil
ft.OriginalContent = nil

Alternatively, guard the outer call in add_workflow_pr.go with a comment noting the inner rollback has already been attempted.

@copilot please address this.

Comment thread pkg/cli/add_command.go
return fmt.Errorf("failed to add workflow '%s' (rollback also failed): %w", resolved.Spec.String(), errors.Join(err, rollbackErr))
}
}
return fmt.Errorf("failed to add workflow '%s': %w", resolved.Spec.String(), err)

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.

[/tdd] Rollback success is not asserted — the test only checks that ok.md does not exist after the error, but it doesn't cover the case where rollback itself fails (partial rollback). The error message for rollback failure uses errors.Join(err, rollbackErr) with %w, which wraps only the first argument under Go's errors.Join semantics; the caller can only errors.Is the original write error, not the rollback error.

💡 Suggested additions
  1. Add a test case that makes rollback fail (e.g. by making the already-written file read-only) and asserts the combined error message contains both the write error and rollback error strings.
  2. Consider returning a dedicated type that carries both errors distinctly, or at minimum confirm the wrapping is intentional so errors.Is(err, writeErr) works as expected.

@copilot please address this.

Quiet: true,
})
require.Error(t, err)
assert.Contains(t, err.Error(), "failed to write destination file")

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.

[/tdd] The test relies on WorkflowName: "nested/blocked" to trigger a write failure, but the failure mechanism isn't explicit — it's relying on an implementation detail (presumably the nested sub-directory not existing causes the write to fail). This makes the test fragile and unclear: if the implementation starts auto-creating parent directories, the test silently stops exercising rollback.

💡 Suggested improvement

Make the failure condition explicit in the test name and comment, or introduce a test-friendly injection point (e.g., a mock writer) so the cause of failure is decoupled from directory creation behaviour:

// WorkflowName "nested/blocked" causes a write failure because the parent
// directory "nested/" does not exist under workflowsDir and is not created
// by the implementation. If that behaviour changes, this test must be updated.

@copilot please address this.


**R-PKG-R004**: After removal, if the target workflow directory is empty, the implementation MAY remove the empty directory. The implementation MUST NOT remove non-empty directories.

For R-PKG-R001 scope, package-installed documentation files are treated the same as workflow files: a `README.md` installed as part of the package MUST be removed during package removal unless it has been modified, in which case R-PKG-R002 applies.

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.

[/grill-with-docs] The new prose for R-PKG-R001 scope uses the term README.md inline without a requirement ID, making it normative prose that can't be directly referenced in the norms table or sync mapping. The new §12 sync row already references R-PKG-003 (install rollback) but the README-removal behaviour this paragraph describes maps to the existing R-PKG-R001/R002 requirements — the paragraph should reference those explicitly rather than restating them.

💡 Suggestion

Replace the free-form paragraph with a cross-reference:

> **Note**: Documentation files installed by the package (e.g., `README.md`) are within
> the scope of R-PKG-R001 and follow the same modified-file protection as R-PKG-R002.

This keeps the statement tied to existing requirement IDs so tooling and the norms table stay consistent.

@copilot please address this.

2. **Initialize Guards**: The implementation MUST initialize a depth counter (starting at 0) and a per-call visited-set before traversing alias references.
3. **Resolve Recursively**: For each alias hop, the implementation MUST increment depth, merge parameters per §8.3, and reject cycles when the next alias key already exists in the visited-set.
4. **Enforce Limits**: The implementation MUST fail with a resolution error when depth exceeds the safeguard ceiling defined by R-MAF-S001.
5. **Return Deterministically**: The implementation MUST return the first conforming concrete model selected by §8 ordering rules, or a descriptive error when no candidate can be resolved.

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.

[/grill-with-docs] The new §16 Operations section is positioned after §15 Norms (the normative reference table) but references requirement IDs defined in §13 (e.g., R-MAF-S001). Placing the Operations section after the norms table and appendices is unusual — readers expect lifecycle/operational steps to come before the reference tables, not after. Also, step 3 says "reject cycles when the next alias key already exists in the visited-set" but the condition should be the current key, not the next key — misreading could cause implementors to skip the last-hop cycle check.

💡 Suggestions
  1. Move §16 Operations to sit between §13 Safeguards and §14 Sync Notes to match the natural reading flow.
  2. Clarify step 3: "reject cycles when the current alias key already exists in the visited-set (before adding it)".

@copilot please address this.

@github-actions github-actions Bot 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.

Review: SPDD 2026-07-08 spec sync + add-flow rollback

Overall: The spec sync work (safe-outputs handler cross-reference table, MCE1–MCE5 wiring notes, model-alias §16 Operations, repository-package README clarification, guard-policy GP-11 test) is well-structured and high-signal. Two blocking issues in the Go change need addressing before merge.

Blocking issues

  1. Double-rollback hazardaddWorkflowsWithTracking now rolls back internally on write failure, but all callers in add_workflow_pr.go (lines 93, 102, 171) also call tracker.RollbackAllFiles() on the error return. The inner rollback deletes the files first, so the outer one is dead code. This creates ambiguous ownership of rollback semantics and risks confusing future callers. Rollback ownership must be consolidated to one side. See inline comment on pkg/cli/add_command.go:310.

  2. Fragile test failure conditionTestAddWorkflowsWithTracking_RollsBackWrittenFilesOnWriteFailure triggers failure via an undocumented side-effect: WorkflowName: "nested/blocked" writes to a subdirectory that doesn't exist, so os.WriteFile fails with a path error. If nested workflow auto-mkdir is ever added (a natural improvement), the test silently passes without testing rollback. The failure mechanism should be made explicit. See inline comment on pkg/cli/add_command_test.go:659.

Non-blocking observations
  • The safe-outputs §7.0.2 handler cross-reference table is a welcome addition, but close_check_run and push_to_pull_request_branch both use non-trivial handler functions — double-check these are correctly mapped.
  • Model-alias §16 Operations steps 3–5 accurately describe the existing implementation. No issues.
  • Guard policy GP-11 test coverage correctly matches the allowed-repos non-null without min-integrity validation path in tools_validation_github.go line 131–134.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · 107.4 AIC · ⌖ 6.58 AIC · ⊞ 4.8K

Comment thread pkg/cli/add_command.go
if err := addWorkflowWithTracking(ctx, resolved, tracker, opts); err != nil {
if tracker != nil {
if rollbackErr := tracker.RollbackAllFiles(opts.Verbose); rollbackErr != nil {
return fmt.Errorf("failed to add workflow '%s' (rollback also failed): %w", resolved.Spec.String(), errors.Join(err, rollbackErr))

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.

Double-rollback hazard introduced by this change.

After this PR, addWorkflowsWithTracking rolls back internally on failure (lines 308-312). But every existing caller that already calls tracker.RollbackAllFiles() on error — e.g., add_workflow_pr.go line 93 — will now trigger a second rollback on the same tracker after addWorkflowsWithTracking returns the error.

The second rollback silently no-ops (files already deleted), but it is misleading. Rollback ownership should be consistent: either the callers own rollback (revert the internal rollback added here), or addWorkflowsWithTracking owns it (remove the external RollbackAllFiles calls in callers).

@copilot please address this.

require.Error(t, err)
assert.Contains(t, err.Error(), "failed to write destination file")

_, statErr := os.Stat(filepath.Join(workflowsDir, "ok.md"))

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.

Test relies on an undocumented failure condition that may be fragile.

The test expects WorkflowName: "nested/blocked" to fail with "failed to write destination file". Looking at the code, this fails because nested/blocked implies a subdirectory (.github/workflows/nested/) that does not exist in the test setup, so os.WriteFile fails.

This is an implicit coupling: the test passes only because os.MkdirAll is not called for the workflow subdirectory before writing. If future refactoring adds auto-mkdir for nested workflow names (which is a natural improvement), this test silently stops covering rollback.

Recommend: make the failure condition explicit — e.g., create a read-only file at the target path or use a stub writer — rather than relying on filesystem path creation failure.

@copilot please address this.

@github-actions github-actions Bot 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.

Two issues need addressing before merge — one is a maintenance contract hazard in the rollback implementation, one is a fragile test.

Review themes

Rollback ownership is split across two callers (high)

The new rollback code inside addWorkflowsWithTracking shares a FileTracker with add_workflow_pr.go, which already calls tracker.RollbackAllFiles on the same error path. This creates a double-rollback that is accidentally safe today but violates clear ownership of the tracker's lifecycle. See inline comment on add_command.go.

Fragile test failure mechanism (medium)

The test for rollback relies on nested/blocked triggering a write failure through a missing directory — this is correct but undocumented, and will silently stop exercising rollback if the implementation ever adds MkdirAll for workflow writes (as it already does for skill/agent files). See inline comment on add_command_test.go.

Rest of the diff is solid

The spec updates (safe-outputs §7.0.2 handler table, §8.3 MCE mappings, model-alias §16 operations, pkg-manifest §5.3 README clarification) are accurate, well-scoped, and improve traceability. Guard policy test coverage for GP-11 correctly targets the allowed-repos non-all without min-integrity code path.

🔎 Code quality review by PR Code Quality Reviewer · 138.3 AIC · ⌖ 5.48 AIC · ⊞ 5.4K
Comment /review to run again

Comment thread pkg/cli/add_command.go

if err := addWorkflowWithTracking(ctx, resolved, tracker, opts); err != nil {
if tracker != nil {
if rollbackErr := tracker.RollbackAllFiles(opts.Verbose); rollbackErr != nil {

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.

Double rollback when caller already runs RollbackAllFiles on the same tracker: add_workflow_pr.go calls tracker.RollbackAllFiles at line 93 after addWorkflowsWithTracking returns an error — which now also calls RollbackAllFiles internally. The second invocation re-runs on an already-rolled-back tracker.

💡 Impact and suggested fix

Today this is safe by coincidence:

  • RollbackCreatedFiles ignores os.IsNotExist, so re-deleting an already-deleted file is a no-op.
  • RollbackModifiedFiles re-writes original content that was already restored, so the result is the same.

But the invariant is broken: the tracker is never cleared after a rollback, and its state looks identical to a pre-rollback state. Any future change to RollbackAllFiles or RollbackModifiedFiles that adds state-dependent logic (e.g., "only restore files not already restored", or clearing the lists post-rollback) will silently change behaviour for the outer caller in add_workflow_pr.go.

Option A (preferred): Move rollback responsibility entirely into addWorkflowsWithTracking and remove the redundant outer call in add_workflow_pr.go line 93.

Option B: After a successful rollback inside addWorkflowsWithTracking, clear the tracker lists so a second call becomes a true no-op:

The PR as written silently relies on two callers sharing a tracker to both call rollback without coordination — that's a maintenance hazard.

},
{
Spec: &WorkflowSpec{
WorkflowPath: "workflows/blocked.md",

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.

Test relies on an implicit, undocumented failure mechanism that will silently stop exercising rollback if the implementation changes: WorkflowName: "nested/blocked" triggers a write failure only because no nested/ subdirectory exists — nothing in the test makes this intention visible.

💡 Why this matters and how to harden it

The test relies on os.WriteFile failing because nested/ does not exist and is not created by addWorkflowWithTracking. This is a valid failure path today, but:

  1. add_command.go already calls os.MkdirAll for skill files (line 736) and agent files (line 809). If a future maintainer adds the same os.MkdirAll for standard workflow writes (a plausible extension for nested workflow organization), the failure condition silently disappears and the rollback path is no longer tested. require.Error would catch this — but only because the test as a whole fails, not because the intent is clear.

  2. The test only asserts ok.md was rolled back. It does not assert that blocked.md (the failing write) left no partial state. While os.WriteFile is atomic enough that partial writes are rare, asserting the negative makes the test complete.

Add a comment and a second assertion:

// WorkflowName "nested/blocked" intentionally causes a write failure because the
// "nested/" subdirectory does not exist and addWorkflowWithTracking does not create
// subdirectories for workflow names. This exercises the rollback path.

_, statErr := os.Stat(filepath.Join(workflowsDir, "ok.md"))
assert.True(t, os.IsNotExist(statErr), "successful writes must be rolled back on later write failure")

// blocked.md should also not exist (parent dir never created)
_, blockedErr := os.Stat(filepath.Join(workflowsDir, "nested", "blocked.md"))
assert.True(t, os.IsNotExist(blockedErr), "failing workflow must not leave partial state")

@pelikhan

pelikhan commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

Copilot AI and others added 4 commits July 9, 2026 01:46
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

@copilot run pr-finisher skill

Ran pr-finisher and pushed follow-up fixes. Latest commit: 7e8eac5 (plus prior related commits in this run). Local make fmt, make lint, make test-unit, make test, and make agent-report-progress all pass. CI on this head remains unverified until a maintainer re-triggers workflows.

@pelikhan pelikhan merged commit afedbd9 into main Jul 9, 2026
29 checks passed
@pelikhan pelikhan deleted the copilot/spdd-daily-spec-work-plan-2026-07-08 branch July 9, 2026 02:15
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.82.7

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[spdd] Daily spec work plan - 2026-07-08

3 participants