Skip to content

chore: Extract hard-coded file paths to constants (120 instances)#38774

Closed
Copilot wants to merge 4 commits into
mainfrom
copilot/lint-monster-extract-file-paths
Closed

chore: Extract hard-coded file paths to constants (120 instances)#38774
Copilot wants to merge 4 commits into
mainfrom
copilot/lint-monster-extract-file-paths

Conversation

Copilot AI commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

120+ inline path string literals were scattered across pkg/workflow/, pkg/parser/, and pkg/cli/, flagged by the custom hardcodedfilepath linter. This PR extracts them all to named constants, eliminating the findings.

New constants

pkg/constants/path_constants.go (new file) — 26 constants grouped by category:

  • .github/ paths: DotGithubDir, DotGithubWorkflowsDir, DotGithubAgentsDir
  • /tmp/gh-aw/ paths: TmpGhAwDir, AgentStdioLogPath, AgentPromptFilePath, MCPLogsDir, MCPConfigDir, MCPConfigServersFilePath, RepoMemoryDir, ThreatDetectionLogPath, AwPatchGlob, AwBundleGlob, etc.

pkg/workflow/setup_action_paths.go — 5 new runner-side constants (shell/GHA-expr variants for MCP config path, safe-jobs dir, upload-artifacts staging dir).

Self-referential linter fix

pkg/linters/hardcodedfilepath/hardcodedfilepath.go — the pathPrefixes var was flagging itself. Extracted the three affected prefix strings as unexported package-level consts (which the linter skips).

Callsite updates

~50 files updated to use constants instead of literals. Representative before/after:

// before
paths = append(paths, "/tmp/gh-aw/mcp-logs/")
env["GH_AW_PROMPT"] = "/tmp/gh-aw/aw-prompts/prompt.txt"
if strings.HasPrefix(path, ".github/") { ... }

// after
paths = append(paths, constants.MCPLogsDir+"/")
env["GH_AW_PROMPT"] = constants.AgentPromptFilePath
if strings.HasPrefix(path, constants.DotGithubDir) { ... }

Platform-specific paths in pkg/cli/shell_completion.go (/opt/homebrew, /usr/local, /etc/bash_completion.d) are defined as file-level constants since they have no natural home in the shared constants package.

Copilot AI and others added 2 commits June 12, 2026 04:06
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] Extract hard-coded file paths to constants chore: Extract hard-coded file paths to constants (120 instances) Jun 12, 2026
Copilot AI requested a review from pelikhan June 12, 2026 04:38
@pelikhan pelikhan marked this pull request as ready for review June 12, 2026 13:48
Copilot AI review requested due to automatic review settings June 12, 2026 13:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR centralizes hard-coded path string literals into named constants (primarily under pkg/constants/ and pkg/workflow/), addressing findings from the hardcodedfilepath linter and improving consistency across workflow compilation, parsing, and CLI logic.

Changes:

  • Added pkg/constants/path_constants.go with shared .github/ and /tmp/gh-aw/... path constants and updated GetWorkflowDir() to return DotGithubWorkflowsDir.
  • Introduced runner-side path constants in pkg/workflow/setup_action_paths.go (shell vs GitHub Actions expression forms) and updated workflow rendering callsites to use them.
  • Updated many callsites across pkg/workflow/, pkg/parser/, pkg/cli/, and a compiled .lock.yml to replace inline path literals with constants (plus a self-referential fix in the hardcodedfilepath linter).
Show a summary per file
File Description
pkg/workflow/threat_detection.go Replaces threat-detection log path literal with a shared constant.
pkg/workflow/step_order_validation.go Uses shared constants for /tmp/gh-aw/ and runner temp path prefix checks.
pkg/workflow/setup_action_paths.go Adds runner-side path constants (shell vs GHA expression forms).
pkg/workflow/safe_outputs_steps.go Uses constants.TmpGhAwDir for artifact download path.
pkg/workflow/safe_outputs_config_generation.go Uses shared repo-memory base dir constant when generating config.
pkg/workflow/safe_jobs.go Switches safe-jobs download/output paths to workflow-level constants.
pkg/workflow/runtime_import_validation.go Uses .github/ prefix constant when normalizing import paths.
pkg/workflow/repo_memory.go Uses shared repo-memory base dir constant when building steps and artifact paths.
pkg/workflow/pi_engine.go Replaces prompt/models.json-related /tmp/gh-aw/... literals with constants.
pkg/workflow/opencode_mcp.go Uses shared MCP servers config path constant.
pkg/workflow/opencode_engine.go Uses shared prompt path constant in command/env wiring.
pkg/workflow/mcp_renderer_builtin.go Uses runner-side gh-aw binary path constant for release-mode entrypoint.
pkg/workflow/mcp_environment.go Uses shared MCP config dir constant for CODEX_HOME.
pkg/workflow/mcp_config_playwright_renderer.go Uses MCP logs directory constants for output-dir and mounts.
pkg/workflow/gemini_mcp.go Uses runner-side MCP servers config path constant (shell form).
pkg/workflow/gemini_logs.go Uses shared agent stdio log path constant.
pkg/workflow/gemini_engine.go Uses shared prompt path and error glob constants.
pkg/workflow/frontmatter_extraction_metadata.go Uses .github/ prefix constant when constructing local source URLs.
pkg/workflow/engine_output.go Uses shared /tmp/gh-aw/ base dir constant for workspace-vs-tmp filtering.
pkg/workflow/crush_mcp.go Uses shared MCP servers config path constant.
pkg/workflow/crush_engine.go Uses shared prompt path constant in command/env wiring.
pkg/workflow/copilot_logs.go Uses shared sandbox logs dir constant (with explicit trailing slash at return site).
pkg/workflow/copilot_engine.go Uses .github/ prefix constant for manifest path prefixes.
pkg/workflow/copilot_engine_execution.go Uses shared prompt/tmp constants and introduces an AWF copilot binary path const.
pkg/workflow/compiler_yaml.go Uses .github/ prefix constant in path normalization logic.
pkg/workflow/compiler_yaml_main_job.go Replaces multiple /tmp/gh-aw/... artifact path literals with constants/globs.
pkg/workflow/compiler_safe_outputs_job.go Uses constants.TmpGhAwDir for artifact download paths.
pkg/workflow/compiler_main_job.go Uses MCP safe-outputs logs dir constant for GH_AW_MCP_LOG_DIR.
pkg/workflow/compiler_difc_proxy.go Uses proxy/TLS constants for env vars and proxy log collection base path.
pkg/workflow/codex_mcp.go Uses runner-side MCP servers config path constant (shell form).
pkg/workflow/codex_engine.go Uses shared prompt/MCP config constants for env wiring.
pkg/workflow/claude_mcp.go Uses runner-side MCP servers config path constant (shell form).
pkg/workflow/claude_engine.go Uses shared prompt and runner-side MCP config path constant (GHA expr form).
pkg/workflow/awf_helpers.go Uses runner temp root and staging dir constants in AWF argument/mount generation.
pkg/workflow/antigravity_mcp.go Uses runner-side MCP servers config path constant (shell form).
pkg/workflow/antigravity_logs.go Uses shared agent stdio log path constant.
pkg/workflow/antigravity_engine.go Uses shared prompt path and error glob constants.
pkg/workflow/agentic_engine.go Uses shared default agent stdio log path constant.
pkg/parser/remote_fetch.go Uses .github/workflows and .github/agents constants for path classification/security checks.
pkg/parser/mcp.go Uses MCP logs dir constant for Playwright docker volume wiring.
pkg/parser/include_expander.go Uses .github/ prefix constant when normalizing include/import paths.
pkg/parser/import_field_extractor.go Uses .github/ prefix constant when computing import relative paths.
pkg/linters/hardcodedfilepath/hardcodedfilepath.go Fixes self-flagging by extracting pathPrefixes entries to package-level consts.
pkg/constants/path_constants.go Introduces shared .github/ and /tmp/gh-aw/... path constants.
pkg/constants/constants.go Updates GetWorkflowDir() default to use DotGithubWorkflowsDir.
pkg/cli/trial_repository.go Uses shared workflows directory constant when compiling trial workflows.
pkg/cli/shell_completion.go Extracts OS-specific completion paths to file-level constants.
pkg/cli/mcp_tools_privileged.go Uses shared agentic-workflows MCP logs dir constant for output paths.
pkg/cli/logs_run_processor.go Uses shared workflows directory constant when inferring workflow paths.
pkg/cli/includes.go Uses .github/ prefix constant when resolving shared includes.
pkg/cli/git.go Uses shared workflows dir constants for staging and gitattributes entries.
pkg/cli/firewall_policy.go Uses constants.AWFAuditDir to avoid hard-coded audit label suffix.
pkg/cli/fetch.go Uses workflows dir constant in remote fetch fallback prefixes.
pkg/cli/experiments_command.go Uses workflows dir constant when building remote API paths.
pkg/cli/audit.go Uses .github/ prefix constant when resolving workflow display names.
pkg/cli/audit_comparison.go Uses .github/ prefix constant when resolving workflow display names.
pkg/cli/add_package_manifest.go Uses .github/ and workflows dir constants for package path validation/resolution.
.github/workflows/smoke-copilot-aoai-entra.lock.yml Replaces hard-coded /home/runner/... with $HOME-based paths and moves related env to exports within the step script.

Copilot's findings

Tip

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

  • Files reviewed: 58/58 changed files
  • Comments generated: 2

Comment thread pkg/workflow/repo_memory.go Outdated
Comment thread pkg/workflow/compiler_difc_proxy.go Outdated
return []string{
"/tmp/gh-aw/proxy-logs/",
constants.ProxyLogsDir + "/",
"!/tmp/gh-aw/proxy-logs/proxy-tls/",
@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel completed test quality analysis.

No test files were added or modified in PR #38774. This PR only extracts hard-coded file paths to constants across production Go source files — no test files (_test.go, .test.cjs, .test.js) were touched. Test Quality Sentinel analysis skipped.

@github-actions

Copy link
Copy Markdown
Contributor

✅ Design Decision Gate — ADR Verified

ADR reviewed: ADR-38742: Enforce File-Path Constant Usage via a Custom Linter — this PR faithfully implements the recorded decision. No divergence found. 🏗️

i️ This gate triggered on code volume (283 additions in business-logic directories). The work is a mechanical refactor that applies an already-recorded architectural decision, so a new ADR was not generated — minor refactors within an existing, documented pattern do not warrant their own ADR.

📋 Verification Summary

ADR-38742 decided to enforce file-path constants mechanically via the custom hardcodedfilepath analyzer, establishing a single source of truth for shared paths. This PR carries out that decision:

  • Single source of truth — adds pkg/constants/path_constants.go with 26 named path constants, exactly the consequence ADR-38742 anticipated ("reinforces a single source of truth for shared paths").
  • Literal extraction — converts ~50 callsites across pkg/cli/, pkg/parser/, and pkg/workflow/ from inline path literals to the new constants.
  • Linter consistency — fixes the analyzer self-flagging its own pathPrefixes var by extracting the prefix strings to unexported consts, consistent with the linter design described in the ADR.
  • Platform paths/opt/homebrew, /usr/local, /etc/bash_completion.d are kept as file-level constants in shell_completion.go; still a named constant per the decision, and the ADR explicitly notes constants outside *constants* packages are valid.

No code contradicts the stated decision and no architectural scope creep was observed.

🔗 Recommended: link the ADR in the PR body

The governing ADR is on the branch but is not referenced in the PR description. Consider adding a line so the link is durable:

ADR: ADR-38742: Enforce File-Path Constant Usage via a Custom Linter

ADR-38742 is also currently Status: Draft — finalize it to Accepted when the decision is ratified.

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · 69.6 AIC · ⌖ 20 AIC · ⊞ 13.8K ·

@github-actions github-actions Bot mentioned this pull request Jun 12, 2026
@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

@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 /zoom-out and /improve-codebase-architecture — no blocking issues; leaving observations and concrete improvement suggestions.

📋 Key Themes & Highlights

Themes

  • Sub-path constants should compose from parent constants — Go constant expressions make this free; skipping it means a future rename of TmpGhAwDir or MCPLogsDir silently leaves 7+ sub-constants stale.
  • Inconsistent trailing-slash policyDotGithubDir carries a trailing slash but DotGithubWorkflowsDir and DotGithubAgentsDir do not, creating a per-constant memory burden for callers.
  • pathPrefixDotGithub in the linter duplicates constants.DotGithubDir — two independent definitions of the same string will drift.
  • Undocumented behavioral change in the lock file/home/runner$HOME and XDG_CONFIG_HOME/GH_AW_MCP_CONFIG moving from the static env block to inline exports is a portability improvement, but is not mentioned in the PR description.
  • Call-site trailing-slash taxconstants.X + "/" repeated across 7+ call sites; the constants package could own this pattern.

Positive Highlights

  • ✅ The self-referential linter fix (pathPrefixRunnerTempShell etc. as package-level consts) is clever and correct.
  • ✅ The new constants file is well-structured with clear doc comments and logical grouping by category.
  • ✅ 58 files mechanically transformed with no logic changes — the blast radius is wide but the transformation is clean.
  • ✅ The /home/runner$HOME portability improvement in the lock file is a real correctness win for non-standard runners.
  • setup_action_paths.go cleanly separates runner-side (shell + GHA-expr forms) from container-side constants.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 517.8 AIC · ⌖ 13.9 AIC · ⊞ 27.8K

const MCPLogsDir = "/tmp/gh-aw/mcp-logs"

// MCPLogsSafeOutputsDir is the directory for safe-outputs MCP server logs.
const MCPLogsSafeOutputsDir = "/tmp/gh-aw/mcp-logs/safeoutputs"

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.

[/improve-codebase-architecture] MCPLogsSafeOutputsDir and other sub-path constants duplicate the /tmp/gh-aw prefix as a literal — if TmpGhAwDir or MCPLogsDir is ever renamed, these sub-constants silently become stale.

💡 Go constant expressions allow safe composition

Go string constants can be concatenated at compile time:

const MCPLogsSafeOutputsDir   = MCPLogsDir   + "/safeoutputs"
const MCPLogsPlaywrightDir    = MCPLogsDir   + "/playwright"
const MCPConfigLogsDir        = MCPConfigDir + "/logs"
const MCPConfigServersFilePath = MCPConfigDir + "/mcp-servers.json"
const SandboxAgentLogsDir     = TmpGhAwDir   + "/sandbox/agent/logs"
const ProxyLogsTLSCACertPath  = ProxyLogsDir + "/proxy-tls/ca.crt"
const ThreatDetectionLogPath  = TmpGhAwDir   + "/threat-detection/detection.log"

This is valid Go — the compiler evaluates these as constant expressions, so there is no runtime cost and any rename propagates automatically to all derivatives.


// DotGithubDir is the repository-relative path to the .github directory (with trailing slash).
// Use for prefix comparisons and directory operations.
const DotGithubDir = ".github/"

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.

[/zoom-out] DotGithubDir includes a trailing slash (".github/") while DotGithubWorkflowsDir and DotGithubAgentsDir do not. This asymmetry forces callers to know which constants need an appended "/" and which already include it, making subtle double-slash bugs easy to introduce.

💡 Pick one convention and apply it consistently

Option A — remove the trailing slash from DotGithubDir (matches the other two):

const DotGithubDir = ".github"

Callers that do strings.HasPrefix(p, constants.DotGithubDir) would use constants.DotGithubDir + "/" — the same pattern already used for DotGithubWorkflowsDir.

Option B — add trailing slash to all three:

const DotGithubDir          = ".github/"
const DotGithubWorkflowsDir = ".github/workflows/"
const DotGithubAgentsDir    = ".github/agents/"

This removes the append-"/" burden from all call sites.

Either is fine; the key is applying one policy uniformly so readers do not need to check each constant before using it.

const (
pathPrefixRunnerTempShell = "${RUNNER_TEMP}/"
pathPrefixRunnerTempGHAExpr = "${{ runner.temp }}/"
pathPrefixDotGithub = ".github/"

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.

[/zoom-out] pathPrefixDotGithub = ".github/" is a second independent definition of the same string already exported as constants.DotGithubDir. If the .github directory prefix is ever changed in the constants package, the linter will silently continue checking the old prefix while production code uses the updated value.

💡 Options to remove the duplication

Option A — import pkg/constants and reference it directly (no circular dependency; the linter package imports are analysis-only):

const pathPrefixDotGithub = constants.DotGithubDir

Option B — if keeping the linter self-contained is a hard requirement, add a test that asserts:

func TestPathPrefixMatchesConstants(t *testing.T) {
    assert.Equal(t, constants.DotGithubDir, pathPrefixDotGithub)
}

This fails the build if the two definitions drift.

trap 'rm -f "$HOME/.copilot/settings.json"' EXIT
mkdir -p "$HOME/.copilot"
printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > "$HOME/.copilot/settings.json"
export XDG_CONFIG_HOME="$HOME"

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.

[/zoom-out] This diff contains a behavioral change beyond constant extraction: XDG_CONFIG_HOME is moved from a static env: block value (/home/runner) to an inline export XDG_CONFIG_HOME="$HOME", and GH_AW_MCP_CONFIG is moved similarly. On enterprise or container runners where $HOME != /home/runner, this changes where the Copilot CLI resolves its config directory.

The change looks correct and makes the compiled workflow more portable, but it is not mentioned in the PR description. A one-line note in the description (e.g. "also makes Copilot config paths $HOME-relative for portability") would help future git blame readers distinguish intentional behavior changes from mechanical refactors.


// Collect MCP logs.
paths = append(paths, "/tmp/gh-aw/mcp-logs/")
paths = append(paths, constants.MCPLogsDir+"/")

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.

[/improve-codebase-architecture] constants.MCPLogsDir + "/" appears seven times in collectArtifactPaths alone. The "no trailing slash" policy on directory constants creates a recurring call-site tax — every caller must remember to append "/", and a missed append silently breaks artifact upload by pointing at a file instead of a directory.

💡 Two options to remove the burden

Option A — directory-form companion constants (valid Go const expressions):

const MCPLogsDirSlash  = MCPLogsDir  + "/"
const AgentDirSlash    = AgentDir    + "/"
const TmpGhAwDirSlash  = TmpGhAwDir  + "/"

Or simply rename the existing constants to always include the slash for directory-only uses.

Option B — a small helper:

// Dir returns path with a guaranteed trailing slash.
func Dir(path string) string { return strings.TrimRight(path, "/") + "/" }

This makes the intent self-documenting at the call site: constants.Dir(constants.MCPLogsDir).

} else {
// Original args for non-sandbox mode
copilotArgs = []string{"--add-dir", "/tmp/", "--add-dir", "/tmp/gh-aw/", "--add-dir", "/tmp/gh-aw/agent/", "--log-level", "all", "--log-dir", logsFolder}
copilotArgs = []string{"--add-dir", "/tmp/", "--add-dir", constants.TmpGhAwDir + "/", "--add-dir", constants.AgentDir + "/", "--log-level", "all", "--log-dir", logsFolder}

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.

[/zoom-out] "/tmp/" remains as the only hardcoded path literal on this line alongside two freshly-extracted constants.* references. It is exempt from the linter (too short), but the asymmetry is noticeable. A brief inline comment explaining why /tmp/ is intentionally left as a literal (e.g. // /tmp/ is the host root, not gh-aw-specific) would make this clearer for future readers and distinguish it from an oversight.

…tant

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

Copilot AI commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

@copilot run pr-finisher skill

Done in 5b3b251:

  • repo_memory.go:664fmt.Fprintf format string now passes constants.RepoMemoryDir as an argument (%s/%s) instead of concatenating it into the format string.
  • compiler_difc_proxy.go:566 — added ProxyLogsTLSDir constant to pkg/constants/path_constants.go and derived the exclusion pattern from it ("!" + constants.ProxyLogsTLSDir + "/").

make fmt, make lint, and make test-unit all pass locally.

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