Skip to content

Prepare FreeOS 0.1.0 runtime and distribution fixes - #107

Draft
XYAIStudio wants to merge 5 commits into
developfrom
codex/freeos-0.1.0-upstream-foundation
Draft

XYAIStudio wants to merge 5 commits into
developfrom
codex/freeos-0.1.0-upstream-foundation

Conversation

@XYAIStudio

Copy link
Copy Markdown
Owner

Prepare FreeOS 0.1.0 with safer SQLite restart, user-controlled Ollama discovery, malformed-config preservation and cleanup of stale system messages before model calls. Align Python/runtime version metadata and route FreeOS distribution through GitHub Releases without publishing the Octop-compatible package name to PyPI.

Validation so far: backend 3,611 passed / 117 skipped; frontend 1,075 passed; Ruff, mypy, frontend lint/typecheck/format checks and production build passed. The 0.1.0 wheel and sdist have been built and the embedded dashboard/version metadata verified.

This is the development integration PR. Packaged runtime, native desktop and security acceptance are in progress; do not publish a stable release until those checks are complete. v0.0.10 remains available until the replacement release and its assets are verified.

Theater-ahyeon and others added 5 commits October 3, 2026 23:22
* fix(update): checkpoint SQLite WAL before desktop process restart

In desktop/portable mode, POST /api/update/restart replaced the process
image with os.execv without flushing the SQLite WAL. Because SqlitePool
uses WAL mode with a large autocheckpoint window, all writes since the
last checkpoint were lost on restart (#800).

Change _restart_desktop_process to accept the OctopServer, and before
execv run PRAGMA wal_checkpoint(TRUNCATE) and close the pool when the
database is SQLite. The restart endpoint now injects the server via
Depends(get_server).

Fixes #800.

* style(tests): format desktop restart regression cases
… (#738)

* fix(config): never rewrite an unparseable config.json as empty (#730)

Four call sites read config.json, treated a parse failure as an empty dict and
wrote that back, destroying every other key. One trailing comma plus one
`octop run --port 8088` wiped 17 of 18 keys including the whole `database`
section; the next boot silently came up as a greenfield SQLite instance (fresh
setup-wizard password, zero warnings) while the PostgreSQL data sat unreachable.
Losing `bind_host` the same way is what makes #730's reporter unable to reach
the server from the LAN.

A torn write creates the same precondition with no user error: three of the four
sites wrote config.json with a bare write_text, so a crash / OOM / full disk
mid-write left invalid JSON that the next plugin toggle then wiped.

One shared policy in a new leaf helper, infra/utils/json_file.py:

  absent file            -> {} and write (first run, unchanged)
  valid JSON object      -> merge and write (unchanged)
  unparseable/non-object -> raise CONFIG_FILE_CORRUPT, write nothing

The error names the file and the parser line/column but never the contents,
which hold database credentials. Writes now go through write_json_atomic (temp +
os.replace, fd closed on every path, temp removed on failure) so no reader can
observe a torn file.

Sites: cli/commands/run.py (_save_configfile_overrides, plus
_load_configfile_overrides which used to fall back to launch defaults),
infra/agents/plugins/manager.py (_write_global_plugin_enabled = the dashboard
toggle), infra/setup/service.py (persist_bind_options),
infra/agents/plugins/seed.py. `octop run` / `octop service start` report the
error and exit 1 via fail_octop (now NoReturn) instead of a traceback.

Supersedes the pinned "treat a malformed file as empty so the next run rewrites
it cleanly" contract: its fixture (`{not valid json`) had nothing to lose, so
the test could not observe the destruction. Rewritten as
test_run_reports_corrupt_config_cleanly and
test_run_does_not_overwrite_corrupt_config.

Verified end-to-end against a real OctopServer + real PostgreSQL. Before: one
`octop run --port` flipped driver postgresql -> sqlite, the instance reverted to
the first-run wizard, and users=1/agents=1 were orphaned in PG with no warning.
After: exit 1, config.json byte-identical, boot fails loudly instead of
switching backends. Mutation check: restoring the lenient policy in the helper
reddens 9 tests spanning all four sites.

make all green (3299 passed, 16 skipped); mypy --strict clean; dashboard
`npx tsc --noEmit` clean.

* fix(config): name the file when config.json is corrupt (#730)

`load_config` let a bare `json.JSONDecodeError` escape: it reports a line and
column but never says *which* file failed, and this entry point is reached from
boot, backup and several CLI commands — not only `octop run`. Raise a ValueError
naming the path and the parser position, refuse a non-object document, and never
echo file contents (config.json holds database credentials).

Same policy as infra/utils/json_file.py, duplicated here because octop.config
must stay free of infra imports (AGENTS.md §5).

`octop run` already exits cleanly on a corrupt config through resolve_bind; this
covers the remaining entry points.

make all green (3301 passed, 16 skipped); mypy --strict clean.

* fix(config): preserve config.json permissions in atomic writes

Self-review against CONTRIBUTING.md / AGENTS.md §7 caught a regression in the
atomic writer added earlier in this branch: tempfile.mkstemp creates the temp
file 0600, so replacing config.json silently tightened an existing 0644 file to
0600. Under `octop service start --scope system` that runs as root and writes
into the service user's home, a root-owned 0600 config.json is unreadable by the
service user and the unit cannot start; the bare write_text it replaced produced
the umask default.

write_json_atomic now chmods the temp file to the mode of the file being
replaced, or to `0o666 & ~umask` for a new file — matching the two pre-existing
config.json writers (infra/db/rebind.py, infra/backup/auto.py; both
write_text + replace) rather than mkstemp's 0600. POSIX-guarded, so Windows
behaviour is unchanged and no test asserts mode bits off POSIX (AGENTS.md §7).

Also adds the missing CHANGELOG line for the load_config error-message change.

Verified: new file -> 0644, existing 0644 -> 0644, existing 0600 -> 0600.
make all green (3303 passed, 16 skipped); make typecheck-frontend green;
eslint 0 errors (67 pre-existing react-hooks warnings).

Note: `make check-all` is red on develop itself — prettier --check fails on
dashboard/src/pages/Agent/Skills/components/SkillsTabs.tsx exactly as committed
by 83378876. Left untouched here; confirmed pre-existing by checking develop's
version of that file in isolation.

This branch has not been deployed

No deployments
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.

3 participants