| name | safe-operations |
| description | Safe file-operation and issue-creation protocol for Agent Orchestra. Use when choosing workspace tools, avoiding unsafe file writes, or creating GitHub issues under the workflow rules. DO NOT USE FOR: application-level debugging or replacing agent judgment on whether work is in scope. |
Safe Operations Instructions
Purpose
Establish safe, consistent rules for file operations and issue creation across all agents in this workflow. These rules prevent silent file corruption and ensure GitHub issues are always properly labeled.
Section 1: File Operation Rules (CRITICAL)
These rules apply whenever any agent uses terminal commands or file tools to read, write, or move files. PowerShell write commands silently corrupt files through incorrect encoding, unwanted BOM markers, or inconsistent line endings. Always use the designated tool for each operation.
Correct Tools by Operation
| Operation | Correct Tool |
|---|
| Create a new file | create_file |
| Edit an existing file | replace_string_in_file / multi_replace_string_in_file |
| Read a file | read_file |
| Delete a file | Remove-Item (terminal) |
| Archive/move a file | Move-Item (terminal) |
Read-Only & Computable Operations
For operations that only inspect state or compute values, always prefer dedicated VS Code tools over terminal commands. Terminal commands trigger a "Run command?" confirmation dialog and return unstructured text — dedicated tools provide structured, typed outputs without interruption.
| Operation | Preferred Method | Do NOT use terminal for |
|---|
| Inspect changed files / diffs | get_changed_files | git diff (working-tree; cross-branch diff is permitted in terminal), git status |
| Read file content | read_file | Get-Content, cat |
| Search for text in files | grep_search | Select-String, grep, git grep |
| List directory contents | list_dir or file_search | Get-ChildItem, ls |
| Check file/directory existence | file_search (glob-based; use exact-path pattern and check for non-empty result) | Test-Path |
| Arithmetic / coordinate math | Agent reasoning directly | node -e, python -c, pwsh -c |
| Semantic / concept search | semantic_search | — |
Exception: The "Do NOT use" restrictions above apply to ad-hoc discovery. Project validation commands explicitly permitted in the Rule below (e.g., quick-validate checks in .github/copilot-instructions.md) may use Get-ChildItem, Select-String, and similar terminal commands.
Rule: By default, use dedicated VS Code tools for all inspection and read operations. Reserve run_in_terminal for: build commands, test runners, file move/delete operations, gh CLI calls, git workflow operations (commit, push, checkout, branch, merge), project validation commands (e.g., quick-validate checks in .github/copilot-instructions.md), targets outside the workspace, and operations with no built-in equivalent (e.g., file timestamps, git log history, complex path-exclusion filters).
Scratch files: write all agent scratch to .tmp/ per skills/terminal-hygiene/SKILL.md ## Scratch & Temp-File Hygiene — never construct host-native absolute paths in a POSIX/git-bash shell.
FORBIDDEN PowerShell Write Commands
Never use any of the following to write or modify file content:
Set-Content
Out-File
Add-Content
New-Item with -Value
echo something > file.txt or echo something >> file.txt
.NET static IO methods: [System.IO.File]::WriteAllText(), ::AppendAllText(), ::WriteAllLines(), ::WriteAllBytes() — same silent encoding risks
These PowerShell commands silently corrupt files through encoding issues (e.g., UTF-16 BOM), incorrect line endings (CRLF where LF is expected), or data truncation. Even when they appear to succeed, the resulting files may break parsers, linters, and downstream tooling.
Section 2: Issue Creation Rules
When authoring new issues under these rules, apply the outsider-first authoring convention in skills/naming-register-policy/SKILL.md § Outsider-first authoring default.
2a. Improvement-First Decision Rule
When any agent discovers an out-of-scope or non-blocking improvement during its work, classify it against the structural-criteria gate (canonical taxonomy in skills/review-judgment/scripts/Test-DeferralCriteria.ps1):
- Inline (fix-in-PR) eligibility: the change is small, single-file or single-system, doesn't introduce a new abstraction, doesn't cross architecture layer boundaries, and doesn't require a separate design decision. Address within the current task (or current PR if one is open).
- Follow-up issue creation: the change matches at least one structural criterion (
S-new-abstraction, S-cross-cutting, S-design-decision, S-schema-or-contract, S-different-surface, S-maintainer-judgment). Create a follow-up GitHub issue immediately using gh issue create (or the Add-FollowUpIssue helper in skills/safe-operations/scripts/Add-FollowUpIssue.ps1 for the canonical sentinel + GraphQL parenting), then continue with in-scope work. Do not block the current PR on the deferred improvement.
Supplementary rationale: as a quick sanity check, deferred (structural) issues typically represent more than a day of work, but structural-criteria match — not the effort estimate — is the load-bearing deferral criterion.
Output capture: After gh issue create succeeds, capture the returned issue URL. Do not re-run the command if it already returned a URL. If terminal output is unclear or truncated, verify by listing recent open issues before retrying:
gh issue list --limit 5 --state open --json number,title --jq '.[] | "\(.number): \(.title)"'
Scan the output for an exact title match. If a match is found, the issue was created — do not re-run. This uses the list API (not the search index) and is not subject to propagation delay. Output capture is the primary defense against rapid re-submission (e.g., terminal retry when output was swallowed); search-based deduplication (Section 2c) cannot prevent sub-second re-submissions due to GitHub's search index propagation delay.
2b. Priority Label Requirement
Every gh issue create command run by any agent MUST include a --label flag specifying a priority. Issues created without a priority label are non-compliant.
# REQUIRED — always include a priority label:
gh issue create --title "..." --body "..." --label "priority: medium"
# WRONG — missing priority label:
gh issue create --title "..." --body "..."
Prerequisite — Priority labels must exist in the target repository.
If they do not yet exist, run these commands once per repository:
gh label create "priority: high" --color "#D93F0B" --description "Critical — must fix this sprint"
gh label create "priority: medium" --color "#FBCA04" --description "Strong improvement — schedule soon"
gh label create "priority: low" --color "#0075CA" --description "Nice-to-have — defer or batch"
Priority Labels
| Label | Description | When to use |
|---|
priority: high | Critical — highest impact, must fix | Correctness bugs, security issues, broken builds |
priority: medium | Strong improvement — depth and polish | Deferred improvements, notable refactors, non-urgent features |
priority: low | Nice-to-have — cosmetic or optional | Cosmetic, optional, or speculative work |
Default for automatically-created follow-up issues: priority: medium
2b-bis. Umbrella or Triage at Creation (Additive to §2b)
Every new issue created by any agent MUST be placed under a tracked umbrella or left as an ungrouped open issue that the portfolio renderer auto-derives into Triage — this rule is additive to the §2b priority mandate and does not replace it. The intent is unchanged: a new issue must not silently disappear from the control-tower tracker. Under Control Tower v2 the mechanism changed (see Documents/Design/control-tower-v2.md).
- Parent umbrella (child issue) — if the work is scoped to a tracked initiative, attach the new issue as a native sub-issue of an existing sequenced umbrella. Either create-and-attach in one step with
Add-FollowUpIssue (canonical create-and-attach helper), or run gh issue create first and then attach the already-created issue with Set-IssueParent (canonical attach-existing helper).
- New umbrella → insert at rank — if you are creating a new umbrella (an issue that will own sub-issues), you MUST also insert its number into
Documents/Planning/sequence.yaml's umbrellas: inline list at the correct priority rank. sequence.yaml is the canonical home for umbrella ranking — do not add a routing-tables JSON entry. Then attach the umbrella's own children as native sub-issues with Set-IssueParent, exactly as for any other umbrella.
- Triage (ungrouped open issue) — if no umbrella applies, just create the issue. Under v2 the renderer derives Triage from parent-edge data (open ∧ no parent ∧ no sub-issues ∧ not listed in
umbrellas:), so an ungrouped open issue is always a Triage candidate under the v2 derivation rules — it is never silently excluded from the board count, though it may fall below the cap-5 rendering fold (see Caveat below). The --label triage flag is now optional/advisory (a human-readable hint only); it is no longer load-bearing for Triage placement, because v2 removed the triage-label query entirely. Caveat: Triage is capped at 5 issues and sorted priority-first (Get-PriorityKey order). An unlabeled issue resolves to Get-PriorityKey = 3 (lowest rank tier) and may fall below the fold if the Triage bucket is already full. See Documents/Design/control-tower-v2.md for cap and ranking mechanics.
# CORRECT — umbrella child (create the issue, then attach it as an existing child with Set-IssueParent):
$url = gh issue create --title "..." --body "..." --label "priority: medium"
# Extract number and attach the already-created issue to the umbrella with Set-IssueParent:
$issueNum = $url -replace '.*/', ''
pwsh skills/safe-operations/scripts/Set-IssueParent.ps1 -ParentIssueNumber 425 -ChildIssueNumber $issueNum
# CORRECT — new umbrella: create it, insert its number into sequence.yaml umbrellas: at
# the right rank, then attach its children as sub-issues. The sequence.yaml edit is
# canonical and mandatory — do NOT add a routing-tables JSON entry.
# CORRECT — ungrouped open issue: v2 auto-derives this into Triage (no label required):
gh issue create --title "..." --body "..." --label "priority: medium"
Why: under Control Tower v2 the renderer surfaces every umbrella listed in sequence.yaml plus every ungrouped open issue (auto-derived into Triage). A new umbrella that is never inserted into umbrellas: is invisible to the board; an ungrouped open issue is surfaced automatically, so no triage label is needed to keep it visible. Issues still must not silently disappear from the tracker — the v2 mechanism is parent-edge derivation, not a label scan.
2b-ter. Creation-Time Board Positioning (Additive to §2b and §2b-bis)
Before every gh issue create or Add-FollowUpIssue call, the agent must make a conscious positioning decision covering two questions:
(a) What priority label to apply — the label controls Get-PriorityKey rank within Triage and umbrella children. A deliberate choice of priority: low (or no label, which resolves to the lowest rank tier) is a valid and acceptable outcome when the issue genuinely represents low-urgency work.
(b) Parent-or-standalone — attach to an active umbrella via Set-IssueParent, or leave as a standalone issue that auto-derives into Triage. A deliberate "low priority / standalone / may not stay on board" decision is a valid and acceptable outcome.
Lever mapping — what the filer controls and its board effect:
| Lever | Mechanism | Board effect |
|---|
Priority label (--label priority:h/m/l) | Sets Get-PriorityKey = 0 (high), 1 (medium), or 2 (low) | Affects rank/sort order within Triage or umbrella children; no label → Get-PriorityKey = 3 (lowest tier, may fall below Triage fold if bucket is full) |
Parent edge (Set-IssueParent -ParentIssueNumber N) | Attaches issue as ActiveChildren of a tracked umbrella | Places issue in the umbrella's children section; requires a spec-listed active umbrella |
Standalone (bare gh issue create) | No parent edge set | Auto-derives into Triage under v2 derivation rules |
Render-derived buckets (NOT filer-controllable): RecentlyClosed, DriftWarnings, and IntegrityWarnings are computed by the renderer from issue state and relationship data — the filer cannot directly place an issue in these zones. See Documents/Design/control-tower-v2.md for cap-5 and priority-ranking mechanics; do not copy those numbers or formulas here.
Positioning residue — at creation time, record a single positioning note in the issue body using this format:
Board positioning: priority=<h|m|l>; placement=standalone|parent #N; rationale=<one line>
- Record positioning-decision content only — do NOT paste finding detail (issue bodies are world-readable).
- No enforcement script is required; this is an honor-system record for auditability.
Automated-path carve-out: on the Add-FollowUpIssue automated path, the canonical [Structural] {criterion_id} title prefix and the injected Parent: #N body field already serve as the positioning record. This satisfies the placement portion of the residue — priority is carried by the issue's --label flag, and no free-text rationale is required on the automated path. No additional gh issue edit step is needed.
2c. Deduplication Check (Mandatory)
Rule-addition proposals: Apply §2d (Prevention-Analysis Advisory, below) before this search — if §2d redirects to an existing issue, this dedup search is unnecessary.
Before every gh issue create, search for existing open issues with matching titles or key terms from the title:
# REQUIRED — search before creating:
# Extract 2-4 distinctive words from the title, e.g. for "Add deduplication guard to issue creation protocol" use "deduplication guard issue creation"
gh issue list --search "{key phrase from title}" --state open --json number,title --jq '.[] | "\(.number): \(.title)"'
If a matching issue exists, do NOT create a duplicate. Instead, reference the existing issue number in the current work context (PR body, review notes, or tracking file).
Exception: Skip when the title contains a high-entropy machine-generated unique identifier — specifically a full commit SHA (40 hex chars) or UUID v4 (128-bit random) — that guarantees no collision. Short tokens, sequential IDs, and timestamps do not qualify.
Note on search-index timing: GitHub's search index has a propagation delay (typically seconds to minutes). The dedup search cannot prevent sub-second re-submissions — that failure mode is addressed by output capture (Section 2a). This search guards against independent code-path convergence (the same topic created by separate agents on different branches or sessions).
Cross-repo gotcha dedup (used by Process-Review §4.8 upstream lifecycle):
# Cross-repo dedup — use --repo flag to target the upstream Agent Orchestra repo:
# Read agent-orchestra-repo from .github/copilot-instructions.md first
gh issue list --repo {agent-orchestra-repo} --search "[Gotcha] {skill-name}" --state all --json number,title --jq '.[] | "\(.number): \(.title)"'
Key differences from the standard pattern:
--repo {agent-orchestra-repo} targets the upstream template repo (not the current repo)
--state all includes closed issues (a resolved gotcha should not be re-submitted)
- Search key format is
[Gotcha] {skill-name} — the [Gotcha] prefix groups all gotcha issues for that skill
- If
gh cannot access the upstream repo, fall back to creating a local issue labeled upstream-gotcha and priority: medium for manual transfer
2d. Prevention-Analysis Advisory (Rule-Addition Proposals Only)
Before creating any issue that proposes adding a new rule, directive, or guidance clause to an agent file, instruction file, or skill, evaluate the following in order. Apply this check before the §2c dedup search — if §2d redirects to an existing issue, the §2c search is unnecessary:
Step 1 — Principle-level consolidation check: Does an open issue already cover the same underlying principle, even if it targets a different agent or file? If yes, comment on the existing issue instead of creating a new one. If multiple matching issues exist, comment on the most recently updated one.
Principle-level consolidation examples:
- "Add input validation to CLI handler" and "Add input schema enforcement to REST handler" → same principle (input validation), consolidate into one issue
- "Add error handling for null responses" and "Add timeout handling for slow responses" → different principles (null safety vs. resilience), separate issues are appropriate
- "Require docstrings on public functions" and "Require inline comments on complex logic" → same principle (documentation completeness), consolidate into one issue
Step 2 — Prevention alternative check: Could the problem be solved structurally instead of adding a rule? Structural alternatives include: contract test that enforces the behavior, upstream catch that prevents the failure, skill extraction that reduces rule density, or consolidation with an existing guideline. If yes, reframe the issue as a structural improvement rather than a rule addition.
Step 3 — Create with justification: If neither Step 1 nor Step 2 applies, create the issue and note briefly in the issue body why a new rule is warranted (e.g., 'no existing principle covers this; structural prevention is not feasible here').
Scope: This advisory applies only to rule-addition proposals (systemic_fix_type: agent-prompt or instruction). It does not apply to:
- Issues that reduce directive count (compression, extraction, consolidation) — these are exempt
- Structural prevention issues (new contract tests, upstream catches)
- Bug reports, configuration fixes, or documentation corrections
Override: This is advisory guidance — agent judgment determines the outcome. Users may always direct issue creation regardless of this advisory.
Gotchas
| Trigger | Gotcha | Fix |
|---|
| Editing workspace files from PowerShell | Silent encoding or line-ending corruption slips into tracked files | Use the designated file tools for content changes and keep terminal writes for move/delete cases only |