Project

Planning and decision records live here so the runtime documentation stays focused on how git-pont works.

Planning

Records

Decision records are intentionally separate from runtime documentation.

Implementation Plan

This plan is intended for another implementation agent. Keep commits small and test each layer before adding providers. The canonical API is in Architecture and Provider Model.

Phase 0: Platform Contract Hygiene

  • Treat the Swift Package as the Swift library, not as the only possible git-pont implementation.
  • Keep core model names, enum raw values, and error cases portable to Kotlin/TypeScript-style representations.
  • Keep provider fixtures JSON-compatible so future kits can reuse them.
  • Avoid Apple-only types in GitPontCore except where Swift requires a standard-library/Foundation representation such as URL, Data, or Date.

Done when:

  • README, Decisions, Architecture, and Provider Model clearly distinguish the shared contract from the Swift library.
  • v1 scope still targets Apple only.

Phase 1: Core

  • Create Swift Package for the Swift library.
  • Add GitPontCore.
  • Define all model types from Provider Model.
  • Define GitProvider, HTTPClient, CredentialStore, ConnectionStore, RetryPolicy, and GitPontError.
  • Define GitAuthenticationProvider, OAuth start/completion models, and refresh contracts.
  • Add InMemoryCredentialStore, InMemoryConnectionStore, and FileConnectionStore.
  • Add URL normalization helpers (strip query/fragment/.git, hex-ref detection, ambiguity candidate generation).
  • Add pagination helper (Link header and x-next-page follower, safety cap, GitList).
  • Add the GitPont facade skeleton with connection resolution rules.
  • Keep Git CLI methods out of the core facade implementation; they are added later by GitPontGitCLI.
  • Add path safety validation (traversal, absolute paths, control characters, empty message).
  • Add test fixture helpers and MockHTTPClient.
  • Add model, URL parser, pagination, and safety tests.

Done when:

  • Package builds.
  • Core tests pass.
  • No provider modules yet.

Phase 2: GitHub

  • Add GitPontGitHub.
  • Implement GitHub URL parser (blob, raw, tree, repo, permalink, ambiguous refs).
  • Implement account validation (GET /user).
  • Implement provider construction with explicit instance registration.
  • Implement GitHub device-flow start/complete where OAuthAppConfig is provided.
  • Implement repository list/get with permission mapping, branch list.
  • Implement file read (including >1 MB blob fallback), directory list.
  • Implement file commit and delete with sha conflict protection.
  • Implement branch create, repository create, fork create (with readiness polling).
  • Implement pull request create (same-repo and fork head).
  • Implement mocked HTTP tests for all operations.

Done when:

  • GitHub module has full mocked tests.
  • GitHub provider supports the Lezin open/commit flow and fork+PR flow.

Phase 3: GitLab

  • Add GitPontGitLab.
  • Implement GitLab.com preset and self-hosted instance config (including path-prefixed base URLs).
  • Implement GitLab URL parser including subgroups, /-/raw/, ambiguous refs.
  • Implement account, project list/get with permission mapping, branch list.
  • Implement provider construction with GitLab.com and explicit self-hosted instance registration.
  • Implement Repository Files API read/update/create/delete with last_commit_id, plus the follow-up read that populates newVersion.
  • Implement tree (directory) listing.
  • Implement branch create, project create, fork (with import_status polling).
  • Implement merge request create (same-project and fork with target_project_id).
  • Add tests for GitLab.com and self-hosted URLs.

Done when:

  • GitLab provider supports file open/commit/delete and MR creation including fork MRs.
  • Self-hosted URL parsing works without hardcoded host assumptions.

Phase 4: Forgejo and Gitea

  • Add GitPontForge.
  • Add the Codeberg preset instance (kind .forgejo).
  • Add custom Forgejo/Gitea instance support.
  • Implement provider construction with Codeberg and explicit custom instance registration.
  • Implement URL parser for /src/branch/..., /src/commit/..., /raw/branch/....
  • Implement account, repo list/get, branch list.
  • Implement file read/write/delete using contents APIs (sha required on update/delete).
  • Implement directory listing.
  • Implement branch create, repository create, fork create.
  • Implement pull request creation (same-repo and fork head).
  • Add version variance handling: map missing endpoints to .unsupportedCapability.

Done when:

  • Codeberg preset works in tests.
  • Custom Forgejo/Gitea instances are configured explicitly.

Phase 5: Auth, Refresh, and Keychain

  • Add provider-neutral auth abstractions and injected OAuthAppConfig.
  • Add PAT/token setup support for all providers.
  • Add GitHub device flow.
  • Add OAuth PKCE for GitLab (access tokens expire in ~2h — refresh is required, not optional) and Forgejo/Gitea where configured.
  • Implement GitAuthenticationProvider.refreshCredential for OAuth providers and .unsupportedCapability for unsupported refresh cases.
  • Implement per-connection refresh serialization (actor) with store-before-release semantics.
  • Add GitPontKeychain.
  • Add refresh race tests and Keychain tests where feasible.

Done when:

  • Apps can store/reuse connections without handling tokens directly.
  • A GitLab OAuth connection survives past token expiry in tests.

Phase 6: Change Submission

  • Implement submitChange on the facade: .directCommit, .branchAndPullRequest, .forkAndPullRequest, .automatic.
  • Implement permission-based strategy selection for .automatic.
  • Implement fork reuse detection and readiness polling.
  • Implement .partialSubmission error reporting.
  • Add orchestration tests per provider (mocked).

Done when:

  • Lezin can save a file it cannot push to, via fork + PR, in a single call.

Phase 7: Git CLI Credentials

  • Add GitPontGitCLI.
  • Add GitCLICredentialContext in GitPontGitCLI and expose gitCredentialContext through a Swift extension on GitPont.
  • Resolve credentials for HTTPS remote URLs (normalize, match instance, resolve connection with preferredConnectionID).
  • Refresh expiring tokens before building the context.
  • Return provider-specific credential helper config and environment.
  • Add tests that token values never appear in arguments.

Done when:

  • GitFolder can replace its GitHub-only token plumbing with GitPont credential context.

Phase 8: App Integration Guides

  • Add small sample integrations for Lezin and GitFolder.
  • Document migration from GitFolder github_token.
  • Document connection setup UI expectations.

Suggested Commit Split

  1. feat(core): add provider-neutral git-pont models and facade
  2. feat(github): support file, branch, repo, fork, and pr operations
  3. feat(gitlab): support cloud and self-hosted instances
  4. feat(forge): support forgejo, gitea, and codeberg preset
  5. feat(auth): add credential/connection stores, refresh, and keychain
  6. feat(core): add change submission orchestration
  7. feat(cli): add git credential contexts
  8. test: add provider contract fixtures
  9. docs: document lezin and gitfolder integrations

Acceptance Criteria

Use this checklist before handing git-pont to Lezin or GitFolder.

Items marked "current" are covered by the checked-in Swift package and local validation. Items marked "release" are still required before declaring v1 complete.

Package

  • Current: swift build passes from the root public SwiftPM manifest.
  • Current: swift test passes from the root public SwiftPM manifest.
  • Current: swift build --package-path libs/swift passes.
  • Current: swift test --package-path libs/swift passes.
  • Current: npm run docs:build passes.
  • Current: npm run validate passes.
  • Current: public top-level types have concise documentation comments.
  • Current: modules are split so apps can depend on only the providers they need.
  • Current: Core has no Apple Keychain dependency.
  • Current: Core has no UI framework dependency.
  • Current: Core has no dependency on GitPontGitCLI; Git CLI support is exposed by importing the Git CLI module.
  • Current: public API matches Architecture signatures, including provider-neutral OAuth start/complete convenience methods.
  • Current: docs distinguish the shared git-pont contract from the v1 Apple Swift Package.
  • Current: repo uses the monorepo layout documented in the README.
  • Current: docs site can be built from docs/content through docs/site/girk.json.
  • Current: core model semantics are portable: no app-specific names, no UI framework types, no Apple-only storage assumptions in GitPontCore.
  • Current: provider response fixtures use portable JSON/data files for representative read/list contract tests, so future kits can reuse the same shape.

URL Parsing

  • GitHub blob/raw/tree/repo URLs parse, including commit permalinks.
  • GitLab blob/raw/repo URLs parse, including subgroups and self-hosted instances.
  • Codeberg/Forgejo/Gitea src/branch, src/commit, and raw URLs parse.
  • Query strings, fragments (#L10), and trailing .git are stripped.
  • Slashed branch names produce .ambiguous and resolve correctly against the branch list.
  • Custom hosts require explicit instance configuration.

Providers

For each of GitHub, GitLab (cloud + self-hosted), and Forgejo/Gitea (incl. Codeberg preset):

  • Account validation loads the current account.
  • Repository list (paginated) and repository metadata with permissions work.
  • Branch list (paginated) works.
  • File read works, including binary files and provider size-limit handling (.fileTooLarge, GitHub blob fallback).
  • Directory listing works.
  • File commit (create and update) works with version conflict protection; newVersion is populated.
  • File delete works with version conflict protection.
  • Branch creation works.
  • Current: branch deletion works and is used by opt-in live write cleanup.
  • Repository creation works.
  • Current: fork creation works.
  • Current: fork creation reuses an existing connected-account fork and confirms newly created forks before returning.
  • Current: pull/merge request creation works, same-repo and cross-fork.
  • Current: file write/delete conflict responses map to .conflict with populated GitConflict.

Change Submission

  • Current: .directCommit, .branchAndPullRequest, .forkAndPullRequest work through the provider-neutral facade.
  • Current: .automatic selects direct commit, branch + PR, or fork + PR from repository permissions and branch protection.
  • Current: partial failures return .partialSubmission with enough context to retry only the missing step.

Auth

  • Current: connections store metadata only; persistence goes through ConnectionStore.
  • Current: credentials are stored through CredentialStore.
  • Current: in-memory stores exist for tests; FileConnectionStore exists for apps.
  • Current: Apple Keychain credential storage exists in GitPontKeychain.
  • Current: PAT/token setup works for all providers.
  • Current: OAuth start/complete models exist in core.
  • Current: GitHub device flow works with an injected client ID.
  • Current: GitLab OAuth PKCE works, and automatic refresh keeps a connection alive past the approximate two-hour token expiry.
  • Current: Forgejo/Gitea browser OAuth works for configured instances.
  • Current: refresh is serialized per connection and persisted before dependent requests proceed.
  • Current: provider refresh hooks are covered by tests.
  • Current: reactive refresh-and-retry after an unexpected provider authentication failure is implemented and tested.
  • Current: multiple accounts on one instance resolve via preferredConnectionID / .ambiguousConnection.
  • Current: OAuth code is isolated so apps can choose whether to use it.

Git CLI

  • Current: HTTPS remote credential context works for GitHub, GitLab.com, self-hosted GitLab, and Codeberg/Forgejo/Gitea.
  • Current: the credential context API is available only when importing GitPontGitCLI.
  • Current: tokens do not appear in command arguments.
  • Current: GIT_TERMINAL_PROMPT=0 is set.
  • Current: expiring tokens are refreshed before the context is built.

Tests

  • Current: URL parser matrix covers the provider forms in the mocked tests, including ambiguity.
  • Current: request construction tests exist for provider write endpoints.
  • Current: response decoding tests exist for provider read endpoints.
  • Current: pagination tests exist for provider list endpoints.
  • Current: conflict mapping tests exist for provider file write/delete endpoints.
  • Current: change submission tests exist for direct, branch + PR, fork + PR, and automatic selection.
  • Current: change submission tests cover partial failures.
  • Current: credential and connection store tests exist.
  • Current: refresh race tests exist.
  • Current: Git CLI credential tests exist.
  • Current: safety tests exist for path traversal, token exposure, delete-without-version, empty commit messages, provider file size limits, delete-is-explicit behavior, and blind-overwrite create/update protection.
  • Current: no default test hits the network.
  • Current: live account smoke tests are opt-in through environment variables only.
  • Current: opt-in live write tests cover disposable branch/file create/read/delete operations and branch cleanup when dedicated test repositories are configured.
  • Release: run the opt-in live write tests against dedicated GitHub, GitLab, and Forgejo/Gitea repositories before tagging v1.

App Readiness

  • Lezin can open a remote file from URL.
  • Lezin can save by committing without local Git.
  • Lezin can propose a change to a repo it cannot push to (fork + PR) in one call.
  • Lezin can check staleness via checkForRemoteChange.
  • GitFolder can resolve provider credentials for git pull and git push.
  • GitFolder can list repositories and branches, and create a new remote repository during folder setup.
  • Existing GitFolder GitHub token migration path is documented.

Decisions

Formerly "Open Questions". All of these are decided; the implementation agent should not revisit them.

Kit Strategy

v1 is a Swift library implemented as a Swift Package for Apple platforms.

Reason:

  • Lezin is native Apple.
  • GitFolder is native macOS.
  • Keychain and OAuth browser flows are Apple-native.

Future platform kits may mirror the same contract for Android, web, and backend use. Nothing in the provider model, error model, URL parsing rules, or authentication concepts may depend on Swift-only behavior that could not be represented in Kotlin, TypeScript, or another mainstream runtime.

The Swift library is the only implementation target for v1. Future libraries or kits must not expand the v1 definition of done.

Repository Location

/Users/silvandiepen/Repositories/_libs/git-pont, as its own Git repository and internal monorepo.

The Swift library lives under libs/swift. Future reusable platform libraries should be added as siblings such as libs/kotlin, libs/typescript, or libs/server. The kits/ namespace is reserved for app/demo kits if needed later. Shared documentation lives under docs/content; the docs site builder lives under docs/site.

OAuth App Ownership

  • Core supports injected OAuthAppConfig only; GitPont ships no default client IDs.
  • Each consuming app (Lezin, GitFolder) registers and provides its own OAuth apps.
  • PAT/token auth works without any OAuth app setup and is the guaranteed path for every provider.

Codeberg

Codeberg is a preset Forgejo GitProviderInstance, not a GitProviderKind. There is exactly one Forgejo/Gitea code path in GitPontForge.

Pull Requests vs Merge Requests

One capability (pullRequestCreate), one request/result type. Wording is a UI concern exposed via GitProvider.changeRequestTerm.

Conflict Protection

Mandatory by default. Updating or deleting an existing file without an expected version requires allowBlindOverwrite = true. This is not configurable globally.

Pagination

Handled inside providers; apps receive complete GitList results with a truncated flag, capped at 30 pages of 100 items. Exposed page tokens are a possible v2 addition, not v1.

SSH

Not in v1, in any module. GitFolder's existing SSH mode stays app-owned.

Naming

Use GitPont in Swift types, repository/package family name git-pont, and Apple product names like GitPontCore.

Use "library" for reusable implementation packages and "kit" only for app/demo bundles or composed starter integrations. Do not name future libraries or kits in code until they exist.

Avoid GitBridge in docs/code to prevent naming drift.

Source of Truth

Where docs disagree, Architecture and Provider Model win. Any API rename must update those two first, then the README and integration docs.