Skip to content

refactor(errors): neverthrow Results with typed problems across validation, config, fold, flows, and the library - #145

Merged
Vivswan merged 7 commits into
mainfrom
refactor/neverthrow
Sep 12, 2026
Merged

Vivswan merged 7 commits into
mainfrom
refactor/neverthrow

Conversation

@Vivswan

@Vivswan Vivswan commented Sep 11, 2026 •

Copy link
Copy Markdown
Owner

One call site, before and after:

// before: src/action/run.ts
const single = await runSingle(api, cfg, io, uploader);
if ("fatal" in single) {
  return failRun(io, single.fatal);
}
return concludeRun(io, { kind: "single", mode: cfg.mode, target: single.target });

// after
return runSingle(api, cfg, io, uploader).match(
  (target) => concludeRun(io, { kind: "single", mode: cfg.mode, target }),
  (problem) => failRun(io, problem),
);
  • One exported union Problem (src/problem.ts, discriminated on code, one member per distinct failure) and one renderer, describeProblem. A library caller branches on the code; the action renders at its edge (failRun), so every printed line, output, summary, and exit code is unchanged. The e2e corpus (217 scenarios) is green with no expectation changed.
  • Converted to Result/ResultAsync: validateSectionShapes, validateSettingsDoc, mergeLayers (the admission walk returns Results; the LayerRefusal throw is gone and its value-free prose lives in describeProblem as the layer-* cases), parseSettingsDoc, readSettingsFile, readLayerFiles, foldLayers, runSingle, runMulti, runMerge, parseConfig, parseReposInput, parseRepoSlug, resolveCentralTargets, discoverRepos, parseRecipient, validateSettings.
  • Async paths use ResultAsync: runMulti and parseConfig are safeTry bodies, discoverRepos is ResultAsync.fromPromise with a typed mapper, runSingle is an andThen chain ending in fromSafePromise.
  • No compat shims: the old {error: string} shapes and ARTIFACT_NEEDS_UPLOADER are gone; requireUploader returns the problem and openReportChannel's backstop throw renders it.
  • Tests assert on code and the structured payload; test/problem.test.ts renders one specimen per member, typed over every code, so a new member fails compilation until it has both a describeProblem case and a specimen.
  • SectionSelection (src/engine/section-selection.ts) replaces the two loose sets requiredSections/onlySections on RepoRunOptions and RunFlowConfig. Its private constructor is reached only through SectionSelection.of({only, required}), which refuses a required section outside a non-empty allowlist as required-sections-excluded, so a library call can no longer pass green on a pair the action's parser would reject. A spread or a literal does not typecheck as one (private fields).
  • The input declarations and parseConfig move from src/action/inputs.ts to src/flows/inputs.ts. parseConfig(read, env) reads through a (name: InputName) => string port and an environment record, so a CLI reuses the same declarations; the action keeps parseActionConfig(), the adapter over @actions/core and process.env. INPUT_DECLS is unchanged byte for byte.
  • Not converted, on purpose: the sections' call()/throwFor() exception channel (the permission policy classifies it), runForRepo's BUG: invariant throws, and checkRepository/applyRepository/renderMergedYaml (no failure path, so a Result would carry an uninhabited error type).
Technical details

Problem members (the code literal, its payload, and where it is minted):

code payload minted by
input-unsupported-value input, value, noun, allowed, fallback parseConfig
input-unknown-sections unknown: [{input, names}], known parseConfig
input-report-key-unused channel parseConfig
input-report-key-missing parseConfig
input-report-key-invalid reason parseConfig
input-rejected-in-merge inputs parseConfig
input-merged-file-missing parseConfig
input-settings-file-empty value parseConfig
input-merge-only inputs, mode parseConfig
input-token-missing parseConfig
input-report-without-redaction parseConfig
input-affiliation-unsupported entry, allowed parseConfig
input-exclude-pattern-invalid pattern parseConfig
input-repository-with-multi parseConfig
input-settings-file-with-multi parseConfig
discovery-filters-without-wildcard filters, targets: single-repo / explicit-repos / repos-dir parseConfig, runMulti
input-defaults-file-without-multi parseConfig
input-settings-file-is-list value, mode parseConfig
input-repository-not-slug value parseConfig
settings-not-mapping source, shape validateSettingsDoc
settings-not-plain-mapping source validateSettingsDoc
settings-unknown-sections source, unknown, known validateSettingsDoc
settings-malformed-sections source, issues validateSectionShapes
yaml-invalid reason parseSettingsDoc
settings-file-unreadable role, path, reason readSettingsFile
layer-cycle layer, site mergeLayers
layer-wrong-shape layer, site, expected, actual, detail? mergeLayers
layer-bad-directive layer, site, actual mergeLayers
layer-no-layering-key layer, site mergeLayers
layer-no-key layer, site, keyField mergeLayers
layer-duplicate-key layer, site, keyField, first, second mergeLayers
required-sections-excluded excluded: SectionKey[] SectionSelection.of (via parseConfig)
artifact-uploader-missing requireUploader
merged-file-is-layer mergedFile, index, layer runMerge (the guard beside the write, where the base put it)
merged-file-unwritable path, reason runMerge
no-targets filteredOut runMulti
repo-slug-invalid value parseRepoSlug
repos-input-wildcard-mixed parseReposInput
repos-input-invalid-entries invalid, duplicated parseReposInput
repos-dir-missing reposDir resolveCentralTargets
repos-dir-unreadable reposDir, reason resolveCentralTargets
repos-dir-invalid-files reposDir, files: CentralFileProblem[] resolveCentralTargets
discovery-request-failed path, status, message, denied discoverRepos
discovery-transport-failed reason discoverRepos
discovery-response-not-a-list path discoverRepos
age-recipient-invalid reason parseRecipient

Changed public signatures (name: old return, new return):

name before after
validateSectionShapes(settings, sourceLabel) {settings} | {error} Result<SettingsFile, ProblemOf<"settings-malformed-sections">>
validateSettingsDoc(doc, sourceLabel, onlySections, io) {settings} | {error} Result<ValidatedSettings, SettingsProblem>
mergeLayers(layers, {layering}) {settings, notices} | {error} Result<{settings, notices}, LayerProblem>
parseSettingsDoc(raw) {doc} | {error} Result<unknown, ProblemOf<"yaml-invalid">>
readSettingsFile(path) {doc} | {error} readSettingsFile(path, role): Result<unknown, ProblemOf<"settings-file-unreadable">>
readLayerFiles(paths) {layers} | {error} Result<Layer[], ProblemOf<"settings-file-unreadable">>
foldLayers(layers, sourceLabel, layering, io) {settings, notices} | {error} Result<{settings, notices}, SettingsProblem | LayerProblem>
runSingle(api, cfg, io, uploader?) Promise<{fatal} | {target}> ResultAsync<SingleOutcome, Problem>
runMulti(api, cfg, io, uploader?) Promise<{fatal: string | null, targets}> ResultAsync<TargetOutcome[], Problem>
runMerge(cfg, io) number (exit code; wrote the summary and outputs itself) Result<FinishedMerge, Problem>; the caller passes the value to concludeMerge
concludeMerge(io, run) run: {layers, mergedFile}, outputs only run: FinishedMerge, writes the merge summary too
failRun(io, message: string) renders a string failRun(io, problem: Problem) renders through describeProblem
parseConfig() (was in src/action/inputs.ts, read @actions/core and process.env) {config} | {error} parseConfig(read: InputReader, env: ConfigEnv): Result<RunConfig, Problem> in src/flows/inputs.ts; the action calls parseActionConfig()
RepoRunOptions / RunFlowConfig requiredSections: Set<SectionKey>; onlySections: Set<SectionKey> sections: SectionSelection (SectionSelection.of({only?, required?}), SectionSelection.ALL)
parseReposInput(raw) {slugs, discover} | {error} Result<{slugs, discover}, ProblemOf<"repos-input-wildcard-mixed" | "repos-input-invalid-entries">>
parseRepoSlug(raw) RepoRef | null Result<RepoRef, ProblemOf<"repo-slug-invalid">>
resolveCentralTargets(reposDir, adminOwner) {targets, warnings} | {error} Result<{targets, warnings}, ProblemOf<"repos-dir-missing" | "repos-dir-unreadable" | "repos-dir-invalid-files">>
discoverRepos(api, filters) Promise<DiscoveryResult | {error}> ResultAsync<DiscoveryResult, DiscoveryProblem>
parseRecipient(recipient) {ok: true} | {ok: false, error} Result<void, ProblemOf<"age-recipient-invalid">>
validateSettings(doc, options?) {ok: true, settings, warnings} | {ok: false, error} Result<{settings, warnings}, SettingsProblem>
missingUploaderProblem(cfg, uploader) string | null renamed requireUploader: Result<void, ProblemOf<"artifact-uploader-missing">>
quoteList exported from src/flows/multi.ts exported from src/problem.ts
ARTIFACT_NEEDS_UPLOADER exported constant removed; describeProblem({code: "artifact-uploader-missing"})

New public names: SectionSelection, INPUT_DECLS, InputDecl, InputName, InputReader, ConfigEnv, RunConfig, MODES, Mode, MERGE_INPUTS, MERGE_REJECTED_INPUTS, FILTER_INPUTS, DEFAULT_PRIVATE_REPOS, parseConfig, Problem, ProblemOf, LayerProblem, SettingsProblem, DiscoveryProblem, CentralFileProblem, SettingsFileRole, TopLevelShape, describeProblem, FinishedMerge, SingleOutcome, concludeMerge. neverthrow itself is not re-exported; consumers import ok/err/Result from it directly.

Remaining throw new in the converted layers, each an invariant and not a run outcome: src/engine/orchestrate.ts (three BUG: throws and the rethrow of a section's classified API failure into the section loop), src/engine/execute.ts and src/engine/diff.ts (the section execution channel's BUG: throws), src/action/artifact.ts (the uploader port rejects without a runtime token; deliverArtifactReport turns it into a warning), src/report/delivery.ts (the backstop for a caller that skipped requireUploader).

Architecture: problem is a new layer in architecture.yml (reads github for RERUN_ADVICE and plain-data for isPlainObject); engine, flows, discovery, report, and library gain the edge; flows also reads plain-data. docs/reference/architecture.md is regenerated.

Reviewer note (Copilot): report a finding only with a concrete input and the wrong output it produces.

@Vivswan Vivswan closed this Sep 11, 2026
@Vivswan Vivswan reopened this Sep 11, 2026
@Vivswan
Vivswan force-pushed the refactor/neverthrow branch from 0b2f6b7 to c9dfe69 Compare September 11, 2026 23:20
@github-actions

github-actions Bot commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

File size check

0 over a hard cap (fails), 90 warning(s).

File Size Tier Cap
.github/scripts/release-pipeline.ts:1 40 comment lines (header) warn 25
.github/scripts/release-pipeline.ts:524 22 comment lines warn 10
.github/scripts/release-pipeline.ts:640 11 comment lines warn 10
.github/scripts/release-pipeline.ts:981 17 comment lines warn 10
.github/scripts/release-pipeline.ts:1065 14 comment lines warn 10
.github/workflows/auto-fix.yml:1 36 comment lines (header) warn 25
.github/workflows/post-green.yml:65 205 chars warn 150
.github/workflows/post-green.yml:66 193 chars warn 150
.github/workflows/post-green.yml:35 13 comment lines warn 10
.github/workflows/update-release.yml:1 32 comment lines (header) warn 25
src/engine/layers.ts:48 11 comment lines warn 10
src/engine/layers.ts:172 11 comment lines warn 10
src/engine/layers.ts:538 13 comment lines warn 10
src/engine/secret-refs.ts:119 11 comment lines warn 10
src/engine/validate.ts:13 13 comment lines warn 10
src/flows/settings-read.ts:16 11 comment lines warn 10
src/report/issue-report.ts:316 13 comment lines warn 10
src/report/issue-report.ts:348 14 comment lines warn 10
src/schema.ts:1 36 comment lines (header) warn 25
src/sections/contract/module.ts:68 12 comment lines warn 10
src/sections/contract/module.ts:95 17 comment lines warn 10
src/sections/contract/module.ts:179 11 comment lines warn 10
src/sections/contract/module.ts:325 11 comment lines warn 10
src/sections/contract/module.ts:370 11 comment lines warn 10
src/sections/contract/module.ts:382 18 comment lines warn 10
src/sections/contract/module.ts:421 11 comment lines warn 10
src/sections/contract/module.ts:515 18 comment lines warn 10
src/sections/contract/module.ts:719 12 comment lines warn 10
src/sections/contract/permissions.ts:113 13 comment lines warn 10
src/sections/environments/nested.ts:63 12 comment lines warn 10
src/sections/environments/nested.ts:173 12 comment lines warn 10
src/sections/environments/nested.ts:228 14 comment lines warn 10
src/sections/environments/schema.ts:144 11 comment lines warn 10
src/sections/shared/repo-secrets.ts:158 13 comment lines warn 10
src/sections/shared/schema-helpers.ts:32 16 comment lines warn 10
test/docs/claims.ts:108 11 comment lines warn 10
test/docs/guides.test.ts:326 13 comment lines warn 10
test/e2e/fuzz.ts:624 18 comment lines warn 10
test/e2e/fuzz.ts:770 14 comment lines warn 10
test/e2e/fuzz.ts:1013 12 comment lines warn 10
test/e2e/fuzz.ts:1450 12 comment lines warn 10
test/e2e/fuzz.ts:1559 12 comment lines warn 10
test/e2e/fuzz.ts:1658 11 comment lines warn 10
test/e2e/fuzz.ts:1685 11 comment lines warn 10
test/e2e/fuzz.ts:1789 11 comment lines warn 10
test/e2e/fuzz.ts:1840 13 comment lines warn 10
test/e2e/gen-support.ts:52 17 comment lines warn 10
test/e2e/gen-support.ts:150 13 comment lines warn 10
test/e2e/generators.ts 2745 lines warn 2560
test/e2e/generators.ts:86 11 comment lines warn 10
test/e2e/generators.ts:225 16 comment lines warn 10
test/e2e/generators.ts:259 19 comment lines warn 10
test/e2e/generators.ts:394 12 comment lines warn 10
test/e2e/generators.ts:809 13 comment lines warn 10
test/e2e/generators.ts:1169 12 comment lines warn 10
test/e2e/generators.ts:1231 11 comment lines warn 10
test/e2e/generators.ts:1252 32 comment lines warn 10
test/e2e/generators.ts:1317 11 comment lines warn 10
test/e2e/generators.ts:1480 13 comment lines warn 10
test/e2e/generators.ts:2295 18 comment lines warn 10
test/e2e/mock/core-paths.ts:75 16 comment lines warn 10
test/e2e/mock/core-paths.ts:173 11 comment lines warn 10
test/e2e/mock/core-paths.ts:242 13 comment lines warn 10
test/e2e/mock/core-paths.ts:373 15 comment lines warn 10
test/e2e/mock/core-paths.ts:418 13 comment lines warn 10
test/e2e/mock/core-paths.ts:588 14 comment lines warn 10
test/e2e/mock/routes.ts:174 17 comment lines warn 10
test/e2e/mock/routes.ts:599 11 comment lines warn 10
test/e2e/mock/state.ts:960 15 comment lines warn 10
test/e2e/mock/state.ts:1122 11 comment lines warn 10
test/e2e/openapi/paths.ts:34 16 comment lines warn 10
test/e2e/openapi/validate.ts:85 23 comment lines warn 10
test/e2e/openapi/validate.ts:416 13 comment lines warn 10
test/e2e/openapi/validate.ts:602 12 comment lines warn 10
test/e2e/oracle.ts:78 13 comment lines warn 10
test/e2e/oracle.ts:121 14 comment lines warn 10
test/e2e/oracle.ts:1032 11 comment lines warn 10
test/e2e/oracle.ts:1117 11 comment lines warn 10
test/e2e/runner.ts:105 11 comment lines warn 10
test/e2e/runner.ts:480 11 comment lines warn 10
test/e2e/runner.ts:540 11 comment lines warn 10
test/e2e/runner.ts:707 15 comment lines warn 10
test/e2e/schema.ts:172 20 comment lines warn 10
test/e2e/schema.ts:296 11 comment lines warn 10
test/e2e/schema.ts:352 22 comment lines warn 10
test/e2e/schema.ts:561 11 comment lines warn 10
test/e2e/schema.ts:670 18 comment lines warn 10
test/scripts/release-pipeline.test.ts 2602 lines warn 2560
test/sections/registry.test.ts:266 13 comment lines warn 10
test/sections/registry.test.ts:467 11 comment lines warn 10

Split the file, wrap the line, shorten or exempt the comment, or list the path in .file-size-allow.local with a # reason.

5 managed file(s) skipped; repo-platform owns them.

@Vivswan
Vivswan force-pushed the refactor/flows-library branch from 649afb3 to 801c7ec Compare September 12, 2026 01:11
Base automatically changed from refactor/flows-library to main September 12, 2026 01:24
Copilot AI balanced review requested due to automatic review settings September 12, 2026 01:41
@Vivswan
Vivswan force-pushed the refactor/neverthrow branch from 8c0e190 to d44b8cb Compare September 12, 2026 01:41
settings-as-code-release added 6 commits September 11, 2026 21:42
The Result library the run flows, validation, config parsing, and the layer fold return their problems through.
…validation, config, fold, flows, and the library

Every failure outside the sections' API-call channel is now a member of one `Problem` union (src/problem.ts, discriminated on `code`) returned as a neverthrow `Result`/`ResultAsync` instead of a thrown domain error or a `{error: string}` shape. `describeProblem` is the one renderer; the action edge (`failRun`, `run()`) renders there, so every line the action prints, every output, summary, and exit code is unchanged (the e2e corpus is green as it was).

Converted: `validateSectionShapes`, `validateSettingsDoc`, `mergeLayers` (the admission walk returns Results; the `LayerRefusal` throw and `describeRefusal` are gone, their prose lives in `describeProblem` as the `layer-*` cases under the same value-free invariant), `parseSettingsDoc`, `readSettingsFile` (now takes the file's role, which selects the advice), `readLayerFiles`, `foldLayers`, `runSingle`, `runMulti` (a `safeTry` async generator; `orTee` flushes the buffered central warnings on the fatal paths), `runMerge` (returns the finished merge; `concludeMerge` writes the summary), `parseConfig` (`safeTry`), `parseReposInput`, `parseRepoSlug`, `resolveCentralTargets`, `discoverRepos` (`ResultAsync.fromPromise`), `parseRecipient`, and the library's `validateSettings`.

`isPlainObject` moves to src/plain-data.ts so the renderer and the fold share it; `quoteList` moves to src/problem.ts. `ARTIFACT_NEEDS_UPLOADER` is gone: `requireUploader` returns the `artifact-uploader-missing` problem and `openReportChannel`'s backstop throw renders it. `architecture.yml` gains the `problem` layer (edges from engine, flows, discovery, report, library; problem reads github and plain-data).

Tests assert on `code` and the structured payload; test/problem.test.ts renders one specimen per member, typed over every code so a new member fails compilation until it has both a case and a specimen.
The fleet's file-size check caps every source and test line at 256 code points. The over-long string and template literals this branch owns (the problem renderer and its specimens, the input declarations, two test expectations) are split into `+` concatenations that reproduce the same characters. One specimen's deliberately misspelled section name is renamed from `tpyo` to `nope`, which the fleet's typos check does not read as a misspelling.
…put declarations into the library

`sectionSelection({only, required})` in src/engine/section-selection.ts is the one construction site of the branded `SectionSelection` that `RepoRunOptions` and `RunFlowConfig` now carry in place of two loose sets. A required section outside a non-empty allowlist is refused there, so the action's parser and a library caller of `checkRepository`/`applyRepository` can no longer disagree: the engine marks an excluded section without attempting it, and the old pair let a library call pass green having proven nothing. The problem is `required-sections-excluded`, rendered to the same line as before.

The input declarations (INPUT_DECLS, InputDecl, InputName, MODES, MERGE_INPUTS, MERGE_REJECTED_INPUTS, FILTER_INPUTS, RunConfig) and `parseConfig` move from src/action/inputs.ts to src/flows/inputs.ts. `parseConfig(read, env)` reads every input through a `(name: InputName) => string` port and the four GITHUB_* keys from an environment record, so a CLI reads the same declarations the action does; the action keeps `parseActionConfig()`, the adapter over `@actions/core` and process.env. INPUT_DECLS is unchanged byte for byte. The docs generator and the tests that import the declarations follow the move.

Tests drive `parseConfig` through a record-backed port instead of the process environment; the adapter keeps one env-driven test; the library test shows the contradictory selection refused before a check can be given one.
…and read layers sequentially

RERUN_ADVICE moves from src/github/api.ts, which only defined it, into src/problem.ts: the renderer is the one place that words it, and importing it from the client module made the leaf problem module load Octokit. multi.ts and the public entry read it from problem.ts; architecture.yml drops the problem -> github edge.

readLayerFiles folds the layer paths sequentially so the first unreadable layer stops the read, as the base did, instead of reading every layer before returning the first problem.
…n's nominal typing

parseConfig trims each value once, at the read port, so an action port (whose getInput trims already) and a CLI port read identically: a whitespace-only token is unset and the environment token applies, instead of a blank token beating it and failing as missing.

test/engine/section-selection.test.ts gains two @ts-expect-error guards asserting that a spread of SectionSelection.ALL and a plain literal are not SectionSelections. The runtime test alone would pass silently if the fields became public; the directives then report unused and fail the build.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Managed guidance and generated documentation contain updates that will be overwritten or direct contributors to the wrong source file.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Refactors error handling to typed neverthrow results, introduces a public library surface, and separates reusable flows from the GitHub Actions adapter.

Changes:

  • Adds typed Problem results and centralized rendering.
  • Moves reusable execution, input, redaction, and delivery logic into src/flows/.
  • Adds SectionSelection, library APIs, and expanded tests.
File summaries
File Description
src/index.ts Defines the public API.
src/problem.ts Defines and renders typed problems.
src/types.ts Adds distributive omit utility.
src/plain-data.ts Centralizes plain-object detection.
src/io.ts Adds collecting and silent IO implementations.
src/github/api.ts Moves shared retry advice.
src/engine/validate.ts Returns typed validation results.
src/engine/orchestrate.ts Uses typed problems and section selections.
src/engine/layers.ts Returns typed layer failures.
src/engine/section-selection.ts Adds validated section selection.
src/engine/secrets.ts Updates secret-reference ownership.
src/engine/secret-refs.ts Moves secret reference logic into the engine.
src/discovery/targets.ts Returns typed slug parsing results.
src/discovery/repos-input.ts Returns structured input failures.
src/discovery/discover.ts Converts discovery to ResultAsync.
src/discovery/central.ts Structures central-target failures.
src/flows/inputs.ts Houses reusable configuration parsing.
src/flows/settings-read.ts Returns typed file and YAML failures.
src/flows/layers.ts Composes layer reads, merges, and validation.
src/flows/merge.ts Implements the reusable merge flow.
src/flows/single.ts Implements the reusable single-repo flow.
src/flows/multi.ts Implements multi-repo orchestration.
src/flows/library.ts Adds library-friendly engine APIs.
src/flows/redact.ts Moves shared redaction behavior.
src/flows/deliver.ts Centralizes flow completion and delivery.
src/flows/summary.ts Moves summary rendering into flows.
src/report/artifact-report.ts Makes artifact upload an explicit port.
src/report/delivery.ts Owns report channel and detail types.
src/action/artifact.ts Implements the Actions artifact adapter.
src/action/inputs.ts Adapts Actions inputs to parseConfig.
src/action/run.ts Delegates execution to reusable flows.
src/action/io.ts Imports IO contracts through the public entry.
src/action/settings-read.ts Removes the old action-local reader.
src/action/merge-mode.ts Removes the old action-local merge flow.
src/sections/actions/actions.test.ts Updates validation-result assertions.
src/sections/actions_secrets/actions_secrets.test.ts Uses typed validation and selections.
src/sections/repository/repository.test.ts Uses centralized problem rendering.
src/sections/secret_scanning_custom_patterns/secret_scanning_custom_patterns.test.ts Updates validation-result handling.
src/sections/webhooks/webhooks.test.ts Uses centralized problem rendering.
test/library/public-surface.test.ts Pins public exports and architecture.
test/io/collecting.test.ts Covers reusable IO implementations.
test/io-fake.ts Removes the superseded test helper.
test/flows/inputs.test.ts Covers reusable configuration parsing.
test/flows/settings-read.test.ts Covers typed YAML parsing.
test/flows/layers.test.ts Covers typed layer reads.
test/flows/merge.test.ts Covers merge results and collisions.
test/flows/single.test.ts Covers single-flow results.
test/flows/library.test.ts Covers public library operations.
test/flows/redact.test.ts Updates moved redaction imports.
test/flows/deliver.test.ts Updates delivery and problem assertions.
test/engine/validate.test.ts Asserts structured validation problems.
test/engine/section-selection.test.ts Covers selection invariants.
test/engine/orchestrate.test.ts Updates engine result assertions.
test/engine/layers.test.ts Pins typed layer failures.
test/engine/secrets.test.ts Uses validated section selections.
test/engine/secret-refs.test.ts Updates moved secret-reference imports.
test/engine/check-purity.test.ts Updates validation and selection usage.
test/discovery/repos-input.test.ts Covers structured repository-input failures.
test/discovery/discover.test.ts Covers typed discovery failures.
test/discovery/central.test.ts Covers structured central-target failures.
test/report/artifact-report.test.ts Updates explicit uploader behavior.
test/report/delivery.test.ts Updates moved delivery types and results.
test/report/issue-report.test.ts Uses centralized problem rendering.
test/action/inputs.test.ts Narrows coverage to the Actions adapter.
test/action/artifact.test.ts Covers the production uploader adapter.
test/action/io.test.ts Updates summary import location.
test/action/action-yml.test.ts Updates declaration imports.
test/published-schema.test.ts Updates validation expectations.
test/private.test.ts Updates private-data importer allowlists.
test/scripts/gen-action-docs.test.ts Updates input declaration import.
test/e2e/schema.ts Updates moved imports.
test/e2e/runner.test.ts Updates recipient-result assertion.
test/e2e/oracle.ts Renders typed validation failures.
test/e2e/oracle.test.ts Renders typed merge failures.
test/e2e/mock/core-paths.ts Updates report-channel import.
test/e2e/generators.ts Uses shared silent IO.
test/e2e/generators.test.ts Handles typed validation failures.
test/e2e/apply-idempotence-proof.ts Updates issue-channel import.
test/docs/settings-examples.ts Uses shared silent IO.
test/docs/readme.test.ts Updates moved constants.
test/docs/guides.test.ts Updates flow imports and result assertions.
package.json Adds neverthrow.
bun.lock Locks the new dependency.
knip.json Registers the public entry.
biome.json Updates the redaction-file override.
architecture.yml Defines library, flows, and problem layers.
docs/reference/architecture.md Regenerates the architecture documentation.
AGENTS.md Updates architecture guidance.
.github/scripts/gen-docs.ts Updates delivery imports.
.github/scripts/gen-action-docs.ts Updates declaration imports.
.github/scripts/changed-sections.ts Makes new cross-cutting paths select all sections.
Review details
  • Files reviewed: 92/94 changed files
  • Comments generated: 3
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread AGENTS.md Resolved
Comment thread .github/scripts/gen-action-docs.ts Resolved
Comment thread src/flows/multi.ts Resolved
Copilot AI review requested due to automatic review settings September 12, 2026 01:52
@Vivswan
Vivswan force-pushed the refactor/neverthrow branch from d44b8cb to 6c438f2 Compare September 12, 2026 01:52

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

The exported SettingsProblem type unintentionally includes settings-file-unreadable, overstating the validation APIs' possible errors.

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 69/70 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread src/problem.ts Resolved
…nt the input hints at src/flows/inputs.ts

The `settings-` prefix match also caught `settings-file-unreadable`, a read failure validateSettingsDoc never returns, so callers had to handle a code they could never see; the four validation codes are now listed. The generated-region hints in action.yml and docs/reference/inputs.md and a comment in multi.ts still named src/action/inputs.ts as the home of the input declarations, which moved to src/flows/inputs.ts.
Copilot AI review requested due to automatic review settings September 12, 2026 02:33

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The public API break lacks a breaking-release signal, and mutable selection sets can bypass their validated invariant.

Review details

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

src/engine/section-selection.ts:49

  • The returned sets expose mutable internal state, so a JavaScript caller can invalidate the selection after of has accepted it.

const s = SectionSelection.ALL;
s.only.add("repository");
runForRepo(..., { sections: SectionSelection.ALL })

That shared default now silently excludes every other section for this and later runs. Return defensive copies (or expose membership methods without returning the sets) so callers cannot mutate onlyKeys or requiredKeys.
src/flows/merge.ts:54

  • The changed public signatures will be released without a breaking-version signal under the current PR title.

Existing callers that expect runMerge(...) to return an exit code now receive a Result, while refactor(errors): ... produces a non-breaking release when the squash title drives release-please (AGENTS.md:14). Mark the PR title as breaking, for example refactor(errors)!: ..., before merge.

  • Files reviewed: 71/72 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@Vivswan Vivswan closed this Sep 12, 2026
@Vivswan Vivswan reopened this Sep 12, 2026
@Vivswan
Vivswan marked this pull request as ready for review September 12, 2026 03:06
@Vivswan
Vivswan merged commit 0998919 into main Sep 12, 2026
41 checks passed
@Vivswan
Vivswan deleted the refactor/neverthrow branch September 12, 2026 03:09
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.

2 participants