Non-functional requirements and operational boundaries

This document defines the current non-functional and resilience contract for Copy GitHub Repository. It complements product-contract.md, which remains authoritative for normative product behavior, and troubleshooting-recovery.md, which remains authoritative for operator recovery.

The project deliberately avoids unsupported performance SLAs. Where a hard threshold has not been characterized and adopted, this document says so explicitly.

Status vocabulary

Use these terms consistently:

  • Implemented — behavior exists in source and is part of the current product contract.
  • Automatically tested — deterministic automated tests protect the behavior.
  • Characterized — behavior has been measured under a documented environment/fixture, but is not necessarily an enforceable product limit.
  • Untested at scale — no repeatable evidence establishes a practical boundary yet.
  • Unsupported — deliberately outside current product scope.
  • Gap / planned — a concrete missing resilience control is tracked by an open issue.

A repository being functionally supported does not imply that every possible repository size, history depth, LFS volume, or network condition has been characterized.

Current operational contract

Area Current expectation Evidence / status
Host GitHub.com only for v0.1.0 Implemented and tested; other hosts fail closed.
PowerShell PowerShell 7.4+ Implemented baseline.
OS Windows, macOS, Linux Cross-platform Quality Gate coverage.
Git / gh Required native prerequisites Implemented prerequisite checks/usage.
Git LFS Required for FullHistory and for Snapshot when approved content needs LFS objects Implemented mode-specific behavior.
Planning Planning and -PlanOnly do not mutate GitHub Implemented/tested safety boundary.
Source drift Approved source state is revalidated before first mutation and again in the copy workspace Implemented/tested fail-closed behavior.
Pagination Snapshot historical tag/release discovery requests paginated API results Automatically tested, including 101-result cases.
Large repository size No hard repository-size or history-size limit is currently claimed Untested at scale; characterization tracked in #111.
Branch/tag count No hard supported maximum is currently claimed Untested at scale beyond existing deterministic fixtures; #111.
LFS dataset size No hard supported maximum is currently claimed Untested at scale; #111.
Disk/temp capacity Operations require enough local workspace/temp capacity for clone/copy/LFS work No reliable preflight bound currently enforced; #112.
Memory No fixed memory ceiling is claimed Not characterized as a release limit; #111.
Native process timeout/cancellation The centralized native-process boundary supports an explicit finite timeout and explicit cancellation token, terminates the child process tree, and preserves captured stream evidence Implemented/tested under #109. No arbitrary finite timeout is enabled by default; release treatment of that default remains a #115 decision.
API throttling/retry Side-effect-free GitHub API reads use bounded transient retry/backoff; mutating GitHub API calls are deliberately not automatically replayed Implemented and deterministically tested under #110.
Retry/idempotency Pre-mutation retries are generally safer than retries after mutation; post-mutation attempts must inspect preserved state/evidence Detailed repeated-invocation contract tracked in #113.
Process interruption Do not assume Ctrl+C/session interruption rolls back GitHub state Cross-platform characterization/control tracked in #114.
Concurrency A single repository-copy operation is designed as a serialized orchestration. No multi-writer coordination guarantee is claimed for concurrent attempts against the same repository names. Concurrent same-target operation is not a supported coordination mechanism.
Automatic rollback Not performed Implemented preservation-first recovery contract.

Performance philosophy

Performance is dominated by repository content, history shape, LFS volume, local disk/network performance, GitHub service behavior, authentication, and API latency. The project therefore does not promise a fixed completion time for repository copy operations.

Performance claims should be introduced only when they are:

  1. measured with a documented fixture and environment;
  2. repeatable enough to be meaningful;
  3. tied to a user-visible decision or safety need; and
  4. distinguishable from uncontrolled GitHub/network variability.

Variable timing measurements belong in characterization evidence rather than flaky blocking CI benchmarks.

Scale dimensions to characterize

The meaningful scale dimensions are:

  • default-branch working-tree size and file count;
  • FullHistory reachable commit count/history depth;
  • branch count;
  • tag count;
  • Git LFS object count and total bytes;
  • repository settings/protection result-set size;
  • API pagination depth;
  • local workspace/temp disk consumption;
  • peak native-process/runtime memory where practical;
  • end-to-end stage durations under documented network conditions.

Issue #111 owns representative large-repository/resource characterization before any hard support thresholds are adopted.

Resource exhaustion

Local workspace, clone, archive, and LFS operations can consume substantial disk space. Exact future disk consumption cannot always be predicted reliably from GitHub metadata alone.

Current contract:

  • a local resource failure must not be described as successful execution;
  • if the failure occurs before GitHub mutation, the operation should leave GitHub unchanged;
  • if it occurs after mutation begins, repositories are preserved and recovery semantics apply;
  • users should not infer rollback merely because the local process failed;
  • a reliable preflight check may only block execution when the project can defend the estimate being used.

Issue #112 tracks resource-preflight improvements.

GitHub API pagination and rate limits

Pagination is required wherever a complete result set is part of the contract. Existing Snapshot history tests explicitly verify paginated tag/release collection and distinguish aggregate counts from display limits.

Rate limiting and transient service failures are handled separately from pagination. Side-effect-free GitHub API reads now route through a centralized bounded retry adapter:

  • recognized transient read failures include HTTP 429, 502, 503, 504, GitHub primary/secondary rate-limit and abuse responses, and a narrow set of transient network/TLS/EOF conditions;
  • ordinary authentication, authorization, validation, permission, and not-found failures fail fast unless they also contain a recognized transient condition;
  • read requests use at most three attempts by default with bounded exponential backoff and jitter;
  • numeric Retry-After guidance takes precedence when it is 60 seconds or less;
  • a server-requested delay greater than 60 seconds is surfaced instead of retrying earlier than GitHub instructed;
  • retry exhaustion reports the attempt count through protected diagnostics;
  • optional reads retain normal 404 Not Found as an absent-resource result;
  • GitHub API mutations are never automatically replayed by this mechanism because a failed response does not prove the mutation did not occur.

The detailed authority is github-api-retry-policy.md. After any ambiguous post-mutation failure, inspect GitHub state and recovery evidence before retrying.

Native commands, hangs, and cancellation

Git, GitHub CLI, and Git LFS are native external processes routed through Invoke-CgrNativeCommand. That centralized boundary now supports:

  • an explicit finite TimeSpan timeout when a caller has a defensible bound;
  • an explicit CancellationToken for controlled cancellation;
  • distinct NativeCommandTimedOut and NativeCommandCancelled terminating errors;
  • best-effort termination of the full child process tree on timeout/cancellation;
  • capture of stdout/stderr before the timeout/cancellation error is surfaced, with the captured lines retained in the exception evidence;
  • unchanged ordinary exit-code behavior when the native process exits normally.

The default timeout remains InfiniteTimeSpan. This is deliberate: #107/#111 did not establish a defensible universal duration for clone, push, LFS, or GitHub CLI operations across repository sizes and network conditions. #115 must explicitly decide whether v0.1.0 accepts that default limitation or requires a characterized finite release default.

An explicit cancellation token is not the same as terminal Ctrl+C/signal behavior. Cross-platform host interruption, signal propagation, and recovery-report behavior remain tracked separately by #114.

Timeout/cancellation after GitHub mutation does not imply rollback. The same preservation/recovery rule applies as for other post-mutation failures: inspect the destination/archive and recovery evidence before retrying.

Network and external-service degradation

GitHub, Git transport, DNS, proxies, credential helpers, and local network conditions are external dependencies.

Required safety behavior is more important than automatic persistence:

  • failure before mutation should remain non-mutating;
  • failure after mutation must not be relabeled as if nothing happened;
  • retries must not weaken exact-confirmation, source-state, identity, or verification checks;
  • external-service slowness must not justify duplicate destination/archive mutation;
  • automatic retries are appropriate only where the operation is demonstrably safe/idempotent and bounded.

The implemented GitHub API read retry contract is documented in github-api-retry-policy.md. It does not grant automatic retry semantics to mutation calls or to Git/Git LFS publication operations.

Retry and idempotency boundaries

Before mutation

A corrected prerequisite, authentication, stale-plan, unsupported-host, or other pre-mutation failure can normally be retried by creating/reviewing a fresh plan. The existing safety checks still apply.

After mutation begins

Do not simply repeat the original command without examining current state. Depending on the stage reached:

  • an original destination may already have been archived;
  • a replacement repository may already exist;
  • Git content may be partially or fully published;
  • content verification may have failed;
  • settings/protection may be partially restored.

The recovery report/state model is authoritative for diagnosis. #113 owns the detailed repeated-invocation/idempotency contract.

Concurrency

The orchestration model assumes one intentional operation controls a given source/destination/archive naming set at a time. Concurrent processes attempting to mutate the same logical repositories can invalidate plans and create naming/identity conflicts.

The product’s stale-state and destination/archive checks should fail closed where they observe conflicts, but the project does not claim distributed locking or multi-writer coordination. Avoid parallel copy/replacement attempts against the same repository names.

Cross-platform considerations

The functional contract is intended to be equivalent on supported Windows, macOS, and Linux hosts, and the Quality Gate runs across all three.

Material OS differences may still exist in:

  • filesystem free-space reporting/path semantics;
  • child-process termination and signal propagation;
  • credential-helper/process behavior;
  • shell/console cancellation behavior;
  • filesystem performance.

Any difference that changes mutation or recovery semantics is a product concern, not merely an implementation detail. #114 tracks explicit interruption/signal characterization.

Resilience scenario inventory

These are the non-functional scenario families that downstream product/quality traceability should adopt where requirements become enforceable:

Scenario family Expected observable safety behavior Current status
Paginated result set > one page Complete contract-required results without silently truncating aggregate state Snapshot tags/releases automatically tested.
Large repository/history Complete correctly or fail explicitly without corrupting source/preserved repositories Characterization needed: #111.
Large branch/tag count Preserve/verify FullHistory refs or fail explicitly Characterization needed: #111.
Large LFS volume Transfer/verify required LFS or fail explicitly Characterization needed: #111.
Insufficient disk/temp Prefer detection before mutation where defensible; otherwise fail and preserve partial state Gap: #112.
API rate limiting Retry bounded side-effect-free reads while never hiding ambiguous mutation behind unsafe replay Implemented/tested under #110.
Transient network/API failure Retry only recognized bounded read failures; fail clearly otherwise and preserve mutation evidence Implemented/tested for GitHub API reads under #110.
Authentication expires mid-operation Fail explicitly; mutation status determined by stage/evidence Fail-fast authentication behavior is covered by #110; broader scenario mapping remains #116.
Native command hangs Explicit finite timeout/cancellation is available at the centralized native boundary; timeout/cancel terminates the process tree and fails distinctly Implemented/tested under #109; no finite universal default is claimed.
User/process interruption Never imply automatic rollback; preserve evidence where process remains capable Gap: #114.
Retry after pre-mutation failure New plan/retry may proceed after cause is corrected Existing safety model; formal mapping via #116.
Retry after partial mutation Inspect state/evidence first; no blind replay Formal contract gap: #113.
Concurrent same-target operations No distributed lock guarantee; conflicts should fail closed when detected Avoid as unsupported coordination pattern.

Issue #116 owns integration of adopted resilience scenarios into the canonical SCN-* and #93 evidence model.

Release-readiness treatment

The existence of an identified non-functional gap does not automatically mean v0.1.0 is blocked; it must be explicitly dispositioned rather than ignored.

Issue #115 requires #104 go/no-go review to classify #109-#114 as required, accepted limitation, deferred, or blocker with rationale and evidence. For #109, that disposition now concerns whether the explicitly unbounded default timeout is acceptable for v0.1.0 or whether characterization justifies adopting a finite default; the timeout/cancellation mechanism itself is implemented. #110’s read-only retry mechanism is implemented; #115 may still disposition its bounded constants and mutation no-retry boundary as part of release readiness rather than treating the control as absent.