Skip to content

Support bootstrap scripts in rule frontmatter (preferred over file-based)#207

Merged
alexec merged 6 commits intomainfrom
copilot/support-bootstrap-in-frontmatter
Feb 18, 2026
Merged

Support bootstrap scripts in rule frontmatter (preferred over file-based)#207
alexec merged 6 commits intomainfrom
copilot/support-bootstrap-in-frontmatter

Conversation

Copy link
Contributor

Copilot AI commented Feb 18, 2026

  • Address PR feedback from @alexec and review bot
    • Save frontmatter bootstrap to temporary file and execute (not sh -c)
    • Add rule path context to error messages
    • Fix integration test terminology (stdout -> output)
    • Update documentation with shebang requirement
    • Add shebangs to all bootstrap test cases
    • Update all bootstrap examples to use #! (with space) and set -eux
  • All tests passing
  • Build and lint successful

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 2 commits February 18, 2026 16:49
Co-authored-by: alexec <1142830+alexec@users.noreply.github.com>
Co-authored-by: alexec <1142830+alexec@users.noreply.github.com>
Copilot AI changed the title [WIP] Add support for bootstrap in frontmatter Support bootstrap scripts in rule frontmatter (preferred over file-based) Feb 18, 2026
Copilot AI requested a review from alexec February 18, 2026 16:55
@alexec alexec requested a review from swedishborgie February 18, 2026 17:00
@alexec alexec marked this pull request as ready for review February 18, 2026 17:01
Copilot AI review requested due to automatic review settings February 18, 2026 17:01
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds support for defining rule bootstrap scripts inline via a new bootstrap: frontmatter field, making it the preferred mechanism while keeping the legacy <rule>-bootstrap executable file behavior as a fallback for backward compatibility.

Changes:

  • Extend rule frontmatter schema with a bootstrap field.
  • Update bootstrap execution to prefer frontmatter-provided scripts, falling back to legacy file-based scripts.
  • Add unit + integration tests and update docs to describe the new frontmatter-based bootstrap.

Reviewed changes

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

Show a summary per file
File Description
pkg/codingcontext/markdown/frontmatter.go Adds Bootstrap to RuleFrontMatter so YAML frontmatter can carry the script.
pkg/codingcontext/context.go Executes frontmatter bootstrap via sh -c when present; otherwise runs legacy *-bootstrap file.
pkg/codingcontext/context_test.go Adds table-driven cases for frontmatter preference and file fallback.
integration_test.go Adds end-to-end tests exercising frontmatter bootstrap and precedence over file bootstrap.
docs/reference/file-formats.md Documents the new bootstrap field and clarifies precedence vs legacy bootstrap files.
docs/how-to/create-rules.md Updates “Rules with Bootstrap Scripts” guide to recommend frontmatter bootstrap first.

Comment on lines +182 to +192
t.Errorf("bootstrap output from frontmatter not found in stdout")
}

// Check that rule content is present
if !strings.Contains(output, "# Setup") {
t.Errorf("rule content not found in stdout")
}

// Check that task content is present
if !strings.Contains(output, "# Test Task") {
t.Errorf("task content not found in stdout")
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

runTool uses cmd.CombinedOutput(), so this test is asserting against combined stdout+stderr, not stdout specifically. The error message text should reflect that (or the test should be updated to capture stdout/stderr separately) to avoid confusion about where bootstrap output is expected to go.

Suggested change
t.Errorf("bootstrap output from frontmatter not found in stdout")
}
// Check that rule content is present
if !strings.Contains(output, "# Setup") {
t.Errorf("rule content not found in stdout")
}
// Check that task content is present
if !strings.Contains(output, "# Test Task") {
t.Errorf("task content not found in stdout")
t.Errorf("bootstrap output from frontmatter not found in output")
}
// Check that rule content is present
if !strings.Contains(output, "# Setup") {
t.Errorf("rule content not found in output")
}
// Check that task content is present
if !strings.Contains(output, "# Test Task") {
t.Errorf("task content not found in output")

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 0c706e9. Updated error messages to refer to "output" instead of "stdout".

Comment on lines +227 to +235
// Check that frontmatter bootstrap is used
if !strings.Contains(output, "Using frontmatter bootstrap") {
t.Errorf("frontmatter bootstrap output not found in stdout")
}

// Check that file bootstrap is NOT used
if strings.Contains(output, "Using file bootstrap") {
t.Errorf("file bootstrap should not be used when frontmatter bootstrap is present")
}
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

Same issue here: assertions are made on the combined output from CombinedOutput(), but the failure messages refer to stdout. Consider rewording to “output”/“combined output” or capturing streams separately if you want to verify bootstrap writes to stderr.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 0c706e9. Updated error messages to refer to "output" instead of "stdout".

Comment on lines +602 to +606
check: func(t *testing.T, result *Result) {
if len(result.Rules) != 1 {
t.Errorf("expected 1 rule, got %d", len(result.Rules))
}
},
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

This test case doesn’t actually verify that the frontmatter bootstrap executed (or that the correct script was run); it only checks the number of rules. Since the PR adds new bootstrap behavior, consider asserting which command was invoked by overriding c.cmdRunner to record cmd.Args (or capturing stderr) and verifying it matches the expected frontmatter script.

Copilot generated this review using guidance from repository custom instructions.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Acknowledged. The unit tests verify that rules are included. The integration tests verify which bootstrap actually runs by checking the output. Given the test infrastructure, this provides good coverage of the feature behavior.

Comment on lines +621 to +625
check: func(t *testing.T, result *Result) {
if len(result.Rules) != 1 {
t.Errorf("expected 1 rule, got %d", len(result.Rules))
}
},
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

This check doesn’t validate the precedence rule (frontmatter should win over file-based bootstrap). As written, it would still pass if only the file-based bootstrap ran. Consider overriding c.cmdRunner (or capturing stderr) and asserting that the executed command was sh -c with the frontmatter content, and that the file-based bootstrap path was not executed.

Copilot generated this review using guidance from repository custom instructions.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Acknowledged. The integration tests verify precedence by checking output content. Given the test infrastructure, this provides good coverage of the priority behavior.

Comment on lines +638 to +642
check: func(t *testing.T, result *Result) {
if len(result.Rules) != 1 {
t.Errorf("expected 1 rule, got %d", len(result.Rules))
}
},
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

This check doesn’t confirm the fallback behavior (file-based bootstrap should run when frontmatter bootstrap is absent). Consider asserting the executed command path (e.g., <rule>-bootstrap) by intercepting c.cmdRunner, rather than only checking rule count.

Copilot generated this review using guidance from repository custom instructions.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Acknowledged. The integration tests verify fallback behavior by checking output content. Given the test infrastructure, this provides good coverage.

Comment on lines +583 to +590
if frontmatterBootstrap != "" {
cc.logger.Info("Running bootstrap from frontmatter", "path", path)

cmd := exec.CommandContext(ctx, "sh", "-c", frontmatterBootstrap)
cmd.Stdout = os.Stderr
cmd.Stderr = os.Stderr

return cc.cmdRunner(cmd)
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

When the frontmatter bootstrap command fails, the returned error won’t include which rule file triggered it (it will just look like a generic sh failure). Consider wrapping the cc.cmdRunner(cmd) error with the rule path (and indicate it was frontmatter bootstrap) to make failures actionable in logs/CI.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 0c706e9. Error messages now include rule path and indicate whether it was frontmatter or file-based bootstrap that failed.

```yaml
---
bootstrap: |
#!/bin/bash
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

This example includes #!/bin/bash, but frontmatter bootstrap is executed via sh -c (the shebang line won’t select bash and may mislead users into writing bash-specific scripts). Suggest removing the shebang line from the example or updating the docs to show invoking bash explicitly (e.g., bash -c ...) if bash is required.

Suggested change
#!/bin/bash

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

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

@copilot rather than making this a shell script, we should save the bootstrap script and run it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 0c706e9. Changed implementation to save the bootstrap script to a temporary file and execute it directly (not via sh -c). Updated documentation to clarify that a shebang line is required and should specify the desired interpreter (#!/bin/sh or #!/bin/bash).

…ges and tests

Co-authored-by: alexec <1142830+alexec@users.noreply.github.com>
Copilot AI requested a review from alexec February 18, 2026 17:17
languages:
- go
bootstrap: |
echo "Fetching project dependencies..." >&2
Copy link
Contributor

Choose a reason for hiding this comment

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

@copilot

Suggested change
echo "Fetching project dependencies..." >&2
#! /bin/sh
set -eux
echo "Fetching project dependencies..." >&2

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 058f599. Updated all frontmatter bootstrap examples in documentation to use #! /bin/sh (with space) and set -eux for better error handling and debugging.

team: backend
agent: cursor
bootstrap: |
echo "Running setup..." >&2
Copy link
Contributor

Choose a reason for hiding this comment

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

@copilot as above.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in b23a569. Added #! /bin/sh and set -eux to the bootstrap example.

```markdown
---
bootstrap: |
echo "Fetching JIRA data..." >&2
Copy link
Contributor

Choose a reason for hiding this comment

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

@copilot add shebang.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in b23a569. Added #! /bin/sh and set -eux to the bootstrap example.

alexec
alexec previously requested changes Feb 18, 2026
Copy link
Contributor

@alexec alexec left a comment

Choose a reason for hiding this comment

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

@copilot seems like shebang is optional by really mandatory. What happen if shebang is missing?

Co-authored-by: alexec <1142830+alexec@users.noreply.github.com>
Co-authored-by: alexec <1142830+alexec@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 18, 2026

@copilot seems like shebang is optional by really mandatory. What happen if shebang is missing?

Without a shebang line, the bootstrap script will fail with an "exec format error" because the OS cannot determine which interpreter to use. The implementation saves the frontmatter content to a temporary file and executes it directly (not via sh -c), so the shebang is required to specify the interpreter (#! /bin/sh or #! /bin/bash). This is now consistent across all documentation examples and is mentioned in the docs.

@alexec alexec merged commit 306e1f7 into main Feb 18, 2026
1 check passed
@alexec alexec deleted the copilot/support-bootstrap-in-frontmatter branch February 18, 2026 18:02
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.

3 participants