Skip to content

Latest commit

 

History

History
255 lines (202 loc) · 19.2 KB

File metadata and controls

255 lines (202 loc) · 19.2 KB

Docs And Release SOT

Public docs

The public documentation site lives in docs-site/ and is built with Astro + Starlight. English is served at the site root, with Korean under /ko, Simplified Chinese under /zh-cn, Traditional Chinese under /zh-tw, Russian under /ru, and Japanese under /ja. docs-site/astro.config.mjs is the locale source of truth.

Manual navigation is defined in docs-site/astro.config.mjs. When adding a public page, update the sidebar and either add localized copies or intentionally accept Starlight fallback behavior.

GitHub Pages

.github/workflows/deploy-docs.yml publishes the docs to:

https://opencodex.me/

The workflow runs on main pushes touching docs-site/** or the workflow itself, builds docs-site, uploads the artifact, and deploys with GitHub Pages.

[Decision Log]

  • 목적과 의도: Serve the public documentation from the memorable first-party opencodex.me domain.
  • 기존 구현 및 제약 조건: The project Pages site was built for lidge-jun.github.io/opencodex, so Astro emitted a /opencodex base path that returns 404 under a root custom domain.
  • 검토한 주요 대안: Keep the GitHub project URL as canonical; redirect the custom domain through Cloudflare; configure the custom domain directly on GitHub Pages and build for the domain root.
  • 선택한 방식: Keep GitHub Actions Pages hosting, configure opencodex.me as the repository custom domain, publish root-relative assets and routes, and retain the default GitHub URL only as GitHub's automatic redirect.
  • 다른 대안 대신 이 방식을 선택한 이유: Direct Pages hosting preserves the existing deployment and HTTPS lifecycle without adding a second proxy or redirect service.
  • 장점, 단점 및 영향: Public links and canonical metadata become stable and branded. DNS and the Pages custom-domain setting are now deployment dependencies, and old hardcoded /opencodex links must not be reintroduced.

Local validation:

cd docs-site
bun install --frozen-lockfile
bun run build

Windows service wrapper and incomplete updates

[Decision Log]

  • 목적과 의도: Prevent a failed npm replacement from making the Task Scheduler wrapper retry missing package files forever.
  • 기존 구현 및 제약 조건: The wrapper deliberately restarts a proxy after runtime crashes, but an absent baked Bun or CLI path cannot recover inside that process. Current updater preflight and stop-first behavior reduce replacement risk but do not provide a transactional restore of npm's package tree and global launchers.
  • 검토한 주요 대안: Keep unconditional five-second retries, add a generic crash ceiling, restore npm directories in-place, or classify only proven missing executable paths as terminal.
  • 선택한 방식: Check the baked Bun and CLI paths before every spawn; log one actionable incomplete-install message and exit with code 3 when either is absent. Preserve the existing retry loop for a child that actually launched and then failed.
  • 다른 대안 대신 이 방식을 선택한 이유: A generic retry ceiling can stop a service after unrelated intermittent crashes, while copying a package directory without matching npm shims, ownership, and lock guarantees is not a safe rollback.
  • 장점, 단점 및 영향: File-less package skeletons no longer produce unbounded service logs or restart churn. The wrapper still recovers ordinary proxy crashes, but repairing an incomplete npm install remains an explicit reinstall plus ocx service repair operation until a verified staged-update design exists.

GitHub workflow map

Workflow Trigger Purpose
.github/workflows/ci.yml pull_request to main/dev, push to main/preview/dev, or manual dispatch when runtime/package paths change Cross-platform runtime/package quality gate. Linux runs the suite as four parallel shards (test 1/44/4) plus a consolidated gates job; macOS runs the full suite. Windows runs the full suite only on a push to main/preview or a manual dispatch — it is the shipping boundary, not the pull-request lane, because it was last to finish in every sampled run at roughly three times the Linux median. The aggregate ci job asserts platform-windows actually succeeded on those boundary events rather than accepting a skip. npm-global-smoke always remains GitHub-hosted because it mutates the global package prefix.
.github/workflows/release.yml Manual dispatch only npm publish/dry-run workflow. It requires the exact GITHUB_SHA to have a successful Cross-platform CI run before publish or dry-run.
.github/workflows/deploy-docs.yml push to main touching docs-site/** or the workflow, or manual dispatch Build and publish the Astro/Starlight docs site to GitHub Pages.
.github/workflows/service-lifecycle.yml pull_request to main/dev and push, both filtered on the service path set (src/service.ts, src/cli.ts, src/cli/index.ts, src/lib/bun-runtime.ts, package.json, bun.lock, the workflow), or manual dispatch Service-lifecycle smoke on three platforms: Linux systemd, macOS launchd, and Windows Scheduled Tasks. Each installs, verifies, stops via ocx stop, and uninstalls. The path list is kept in sync with the release.yml service-gate regex.
.github/workflows/enforce-pr-target.yml pull_request_target (opened, reopened, edited, labeled, unlabeled, ready_for_review, synchronize) plus default-branch status events filtered to successful CodeRabbit statuses The enforce-target gate: rejects pull requests whose head ancestry sits on the main tip while far behind dev, rejects empty or malformed descriptions, requires a GUI screenshot when the title/body mentions gui (immediately waivable with the maintainer-controlled gui-screenshot-waived label; legacy maintainer comments remain compatibility evidence on later PR events), keeps contributor PRs in draft until a four-box readiness checklist is complete, verifies the CI / latest-dev / Codex+CodeRabbit-findings claims (review threads plus current-head CodeRabbit review-body findings outside the diff range), and adds a review-ready status label at the ready moment. CodeRabbit status SHAs must resolve to exactly one open current-head PR before writes. Stacked child PRs targeting another open PR's head skip the wrong-base gate.
.github/workflows/enforce-issue-quality.yml issues (opened, edited, reopened), issue_comment (created, edited), or manual dispatch with an issue number Issue-template compliance gate.
.github/workflows/issue-quality-tests.yml pull_request and push filtered on the issue/PR automation scripts, templates, and their workflows Tests the issue and PR automation scripts themselves, so the gates cannot rot silently.
.github/workflows/issue-triage.yml issues (opened) Duplicate detection and triage labeling for new issues.
.github/workflows/pr-labeler.yml pull_request_target (opened, edited, synchronize, labeled, unlabeled) Type and path labeling plus title sync; labeled/unlabeled let a human override enqueue a fresher run in the per-PR concurrency group.
.github/workflows/react-doctor.yml pull_request (opened, synchronize, reopened, ready_for_review) and push to main; no path filter React-focused static review. Findings fail the job; write-scoped outputs stay disabled, a contract pinned by tests/ci-workflows.test.ts.
.github/workflows/stale-needs-info.yml schedule only (daily 06:15 UTC); deliberately no manual dispatch Closes issues left in needs-info past the grace period. Manual dispatch is omitted so a branch-selected run cannot execute that branch's body with issue write scope.

pull_request_target, issues, and schedule workflows always load from the repository default branch, not from dev. Landing a change to one of them on dev does not change live behavior until it is promoted, so those files follow the promotion model rather than ordinary integration.

The Windows selector is an operational stability control, not a security boundary. A pull request controls the pull_request workflow body and can rewrite an event-name check, repository variable, or selector output. Because this is a public user-owned repository and runner groups are unavailable, the repository setting Fork pull request workflows from outside collaborators: Require approval for all outside collaborators (all_external_contributors) must remain enabled before any self- hosted runner is registered. Maintainers must inspect workflow changes before approving an external run. If that setting cannot be verified, unset OCX_SELF_HOSTED_WINDOWS and deregister the runner; the workflow then fails back to windows-latest rather than exposing a persistent maintainer host.

Docs-only changes intentionally route through the docs workflow instead of the runtime CI gate. If a docs change also edits runtime/package/release files, run the relevant local runtime checks before push and let ci.yml provide the Linux/Windows confirmation. Service-related changes (src/service.ts, src/cli/index.ts, and the rest of the service path set) additionally trigger the service-lifecycle.yml smoke test on all three platforms.

Root README

The root READMEs are the concise product entrypoint. They should explain what opencodex does, how to install/start it, where Codex state is touched, and where the full docs live. Deep implementation invariants belong in structure/, not the README.

Historical docs

docs/ contains investigations and diagnostic notes. Do not treat it as the current public user manual. When an investigation graduates into a maintained invariant, summarize it here under structure/ and link public workflows from docs-site/.

Branch and devlog policy

AGENTS.md and MAINTAINERS.md are authoritative; this section exists so the repository-shape source of truth does not omit the shape of its own history.

  • dev is the single integration branch and the target for ordinary pull requests. main moves only by maintainer-controlled promotion; preview carries the x.y.z-preview.* train. One documented exception: a stacked child PR may target another open PR's head branch as a review workflow, and is retargeted to dev once the parent lands or closes.
  • Bun-native TypeScript on dev is the only runtime line. The former Go native-runtime experiment is retired and archived, and no go/ tree is tracked in this repository; a local go/ directory is untracked leftovers. If native code returns, the expectation is an incremental module landing on dev, not a second full-runtime branch.
  • devlog/ is a tracked directory in this repository — no submodule, no private mirror. Open units live in devlog/_plan/, closed units in devlog/_fin/, and external parity references in devlog/_chase/ (the reference clones themselves are gitignored).
  • The runtime does not consume devlog/, so a contributor who ignores it still builds and runs. Repository checks do read it deliberately: privacy:scan scans it, and tests/repo-hygiene.test.ts enforces the mechanical guards — no tracked 160000 gitlink anywhere, devlog Markdown tracked as ordinary blobs, no .gitmodules, and no open plan carrying an unresolved security verdict on a security-boundary topic. Some unit-scoped release gate scripts resolve their evidence directory from devlog/_plan or _fin as well.
  • Security work in progress does not go in any tracked directory. Scratch space only; only the published outcome — the fix, its regression test, the release note, the advisory once public — reaches the repository.

Maintenance governance

MAINTAINERS.md is the source of truth for current project roles and the review and merge policy. .github/CODEOWNERS declares default reviewers and repeats ownership for authentication, repository automation, release, and governance paths where an explicit security review is required. GitHub repository settings remain the source of truth for actual account permissions and protected-branch enforcement.

[Decision Log]

  • 목적과 의도: Make project ownership and review authority discoverable without exposing credentials or treating a documentation file as an access-control mechanism.
  • 기존 구현 및 제약 조건: Contribution and security docs referred to maintainers generically, while the repository had no maintainer roster or CODEOWNERS policy. GitHub permissions can change independently of the source tree.
  • 검토한 주요 대안: Keep the roster only in GitHub settings; introduce a larger standalone governance charter; list raw GitHub permission levels in the repository.
  • 선택한 방식: Add a concise maintainer roster and merge policy, use CODEOWNERS for review routing, and keep actual permission state authoritative in GitHub settings.
  • 다른 대안 대신 이 방식을 선택한 이유: A two-maintainer project needs clear ownership and sensitive-path review rules but does not yet need a separate governance framework.
  • 장점, 단점 및 영향: Contributors can identify reviewers and merge expectations directly from the repository. The roster must be updated when responsibilities change, and CODEOWNERS still requires branch-protection configuration to enforce approvals.

Package runtime (bundled Bun)

The source runs on Bun, but the published package does not require a user-installed Bun. package.json bin points at bin/ocx.mjs (a Node shim), and the Bun runtime ships as the bun npm dependency (esbuild-style: a tiny main package plus platform-specific @oven/bun-* optionalDependencies, finalized by the dependency's own postinstall: node install.js).

Invariants:

  • bin/ocx.mjs resolves the bundled binary via require.resolve("bun/package.json") and a size gate (>= 1 MB) that rejects the ~450-byte placeholder stub left by --ignore-scripts/pnpm; it then lazy-runs install.js and execs src/cli/index.ts under Bun, propagating exit code and signal.
  • package.json carries "trustedDependencies": ["bun"] so bun install runs the dependency's postinstall, and "engines": { "node": ">=18" } (Bun is no longer a user prerequisite).
  • The plain-Node launcher owns OPENCODEX_BUN_PATH selection before Bun can load project dotenv and stamps the chosen source/path pair. src/service.ts and src/codex/shim.ts bake that already- selected executable (normally the bundled binary, stable under the npm global prefix) into launchd/systemd/Task Scheduler and the Codex autostart shim. Bun-side code never re-selects a durable executable from the post-dotenv environment.
  • Public docs (root READMEs + docs-site installation pages, all locales) state Node 18+ as the only prerequisite. Do not reintroduce "install Bun first" / "bun must be on PATH" guidance for npm users.

Release workflow

Package release is npm-focused. package.json exposes opencodex and ocx, prepublishOnly runs typecheck and GUI build, and scripts/release.ts now runs local typecheck, bun test --isolate tests, and bun run privacy:scan before the version bump, commit/push, Cross-platform CI wait, and GitHub Release workflow dispatch. Docs publishing is separate from npm release publishing.

Release notes

Release notes are rendered OpenAI-Codex-style by scripts/release-notes.ts render inside .github/workflows/release.yml: ## New Features / ## Bug Fixes / ## Documentation / ## Chores / ## Other Changes sections with prefix-free, scope-grouped summary bullets (- Providers: Add X; Add Y (#1, #2)), followed by a ## Changelog section listing every PR as - #N <title> @author; when a comparison baseline exists, that section also includes a compare link. Carried preview changelogs and the since-preview delta feed the same renderer, so stable notes are the aggregate of their preview train. The raw commit dump is intentionally gone — non-PR commits stay reachable via the Full Changelog compare link when that link is available.

The deterministic renderer produces the structure but not curated prose. Maintainers who want the OpenAI-style grouped summaries can run the optional local polish step against the rendered body (needs an OpenAI-compatible API key):

bun scripts/release-notes.ts render ... --out notes.md
bun scripts/release-notes.ts polish --in notes.md --out notes.md

polish rewrites only the category sections, keeps the machine-rendered Changelog verbatim, and fails closed when the rewrite drops, invents, or re-heads any PR reference. It is never called from CI — there is no LLM credential on the runner — so the workflow ships the deterministic body whenever the maintainer skips it.

Release metadata invariants

Every npm release version must map cleanly across four surfaces:

Surface Required state
package.json version equals the release workflow version input.
npm registry @bitkyc08/opencodex@<version> does not exist before publish, then exists after publish with the requested dist-tag.
Git tag v<version> does not exist before publish, then points at the exact release commit.
GitHub Release v<version> does not exist before publish, then is created from the exact release commit.

The release must fail before npm publish if npm, the Git tag, or the GitHub Release already has the requested version. This prevents partial releases where npm is published but GitHub Release creation fails afterward.

Do not force-move public version tags by default. If release metadata is already inconsistent, treat the version as consumed and publish the next unused patch version instead. Only rewrite a public tag after an explicit human decision that the public history rewrite is acceptable.

Manual preflight checks when debugging a release:

npm view @bitkyc08/opencodex@<version> version
git ls-remote origin refs/tags/v<version>
gh release view v<version>

If any of these commands reports an existing artifact for the requested version, stop before publishing. For a non-destructive recovery, choose the next unused patch version and release that version through scripts/release.ts.

Cross-platform CI

.github/workflows/ci.yml is the ordinary quality gate for runtime/package changes. Linux runs the suite in four shards with a separate gates job, macOS runs it whole, and Windows runs whole but only at the shipping boundary (push to main/preview, or manual dispatch). Each lane runs:

bun install --frozen-lockfile
bun x tsc --noEmit
bun test --isolate tests
bun run privacy:scan
bun build scripts/release.ts --target=bun --outdir=.tmp/ci-release-script-check
cd gui && bun install --frozen-lockfile && bun run lint && bun run build
bun run src/cli/index.ts help

and the Node-only global-install smoke path:

npm install
npm run build:gui
npm pack --json > pack.json
npm install -g ./bitkyc08-opencodex-*.tgz
ocx help

The CI intentionally does not build docs, run coverage, or perform remote Ubuntu/RDP smoke tests. Those stay outside the default gate until a concrete regression justifies the extra runtime.

The Release workflow remains manual and publish-focused. Before any dry-run or publish step, it checks that the exact release commit (GITHUB_SHA) already has a successful Cross-platform CI run. This keeps release runs short and makes release a deployment of a verified commit rather than a second CI pipeline.