Skip to content

Add experimental schema-aware gh aw edit - #55475

Merged
pelikhan merged 9 commits into
mainfrom
copilot/proposal-add-gh-aw-edit-command
Aug 24, 2026
Merged

Add experimental schema-aware gh aw edit#55475
pelikhan merged 9 commits into
mainfrom
copilot/proposal-add-gh-aw-edit-command

Conversation

Copilot AI commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Adds an experimental control-plane command for safely changing workflow frontmatter without manually editing Markdown. It supports typed mutations, schema validation, recompilation, and source-managed workflow protection.

  • Command surface

    • Adds gh aw edit <workflow> [path: value]
    • Supports --set, --unset, --add, --remove, --schedule, and --add-import
    • Resolves workflow IDs, .md filenames, and paths consistently with existing commands
  • Workflow-safe edits

    • Validates changed frontmatter against the workflow schema before writing
    • Recompiles the corresponding .lock.yml
    • Restores source and generated output when compilation fails
    • Rejects workflows managed through source:
  • Schedules and imports

    • Supports compact schedules such as 1h, 3h, 1d, 1w, and weekdays
    • Preserves existing triggers when adding or removing on.schedule
    • Adds imports to both list and imports.aw object forms
gh aw edit repo-assist "max-turns: 20"
gh aw edit repo-assist --schedule weekdays
gh aw edit repo-assist --add-import shared/common.md
gh aw edit repo-assist --unset max-ai-credits

Copilot AI and others added 3 commits August 24, 2026 19:05
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 changed the title [WIP] Add gh aw edit for schema-aware workflow configuration changes Add experimental schema-aware gh aw edit Aug 24, 2026
Copilot AI requested a review from pelikhan August 24, 2026 19:11

@pelikhan pelikhan left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@copilot add specialize commands for --add-skill / --remove-skill

Comment thread pkg/cli/edit_command.go
Comment thread pkg/cli/edit_command.go Outdated
Comment thread pkg/cli/edit_command.go Outdated
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

@copilot add specialize commands for --add-skill / --remove-skill

Added --add-skill and --remove-skill in 7fd2c0a.

Copilot AI requested a review from pelikhan August 24, 2026 19:29
@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot add extensive exhaustive integration workflow tests of the cli command

@pelikhan
pelikhan marked this pull request as ready for review August 24, 2026 20:12
Copilot AI balanced review requested due to automatic review settings August 24, 2026 20:12
@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Ponytail Reviewer completed successfully!

Generated by Ponytail Reviewer for #55475

@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

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

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer

@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

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

🏗️ ADR gate enforced by Design Decision Gate 🏗️

@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

Test Quality Sentinel skipped because pre-fetch PR data was unavailable: unable to fetch test file diff

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

Copy link
Copy Markdown
Contributor

Comment Memory

reviewed_at: 2026-08-24T00:00:00Z
review_event: REQUEST_CHANGES
top_themes:
  - on.schedule removal rewrites trigger shape and creates unrelated diffs
  - grumpy-coder unavailable; review based on direct analysis only
files_reviewed:
  - cmd/gh-aw/main.go
  - pkg/cli/edit_command.go
  - pkg/cli/edit_command_test.go
comment_count: 1

Note

This comment is managed by comment memory.

It stores persistent context for this thread in the code block at the top of this comment.
Edit only the text inside the backtick fences; workflow metadata and the footer are regenerated automatically.

Learn more about comment memory

🔎 Code quality review by PR Code Quality Reviewer · pi · gpt54 · 6.2 AIC · ⌖ 8.09 AIC · ⊞ 7K ·
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.

Request changes

The new edit command still mutates workflow frontmatter in at least one case where the user asked for a semantic no-op: removing on.schedule from a workflow that originally used the short on: syntax rewrites the trigger block into a different shape instead of preserving the original representation.

Blocking theme
  • unset on.schedule leaves on expanded as a map, so workflows can get unrelated source churn from an edit that should only remove scheduling.
  • I ignored the grumpy-coder pass because the sub-agent binary was unavailable in this environment.

🔎 Code quality review by PR Code Quality Reviewer · pi · gpt54 · 6.2 AIC · ⌖ 8.09 AIC · ⊞ 7K
Comment /review to run again

Comment thread pkg/cli/edit_command.go
switch change.kind {
case "set":
parent[key] = change.value
case "unset":

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.

Removing on.schedule only deletes the nested key and never collapses on back to its scalar/list form, so a no-op edit can still rewrite untouched triggers into a different YAML shape and create noisy diffs that are unrelated to the requested change.

💡 Why this is risky and how to fix it

editChangeParent eagerly normalizes on: workflow_dispatch and on: [push, workflow_dispatch] into a map so unset on.schedule can operate on parent["schedule"]. After delete(parent, key), nothing converts that synthetic map back to the original scalar/list representation when no structured keys remain.

That means gh aw edit ... --schedule off mutates:

on: workflow_dispatch

into:

on:
  workflow_dispatch:

which is semantically different formatting for an unchanged trigger set and will cause unnecessary churn in source control. Add a post-edit cleanup step for on that collapses map entries with only nil-valued trigger keys back into the original short form.

@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 small simplifications look worth making here:

  • pkg/cli/edit_command.go:177: shrink: YAML-backed assignment parser for simple path=value input. A tiny string/number/bool parser would cut most of the complexity.
  • pkg/cli/edit_command.go:190: yagni: generic frontmatter edit engine with six helper layers for one CLI. Inline the few supported mutations until a second caller needs it.
    net: -90 lines possible.

Generated by ✂️ Ponytail Reviewer for #55475 · codex · mai10 · 5.69 AIC · ⌖ 0.979 AIC · ⊞ 18.3K
Comment /ponytail to run again

Comment thread pkg/cli/edit_command.go
return editChange{kind: "set", path: "on.schedule", value: []any{map[string]any{"cron": cron}}}, nil
}

func parseEditAssignment(assignment, separator string) (editChange, error) {

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.

pkg/cli/edit_command.go:177: shrink: YAML-backed assignment parser for simple path=value input. A tiny string/number/bool parser would cut most of the complexity.

Comment thread pkg/cli/edit_command.go Outdated
return editChange{kind: "set", path: path, value: value["value"]}, nil
}

func applyEditChange(frontmatter map[string]any, change editChange) error {

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.

pkg/cli/edit_command.go:190: yagni: generic frontmatter edit engine with six helper layers for one CLI. Inline the few supported mutations until a second caller needs it.

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

Adds an experimental gh aw edit command for schema-aware workflow frontmatter mutations and recompilation.

Changes:

  • Adds typed edit, schedule, import, and skill operations.
  • Adds validation, dry-run, recompilation, and rollback handling.
  • Registers the command and adds CLI tests.
Show a summary per file
File Description
pkg/cli/edit_command.go Implements workflow editing and recompilation.
pkg/cli/edit_command_test.go Tests core edit behavior.
cmd/gh-aw/main.go Registers the new command.

Review details

  • Files reviewed: 3/3 changed files
  • Comments generated: 5
  • Review effort level: Balanced

Comment thread pkg/cli/edit_command.go Outdated
if strings.EqualFold(schedule, "off") {
return editChange{kind: "unset", path: "on.schedule"}, nil
}
cron, _, err := parser.ParseSchedule(schedule)
Comment thread pkg/cli/edit_command.go Outdated
Comment on lines +231 to +232
case string:
child = map[string]any{triggers: nil}
Comment thread pkg/cli/edit_command.go
Comment on lines +286 to +287
func replaceFrontmatter(content string, frontmatter map[string]any) (string, error) {
encoded, err := yaml.MarshalWithOptions(frontmatter, yaml.Indent(2), yaml.IndentSequence(true))
Comment thread pkg/cli/edit_command.go Outdated
if lockErr != nil && !os.IsNotExist(lockErr) {
return fmt.Errorf("read generated lock file: %w", lockErr)
}
if err := os.WriteFile(workflowPath, []byte(updated), 0o644); err != nil {
Comment thread pkg/cli/edit_command.go Outdated
Comment on lines +91 to +104
return writeAndCompileEditedWorkflow(workflowPath, content, updated)
}

func writeAndCompileEditedWorkflow(workflowPath string, content []byte, updated string) error {
lockPath := stringutil.MarkdownToLockFile(workflowPath)
previousLock, lockErr := os.ReadFile(lockPath)
lockExisted := lockErr == nil
if lockErr != nil && !os.IsNotExist(lockErr) {
return fmt.Errorf("read generated lock file: %w", lockErr)
}
if err := os.WriteFile(workflowPath, []byte(updated), 0o644); err != nil {
return fmt.Errorf("write workflow: %w", err)
}
if err := compileWorkflow(context.Background(), workflowPath, false, true, ""); err != nil {

@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 /codebase-design and /tdd — requesting changes on correctness risks and test coverage gaps.

📋 Key Themes & Highlights

Key Themes

  1. --schedule silently replaces all existing cron entries (line 170) — the PR description implies preservation, but kind: "set" overwrites the entire list.
  2. Colon-in-value parsing bug (line 196) — strings.Cut on : truncates values like gpt-4o:preview; use direct YAML unmarshal.
  3. Silent restore failures in rollback path (line 129) — _ = os.WriteFile(...) hides errors that leave the working tree inconsistent.
  4. YAML reformatting is a destructive side effect (line 271) — key ordering and comments are lost on every edit; should be documented in --help.
  5. fmt.Sprint comparison is lossy for non-scalar list values (line 255) — safe today for strings-only lists, but fragile as the generic function evolves.
  6. Rollback path has no test coverage (test line 114) — the most complex behaviour in the PR is entirely untested.
  7. Over-bundled test (test line 22) — pack too many orthogonal concerns into one case; split for specificity.

Positive Highlights

  • ✅ Source-managed workflow guard is clean and well-tested
  • --dry-run is a good safety valve and is tested
  • on trigger coercion (string → map, list → map) handles the most common real-world shapes
  • ✅ Delegating schedule parsing to parser.ParseSchedule (after the review comment) is the right design

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · copilot · sonnet46 · 53.9 AIC · ⌖ 11.8 AIC · ⊞ 7.6K
Comment /matt to run again

Comment thread pkg/cli/edit_command.go Outdated
if strings.EqualFold(schedule, "off") {
return editChange{kind: "unset", path: "on.schedule"}, nil
}
cron, _, err := parser.ParseSchedule(schedule)

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.

[/codebase-design] --schedule silently replaces all existing cron entries — a workflow with two cron triggers loses one without warning.

💡 Details

The PR description says "Preserves existing triggers when adding or removing on.schedule" — this is true for non-schedule triggers (push, pull_request) but the scheduleChange function always produces a kind: "set" that overwrites the entire on.schedule list. At minimum, emit a warning to stderr when existing cron entries are discarded, or introduce --add-schedule / --remove-schedule for additive edits.

@copilot please address this.

Comment thread pkg/cli/edit_command.go
return err
}
parent, key, err := editChangeParent(frontmatter, change.path)
if err != 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.

[/codebase-design] parseEditAssignment uses strings.Cut(assignment, separator) which splits on the first occurrence — fine for =, but a YAML inline-value like "model: gpt-4o:preview" would be split at the first :, leaving rawValue = "gpt-4o" and silently dropping :preview.

💡 Suggested fix

For the positional [path: value] argument, consider parsing the entire string as YAML directly (one-key mapping) rather than splitting on : and re-wrapping:

var kv map[string]any
if err := yaml.Unmarshal([]byte(assignment), &kv); err != nil || len(kv) != 1 {
    return editChange{}, fmt.Errorf("invalid assignment %q", assignment)
}

This lets YAML handle quoting, colons-in-values, etc.

@copilot please address this.

Comment thread pkg/cli/edit_command.go
return nil, err
}
changes = append(changes, change)
}

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] writeAndCompileEditedWorkflow restores the original .md on compile failure, but the restore itself is fire-and-forget (_ = os.WriteFile(...)). If the restore fails (e.g., disk full), the workflow on disk will be in the edited state while the caller receives a compile error — leaving the working tree inconsistent.

💡 Suggested fix
restoreErr := os.WriteFile(workflowPath, content, 0o644)
if restoreErr != nil {
    return fmt.Errorf("compile failed (%w); also failed to restore original: %v", err, restoreErr)
}

Same pattern for the lock-file restore.

@copilot please address this.

Comment thread pkg/cli/edit_command.go Outdated
child, ok := parent[part].(map[string]any)
if !ok {
if part == "on" {
switch triggers := parent[part].(type) {

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.

[/codebase-design] editChangeParent creates intermediate maps on demand when a path segment is missing, but never writes those maps back if they were created inside the on special-case branch — specifically, the new child map for string/list on is stored in parent[part], then parent = child, but the map is only persisted for the on key during the next loop iteration. If the path is only one level deep (e.g., on itself), the converted map would be discarded.

💡 Confirm or add a test

Add a test:

frontmatter := map[string]any{"on": []any{"push", "pull_request"}}
applyEditChange(frontmatter, editChange{kind: "set", path: "on.schedule", value: ...})
// verify on still contains push and pull_request keys

@copilot please address this.

Comment thread pkg/cli/edit_command.go
child = map[string]any{}
parent[part] = child
}
parent = child

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] applyEditChangeToParent uses fmt.Sprint(value) == fmt.Sprint(change.value) for deduplication/removal — this will silently mismatch for values where fmt.Sprint is lossy (e.g., map[string]any{"cron": "0 * * * *"} becomes "map[cron:0 * * * *]"). For imports and skills (always strings) this is safe today, but the function is generic and used for all list paths.

💡 Suggested fix

Narrow the dedup/removal to fmt.Sprintf("%v", ...) comparisons or use reflect.DeepEqual for correctness, and add a doc comment explaining the limitation:

// String-coerced comparison; works for scalar list elements.
// Do not use for map-typed list entries.

@copilot please address this.

Comment thread pkg/cli/edit_command.go Outdated
if !ok && parent[key] != nil {
return fmt.Errorf("cannot add to %q because it is not a list", change.path)
}
if !slices.ContainsFunc(values, func(value any) bool { return fmt.Sprint(value) == fmt.Sprint(change.value) }) {

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.

[/codebase-design] replaceFrontmatter re-encodes the entire frontmatter map with go-yaml, which will reorder YAML keys alphabetically and strip all comments. For a tool meant to be "safe" to run on human-authored workflows, this is a significant destructive side effect that authors won't expect.

💡 Options
  • Accept the limitation and document it clearly in the command's Long help text (e.g., "Note: frontmatter will be reformatted and comments removed").
  • Or use a YAML-node–level round-trip library to preserve key order and comments.

At minimum, the PR description and --help output should call this out.

@copilot please address this.


changes, err := editChangesFromCommand(cmd, []string{"workflow", "model: small"})
require.NoError(t, err)
frontmatter := map[string]any{"on": "workflow_dispatch"}

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] TestEditChangesSupportAssignmentsSchedulesAndImports asserts the expected cron value as a literal fuzzy string ("FUZZY:HOURLY/6 * * *"). If parser.ParseSchedule changes its output format, this test will catch it — but the test name doesn't communicate what behaviour is being verified. The name also packs too many orthogonal concerns into one test.

💡 Suggested split

Split into focused tests following Arrange/Act/Assert:

  • TestEditSchedule_ConvertsHourlyInterval
  • TestEditAssignment_SetsStringPath
  • TestEditAddImport_AppendsToList

Each should verify one behaviour with a descriptive name that reads as a specification.

@copilot please address this.

cmd := NewEditCommand()
cmd.SetArgs([]string{workflowPath, "max-turns: 20"})
assert.ErrorContains(t, cmd.Execute(), "source-managed")
}

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] There is no test for writeAndCompileEditedWorkflow — specifically, the rollback path (compile failure → restore original .md and lock file) is untested. This is the most complex and highest-risk code in the PR.

💡 Suggested test cases
  • Compile fails → original .md content is restored on disk
  • Compile fails when lock file existed → lock file is restored
  • Compile fails when lock file did not exist → lock file is removed

These can use a table-driven test with a mock compileWorkflow that returns a deliberate error.

@copilot please address this.

Documents the architectural decision to introduce `gh aw edit` as a
dedicated, schema-validated mutation path for workflow frontmatter,
including alternatives considered and consequences.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

ADR Required — Draft Generated

Status: Draft ADR committed to this branch · merge blocked pending review

This PR adds 444 new lines to pkg/cli/ (business logic) and no Architecture Decision Record was found in the PR body, branch, or linked issues. A draft ADR has been generated and committed to this branch at:

📄 docs/adr/55475-schema-validated-workflow-frontmatter-edit-command.md

Decision captured: Introduce gh aw edit as a dedicated CLI command for schema-validated, programmatic mutation of workflow YAML frontmatter with automatic recompilation and rollback on failure.

What to do next
  1. Review the draft ADR at docs/adr/55475-schema-validated-workflow-frontmatter-edit-command.md in this branch.
  2. Fill in any [TODO: verify] placeholders and verify that the Alternatives and Consequences sections reflect the real trade-offs.
  3. Change the status from Draft to Accepted (or Proposed if team review is still needed) once the content is accurate.
  4. Update the PR description to link to the ADR so reviewers can find it easily, e.g. add: ADR: docs/adr/55475-schema-validated-workflow-frontmatter-edit-command.md
Why ADRs matter here

ADRs create a durable record of why a decision was made, not just what was built. For a CLI command like gh aw edit, capturing the decision now means future contributors will understand:

  • Why a new command was preferred over extending gh aw update
  • What the known trade-offs are (YAML re-serialisation, source-managed workflow restriction)
  • What alternatives were considered and rejected

This context is especially valuable for experimental features that may evolve or be superseded.

ADR sections reference
Section What to write
Context Situation, problem, constraints — 3–5 sentences for a newcomer
Decision Active voice: "We will..." plus the primary driver
Alternatives Considered ≥2 genuine options with honest rejection reasons
Consequences ≥2 positive, ≥2 negative, neutral side-effects

Merge blocker: This PR cannot merge until the ADR status is changed from Draft to Accepted (or Proposed with team sign-off). Please update the ADR and re-request review.

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · claude · sonnet46 · 61 AIC · ⌖ 19.8 AIC · ⊞ 8.6K ·
Comment /review to run again

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

@copilot add extensive exhaustive integration workflow tests of the cli command

Added end-to-end CLI integration coverage in 8d95230 for all mutation flags, workflow ID/filename/path resolution, dry runs, compilation rollback, and source-managed rejection.

@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: gh aw edit (experimental)

Good overall structure — the command is well-scoped, the schema validation before writing is the right approach, and the rollback on compile failure is a thoughtful safety net. Three non-blocking issues found:

  1. Silent rollback failure (line 110) — if the rollback os.WriteFile fails, the error is swallowed. A write-then-rename pattern avoids the need for rollback entirely.
  2. Colon-separator ambiguity (line 157) — positional path: value args split on the first colon, so values containing colons (e.g. multi-key YAML) can be misparsed. Worth documenting or hardening.
  3. fmt.Sprint dedup (line 280) — the add dedup check is fragile for non-string values; reflect.DeepEqual is more correct.

No blocking issues. The test coverage is solid for the happy paths and several edge cases.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · copilot · sonnet46 · 30.6 AIC · ⌖ 9.3 AIC · ⊞ 6.2K

Comment thread pkg/cli/edit_command.go Outdated
_ = os.WriteFile(lockPath, previousLock, 0o644)
} else {
_ = os.Remove(lockPath)
}

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.

Reliability: silent rollback failure can leave the workflow file in a broken state

If compileWorkflow fails and one of the rollback os.WriteFile calls also fails (disk full, permission error), the error is silently swallowed with _ = os.WriteFile(...), leaving the user with no diagnostic and potentially a corrupted/partially-written workflow.

Consider using a write-then-rename pattern: write the updated content to a temp file in the same directory, then os.Rename it over the original only after compilation succeeds. That way the original file is never modified until the new version is proven good and no rollback is needed at all.

@copilot please address this.

Comment thread pkg/cli/edit_command.go
}
if schedule := editFlagString(cmd, "schedule"); schedule != "" {
change, err := scheduleChange(schedule)
if err != 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.

Correctness: parseEditAssignment with : separator will misparse values containing colons

Using strings.Cut(assignment, ":") to parse positional args like "on.schedule: daily" will stop at the first colon. This is intentional for simple values, but values like "trigger: push: [main]" or any YAML value containing a colon would be split at the wrong position.

The current approach parses the right-hand side as YAML after the cut, so the value daily: 10 would only hand daily to the YAML parser. Consider documenting this limitation clearly, or using a regex/index-of-first-space approach to split path: rest more robustly.

@copilot please address this.

Comment thread pkg/cli/edit_command.go
return fmt.Errorf("cannot remove from %q because it is not a list", change.path)
}
parent[key] = slices.DeleteFunc(values, func(value any) bool { return fmt.Sprint(value) == fmt.Sprint(change.value) })
default:

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.

Harden: add dedup uses fmt.Sprint equality, which is fragile for non-string values

if !slices.ContainsFunc(values, func(value any) bool { return fmt.Sprint(value) == fmt.Sprint(change.value) }) {

fmt.Sprint on maps/slices is order-dependent and not a stable equality check. For the imports/skills cases the values will always be strings, but this function is generic and could be called with on.schedule or nested objects in the future. Using fmt.Sprintf("%v", ...) on a map[string]any produces Go's default map printing which has non-deterministic key ordering.

Consider restricting dedup to string-typed values, or using a proper reflect.DeepEqual comparison.

@copilot please address this.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

gh-aw-pr-sous-chef
@copilot PR Sous Chef triage: this PR still needs a forward-progress pass before maintainer investigation is likely to be productive.

Please do one focused follow-up pass:

  • address the current blocking github-actions review and any remaining maintainer review comments directly in the PR threads
  • refresh the branch if needed and use the pr-finisher skill before handing back for review
  • the blocked workflow approvals for this head were handled in this run; no extra workflow action is needed unless new commits land

Run: https://github.com/github/gh-aw/actions/runs/32774702076

Generated by 👨‍🍳 PR Sous Chef · pi · gpt54 · 25.7 AIC · ⌖ 8.08 AIC · ⊞ 8.7K ·
Comment /souschef to run again

Copilot AI and others added 2 commits August 24, 2026 21:02
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

Copilot AI commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

gh-aw-pr-sous-chef
@copilot PR Sous Chef triage: this PR still needs a forward-progress pass before maintainer investigation is likely to be productive.
...

Forward-progress pass done in b7f2e0f/latest commits, addressing the blocking review and the open reviewer threads:

  • Edits that change nothing are now detected and skip writing entirely, so --schedule off on a workflow using shorthand on: push leaves the file byte-identical (blocking review).
  • String-form on: values are expanded through parser.ParseSchedule / workflow.ParseTriggerShorthand; shorthands that cannot be expanded safely (e.g. /bot) are rejected instead of becoming an invalid trigger map.
  • Workflow and lock writes go through a sibling temp file plus rename, keeping the original file permissions, and rollback failures are surfaced via errors.Join instead of being discarded.
  • Compilation now uses cmd.Context() instead of context.Background().
  • --help and the ADR document that changed frontmatter is re-serialized (comments/ordering not preserved) and that no-op edits never rewrite a workflow.
  • Added unit and integration coverage for shorthand preservation, shorthand expansion, no-op detection, and atomic writes.

The compact schedule forms (1h, weekdays) mentioned in the original PR description are not accepted by the shared schedule parser; the supported forms are daily, every 6h, daily on weekdays, and cron strings, as shown in the command examples.

gh-aw-pr-sous-chef
@copilot PR Sous Chef triage: this PR still needs a forward-progress pass before maintainer investigation is likely to be productive.
...

Correction on the commit references in my previous reply: the follow-up pass landed in 502dfcd and 6260e69.

Copilot AI requested a review from gh-aw-bot August 24, 2026 21:05
@pelikhan
pelikhan merged commit 896c381 into main Aug 24, 2026
32 of 33 checks passed
@pelikhan
pelikhan deleted the copilot/proposal-add-gh-aw-edit-command branch August 24, 2026 22:18
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.

Proposal: add gh aw edit for schema-aware workflow configuration changes

4 participants