Skip to main content
Version: Next (unreleased)

File filtering

GrillMyCode detects each repository's languages and frameworks and excludes the files they generate, with no configuration needed in most cases. This page lists every rule and how patterns are matched. For a plain-language overview, see Choosing which files are assessed.

How the decision flow works​

For each file in the changed diff, the action applies this logic in order:

On the left, three lists (always excluded, detected for the repo's stack, and additional_exclude_patterns) combine into the exclude patterns. On the right, each changed file goes through three questions. Binary, with a null byte? If yes, skipped, and nothing brings it back. If no: matches an exclude pattern? If no, assessed. If yes: matches an entry in exclude_pattern_overrides? If yes, assessed, unless it is protected. If no, skipped.

The exclude patterns themselves come from three sources, merged in this order:

SourceInputPurpose
Always-excluded(hardcoded)Lock files, env files, OS noise, source maps, logs, Markdown, editor and IDE settings, diagrams, tabular data — never relevant to assessment
Auto-detected stack(automatic), or stack_templates to name it yourselfBuild artifacts, dependency dirs, generated files for your specific language/framework
Instructor additionsadditional_exclude_patternsAssignment-specific files the auto-detection wouldn't know about

The final exclude list is the union of all three. exclude_pattern_overrides can punch individual files back through after the fact, with one limit: see Protected files.

How auto-detection works​

When the action runs it combines two sources:

  1. GitHub Languages API — queries /repos/{owner}/{repo}/languages with the already-available github_token to identify all languages present in the repository (the same data shown on the repo's language bar). The data covers the whole repository, so the templates it selects apply at the repository root.
  2. Project folder scan — lists every file in the commit being assessed and looks for well-known config files and directories (package.json, pom.xml, Cargo.toml, go.mod, artisan, wp-config.php, grails-app/, project.godot, ProjectSettings/, firebase.json, angular.json, deno.json, .vs/, .idea/, etc.) to detect frameworks and editors. The repository root is always a project folder, and any folder below it that holds one of these files becomes one too (see Monorepos and nested projects). Editor settings themselves are always excluded (see below); an editor detected here only adds its template's extra build-output patterns, such as JetBrains' out/.

Within each project folder, the scan also checks:

  1. Filename suffixes — detects frameworks whose project file includes a variable component by checking whether any entry ends with a known suffix: .xcodeproj / .xcworkspace → Xcode, .uproject → Unreal Engine, .pro → Qt, .ipynb → Jupyter Notebooks.
  2. package.json dependencies — its dependencies and devDependencies are matched against known framework packages (next, @angular/core, svelte, vue, nuxt, @tauri-apps/api, etc.). This catches the correct framework regardless of which config filename convention the project uses.
  3. composer.json dependencies — its require and require-dev entries are matched against known framework packages (laravel/framework, symfony/framework-bundle, drupal/core, codeigniter4/framework, yiisoft/yii2, cakephp/cakephp, WordPress installers like roots/wordpress, etc.). Like the package.json scan, this identifies the framework even when its config files aren't beside composer.json — for example Bedrock relocates wp-config.php, and Symfony Flex projects may not commit symfony.lock.
  4. Gemfile gems — its gem declarations are matched against known framework gems (rails, jekyll, nanoc). This is more reliable than inferring the framework from a Rakefile, since many non-Rails projects ship a Rakefile and many Rails apps don't.
  5. mix.exs dependencies — its dependency tuples (e.g. {:phoenix, "~> 1.7"}) are matched against known framework packages (phoenix). The base Elixir template already covers _build/ and deps/; this adds the Phoenix web artifacts (priv/static/**, tmp/) on top.

Manifests are read from the commit being assessed, so a framework the student added in this push is detected in the same run.

Each detected signal is mapped to one or more github/gitignore templates, or to a set of known artifact paths for frameworks that have no upstream template (e.g. SvelteKit's .svelte-kit/, Nuxt's .nuxt/ and .output/). The action ships with all 300+ templates bundled in the Docker image (kept current via a weekly automated PR).

Template patterns are emitted depth-independently: node_modules/ in the upstream template becomes **/node_modules/**, so a nested frontend/node_modules/ is excluded just as a root-level one is. Only patterns the upstream template anchors, with a leading slash (/vendor/) or a slash in the middle (build/Release), stay anchored — to the project folder that turned the template on. The examples below name each template's patterns in their unprefixed form for brevity.

Examples:

  • A plain Node repo → Node template: node_modules/**, dist/**, coverage/**, etc.
  • A Next.js repo → Node + Nextjs templates: adds .next/** on top of the Node exclusions.
  • A SvelteKit repo → Node template + .svelte-kit/** (no upstream template exists for Svelte).
  • A Angular repo → Node + Angular templates: adds .angular/**.
  • A plain PHP repo with a composer.json → Composer template: vendor/**, composer.phar, etc.
  • A Laravel repo → Composer template + Laravel template: vendor/**, bootstrap/compiled.php, storage cache dirs, etc.
  • A Symfony / Drupal / CodeIgniter / Yii / CakePHP / WordPress repo → Composer template + the matching framework template, detected from the composer.json dependency scan.
  • A Unity repo → Dotnet template + Unity template: Library/**, Temp/**, obj/**, etc.
  • A Godot repo → Godot template: .godot/**, *.import, export presets, etc.
  • A plain Ruby repo with a Gemfile → Ruby template: *.gem, /.bundle/, /vendor/bundle, etc.
  • A Rails repo → Ruby + Rails templates: adds /log/**, /tmp/**, storage/**, public/assets, etc., detected from the Gemfile gem scan.
  • A Jekyll repo → Ruby + Jekyll templates: adds _site/, .jekyll-cache/, .jekyll-metadata.
  • A plain Elixir repo with a mix.exs → Elixir template: _build/**, deps/**, *.beam, erl_crash.dump, etc.
  • A Phoenix repo → Elixir + community/Elixir/Phoenix templates: adds priv/static/**, tmp/**, assets/node_modules, detected from the mix.exs dependency scan.
  • A Python repo → Python template: __pycache__/**, *.pyc, .venv/**, *.egg-info/**, etc.
  • A Jupyter Notebooks repo (any .ipynb file) → Python + community/Python/JupyterNotebooks templates.
  • A Java repo with a pom.xml → Java + Maven templates: target/**, .gradle/**, *.class, etc.
  • A Grails repo (has a grails-app/ directory) → Java + Gradle + Grails templates: adds web-app/WEB-INF/classes, *Db.*, stacktrace.log, etc.
  • A mixed JS + Python repo → gets the union of all matched template sets.
Python frameworks need no per-framework detection

Unlike JavaScript and PHP — where each framework ships its own gitignore template or build directory — Python's upstream Python template is a single comprehensive file that already folds in the artifacts for Django (db.sqlite3, local_settings.py), Flask (instance/**, .webassets-cache), Scrapy (.scrapy), Celery (celerybeat-*), Sphinx/MkDocs, and more. A Django or Flask repo is therefore fully covered the moment Python is detected — there is no requirements.txt / pyproject.toml dependency scan because it would add nothing the Python template doesn't already exclude. The only Python tool caches not in that template — .gradio/** and .dvc/cache/** — are added to the always-excluded list below.

If nothing is detected (for example, the GitHub API is unreachable and no project folder holds a known file) the action falls back to a broad built-in list covering the most common languages, together with the patterns always excluded.

Monorepos and nested projects​

The project doesn't have to sit at the repository root. A repository can hold one app in a subfolder, a frontend/ and a backend/, or one folder per lab, and each project folder gets the templates its own files turn on. A template's anchored patterns are applied inside the folder that turned it on — the meaning the upstream template gives them, relative to the project instead of the repository:

LayoutPatterns applied (examples)
Laravel at the root (artisan)vendor/**, bootstrap/compiled.php
Laravel in myapp/ (myapp/artisan)myapp/vendor/**, myapp/bootstrap/compiled.php
Next.js in frontend/, Laravel in backend/frontend/.next/**, backend/vendor/**
  • A folder inside a dependency or build folder — node_modules/, vendor/, dist/, build/, target/ and the other dependency and build folders in the built-in fallback list — is never a project folder, so a library's own package.json or composer.json adds nothing.
  • Templates selected by the Languages API apply at the repository root only, since language data has no folder. Patterns that match at any depth (**/node_modules/**, **/__pycache__/**) cover every folder however they were selected.
  • Up to 100 project folders are scanned, shallowest first. Beyond that the run logs a warning, and deeper folders get only the patterns that match at any depth; add anything else they commit with additional_exclude_patterns.
  • A folder name containing glob characters is escaped in the pattern, e.g. lab \[1\]/vendor/**. Pass the pattern exactly as the run log shows it to exclude_pattern_overrides.

Using the same stack for every student​

Auto-detection runs in each repository, on every run. Two students' repositories can therefore get different exclude patterns, and a student who adds a project file, such as an empty Cargo.toml, turns its template on. To give every repository the same patterns, name the stack templates yourself in stack_templates:

- uses: NSCC-ITC-Assessment/GrillMyCode@v0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
api_key: ${{ secrets.OPENROUTER_API_KEY }}
stack_templates: 'Node, Nextjs, Composer@api, Laravel@api'

Separate entries with commas or line breaks. Each one names a template, and may add @ and a folder:

EntryWhat it applies
NodeThe Node template at the repository root
Laravel@apiThe Laravel template inside api/, as for a nested project folder: its anchored patterns become api/vendor/**, api/bootstrap/compiled.php and so on

With stack_templates set, a run:

Template names​

A name is a github/gitignore template as the run log's Using gitignore templates lines print it: Node, Python, Global/JetBrains, community/JavaScript/Vue. SvelteKit and Nuxt name the two pattern sets the action keeps for frameworks with no upstream template. Case is ignored.

A language isn't always a template: JavaScript and TypeScript use Node, and C# uses Dotnet. A run warns about a name it doesn't know, says which template a language name means, and leaves that entry out. It doesn't fail, so a workflow keeps running if a template is later removed from the upstream collection. If no entry names a known template, the run uses the fallback list.

Getting the value​

You don't have to write the list by hand:

  • In the Workflow Wizard's Files step, preview a folder holding your solution and tick Use these templates for every student. The Wizard writes the stack it shows into the workflow.
  • The run summary of any run that detected its stack gives the value for that repository, in the Stack templates row of Configuration used by this run. A preview run in a repository holding your solution shows it without generating questions.

When to leave it empty​

  • Students choose their own language or framework. A template you didn't name is never applied, so their build output is assessed unless an always-excluded pattern covers it.
  • Students lay their projects out in folders of their own. A template's anchored patterns apply only in the folder you named; a project in another folder gets only the patterns that match at any depth (**/node_modules/**).

If you're not sure the same stack suits every student's repository, don't set it.

A folder whose name contains a comma, or starts or ends with a space, can't be named in an entry.

Patterns always excluded​

The following are excluded from every run regardless of detected stack:

PatternReason
**/.git/**Git internals
**/.gitignore, **/.gitattributes, **/.gitmodules, **/.mailmap, **/.git-blame-ignore-revsVCS config, not student code
**/.classroom50.yamlClassroom 50 accept-time metadata, written by gh student accept under the student's own commit identity — not student-authored code
.github/workflows/**GitHub Actions workflow files — usually not student-authored code. You can override certain files for evaluation if needed.
**/*.lock, **/package-lock.json, **/yarn.lock, **/pnpm-lock.yaml, **/Pipfile.lock, **/poetry.lockLock files — machine-generated, often enormous
**/*.min.js, **/*.min.cssMinified assets — unreadable by design
**/.env, **/.env.*Environment files — may contain secrets
**/*.tsbuildinfoTypeScript incremental build metadata
**/.gradio/**, **/.dvc/cache/**Python tool caches (Gradio, DVC) not covered by the bundled Python template
**/.DS_Store, **/Thumbs.dbOS-generated noise
**/*.mapSource maps (generated, not authored)
**/*.logLog output
**/*.mdMarkdown docs — pass assignment briefs via assignment_context instead
**/*.svgSVG assets

Editor and IDE settings​

Editor configuration is excluded at any depth, so a project nested one folder down (app/.vscode/) is covered as well as one at the root. This applies even when stack detection falls back to the built-in list.

EditorPatterns
VS Code and its forks**/.vscode/**, **/.vscode-test/**, **/*.code-workspace, **/.history/**
Visual Studio**/.vs/**
JetBrains IDEs, Fleet**/.idea/**, **/*.iml, **/*.ipr, **/*.iws, **/.fleet/**
Eclipse**/.project, **/.classpath, **/.factorypath, **/.settings/**
NetBeans**/nbproject/**
Xcode**/*.xcodeproj/**, **/*.xcworkspace/**, **/xcuserdata/**
Sublime Text, Zed, Nova, Theia**/*.sublime-project, **/*.sublime-workspace, **/.zed/**, **/.nova/**, **/.theia/**
Vim, Emacs**/*.swp, **/*.swo, **/*~, **/.#*, **/#*#, **/.netrwhist, **/Session.vim
AI coding assistants**/.cursor/**, **/.cursorrules, **/.cursorignore, **/.windsurf/**, **/.windsurfrules, **/.claude/**, **/.continue/**
Editor-agnostic**/.editorconfig, **/.devcontainer/**

If an assignment asks students to write one of these — a dev container definition in a containers course, for example — re-include it with exclude_pattern_overrides: '**/.devcontainer/**'.

Diagrams and data​

Students sometimes commit a diagram of what they built, or a data file their program reads. These are plain text, so the binary check lets them through, but questions about a diagram's XML or a CSV's rows don't test the student's code.

PatternReason
**/*.drawio, **/*.diodraw.io / diagrams.net diagrams
**/*.excalidrawExcalidraw drawings
**/*.bpmnBPMN process diagrams
**/*.puml, **/*.plantuml, **/*.mmdPlantUML and Mermaid diagram sources
**/*.csv, **/*.tsvTabular data

Re-include any of them with exclude_pattern_overrides when they are the deliverable (e.g. '**/*.puml' for a UML assignment).

Pattern syntax​

This section describes the patterns you write in additional_exclude_patterns and exclude_pattern_overrides.

Separate patterns with commas or line breaks. A comma inside braces belongs to its pattern, so *.{js,ts} is one pattern.

Patterns use minimatch glob syntax with dot: true, so they match dotfiles. A pattern with no / matches by file name at any depth: the action writes it as **/ plus the pattern, and that is the form the run log's Exclude patterns applied list shows (*.sql appears as **/*.sql). A pattern with a / is matched from the repository root.

The usual ways of writing a path all work:

You writeRead asMeaning
data**/data and **/data/**A plain name or path, with no wildcards, covers the file of that name and everything in a folder of that name
data/**/data/**A trailing / names a folder only
/data/** or ./data/**data/**A leading / or ./ means the repository root
/config.jsonconfig.json and config.json/**Only at the root, not at any depth

Case is ignored in your patterns and in the patterns always excluded: data/** covers Data/, and README.MD is excluded like README.md. The auto-detected template patterns are matched as written, case included. A template pattern with no slash, such as WordPress's index.php, also applies only in the project folder that turned the template on, not to every index.php in the repository.

Two .gitignore habits don't carry over, and the run warns about both:

  • A leading ! doesn't re-include a file. It inverts the pattern, so !src/** excludes everything outside src/. To re-include a file, use exclude_pattern_overrides.
  • A line starting with # isn't a comment. It is read as a file name.
PatternWhat it matches
tests/**Everything inside the top-level tests/ directory
*.pycAny file ending in .pyc in any directory (no /, so any depth)
data/*.csv.csv files directly inside a data/ directory
**/*.test.jsAny .test.js file at any depth
provided_starter/**All files inside provided_starter/
provided_starter/All files inside any folder named provided_starter
config.jsonAny file named exactly config.json at any depth (no /)
src/config.jsonOnly src/config.json specifically (has a /, so anchored)

Worked examples:

File pathPatternMatch?Why
src/index.jssrc/**✅** covers all descendants
src/utils/helper.jssrc/**✅nested path, still under src/
src/utils/helper.jssrc/*.js❌* doesn't cross / — only direct children of src/
tests/unit/auth.test.js**/*.test.js✅** matches any prefix path
tests/fixtures/users.jsontests/fixtures/**✅anchored subfolder glob
e2e/fixtures/users.jsontests/fixtures/**❌anchored — wrong top-level dir
config.jsonconfig.json✅no slash — matches the file name anywhere
src/config.jsonconfig.json✅no slash — any depth
src/config.jsonsrc/config.json✅anchored exact path
lib/config.jsonsrc/config.json❌anchored — path doesn't match
provided_code/solution.pyprovided_code/**✅everything under the dir
src/provided_code/solution.pyprovided_code/**❌anchored — not at root level
src/provided_code/solution.py**/provided_code/**✅leading ** matches any prefix

Additional patterns​

Use additional_exclude_patterns for files specific to your assignment that the auto-detected templates wouldn't know about:

- uses: NSCC-ITC-Assessment/GrillMyCode@v0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
api_key: ${{ secrets.OPENROUTER_API_KEY }}
additional_exclude_patterns: 'data/**, tests/fixtures/**, provided_starter/**'

Common use cases for instructors:

ScenarioPattern
Exclude provided starter filesprovided_starter/**
Exclude test fixtures or sample datatests/fixtures/**, data/**
Exclude a specific config file you providedconfig.json
Exclude everything in a specific subdirectorysrc/lib/**
Exclude all SQL migration files**/*.sql

Override exclude patterns​

If a file is excluded but you want it assessed, use exclude_pattern_overrides. This takes precedence over both auto-detected patterns and additional_exclude_patterns, except for protected files. Entries follow the same pattern syntax and always ignore case.

Each entry can be:

  • A pattern — re-includes all files matching that pattern:
    exclude_pattern_overrides: '**/*.md' # re-includes all Markdown files
  • A specific file path — only that one file passes through:
    exclude_pattern_overrides: 'README.md' # only README.md; other .md files stay excluded

Both forms can be combined:

exclude_pattern_overrides: 'README.md, SOLUTION.md'
When to use overrides vs additional patterns

Use additional_exclude_patterns to narrow what gets assessed (exclude more). Use exclude_pattern_overrides to widen what gets assessed (re-include something excluded by default).

Protected files​

A broad override is usually written to bring back something else, so three kinds of file come back only when an override names them:

KindPatterns
Environment files.env, .env.*
Lock files*.lock, package-lock.json, yarn.lock, pnpm-lock.yaml, Pipfile.lock, poetry.lock
Dependency foldersnode_modules/, bower_components/, vendor/, .venv/, venv/

An override names a protected file when the protected pattern also fits the override itself:

Overridefrontend/.envfrontend/node_modules/…Why
frontend/**Kept outKept outNames neither
*.json—Kept outDoesn't name node_modules
frontend/.env or .envAssessed—Names the file
node_modules/ or **/node_modules/**—AssessedNames the folder
vendor/——Brings back vendor/, but not a .env or lock file inside it; name those too

A file kept out this way stays in the Excluded list, and the run log lists it again under Kept out, with the protected pattern to name.

Previewing in the Workflow Wizard​

The Workflow Wizard's Files step can try your patterns on real files before any run. Under Try the patterns on your files, choose a folder on your computer, or paste a list of paths such as the output of git ls-files. The Wizard then shows:

  • the files that would be assessed, and those left out, grouped by the pattern responsible and where it comes from: an always-excluded list, a stack template, the fallback list, or one of your own patterns;
  • under each pattern box, what each entry does: how many files it leaves out or brings back, whether it matches nothing, and which protected files an override matches without naming;
  • a warning when a stack template leaves out source files, such as src/lib/util.js under Python's **/lib/**, with a Bring these back link that adds the override.

The preview runs the action's own matching code in your browser. Nothing is uploaded: the page reads the file names, the start of each file to tell whether it is binary, and the text of the dependency manifests (package.json, composer.json, Gemfile, mix.exs) in each project folder.

It is an estimate, because some things are only known at run time:

A run…The preview…
Asks the GitHub Languages API for the repository's languagesWorks them out from file names, using the extension lists of Linguist, the library behind that API. They are listed under Languages, each with a tick box. An extension shared by several languages, such as .h, is listed unticked
Detects the stack of each student's repository separately, unless stack_templates names itShows the result for the folder you chose. A student who adds another language or framework gets its patterns too. Tick Use these templates for every student, and every run applies the stack shown instead
Considers only the files changed in the commit range, and skips deleted filesTreats every file as changed
Skips binary files, such as images, whatever the patterns sayDoes the same for a folder, by reading the first 8,000 bytes of each file. A pasted list has no file contents, so its binary files are listed as assessed
Sees only committed filesSees everything in the folder, committed or not. The Wizard in the VS Code extension leaves out what Git ignores. A pasted git ls-files list is exact, but has no file contents, so frameworks named only in a manifest aren't detected

To keep large folders quick, the preview does not open .git or the dependency folders node_modules, bower_components, vendor, .venv and venv. It says whether a rule leaves each of those out. It reads at most 5,000 files, shallowest folders first.

For the exact answer in one repository, preview in a run.

Previewing in a run​

Set preview_only: "true" and a run stops once it has worked out which files it would assess. It does everything a normal run does up to that point, in the real repository: it asks the Languages API, detects the stack, applies your patterns to the files changed in the commit range, reads them, and chooses the context files. Then it writes the run summary and ends, without calling the AI.

The summary is headed GrillMyCode — file preview, no questions generated and shows:

  • how many of the changed files would be assessed, and how many are left out;
  • Files that would be assessed, with the lines added and removed in each;
  • Files left out, one row per pattern or rule, with where the pattern comes from; see Confirming what was applied;
  • a warning when a detected pattern leaves out source files;
  • the settings the run used, including the assignment and codebase context files it would have sent.

A preview run:

  • costs nothing, and works without api_key, so you can try it before the key is set up;
  • produces no questions, issue, PDF, instructor copy or repository labels, and sets no outputs;
  • leaves an assessment from an earlier run as it is, and isn't counted as a submission under a submission tag;
  • adds a notice to the run page saying that it was a preview;
  • succeeds when it finds nothing to assess, whatever fail_on_empty_assessment is set to, and says why in the summary.

The usual way to run one is from the Run workflow form: the Workflow Wizard's Manual runs step puts preview_only on it, ticked by default. Run it in a repository that holds some work, such as your own solution. Don't set preview_only: "true" in the workflow file itself and leave it there: every run would then be a preview, and no student would be assessed.

Because the stack is detected in each repository separately, a preview is exact for the repository it ran in. Another student's repository can still differ, unless you use the same stack for every student.

Confirming what was applied​

The action logs the full exclude list on every run. Look for these lines in the workflow step output:

Detected languages: JavaScript, TypeScript, PHP
Scanned package.json — 42 deps
Scanned api/composer.json — 6 deps
Using gitignore templates: Node, Nextjs, Global/VisualStudioCode
Using gitignore templates in api/: Composer, Laravel
Additional exclude patterns (from input): data/**, tests/fixtures/**
Exclude pattern overrides (re-included): README.md
Exclude patterns applied (94):
**/.git/**
**/.gitignore
**/node_modules/**
...
Excluded 2 file(s):
package-lock.json (**/package-lock.json)
README.md (**/*.md)
Kept out 1 file(s) that an override matched but did not name. Environment files, lock files and dependency folders are re-included only by an override that names them, such as the file's own path:
frontend/.env (**/.env)
1 source file(s) were left out by a pattern detected from the repository, not one from additional_exclude_patterns. If any is work that should be assessed, name it or its pattern in exclude_pattern_overrides:
src/lib/util.js (**/lib/**)
Left out 1 file(s) with no text to assess:
public/logo.png (binary)
Assessing 3 file(s): src/index.js, src/utils.js, src/api.js

There is one Scanned … line per manifest read — package.json, composer.json for PHP, Gemfile for Ruby, mix.exs for Elixir — named by its path. Using gitignore templates: lists the templates applied at the repository root (e.g. Composer, Laravel; Ruby, Rails; Elixir, community/Elixir/Phoenix), and each Using gitignore templates in <folder>/: line lists those of a nested project folder.

With stack_templates set, the Detected languages and Scanned … lines are replaced by one saying the stack was set by stack_templates, and the Using gitignore templates lines list the templates you named.

If a file you expected to be assessed is missing from the Assessing N file(s) line, it was excluded. The Excluded N file(s) list names the first pattern that matched each file, so you can decide whether to add an override for the path or the pattern. The Kept out N file(s) list appears only when an override matched a protected file without naming it. The Left out N file(s) list names the files no pattern matched that are binary or were deleted; see Filtering the files.

Source files a detected pattern left out​

The N source file(s) were left out by a pattern detected from the repository list appears when a pattern nobody wrote down may have removed a student's own work. A file is listed when all of these hold:

  • the pattern that left it out comes from a stack template, whether detected or named in stack_templates, a project file or the fallback list, not from an always-excluded list;
  • its name marks it as source code, by the extension and file name lists of Linguist, the library behind the GitHub Languages API;
  • it isn't a protected file, such as one in node_modules;
  • none of your own additional_exclude_patterns matches it.

src/lib/util.js under the Python template's **/lib/** is the usual case. Build output such as dist/app.js is listed too, because by its name it is source code, and is rightly left out. Treat the list as a prompt to look.

In the run summary​

The run summary's first line counts the changed files assessed and those left out, such as 7 of 19 changed files assessed · 12 left out. Under it, a warning names up to five of the source files a detected pattern left out.

The Files left out section has one row per pattern, largest group first, then one for binary files and one for deleted files. Each row gives the pattern, where it comes from, and a collapsed list of its files. The origin is one of:

FromMeaning
always left out: list nameOne of the always-excluded lists
Name templateA stack template, detected or named in stack_templates; "in folder/" when it applies in a nested project folder
project filesA pattern a project file adds without a template
fallback listThe list used when no stack is detected
additional_exclude_patternsOne of your own patterns

In the Configuration used by this run table, the Stack templates row says whether the stack was detected or set by stack_templates, and lists it as stack_templates entries, ready to copy into a workflow.

A row marked ⚠️ holds at least one of the flagged source files. The lists show at most 1,000 paths in total, shared so that small groups are always listed in full; the run log always has every path. In the Configuration used by this run table, the Codebase context row lists the files sent as context the same way.