Retire .root7 extension from runtime and docs#487
Conversation
Migrate all source databases from v6 (32-bit LE) to v7 (64-bit BE) format. `make dist` now copies databases directly instead of migrating on-the-fly. Key changes: - All databases/ files are now v7 format with .root extension - Makefile dist target: cp instead of --migrate + .root7 rename - run_headless_tests.sh: remove v6 protection and on-the-fly migration - run_integration_tests.sh: use Frontier.root directly (no .root7 migration) - Test code: update all Frontier.root7 references to Frontier.root - v6 test fixtures preserved in tests/fixtures/v6/ for migration tests - Install pending UserTalk verbs (string.isValidUrl, sys.openUrl, webBrowser.openURL, script.newScriptObject, op.newOutlineObject) into both Virgin.root and Frontier.root via protocol layer - Remove skip flags from string_isvalidurl.yaml integration tests - Add planning doc for .root7 extension retirement (next phase) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the .root7 naming convention with in-place v7 at .root: - Migration renames v6 to .v6.root, writes v7 to original .root path - Crash-safe: writes to temp file first, then atomic renames - .v6.root heuristic verifies v7 header before trusting backup exists - --output flag preserves v6 original, writes v7 to specified path Runtime changes (db_format.c, dbverbs.c, main.c): - migrate_internal: rename-to-backup + temp-write + rename-to-final - ensure_database_v7: checks .v6.root sibling + transitional .root7 fallback - Remove odb_ensure_root7_extension, odb_check_root7_exists, ROOT7_EXTENSION_LEN - New odb_ensure_root_extension (ensures .root, converts legacy .root7) - Simplify dbopenverb from ~90 to ~40 lines - Remove .root7 fallback from getodbparam - Collapse 8 interleaved search paths to 4 .root-only paths - --output restoration: proper error handling with strerror(errno) Test/tool updates (11 files): - Update all Frontier.root7 references to Frontier.root - test_migrate_flag.sh: 17/17 tests pass with new naming - Bisect scripts: clean up both .root7 and .v6.root artifacts - cli_positional_root_tests.sh: .root7 tests kept for backward compat Documentation updates (17 files): - CLI_USAGE_GUIDE, TESTING_GUIDE, GETTING_STARTED, DEBUGGING_GUIDE - GUEST_DATABASES, GUEST_DATABASE_ARCHITECTURE, INSTALL.md - CLAUDE.md, agent definitions, test READMEs - All note .root7 is deprecated, .root is the standard extension Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Code Review: Retire .root7 Extension from Runtime and DocsOverall this is a well-structured and carefully executed refactor. The migration strategy (rename v6 to Concern 1: Unit test regression in committed reports (significant)
The PR description acknowledges Concern 2:
|
Fix 2: --output passes explicit output path to migrate_internal - Add explicit_output param to migrate_internal; when set, writes v7 directly to that path without touching the input file - New migrate_32bit_to_64bit_to_output() public API + declaration - Rewrite --output branch in main.c to call this directly instead of the in-place + copy + restore dance Fix 3: Crash recovery when .v6.root exists but .root is missing - In ensure_database_v7, when .v6.root exists but .root is not v7, restore .v6.root back to .root and fall through to re-migrate - Handles crash-between-renames scenario Fix 4: Rename last_backup_path to last_migration_output_path - Static var, getter, and clearer all renamed for semantic accuracy - Updated all 7 callers across db_format, dbverbs, odbengine, tests Fix 6: Document handle leak in fork child (shellsysverbs.c) - Add comment explaining hurl is intentionally not freed in child since _exit() tears down the address space Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Code Review: Retire .root7 Extension (#487)This is a well-scoped, well-documented PR that completes a clean naming convention transition. The core design is sound — placing the v7 database at the original Overall this is solid work. A few issues need attention before merge, ranging from a stale test file with the old convention to a couple of correctness concerns in the new code. Issues That Need Attention1.
|
1. Update db_migration.yaml: remove stale .root7 refs, update comments to reflect .v6.root backup + in-place v7 behavior 2. Add sys.openUrl smoke tests: type check, empty string, wrong arg type 3. Add root7_retirement_plan.md to planning/INDEX.md 4. Add comment explaining conservative path length check in ensure_database_v7 5. Clarify handle dispose ordering comment in sys.openUrl fork pattern 6. Deduplicate: remove migrate_32bit_to_64bit_drop_cancoon (identical to migrate_32bit_to_64bit, no callers) 7. Add log_warn on dbopenverb migration failure fallthrough Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PR 487 Review: Retire .root7 ExtensionOverall this is a well-structured, well-documented cleanup. The atomic rename strategy for in-place migration is correct, the crash-recovery path in ensure_database_v7 is thorough, and the code reduction in dbopenverb (~90 to ~40 lines) is a clear improvement. A few issues to address: Bug: In-place migration prints misleading output message In frontier-cli/main.c, the --migrate in-place path calls: For in-place migration, migration_output equals input (both are the original .root path). The output reads "Migrated: Frontier.root -> Frontier.root" which looks like a no-op. The backup path is derived separately on a subsequent line. Suggested fix: derive v6_backup first and make it the primary message: sys.openUrl naming inconsistency All other sys.* verbs are lowercase (osversion, systemtask, memavail, etc.). openUrl uses camelCase. If intentional, document this in the kernelverbs comment and docs. If not, consider openurl for consistency. sys.openUrl missing integration test The test plan item is unchecked. sys.openUrl has no integration test. A new verb that forks external processes needs at least a minimal smoke test (verify it returns boolean, does not crash with a well-formed URL) before merging. ensure_database_v7: .root7 advisory log only fires in headless mode The advisory log message encouraging users to rename legacy .root7 files is guarded by #if defined(FRONTIER_HEADLESS). Non-headless users with a lingering .root7 file will get the legacy path silently used with no guidance. If retiring .root7 is a goal, this warning should be unconditional or user-visible. TESTING_GUIDE.md clean migration workflow is a no-op on v7 databases The guide now says to run ./frontier-cli/frontier-cli --migrate databases/Frontier.root as the clean migration step. But databases/Frontier.root is already v7 (committed in this PR), so --migrate prints "Already v7 format" and does nothing. The guide should reference tests/fixtures/v6/Frontier.root as the v6 source, or clarify that the committed database is already v7 and no migration step is needed. Minor: backup path re-derived in main.c instead of using the API After ensure_database_v7 fills migration_output, main.c re-derives the .v6.root backup path using inline strrchr logic that duplicates derivation already inside migrate_internal. If the naming convention changes in db_format.c, main.c will not track it automatically. Consider a second output parameter or a cross-reference comment. Things done well
Verdict: The migration runtime work (db_format.c, dbverbs.c, main.c) is solid and can merge with the in-place output message bug fixed. sys.openUrl needs at least one integration test before merging. The doc/guide issues should be addressed in this PR since it is explicitly a doc cleanup. |
Review round 3 fixes: 1. Fix misleading in-place migration output (now shows backup path) 2. Verify sys.openUrl tests exist and are not skipped (added header comment) 3. TESTING_GUIDE.md: reference v6 fixture for migration testing 4. Document openUrl camelCase naming convention (kernelverbs.rc, shellsysverbs.c) 5. Expand .root7 fallback TODO re: non-headless advisory gap 6. Extract db_format_derive_v6_backup_path() shared helper — single source of truth for backup naming, used by migrate_internal and CLI output 7. Add log_warn on dbopenverb migration failure fallthrough The backup path derivation was duplicated in main.c and db_format.c. Now db_format_derive_v6_backup_path() is the canonical implementation, declared in db_format.h and used from both call sites. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PR #487 Code Review: Retire .root7 ExtensionOverall this is a well-structured, clearly-motivated cleanup. The atomic rename strategy for in-place migration is crash-safe and the crash recovery logic in Issues1. Scope creep:
|
Review round 4 fixes: Remove drop_cancoon from migrate_internal and db_format_mode struct: - All callers passed true; the false path (legacy Cancoon rewrite) was dead - Removed from struct, function signature, all initializers across 11 files - Simplifies migrate_internal signature to (db_path, explicit_output) Harden crash recovery in ensure_database_v7: - Verify .v6.root backup is actually v6 before restoring (guard against user-renamed files) - Make last_migration_output_path _Thread_local for consistency Additional cleanup: - MAX_SEARCH_PATHS: 5 → 4 to match actual search path count - test_migrate_flag.sh: rename src_hash_before/after to original_hash/ backup_hash for clarity - Verified no stale db_format_clear_last_backup_path callers remain Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Code Review: PR #487 — Retire .root7 extensionOverall this is a clean, well-motivated simplification. The in-place atomic rename strategy is correct, the Behavioral change in
|
Review round 5 fixes: - dbopenverb: hard-fail with langerrormessage when ensure_database_v7 fails, instead of warn + fallthrough to open as-is - --output: use detect_database_format + db_format_mode_apply for v7 detection instead of raw versionnumber comparison (handles endianness correctly, matches ensure_database_v7 pattern) - Remove dead migration_output buffer in --migrate in-place path (pass NULL to ensure_database_v7 since output path is unused) - Fix comment: .v6.root is 8 chars, reserve 9 bytes (suffix + null) - Add manual recovery instructions to crash-recovery restore failure Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Code Review: Retire .root7 Extension This is a well-executed cleanup. The overall direction is right and the crash-safe rename strategy is solid. A few things worth addressing before merge. ISSUES
In migrate_internal, the rename(db_path, backup_path) call on POSIX silently overwrites an existing .v6.root file. If something caused a partial state where ensure_database_v7 falls through to migrate_internal with a pre-existing .v6.root, the original backup is silently destroyed. Consider logging a warning or refusing unless --force is set.
The old 8-path search list included ~/.frontier/Frontier.root. The new 4-path list drops it. This is a silent breaking change for Linux users who followed previous GETTING_STARTED instructions pointing at ~/.frontier/. The INSTALL.md uninstallation section still references this location, but the runtime no longer discovers it automatically.
The --force check in main.c only applies when output_path is not NULL. In in-place mode (no --output), --force does nothing but no warning is issued. Worth documenting in print_usage.
The test plan leaves "cd tests && make test-integration" unchecked. Since this PR touches dbopenverb auto-migration, getodbparam, and db.open/db.new codepaths, integration tests are the primary verb-level coverage. Worth completing before merge. OBSERVATIONS (no action needed) Crash recovery logic is well-designed. The ensure_database_v7 recovery path (detect partial migration, verify backup is valid v6, restore, re-migrate) is thoughtful. The three-file pattern (write temp, rename v6 to backup, rename temp to final) is correctly implemented. dbopenverb simplification is excellent. Went from 90 to 40 lines by removing the fragile TOCTOU double-check and scattered .root7 fallback code. The old TOCTOU comment acknowledged a race but the fix was incomplete anyway; delegating to ensure_database_v7 is the right call. drop_cancoon field removal is clean. Since all migrations now unconditionally drop Cancoon, the boolean field was dead weight. Removing it simplifies struct initializers throughout. Single source of truth for backup path naming. db_format_derive_v6_backup_path as a shared helper is correct -- the old code had the naming logic duplicated across migrate_internal and callers. TODO comment for .root7 fallback has a date, which is good. Would be stronger with a linked issue. SUMMARY The core migration strategy is sound and the dbopenverb simplification is a real improvement. The silent backup overwrite risk, dropped ~/.frontier search path, and incomplete integration test verification are the items worth resolving before merge. Generated with Claude Code |
Review round 6 fixes: - migrate_internal: refuse to overwrite existing .v6.root if it contains a valid v6 database (protects original backup from accidental destruction on re-migration) - Restore ~/.frontier/Frontier.root as search path #5 (Linux convention, was dropped when collapsing from 8 to 4 paths - Update MAX_SEARCH_PATHS from 4 to 5, fix INSTALL.md search order - --force: warn when used without --output (no effect in in-place mode), update help text to clarify scope Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> EOF )
|
Code Review: PR 487 - Retire .root7 Extension Overall this is a clean, well-structured change. The atomic-ish rename sequence (write temp -> rename v6 -> rename temp-to-final) is a sound approach, and the crash-recovery path in ensure_database_v7 is a nice defensive addition. The simplification to dbopenverb (~90 to ~40 lines) is a real improvement in readability. A few issues worth addressing before merge: Issue 1: ensure_database_v7 duplicates backup path derivation (DRY violation) db_format_derive_v6_backup_path is documented as "the single source of truth for backup naming", but ensure_database_v7 derives the backup path inline using its own strrchr/snprintf instead of calling the helper. The logic happens to match, but having two independent derivations contradicts the stated intent and risks divergence. ensure_database_v7 should call db_format_derive_v6_backup_path directly. Issue 2: _Thread_local for last_migration_output_path - subtle threading concern The variable was previously a plain global. Making it _Thread_local means the migration result is only readable from the thread that set it. Under the GIL model this is safe - current callers all read the value immediately after migrating in the same call stack. But if future code ever calls migration on a worker thread and reads the result on the main thread, it will silently get an empty string. A brief comment explaining the GIL-based reasoning would prevent the next reader from re-questioning this. Issue 3: Backup file opened twice in ensure_database_v7 v6_backup_path is fopened once just to test existence (fclose immediately), then re-opened later to read the header. Reading the header on the first open would eliminate the redundant syscall and the existence-then-open TOCTOU window. Issue 4: Missing strerror(errno) when v6->backup rename fails In migrate_internal, the rename error code is discarded when rename(db_path, backup_path) fails. The cleanup block logs fail_step but not errno. If the rename fails (cross-device link, permissions, etc.) the user gets no diagnostic. A log_error with strerror(errno) before the goto cleanup would help. Issue 5: No recovery guidance when restore-after-failure also fails When rename(temp_path, output_path) fails and the best-effort rename(backup_path, db_path) also fails, the user is left with Frontier.v6.root (the v6 backup) and Frontier.root.v7.tmp (the completed v7 file), but no message explaining the state or how to recover. The ensure_database_v7 crash-recovery path has good diagnostics; this failure path deserves at least a log_error with manual recovery guidance. Minor: planning doc status and naming are stale planning/root7_retirement_plan.md is marked "Status: Planning" but implementation is complete. The doc also describes the output extension as .v7.root throughout, while the actual implementation uses .v6.root for the backup and .root for the v7 output - the inverse of what the doc describes. Correcting the status and naming would prevent confusion for future readers. Positive callouts:
Generated with Claude Code |
Review round 7 fixes: - ensure_database_v7: use db_format_derive_v6_backup_path() instead of inline strrchr/snprintf (DRY — single source of truth) - ensure_database_v7: eliminate redundant fopen of .v6.root — read header on first open, reuse for existence + version check - migrate_internal: add strerror(errno) to v6→backup rename failure - migrate_internal: add recovery guidance when both temp→final rename AND best-effort restore fail (shows file locations + manual fix) - Add GIL threading model comment to _Thread_local declaration - Update planning doc: status → Implemented, note naming divergence Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
test comment to verify posting works |
Code Review: PR 487Overall: Well-scoped and technically solid. The in-place rename strategy (v6 backed up to .v6.root, v7 at original .root path) is the right call. The crash-safe atomic rename sequence is a meaningful improvement over what was there before. Correctness Issues (confirm before merge)
Design Observations (lower risk)
Minor
Positive observations
Summary: Core design and crash-safety reasoning are correct. Items 1-3 are worth confirming before merge. Items 4-6 are lower risk. Item 7 is planning-doc housekeeping. Generated with Claude Code |
Review round 8 fixes:
Correctness:
- migrate_internal: confirm CRITICAL path reaches cleanup (remove temp),
include temp path in CRITICAL log message
- ensure_database_v7: skip .root7 fallback after crash recovery to
prevent using stale .root7 instead of re-migrating restored v6
- dbopenverb: generic error message ("could not be verified as v7")
instead of misleading "migration failed" for non-migration errors
Documentation:
- db_format.h: comprehensive doc comments for migrate_32bit_to_64bit
(destructive, in-place, per-file rollback strategy),
migrate_32bit_to_64bit_to_output (non-destructive), and
ensure_database_v7 (verify-or-migrate with crash recovery)
- last_migration_output_path: clarify dual-writer semantics
(create_root_backup vs migrate_internal)
- planning doc: mark code examples as superseded, note actual naming
Hardening:
- db_format_derive_v6_backup_path: check snprintf truncation, return
false on overflow
- --force note: move from stderr to stdout (prevent script false alarms)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Code Review: PR #487 — Retire .root7 ExtensionOverallThis is a well-executed simplification. Moving from a two-phase Issues1.
|
Review round 9 fixes: Split last_migration_output_path into two separate variables: - last_migration_output_path: written by migrate_internal (v7 output) - last_backup_output_path: written by create_root_backup (backup path) Each has its own accessor/clear function. Eliminates the dual-writer footgun where two functions wrote different semantics to the same slot. Harden backup overwrite check: - Refuse to overwrite .v6.root if header is unreadable (could be valid but corrupted v6 data). Previously treated as "safe to overwrite" which could silently destroy a v6 backup. Cleanup: - Remove double-brace block in ensure_database_v7 crash recovery - Replace unused DEFAULT_SYSTEM_ROOT with SYSTEM_ROOT_FILENAME constant used by all 4 search path constructions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
PR 487 Review: Retire .root7 Extension Good PR overall - the migration logic is substantially cleaner and the crash-safety design is solid. Strengths Atomic-ish rename sequence in migrate_internal: The three-step sequence (write to .v7.tmp, rename v6 to .v6.root, rename temp to final) is the right approach. The rollback path (restore .v6.root on temp-to-final failure) is correctly implemented. The CRITICAL log message in the dual-failure case includes explicit recovery instructions. Single source of truth for backup naming: db_format_derive_v6_backup_path eliminates the previous pattern of computing the same name in multiple places. The bounds-checking (written >= backup_size) is correct. dbopenverb simplification: Reducing ~90 lines to ~40 by delegating to ensure_database_v7 is a real improvement. The TOCTOU double-check present before is now irrelevant under the in-place model, and removing it is correct. drop_cancoon removal: Always dropping Cancoon in v7 migration is the right call now that Phase 1 is complete. Removing the conditional branch cleans up a long-standing complexity. Issues to Address 1. dbopenverb failure path behavioral change (medium) Previously, if odb_detect_database_version failed (e.g., file does not exist), dbopenverb logged and continued without returning an error -- the open attempt would simply fail later. Now, ensure_database_v7 returns false for any unreadable file, and dbopenverb immediately returns false with langerrormessage. This changes behavior for scripts that call db.open(path) on a path that may not exist yet and handle the failure differently. Worth verifying this does not break any existing UserTalk scripts that relied on the old failure mode. A grep for integration tests calling db.open on non-existent paths would confirm. 2. Crash recovery fallthrough when backup is v7 (minor) In ensure_database_v7, when .v6.root exists but has versionnumber >= 7, the fallthrough reaches fopen(db_path). But db_path may not exist (the original v6 was renamed to backup and the v7 write never completed). Migration will fail gracefully but the error log will not mention the .v6.root context, making diagnosis harder. Consider logging the backup path before falling through. 3. Integration tests unchecked in test plan The integration test item is unchecked in the test plan. This is the primary test for UserTalk behavior including db.open and db.new. The PR should not merge with this unchecked given that both dbopenverb and dbnewverb changed. Please complete and confirm. 4. --force semantics with in-place migration The note about --force having no effect in in-place mode prints to stdout (not stderr). If a user passes --force expecting it to overwrite an existing .v6.root backup, they will be surprised. Worth documenting in --help output that --force only applies when --output is specified. 5. Error message in dbopenverb is overly generic The "Can't open the database because it could not be verified as v7 format" message fires for any ensure_database_v7 failure: file not found, header unreadable, migration failed, etc. The previous code had more specific messages per branch. Not a blocker, but worth noting for future UX improvement. Minor Notes
Summary The design is sound and the implementation is careful. The main asks before merging:
Generated with Claude Code |
- sys.openUrl: guard against empty string input (return false without forking), add comment confirming no handle leak on #else path - dbopenverb: improve error message to mention environmental causes (permissions, disk space) not just format verification - migrate_internal: upgrade non-v6 backup overwrite from log_info to log_warn - startupScript.ut: add TODO for window.update try-block inclusion - Update empty string integration test to expect false result - Planning doc: reference both PR #487 and #486 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: Modernize startup flow for headless/CLI compatibility - script.newScriptObject: Normalize CRLF/LF line endings to CR for cross-platform .ut file import compatibility - op.newOutlineObject: Same line-ending normalization - webBrowser.openURL: Three-mode support — GUI (AppleEvent), CLI (macOS `open` command), headless (msg() fallback to console) - startupScript: Wrap browser-open and trialVersionCheck in try blocks (.ut reference only — ODB change requires op verb support in headless) Verified end-to-end: fresh dist → startup → setupFrontier page renders at http://127.0.0.1:5336/setupFrontier with all form fields. 302 unit tests passed, 1846 integration tests passed, 0 failed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Add URL validation guard to webBrowser.openURL Reject non-HTTP URLs before passing to shell commands to prevent command injection via crafted URL strings. The guard validates that the URL begins with "http" (covers both http:// and https://). Addresses review feedback on PR #486. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: Add string.isValidUrl verb, use in webBrowser.openURL New UserTalk verb string.isValidUrl(s) validates URLs by checking for a :// separator and an allowed scheme (http, https, ftp, ftps, file). Rejects javascript:, data:, and other potentially dangerous schemes. webBrowser.openURL now calls string.isValidUrl before passing URLs to shell commands in headless mode. Error message follows Frontier conventions: "Can't open the URL because it isn't a valid URL." Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Reject double-quote injection in URLs, fix Linux xdg-open return - string.isValidUrl: Reject URLs containing double-quote characters to prevent shell injection via quote escaping - webBrowser.openURL: Fix Linux path to mirror macOS pattern — call xdg-open then return(true), instead of returning command stdout Addresses follow-up review feedback on PR #486. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: Add sys.openUrl kernel verb, eliminate shell injection New kernel verb sys.openUrl(s) opens URLs without shell interpolation: - macOS: fork/execlp("open", url) — no shell, no metachar interpretation - Linux: fork/execlp("xdg-open", url) — same approach - Windows: ShellExecuteA — native Win32 API webBrowser.openURL now calls sys.openUrl instead of sys.unixShellCommand in headless mode. This eliminates the entire class of shell injection attacks (backticks, $(), semicolons, etc.) regardless of URL content. string.isValidUrl remains as defense-in-depth (scheme allowlist + double-quote rejection). Also fixes startupScript comment placement (blog-style, newest first). 302 unit tests passed, 1846 integration tests passed, 0 failed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: Add sys.openUrl kernel verb with double-fork, integration tests - sys.openUrl: fork/execlp without shell interpolation, double-fork pattern to avoid zombie processes - webBrowser.openURL: now uses sys.openUrl instead of unixShellCommand - string.isValidUrl: added file:// documentation comment - Integration tests for isValidUrl (skipped pending v6 retirement) - startupScript: moved change comment to top (blog-style) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Retire .root7 extension from runtime and docs (#487) * feat: Make v7 databases the source of truth, retire v6 from databases/ Migrate all source databases from v6 (32-bit LE) to v7 (64-bit BE) format. `make dist` now copies databases directly instead of migrating on-the-fly. Key changes: - All databases/ files are now v7 format with .root extension - Makefile dist target: cp instead of --migrate + .root7 rename - run_headless_tests.sh: remove v6 protection and on-the-fly migration - run_integration_tests.sh: use Frontier.root directly (no .root7 migration) - Test code: update all Frontier.root7 references to Frontier.root - v6 test fixtures preserved in tests/fixtures/v6/ for migration tests - Install pending UserTalk verbs (string.isValidUrl, sys.openUrl, webBrowser.openURL, script.newScriptObject, op.newOutlineObject) into both Virgin.root and Frontier.root via protocol layer - Remove skip flags from string_isvalidurl.yaml integration tests - Add planning doc for .root7 extension retirement (next phase) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: Retire .root7 extension from runtime, update all docs Replace the .root7 naming convention with in-place v7 at .root: - Migration renames v6 to .v6.root, writes v7 to original .root path - Crash-safe: writes to temp file first, then atomic renames - .v6.root heuristic verifies v7 header before trusting backup exists - --output flag preserves v6 original, writes v7 to specified path Runtime changes (db_format.c, dbverbs.c, main.c): - migrate_internal: rename-to-backup + temp-write + rename-to-final - ensure_database_v7: checks .v6.root sibling + transitional .root7 fallback - Remove odb_ensure_root7_extension, odb_check_root7_exists, ROOT7_EXTENSION_LEN - New odb_ensure_root_extension (ensures .root, converts legacy .root7) - Simplify dbopenverb from ~90 to ~40 lines - Remove .root7 fallback from getodbparam - Collapse 8 interleaved search paths to 4 .root-only paths - --output restoration: proper error handling with strerror(errno) Test/tool updates (11 files): - Update all Frontier.root7 references to Frontier.root - test_migrate_flag.sh: 17/17 tests pass with new naming - Bisect scripts: clean up both .root7 and .v6.root artifacts - cli_positional_root_tests.sh: .root7 tests kept for backward compat Documentation updates (17 files): - CLI_USAGE_GUIDE, TESTING_GUIDE, GETTING_STARTED, DEBUGGING_GUIDE - GUEST_DATABASES, GUEST_DATABASE_ARCHITECTURE, INSTALL.md - CLAUDE.md, agent definitions, test READMEs - All note .root7 is deprecated, .root is the standard extension Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Address PR #487 review feedback (items 2,3,4,6) Fix 2: --output passes explicit output path to migrate_internal - Add explicit_output param to migrate_internal; when set, writes v7 directly to that path without touching the input file - New migrate_32bit_to_64bit_to_output() public API + declaration - Rewrite --output branch in main.c to call this directly instead of the in-place + copy + restore dance Fix 3: Crash recovery when .v6.root exists but .root is missing - In ensure_database_v7, when .v6.root exists but .root is not v7, restore .v6.root back to .root and fall through to re-migrate - Handles crash-between-renames scenario Fix 4: Rename last_backup_path to last_migration_output_path - Static var, getter, and clearer all renamed for semantic accuracy - Updated all 7 callers across db_format, dbverbs, odbengine, tests Fix 6: Document handle leak in fork child (shellsysverbs.c) - Add comment explaining hurl is intentionally not freed in child since _exit() tears down the address space Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Address PR #487 review round 2 (items 1-7) 1. Update db_migration.yaml: remove stale .root7 refs, update comments to reflect .v6.root backup + in-place v7 behavior 2. Add sys.openUrl smoke tests: type check, empty string, wrong arg type 3. Add root7_retirement_plan.md to planning/INDEX.md 4. Add comment explaining conservative path length check in ensure_database_v7 5. Clarify handle dispose ordering comment in sys.openUrl fork pattern 6. Deduplicate: remove migrate_32bit_to_64bit_drop_cancoon (identical to migrate_32bit_to_64bit, no callers) 7. Add log_warn on dbopenverb migration failure fallthrough Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Address PR #487 review round 3 + refactor backup path helper Review round 3 fixes: 1. Fix misleading in-place migration output (now shows backup path) 2. Verify sys.openUrl tests exist and are not skipped (added header comment) 3. TESTING_GUIDE.md: reference v6 fixture for migration testing 4. Document openUrl camelCase naming convention (kernelverbs.rc, shellsysverbs.c) 5. Expand .root7 fallback TODO re: non-headless advisory gap 6. Extract db_format_derive_v6_backup_path() shared helper — single source of truth for backup naming, used by migrate_internal and CLI output 7. Add log_warn on dbopenverb migration failure fallthrough The backup path derivation was duplicated in main.c and db_format.c. Now db_format_derive_v6_backup_path() is the canonical implementation, declared in db_format.h and used from both call sites. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: Remove dead drop_cancoon parameter, harden crash recovery Review round 4 fixes: Remove drop_cancoon from migrate_internal and db_format_mode struct: - All callers passed true; the false path (legacy Cancoon rewrite) was dead - Removed from struct, function signature, all initializers across 11 files - Simplifies migrate_internal signature to (db_path, explicit_output) Harden crash recovery in ensure_database_v7: - Verify .v6.root backup is actually v6 before restoring (guard against user-renamed files) - Make last_migration_output_path _Thread_local for consistency Additional cleanup: - MAX_SEARCH_PATHS: 5 → 4 to match actual search path count - test_migrate_flag.sh: rename src_hash_before/after to original_hash/ backup_hash for clarity - Verified no stale db_format_clear_last_backup_path callers remain Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Hard-fail on migration failure, fix --output v7 check, cleanup Review round 5 fixes: - dbopenverb: hard-fail with langerrormessage when ensure_database_v7 fails, instead of warn + fallthrough to open as-is - --output: use detect_database_format + db_format_mode_apply for v7 detection instead of raw versionnumber comparison (handles endianness correctly, matches ensure_database_v7 pattern) - Remove dead migration_output buffer in --migrate in-place path (pass NULL to ensure_database_v7 since output path is unused) - Fix comment: .v6.root is 8 chars, reserve 9 bytes (suffix + null) - Add manual recovery instructions to crash-recovery restore failure Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Guard v6 backup overwrite, restore search path, document --force Review round 6 fixes: - migrate_internal: refuse to overwrite existing .v6.root if it contains a valid v6 database (protects original backup from accidental destruction on re-migration) - Restore ~/.frontier/Frontier.root as search path #5 (Linux convention, was dropped when collapsing from 8 to 4 paths - Update MAX_SEARCH_PATHS from 4 to 5, fix INSTALL.md search order - --force: warn when used without --output (no effect in in-place mode), update help text to clarify scope Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> EOF ) * fix: DRY backup path, eliminate redundant fopen, add error diagnostics Review round 7 fixes: - ensure_database_v7: use db_format_derive_v6_backup_path() instead of inline strrchr/snprintf (DRY — single source of truth) - ensure_database_v7: eliminate redundant fopen of .v6.root — read header on first open, reuse for existence + version check - migrate_internal: add strerror(errno) to v6→backup rename failure - migrate_internal: add recovery guidance when both temp→final rename AND best-effort restore fail (shows file locations + manual fix) - Add GIL threading model comment to _Thread_local declaration - Update planning doc: status → Implemented, note naming divergence Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Skip stale .root7 after recovery, document rollback strategy Review round 8 fixes: Correctness: - migrate_internal: confirm CRITICAL path reaches cleanup (remove temp), include temp path in CRITICAL log message - ensure_database_v7: skip .root7 fallback after crash recovery to prevent using stale .root7 instead of re-migrating restored v6 - dbopenverb: generic error message ("could not be verified as v7") instead of misleading "migration failed" for non-migration errors Documentation: - db_format.h: comprehensive doc comments for migrate_32bit_to_64bit (destructive, in-place, per-file rollback strategy), migrate_32bit_to_64bit_to_output (non-destructive), and ensure_database_v7 (verify-or-migrate with crash recovery) - last_migration_output_path: clarify dual-writer semantics (create_root_backup vs migrate_internal) - planning doc: mark code examples as superseded, note actual naming Hardening: - db_format_derive_v6_backup_path: check snprintf truncation, return false on overflow - --force note: move from stderr to stdout (prevent script false alarms) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: Split dual-writer global, refuse unreadable backup overwrite Review round 9 fixes: Split last_migration_output_path into two separate variables: - last_migration_output_path: written by migrate_internal (v7 output) - last_backup_output_path: written by create_root_backup (backup path) Each has its own accessor/clear function. Eliminates the dual-writer footgun where two functions wrote different semantics to the same slot. Harden backup overwrite check: - Refuse to overwrite .v6.root if header is unreadable (could be valid but corrupted v6 data). Previously treated as "safe to overwrite" which could silently destroy a v6 backup. Cleanup: - Remove double-brace block in ensure_database_v7 crash recovery - Replace unused DEFAULT_SYSTEM_ROOT with SYSTEM_ROOT_FILENAME constant used by all 4 search path constructions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Address PR #486 review — handle guard, error messages, empty URL - sys.openUrl: guard against empty string input (return false without forking), add comment confirming no handle leak on #else path - dbopenverb: improve error message to mention environmental causes (permissions, disk space) not just format verification - migrate_internal: upgrade non-v6 backup overwrite from log_info to log_warn - startupScript.ut: add TODO for window.update try-block inclusion - Update empty string integration test to expect false result - Planning doc: reference both PR #487 and #486 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Check backup path derivation return, remove dead GetHandleSize - migrate_internal: check db_format_derive_v6_backup_path return value and goto cleanup on failure (prevents proceeding with truncated path) - sys.openUrl: remove dead GetHandleSize(hurl) == 0 check (after enlargehandle adds null byte, handle is always >= 1 byte; empty URL is caught by url[0] == '\0') Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: Regenerate unit test OPML from full build (302 tests, 30 executables) Previous reports were generated from worktree partial build (152 tests, 17 executables). Full build produces 302 tests across 30 executables. 258 pass, 44 fail (all table_operations_integration — pre-existing). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: sys.openUrl error semantics, endianness comments, planning doc sys.openUrl behavior changes: - Empty URL now triggers scriptError (was: return false). Invalid input should kill the script, not silently succeed. - Second fork failure: first child exits non-zero, parent checks waitpid status and returns false (was: always return true). - Valid URL that dispatches successfully returns true. The return value means "dispatched without error", not "browser opened" (grandchild runs independently after double-fork). Add endianness comments to raw versionnumber comparisons in migrate_internal and ensure_database_v7 — documents why "< 7" and ">= 7" work correctly on little-endian hosts. Strengthen planning doc superseded notice — code examples show .v7.root convention that was not implemented. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Fix 44 table_operations_integration failures, 302/302 tests pass Two issues: - table_operations_integration eval_cli() used 2>&1 which captured stderr warnings ("tablesavesystemtable failed") into test output, causing string comparison mismatches. Changed to 2>/dev/null to match table_verb_tests.c pattern. - save_migration_tests: leftover .v6.root backup from previous run triggered the new "refuse to overwrite v6 backup" safety guard. Added cleanup of .v6.root before each test run. Result: 302/302 tests pass across 30 executables (was 258/302). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Audit .root7 in YAML tests, extract v6 header helper, doc cleanup Address all remaining PR #486 review items: YAML test audit (6 files, ~60 replacements): - Update stale .root7 filenames in db.new()/db.open() paths to .root in db_verbs, filemenu_verbs, persistence_save_tests, error_recovery_concurrency_tests, dist_stability, repl_commands - Leave backward-compat tests and non-db.new paths unchanged Extract db_format_is_v6_header() helper (db_format.h, db_format.c): - Encapsulates LE-specific versionnumber < 7 check with endianness documentation. Replaces 6 raw comparisons in migrate_internal and ensure_database_v7. Other fixes: - shellsysverbs.c: document bserror-only pattern in #else stub is intentional and consistent with winshellcommand stub - db_format.c: add GIL/ADR-014 comment to last_backup_output_path - planning doc: add section-level superseded warnings to 7 sections Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
.root7file extension from the Frontier runtime, tests, and documentation.v6.rootand writes v7 to the original.rootpath (crash-safe: temp file → atomic renames)dbopenverbfrom ~90 to ~40 lines by removing scattered.root7fallback logic.root7still recognized for backward compatibility (positional args, transitional fallback with dated TODO)Depends on: PR #486 (feature/setup-frontier-startup) — that PR migrates source databases to v7 format; this PR retires
.root7from the runtimeChanges
Runtime (3 files):
db_format.c:migrate_internaluses rename-to-backup + temp-write + rename-to-final;ensure_database_v7checks.v6.rootsibling with v7 header verification + transitional.root7fallbackdbverbs.c: Removeodb_ensure_root7_extension,odb_check_root7_exists,ROOT7_EXTENSION_LEN; newodb_ensure_root_extension; simplifieddbopenverbandgetodbparammain.c: Collapse 8 search paths to 4; update--migratedefaults;--outputerror handling withstrerror(errno)Tests/tools (14 files):
Frontier.root7references →Frontier.rootDocs (17 files):
Test plan
make -C frontier-cli— builds clean (6 pre-existing warnings)make -C tests test— all pass (table_operations_integration failures pre-existing)make -C tests test-migrate-flag— 17/17 passgrep -rn "root7"verification — only backward-compat references remaincd tests && make test-integration— integration tests./frontier-cli --migrate tests/fixtures/v6/Frontier.rootproduces.v6.rootbackup + v7 at.root🤖 Generated with Claude Code